FOSSology  4.6.0-rc1
Open Source License Compliance by Open Source Software
UploadPageBase.php
1 <?php
2 /*
3  SPDX-FileCopyrightText: © 2015 Siemens AG
4 
5  SPDX-License-Identifier: GPL-2.0-only
6 */
7 
8 namespace Fossology\UI\Page;
9 
16 use Monolog\Logger;
17 use Symfony\Component\HttpFoundation\Request;
18 
19 abstract class UploadPageBase extends DefaultPlugin
20 {
21  const NAME = "upload_file";
22  const FOLDER_PARAMETER_NAME = 'folder';
23 
24  const DESCRIPTION_INPUT_NAME = 'descriptionInputName';
25  const DESCRIPTION_VALUE = 'descriptionValue';
26  const UPLOAD_FORM_BUILD_PARAMETER_NAME = 'uploadformbuild';
27  const PUBLIC_ALL = 'public';
28  const PUBLIC_GROUPS = 'protected';
29 
31  private $folderDao;
33  private $uploadDao;
35  private $logger;
37  private $userDao;
38 
39  public function __construct($name, $parameters = array())
40  {
41  parent::__construct($name, $parameters);
42 
43  $this->folderDao = $this->getObject('dao.folder');
44  $this->uploadDao = $this->getObject('dao.upload');
45  $this->logger = $this->getObject('logger');
46  $this->userDao = $this->getObject('dao.user');
47  }
48  abstract protected function handleUpload(Request $request);
49  abstract protected function handleView(Request $request, $vars);
50 
51  protected function handle(Request $request)
52  {
53  // Handle request
54  $this->folderDao->ensureTopLevelFolder();
55 
56  $message = "";
57  $description = "";
58  if ($request->isMethod(Request::METHOD_POST)) {
59  list($success, $message, $description) = $this->handleUpload($request);
60  }
61  $vars['message'] = $message;
62  $vars['descriptionInputValue'] = $description ?: "";
63  $vars['descriptionInputName'] = self::DESCRIPTION_INPUT_NAME;
64  $vars['folderParameterName'] = self::FOLDER_PARAMETER_NAME;
65  $vars['upload_max_filesize'] = ini_get('upload_max_filesize');
66  $vars['agentCheckBoxMake'] = '';
67  global $SysConf;
68  $userId = Auth::getUserId();
69  $UserRec = $this->userDao->getUserByPk($userId);
70  if (!empty($UserRec['upload_visibility'])) {
71  $vars['uploadVisibility'] = $UserRec['upload_visibility'];
72  } else {
73  $vars['uploadVisibility'] = $SysConf['SYSCONFIG']['UploadVisibility'];
74  }
75  $rootFolder = $this->folderDao->getDefaultFolder(Auth::getUserId());
76  if ($rootFolder == NULL) {
77  $rootFolder = $this->folderDao->getRootFolder(Auth::getUserId());
78  }
79  $folderStructure = $this->folderDao->getFolderStructure($rootFolder->getId());
80 
81  $vars['folderStructure'] = $folderStructure;
82  $vars['baseUrl'] = $request->getBaseUrl();
83  $vars['moduleName'] = $this->getName();
84  $vars[self::FOLDER_PARAMETER_NAME] = $request->get(self::FOLDER_PARAMETER_NAME);
85 
86  $parmAgentList = MenuHook::getAgentPluginNames("ParmAgents");
87  $vars['parmAgentContents'] = array();
88  $vars['parmAgentFoots'] = array();
89  foreach ($parmAgentList as $parmAgent) {
90  $agent = plugin_find($parmAgent);
91  $vars['parmAgentContents'][] = $agent->renderContent($vars);
92  $vars['parmAgentFoots'][] = $agent->renderFoot($vars);
93  }
94 
95  $session = $request->getSession();
96  $session->set(self::UPLOAD_FORM_BUILD_PARAMETER_NAME, time().':'.$_SERVER['REMOTE_ADDR']);
97  $vars['uploadFormBuild'] = $session->get(self::UPLOAD_FORM_BUILD_PARAMETER_NAME);
98  $vars['uploadFormBuildParameterName'] = self::UPLOAD_FORM_BUILD_PARAMETER_NAME;
99 
100  if (@$_SESSION[Auth::USER_LEVEL] >= PLUGIN_DB_WRITE) {
101  $skip = array("agent_unpack", "agent_adj2nest", "wget_agent");
102  $vars['agentCheckBoxMake'] = AgentCheckBoxMake(-1, $skip);
103  }
104  $vars['configureExcludeFolders'] = ($exclude = $this->sanitizeExcludePatterns($SysConf['SYSCONFIG']['ExcludeFolders'] ?? '')) ? $exclude : "No Folder Configured";
105 
106  return $this->handleView($request, $vars);
107  }
108 
109  protected function postUploadAddJobs(Request $request, $fileName, $uploadId, $jobId = null, $wgetDependency = false)
110  {
111  $userId = Auth::getUserId();
112  $groupId = Auth::getGroupId();
113 
114  if ($jobId === null) {
115  $jobId = JobAddJob($userId, $groupId, $fileName, $uploadId);
116  }
117  $dummy = "";
118 
119  global $SysConf;
120  $unpackArgs = intval($request->get('scm')) === 1 ? '-I' : '';
121 
122  // Only process exclude folders if checkbox is checked
123  if (intval($request->get('excludefolder')) === 1) {
124  $userExclude = $request->get('excludefolderSpecific');
125  if ($userExclude) {
126  $sanitized = $this->sanitizeExcludePatterns($userExclude);
127  if ($sanitized !== '') {
128  $unpackArgs .= ' -E ' . $sanitized;
129  }
130  }
131  }
132  $adj2nestDependencies = array();
133  if ($wgetDependency) {
134  $adj2nestDependencies = array(array('name'=>'agent_unpack','args'=>$unpackArgs,AgentPlugin::PRE_JOB_QUEUE=>array('wget_agent')));
135  }
136  $adj2nestplugin = \plugin_find('agent_adj2nest');
137  $adj2nestplugin->AgentAdd($jobId, $uploadId, $dummy, $adj2nestDependencies,
138  null, null, (empty($adj2nestDependencies) ? $unpackArgs : ''));
139 
140  $checkedAgents = checkedAgents();
141  AgentSchedule($jobId, $uploadId, $checkedAgents);
142 
143  $errorMsg = '';
144  $parmAgentList = MenuHook::getAgentPluginNames("ParmAgents");
145  $plainAgentList = MenuHook::getAgentPluginNames("Agents");
146  $agentList = array_merge($plainAgentList, $parmAgentList);
147 
148  $this->rearrangeDependencies($parmAgentList);
149 
150  foreach ($parmAgentList as $parmAgent) {
151  $agent = plugin_find($parmAgent);
152  $agent->scheduleAgent($jobId, $uploadId, $errorMsg, $request, $agentList);
153  }
154 
155  $status = GetRunnableJobList();
156  $message = empty($status) ? _("Is the scheduler running? ") : "";
157  $jobUrl = Traceback_uri() . "?mod=showjobs&upload=$uploadId";
158  $message .= _("The file") . " " . $fileName . " " . _("has been uploaded. It is") .
159  ' <a href=' . $jobUrl . '>upload #' . $uploadId . "</a>.\n";
160  if ($request->get('public')==self::PUBLIC_GROUPS) {
161  $this->getObject('dao.upload.permission')->makeAccessibleToAllGroupsOf($uploadId, $userId);
162  }
163  return $message;
164  }
165 
175  function str_contains_notescaped_char($str, $char)
176  {
177  $pos = 0;
178  while ($pos < strlen($str) &&
179  ($pos = strpos($str,$char,$pos)) !== false) {
180  foreach (range(($pos++) -1, 1, -2) as $tpos) {
181  if ($tpos > 0 && $str[$tpos] !== '\\') {
182  break;
183  }
184  if ($tpos > 1 && $str[$tpos - 1] !== '\\') {
185  continue 2;
186  }
187  }
188  return true;
189  }
190  return false;
191  }
192 
200  function path_is_pattern($path)
201  {
202  return $this->str_contains_notescaped_char($path, '*')
203  || $this->str_contains_notescaped_char($path, '?')
204  || $this->str_contains_notescaped_char($path, '[')
205  || $this->str_contains_notescaped_char($path, '{');
206  }
207 
216  protected function path_can_escape($path)
217  {
218  return $this->str_contains_notescaped_char($path, '$')
219  || strpos($path,'..') !== false;
220  }
221 
232  function normalize_path($path, $host="localhost", $appendix="")
233  {
234  if (strpos($path,'/') === false || $path === '/') {
235  return false;
236  }
237  if ($this->path_is_pattern($path)) {
238  $bpath = basename($path);
239  if ($this->path_can_escape($bpath)) {
240  return false;
241  }
242 
243  if (strcmp($host,"localhost") === 0) {
244  return $this->normalize_path(dirname($path),
245  $host,
246  $bpath . ($appendix == '' ?
247  '' :
248  '/' . $appendix));
249  } else {
250  if ($this->path_can_escape($path)) {
251  return false;
252  }
253  return $path . ($appendix == '' ?
254  '' :
255  '/' . $appendix);
256  }
257  } else {
258  $rpath = realpath($path);
259  if ($rpath === false) {
260  return false;
261  }
262  return $rpath . ($appendix == '' ?
263  '' :
264  '/' . $appendix);
265  }
266  }
267 
268  function basicShEscaping($str)
269  {
270  $str = str_replace('\\', '\\\\', $str);
271  $str = str_replace('"', '\"', $str);
272  $str = str_replace('`', '\`', $str);
273  $str = str_replace('$', '\$', $str);
274  return $str;
275  }
276 
282  private function rearrangeDependencies(&$parmList)
283  {
284  $deciderKey = array_search('agent_decider', $parmList);
285  $reuserKey = array_search('agent_reuser', $parmList);
286  if ($deciderKey !== false && $reuserKey !== false) {
287  $temp = $parmList[$deciderKey];
288  $parmList[$deciderKey] = $parmList[$reuserKey];
289  $parmList[$reuserKey] = $temp;
290  }
291  }
311  private function sanitizeExcludePatterns($patternStr)
312  {
313  $patterns = explode(',', $patternStr);
314  $sanitized = [];
315 
316  foreach ($patterns as $pattern) {
317  $trimmed = trim($pattern);
318  // Skip empty strings, relative paths (./, ../), or ones with special characters
319  if (
320  $trimmed === '' ||
321  preg_match('#(^|/)(\.\.?)(/|$)|^[/.?]+$|[?,"{}:]#', $trimmed)
322  ) {
323  continue;
324  }
325 
326  // Ensure the pattern ends with "/"
327  if (substr($trimmed, -1) !== '/') {
328  $trimmed .= '/';
329  }
330 
331  $sanitized[] = $trimmed;
332  }
333  return implode(',', $sanitized);
334  }
335 }
Contains the constants and helpers for authentication of user.
Definition: Auth.php:24
static getUserId()
Get the current user's id.
Definition: Auth.php:68
static getGroupId()
Get the current user's group id.
Definition: Auth.php:80
static getAgentPluginNames($hook='Agents')
Definition: MenuHook.php:16
path_is_pattern($path)
checks, whether a path is a pattern from the perspective of a shell
path_can_escape($path)
checks, whether a path contains substrings, which could enable it to escape his prefix
str_contains_notescaped_char($str, $char)
checks, whether a string contains some special character without escaping
normalize_path($path, $host="localhost", $appendix="")
normalizes an path and returns FALSE on errors
AgentSchedule($jobId, $uploadId, $agents)
Schedule all given agents.
checkedAgents($agents=null)
read the UI form and return array of user selected agents Because input comes from the user,...
AgentCheckBoxMake($upload_pk, $SkipAgents=array(), $specified_username="")
Generate a checkbox list of available agents.
Traceback_uri()
Get the URI without query to this location.
Definition: common-parm.php:97
plugin_find($pluginName)
Given the official name of a plugin, return the $Plugins object.
GetRunnableJobList()
Get runnable job list, the process is below:
char * trim(char *ptext)
Trimming whitespace.
Definition: fossconfig.c:690
#define PLUGIN_DB_WRITE
Plugin requires write permission on DB.
Definition: libfossology.h:38
list_t type structure used to keep various lists. (e.g. there are multiple lists).
Definition: nomos.h:308