Issues (108)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

service/configservice.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Gallery
4
 *
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later. See the COPYING file.
7
 *
8
 * @author Olivier Paroz <[email protected]>
9
 *
10
 * @copyright Olivier Paroz 2016
11
 */
12
13
namespace OCA\Gallery\Service;
14
15
use OCP\Files\Folder;
16
use OCP\ILogger;
17
18
use OCA\Gallery\Config\ConfigParser;
19
use OCA\Gallery\Config\ConfigException;
20
use OCA\Gallery\Environment\Environment;
21
use OCA\Gallery\Preview\Preview;
22
23
/**
24
 * Finds configurations files and returns a configuration array
25
 *
26
 * Checks the current and parent folders for configuration files and to see if we're allowed to
27
 * look for media file
28
 * Supports explicit inheritance
29
 *
30
 * @package OCA\Gallery\Service
31
 */
32
class ConfigService extends FilesService {
33
34
	/** @var string */
35
	private $configName = 'gallery.cnf';
36
	/** @var array <string,bool> */
37
	private $completionStatus = ['design' => false, 'information' => false, 'sorting' => false];
38
	/** @var ConfigParser */
39
	private $configParser;
40
	/** @var Preview */
41
	private $previewManager;
42
	/**
43
	 * @todo This hard-coded array could be replaced by admin settings
44
	 *
45
	 * @var string[]
46
	 */
47
	private $baseMimeTypes = [
48
		'image/png',
49
		'image/jpeg',
50
		'image/gif',
51
		'image/x-xbitmap',
52
		'image/bmp',
53
		'image/tiff',
54
		'image/x-dcraw',
55
		'image/heic',
56
		'image/heif',
57
		'application/x-photoshop',
58
		'application/illustrator',
59
		'application/postscript',
60
	];
61
	/**
62
	 * These types are useful for files preview in the files app, but
63
	 * not for the gallery side
64
	 *
65
	 * @var string[]
66
	 */
67
	private $slideshowMimeTypes = [
68
		'application/font-sfnt',
69
		'application/x-font',
70
	];
71
72
	/**
73
	 * Constructor
74
	 *
75
	 * @param string $appName
76
	 * @param Environment $environment
77
	 * @param ConfigParser $configParser
78
	 * @param Preview $previewManager
79
	 * @param ILogger $logger
80
	 */
81
	public function __construct(
82
		$appName,
83
		Environment $environment,
84
		ConfigParser $configParser,
85
		Preview $previewManager,
86
		ILogger $logger
87
	) {
88
		parent::__construct($appName, $environment, $logger);
89
90
		$this->configParser = $configParser;
91
		$this->previewManager = $previewManager;
92
	}
93
94
	/**
95
	 * Returns a list of supported features
96
	 *
97
	 * @return string[]
0 ignored issues
show
Should the return type not be null|array? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
98
	 */
99
	public function getFeaturesList() {
100
		$featuresList = [];
101
		/** @var Folder $rootFolder */
102
		$rootFolder = $this->environment->getVirtualRootFolder();
103
		if ($this->isAllowedAndAvailable($rootFolder) && $this->configExists($rootFolder)) {
104
			try {
105
				$featuresList =
106
					$this->configParser->getFeaturesList($rootFolder, $this->configName);
107
			} catch (ConfigException $exception) {
108
				$featuresList = $this->buildErrorMessage($exception, $rootFolder);
109
			}
110
		}
111
112
		return $featuresList;
113
	}
114
115
	/**
116
	 * This builds and returns a list of all supported media types
117
	 *
118
	 * @todo Native SVG could be disabled via admin settings
119
	 *
120
	 * @param bool $extraMediaTypes
121
	 * @param bool $nativeSvgSupport
122
	 *
123
	 * @return string[] all supported media types
124
	 */
125
	public function getSupportedMediaTypes($extraMediaTypes, $nativeSvgSupport) {
126
		$supportedMimes = [];
127
		$wantedMimes = $this->baseMimeTypes;
128
		if ($extraMediaTypes) {
129
			$wantedMimes = \array_merge($wantedMimes, $this->slideshowMimeTypes);
130
		}
131
		foreach ($wantedMimes as $wantedMime) {
132
			// Let's see if a preview of files of that media type can be generated
133
			if ($this->isMimeSupported($wantedMime)) {
134
				// We store the media type
135
				$supportedMimes[] = $wantedMime;
136
			}
137
		}
138
		$supportedMimes = $this->addSvgSupport($supportedMimes, $nativeSvgSupport);
139
140
		//$this->logger->debug("Supported Mimes: {mimes}", ['mimes' => $supportedMimes]);
141
142
		return $supportedMimes;
143
	}
144
145
	/**
146
	 * Returns the configuration of the currently selected folder
147
	 *
148
	 *    * information (description, copyright)
149
	 *    * sorting (date, name, inheritance)
150
	 *    * design (colour)
151
	 *    * if the album should be ignored
152
	 *
153
	 * @param Folder $folderNode the current folder
154
	 * @param array $features the list of features retrieved fro the configuration file
155
	 *
156
	 * @return array|null
157
	 * @throws ForbiddenServiceException
158
	 */
159
	public function getConfig($folderNode, $features) {
160
		$this->features = $features;
161
		list($albumConfig, $ignored) =
162
			$this->collectConfig($folderNode, $this->ignoreAlbum, $this->configName);
163
		if ($ignored) {
164
			throw new ForbiddenServiceException(
165
				'The owner has placed a restriction or the storage location is unavailable'
166
			);
167
		}
168
169
		return $albumConfig;
170
	}
171
172
	/**
173
	 * Throws an exception if the media type of the file is not part of what the app allows
174
	 *
175
	 * @param $mimeType
176
	 *
177
	 * @throws ForbiddenServiceException
178
	 */
179
	public function validateMimeType($mimeType) {
180
		if (!\in_array($mimeType, $this->getSupportedMediaTypes(true, true))) {
181
			throw new ForbiddenServiceException('Media type not allowed');
182
		}
183
	}
184
185
	/**
186
	 * Determines if we have a configuration file to work with
187
	 *
188
	 * @param Folder $rootFolder the virtual root folder
189
	 *
190
	 * @return bool
191
	 */
192
	private function configExists($rootFolder) {
193
		return $rootFolder && $rootFolder->nodeExists($this->configName);
194
	}
195
196
	/**
197
	 * Adds the SVG media type if it's not already there
198
	 *
199
	 * If it's enabled, but doesn't work, an exception will be raised when trying to generate a
200
	 * preview. If it's disabled, we support it via the browser's native support
201
	 *
202
	 * @param string[] $supportedMimes
203
	 * @param bool $nativeSvgSupport
204
	 *
205
	 * @return string[]
206
	 */
207
	private function addSvgSupport($supportedMimes, $nativeSvgSupport) {
208
		if (!\in_array('image/svg+xml', $supportedMimes) && $nativeSvgSupport) {
209
			$supportedMimes[] = 'image/svg+xml';
210
		}
211
212
		return $supportedMimes;
213
	}
214
215
	/**
216
	 * Returns true if the passed mime type is supported
217
	 *
218
	 * In case of a failure, we just return that the media type is not supported
219
	 *
220
	 * @param string $mimeType
221
	 *
222
	 * @return boolean
223
	 */
224
	private function isMimeSupported($mimeType = '*') {
225
		try {
226
			return $this->previewManager->isMimeSupported($mimeType);
227
		} catch (\Exception $exception) {
228
			unset($exception);
229
230
			return false;
231
		}
232
	}
233
234
	/**
235
	 * Returns an album configuration array
236
	 *
237
	 * Goes through all the parent folders until either we're told the album is private or we've
238
	 * reached the root folder
239
	 *
240
	 * @param Folder $folder the current folder
241
	 * @param string $ignoreAlbum name of the file which blacklists folders
242
	 * @param string $configName name of the configuration file
243
	 * @param int $level the starting level is 0 and we add 1 each time we visit a parent folder
244
	 * @param array $configSoFar the configuration collected so far
245
	 *
246
	 * @return array <null|array,bool>
247
	 */
248
	private function collectConfig(
249
		$folder, $ignoreAlbum, $configName, $level = 0, $configSoFar = []
250
	) {
251
		if ($folder->nodeExists($ignoreAlbum)) {
252
			// Cancel as soon as we find out that the folder is private or external
253
			return [null, true];
254
		}
255
		$isRootFolder = $this->isRootFolder($folder, $level);
256
		if ($folder->nodeExists($configName)) {
257
			$configSoFar = $this->buildFolderConfig($folder, $configName, $configSoFar, $level);
258
		}
259
		if (!$isRootFolder) {
260
			return $this->getParentConfig($folder, $ignoreAlbum, $configName, $level, $configSoFar);
261
		}
262
		$configSoFar = $this->validatesInfoConfig($configSoFar);
263
264
		// We have reached the root folder
265
		return [$configSoFar, false];
266
	}
267
268
	/**
269
	 * Returns a parsed configuration if one was found in the current folder or generates an error
270
	 * message to send back
271
	 *
272
	 * @param Folder $folder the current folder
273
	 * @param string $configName name of the configuration file
274
	 * @param array $collectedConfig the configuration collected so far
275
	 * @param int $level the starting level is 0 and we add 1 each time we visit a parent folder
276
	 *
277
	 * @return array
278
	 */
279
	private function buildFolderConfig($folder, $configName, $collectedConfig, $level) {
280
		try {
281
			list($collectedConfig, $completionStatus) = $this->configParser->getFolderConfig(
282
				$folder, $configName, $collectedConfig, $this->completionStatus, $level
283
			);
284
			$this->completionStatus = $completionStatus;
285
		} catch (ConfigException $exception) {
286
			$collectedConfig = $this->buildErrorMessage($exception, $folder);
287
		}
288
289
		return $collectedConfig;
290
	}
291
292
	/**
293
	 * Builds the error message to send back when there is an error
294
	 *
295
	 * @fixme Missing translation
296
	 *
297
	 * @param ConfigException $exception
298
	 * @param Folder $folder the current folder
299
	 *
300
	 * @return array<array<string,string>,bool>
0 ignored issues
show
Should the return type not be array<string,array<string,string>>[]?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
301
	 */
302
	private function buildErrorMessage($exception, $folder) {
303
		$configPath = $this->environment->getPathFromVirtualRoot($folder);
304
		$errorMessage = $exception->getMessage() . ". Config location: /$configPath";
305
		$this->logger->error($errorMessage);
306
		$config = ['error' => ['message' => $errorMessage]];
307
308
		$completionStatus = $this->completionStatus;
309
		foreach ($completionStatus as $key) {
310
			$completionStatus[$key] = true;
311
		}
312
		$this->completionStatus = $completionStatus;
313
314
		return [$config];
315
	}
316
317
	/**
318
	 * Removes links if they were collected outside of the virtual root
319
	 *
320
	 * This is for shared folders which have a virtual root
321
	 *
322
	 * @param array $albumConfig
323
	 *
324
	 * @return array
325
	 */
326
	private function validatesInfoConfig($albumConfig) {
327
		$this->virtualRootLevel;
328
		if (\array_key_exists('information', $albumConfig)) {
329
			$info = $albumConfig['information'];
330
			if (\array_key_exists('level', $info)) {
331
				$level = $info['level'];
332
				if ($level > $this->virtualRootLevel) {
333
					$albumConfig['information']['description_link'] = null;
334
					$albumConfig['information']['copyright_link'] = null;
335
				}
336
			}
337
		}
338
339
		return $albumConfig;
340
	}
341
342
	/**
343
	 * Looks for an album configuration in the parent folder
344
	 *
345
	 * We will look up to the virtual root of a shared folder, for privacy reasons
346
	 *
347
	 * @param Folder $folder the current folder
348
	 * @param string $privacyChecker name of the file which blacklists folders
349
	 * @param string $configName name of the configuration file
350
	 * @param int $level the starting level is 0 and we add 1 each time we visit a parent folder
351
	 * @param array $collectedConfig the configuration collected so far
352
	 *
353
	 * @return array<null|array,bool>
354
	 */
355
	private function getParentConfig($folder, $privacyChecker, $configName, $level, $collectedConfig
356
	) {
357
		$parentFolder = $folder->getParent();
358
		$level++;
359
360
		return $this->collectConfig(
361
			$parentFolder, $privacyChecker, $configName, $level, $collectedConfig
362
		);
363
	}
364
}
365