FOSSology  4.5.1
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  if (intval($request->get('excludefolder'))) {
123  $rawExclude = $SysConf['SYSCONFIG']['ExcludeFolders'] ?? '';
124  if (trim($rawExclude) !== '') {
125  $excludeFolders = $this->sanitizeExcludePatterns($rawExclude);
126  $excludeArgs = '-E ' . $excludeFolders;
127  $unpackArgs .= ' ' . $excludeArgs;
128  }
129  }
130  $adj2nestDependencies = array();
131  if ($wgetDependency) {
132  $adj2nestDependencies = array(array('name'=>'agent_unpack','args'=>$unpackArgs,AgentPlugin::PRE_JOB_QUEUE=>array('wget_agent')));
133  }
134  $adj2nestplugin = \plugin_find('agent_adj2nest');
135  $adj2nestplugin->AgentAdd($jobId, $uploadId, $dummy, $adj2nestDependencies,
136  null, null, (empty($adj2nestDependencies) ? $unpackArgs : ''));
137 
138  $checkedAgents = checkedAgents();
139  AgentSchedule($jobId, $uploadId, $checkedAgents);
140 
141  $errorMsg = '';
142  $parmAgentList = MenuHook::getAgentPluginNames("ParmAgents");
143  $plainAgentList = MenuHook::getAgentPluginNames("Agents");
144  $agentList = array_merge($plainAgentList, $parmAgentList);
145 
146  $this->rearrangeDependencies($parmAgentList);
147 
148  foreach ($parmAgentList as $parmAgent) {
149  $agent = plugin_find($parmAgent);
150  $agent->scheduleAgent($jobId, $uploadId, $errorMsg, $request, $agentList);
151  }
152 
153  $status = GetRunnableJobList();
154  $message = empty($status) ? _("Is the scheduler running? ") : "";
155  $jobUrl = Traceback_uri() . "?mod=showjobs&upload=$uploadId";
156  $message .= _("The file") . " " . $fileName . " " . _("has been uploaded. It is") .
157  ' <a href=' . $jobUrl . '>upload #' . $uploadId . "</a>.\n";
158  if ($request->get('public')==self::PUBLIC_GROUPS) {
159  $this->getObject('dao.upload.permission')->makeAccessibleToAllGroupsOf($uploadId, $userId);
160  }
161  return $message;
162  }
163 
173  function str_contains_notescaped_char($str, $char)
174  {
175  $pos = 0;
176  while ($pos < strlen($str) &&
177  ($pos = strpos($str,$char,$pos)) !== false) {
178  foreach (range(($pos++) -1, 1, -2) as $tpos) {
179  if ($tpos > 0 && $str[$tpos] !== '\\') {
180  break;
181  }
182  if ($tpos > 1 && $str[$tpos - 1] !== '\\') {
183  continue 2;
184  }
185  }
186  return true;
187  }
188  return false;
189  }
190 
198  function path_is_pattern($path)
199  {
200  return $this->str_contains_notescaped_char($path, '*')
201  || $this->str_contains_notescaped_char($path, '?')
202  || $this->str_contains_notescaped_char($path, '[')
203  || $this->str_contains_notescaped_char($path, '{');
204  }
205 
214  protected function path_can_escape($path)
215  {
216  return $this->str_contains_notescaped_char($path, '$')
217  || strpos($path,'..') !== false;
218  }
219 
230  function normalize_path($path, $host="localhost", $appendix="")
231  {
232  if (strpos($path,'/') === false || $path === '/') {
233  return false;
234  }
235  if ($this->path_is_pattern($path)) {
236  $bpath = basename($path);
237  if ($this->path_can_escape($bpath)) {
238  return false;
239  }
240 
241  if (strcmp($host,"localhost") === 0) {
242  return $this->normalize_path(dirname($path),
243  $host,
244  $bpath . ($appendix == '' ?
245  '' :
246  '/' . $appendix));
247  } else {
248  if ($this->path_can_escape($path)) {
249  return false;
250  }
251  return $path . ($appendix == '' ?
252  '' :
253  '/' . $appendix);
254  }
255  } else {
256  $rpath = realpath($path);
257  if ($rpath === false) {
258  return false;
259  }
260  return $rpath . ($appendix == '' ?
261  '' :
262  '/' . $appendix);
263  }
264  }
265 
266  function basicShEscaping($str)
267  {
268  $str = str_replace('\\', '\\\\', $str);
269  $str = str_replace('"', '\"', $str);
270  $str = str_replace('`', '\`', $str);
271  $str = str_replace('$', '\$', $str);
272  return $str;
273  }
274 
280  private function rearrangeDependencies(&$parmList)
281  {
282  $deciderKey = array_search('agent_decider', $parmList);
283  $reuserKey = array_search('agent_reuser', $parmList);
284  if ($deciderKey !== false && $reuserKey !== false) {
285  $temp = $parmList[$deciderKey];
286  $parmList[$deciderKey] = $parmList[$reuserKey];
287  $parmList[$reuserKey] = $temp;
288  }
289  }
309  private function sanitizeExcludePatterns($patternStr)
310  {
311  $patterns = explode(',', $patternStr);
312  $sanitized = [];
313 
314  foreach ($patterns as $pattern) {
315  $trimmed = trim($pattern);
316  // Skip empty strings, relative paths (./, ../), or ones with special characters
317  if (
318  $trimmed === '' ||
319  preg_match('#(^|/)(\.\.?)(/|$)|^[/.?]+$|[?,"{}:]#', $trimmed)
320  ) {
321  continue;
322  }
323 
324  // Ensure the pattern ends with "/"
325  if (substr($trimmed, -1) !== '/') {
326  $trimmed .= '/';
327  }
328 
329  $sanitized[] = $trimmed;
330  }
331  return implode(',', $sanitized);
332  }
333 }
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