Completed
Push — master ( c62fa5...cac44f )
by John
30:47 queued 13s
created
lib/public/Files/IMimeTypeDetector.php 1 patch
Indentation   +70 added lines, -70 removed lines patch added patch discarded remove patch
@@ -17,82 +17,82 @@
 block discarded – undo
17 17
  * Interface to handle MIME type (detection and icon retrieval)
18 18
  **/
19 19
 interface IMimeTypeDetector {
20
-	/**
21
-	 * Detect MIME type only based on filename, content of file is not used
22
-	 * @param string $path
23
-	 * @return string
24
-	 * @since 8.2.0
25
-	 */
26
-	public function detectPath($path);
20
+    /**
21
+     * Detect MIME type only based on filename, content of file is not used
22
+     * @param string $path
23
+     * @return string
24
+     * @since 8.2.0
25
+     */
26
+    public function detectPath($path);
27 27
 
28
-	/**
29
-	 * Detect MIME type only based on the content of file
30
-	 * @param string $path
31
-	 * @return string
32
-	 * @since 18.0.0
33
-	 */
34
-	public function detectContent(string $path): string;
28
+    /**
29
+     * Detect MIME type only based on the content of file
30
+     * @param string $path
31
+     * @return string
32
+     * @since 18.0.0
33
+     */
34
+    public function detectContent(string $path): string;
35 35
 
36
-	/**
37
-	 * Detect MIME type based on both filename and content
38
-	 *
39
-	 * @param string $path
40
-	 * @return string
41
-	 * @since 8.2.0
42
-	 */
43
-	public function detect($path);
36
+    /**
37
+     * Detect MIME type based on both filename and content
38
+     *
39
+     * @param string $path
40
+     * @return string
41
+     * @since 8.2.0
42
+     */
43
+    public function detect($path);
44 44
 
45
-	/**
46
-	 * Get a secure MIME type that won't expose potential XSS.
47
-	 *
48
-	 * @param string $mimeType
49
-	 * @return string
50
-	 * @since 8.2.0
51
-	 */
52
-	public function getSecureMimeType($mimeType);
45
+    /**
46
+     * Get a secure MIME type that won't expose potential XSS.
47
+     *
48
+     * @param string $mimeType
49
+     * @return string
50
+     * @since 8.2.0
51
+     */
52
+    public function getSecureMimeType($mimeType);
53 53
 
54
-	/**
55
-	 * Detect MIME type based on the content of a string
56
-	 *
57
-	 * @param string $data
58
-	 * @return string
59
-	 * @since 8.2.0
60
-	 */
61
-	public function detectString($data);
54
+    /**
55
+     * Detect MIME type based on the content of a string
56
+     *
57
+     * @param string $data
58
+     * @return string
59
+     * @since 8.2.0
60
+     */
61
+    public function detectString($data);
62 62
 
63
-	/**
64
-	 * Get path to the icon of a file type
65
-	 * @param string $mimeType the MIME type
66
-	 * @return string the url
67
-	 * @since 8.2.0
68
-	 */
69
-	public function mimeTypeIcon($mimeType);
63
+    /**
64
+     * Get path to the icon of a file type
65
+     * @param string $mimeType the MIME type
66
+     * @return string the url
67
+     * @since 8.2.0
68
+     */
69
+    public function mimeTypeIcon($mimeType);
70 70
 
71
-	/**
72
-	 * @return array<string,string>
73
-	 * @since 28.0.0
74
-	 */
75
-	public function getAllAliases(): array;
71
+    /**
72
+     * @return array<string,string>
73
+     * @since 28.0.0
74
+     */
75
+    public function getAllAliases(): array;
76 76
 
77
-	/**
78
-	 * Get all extension to MIME type mappings.
79
-	 *
80
-	 * The return format is an array of the file extension, as the key,
81
-	 * mapped to a list where the first entry is the MIME type
82
-	 * and the second entry is the secure MIME type (or null if none).
83
-	 * Due to PHP idiosyncrasies if a numeric string is set as the extension,
84
-	 * then also the array key (file extension) is a number instead of a string.
85
-	 *
86
-	 * @return array<list{string, string|null}>
87
-	 * @since 32.0.0
88
-	 */
89
-	public function getAllMappings(): array;
77
+    /**
78
+     * Get all extension to MIME type mappings.
79
+     *
80
+     * The return format is an array of the file extension, as the key,
81
+     * mapped to a list where the first entry is the MIME type
82
+     * and the second entry is the secure MIME type (or null if none).
83
+     * Due to PHP idiosyncrasies if a numeric string is set as the extension,
84
+     * then also the array key (file extension) is a number instead of a string.
85
+     *
86
+     * @return array<list{string, string|null}>
87
+     * @since 32.0.0
88
+     */
89
+    public function getAllMappings(): array;
90 90
 
91
-	/**
92
-	 * Get all human readable mime names
93
-	 *
94
-	 * @return array<string,string>
95
-	 * @since 32.0.0
96
-	 */
97
-	public function getAllNamings(): array;
91
+    /**
92
+     * Get all human readable mime names
93
+     *
94
+     * @return array<string,string>
95
+     * @since 32.0.0
96
+     */
97
+    public function getAllNamings(): array;
98 98
 }
Please login to merge, or discard this patch.
lib/private/IntegrityCheck/Checker.php 2 patches
Indentation   +519 added lines, -519 removed lines patch added patch discarded remove patch
@@ -36,523 +36,523 @@
 block discarded – undo
36 36
  * @package OC\IntegrityCheck
37 37
  */
38 38
 class Checker {
39
-	public const CACHE_KEY = 'oc.integritycheck.checker';
40
-
41
-	private ICache $cache;
42
-
43
-	public function __construct(
44
-		private ServerVersion $serverVersion,
45
-		private EnvironmentHelper $environmentHelper,
46
-		private FileAccessHelper $fileAccessHelper,
47
-		private AppLocator $appLocator,
48
-		private ?IConfig $config,
49
-		private ?IAppConfig $appConfig,
50
-		ICacheFactory $cacheFactory,
51
-		private IAppManager $appManager,
52
-		private IMimeTypeDetector $mimeTypeDetector,
53
-	) {
54
-		$this->cache = $cacheFactory->createDistributed(self::CACHE_KEY);
55
-	}
56
-
57
-	/**
58
-	 * Whether code signing is enforced or not.
59
-	 *
60
-	 * @return bool
61
-	 */
62
-	public function isCodeCheckEnforced(): bool {
63
-		$notSignedChannels = [ '', 'git'];
64
-		if (\in_array($this->serverVersion->getChannel(), $notSignedChannels, true)) {
65
-			return false;
66
-		}
67
-
68
-		/**
69
-		 * This config option is undocumented and supposed to be so, it's only
70
-		 * applicable for very specific scenarios and we should not advertise it
71
-		 * too prominent. So please do not add it to config.sample.php.
72
-		 */
73
-		return !($this->config?->getSystemValueBool('integrity.check.disabled', false) ?? false);
74
-	}
75
-
76
-	/**
77
-	 * Enumerates all files belonging to the folder. Sensible defaults are excluded.
78
-	 *
79
-	 * @param string $folderToIterate
80
-	 * @param string $root
81
-	 * @return \RecursiveIteratorIterator
82
-	 * @throws \Exception
83
-	 */
84
-	private function getFolderIterator(string $folderToIterate, string $root = ''): \RecursiveIteratorIterator {
85
-		$dirItr = new \RecursiveDirectoryIterator(
86
-			$folderToIterate,
87
-			\RecursiveDirectoryIterator::SKIP_DOTS
88
-		);
89
-		if ($root === '') {
90
-			$root = \OC::$SERVERROOT;
91
-		}
92
-		$root = rtrim($root, '/');
93
-
94
-		$excludeGenericFilesIterator = new ExcludeFileByNameFilterIterator($dirItr);
95
-		$excludeFoldersIterator = new ExcludeFoldersByPathFilterIterator($excludeGenericFilesIterator, $root);
96
-
97
-		return new \RecursiveIteratorIterator(
98
-			$excludeFoldersIterator,
99
-			\RecursiveIteratorIterator::SELF_FIRST
100
-		);
101
-	}
102
-
103
-	/**
104
-	 * Returns an array of ['filename' => 'SHA512-hash-of-file'] for all files found
105
-	 * in the iterator.
106
-	 *
107
-	 * @param \RecursiveIteratorIterator $iterator
108
-	 * @param string $path
109
-	 * @return array Array of hashes.
110
-	 */
111
-	private function generateHashes(\RecursiveIteratorIterator $iterator,
112
-		string $path): array {
113
-		$hashes = [];
114
-
115
-		$baseDirectoryLength = \strlen($path);
116
-		foreach ($iterator as $filename => $data) {
117
-			/** @var \DirectoryIterator $data */
118
-			if ($data->isDir()) {
119
-				continue;
120
-			}
121
-
122
-			$relativeFileName = substr($filename, $baseDirectoryLength);
123
-			$relativeFileName = ltrim($relativeFileName, '/');
124
-
125
-			// Exclude signature.json files in the appinfo and root folder
126
-			if ($relativeFileName === 'appinfo/signature.json') {
127
-				continue;
128
-			}
129
-			// Exclude signature.json files in the appinfo and core folder
130
-			if ($relativeFileName === 'core/signature.json') {
131
-				continue;
132
-			}
133
-
134
-			// The .htaccess file in the root folder of ownCloud can contain
135
-			// custom content after the installation due to the fact that dynamic
136
-			// content is written into it at installation time as well. This
137
-			// includes for example the 404 and 403 instructions.
138
-			// Thus we ignore everything below the first occurrence of
139
-			// "#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####" and have the
140
-			// hash generated based on this.
141
-			if ($filename === $this->environmentHelper->getServerRoot() . '/.htaccess') {
142
-				$fileContent = file_get_contents($filename);
143
-				$explodedArray = explode('#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####', $fileContent);
144
-				if (\count($explodedArray) === 2) {
145
-					$hashes[$relativeFileName] = hash('sha512', $explodedArray[0]);
146
-					continue;
147
-				}
148
-			}
149
-			if ($filename === $this->environmentHelper->getServerRoot() . '/core/js/mimetypelist.js') {
150
-				$oldMimetypeList = new GenerateMimetypeFileBuilder();
151
-				$newFile = $oldMimetypeList->generateFile($this->mimeTypeDetector->getAllAliases(), $this->mimeTypeDetector->getAllNamings());
152
-				$oldFile = $this->fileAccessHelper->file_get_contents($filename);
153
-				if ($newFile === $oldFile) {
154
-					$hashes[$relativeFileName] = hash('sha512', $oldMimetypeList->generateFile($this->mimeTypeDetector->getOnlyDefaultAliases(), $this->mimeTypeDetector->getAllNamings()));
155
-					continue;
156
-				}
157
-			}
158
-
159
-			$hashes[$relativeFileName] = hash_file('sha512', $filename);
160
-		}
161
-
162
-		return $hashes;
163
-	}
164
-
165
-	/**
166
-	 * Creates the signature data
167
-	 *
168
-	 * @param array $hashes
169
-	 * @param X509 $certificate
170
-	 * @param RSA $privateKey
171
-	 * @return array
172
-	 */
173
-	private function createSignatureData(array $hashes,
174
-		X509 $certificate,
175
-		RSA $privateKey): array {
176
-		ksort($hashes);
177
-
178
-		$privateKey->setSignatureMode(RSA::SIGNATURE_PSS);
179
-		$privateKey->setMGFHash('sha512');
180
-		// See https://tools.ietf.org/html/rfc3447#page-38
181
-		$privateKey->setSaltLength(0);
182
-		$signature = $privateKey->sign(json_encode($hashes));
183
-
184
-		return [
185
-			'hashes' => $hashes,
186
-			'signature' => base64_encode($signature),
187
-			'certificate' => $certificate->saveX509($certificate->currentCert),
188
-		];
189
-	}
190
-
191
-	/**
192
-	 * Write the signature of the app in the specified folder
193
-	 *
194
-	 * @param string $path
195
-	 * @param X509 $certificate
196
-	 * @param RSA $privateKey
197
-	 * @throws \Exception
198
-	 */
199
-	public function writeAppSignature($path,
200
-		X509 $certificate,
201
-		RSA $privateKey) {
202
-		$appInfoDir = $path . '/appinfo';
203
-		try {
204
-			$this->fileAccessHelper->assertDirectoryExists($appInfoDir);
205
-
206
-			$iterator = $this->getFolderIterator($path);
207
-			$hashes = $this->generateHashes($iterator, $path);
208
-			$signature = $this->createSignatureData($hashes, $certificate, $privateKey);
209
-			$this->fileAccessHelper->file_put_contents(
210
-				$appInfoDir . '/signature.json',
211
-				json_encode($signature, JSON_PRETTY_PRINT)
212
-			);
213
-		} catch (\Exception $e) {
214
-			if (!$this->fileAccessHelper->is_writable($appInfoDir)) {
215
-				throw new \Exception($appInfoDir . ' is not writable');
216
-			}
217
-			throw $e;
218
-		}
219
-	}
220
-
221
-	/**
222
-	 * Write the signature of core
223
-	 *
224
-	 * @param X509 $certificate
225
-	 * @param RSA $rsa
226
-	 * @param string $path
227
-	 * @throws \Exception
228
-	 */
229
-	public function writeCoreSignature(X509 $certificate,
230
-		RSA $rsa,
231
-		$path) {
232
-		$coreDir = $path . '/core';
233
-		try {
234
-			$this->fileAccessHelper->assertDirectoryExists($coreDir);
235
-			$iterator = $this->getFolderIterator($path, $path);
236
-			$hashes = $this->generateHashes($iterator, $path);
237
-			$signatureData = $this->createSignatureData($hashes, $certificate, $rsa);
238
-			$this->fileAccessHelper->file_put_contents(
239
-				$coreDir . '/signature.json',
240
-				json_encode($signatureData, JSON_PRETTY_PRINT)
241
-			);
242
-		} catch (\Exception $e) {
243
-			if (!$this->fileAccessHelper->is_writable($coreDir)) {
244
-				throw new \Exception($coreDir . ' is not writable');
245
-			}
246
-			throw $e;
247
-		}
248
-	}
249
-
250
-	/**
251
-	 * Split the certificate file in individual certs
252
-	 *
253
-	 * @param string $cert
254
-	 * @return string[]
255
-	 */
256
-	private function splitCerts(string $cert): array {
257
-		preg_match_all('([\-]{3,}[\S\ ]+?[\-]{3,}[\S\s]+?[\-]{3,}[\S\ ]+?[\-]{3,})', $cert, $matches);
258
-
259
-		return $matches[0];
260
-	}
261
-
262
-	/**
263
-	 * Verifies the signature for the specified path.
264
-	 *
265
-	 * @param string $signaturePath
266
-	 * @param string $basePath
267
-	 * @param string $certificateCN
268
-	 * @param bool $forceVerify
269
-	 * @return array
270
-	 * @throws InvalidSignatureException
271
-	 * @throws \Exception
272
-	 */
273
-	private function verify(string $signaturePath, string $basePath, string $certificateCN, bool $forceVerify = false): array {
274
-		if (!$forceVerify && !$this->isCodeCheckEnforced()) {
275
-			return [];
276
-		}
277
-
278
-		$content = $this->fileAccessHelper->file_get_contents($signaturePath);
279
-		$signatureData = null;
280
-
281
-		if (\is_string($content)) {
282
-			$signatureData = json_decode($content, true);
283
-		}
284
-		if (!\is_array($signatureData)) {
285
-			throw new InvalidSignatureException('Signature data not found.');
286
-		}
287
-
288
-		$expectedHashes = $signatureData['hashes'];
289
-		ksort($expectedHashes);
290
-		$signature = base64_decode($signatureData['signature']);
291
-		$certificate = $signatureData['certificate'];
292
-
293
-		// Check if certificate is signed by Nextcloud Root Authority
294
-		$x509 = new \phpseclib\File\X509();
295
-		$rootCertificatePublicKey = $this->fileAccessHelper->file_get_contents($this->environmentHelper->getServerRoot() . '/resources/codesigning/root.crt');
296
-
297
-		$rootCerts = $this->splitCerts($rootCertificatePublicKey);
298
-		foreach ($rootCerts as $rootCert) {
299
-			$x509->loadCA($rootCert);
300
-		}
301
-		$x509->loadX509($certificate);
302
-		if (!$x509->validateSignature()) {
303
-			throw new InvalidSignatureException('Certificate is not valid.');
304
-		}
305
-		// Verify if certificate has proper CN. "core" CN is always trusted.
306
-		if ($x509->getDN(X509::DN_OPENSSL)['CN'] !== $certificateCN && $x509->getDN(X509::DN_OPENSSL)['CN'] !== 'core') {
307
-			throw new InvalidSignatureException(
308
-				sprintf('Certificate is not valid for required scope. (Requested: %s, current: CN=%s)', $certificateCN, $x509->getDN(true)['CN'])
309
-			);
310
-		}
311
-
312
-		// Check if the signature of the files is valid
313
-		$rsa = new \phpseclib\Crypt\RSA();
314
-		$rsa->loadKey($x509->currentCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']);
315
-		$rsa->setSignatureMode(RSA::SIGNATURE_PSS);
316
-		$rsa->setMGFHash('sha512');
317
-		// See https://tools.ietf.org/html/rfc3447#page-38
318
-		$rsa->setSaltLength(0);
319
-		if (!$rsa->verify(json_encode($expectedHashes), $signature)) {
320
-			throw new InvalidSignatureException('Signature could not get verified.');
321
-		}
322
-
323
-		// Fixes for the updater as shipped with ownCloud 9.0.x: The updater is
324
-		// replaced after the code integrity check is performed.
325
-		//
326
-		// Due to this reason we exclude the whole updater/ folder from the code
327
-		// integrity check.
328
-		if ($basePath === $this->environmentHelper->getServerRoot()) {
329
-			foreach ($expectedHashes as $fileName => $hash) {
330
-				if (str_starts_with($fileName, 'updater/')) {
331
-					unset($expectedHashes[$fileName]);
332
-				}
333
-			}
334
-		}
335
-
336
-		// Compare the list of files which are not identical
337
-		$currentInstanceHashes = $this->generateHashes($this->getFolderIterator($basePath), $basePath);
338
-		$differencesA = array_diff_assoc($expectedHashes, $currentInstanceHashes);
339
-		$differencesB = array_diff_assoc($currentInstanceHashes, $expectedHashes);
340
-		$differences = array_unique(array_merge($differencesA, $differencesB));
341
-		$differenceArray = [];
342
-		foreach ($differences as $filename => $hash) {
343
-			// Check if file should not exist in the new signature table
344
-			if (!array_key_exists($filename, $expectedHashes)) {
345
-				$differenceArray['EXTRA_FILE'][$filename]['expected'] = '';
346
-				$differenceArray['EXTRA_FILE'][$filename]['current'] = $hash;
347
-				continue;
348
-			}
349
-
350
-			// Check if file is missing
351
-			if (!array_key_exists($filename, $currentInstanceHashes)) {
352
-				$differenceArray['FILE_MISSING'][$filename]['expected'] = $expectedHashes[$filename];
353
-				$differenceArray['FILE_MISSING'][$filename]['current'] = '';
354
-				continue;
355
-			}
356
-
357
-			// Check if hash does mismatch
358
-			if ($expectedHashes[$filename] !== $currentInstanceHashes[$filename]) {
359
-				$differenceArray['INVALID_HASH'][$filename]['expected'] = $expectedHashes[$filename];
360
-				$differenceArray['INVALID_HASH'][$filename]['current'] = $currentInstanceHashes[$filename];
361
-				continue;
362
-			}
363
-
364
-			// Should never happen.
365
-			throw new \Exception('Invalid behaviour in file hash comparison experienced. Please report this error to the developers.');
366
-		}
367
-
368
-		return $differenceArray;
369
-	}
370
-
371
-	/**
372
-	 * Whether the code integrity check has passed successful or not
373
-	 *
374
-	 * @return bool
375
-	 */
376
-	public function hasPassedCheck(): bool {
377
-		$results = $this->getResults();
378
-		if ($results !== null && empty($results)) {
379
-			return true;
380
-		}
381
-
382
-		return false;
383
-	}
384
-
385
-	/**
386
-	 * @return array|null Either the results or null if no results available
387
-	 */
388
-	public function getResults(): ?array {
389
-		$cachedResults = $this->cache->get(self::CACHE_KEY);
390
-		if (!\is_null($cachedResults) and $cachedResults !== false) {
391
-			return json_decode($cachedResults, true);
392
-		}
393
-
394
-		if ($this->appConfig?->hasKey('core', self::CACHE_KEY, lazy: true)) {
395
-			return $this->appConfig->getValueArray('core', self::CACHE_KEY, lazy: true);
396
-		}
397
-
398
-		// No results available
399
-		return null;
400
-	}
401
-
402
-	/**
403
-	 * Stores the results in the app config as well as cache
404
-	 *
405
-	 * @param string $scope
406
-	 * @param array $result
407
-	 */
408
-	private function storeResults(string $scope, array $result) {
409
-		$resultArray = $this->getResults() ?? [];
410
-		unset($resultArray[$scope]);
411
-		if (!empty($result)) {
412
-			$resultArray[$scope] = $result;
413
-		}
414
-		$this->appConfig?->setValueArray('core', self::CACHE_KEY, $resultArray, lazy: true);
415
-		$this->cache->set(self::CACHE_KEY, json_encode($resultArray));
416
-	}
417
-
418
-	/**
419
-	 *
420
-	 * Clean previous results for a proper rescanning. Otherwise
421
-	 */
422
-	private function cleanResults() {
423
-		$this->appConfig->deleteKey('core', self::CACHE_KEY);
424
-		$this->cache->remove(self::CACHE_KEY);
425
-	}
426
-
427
-	/**
428
-	 * Verify the signature of $appId. Returns an array with the following content:
429
-	 * [
430
-	 * 	'FILE_MISSING' =>
431
-	 * 	[
432
-	 * 		'filename' => [
433
-	 * 			'expected' => 'expectedSHA512',
434
-	 * 			'current' => 'currentSHA512',
435
-	 * 		],
436
-	 * 	],
437
-	 * 	'EXTRA_FILE' =>
438
-	 * 	[
439
-	 * 		'filename' => [
440
-	 * 			'expected' => 'expectedSHA512',
441
-	 * 			'current' => 'currentSHA512',
442
-	 * 		],
443
-	 * 	],
444
-	 * 	'INVALID_HASH' =>
445
-	 * 	[
446
-	 * 		'filename' => [
447
-	 * 			'expected' => 'expectedSHA512',
448
-	 * 			'current' => 'currentSHA512',
449
-	 * 		],
450
-	 * 	],
451
-	 * ]
452
-	 *
453
-	 * Array may be empty in case no problems have been found.
454
-	 *
455
-	 * @param string $appId
456
-	 * @param string $path Optional path. If none is given it will be guessed.
457
-	 * @param bool $forceVerify
458
-	 * @return array
459
-	 */
460
-	public function verifyAppSignature(string $appId, string $path = '', bool $forceVerify = false): array {
461
-		try {
462
-			if ($path === '') {
463
-				$path = $this->appLocator->getAppPath($appId);
464
-			}
465
-			$result = $this->verify(
466
-				$path . '/appinfo/signature.json',
467
-				$path,
468
-				$appId,
469
-				$forceVerify
470
-			);
471
-		} catch (\Exception $e) {
472
-			$result = [
473
-				'EXCEPTION' => [
474
-					'class' => \get_class($e),
475
-					'message' => $e->getMessage(),
476
-				],
477
-			];
478
-		}
479
-		$this->storeResults($appId, $result);
480
-
481
-		return $result;
482
-	}
483
-
484
-	/**
485
-	 * Verify the signature of core. Returns an array with the following content:
486
-	 * [
487
-	 * 	'FILE_MISSING' =>
488
-	 * 	[
489
-	 * 		'filename' => [
490
-	 * 			'expected' => 'expectedSHA512',
491
-	 * 			'current' => 'currentSHA512',
492
-	 * 		],
493
-	 * 	],
494
-	 * 	'EXTRA_FILE' =>
495
-	 * 	[
496
-	 * 		'filename' => [
497
-	 * 			'expected' => 'expectedSHA512',
498
-	 * 			'current' => 'currentSHA512',
499
-	 * 		],
500
-	 * 	],
501
-	 * 	'INVALID_HASH' =>
502
-	 * 	[
503
-	 * 		'filename' => [
504
-	 * 			'expected' => 'expectedSHA512',
505
-	 * 			'current' => 'currentSHA512',
506
-	 * 		],
507
-	 * 	],
508
-	 * ]
509
-	 *
510
-	 * Array may be empty in case no problems have been found.
511
-	 *
512
-	 * @return array
513
-	 */
514
-	public function verifyCoreSignature(): array {
515
-		try {
516
-			$result = $this->verify(
517
-				$this->environmentHelper->getServerRoot() . '/core/signature.json',
518
-				$this->environmentHelper->getServerRoot(),
519
-				'core'
520
-			);
521
-		} catch (\Exception $e) {
522
-			$result = [
523
-				'EXCEPTION' => [
524
-					'class' => \get_class($e),
525
-					'message' => $e->getMessage(),
526
-				],
527
-			];
528
-		}
529
-		$this->storeResults('core', $result);
530
-
531
-		return $result;
532
-	}
533
-
534
-	/**
535
-	 * Verify the core code of the instance as well as all applicable applications
536
-	 * and store the results.
537
-	 */
538
-	public function runInstanceVerification() {
539
-		$this->cleanResults();
540
-		$this->verifyCoreSignature();
541
-		$appIds = $this->appManager->getAllAppsInAppsFolders();
542
-		foreach ($appIds as $appId) {
543
-			// If an application is shipped a valid signature is required
544
-			$isShipped = $this->appManager->isShipped($appId);
545
-			$appNeedsToBeChecked = false;
546
-			if ($isShipped) {
547
-				$appNeedsToBeChecked = true;
548
-			} elseif ($this->fileAccessHelper->file_exists($this->appLocator->getAppPath($appId) . '/appinfo/signature.json')) {
549
-				// Otherwise only if the application explicitly ships a signature.json file
550
-				$appNeedsToBeChecked = true;
551
-			}
552
-
553
-			if ($appNeedsToBeChecked) {
554
-				$this->verifyAppSignature($appId);
555
-			}
556
-		}
557
-	}
39
+    public const CACHE_KEY = 'oc.integritycheck.checker';
40
+
41
+    private ICache $cache;
42
+
43
+    public function __construct(
44
+        private ServerVersion $serverVersion,
45
+        private EnvironmentHelper $environmentHelper,
46
+        private FileAccessHelper $fileAccessHelper,
47
+        private AppLocator $appLocator,
48
+        private ?IConfig $config,
49
+        private ?IAppConfig $appConfig,
50
+        ICacheFactory $cacheFactory,
51
+        private IAppManager $appManager,
52
+        private IMimeTypeDetector $mimeTypeDetector,
53
+    ) {
54
+        $this->cache = $cacheFactory->createDistributed(self::CACHE_KEY);
55
+    }
56
+
57
+    /**
58
+     * Whether code signing is enforced or not.
59
+     *
60
+     * @return bool
61
+     */
62
+    public function isCodeCheckEnforced(): bool {
63
+        $notSignedChannels = [ '', 'git'];
64
+        if (\in_array($this->serverVersion->getChannel(), $notSignedChannels, true)) {
65
+            return false;
66
+        }
67
+
68
+        /**
69
+         * This config option is undocumented and supposed to be so, it's only
70
+         * applicable for very specific scenarios and we should not advertise it
71
+         * too prominent. So please do not add it to config.sample.php.
72
+         */
73
+        return !($this->config?->getSystemValueBool('integrity.check.disabled', false) ?? false);
74
+    }
75
+
76
+    /**
77
+     * Enumerates all files belonging to the folder. Sensible defaults are excluded.
78
+     *
79
+     * @param string $folderToIterate
80
+     * @param string $root
81
+     * @return \RecursiveIteratorIterator
82
+     * @throws \Exception
83
+     */
84
+    private function getFolderIterator(string $folderToIterate, string $root = ''): \RecursiveIteratorIterator {
85
+        $dirItr = new \RecursiveDirectoryIterator(
86
+            $folderToIterate,
87
+            \RecursiveDirectoryIterator::SKIP_DOTS
88
+        );
89
+        if ($root === '') {
90
+            $root = \OC::$SERVERROOT;
91
+        }
92
+        $root = rtrim($root, '/');
93
+
94
+        $excludeGenericFilesIterator = new ExcludeFileByNameFilterIterator($dirItr);
95
+        $excludeFoldersIterator = new ExcludeFoldersByPathFilterIterator($excludeGenericFilesIterator, $root);
96
+
97
+        return new \RecursiveIteratorIterator(
98
+            $excludeFoldersIterator,
99
+            \RecursiveIteratorIterator::SELF_FIRST
100
+        );
101
+    }
102
+
103
+    /**
104
+     * Returns an array of ['filename' => 'SHA512-hash-of-file'] for all files found
105
+     * in the iterator.
106
+     *
107
+     * @param \RecursiveIteratorIterator $iterator
108
+     * @param string $path
109
+     * @return array Array of hashes.
110
+     */
111
+    private function generateHashes(\RecursiveIteratorIterator $iterator,
112
+        string $path): array {
113
+        $hashes = [];
114
+
115
+        $baseDirectoryLength = \strlen($path);
116
+        foreach ($iterator as $filename => $data) {
117
+            /** @var \DirectoryIterator $data */
118
+            if ($data->isDir()) {
119
+                continue;
120
+            }
121
+
122
+            $relativeFileName = substr($filename, $baseDirectoryLength);
123
+            $relativeFileName = ltrim($relativeFileName, '/');
124
+
125
+            // Exclude signature.json files in the appinfo and root folder
126
+            if ($relativeFileName === 'appinfo/signature.json') {
127
+                continue;
128
+            }
129
+            // Exclude signature.json files in the appinfo and core folder
130
+            if ($relativeFileName === 'core/signature.json') {
131
+                continue;
132
+            }
133
+
134
+            // The .htaccess file in the root folder of ownCloud can contain
135
+            // custom content after the installation due to the fact that dynamic
136
+            // content is written into it at installation time as well. This
137
+            // includes for example the 404 and 403 instructions.
138
+            // Thus we ignore everything below the first occurrence of
139
+            // "#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####" and have the
140
+            // hash generated based on this.
141
+            if ($filename === $this->environmentHelper->getServerRoot() . '/.htaccess') {
142
+                $fileContent = file_get_contents($filename);
143
+                $explodedArray = explode('#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####', $fileContent);
144
+                if (\count($explodedArray) === 2) {
145
+                    $hashes[$relativeFileName] = hash('sha512', $explodedArray[0]);
146
+                    continue;
147
+                }
148
+            }
149
+            if ($filename === $this->environmentHelper->getServerRoot() . '/core/js/mimetypelist.js') {
150
+                $oldMimetypeList = new GenerateMimetypeFileBuilder();
151
+                $newFile = $oldMimetypeList->generateFile($this->mimeTypeDetector->getAllAliases(), $this->mimeTypeDetector->getAllNamings());
152
+                $oldFile = $this->fileAccessHelper->file_get_contents($filename);
153
+                if ($newFile === $oldFile) {
154
+                    $hashes[$relativeFileName] = hash('sha512', $oldMimetypeList->generateFile($this->mimeTypeDetector->getOnlyDefaultAliases(), $this->mimeTypeDetector->getAllNamings()));
155
+                    continue;
156
+                }
157
+            }
158
+
159
+            $hashes[$relativeFileName] = hash_file('sha512', $filename);
160
+        }
161
+
162
+        return $hashes;
163
+    }
164
+
165
+    /**
166
+     * Creates the signature data
167
+     *
168
+     * @param array $hashes
169
+     * @param X509 $certificate
170
+     * @param RSA $privateKey
171
+     * @return array
172
+     */
173
+    private function createSignatureData(array $hashes,
174
+        X509 $certificate,
175
+        RSA $privateKey): array {
176
+        ksort($hashes);
177
+
178
+        $privateKey->setSignatureMode(RSA::SIGNATURE_PSS);
179
+        $privateKey->setMGFHash('sha512');
180
+        // See https://tools.ietf.org/html/rfc3447#page-38
181
+        $privateKey->setSaltLength(0);
182
+        $signature = $privateKey->sign(json_encode($hashes));
183
+
184
+        return [
185
+            'hashes' => $hashes,
186
+            'signature' => base64_encode($signature),
187
+            'certificate' => $certificate->saveX509($certificate->currentCert),
188
+        ];
189
+    }
190
+
191
+    /**
192
+     * Write the signature of the app in the specified folder
193
+     *
194
+     * @param string $path
195
+     * @param X509 $certificate
196
+     * @param RSA $privateKey
197
+     * @throws \Exception
198
+     */
199
+    public function writeAppSignature($path,
200
+        X509 $certificate,
201
+        RSA $privateKey) {
202
+        $appInfoDir = $path . '/appinfo';
203
+        try {
204
+            $this->fileAccessHelper->assertDirectoryExists($appInfoDir);
205
+
206
+            $iterator = $this->getFolderIterator($path);
207
+            $hashes = $this->generateHashes($iterator, $path);
208
+            $signature = $this->createSignatureData($hashes, $certificate, $privateKey);
209
+            $this->fileAccessHelper->file_put_contents(
210
+                $appInfoDir . '/signature.json',
211
+                json_encode($signature, JSON_PRETTY_PRINT)
212
+            );
213
+        } catch (\Exception $e) {
214
+            if (!$this->fileAccessHelper->is_writable($appInfoDir)) {
215
+                throw new \Exception($appInfoDir . ' is not writable');
216
+            }
217
+            throw $e;
218
+        }
219
+    }
220
+
221
+    /**
222
+     * Write the signature of core
223
+     *
224
+     * @param X509 $certificate
225
+     * @param RSA $rsa
226
+     * @param string $path
227
+     * @throws \Exception
228
+     */
229
+    public function writeCoreSignature(X509 $certificate,
230
+        RSA $rsa,
231
+        $path) {
232
+        $coreDir = $path . '/core';
233
+        try {
234
+            $this->fileAccessHelper->assertDirectoryExists($coreDir);
235
+            $iterator = $this->getFolderIterator($path, $path);
236
+            $hashes = $this->generateHashes($iterator, $path);
237
+            $signatureData = $this->createSignatureData($hashes, $certificate, $rsa);
238
+            $this->fileAccessHelper->file_put_contents(
239
+                $coreDir . '/signature.json',
240
+                json_encode($signatureData, JSON_PRETTY_PRINT)
241
+            );
242
+        } catch (\Exception $e) {
243
+            if (!$this->fileAccessHelper->is_writable($coreDir)) {
244
+                throw new \Exception($coreDir . ' is not writable');
245
+            }
246
+            throw $e;
247
+        }
248
+    }
249
+
250
+    /**
251
+     * Split the certificate file in individual certs
252
+     *
253
+     * @param string $cert
254
+     * @return string[]
255
+     */
256
+    private function splitCerts(string $cert): array {
257
+        preg_match_all('([\-]{3,}[\S\ ]+?[\-]{3,}[\S\s]+?[\-]{3,}[\S\ ]+?[\-]{3,})', $cert, $matches);
258
+
259
+        return $matches[0];
260
+    }
261
+
262
+    /**
263
+     * Verifies the signature for the specified path.
264
+     *
265
+     * @param string $signaturePath
266
+     * @param string $basePath
267
+     * @param string $certificateCN
268
+     * @param bool $forceVerify
269
+     * @return array
270
+     * @throws InvalidSignatureException
271
+     * @throws \Exception
272
+     */
273
+    private function verify(string $signaturePath, string $basePath, string $certificateCN, bool $forceVerify = false): array {
274
+        if (!$forceVerify && !$this->isCodeCheckEnforced()) {
275
+            return [];
276
+        }
277
+
278
+        $content = $this->fileAccessHelper->file_get_contents($signaturePath);
279
+        $signatureData = null;
280
+
281
+        if (\is_string($content)) {
282
+            $signatureData = json_decode($content, true);
283
+        }
284
+        if (!\is_array($signatureData)) {
285
+            throw new InvalidSignatureException('Signature data not found.');
286
+        }
287
+
288
+        $expectedHashes = $signatureData['hashes'];
289
+        ksort($expectedHashes);
290
+        $signature = base64_decode($signatureData['signature']);
291
+        $certificate = $signatureData['certificate'];
292
+
293
+        // Check if certificate is signed by Nextcloud Root Authority
294
+        $x509 = new \phpseclib\File\X509();
295
+        $rootCertificatePublicKey = $this->fileAccessHelper->file_get_contents($this->environmentHelper->getServerRoot() . '/resources/codesigning/root.crt');
296
+
297
+        $rootCerts = $this->splitCerts($rootCertificatePublicKey);
298
+        foreach ($rootCerts as $rootCert) {
299
+            $x509->loadCA($rootCert);
300
+        }
301
+        $x509->loadX509($certificate);
302
+        if (!$x509->validateSignature()) {
303
+            throw new InvalidSignatureException('Certificate is not valid.');
304
+        }
305
+        // Verify if certificate has proper CN. "core" CN is always trusted.
306
+        if ($x509->getDN(X509::DN_OPENSSL)['CN'] !== $certificateCN && $x509->getDN(X509::DN_OPENSSL)['CN'] !== 'core') {
307
+            throw new InvalidSignatureException(
308
+                sprintf('Certificate is not valid for required scope. (Requested: %s, current: CN=%s)', $certificateCN, $x509->getDN(true)['CN'])
309
+            );
310
+        }
311
+
312
+        // Check if the signature of the files is valid
313
+        $rsa = new \phpseclib\Crypt\RSA();
314
+        $rsa->loadKey($x509->currentCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']);
315
+        $rsa->setSignatureMode(RSA::SIGNATURE_PSS);
316
+        $rsa->setMGFHash('sha512');
317
+        // See https://tools.ietf.org/html/rfc3447#page-38
318
+        $rsa->setSaltLength(0);
319
+        if (!$rsa->verify(json_encode($expectedHashes), $signature)) {
320
+            throw new InvalidSignatureException('Signature could not get verified.');
321
+        }
322
+
323
+        // Fixes for the updater as shipped with ownCloud 9.0.x: The updater is
324
+        // replaced after the code integrity check is performed.
325
+        //
326
+        // Due to this reason we exclude the whole updater/ folder from the code
327
+        // integrity check.
328
+        if ($basePath === $this->environmentHelper->getServerRoot()) {
329
+            foreach ($expectedHashes as $fileName => $hash) {
330
+                if (str_starts_with($fileName, 'updater/')) {
331
+                    unset($expectedHashes[$fileName]);
332
+                }
333
+            }
334
+        }
335
+
336
+        // Compare the list of files which are not identical
337
+        $currentInstanceHashes = $this->generateHashes($this->getFolderIterator($basePath), $basePath);
338
+        $differencesA = array_diff_assoc($expectedHashes, $currentInstanceHashes);
339
+        $differencesB = array_diff_assoc($currentInstanceHashes, $expectedHashes);
340
+        $differences = array_unique(array_merge($differencesA, $differencesB));
341
+        $differenceArray = [];
342
+        foreach ($differences as $filename => $hash) {
343
+            // Check if file should not exist in the new signature table
344
+            if (!array_key_exists($filename, $expectedHashes)) {
345
+                $differenceArray['EXTRA_FILE'][$filename]['expected'] = '';
346
+                $differenceArray['EXTRA_FILE'][$filename]['current'] = $hash;
347
+                continue;
348
+            }
349
+
350
+            // Check if file is missing
351
+            if (!array_key_exists($filename, $currentInstanceHashes)) {
352
+                $differenceArray['FILE_MISSING'][$filename]['expected'] = $expectedHashes[$filename];
353
+                $differenceArray['FILE_MISSING'][$filename]['current'] = '';
354
+                continue;
355
+            }
356
+
357
+            // Check if hash does mismatch
358
+            if ($expectedHashes[$filename] !== $currentInstanceHashes[$filename]) {
359
+                $differenceArray['INVALID_HASH'][$filename]['expected'] = $expectedHashes[$filename];
360
+                $differenceArray['INVALID_HASH'][$filename]['current'] = $currentInstanceHashes[$filename];
361
+                continue;
362
+            }
363
+
364
+            // Should never happen.
365
+            throw new \Exception('Invalid behaviour in file hash comparison experienced. Please report this error to the developers.');
366
+        }
367
+
368
+        return $differenceArray;
369
+    }
370
+
371
+    /**
372
+     * Whether the code integrity check has passed successful or not
373
+     *
374
+     * @return bool
375
+     */
376
+    public function hasPassedCheck(): bool {
377
+        $results = $this->getResults();
378
+        if ($results !== null && empty($results)) {
379
+            return true;
380
+        }
381
+
382
+        return false;
383
+    }
384
+
385
+    /**
386
+     * @return array|null Either the results or null if no results available
387
+     */
388
+    public function getResults(): ?array {
389
+        $cachedResults = $this->cache->get(self::CACHE_KEY);
390
+        if (!\is_null($cachedResults) and $cachedResults !== false) {
391
+            return json_decode($cachedResults, true);
392
+        }
393
+
394
+        if ($this->appConfig?->hasKey('core', self::CACHE_KEY, lazy: true)) {
395
+            return $this->appConfig->getValueArray('core', self::CACHE_KEY, lazy: true);
396
+        }
397
+
398
+        // No results available
399
+        return null;
400
+    }
401
+
402
+    /**
403
+     * Stores the results in the app config as well as cache
404
+     *
405
+     * @param string $scope
406
+     * @param array $result
407
+     */
408
+    private function storeResults(string $scope, array $result) {
409
+        $resultArray = $this->getResults() ?? [];
410
+        unset($resultArray[$scope]);
411
+        if (!empty($result)) {
412
+            $resultArray[$scope] = $result;
413
+        }
414
+        $this->appConfig?->setValueArray('core', self::CACHE_KEY, $resultArray, lazy: true);
415
+        $this->cache->set(self::CACHE_KEY, json_encode($resultArray));
416
+    }
417
+
418
+    /**
419
+     *
420
+     * Clean previous results for a proper rescanning. Otherwise
421
+     */
422
+    private function cleanResults() {
423
+        $this->appConfig->deleteKey('core', self::CACHE_KEY);
424
+        $this->cache->remove(self::CACHE_KEY);
425
+    }
426
+
427
+    /**
428
+     * Verify the signature of $appId. Returns an array with the following content:
429
+     * [
430
+     * 	'FILE_MISSING' =>
431
+     * 	[
432
+     * 		'filename' => [
433
+     * 			'expected' => 'expectedSHA512',
434
+     * 			'current' => 'currentSHA512',
435
+     * 		],
436
+     * 	],
437
+     * 	'EXTRA_FILE' =>
438
+     * 	[
439
+     * 		'filename' => [
440
+     * 			'expected' => 'expectedSHA512',
441
+     * 			'current' => 'currentSHA512',
442
+     * 		],
443
+     * 	],
444
+     * 	'INVALID_HASH' =>
445
+     * 	[
446
+     * 		'filename' => [
447
+     * 			'expected' => 'expectedSHA512',
448
+     * 			'current' => 'currentSHA512',
449
+     * 		],
450
+     * 	],
451
+     * ]
452
+     *
453
+     * Array may be empty in case no problems have been found.
454
+     *
455
+     * @param string $appId
456
+     * @param string $path Optional path. If none is given it will be guessed.
457
+     * @param bool $forceVerify
458
+     * @return array
459
+     */
460
+    public function verifyAppSignature(string $appId, string $path = '', bool $forceVerify = false): array {
461
+        try {
462
+            if ($path === '') {
463
+                $path = $this->appLocator->getAppPath($appId);
464
+            }
465
+            $result = $this->verify(
466
+                $path . '/appinfo/signature.json',
467
+                $path,
468
+                $appId,
469
+                $forceVerify
470
+            );
471
+        } catch (\Exception $e) {
472
+            $result = [
473
+                'EXCEPTION' => [
474
+                    'class' => \get_class($e),
475
+                    'message' => $e->getMessage(),
476
+                ],
477
+            ];
478
+        }
479
+        $this->storeResults($appId, $result);
480
+
481
+        return $result;
482
+    }
483
+
484
+    /**
485
+     * Verify the signature of core. Returns an array with the following content:
486
+     * [
487
+     * 	'FILE_MISSING' =>
488
+     * 	[
489
+     * 		'filename' => [
490
+     * 			'expected' => 'expectedSHA512',
491
+     * 			'current' => 'currentSHA512',
492
+     * 		],
493
+     * 	],
494
+     * 	'EXTRA_FILE' =>
495
+     * 	[
496
+     * 		'filename' => [
497
+     * 			'expected' => 'expectedSHA512',
498
+     * 			'current' => 'currentSHA512',
499
+     * 		],
500
+     * 	],
501
+     * 	'INVALID_HASH' =>
502
+     * 	[
503
+     * 		'filename' => [
504
+     * 			'expected' => 'expectedSHA512',
505
+     * 			'current' => 'currentSHA512',
506
+     * 		],
507
+     * 	],
508
+     * ]
509
+     *
510
+     * Array may be empty in case no problems have been found.
511
+     *
512
+     * @return array
513
+     */
514
+    public function verifyCoreSignature(): array {
515
+        try {
516
+            $result = $this->verify(
517
+                $this->environmentHelper->getServerRoot() . '/core/signature.json',
518
+                $this->environmentHelper->getServerRoot(),
519
+                'core'
520
+            );
521
+        } catch (\Exception $e) {
522
+            $result = [
523
+                'EXCEPTION' => [
524
+                    'class' => \get_class($e),
525
+                    'message' => $e->getMessage(),
526
+                ],
527
+            ];
528
+        }
529
+        $this->storeResults('core', $result);
530
+
531
+        return $result;
532
+    }
533
+
534
+    /**
535
+     * Verify the core code of the instance as well as all applicable applications
536
+     * and store the results.
537
+     */
538
+    public function runInstanceVerification() {
539
+        $this->cleanResults();
540
+        $this->verifyCoreSignature();
541
+        $appIds = $this->appManager->getAllAppsInAppsFolders();
542
+        foreach ($appIds as $appId) {
543
+            // If an application is shipped a valid signature is required
544
+            $isShipped = $this->appManager->isShipped($appId);
545
+            $appNeedsToBeChecked = false;
546
+            if ($isShipped) {
547
+                $appNeedsToBeChecked = true;
548
+            } elseif ($this->fileAccessHelper->file_exists($this->appLocator->getAppPath($appId) . '/appinfo/signature.json')) {
549
+                // Otherwise only if the application explicitly ships a signature.json file
550
+                $appNeedsToBeChecked = true;
551
+            }
552
+
553
+            if ($appNeedsToBeChecked) {
554
+                $this->verifyAppSignature($appId);
555
+            }
556
+        }
557
+    }
558 558
 }
Please login to merge, or discard this patch.
Spacing   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -60,7 +60,7 @@  discard block
 block discarded – undo
60 60
 	 * @return bool
61 61
 	 */
62 62
 	public function isCodeCheckEnforced(): bool {
63
-		$notSignedChannels = [ '', 'git'];
63
+		$notSignedChannels = ['', 'git'];
64 64
 		if (\in_array($this->serverVersion->getChannel(), $notSignedChannels, true)) {
65 65
 			return false;
66 66
 		}
@@ -138,7 +138,7 @@  discard block
 block discarded – undo
138 138
 			// Thus we ignore everything below the first occurrence of
139 139
 			// "#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####" and have the
140 140
 			// hash generated based on this.
141
-			if ($filename === $this->environmentHelper->getServerRoot() . '/.htaccess') {
141
+			if ($filename === $this->environmentHelper->getServerRoot().'/.htaccess') {
142 142
 				$fileContent = file_get_contents($filename);
143 143
 				$explodedArray = explode('#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####', $fileContent);
144 144
 				if (\count($explodedArray) === 2) {
@@ -146,7 +146,7 @@  discard block
 block discarded – undo
146 146
 					continue;
147 147
 				}
148 148
 			}
149
-			if ($filename === $this->environmentHelper->getServerRoot() . '/core/js/mimetypelist.js') {
149
+			if ($filename === $this->environmentHelper->getServerRoot().'/core/js/mimetypelist.js') {
150 150
 				$oldMimetypeList = new GenerateMimetypeFileBuilder();
151 151
 				$newFile = $oldMimetypeList->generateFile($this->mimeTypeDetector->getAllAliases(), $this->mimeTypeDetector->getAllNamings());
152 152
 				$oldFile = $this->fileAccessHelper->file_get_contents($filename);
@@ -199,7 +199,7 @@  discard block
 block discarded – undo
199 199
 	public function writeAppSignature($path,
200 200
 		X509 $certificate,
201 201
 		RSA $privateKey) {
202
-		$appInfoDir = $path . '/appinfo';
202
+		$appInfoDir = $path.'/appinfo';
203 203
 		try {
204 204
 			$this->fileAccessHelper->assertDirectoryExists($appInfoDir);
205 205
 
@@ -207,12 +207,12 @@  discard block
 block discarded – undo
207 207
 			$hashes = $this->generateHashes($iterator, $path);
208 208
 			$signature = $this->createSignatureData($hashes, $certificate, $privateKey);
209 209
 			$this->fileAccessHelper->file_put_contents(
210
-				$appInfoDir . '/signature.json',
210
+				$appInfoDir.'/signature.json',
211 211
 				json_encode($signature, JSON_PRETTY_PRINT)
212 212
 			);
213 213
 		} catch (\Exception $e) {
214 214
 			if (!$this->fileAccessHelper->is_writable($appInfoDir)) {
215
-				throw new \Exception($appInfoDir . ' is not writable');
215
+				throw new \Exception($appInfoDir.' is not writable');
216 216
 			}
217 217
 			throw $e;
218 218
 		}
@@ -229,19 +229,19 @@  discard block
 block discarded – undo
229 229
 	public function writeCoreSignature(X509 $certificate,
230 230
 		RSA $rsa,
231 231
 		$path) {
232
-		$coreDir = $path . '/core';
232
+		$coreDir = $path.'/core';
233 233
 		try {
234 234
 			$this->fileAccessHelper->assertDirectoryExists($coreDir);
235 235
 			$iterator = $this->getFolderIterator($path, $path);
236 236
 			$hashes = $this->generateHashes($iterator, $path);
237 237
 			$signatureData = $this->createSignatureData($hashes, $certificate, $rsa);
238 238
 			$this->fileAccessHelper->file_put_contents(
239
-				$coreDir . '/signature.json',
239
+				$coreDir.'/signature.json',
240 240
 				json_encode($signatureData, JSON_PRETTY_PRINT)
241 241
 			);
242 242
 		} catch (\Exception $e) {
243 243
 			if (!$this->fileAccessHelper->is_writable($coreDir)) {
244
-				throw new \Exception($coreDir . ' is not writable');
244
+				throw new \Exception($coreDir.' is not writable');
245 245
 			}
246 246
 			throw $e;
247 247
 		}
@@ -292,7 +292,7 @@  discard block
 block discarded – undo
292 292
 
293 293
 		// Check if certificate is signed by Nextcloud Root Authority
294 294
 		$x509 = new \phpseclib\File\X509();
295
-		$rootCertificatePublicKey = $this->fileAccessHelper->file_get_contents($this->environmentHelper->getServerRoot() . '/resources/codesigning/root.crt');
295
+		$rootCertificatePublicKey = $this->fileAccessHelper->file_get_contents($this->environmentHelper->getServerRoot().'/resources/codesigning/root.crt');
296 296
 
297 297
 		$rootCerts = $this->splitCerts($rootCertificatePublicKey);
298 298
 		foreach ($rootCerts as $rootCert) {
@@ -463,7 +463,7 @@  discard block
 block discarded – undo
463 463
 				$path = $this->appLocator->getAppPath($appId);
464 464
 			}
465 465
 			$result = $this->verify(
466
-				$path . '/appinfo/signature.json',
466
+				$path.'/appinfo/signature.json',
467 467
 				$path,
468 468
 				$appId,
469 469
 				$forceVerify
@@ -514,7 +514,7 @@  discard block
 block discarded – undo
514 514
 	public function verifyCoreSignature(): array {
515 515
 		try {
516 516
 			$result = $this->verify(
517
-				$this->environmentHelper->getServerRoot() . '/core/signature.json',
517
+				$this->environmentHelper->getServerRoot().'/core/signature.json',
518 518
 				$this->environmentHelper->getServerRoot(),
519 519
 				'core'
520 520
 			);
@@ -545,7 +545,7 @@  discard block
 block discarded – undo
545 545
 			$appNeedsToBeChecked = false;
546 546
 			if ($isShipped) {
547 547
 				$appNeedsToBeChecked = true;
548
-			} elseif ($this->fileAccessHelper->file_exists($this->appLocator->getAppPath($appId) . '/appinfo/signature.json')) {
548
+			} elseif ($this->fileAccessHelper->file_exists($this->appLocator->getAppPath($appId).'/appinfo/signature.json')) {
549 549
 				// Otherwise only if the application explicitly ships a signature.json file
550 550
 				$appNeedsToBeChecked = true;
551 551
 			}
Please login to merge, or discard this patch.
lib/private/Files/Type/Detection.php 2 patches
Indentation   +352 added lines, -352 removed lines patch added patch discarded remove patch
@@ -22,356 +22,356 @@
 block discarded – undo
22 22
  * @package OC\Files\Type
23 23
  */
24 24
 class Detection implements IMimeTypeDetector {
25
-	private const CUSTOM_MIMETYPEMAPPING = 'mimetypemapping.json';
26
-	private const CUSTOM_MIMETYPEALIASES = 'mimetypealiases.json';
27
-	private const CUSTOM_MIMETYPENAMES = 'mimetypenames.json';
28
-
29
-	/** @var array<list{string, string|null}> */
30
-	protected array $mimeTypes = [];
31
-	protected array $secureMimeTypes = [];
32
-
33
-	protected array $mimeTypeIcons = [];
34
-	/** @var array<string,string> */
35
-	protected array $mimeTypeAlias = [];
36
-	/** @var array<string,string> */
37
-	protected array $mimeTypesNames = [];
38
-
39
-	public function __construct(
40
-		private IURLGenerator $urlGenerator,
41
-		private LoggerInterface $logger,
42
-		private string $customConfigDir,
43
-		private string $defaultConfigDir,
44
-	) {
45
-	}
46
-
47
-	/**
48
-	 * Add an extension -> MIME type mapping
49
-	 *
50
-	 * $mimeType is the assumed correct mime type
51
-	 * The optional $secureMimeType is an alternative to send to send
52
-	 * to avoid potential XSS.
53
-	 *
54
-	 * @param string $extension
55
-	 * @param string $mimeType
56
-	 * @param string|null $secureMimeType
57
-	 */
58
-	public function registerType(string $extension,
59
-		string $mimeType,
60
-		?string $secureMimeType = null): void {
61
-		// Make sure the extension is a string
62
-		// https://github.com/nextcloud/server/issues/42902
63
-		$this->mimeTypes[$extension] = [$mimeType, $secureMimeType];
64
-		$this->secureMimeTypes[$mimeType] = $secureMimeType ?? $mimeType;
65
-	}
66
-
67
-	/**
68
-	 * Add an array of extension -> MIME type mappings
69
-	 *
70
-	 * The mimeType value is in itself an array where the first index is
71
-	 * the assumed correct mimeType and the second is either a secure alternative
72
-	 * or null if the correct is considered secure.
73
-	 *
74
-	 * @param array $types
75
-	 */
76
-	public function registerTypeArray(array $types): void {
77
-		// Register the types,
78
-		foreach ($types as $extension => $mimeType) {
79
-			$this->registerType((string)$extension, $mimeType[0], $mimeType[1] ?? null);
80
-		}
81
-
82
-		// Update the alternative mimeTypes to avoid having to look them up each time.
83
-		foreach ($this->mimeTypes as $extension => $mimeType) {
84
-			if (str_starts_with((string)$extension, '_comment')) {
85
-				continue;
86
-			}
87
-
88
-			$this->secureMimeTypes[$mimeType[0]] = $mimeType[1] ?? $mimeType[0];
89
-			if (isset($mimeType[1])) {
90
-				$this->secureMimeTypes[$mimeType[1]] = $mimeType[1];
91
-			}
92
-		}
93
-	}
94
-
95
-	private function loadCustomDefinitions(string $fileName, array $definitions): array {
96
-		if (file_exists($this->customConfigDir . '/' . $fileName)) {
97
-			$custom = json_decode(file_get_contents($this->customConfigDir . '/' . $fileName), true);
98
-			if (json_last_error() === JSON_ERROR_NONE) {
99
-				$definitions = array_merge($definitions, $custom);
100
-			} else {
101
-				$this->logger->warning('Failed to parse ' . $fileName . ': ' . json_last_error_msg());
102
-			}
103
-		}
104
-		return $definitions;
105
-	}
106
-
107
-	/**
108
-	 * Add the MIME type aliases if they are not yet present
109
-	 */
110
-	private function loadAliases(): void {
111
-		if (!empty($this->mimeTypeAlias)) {
112
-			return;
113
-		}
114
-
115
-		$this->mimeTypeAlias = json_decode(file_get_contents($this->defaultConfigDir . '/mimetypealiases.dist.json'), true);
116
-		$this->mimeTypeAlias = $this->loadCustomDefinitions(self::CUSTOM_MIMETYPEALIASES, $this->mimeTypeAlias);
117
-	}
118
-
119
-	/**
120
-	 * @return array<string,string>
121
-	 */
122
-	public function getAllAliases(): array {
123
-		$this->loadAliases();
124
-		return $this->mimeTypeAlias;
125
-	}
126
-
127
-	public function getOnlyDefaultAliases(): array {
128
-		$this->loadMappings();
129
-		$this->mimeTypeAlias = json_decode(file_get_contents($this->defaultConfigDir . '/mimetypealiases.dist.json'), true);
130
-		return $this->mimeTypeAlias;
131
-	}
132
-
133
-	/**
134
-	 * Add MIME type mappings if they are not yet present
135
-	 */
136
-	private function loadMappings(): void {
137
-		if (!empty($this->mimeTypes)) {
138
-			return;
139
-		}
140
-
141
-		$mimeTypeMapping = json_decode(file_get_contents($this->defaultConfigDir . '/mimetypemapping.dist.json'), true);
142
-		$mimeTypeMapping = $this->loadCustomDefinitions(self::CUSTOM_MIMETYPEMAPPING, $mimeTypeMapping);
143
-
144
-		$this->registerTypeArray($mimeTypeMapping);
145
-	}
146
-
147
-	/**
148
-	 * @return array<list{string, string|null}>
149
-	 */
150
-	public function getAllMappings(): array {
151
-		$this->loadMappings();
152
-		return $this->mimeTypes;
153
-	}
154
-
155
-	private function loadNamings(): void {
156
-		if (!empty($this->mimeTypesNames)) {
157
-			return;
158
-		}
159
-
160
-		$mimeTypeMapping = json_decode(file_get_contents($this->defaultConfigDir . '/mimetypenames.dist.json'), true);
161
-		$mimeTypeMapping = $this->loadCustomDefinitions(self::CUSTOM_MIMETYPENAMES, $mimeTypeMapping);
162
-
163
-		$this->mimeTypesNames = $mimeTypeMapping;
164
-	}
165
-
166
-	/**
167
-	 * @return array<string,string>
168
-	 */
169
-	public function getAllNamings(): array {
170
-		$this->loadNamings();
171
-		return $this->mimeTypesNames;
172
-	}
173
-
174
-	/**
175
-	 * detect MIME type only based on filename, content of file is not used
176
-	 *
177
-	 * @param string $path
178
-	 * @return string
179
-	 */
180
-	public function detectPath($path): string {
181
-		$this->loadMappings();
182
-
183
-		$fileName = basename($path);
184
-
185
-		// remove leading dot on hidden files with a file extension
186
-		$fileName = ltrim($fileName, '.');
187
-
188
-		// note: leading dot doesn't qualify as extension
189
-		if (strpos($fileName, '.') > 0) {
190
-			// remove versioning extension: name.v1508946057 and transfer extension: name.ocTransferId2057600214.part
191
-			$fileName = preg_replace('!((\.v\d+)|((\.ocTransferId\d+)?\.part))$!', '', $fileName);
192
-
193
-			//try to guess the type by the file extension
194
-			$extension = strrchr($fileName, '.');
195
-			if ($extension !== false) {
196
-				$extension = strtolower($extension);
197
-				$extension = substr($extension, 1); // remove leading .
198
-				return $this->mimeTypes[$extension][0] ?? 'application/octet-stream';
199
-			}
200
-		}
201
-
202
-		return 'application/octet-stream';
203
-	}
204
-
205
-	/**
206
-	 * Detect MIME type only based on the content of file.
207
-	 *
208
-	 * @param string $path
209
-	 * @return string
210
-	 * @since 18.0.0
211
-	 */
212
-	public function detectContent(string $path): string {
213
-		$this->loadMappings();
214
-
215
-		if (@is_dir($path)) {
216
-			// directories are easy
217
-			return 'httpd/unix-directory';
218
-		}
219
-
220
-		if (function_exists('finfo_open')
221
-			&& function_exists('finfo_file')
222
-			&& $finfo = finfo_open(FILEINFO_MIME)) {
223
-			$info = @finfo_file($finfo, $path);
224
-			finfo_close($finfo);
225
-			if ($info) {
226
-				$info = strtolower($info);
227
-				$mimeType = str_contains($info, ';') ? substr($info, 0, strpos($info, ';')) : $info;
228
-				$mimeType = $this->getSecureMimeType($mimeType);
229
-				if ($mimeType !== 'application/octet-stream') {
230
-					return $mimeType;
231
-				}
232
-			}
233
-		}
234
-
235
-		if (str_starts_with($path, 'file://')) {
236
-			// Is the file wrapped in a stream?
237
-			return 'application/octet-stream';
238
-		}
239
-
240
-		if (function_exists('mime_content_type')) {
241
-			// use mime magic extension if available
242
-			$mimeType = mime_content_type($path);
243
-			if ($mimeType !== false) {
244
-				$mimeType = $this->getSecureMimeType($mimeType);
245
-				if ($mimeType !== 'application/octet-stream') {
246
-					return $mimeType;
247
-				}
248
-			}
249
-		}
250
-
251
-		$binaryFinder = \OCP\Server::get(IBinaryFinder::class);
252
-		$program = $binaryFinder->findBinaryPath('file');
253
-		if ($program !== false) {
254
-			// it looks like we have a 'file' command,
255
-			// lets see if it does have mime support
256
-			$path = escapeshellarg($path);
257
-			$fp = popen("test -f $path && $program -b --mime-type $path", 'r');
258
-			if ($fp !== false) {
259
-				$mimeType = fgets($fp);
260
-				pclose($fp);
261
-				if ($mimeType !== false) {
262
-					//trim the newline
263
-					$mimeType = trim($mimeType);
264
-					$mimeType = $this->getSecureMimeType($mimeType);
265
-					return $mimeType;
266
-				}
267
-			}
268
-		}
269
-
270
-		return 'application/octet-stream';
271
-	}
272
-
273
-	/**
274
-	 * Detect MIME type based on both filename and content
275
-	 *
276
-	 * @param string $path
277
-	 * @return string
278
-	 */
279
-	public function detect($path): string {
280
-		$mimeType = $this->detectPath($path);
281
-
282
-		if ($mimeType !== 'application/octet-stream') {
283
-			return $mimeType;
284
-		}
285
-
286
-		return $this->detectContent($path);
287
-	}
288
-
289
-	/**
290
-	 * Detect MIME type based on the content of a string
291
-	 *
292
-	 * @param string $data
293
-	 * @return string
294
-	 */
295
-	public function detectString($data): string {
296
-		if (function_exists('finfo_open') && function_exists('finfo_file')) {
297
-			$finfo = finfo_open(FILEINFO_MIME);
298
-			$info = finfo_buffer($finfo, $data);
299
-			return str_contains($info, ';') ? substr($info, 0, strpos($info, ';')) : $info;
300
-		}
301
-
302
-		$tmpFile = \OCP\Server::get(ITempManager::class)->getTemporaryFile();
303
-		$fh = fopen($tmpFile, 'wb');
304
-		fwrite($fh, $data, 8024);
305
-		fclose($fh);
306
-		$mime = $this->detect($tmpFile);
307
-		unset($tmpFile);
308
-		return $mime;
309
-	}
310
-
311
-	/**
312
-	 * Get a secure MIME type that won't expose potential XSS.
313
-	 *
314
-	 * @param string $mimeType
315
-	 * @return string
316
-	 */
317
-	public function getSecureMimeType($mimeType): string {
318
-		$this->loadMappings();
319
-
320
-		return $this->secureMimeTypes[$mimeType] ?? 'application/octet-stream';
321
-	}
322
-
323
-	/**
324
-	 * Get path to the icon of a file type
325
-	 * @param string $mimeType the MIME type
326
-	 * @return string the url
327
-	 */
328
-	public function mimeTypeIcon($mimeType): string {
329
-		$this->loadAliases();
330
-
331
-		while (isset($this->mimeTypeAlias[$mimeType])) {
332
-			$mimeType = $this->mimeTypeAlias[$mimeType];
333
-		}
334
-		if (isset($this->mimeTypeIcons[$mimeType])) {
335
-			return $this->mimeTypeIcons[$mimeType];
336
-		}
337
-
338
-		// Replace slash and backslash with a minus
339
-		$icon = str_replace(['/', '\\'], '-', $mimeType);
340
-
341
-		// Is it a dir?
342
-		if ($mimeType === 'dir') {
343
-			$this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/folder.svg');
344
-			return $this->mimeTypeIcons[$mimeType];
345
-		}
346
-		if ($mimeType === 'dir-shared') {
347
-			$this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/folder-shared.svg');
348
-			return $this->mimeTypeIcons[$mimeType];
349
-		}
350
-		if ($mimeType === 'dir-external') {
351
-			$this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/folder-external.svg');
352
-			return $this->mimeTypeIcons[$mimeType];
353
-		}
354
-
355
-		// Icon exists?
356
-		try {
357
-			$this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/' . $icon . '.svg');
358
-			return $this->mimeTypeIcons[$mimeType];
359
-		} catch (\RuntimeException $e) {
360
-			// Specified image not found
361
-		}
362
-
363
-		// Try only the first part of the filetype
364
-		if (strpos($icon, '-')) {
365
-			$mimePart = substr($icon, 0, strpos($icon, '-'));
366
-			try {
367
-				$this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/' . $mimePart . '.svg');
368
-				return $this->mimeTypeIcons[$mimeType];
369
-			} catch (\RuntimeException $e) {
370
-				// Image for the first part of the MIME type not found
371
-			}
372
-		}
373
-
374
-		$this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/file.svg');
375
-		return $this->mimeTypeIcons[$mimeType];
376
-	}
25
+    private const CUSTOM_MIMETYPEMAPPING = 'mimetypemapping.json';
26
+    private const CUSTOM_MIMETYPEALIASES = 'mimetypealiases.json';
27
+    private const CUSTOM_MIMETYPENAMES = 'mimetypenames.json';
28
+
29
+    /** @var array<list{string, string|null}> */
30
+    protected array $mimeTypes = [];
31
+    protected array $secureMimeTypes = [];
32
+
33
+    protected array $mimeTypeIcons = [];
34
+    /** @var array<string,string> */
35
+    protected array $mimeTypeAlias = [];
36
+    /** @var array<string,string> */
37
+    protected array $mimeTypesNames = [];
38
+
39
+    public function __construct(
40
+        private IURLGenerator $urlGenerator,
41
+        private LoggerInterface $logger,
42
+        private string $customConfigDir,
43
+        private string $defaultConfigDir,
44
+    ) {
45
+    }
46
+
47
+    /**
48
+     * Add an extension -> MIME type mapping
49
+     *
50
+     * $mimeType is the assumed correct mime type
51
+     * The optional $secureMimeType is an alternative to send to send
52
+     * to avoid potential XSS.
53
+     *
54
+     * @param string $extension
55
+     * @param string $mimeType
56
+     * @param string|null $secureMimeType
57
+     */
58
+    public function registerType(string $extension,
59
+        string $mimeType,
60
+        ?string $secureMimeType = null): void {
61
+        // Make sure the extension is a string
62
+        // https://github.com/nextcloud/server/issues/42902
63
+        $this->mimeTypes[$extension] = [$mimeType, $secureMimeType];
64
+        $this->secureMimeTypes[$mimeType] = $secureMimeType ?? $mimeType;
65
+    }
66
+
67
+    /**
68
+     * Add an array of extension -> MIME type mappings
69
+     *
70
+     * The mimeType value is in itself an array where the first index is
71
+     * the assumed correct mimeType and the second is either a secure alternative
72
+     * or null if the correct is considered secure.
73
+     *
74
+     * @param array $types
75
+     */
76
+    public function registerTypeArray(array $types): void {
77
+        // Register the types,
78
+        foreach ($types as $extension => $mimeType) {
79
+            $this->registerType((string)$extension, $mimeType[0], $mimeType[1] ?? null);
80
+        }
81
+
82
+        // Update the alternative mimeTypes to avoid having to look them up each time.
83
+        foreach ($this->mimeTypes as $extension => $mimeType) {
84
+            if (str_starts_with((string)$extension, '_comment')) {
85
+                continue;
86
+            }
87
+
88
+            $this->secureMimeTypes[$mimeType[0]] = $mimeType[1] ?? $mimeType[0];
89
+            if (isset($mimeType[1])) {
90
+                $this->secureMimeTypes[$mimeType[1]] = $mimeType[1];
91
+            }
92
+        }
93
+    }
94
+
95
+    private function loadCustomDefinitions(string $fileName, array $definitions): array {
96
+        if (file_exists($this->customConfigDir . '/' . $fileName)) {
97
+            $custom = json_decode(file_get_contents($this->customConfigDir . '/' . $fileName), true);
98
+            if (json_last_error() === JSON_ERROR_NONE) {
99
+                $definitions = array_merge($definitions, $custom);
100
+            } else {
101
+                $this->logger->warning('Failed to parse ' . $fileName . ': ' . json_last_error_msg());
102
+            }
103
+        }
104
+        return $definitions;
105
+    }
106
+
107
+    /**
108
+     * Add the MIME type aliases if they are not yet present
109
+     */
110
+    private function loadAliases(): void {
111
+        if (!empty($this->mimeTypeAlias)) {
112
+            return;
113
+        }
114
+
115
+        $this->mimeTypeAlias = json_decode(file_get_contents($this->defaultConfigDir . '/mimetypealiases.dist.json'), true);
116
+        $this->mimeTypeAlias = $this->loadCustomDefinitions(self::CUSTOM_MIMETYPEALIASES, $this->mimeTypeAlias);
117
+    }
118
+
119
+    /**
120
+     * @return array<string,string>
121
+     */
122
+    public function getAllAliases(): array {
123
+        $this->loadAliases();
124
+        return $this->mimeTypeAlias;
125
+    }
126
+
127
+    public function getOnlyDefaultAliases(): array {
128
+        $this->loadMappings();
129
+        $this->mimeTypeAlias = json_decode(file_get_contents($this->defaultConfigDir . '/mimetypealiases.dist.json'), true);
130
+        return $this->mimeTypeAlias;
131
+    }
132
+
133
+    /**
134
+     * Add MIME type mappings if they are not yet present
135
+     */
136
+    private function loadMappings(): void {
137
+        if (!empty($this->mimeTypes)) {
138
+            return;
139
+        }
140
+
141
+        $mimeTypeMapping = json_decode(file_get_contents($this->defaultConfigDir . '/mimetypemapping.dist.json'), true);
142
+        $mimeTypeMapping = $this->loadCustomDefinitions(self::CUSTOM_MIMETYPEMAPPING, $mimeTypeMapping);
143
+
144
+        $this->registerTypeArray($mimeTypeMapping);
145
+    }
146
+
147
+    /**
148
+     * @return array<list{string, string|null}>
149
+     */
150
+    public function getAllMappings(): array {
151
+        $this->loadMappings();
152
+        return $this->mimeTypes;
153
+    }
154
+
155
+    private function loadNamings(): void {
156
+        if (!empty($this->mimeTypesNames)) {
157
+            return;
158
+        }
159
+
160
+        $mimeTypeMapping = json_decode(file_get_contents($this->defaultConfigDir . '/mimetypenames.dist.json'), true);
161
+        $mimeTypeMapping = $this->loadCustomDefinitions(self::CUSTOM_MIMETYPENAMES, $mimeTypeMapping);
162
+
163
+        $this->mimeTypesNames = $mimeTypeMapping;
164
+    }
165
+
166
+    /**
167
+     * @return array<string,string>
168
+     */
169
+    public function getAllNamings(): array {
170
+        $this->loadNamings();
171
+        return $this->mimeTypesNames;
172
+    }
173
+
174
+    /**
175
+     * detect MIME type only based on filename, content of file is not used
176
+     *
177
+     * @param string $path
178
+     * @return string
179
+     */
180
+    public function detectPath($path): string {
181
+        $this->loadMappings();
182
+
183
+        $fileName = basename($path);
184
+
185
+        // remove leading dot on hidden files with a file extension
186
+        $fileName = ltrim($fileName, '.');
187
+
188
+        // note: leading dot doesn't qualify as extension
189
+        if (strpos($fileName, '.') > 0) {
190
+            // remove versioning extension: name.v1508946057 and transfer extension: name.ocTransferId2057600214.part
191
+            $fileName = preg_replace('!((\.v\d+)|((\.ocTransferId\d+)?\.part))$!', '', $fileName);
192
+
193
+            //try to guess the type by the file extension
194
+            $extension = strrchr($fileName, '.');
195
+            if ($extension !== false) {
196
+                $extension = strtolower($extension);
197
+                $extension = substr($extension, 1); // remove leading .
198
+                return $this->mimeTypes[$extension][0] ?? 'application/octet-stream';
199
+            }
200
+        }
201
+
202
+        return 'application/octet-stream';
203
+    }
204
+
205
+    /**
206
+     * Detect MIME type only based on the content of file.
207
+     *
208
+     * @param string $path
209
+     * @return string
210
+     * @since 18.0.0
211
+     */
212
+    public function detectContent(string $path): string {
213
+        $this->loadMappings();
214
+
215
+        if (@is_dir($path)) {
216
+            // directories are easy
217
+            return 'httpd/unix-directory';
218
+        }
219
+
220
+        if (function_exists('finfo_open')
221
+            && function_exists('finfo_file')
222
+            && $finfo = finfo_open(FILEINFO_MIME)) {
223
+            $info = @finfo_file($finfo, $path);
224
+            finfo_close($finfo);
225
+            if ($info) {
226
+                $info = strtolower($info);
227
+                $mimeType = str_contains($info, ';') ? substr($info, 0, strpos($info, ';')) : $info;
228
+                $mimeType = $this->getSecureMimeType($mimeType);
229
+                if ($mimeType !== 'application/octet-stream') {
230
+                    return $mimeType;
231
+                }
232
+            }
233
+        }
234
+
235
+        if (str_starts_with($path, 'file://')) {
236
+            // Is the file wrapped in a stream?
237
+            return 'application/octet-stream';
238
+        }
239
+
240
+        if (function_exists('mime_content_type')) {
241
+            // use mime magic extension if available
242
+            $mimeType = mime_content_type($path);
243
+            if ($mimeType !== false) {
244
+                $mimeType = $this->getSecureMimeType($mimeType);
245
+                if ($mimeType !== 'application/octet-stream') {
246
+                    return $mimeType;
247
+                }
248
+            }
249
+        }
250
+
251
+        $binaryFinder = \OCP\Server::get(IBinaryFinder::class);
252
+        $program = $binaryFinder->findBinaryPath('file');
253
+        if ($program !== false) {
254
+            // it looks like we have a 'file' command,
255
+            // lets see if it does have mime support
256
+            $path = escapeshellarg($path);
257
+            $fp = popen("test -f $path && $program -b --mime-type $path", 'r');
258
+            if ($fp !== false) {
259
+                $mimeType = fgets($fp);
260
+                pclose($fp);
261
+                if ($mimeType !== false) {
262
+                    //trim the newline
263
+                    $mimeType = trim($mimeType);
264
+                    $mimeType = $this->getSecureMimeType($mimeType);
265
+                    return $mimeType;
266
+                }
267
+            }
268
+        }
269
+
270
+        return 'application/octet-stream';
271
+    }
272
+
273
+    /**
274
+     * Detect MIME type based on both filename and content
275
+     *
276
+     * @param string $path
277
+     * @return string
278
+     */
279
+    public function detect($path): string {
280
+        $mimeType = $this->detectPath($path);
281
+
282
+        if ($mimeType !== 'application/octet-stream') {
283
+            return $mimeType;
284
+        }
285
+
286
+        return $this->detectContent($path);
287
+    }
288
+
289
+    /**
290
+     * Detect MIME type based on the content of a string
291
+     *
292
+     * @param string $data
293
+     * @return string
294
+     */
295
+    public function detectString($data): string {
296
+        if (function_exists('finfo_open') && function_exists('finfo_file')) {
297
+            $finfo = finfo_open(FILEINFO_MIME);
298
+            $info = finfo_buffer($finfo, $data);
299
+            return str_contains($info, ';') ? substr($info, 0, strpos($info, ';')) : $info;
300
+        }
301
+
302
+        $tmpFile = \OCP\Server::get(ITempManager::class)->getTemporaryFile();
303
+        $fh = fopen($tmpFile, 'wb');
304
+        fwrite($fh, $data, 8024);
305
+        fclose($fh);
306
+        $mime = $this->detect($tmpFile);
307
+        unset($tmpFile);
308
+        return $mime;
309
+    }
310
+
311
+    /**
312
+     * Get a secure MIME type that won't expose potential XSS.
313
+     *
314
+     * @param string $mimeType
315
+     * @return string
316
+     */
317
+    public function getSecureMimeType($mimeType): string {
318
+        $this->loadMappings();
319
+
320
+        return $this->secureMimeTypes[$mimeType] ?? 'application/octet-stream';
321
+    }
322
+
323
+    /**
324
+     * Get path to the icon of a file type
325
+     * @param string $mimeType the MIME type
326
+     * @return string the url
327
+     */
328
+    public function mimeTypeIcon($mimeType): string {
329
+        $this->loadAliases();
330
+
331
+        while (isset($this->mimeTypeAlias[$mimeType])) {
332
+            $mimeType = $this->mimeTypeAlias[$mimeType];
333
+        }
334
+        if (isset($this->mimeTypeIcons[$mimeType])) {
335
+            return $this->mimeTypeIcons[$mimeType];
336
+        }
337
+
338
+        // Replace slash and backslash with a minus
339
+        $icon = str_replace(['/', '\\'], '-', $mimeType);
340
+
341
+        // Is it a dir?
342
+        if ($mimeType === 'dir') {
343
+            $this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/folder.svg');
344
+            return $this->mimeTypeIcons[$mimeType];
345
+        }
346
+        if ($mimeType === 'dir-shared') {
347
+            $this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/folder-shared.svg');
348
+            return $this->mimeTypeIcons[$mimeType];
349
+        }
350
+        if ($mimeType === 'dir-external') {
351
+            $this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/folder-external.svg');
352
+            return $this->mimeTypeIcons[$mimeType];
353
+        }
354
+
355
+        // Icon exists?
356
+        try {
357
+            $this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/' . $icon . '.svg');
358
+            return $this->mimeTypeIcons[$mimeType];
359
+        } catch (\RuntimeException $e) {
360
+            // Specified image not found
361
+        }
362
+
363
+        // Try only the first part of the filetype
364
+        if (strpos($icon, '-')) {
365
+            $mimePart = substr($icon, 0, strpos($icon, '-'));
366
+            try {
367
+                $this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/' . $mimePart . '.svg');
368
+                return $this->mimeTypeIcons[$mimeType];
369
+            } catch (\RuntimeException $e) {
370
+                // Image for the first part of the MIME type not found
371
+            }
372
+        }
373
+
374
+        $this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/file.svg');
375
+        return $this->mimeTypeIcons[$mimeType];
376
+    }
377 377
 }
Please login to merge, or discard this patch.
Spacing   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -76,12 +76,12 @@  discard block
 block discarded – undo
76 76
 	public function registerTypeArray(array $types): void {
77 77
 		// Register the types,
78 78
 		foreach ($types as $extension => $mimeType) {
79
-			$this->registerType((string)$extension, $mimeType[0], $mimeType[1] ?? null);
79
+			$this->registerType((string) $extension, $mimeType[0], $mimeType[1] ?? null);
80 80
 		}
81 81
 
82 82
 		// Update the alternative mimeTypes to avoid having to look them up each time.
83 83
 		foreach ($this->mimeTypes as $extension => $mimeType) {
84
-			if (str_starts_with((string)$extension, '_comment')) {
84
+			if (str_starts_with((string) $extension, '_comment')) {
85 85
 				continue;
86 86
 			}
87 87
 
@@ -93,12 +93,12 @@  discard block
 block discarded – undo
93 93
 	}
94 94
 
95 95
 	private function loadCustomDefinitions(string $fileName, array $definitions): array {
96
-		if (file_exists($this->customConfigDir . '/' . $fileName)) {
97
-			$custom = json_decode(file_get_contents($this->customConfigDir . '/' . $fileName), true);
96
+		if (file_exists($this->customConfigDir.'/'.$fileName)) {
97
+			$custom = json_decode(file_get_contents($this->customConfigDir.'/'.$fileName), true);
98 98
 			if (json_last_error() === JSON_ERROR_NONE) {
99 99
 				$definitions = array_merge($definitions, $custom);
100 100
 			} else {
101
-				$this->logger->warning('Failed to parse ' . $fileName . ': ' . json_last_error_msg());
101
+				$this->logger->warning('Failed to parse '.$fileName.': '.json_last_error_msg());
102 102
 			}
103 103
 		}
104 104
 		return $definitions;
@@ -112,7 +112,7 @@  discard block
 block discarded – undo
112 112
 			return;
113 113
 		}
114 114
 
115
-		$this->mimeTypeAlias = json_decode(file_get_contents($this->defaultConfigDir . '/mimetypealiases.dist.json'), true);
115
+		$this->mimeTypeAlias = json_decode(file_get_contents($this->defaultConfigDir.'/mimetypealiases.dist.json'), true);
116 116
 		$this->mimeTypeAlias = $this->loadCustomDefinitions(self::CUSTOM_MIMETYPEALIASES, $this->mimeTypeAlias);
117 117
 	}
118 118
 
@@ -126,7 +126,7 @@  discard block
 block discarded – undo
126 126
 
127 127
 	public function getOnlyDefaultAliases(): array {
128 128
 		$this->loadMappings();
129
-		$this->mimeTypeAlias = json_decode(file_get_contents($this->defaultConfigDir . '/mimetypealiases.dist.json'), true);
129
+		$this->mimeTypeAlias = json_decode(file_get_contents($this->defaultConfigDir.'/mimetypealiases.dist.json'), true);
130 130
 		return $this->mimeTypeAlias;
131 131
 	}
132 132
 
@@ -138,7 +138,7 @@  discard block
 block discarded – undo
138 138
 			return;
139 139
 		}
140 140
 
141
-		$mimeTypeMapping = json_decode(file_get_contents($this->defaultConfigDir . '/mimetypemapping.dist.json'), true);
141
+		$mimeTypeMapping = json_decode(file_get_contents($this->defaultConfigDir.'/mimetypemapping.dist.json'), true);
142 142
 		$mimeTypeMapping = $this->loadCustomDefinitions(self::CUSTOM_MIMETYPEMAPPING, $mimeTypeMapping);
143 143
 
144 144
 		$this->registerTypeArray($mimeTypeMapping);
@@ -157,7 +157,7 @@  discard block
 block discarded – undo
157 157
 			return;
158 158
 		}
159 159
 
160
-		$mimeTypeMapping = json_decode(file_get_contents($this->defaultConfigDir . '/mimetypenames.dist.json'), true);
160
+		$mimeTypeMapping = json_decode(file_get_contents($this->defaultConfigDir.'/mimetypenames.dist.json'), true);
161 161
 		$mimeTypeMapping = $this->loadCustomDefinitions(self::CUSTOM_MIMETYPENAMES, $mimeTypeMapping);
162 162
 
163 163
 		$this->mimeTypesNames = $mimeTypeMapping;
@@ -354,7 +354,7 @@  discard block
 block discarded – undo
354 354
 
355 355
 		// Icon exists?
356 356
 		try {
357
-			$this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/' . $icon . '.svg');
357
+			$this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/'.$icon.'.svg');
358 358
 			return $this->mimeTypeIcons[$mimeType];
359 359
 		} catch (\RuntimeException $e) {
360 360
 			// Specified image not found
@@ -364,7 +364,7 @@  discard block
 block discarded – undo
364 364
 		if (strpos($icon, '-')) {
365 365
 			$mimePart = substr($icon, 0, strpos($icon, '-'));
366 366
 			try {
367
-				$this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/' . $mimePart . '.svg');
367
+				$this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/'.$mimePart.'.svg');
368 368
 				return $this->mimeTypeIcons[$mimeType];
369 369
 			} catch (\RuntimeException $e) {
370 370
 				// Image for the first part of the MIME type not found
Please login to merge, or discard this patch.
core/Command/Maintenance/Mimetype/GenerateMimetypeFileBuilder.php 2 patches
Indentation   +65 added lines, -65 removed lines patch added patch discarded remove patch
@@ -9,79 +9,79 @@  discard block
 block discarded – undo
9 9
 namespace OC\Core\Command\Maintenance\Mimetype;
10 10
 
11 11
 class GenerateMimetypeFileBuilder {
12
-	/**
13
-	 * Generate mime type list file
14
-	 *
15
-	 * @param array<string,string> $aliases
16
-	 * @return string
17
-	 */
18
-	public function generateFile(array $aliases, array $names): string {
19
-		// Remove comments
20
-		$aliases = array_filter($aliases, static function ($key) {
21
-			// Single digit extensions will be treated as integers
22
-			// Let's make sure they are strings
23
-			// https://github.com/nextcloud/server/issues/42902
24
-			$key = (string)$key;
25
-			return !($key === '' || $key[0] === '_');
26
-		}, ARRAY_FILTER_USE_KEY);
12
+    /**
13
+     * Generate mime type list file
14
+     *
15
+     * @param array<string,string> $aliases
16
+     * @return string
17
+     */
18
+    public function generateFile(array $aliases, array $names): string {
19
+        // Remove comments
20
+        $aliases = array_filter($aliases, static function ($key) {
21
+            // Single digit extensions will be treated as integers
22
+            // Let's make sure they are strings
23
+            // https://github.com/nextcloud/server/issues/42902
24
+            $key = (string)$key;
25
+            return !($key === '' || $key[0] === '_');
26
+        }, ARRAY_FILTER_USE_KEY);
27 27
 
28
-		// Fetch all files
29
-		$dir = new \DirectoryIterator(\OC::$SERVERROOT . '/core/img/filetypes');
28
+        // Fetch all files
29
+        $dir = new \DirectoryIterator(\OC::$SERVERROOT . '/core/img/filetypes');
30 30
 
31
-		$files = [];
32
-		foreach ($dir as $fileInfo) {
33
-			if ($fileInfo->isFile()) {
34
-				$file = preg_replace('/.[^.]*$/', '', $fileInfo->getFilename());
35
-				$files[] = $file;
36
-			}
37
-		}
31
+        $files = [];
32
+        foreach ($dir as $fileInfo) {
33
+            if ($fileInfo->isFile()) {
34
+                $file = preg_replace('/.[^.]*$/', '', $fileInfo->getFilename());
35
+                $files[] = $file;
36
+            }
37
+        }
38 38
 
39
-		//Remove duplicates
40
-		$files = array_values(array_unique($files));
41
-		sort($files);
39
+        //Remove duplicates
40
+        $files = array_values(array_unique($files));
41
+        sort($files);
42 42
 
43
-		// Fetch all themes!
44
-		$themes = [];
45
-		$dirs = new \DirectoryIterator(\OC::$SERVERROOT . '/themes/');
46
-		foreach ($dirs as $dir) {
47
-			//Valid theme dir
48
-			if ($dir->isFile() || $dir->isDot()) {
49
-				continue;
50
-			}
43
+        // Fetch all themes!
44
+        $themes = [];
45
+        $dirs = new \DirectoryIterator(\OC::$SERVERROOT . '/themes/');
46
+        foreach ($dirs as $dir) {
47
+            //Valid theme dir
48
+            if ($dir->isFile() || $dir->isDot()) {
49
+                continue;
50
+            }
51 51
 
52
-			$theme = $dir->getFilename();
53
-			$themeDir = $dir->getPath() . '/' . $theme . '/core/img/filetypes/';
54
-			// Check if this theme has its own filetype icons
55
-			if (!file_exists($themeDir)) {
56
-				continue;
57
-			}
52
+            $theme = $dir->getFilename();
53
+            $themeDir = $dir->getPath() . '/' . $theme . '/core/img/filetypes/';
54
+            // Check if this theme has its own filetype icons
55
+            if (!file_exists($themeDir)) {
56
+                continue;
57
+            }
58 58
 
59
-			$themes[$theme] = [];
60
-			// Fetch all the theme icons!
61
-			$themeIt = new \DirectoryIterator($themeDir);
62
-			foreach ($themeIt as $fileInfo) {
63
-				if ($fileInfo->isFile()) {
64
-					$file = preg_replace('/.[^.]*$/', '', $fileInfo->getFilename());
65
-					$themes[$theme][] = $file;
66
-				}
67
-			}
59
+            $themes[$theme] = [];
60
+            // Fetch all the theme icons!
61
+            $themeIt = new \DirectoryIterator($themeDir);
62
+            foreach ($themeIt as $fileInfo) {
63
+                if ($fileInfo->isFile()) {
64
+                    $file = preg_replace('/.[^.]*$/', '', $fileInfo->getFilename());
65
+                    $themes[$theme][] = $file;
66
+                }
67
+            }
68 68
 
69
-			//Remove Duplicates
70
-			$themes[$theme] = array_values(array_unique($themes[$theme]));
71
-			sort($themes[$theme]);
72
-		}
69
+            //Remove Duplicates
70
+            $themes[$theme] = array_values(array_unique($themes[$theme]));
71
+            sort($themes[$theme]);
72
+        }
73 73
 
74
-		$namesOutput = '';
75
-		foreach ($names as $key => $name) {
76
-			if (str_starts_with($key, '_')) {
77
-				// Skip internal names
78
-				continue;
79
-			}
80
-			$namesOutput .= "'$key': t('core', " . json_encode($name) . "),\n";
81
-		}
74
+        $namesOutput = '';
75
+        foreach ($names as $key => $name) {
76
+            if (str_starts_with($key, '_')) {
77
+                // Skip internal names
78
+                continue;
79
+            }
80
+            $namesOutput .= "'$key': t('core', " . json_encode($name) . "),\n";
81
+        }
82 82
 
83
-		//Generate the JS
84
-		return '/**
83
+        //Generate the JS
84
+        return '/**
85 85
 * This file is automatically generated
86 86
 * DO NOT EDIT MANUALLY!
87 87
 *
@@ -96,5 +96,5 @@  discard block
 block discarded – undo
96 96
 	names: {' . $namesOutput . '},
97 97
 };
98 98
 ';
99
-	}
99
+    }
100 100
 }
Please login to merge, or discard this patch.
Spacing   +10 added lines, -10 removed lines patch added patch discarded remove patch
@@ -17,16 +17,16 @@  discard block
 block discarded – undo
17 17
 	 */
18 18
 	public function generateFile(array $aliases, array $names): string {
19 19
 		// Remove comments
20
-		$aliases = array_filter($aliases, static function ($key) {
20
+		$aliases = array_filter($aliases, static function($key) {
21 21
 			// Single digit extensions will be treated as integers
22 22
 			// Let's make sure they are strings
23 23
 			// https://github.com/nextcloud/server/issues/42902
24
-			$key = (string)$key;
24
+			$key = (string) $key;
25 25
 			return !($key === '' || $key[0] === '_');
26 26
 		}, ARRAY_FILTER_USE_KEY);
27 27
 
28 28
 		// Fetch all files
29
-		$dir = new \DirectoryIterator(\OC::$SERVERROOT . '/core/img/filetypes');
29
+		$dir = new \DirectoryIterator(\OC::$SERVERROOT.'/core/img/filetypes');
30 30
 
31 31
 		$files = [];
32 32
 		foreach ($dir as $fileInfo) {
@@ -42,7 +42,7 @@  discard block
 block discarded – undo
42 42
 
43 43
 		// Fetch all themes!
44 44
 		$themes = [];
45
-		$dirs = new \DirectoryIterator(\OC::$SERVERROOT . '/themes/');
45
+		$dirs = new \DirectoryIterator(\OC::$SERVERROOT.'/themes/');
46 46
 		foreach ($dirs as $dir) {
47 47
 			//Valid theme dir
48 48
 			if ($dir->isFile() || $dir->isDot()) {
@@ -50,7 +50,7 @@  discard block
 block discarded – undo
50 50
 			}
51 51
 
52 52
 			$theme = $dir->getFilename();
53
-			$themeDir = $dir->getPath() . '/' . $theme . '/core/img/filetypes/';
53
+			$themeDir = $dir->getPath().'/'.$theme.'/core/img/filetypes/';
54 54
 			// Check if this theme has its own filetype icons
55 55
 			if (!file_exists($themeDir)) {
56 56
 				continue;
@@ -77,7 +77,7 @@  discard block
 block discarded – undo
77 77
 				// Skip internal names
78 78
 				continue;
79 79
 			}
80
-			$namesOutput .= "'$key': t('core', " . json_encode($name) . "),\n";
80
+			$namesOutput .= "'$key': t('core', ".json_encode($name)."),\n";
81 81
 		}
82 82
 
83 83
 		//Generate the JS
@@ -90,10 +90,10 @@  discard block
 block discarded – undo
90 90
 * To regenerate this file run ./occ maintenance:mimetype:update-js
91 91
 */
92 92
 OC.MimeTypeList={
93
-	aliases: ' . json_encode($aliases, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . ',
94
-	files: ' . json_encode($files, JSON_PRETTY_PRINT) . ',
95
-	themes: ' . json_encode($themes, JSON_PRETTY_PRINT) . ',
96
-	names: {' . $namesOutput . '},
93
+	aliases: ' . json_encode($aliases, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES).',
94
+	files: ' . json_encode($files, JSON_PRETTY_PRINT).',
95
+	themes: ' . json_encode($themes, JSON_PRETTY_PRINT).',
96
+	names: {' . $namesOutput.'},
97 97
 };
98 98
 ';
99 99
 	}
Please login to merge, or discard this patch.
core/Command/Maintenance/Mimetype/UpdateJS.php 2 patches
Indentation   +24 added lines, -24 removed lines patch added patch discarded remove patch
@@ -14,28 +14,28 @@
 block discarded – undo
14 14
 use Symfony\Component\Console\Output\OutputInterface;
15 15
 
16 16
 class UpdateJS extends Command {
17
-	public function __construct(
18
-		protected IMimeTypeDetector $mimetypeDetector,
19
-	) {
20
-		parent::__construct();
21
-	}
22
-
23
-	protected function configure() {
24
-		$this
25
-			->setName('maintenance:mimetype:update-js')
26
-			->setDescription('Update mimetypelist.js');
27
-	}
28
-
29
-	protected function execute(InputInterface $input, OutputInterface $output): int {
30
-		// Fetch all the aliases
31
-		$aliases = $this->mimetypeDetector->getAllAliases();
32
-
33
-		// Output the JS
34
-		$generatedMimetypeFile = new GenerateMimetypeFileBuilder();
35
-		$namings = $this->mimetypeDetector->getAllNamings();
36
-		file_put_contents(\OC::$SERVERROOT . '/core/js/mimetypelist.js', $generatedMimetypeFile->generateFile($aliases, $namings));
37
-
38
-		$output->writeln('<info>mimetypelist.js is updated');
39
-		return 0;
40
-	}
17
+    public function __construct(
18
+        protected IMimeTypeDetector $mimetypeDetector,
19
+    ) {
20
+        parent::__construct();
21
+    }
22
+
23
+    protected function configure() {
24
+        $this
25
+            ->setName('maintenance:mimetype:update-js')
26
+            ->setDescription('Update mimetypelist.js');
27
+    }
28
+
29
+    protected function execute(InputInterface $input, OutputInterface $output): int {
30
+        // Fetch all the aliases
31
+        $aliases = $this->mimetypeDetector->getAllAliases();
32
+
33
+        // Output the JS
34
+        $generatedMimetypeFile = new GenerateMimetypeFileBuilder();
35
+        $namings = $this->mimetypeDetector->getAllNamings();
36
+        file_put_contents(\OC::$SERVERROOT . '/core/js/mimetypelist.js', $generatedMimetypeFile->generateFile($aliases, $namings));
37
+
38
+        $output->writeln('<info>mimetypelist.js is updated');
39
+        return 0;
40
+    }
41 41
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -33,7 +33,7 @@
 block discarded – undo
33 33
 		// Output the JS
34 34
 		$generatedMimetypeFile = new GenerateMimetypeFileBuilder();
35 35
 		$namings = $this->mimetypeDetector->getAllNamings();
36
-		file_put_contents(\OC::$SERVERROOT . '/core/js/mimetypelist.js', $generatedMimetypeFile->generateFile($aliases, $namings));
36
+		file_put_contents(\OC::$SERVERROOT.'/core/js/mimetypelist.js', $generatedMimetypeFile->generateFile($aliases, $namings));
37 37
 
38 38
 		$output->writeln('<info>mimetypelist.js is updated');
39 39
 		return 0;
Please login to merge, or discard this patch.
tests/lib/IntegrityCheck/CheckerTest.php 2 patches
Indentation   +962 added lines, -962 removed lines patch added patch discarded remove patch
@@ -23,107 +23,107 @@  discard block
 block discarded – undo
23 23
 use Test\TestCase;
24 24
 
25 25
 class CheckerTest extends TestCase {
26
-	/** @var ServerVersion|\PHPUnit\Framework\MockObject\MockObject */
27
-	private $serverVersion;
28
-	/** @var EnvironmentHelper|\PHPUnit\Framework\MockObject\MockObject */
29
-	private $environmentHelper;
30
-	/** @var AppLocator|\PHPUnit\Framework\MockObject\MockObject */
31
-	private $appLocator;
32
-	/** @var Checker */
33
-	private $checker;
34
-	/** @var FileAccessHelper|\PHPUnit\Framework\MockObject\MockObject */
35
-	private $fileAccessHelper;
36
-	/** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
37
-	private $config;
38
-	/** @var IAppConfig|\PHPUnit\Framework\MockObject\MockObject */
39
-	private $appConfig;
40
-	/** @var ICacheFactory|\PHPUnit\Framework\MockObject\MockObject */
41
-	private $cacheFactory;
42
-	/** @var IAppManager|\PHPUnit\Framework\MockObject\MockObject */
43
-	private $appManager;
44
-	/** @var \OC\Files\Type\Detection|\PHPUnit\Framework\MockObject\MockObject */
45
-	private $mimeTypeDetector;
46
-
47
-	protected function setUp(): void {
48
-		parent::setUp();
49
-		$this->serverVersion = $this->createMock(ServerVersion::class);
50
-		$this->environmentHelper = $this->createMock(EnvironmentHelper::class);
51
-		$this->fileAccessHelper = $this->createMock(FileAccessHelper::class);
52
-		$this->appLocator = $this->createMock(AppLocator::class);
53
-		$this->config = $this->createMock(IConfig::class);
54
-		$this->appConfig = $this->createMock(IAppConfig::class);
55
-		$this->cacheFactory = $this->createMock(ICacheFactory::class);
56
-		$this->appManager = $this->createMock(IAppManager::class);
57
-		$this->mimeTypeDetector = $this->createMock(\OC\Files\Type\Detection::class);
58
-
59
-		$this->config->method('getAppValue')
60
-			->willReturnArgument(2);
61
-
62
-		$this->cacheFactory
63
-			->expects($this->any())
64
-			->method('createDistributed')
65
-			->with('oc.integritycheck.checker')
66
-			->willReturn(new NullCache());
67
-
68
-		$this->checker = new Checker(
69
-			$this->serverVersion,
70
-			$this->environmentHelper,
71
-			$this->fileAccessHelper,
72
-			$this->appLocator,
73
-			$this->config,
74
-			$this->appConfig,
75
-			$this->cacheFactory,
76
-			$this->appManager,
77
-			$this->mimeTypeDetector
78
-		);
79
-	}
80
-
81
-
82
-	public function testWriteAppSignatureOfNotExistingApp(): void {
83
-		$this->expectException(\Exception::class);
84
-		$this->expectExceptionMessage('Exception message');
85
-
86
-		$this->fileAccessHelper
87
-			->expects($this->once())
88
-			->method('assertDirectoryExists')
89
-			->with('NotExistingApp/appinfo')
90
-			->willThrowException(new \Exception('Exception message'));
91
-		$this->fileAccessHelper
92
-			->expects($this->once())
93
-			->method('is_writable')
94
-			->with('NotExistingApp/appinfo')
95
-			->willReturn(true);
96
-
97
-		$keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.crt');
98
-		$rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.key');
99
-		$rsa = new RSA();
100
-		$rsa->loadKey($rsaPrivateKey);
101
-		$x509 = new X509();
102
-		$x509->loadX509($keyBundle);
103
-		$this->checker->writeAppSignature('NotExistingApp', $x509, $rsa);
104
-	}
105
-
106
-
107
-	public function testWriteAppSignatureWrongPermissions(): void {
108
-		$this->expectException(\Exception::class);
109
-		$this->expectExceptionMessageMatches('/[a-zA-Z\\/_-]+ is not writable/');
110
-
111
-		$this->fileAccessHelper
112
-			->expects($this->once())
113
-			->method('file_put_contents')
114
-			->will($this->throwException(new \Exception('Exception message')));
115
-
116
-		$keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.crt');
117
-		$rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.key');
118
-		$rsa = new RSA();
119
-		$rsa->loadKey($rsaPrivateKey);
120
-		$x509 = new X509();
121
-		$x509->loadX509($keyBundle);
122
-		$this->checker->writeAppSignature(\OC::$SERVERROOT . '/tests/data/integritycheck/app/', $x509, $rsa);
123
-	}
124
-
125
-	public function testWriteAppSignature(): void {
126
-		$expectedSignatureFileData = '{
26
+    /** @var ServerVersion|\PHPUnit\Framework\MockObject\MockObject */
27
+    private $serverVersion;
28
+    /** @var EnvironmentHelper|\PHPUnit\Framework\MockObject\MockObject */
29
+    private $environmentHelper;
30
+    /** @var AppLocator|\PHPUnit\Framework\MockObject\MockObject */
31
+    private $appLocator;
32
+    /** @var Checker */
33
+    private $checker;
34
+    /** @var FileAccessHelper|\PHPUnit\Framework\MockObject\MockObject */
35
+    private $fileAccessHelper;
36
+    /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
37
+    private $config;
38
+    /** @var IAppConfig|\PHPUnit\Framework\MockObject\MockObject */
39
+    private $appConfig;
40
+    /** @var ICacheFactory|\PHPUnit\Framework\MockObject\MockObject */
41
+    private $cacheFactory;
42
+    /** @var IAppManager|\PHPUnit\Framework\MockObject\MockObject */
43
+    private $appManager;
44
+    /** @var \OC\Files\Type\Detection|\PHPUnit\Framework\MockObject\MockObject */
45
+    private $mimeTypeDetector;
46
+
47
+    protected function setUp(): void {
48
+        parent::setUp();
49
+        $this->serverVersion = $this->createMock(ServerVersion::class);
50
+        $this->environmentHelper = $this->createMock(EnvironmentHelper::class);
51
+        $this->fileAccessHelper = $this->createMock(FileAccessHelper::class);
52
+        $this->appLocator = $this->createMock(AppLocator::class);
53
+        $this->config = $this->createMock(IConfig::class);
54
+        $this->appConfig = $this->createMock(IAppConfig::class);
55
+        $this->cacheFactory = $this->createMock(ICacheFactory::class);
56
+        $this->appManager = $this->createMock(IAppManager::class);
57
+        $this->mimeTypeDetector = $this->createMock(\OC\Files\Type\Detection::class);
58
+
59
+        $this->config->method('getAppValue')
60
+            ->willReturnArgument(2);
61
+
62
+        $this->cacheFactory
63
+            ->expects($this->any())
64
+            ->method('createDistributed')
65
+            ->with('oc.integritycheck.checker')
66
+            ->willReturn(new NullCache());
67
+
68
+        $this->checker = new Checker(
69
+            $this->serverVersion,
70
+            $this->environmentHelper,
71
+            $this->fileAccessHelper,
72
+            $this->appLocator,
73
+            $this->config,
74
+            $this->appConfig,
75
+            $this->cacheFactory,
76
+            $this->appManager,
77
+            $this->mimeTypeDetector
78
+        );
79
+    }
80
+
81
+
82
+    public function testWriteAppSignatureOfNotExistingApp(): void {
83
+        $this->expectException(\Exception::class);
84
+        $this->expectExceptionMessage('Exception message');
85
+
86
+        $this->fileAccessHelper
87
+            ->expects($this->once())
88
+            ->method('assertDirectoryExists')
89
+            ->with('NotExistingApp/appinfo')
90
+            ->willThrowException(new \Exception('Exception message'));
91
+        $this->fileAccessHelper
92
+            ->expects($this->once())
93
+            ->method('is_writable')
94
+            ->with('NotExistingApp/appinfo')
95
+            ->willReturn(true);
96
+
97
+        $keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.crt');
98
+        $rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.key');
99
+        $rsa = new RSA();
100
+        $rsa->loadKey($rsaPrivateKey);
101
+        $x509 = new X509();
102
+        $x509->loadX509($keyBundle);
103
+        $this->checker->writeAppSignature('NotExistingApp', $x509, $rsa);
104
+    }
105
+
106
+
107
+    public function testWriteAppSignatureWrongPermissions(): void {
108
+        $this->expectException(\Exception::class);
109
+        $this->expectExceptionMessageMatches('/[a-zA-Z\\/_-]+ is not writable/');
110
+
111
+        $this->fileAccessHelper
112
+            ->expects($this->once())
113
+            ->method('file_put_contents')
114
+            ->will($this->throwException(new \Exception('Exception message')));
115
+
116
+        $keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.crt');
117
+        $rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.key');
118
+        $rsa = new RSA();
119
+        $rsa->loadKey($rsaPrivateKey);
120
+        $x509 = new X509();
121
+        $x509->loadX509($keyBundle);
122
+        $this->checker->writeAppSignature(\OC::$SERVERROOT . '/tests/data/integritycheck/app/', $x509, $rsa);
123
+    }
124
+
125
+    public function testWriteAppSignature(): void {
126
+        $expectedSignatureFileData = '{
127 127
     "hashes": {
128 128
         "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
129 129
         "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
@@ -131,65 +131,65 @@  discard block
 block discarded – undo
131 131
     "signature": "Y5yvXvcGHVPuRRatKVDUONWq1FpLXugZd6Km\/+aEHsQj7coVl9FeMj9OsWamBf7yRIw3dtNLguTLlAA9QAv\/b0uHN3JnbNZN+dwFOve4NMtqXfSDlWftqKN00VS+RJXpG1S2IIx9Poyp2NoghL\/5AuTv4GHiNb7zU\/DT\/kt71pUGPgPR6IIFaE+zHOD96vjYkrH+GfWZzKR0FCdLib9yyNvk+EGrcjKM6qjs2GKfS\/XFjj\/\/neDnh\/0kcPuKE3ZbofnI4TIDTv0CGqvOp7PtqVNc3Vy\/UKa7uF1PT0MAUKMww6EiMUSFZdUVP4WWF0Y72W53Qdtf1hrAZa2kfKyoK5kd7sQmCSKUPSU8978AUVZlBtTRlyT803IKwMV0iHMkw+xYB1sN2FlHup\/DESADqxhdgYuK35bCPvgkb4SBe4B8Voz\/izTvcP7VT5UvkYdAO+05\/jzdaHEmzmsD92CFfvX0q8O\/Y\/29ubftUJsqcHeMDKgcR4eZOE8+\/QVc\/89QO6WnKNuNuV+5bybO6g6PAdC9ZPsCvnihS61O2mwRXHLR3jv2UleFWm+lZEquPKtkhi6SLtDiijA4GV6dmS+dzujSLb7hGeD5o1plZcZ94uhWljl+QIp82+zU\/lYB1Zfr4Mb4e+V7r2gv7Fbv7y6YtjE2GIQwRhC5jq56bD0ZB+I=",
132 132
     "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEwTCCAqmgAwIBAgIUWv0iujufs5lUr0svCf\/qTQvoyKAwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIyNDk1M1oXDTE2MTEwMzIyNDk1M1owEjEQMA4GA1UEAwwHU29tZUFwcDCCAiIw\r\nDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK8q0x62agGSRBqeWsaeEwFfepMk\r\nF8cAobMMi50qHCv9IrOn\/ZH9l52xBrbIkErVmRjmly0d4JhD8Ymhidsh9ONKYl\/j\r\n+ishsZDM8eNNdp3Ew+fEYVvY1W7mR1qU24NWj0bzVsClI7hvPVIuw7AjfBDq1C5+\r\nA+ZSLSXYvOK2cEWjdxQfuNZwEZSjmA63DUllBIrm35IaTvfuyhU6BW9yHZxmb8+M\r\nw0xDv30D5UkE\/2N7Pa\/HQJLxCR+3zKibRK3nUyRDLSXxMkU9PnFNaPNX59VPgyj4\r\nGB1CFSToldJVPF4pzh7p36uGXZVxs8m3LFD4Ol8mhi7jkxDZjqFN46gzR0r23Py6\r\ndol9vfawGIoUwp9LvL0S7MvdRY0oazLXwClLP4OQ17zpSMAiCj7fgNT661JamPGj\r\nt5O7Zn2wA7I4ddDS\/HDTWCu98Zwc9fHIpsJPgCZ9awoqxi4Mnf7Pk9g5nnXhszGC\r\ncxxIASQKM+GhdzoRxKknax2RzUCwCzcPRtCj8AQT\/x\/mqN3PfRmlnFBNACUw9bpZ\r\nSOoNq2pCF9igftDWpSIXQ38pVpKLWowjjg3DVRmVKBgivHnUnVLyzYBahHPj0vaz\r\ntFtUFRaqXDnt+4qyUGyrT5h5pjZaTcHIcSB4PiarYwdVvgslgwnQzOUcGAzRWBD4\r\n6jV2brP5vFY3g6iPAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBACTY3CCHC+Z28gCf\r\nFWGKQ3wAKs+k4+0yoti0qm2EKX7rSGQ0PHSas6uW79WstC4Rj+DYkDtIhGMSg8FS\r\nHVGZHGBCc0HwdX+BOAt3zi4p7Sf3oQef70\/4imPoKxbAVCpd\/cveVcFyDC19j1yB\r\nBapwu87oh+muoeaZxOlqQI4UxjBlR\/uRSMhOn2UGauIr3dWJgAF4pGt7TtIzt+1v\r\n0uA6FtN1Y4R5O8AaJPh1bIG0CVvFBE58esGzjEYLhOydgKFnEP94kVPgJD5ds9C3\r\npPhEpo1dRpiXaF7WGIV1X6DI\/ipWvfrF7CEy6I\/kP1InY\/vMDjQjeDnJ\/VrXIWXO\r\nyZvHXVaN\/m+1RlETsH7YO\/QmxRue9ZHN3gvvWtmpCeA95sfpepOk7UcHxHZYyQbF\r\n49\/au8j+5tsr4A83xzsT1JbcKRxkAaQ7WDJpOnE5O1+H0fB+BaLakTg6XX9d4Fo7\r\n7Gin7hVWX7pL+JIyxMzME3LhfI61+CRcqZQIrpyaafUziPQbWIPfEs7h8tCOWyvW\r\nUO8ZLervYCB3j44ivkrxPlcBklDCqqKKBzDP9dYOtS\/P4RB1NkHA9+NTvmBpTonS\r\nSFXdg9fFMD7VfjDE3Vnk+8DWkVH5wBYowTAD7w9Wuzr7DumiAULexnP\/Y7xwxLv7\r\n4B+pXTAcRK0zECDEaX3npS8xWzrB\r\n-----END CERTIFICATE-----"
133 133
 }';
134
-		$this->fileAccessHelper
135
-			->expects($this->once())
136
-			->method('file_put_contents')
137
-			->with(
138
-				$this->equalTo(\OC::$SERVERROOT . '/tests/data/integritycheck/app//appinfo/signature.json'),
139
-				$this->callback(function ($signature) use ($expectedSignatureFileData) {
140
-					$expectedArray = json_decode($expectedSignatureFileData, true);
141
-					$actualArray = json_decode($signature, true);
142
-					$this->assertEquals($expectedArray, $actualArray);
143
-					return true;
144
-				})
145
-			);
146
-
147
-		$keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.crt');
148
-		$rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.key');
149
-		$rsa = new RSA();
150
-		$rsa->loadKey($rsaPrivateKey);
151
-		$x509 = new X509();
152
-		$x509->loadX509($keyBundle);
153
-		$this->checker->writeAppSignature(\OC::$SERVERROOT . '/tests/data/integritycheck/app/', $x509, $rsa);
154
-	}
155
-
156
-	public function testVerifyAppSignatureWithoutSignatureData(): void {
157
-		$this->serverVersion
158
-			->expects($this->once())
159
-			->method('getChannel')
160
-			->willReturn('stable');
161
-		$this->config
162
-			->expects($this->any())
163
-			->method('getSystemValueBool')
164
-			->with('integrity.check.disabled', false)
165
-			->willReturn(false);
166
-
167
-		$expected = [
168
-			'EXCEPTION' => [
169
-				'class' => 'OC\IntegrityCheck\Exceptions\InvalidSignatureException',
170
-				'message' => 'Signature data not found.',
171
-			],
172
-		];
173
-		$this->assertSame($expected, $this->checker->verifyAppSignature('SomeApp'));
174
-	}
175
-
176
-	public function testVerifyAppSignatureWithValidSignatureData(): void {
177
-		$this->serverVersion
178
-			->expects($this->once())
179
-			->method('getChannel')
180
-			->willReturn('stable');
181
-		$this->config
182
-			->expects($this->any())
183
-			->method('getSystemValueBool')
184
-			->with('integrity.check.disabled', false)
185
-			->willReturn(false);
186
-
187
-		$this->appLocator
188
-			->expects($this->once())
189
-			->method('getAppPath')
190
-			->with('SomeApp')
191
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
192
-		$signatureDataFile = '{
134
+        $this->fileAccessHelper
135
+            ->expects($this->once())
136
+            ->method('file_put_contents')
137
+            ->with(
138
+                $this->equalTo(\OC::$SERVERROOT . '/tests/data/integritycheck/app//appinfo/signature.json'),
139
+                $this->callback(function ($signature) use ($expectedSignatureFileData) {
140
+                    $expectedArray = json_decode($expectedSignatureFileData, true);
141
+                    $actualArray = json_decode($signature, true);
142
+                    $this->assertEquals($expectedArray, $actualArray);
143
+                    return true;
144
+                })
145
+            );
146
+
147
+        $keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.crt');
148
+        $rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.key');
149
+        $rsa = new RSA();
150
+        $rsa->loadKey($rsaPrivateKey);
151
+        $x509 = new X509();
152
+        $x509->loadX509($keyBundle);
153
+        $this->checker->writeAppSignature(\OC::$SERVERROOT . '/tests/data/integritycheck/app/', $x509, $rsa);
154
+    }
155
+
156
+    public function testVerifyAppSignatureWithoutSignatureData(): void {
157
+        $this->serverVersion
158
+            ->expects($this->once())
159
+            ->method('getChannel')
160
+            ->willReturn('stable');
161
+        $this->config
162
+            ->expects($this->any())
163
+            ->method('getSystemValueBool')
164
+            ->with('integrity.check.disabled', false)
165
+            ->willReturn(false);
166
+
167
+        $expected = [
168
+            'EXCEPTION' => [
169
+                'class' => 'OC\IntegrityCheck\Exceptions\InvalidSignatureException',
170
+                'message' => 'Signature data not found.',
171
+            ],
172
+        ];
173
+        $this->assertSame($expected, $this->checker->verifyAppSignature('SomeApp'));
174
+    }
175
+
176
+    public function testVerifyAppSignatureWithValidSignatureData(): void {
177
+        $this->serverVersion
178
+            ->expects($this->once())
179
+            ->method('getChannel')
180
+            ->willReturn('stable');
181
+        $this->config
182
+            ->expects($this->any())
183
+            ->method('getSystemValueBool')
184
+            ->with('integrity.check.disabled', false)
185
+            ->willReturn(false);
186
+
187
+        $this->appLocator
188
+            ->expects($this->once())
189
+            ->method('getAppPath')
190
+            ->with('SomeApp')
191
+            ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
192
+        $signatureDataFile = '{
193 193
     "hashes": {
194 194
         "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
195 195
         "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
@@ -197,34 +197,34 @@  discard block
 block discarded – undo
197 197
     "signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
198 198
     "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
199 199
 }';
200
-		$this->fileAccessHelper
201
-			->expects($this->exactly(2))
202
-			->method('file_get_contents')
203
-			->willReturnMap([
204
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/app//appinfo/signature.json', $signatureDataFile],
205
-				['/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
206
-			]);
207
-
208
-		$this->assertSame([], $this->checker->verifyAppSignature('SomeApp'));
209
-	}
210
-
211
-	public function testVerifyAppSignatureWithTamperedSignatureData(): void {
212
-		$this->serverVersion
213
-			->expects($this->once())
214
-			->method('getChannel')
215
-			->willReturn('stable');
216
-		$this->config
217
-			->expects($this->any())
218
-			->method('getSystemValueBool')
219
-			->with('integrity.check.disabled', false)
220
-			->willReturn(false);
221
-
222
-		$this->appLocator
223
-			->expects($this->once())
224
-			->method('getAppPath')
225
-			->with('SomeApp')
226
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
227
-		$signatureDataFile = '{
200
+        $this->fileAccessHelper
201
+            ->expects($this->exactly(2))
202
+            ->method('file_get_contents')
203
+            ->willReturnMap([
204
+                [\OC::$SERVERROOT . '/tests/data/integritycheck/app//appinfo/signature.json', $signatureDataFile],
205
+                ['/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
206
+            ]);
207
+
208
+        $this->assertSame([], $this->checker->verifyAppSignature('SomeApp'));
209
+    }
210
+
211
+    public function testVerifyAppSignatureWithTamperedSignatureData(): void {
212
+        $this->serverVersion
213
+            ->expects($this->once())
214
+            ->method('getChannel')
215
+            ->willReturn('stable');
216
+        $this->config
217
+            ->expects($this->any())
218
+            ->method('getSystemValueBool')
219
+            ->with('integrity.check.disabled', false)
220
+            ->willReturn(false);
221
+
222
+        $this->appLocator
223
+            ->expects($this->once())
224
+            ->method('getAppPath')
225
+            ->with('SomeApp')
226
+            ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
227
+        $signatureDataFile = '{
228 228
     "hashes": {
229 229
         "AnotherFile.txt": "tampered",
230 230
         "subfolder\/file.txt": "tampered"
@@ -232,40 +232,40 @@  discard block
 block discarded – undo
232 232
     "signature": "EL49UaSeyMAqyMtqId+tgOhhwgOevPZsRLX4j2blnybAB6fN07z0936JqZV7+eMPiE30Idx+UCY6rCFN531Kqe9vAOCdgtHUSOjjKyKc+lvULESlMb6YQcrZrvDlEMMjzjH49ewG7Ai8sNN6HrRUd9U8ws+ewSkW2DOOBItj\/21RBnkrSt+2AtGXGigEvuTm57HrCYDj8\/lSkumC2GVkjLUHeLOKYo4PRNOr6yP5mED5v7zo66AWvXl2fKv54InZcdxsAk35lyK9DGZbk\/027ZRd0AOHT3LImRLvQ+8EAg3XLlRUy0hOFGgPC+jYonMzgYvsAXAXi2j8LnLJlsLwpFwu1k1B+kZVPMumKZvP9OvJb70EirecXmz62V+Jiyuaq7ne4y7Kp5gKZT\/T8SeZ0lFtCmPfYyzBB0y8s5ldmTTmdVYHs54t\/OCCW82HzQZxnFNPzDTRa8HglsaMKrqPtW59+R4UvRKSWhB8M\/Ah57qgzycvPV4KMz\/FbD4l\/\/9chRKSlCfc2k3b8ZSHNmi+EzCKgJjWIoKdgN1yax94puU8jfn8UW+G7H9Y1Jsf\/jox6QLyYEgtV1vOHY2xLT7fVs2vhyvkN2MNjJnmQ70gFG5Qz2lBz5wi6ZpB+tOfCcpbLxWAkoWoIrmC\/Ilqh7mfmRZ43g5upjkepHNd93ONuY8=",
233 233
     "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEwTCCAqmgAwIBAgIUWv0iujufs5lUr0svCf\/qTQvoyKAwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIyNDk1M1oXDTE2MTEwMzIyNDk1M1owEjEQMA4GA1UEAwwHU29tZUFwcDCCAiIw\r\nDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK8q0x62agGSRBqeWsaeEwFfepMk\r\nF8cAobMMi50qHCv9IrOn\/ZH9l52xBrbIkErVmRjmly0d4JhD8Ymhidsh9ONKYl\/j\r\n+ishsZDM8eNNdp3Ew+fEYVvY1W7mR1qU24NWj0bzVsClI7hvPVIuw7AjfBDq1C5+\r\nA+ZSLSXYvOK2cEWjdxQfuNZwEZSjmA63DUllBIrm35IaTvfuyhU6BW9yHZxmb8+M\r\nw0xDv30D5UkE\/2N7Pa\/HQJLxCR+3zKibRK3nUyRDLSXxMkU9PnFNaPNX59VPgyj4\r\nGB1CFSToldJVPF4pzh7p36uGXZVxs8m3LFD4Ol8mhi7jkxDZjqFN46gzR0r23Py6\r\ndol9vfawGIoUwp9LvL0S7MvdRY0oazLXwClLP4OQ17zpSMAiCj7fgNT661JamPGj\r\nt5O7Zn2wA7I4ddDS\/HDTWCu98Zwc9fHIpsJPgCZ9awoqxi4Mnf7Pk9g5nnXhszGC\r\ncxxIASQKM+GhdzoRxKknax2RzUCwCzcPRtCj8AQT\/x\/mqN3PfRmlnFBNACUw9bpZ\r\nSOoNq2pCF9igftDWpSIXQ38pVpKLWowjjg3DVRmVKBgivHnUnVLyzYBahHPj0vaz\r\ntFtUFRaqXDnt+4qyUGyrT5h5pjZaTcHIcSB4PiarYwdVvgslgwnQzOUcGAzRWBD4\r\n6jV2brP5vFY3g6iPAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBACTY3CCHC+Z28gCf\r\nFWGKQ3wAKs+k4+0yoti0qm2EKX7rSGQ0PHSas6uW79WstC4Rj+DYkDtIhGMSg8FS\r\nHVGZHGBCc0HwdX+BOAt3zi4p7Sf3oQef70\/4imPoKxbAVCpd\/cveVcFyDC19j1yB\r\nBapwu87oh+muoeaZxOlqQI4UxjBlR\/uRSMhOn2UGauIr3dWJgAF4pGt7TtIzt+1v\r\n0uA6FtN1Y4R5O8AaJPh1bIG0CVvFBE58esGzjEYLhOydgKFnEP94kVPgJD5ds9C3\r\npPhEpo1dRpiXaF7WGIV1X6DI\/ipWvfrF7CEy6I\/kP1InY\/vMDjQjeDnJ\/VrXIWXO\r\nyZvHXVaN\/m+1RlETsH7YO\/QmxRue9ZHN3gvvWtmpCeA95sfpepOk7UcHxHZYyQbF\r\n49\/au8j+5tsr4A83xzsT1JbcKRxkAaQ7WDJpOnE5O1+H0fB+BaLakTg6XX9d4Fo7\r\n7Gin7hVWX7pL+JIyxMzME3LhfI61+CRcqZQIrpyaafUziPQbWIPfEs7h8tCOWyvW\r\nUO8ZLervYCB3j44ivkrxPlcBklDCqqKKBzDP9dYOtS\/P4RB1NkHA9+NTvmBpTonS\r\nSFXdg9fFMD7VfjDE3Vnk+8DWkVH5wBYowTAD7w9Wuzr7DumiAULexnP\/Y7xwxLv7\r\n4B+pXTAcRK0zECDEaX3npS8xWzrB\r\n-----END CERTIFICATE-----"
234 234
 }';
235
-		$this->fileAccessHelper
236
-			->expects($this->exactly(2))
237
-			->method('file_get_contents')
238
-			->willReturnMap([
239
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/app//appinfo/signature.json', $signatureDataFile],
240
-				['/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
241
-			]);
242
-
243
-		$expected = [
244
-			'EXCEPTION' => [
245
-				'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
246
-				'message' => 'Signature could not get verified.',
247
-			],
248
-		];
249
-		$this->assertEquals($expected, $this->checker->verifyAppSignature('SomeApp'));
250
-	}
251
-
252
-	public function testVerifyAppSignatureWithTamperedFiles(): void {
253
-		$this->serverVersion
254
-			->expects($this->once())
255
-			->method('getChannel')
256
-			->willReturn('stable');
257
-		$this->config
258
-			->expects($this->any())
259
-			->method('getSystemValueBool')
260
-			->with('integrity.check.disabled', false)
261
-			->willReturn(false);
262
-
263
-		$this->appLocator
264
-			->expects($this->once())
265
-			->method('getAppPath')
266
-			->with('SomeApp')
267
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/');
268
-		$signatureDataFile = '{
235
+        $this->fileAccessHelper
236
+            ->expects($this->exactly(2))
237
+            ->method('file_get_contents')
238
+            ->willReturnMap([
239
+                [\OC::$SERVERROOT . '/tests/data/integritycheck/app//appinfo/signature.json', $signatureDataFile],
240
+                ['/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
241
+            ]);
242
+
243
+        $expected = [
244
+            'EXCEPTION' => [
245
+                'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
246
+                'message' => 'Signature could not get verified.',
247
+            ],
248
+        ];
249
+        $this->assertEquals($expected, $this->checker->verifyAppSignature('SomeApp'));
250
+    }
251
+
252
+    public function testVerifyAppSignatureWithTamperedFiles(): void {
253
+        $this->serverVersion
254
+            ->expects($this->once())
255
+            ->method('getChannel')
256
+            ->willReturn('stable');
257
+        $this->config
258
+            ->expects($this->any())
259
+            ->method('getSystemValueBool')
260
+            ->with('integrity.check.disabled', false)
261
+            ->willReturn(false);
262
+
263
+        $this->appLocator
264
+            ->expects($this->once())
265
+            ->method('getAppPath')
266
+            ->with('SomeApp')
267
+            ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/');
268
+        $signatureDataFile = '{
269 269
     "hashes": {
270 270
         "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
271 271
         "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
@@ -273,55 +273,55 @@  discard block
 block discarded – undo
273 273
     "signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
274 274
     "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
275 275
 }';
276
-		$this->fileAccessHelper
277
-			->expects($this->exactly(2))
278
-			->method('file_get_contents')
279
-			->willReturnMap([
280
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//appinfo/signature.json', $signatureDataFile],
281
-				['/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
282
-			]);
283
-
284
-
285
-		$expected = [
286
-			'INVALID_HASH' => [
287
-				'AnotherFile.txt' => [
288
-					'expected' => '1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112',
289
-					'current' => '7322348ba269c6d5522efe02f424fa3a0da319a7cd9c33142a5afe32a2d9af2da3a411f086fcfc96ff4301ea566f481dba0960c2abeef3594c4d930462f6584c',
290
-				],
291
-			],
292
-			'FILE_MISSING' => [
293
-				'subfolder/file.txt' => [
294
-					'expected' => '410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b',
295
-					'current' => '',
296
-				],
297
-			],
298
-			'EXTRA_FILE' => [
299
-				'UnecessaryFile' => [
300
-					'expected' => '',
301
-					'current' => 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e',
302
-				],
303
-			],
304
-
305
-		];
306
-		$this->assertSame($expected, $this->checker->verifyAppSignature('SomeApp'));
307
-	}
308
-
309
-	public function testVerifyAppSignatureWithTamperedFilesAndAlternatePath(): void {
310
-		$this->serverVersion
311
-			->expects($this->once())
312
-			->method('getChannel')
313
-			->willReturn('stable');
314
-		$this->config
315
-			->expects($this->any())
316
-			->method('getSystemValueBool')
317
-			->with('integrity.check.disabled', false)
318
-			->willReturn(false);
319
-
320
-		$this->appLocator
321
-			->expects($this->never())
322
-			->method('getAppPath')
323
-			->with('SomeApp');
324
-		$signatureDataFile = '{
276
+        $this->fileAccessHelper
277
+            ->expects($this->exactly(2))
278
+            ->method('file_get_contents')
279
+            ->willReturnMap([
280
+                [\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//appinfo/signature.json', $signatureDataFile],
281
+                ['/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
282
+            ]);
283
+
284
+
285
+        $expected = [
286
+            'INVALID_HASH' => [
287
+                'AnotherFile.txt' => [
288
+                    'expected' => '1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112',
289
+                    'current' => '7322348ba269c6d5522efe02f424fa3a0da319a7cd9c33142a5afe32a2d9af2da3a411f086fcfc96ff4301ea566f481dba0960c2abeef3594c4d930462f6584c',
290
+                ],
291
+            ],
292
+            'FILE_MISSING' => [
293
+                'subfolder/file.txt' => [
294
+                    'expected' => '410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b',
295
+                    'current' => '',
296
+                ],
297
+            ],
298
+            'EXTRA_FILE' => [
299
+                'UnecessaryFile' => [
300
+                    'expected' => '',
301
+                    'current' => 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e',
302
+                ],
303
+            ],
304
+
305
+        ];
306
+        $this->assertSame($expected, $this->checker->verifyAppSignature('SomeApp'));
307
+    }
308
+
309
+    public function testVerifyAppSignatureWithTamperedFilesAndAlternatePath(): void {
310
+        $this->serverVersion
311
+            ->expects($this->once())
312
+            ->method('getChannel')
313
+            ->willReturn('stable');
314
+        $this->config
315
+            ->expects($this->any())
316
+            ->method('getSystemValueBool')
317
+            ->with('integrity.check.disabled', false)
318
+            ->willReturn(false);
319
+
320
+        $this->appLocator
321
+            ->expects($this->never())
322
+            ->method('getAppPath')
323
+            ->with('SomeApp');
324
+        $signatureDataFile = '{
325 325
     "hashes": {
326 326
         "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
327 327
         "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
@@ -329,55 +329,55 @@  discard block
 block discarded – undo
329 329
     "signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
330 330
     "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
331 331
 }';
332
-		$this->fileAccessHelper
333
-			->expects($this->exactly(2))
334
-			->method('file_get_contents')
335
-			->willReturnMap([
336
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//appinfo/signature.json', $signatureDataFile],
337
-				['/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
338
-			]);
339
-
340
-		$expected = [
341
-			'INVALID_HASH' => [
342
-				'AnotherFile.txt' => [
343
-					'expected' => '1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112',
344
-					'current' => '7322348ba269c6d5522efe02f424fa3a0da319a7cd9c33142a5afe32a2d9af2da3a411f086fcfc96ff4301ea566f481dba0960c2abeef3594c4d930462f6584c',
345
-				],
346
-			],
347
-			'FILE_MISSING' => [
348
-				'subfolder/file.txt' => [
349
-					'expected' => '410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b',
350
-					'current' => '',
351
-				],
352
-			],
353
-			'EXTRA_FILE' => [
354
-				'UnecessaryFile' => [
355
-					'expected' => '',
356
-					'current' => 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e',
357
-				],
358
-			],
359
-
360
-		];
361
-		$this->assertSame($expected, $this->checker->verifyAppSignature('SomeApp', \OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/'));
362
-	}
363
-
364
-	public function testVerifyAppWithDifferentScope(): void {
365
-		$this->serverVersion
366
-			->expects($this->once())
367
-			->method('getChannel')
368
-			->willReturn('stable');
369
-		$this->config
370
-			->expects($this->any())
371
-			->method('getSystemValueBool')
372
-			->with('integrity.check.disabled', false)
373
-			->willReturn(false);
374
-
375
-		$this->appLocator
376
-			->expects($this->once())
377
-			->method('getAppPath')
378
-			->with('SomeApp')
379
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/');
380
-		$signatureDataFile = '{
332
+        $this->fileAccessHelper
333
+            ->expects($this->exactly(2))
334
+            ->method('file_get_contents')
335
+            ->willReturnMap([
336
+                [\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//appinfo/signature.json', $signatureDataFile],
337
+                ['/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
338
+            ]);
339
+
340
+        $expected = [
341
+            'INVALID_HASH' => [
342
+                'AnotherFile.txt' => [
343
+                    'expected' => '1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112',
344
+                    'current' => '7322348ba269c6d5522efe02f424fa3a0da319a7cd9c33142a5afe32a2d9af2da3a411f086fcfc96ff4301ea566f481dba0960c2abeef3594c4d930462f6584c',
345
+                ],
346
+            ],
347
+            'FILE_MISSING' => [
348
+                'subfolder/file.txt' => [
349
+                    'expected' => '410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b',
350
+                    'current' => '',
351
+                ],
352
+            ],
353
+            'EXTRA_FILE' => [
354
+                'UnecessaryFile' => [
355
+                    'expected' => '',
356
+                    'current' => 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e',
357
+                ],
358
+            ],
359
+
360
+        ];
361
+        $this->assertSame($expected, $this->checker->verifyAppSignature('SomeApp', \OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/'));
362
+    }
363
+
364
+    public function testVerifyAppWithDifferentScope(): void {
365
+        $this->serverVersion
366
+            ->expects($this->once())
367
+            ->method('getChannel')
368
+            ->willReturn('stable');
369
+        $this->config
370
+            ->expects($this->any())
371
+            ->method('getSystemValueBool')
372
+            ->with('integrity.check.disabled', false)
373
+            ->willReturn(false);
374
+
375
+        $this->appLocator
376
+            ->expects($this->once())
377
+            ->method('getAppPath')
378
+            ->with('SomeApp')
379
+            ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/');
380
+        $signatureDataFile = '{
381 381
     "hashes": {
382 382
         "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
383 383
         "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
@@ -385,40 +385,40 @@  discard block
 block discarded – undo
385 385
     "signature": "eXesvDm3pkek12xSwMG10y9suRES79Nye3jYNe5KYq1tTUPqRRNgxmMGAfcUro0zpLeAr2YgHeSMWtglblGOW7pmwGVPZ0O1Y4r1fE6jnep0kW+35PLIaqCorIOnCAtSzDNKBhwd1ow3zW2wC0DFouuEkIO8u5Fw28g8E8dp8zEk1xMblNPy+xtWkmYHrVJ\/dQgun1bYOF2ZFtAzatwndTI\/bGsy1i3Wsl+x6HyWKQdq8y8VObtOqKDH7uERBEpB9DHVyKflj1v1gQuEH6BhaRdATc7ee0MiQdGblraIySwYRdfo2d8i82OVKrenMB3SLwyCvDPyQ9iKpTOnSF52ZBqaqSXKM2N\/RAkweeBFQQCwcHhqxvB0cfbyHcbkOLeCZe\/tsh68IxwTiYgzvLfl7sOZ5arnZbzrPpZmB+hfV2omkoJ1tDwOWz9hEmLLNtfo2OxyUH1m0+XFaC+Gbn4WkVDgf7YZkwUcG+Qoa3oKDNMss8MEyZxewl2iDGZcf402dlidHRprlfmXbAYuVQ08\/a0HxIKYPGh\/nsMGmwnO15CWtFpAbhUA\/D5oRjsIxnvXaMDg0iAFpdu\/5Ffsj7g3EPdBkiQHNYK7YU1RRx609eH0bZyiIYHdUPw7ikLupvrebZmELqi3mqDFO99u4eISlxFJlUbUND3L4BtmWTWrKwI=",
386 386
     "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIExjCCAq6gAwIBAgIUHSJjhJqMwr+3TkoiQFg4SVVYQ1gwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIzMjc1NVoXDTE2MTEwMzIzMjc1NVowFzEVMBMGA1UEAwwMQW5vdGhlclNjb3Bl\r\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA33npb5RmUkXrDT+TbwMf\r\n0zQ33SlzsjoGxCrbSwJOn6leGGInJ6ZrdzLL0WTi\/dTpg+Y\/JS+72XWm5NSjaTxo\r\n7OHc3cQBwXQj4tN6j\/y5qqY0GDLYufEkx2rpazqt9lBSJ72u1bGl2yoOXzYCz5i0\r\n60KsJXC9K44LKzGsarzbwAgskSVNkjAsPgjnCWZmcl6icpLi5Fz9rs2UMOWbdvdI\r\nAROsn0eC9E\/akmXTy5YMu6bAIGpvjZFHzyA83FQRbvv5o1V5Gsye\/VQLEgh7rqfz\r\nT\/jgWifP+JgoeB6otzuRZ3fFsmbBiyCIRtIOzQQflozhUlWtmiEGwg4GySuMUjEH\r\nA1LF86LO+ZzDQgd2oYNKmrQ8O+EcLqx9BpV4AFhEvqdk7uycJYPHs6yl+yfbzTeJ\r\n2Xd0yVAfd9r\/iDr36clLj2bzEObdl9xzKjcCIXE4Q0G4Pur41\/BJUDK9PI390ccQ\r\nnFjjVYBMsC859OwW64tMP0zkM9Vv72LCaEzaR8jqH0j11catqxunr+StfMcmxLTN\r\nbqBJbSEq4ER3mJxCTI2UrIVmdQ7+wRxgv3QTDNOZyqrz2L8A1Rpb3h0APxtQv+oA\r\n8KIZYID5\/qsS2V2jITkMQ8Nd1W3b0cZhZ600z+znh3jLJ0TYLvwN6\/qBQTUDaM2o\r\ng1+icMqXIXIeKuoPCVVsG7cCAwEAATANBgkqhkiG9w0BAQUFAAOCAgEAHc4F\/kOV\r\nHc8In5MmGg2YtjwZzjdeoC5TIPZczRqz0B+wRbJzN6aYryKZKLmP+wKpgRnJWDzp\r\nrgKGyyEQIAfK63DEv4B9p4N1B+B3aeMKsSpVcw7wbFTD57V5A7pURGoo31d0mw5L\r\nUIXZ2u+TUfGbzucMxLdFhTwjGpz9M6Kkm\/POxmV0tvLija5LdbdKnYR9BFmyu4IX\r\nqyoIAtComATNLl+3URu3SZxhE3NxhzMz+eAeNfh1KuIf2gWIIeDCXalVSJLym+OQ\r\nHFDpqRhJqfTMprrRlmmU7Zntgbj8\/RRZuXnBvH9cQ2KykLOb4UoCPlGUqOqKyP9m\r\nDJSFRiMJfpgMQUaJk1TLhKF+IR6FnmwURLEtkONJumtDQju9KaWPlhueONdyGi0p\r\nqxLVUo1Vb52XnPhk2GEEduxpDc9V5ePJ+pdcEdMifY\/uPNBRuBj2c87yq1DLH+U4\r\n3XzP1MlwjnBWZYuoFo0j6Jq0r\/MG6HjGdmkGIsRoheRi8Z8Scz5AW5QRkNz8pKop\r\nTELFqQy9g6TyQzzC8t6HZcpNe842ZUk4raEAbCZe\/XqxWMw5svPgNceBqM3fh7sZ\r\nBSykOHLaL8kiRO\/IS3y1yZEAuiWBvtxcTNLzBb+hdRpm2y8\/qH\/pKo+CMj1VzjNT\r\nD8YRQg0cjmDytJzHDrtV\/aTc9W1aPHun0vw=\r\n-----END CERTIFICATE-----"
387 387
 }';
388
-		$this->fileAccessHelper
389
-			->expects($this->exactly(2))
390
-			->method('file_get_contents')
391
-			->willReturnMap([
392
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//appinfo/signature.json', $signatureDataFile],
393
-				['/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
394
-			]);
395
-
396
-		$expected = [
397
-			'EXCEPTION' => [
398
-				'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
399
-				'message' => 'Certificate is not valid for required scope. (Requested: SomeApp, current: CN=AnotherScope)',
400
-			],
401
-		];
402
-		$this->assertSame($expected, $this->checker->verifyAppSignature('SomeApp'));
403
-	}
404
-
405
-	public function testVerifyAppWithDifferentScopeAndAlwaysTrustedCore(): void {
406
-		$this->serverVersion
407
-			->expects($this->once())
408
-			->method('getChannel')
409
-			->willReturn('stable');
410
-		$this->config
411
-			->expects($this->any())
412
-			->method('getSystemValueBool')
413
-			->with('integrity.check.disabled', false)
414
-			->willReturn(false);
415
-
416
-		$this->appLocator
417
-			->expects($this->once())
418
-			->method('getAppPath')
419
-			->with('SomeApp')
420
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
421
-		$signatureDataFile = '{
388
+        $this->fileAccessHelper
389
+            ->expects($this->exactly(2))
390
+            ->method('file_get_contents')
391
+            ->willReturnMap([
392
+                [\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//appinfo/signature.json', $signatureDataFile],
393
+                ['/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
394
+            ]);
395
+
396
+        $expected = [
397
+            'EXCEPTION' => [
398
+                'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
399
+                'message' => 'Certificate is not valid for required scope. (Requested: SomeApp, current: CN=AnotherScope)',
400
+            ],
401
+        ];
402
+        $this->assertSame($expected, $this->checker->verifyAppSignature('SomeApp'));
403
+    }
404
+
405
+    public function testVerifyAppWithDifferentScopeAndAlwaysTrustedCore(): void {
406
+        $this->serverVersion
407
+            ->expects($this->once())
408
+            ->method('getChannel')
409
+            ->willReturn('stable');
410
+        $this->config
411
+            ->expects($this->any())
412
+            ->method('getSystemValueBool')
413
+            ->with('integrity.check.disabled', false)
414
+            ->willReturn(false);
415
+
416
+        $this->appLocator
417
+            ->expects($this->once())
418
+            ->method('getAppPath')
419
+            ->with('SomeApp')
420
+            ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
421
+        $signatureDataFile = '{
422 422
     "hashes": {
423 423
         "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
424 424
         "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
@@ -426,67 +426,67 @@  discard block
 block discarded – undo
426 426
     "signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
427 427
     "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
428 428
 }';
429
-		$this->fileAccessHelper
430
-			->expects($this->exactly(2))
431
-			->method('file_get_contents')
432
-			->willReturnMap([
433
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/app//appinfo/signature.json', $signatureDataFile],
434
-				['/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
435
-			]);
436
-
437
-		$this->assertSame([], $this->checker->verifyAppSignature('SomeApp'));
438
-	}
439
-
440
-
441
-	public function testWriteCoreSignatureWithException(): void {
442
-		$this->expectException(\Exception::class);
443
-		$this->expectExceptionMessage('Exception message');
444
-
445
-		$this->fileAccessHelper
446
-			->expects($this->once())
447
-			->method('assertDirectoryExists')
448
-			->will($this->throwException(new \Exception('Exception message')));
449
-		$this->fileAccessHelper
450
-			->expects($this->once())
451
-			->method('is_writable')
452
-			->with(__DIR__ . '/core')
453
-			->willReturn(true);
454
-
455
-		$keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.crt');
456
-		$rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.key');
457
-		$rsa = new RSA();
458
-		$rsa->loadKey($rsaPrivateKey);
459
-		$x509 = new X509();
460
-		$x509->loadX509($keyBundle);
461
-		$this->checker->writeCoreSignature($x509, $rsa, __DIR__);
462
-	}
463
-
464
-
465
-	public function testWriteCoreSignatureWrongPermissions(): void {
466
-		$this->expectException(\Exception::class);
467
-		$this->expectExceptionMessageMatches('/[a-zA-Z\\/_-]+ is not writable/');
468
-
469
-		$this->fileAccessHelper
470
-			->expects($this->once())
471
-			->method('assertDirectoryExists')
472
-			->will($this->throwException(new \Exception('Exception message')));
473
-		$this->fileAccessHelper
474
-			->expects($this->once())
475
-			->method('is_writable')
476
-			->with(__DIR__ . '/core')
477
-			->willReturn(false);
478
-
479
-		$keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.crt');
480
-		$rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.key');
481
-		$rsa = new RSA();
482
-		$rsa->loadKey($rsaPrivateKey);
483
-		$x509 = new X509();
484
-		$x509->loadX509($keyBundle);
485
-		$this->checker->writeCoreSignature($x509, $rsa, __DIR__);
486
-	}
487
-
488
-	public function testWriteCoreSignature(): void {
489
-		$expectedSignatureFileData = '{
429
+        $this->fileAccessHelper
430
+            ->expects($this->exactly(2))
431
+            ->method('file_get_contents')
432
+            ->willReturnMap([
433
+                [\OC::$SERVERROOT . '/tests/data/integritycheck/app//appinfo/signature.json', $signatureDataFile],
434
+                ['/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
435
+            ]);
436
+
437
+        $this->assertSame([], $this->checker->verifyAppSignature('SomeApp'));
438
+    }
439
+
440
+
441
+    public function testWriteCoreSignatureWithException(): void {
442
+        $this->expectException(\Exception::class);
443
+        $this->expectExceptionMessage('Exception message');
444
+
445
+        $this->fileAccessHelper
446
+            ->expects($this->once())
447
+            ->method('assertDirectoryExists')
448
+            ->will($this->throwException(new \Exception('Exception message')));
449
+        $this->fileAccessHelper
450
+            ->expects($this->once())
451
+            ->method('is_writable')
452
+            ->with(__DIR__ . '/core')
453
+            ->willReturn(true);
454
+
455
+        $keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.crt');
456
+        $rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.key');
457
+        $rsa = new RSA();
458
+        $rsa->loadKey($rsaPrivateKey);
459
+        $x509 = new X509();
460
+        $x509->loadX509($keyBundle);
461
+        $this->checker->writeCoreSignature($x509, $rsa, __DIR__);
462
+    }
463
+
464
+
465
+    public function testWriteCoreSignatureWrongPermissions(): void {
466
+        $this->expectException(\Exception::class);
467
+        $this->expectExceptionMessageMatches('/[a-zA-Z\\/_-]+ is not writable/');
468
+
469
+        $this->fileAccessHelper
470
+            ->expects($this->once())
471
+            ->method('assertDirectoryExists')
472
+            ->will($this->throwException(new \Exception('Exception message')));
473
+        $this->fileAccessHelper
474
+            ->expects($this->once())
475
+            ->method('is_writable')
476
+            ->with(__DIR__ . '/core')
477
+            ->willReturn(false);
478
+
479
+        $keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.crt');
480
+        $rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.key');
481
+        $rsa = new RSA();
482
+        $rsa->loadKey($rsaPrivateKey);
483
+        $x509 = new X509();
484
+        $x509->loadX509($keyBundle);
485
+        $this->checker->writeCoreSignature($x509, $rsa, __DIR__);
486
+    }
487
+
488
+    public function testWriteCoreSignature(): void {
489
+        $expectedSignatureFileData = '{
490 490
     "hashes": {
491 491
         "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
492 492
         "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
@@ -494,34 +494,34 @@  discard block
 block discarded – undo
494 494
     "signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
495 495
     "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
496 496
 }';
497
-		$this->environmentHelper
498
-			->expects($this->any())
499
-			->method('getServerRoot')
500
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
501
-		$this->fileAccessHelper
502
-			->expects($this->once())
503
-			->method('file_put_contents')
504
-			->with(
505
-				\OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json',
506
-				$this->callback(function ($signature) use ($expectedSignatureFileData) {
507
-					$expectedArray = json_decode($expectedSignatureFileData, true);
508
-					$actualArray = json_decode($signature, true);
509
-					$this->assertEquals($expectedArray, $actualArray);
510
-					return true;
511
-				})
512
-			);
513
-
514
-		$keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/core.crt');
515
-		$rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/core.key');
516
-		$rsa = new RSA();
517
-		$rsa->loadKey($rsaPrivateKey);
518
-		$x509 = new X509();
519
-		$x509->loadX509($keyBundle);
520
-		$this->checker->writeCoreSignature($x509, $rsa, \OC::$SERVERROOT . '/tests/data/integritycheck/app/');
521
-	}
522
-
523
-	public function testWriteCoreSignatureWithUnmodifiedHtaccess(): void {
524
-		$expectedSignatureFileData = '{
497
+        $this->environmentHelper
498
+            ->expects($this->any())
499
+            ->method('getServerRoot')
500
+            ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
501
+        $this->fileAccessHelper
502
+            ->expects($this->once())
503
+            ->method('file_put_contents')
504
+            ->with(
505
+                \OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json',
506
+                $this->callback(function ($signature) use ($expectedSignatureFileData) {
507
+                    $expectedArray = json_decode($expectedSignatureFileData, true);
508
+                    $actualArray = json_decode($signature, true);
509
+                    $this->assertEquals($expectedArray, $actualArray);
510
+                    return true;
511
+                })
512
+            );
513
+
514
+        $keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/core.crt');
515
+        $rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/core.key');
516
+        $rsa = new RSA();
517
+        $rsa->loadKey($rsaPrivateKey);
518
+        $x509 = new X509();
519
+        $x509->loadX509($keyBundle);
520
+        $this->checker->writeCoreSignature($x509, $rsa, \OC::$SERVERROOT . '/tests/data/integritycheck/app/');
521
+    }
522
+
523
+    public function testWriteCoreSignatureWithUnmodifiedHtaccess(): void {
524
+        $expectedSignatureFileData = '{
525 525
     "hashes": {
526 526
         ".htaccess": "dc479770a6232061e04a768ee1f9133fdb3aea7b3a99f7105b0e0b6197474733e8d14b5b2bbad054e6b62a410fe5d0b3d790242dee1e0f11274af2100f5289e2",
527 527
         "subfolder\/.htaccess": "2c57b1e25050e11dc3ae975832f378c452159f7b69f818e47eeeafadd6ba568517461dcb4d843b90b906cd7c89d161bc1b89dff8e3ae0eb6f5088508c47befd1"
@@ -529,64 +529,64 @@  discard block
 block discarded – undo
529 529
     "signature": "nRtR377DB\/I\/4hmh9q3elMQYfSHnQFlNtjchNgrdfmUQqVmgkU\/4qgGyxDqYkV8mSMbH2gYysfP42nx\/3zSo7n0dBYDfU87Q6f96Cv597vEV27do8CaBkEk8Xjn2SxhHw8hVxracvE2OBAPxk0H3sRp\/cQBgjoXpju4kQin0N5E+DEJMh7Sp+u8aKoFpb+2FaAZJFn\/hnqxLTlVi2nyDxGL3U0eobWY+jWH9XPt52v3Hyh8TDhcAnQ1cN30B8Jn2+jkrm8ib+buchaCXHk0cPX72xuPECdwOEKLCBNrJa3FGSvO1zWiecnCgxCXgt+R8hUgsVPTsbrdFY2YRJGIhHndYZL98XzgG7cw85SnnMMe2SulzeL7xANGF8qiEVyiC7x83bbj5xOkeM\/CUTajrLBO3vyZ23KKOxvskjgI0t+Zw1zFsl+sYW0\/O\/V5WzPOwMwV8+iApQ8k9gEMiYQg98QLEMYnSohncmp0Z9qx2qFcQuHLcKJVa1J6wGtE\/EHR\/4d0aYPd6IRjg+qshCJmdzud\/12xjpGTl+BT0Hi0VsU5o7ZMi7WhmukZmmv8u0uZsvKREQNATm4cO4WCkYySt5O9gZEJOF+jjgeynDoAh09lyrNXIgMpM9ufm\/XEG\/I\/f2zIwbAUc6J6qks5OuYlJzW5vscTiOKhwcGZU9WBLgh0=",
530 530
     "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
531 531
 }';
532
-		$this->environmentHelper
533
-			->expects($this->any())
534
-			->method('getServerRoot')
535
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/htaccessUnmodified/');
536
-		$this->fileAccessHelper
537
-			->expects($this->once())
538
-			->method('file_put_contents')
539
-			->with(
540
-				\OC::$SERVERROOT . '/tests/data/integritycheck/htaccessUnmodified//core/signature.json',
541
-				$this->callback(function ($signature) use ($expectedSignatureFileData) {
542
-					$expectedArray = json_decode($expectedSignatureFileData, true);
543
-					$actualArray = json_decode($signature, true);
544
-					$this->assertEquals($expectedArray, $actualArray);
545
-					return true;
546
-				})
547
-			);
548
-
549
-		$keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/core.crt');
550
-		$rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/core.key');
551
-		$rsa = new RSA();
552
-		$rsa->loadKey($rsaPrivateKey);
553
-		$x509 = new X509();
554
-		$x509->loadX509($keyBundle);
555
-		$this->checker->writeCoreSignature($x509, $rsa, \OC::$SERVERROOT . '/tests/data/integritycheck/htaccessUnmodified/');
556
-	}
557
-
558
-	public function testWriteCoreSignatureWithInvalidModifiedHtaccess(): void {
559
-		$expectedSignatureFileData = '{
532
+        $this->environmentHelper
533
+            ->expects($this->any())
534
+            ->method('getServerRoot')
535
+            ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/htaccessUnmodified/');
536
+        $this->fileAccessHelper
537
+            ->expects($this->once())
538
+            ->method('file_put_contents')
539
+            ->with(
540
+                \OC::$SERVERROOT . '/tests/data/integritycheck/htaccessUnmodified//core/signature.json',
541
+                $this->callback(function ($signature) use ($expectedSignatureFileData) {
542
+                    $expectedArray = json_decode($expectedSignatureFileData, true);
543
+                    $actualArray = json_decode($signature, true);
544
+                    $this->assertEquals($expectedArray, $actualArray);
545
+                    return true;
546
+                })
547
+            );
548
+
549
+        $keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/core.crt');
550
+        $rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/core.key');
551
+        $rsa = new RSA();
552
+        $rsa->loadKey($rsaPrivateKey);
553
+        $x509 = new X509();
554
+        $x509->loadX509($keyBundle);
555
+        $this->checker->writeCoreSignature($x509, $rsa, \OC::$SERVERROOT . '/tests/data/integritycheck/htaccessUnmodified/');
556
+    }
557
+
558
+    public function testWriteCoreSignatureWithInvalidModifiedHtaccess(): void {
559
+        $expectedSignatureFileData = '{
560 560
     "hashes": {
561 561
         ".htaccess": "4a54273dc8d697b2ca615acf2ae2c1ee3c1c643492cb04f42b10984fa9aacff1420dc829fd82f93ad3476fbd0cdab0251142c887dc8f872d03e39a3a3eb6d381"
562 562
     },
563 563
     "signature": "qpDddYGgAKNR3TszOgjPXRphUl2P9Ym5OQaetltocgZASGDkOun5D64+1D0QJRKb4SG2+48muxGOHyL2Ngos4NUrrSR+SIkywZacay82YQBCEdr7\/4MjW1WHRPjvboLwEJwViw0EdAjsWRpD68aPnzUGrGsy2BsCo06P5iwjk9cXcHxdjC9R39npvoC3QNvQ2jmNIbh1Lc4U97dbb+CsXEQCLU1OSa9p3q6cEFV98Easwt7uF\/DzHK+CbeZlxVZ0DwLh2\/ylT1PyGou8QC1b3vKAnPjLWMO+UsCPpCKhk3C5pV+5etQ8puGd+0x2t5tEU+qXxLzek91zWNC+rqgC\/WlqLKbwPb\/BCHs4zLGV55Q2fEQmT21x0KCUELdPs4dBnYP4Ox5tEDugtJujWFzOHzoY6gGa\/BY\/78pSZXmq9o8dWkBEtioWWvaNZ1rM0ddE83GBlBTgjigi9Ay1D++bUW\/FCBB7CMk6qyNlV81H+cBuIEODw2aymmkM9LLDD2Qbmvo8gHEPRjiQxPC5OpDlcdSNiL+zcxVxeuX4FpT+9xzz\/\/DRONhufxRpsbuCOMxd96RW7y9U2N2Uxb3Bzn\/BIqEayUUsdgZjfaGcXXYKR+chu\/LOwNYN6RlnLsgqL\/dhGKwlRVKXw1RA2\/af\/CpqyR7uVP6al1YJo\/YJ+5XJ6zE=",
564 564
     "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
565 565
 }';
566
-		$this->fileAccessHelper
567
-			->expects($this->once())
568
-			->method('file_put_contents')
569
-			->with(
570
-				\OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithInvalidModifiedContent//core/signature.json',
571
-				$this->callback(function ($signature) use ($expectedSignatureFileData) {
572
-					$expectedArray = json_decode($expectedSignatureFileData, true);
573
-					$actualArray = json_decode($signature, true);
574
-					$this->assertEquals($expectedArray, $actualArray);
575
-					return true;
576
-				})
577
-			);
578
-
579
-		$keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/core.crt');
580
-		$rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/core.key');
581
-		$rsa = new RSA();
582
-		$rsa->loadKey($rsaPrivateKey);
583
-		$x509 = new X509();
584
-		$x509->loadX509($keyBundle);
585
-		$this->checker->writeCoreSignature($x509, $rsa, \OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithInvalidModifiedContent/');
586
-	}
587
-
588
-	public function testWriteCoreSignatureWithValidModifiedHtaccess(): void {
589
-		$expectedSignatureFileData = '{
566
+        $this->fileAccessHelper
567
+            ->expects($this->once())
568
+            ->method('file_put_contents')
569
+            ->with(
570
+                \OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithInvalidModifiedContent//core/signature.json',
571
+                $this->callback(function ($signature) use ($expectedSignatureFileData) {
572
+                    $expectedArray = json_decode($expectedSignatureFileData, true);
573
+                    $actualArray = json_decode($signature, true);
574
+                    $this->assertEquals($expectedArray, $actualArray);
575
+                    return true;
576
+                })
577
+            );
578
+
579
+        $keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/core.crt');
580
+        $rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/core.key');
581
+        $rsa = new RSA();
582
+        $rsa->loadKey($rsaPrivateKey);
583
+        $x509 = new X509();
584
+        $x509->loadX509($keyBundle);
585
+        $this->checker->writeCoreSignature($x509, $rsa, \OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithInvalidModifiedContent/');
586
+    }
587
+
588
+    public function testWriteCoreSignatureWithValidModifiedHtaccess(): void {
589
+        $expectedSignatureFileData = '{
590 590
     "hashes": {
591 591
         ".htaccess": "7e6a7a4d8ee4f3fbc45dd579407c643471575a9d127d1c75f6d0a49e80766c3c587104b2139ef76d2a4bffce3f45777900605aaa49519c9532909b71e5030227",
592 592
         "subfolder\/.htaccess": "2c57b1e25050e11dc3ae975832f378c452159f7b69f818e47eeeafadd6ba568517461dcb4d843b90b906cd7c89d161bc1b89dff8e3ae0eb6f5088508c47befd1"
@@ -594,68 +594,68 @@  discard block
 block discarded – undo
594 594
     "signature": "YVwQvl9Dh8UebCumfgzFxfz3NiZJLmYG8oJVTfEBhulI4KXBnTG1jZTprf4XxG2XIriEYAZXsoXpu9xWsUFe9QfdncwoEpqJtGq7l6aVDTofX5Be5b03MQFJr4cflgllqW77QZ84D9O9qWF\/vNDAofXcwrzT04CxLDhyQgTCgYUnRjG9pnuP\/gtbDKbTjRvxhTyfg3T0Phv1+XAvpTPnH2q5A+1+LmiqziUJ1sMipsKo+jQP614eCi9qjmqhHIgLRgcuOBvsi4g5WUcdcAIZ6qLt5gm2Y3r6rKNVchosU9ZydMUTfjuejDbVwE2fNH5UUnV57fQBxwg9CfX7iFHqKv1bfv5Zviu12paShgWCB12uR3iH\/3lmTJn8K5Xqit3G4eymFaJ5IChdUThBp\/jhQSI2r8sPcZDYSJ\/UZKuFnezFdKhEBd5hMXe8aKAd6ijGDjLARksFuqpi1sS8llC5K1Q+DzktSL\/o64TY4Vuvykiwe\/BAk2SkL9voOtrvU7vfDBcuCPbDJnSBBC0ESpcXeClTBIn6xZ9WaxqoS7sinE\/kUwtWsRd04I7d79\/ouotyNb+mBhTuRsZT12p\/gn4JHXXNUAIpTwchYzGxbfNJ4kxnYBFZWVmvsSqOLFZu1yi5BP3ktA9yhFyWIa5659azRFEKRdXpVHtQVa4IgdhxEqA=",
595 595
     "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
596 596
 }';
597
-		$this->environmentHelper
598
-			->expects($this->any())
599
-			->method('getServerRoot')
600
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithValidModifiedContent');
601
-		$this->fileAccessHelper
602
-			->expects($this->once())
603
-			->method('file_put_contents')
604
-			->with(
605
-				\OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithValidModifiedContent/core/signature.json',
606
-				$this->callback(function ($signature) use ($expectedSignatureFileData) {
607
-					$expectedArray = json_decode($expectedSignatureFileData, true);
608
-					$actualArray = json_decode($signature, true);
609
-					$this->assertEquals($expectedArray, $actualArray);
610
-					return true;
611
-				})
612
-			);
613
-
614
-		$keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/core.crt');
615
-		$rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/core.key');
616
-		$rsa = new RSA();
617
-		$rsa->loadKey($rsaPrivateKey);
618
-		$x509 = new X509();
619
-		$x509->loadX509($keyBundle);
620
-		$this->checker->writeCoreSignature($x509, $rsa, \OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithValidModifiedContent');
621
-	}
622
-
623
-	public function testVerifyCoreSignatureWithoutSignatureData(): void {
624
-		$this->serverVersion
625
-			->expects($this->once())
626
-			->method('getChannel')
627
-			->willReturn('stable');
628
-		$this->config
629
-			->expects($this->any())
630
-			->method('getSystemValueBool')
631
-			->with('integrity.check.disabled', false)
632
-			->willReturn(false);
633
-
634
-		$expected = [
635
-			'EXCEPTION' => [
636
-				'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
637
-				'message' => 'Signature data not found.',
638
-			],
639
-		];
640
-		$this->assertSame($expected, $this->checker->verifyCoreSignature());
641
-	}
642
-
643
-	public function testVerifyCoreSignatureWithValidSignatureData(): void {
644
-		$this->serverVersion
645
-			->expects($this->once())
646
-			->method('getChannel')
647
-			->willReturn('stable');
648
-		$this->config
649
-			->expects($this->any())
650
-			->method('getSystemValueBool')
651
-			->with('integrity.check.disabled', false)
652
-			->willReturn(false);
653
-
654
-		$this->environmentHelper
655
-			->expects($this->any())
656
-			->method('getServerRoot')
657
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
658
-		$signatureDataFile = '{
597
+        $this->environmentHelper
598
+            ->expects($this->any())
599
+            ->method('getServerRoot')
600
+            ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithValidModifiedContent');
601
+        $this->fileAccessHelper
602
+            ->expects($this->once())
603
+            ->method('file_put_contents')
604
+            ->with(
605
+                \OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithValidModifiedContent/core/signature.json',
606
+                $this->callback(function ($signature) use ($expectedSignatureFileData) {
607
+                    $expectedArray = json_decode($expectedSignatureFileData, true);
608
+                    $actualArray = json_decode($signature, true);
609
+                    $this->assertEquals($expectedArray, $actualArray);
610
+                    return true;
611
+                })
612
+            );
613
+
614
+        $keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/core.crt');
615
+        $rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/core.key');
616
+        $rsa = new RSA();
617
+        $rsa->loadKey($rsaPrivateKey);
618
+        $x509 = new X509();
619
+        $x509->loadX509($keyBundle);
620
+        $this->checker->writeCoreSignature($x509, $rsa, \OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithValidModifiedContent');
621
+    }
622
+
623
+    public function testVerifyCoreSignatureWithoutSignatureData(): void {
624
+        $this->serverVersion
625
+            ->expects($this->once())
626
+            ->method('getChannel')
627
+            ->willReturn('stable');
628
+        $this->config
629
+            ->expects($this->any())
630
+            ->method('getSystemValueBool')
631
+            ->with('integrity.check.disabled', false)
632
+            ->willReturn(false);
633
+
634
+        $expected = [
635
+            'EXCEPTION' => [
636
+                'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
637
+                'message' => 'Signature data not found.',
638
+            ],
639
+        ];
640
+        $this->assertSame($expected, $this->checker->verifyCoreSignature());
641
+    }
642
+
643
+    public function testVerifyCoreSignatureWithValidSignatureData(): void {
644
+        $this->serverVersion
645
+            ->expects($this->once())
646
+            ->method('getChannel')
647
+            ->willReturn('stable');
648
+        $this->config
649
+            ->expects($this->any())
650
+            ->method('getSystemValueBool')
651
+            ->with('integrity.check.disabled', false)
652
+            ->willReturn(false);
653
+
654
+        $this->environmentHelper
655
+            ->expects($this->any())
656
+            ->method('getServerRoot')
657
+            ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
658
+        $signatureDataFile = '{
659 659
     "hashes": {
660 660
         "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
661 661
         "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
@@ -663,33 +663,33 @@  discard block
 block discarded – undo
663 663
     "signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
664 664
     "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
665 665
 }';
666
-		$this->fileAccessHelper
667
-			->expects($this->exactly(2))
668
-			->method('file_get_contents')
669
-			->willReturnMap([
670
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json', $signatureDataFile],
671
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/app//resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
672
-			]);
673
-
674
-		$this->assertSame([], $this->checker->verifyCoreSignature());
675
-	}
676
-
677
-	public function testVerifyCoreSignatureWithValidModifiedHtaccessSignatureData(): void {
678
-		$this->serverVersion
679
-			->expects($this->once())
680
-			->method('getChannel')
681
-			->willReturn('stable');
682
-		$this->config
683
-			->expects($this->any())
684
-			->method('getSystemValueBool')
685
-			->with('integrity.check.disabled', false)
686
-			->willReturn(false);
687
-
688
-		$this->environmentHelper
689
-			->expects($this->any())
690
-			->method('getServerRoot')
691
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithValidModifiedContent');
692
-		$signatureDataFile = '{
666
+        $this->fileAccessHelper
667
+            ->expects($this->exactly(2))
668
+            ->method('file_get_contents')
669
+            ->willReturnMap([
670
+                [\OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json', $signatureDataFile],
671
+                [\OC::$SERVERROOT . '/tests/data/integritycheck/app//resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
672
+            ]);
673
+
674
+        $this->assertSame([], $this->checker->verifyCoreSignature());
675
+    }
676
+
677
+    public function testVerifyCoreSignatureWithValidModifiedHtaccessSignatureData(): void {
678
+        $this->serverVersion
679
+            ->expects($this->once())
680
+            ->method('getChannel')
681
+            ->willReturn('stable');
682
+        $this->config
683
+            ->expects($this->any())
684
+            ->method('getSystemValueBool')
685
+            ->with('integrity.check.disabled', false)
686
+            ->willReturn(false);
687
+
688
+        $this->environmentHelper
689
+            ->expects($this->any())
690
+            ->method('getServerRoot')
691
+            ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithValidModifiedContent');
692
+        $signatureDataFile = '{
693 693
     "hashes": {
694 694
         ".htaccess": "7e6a7a4d8ee4f3fbc45dd579407c643471575a9d127d1c75f6d0a49e80766c3c587104b2139ef76d2a4bffce3f45777900605aaa49519c9532909b71e5030227",
695 695
         "subfolder\/.htaccess": "2c57b1e25050e11dc3ae975832f378c452159f7b69f818e47eeeafadd6ba568517461dcb4d843b90b906cd7c89d161bc1b89dff8e3ae0eb6f5088508c47befd1"
@@ -697,92 +697,92 @@  discard block
 block discarded – undo
697 697
     "signature": "YVwQvl9Dh8UebCumfgzFxfz3NiZJLmYG8oJVTfEBhulI4KXBnTG1jZTprf4XxG2XIriEYAZXsoXpu9xWsUFe9QfdncwoEpqJtGq7l6aVDTofX5Be5b03MQFJr4cflgllqW77QZ84D9O9qWF\/vNDAofXcwrzT04CxLDhyQgTCgYUnRjG9pnuP\/gtbDKbTjRvxhTyfg3T0Phv1+XAvpTPnH2q5A+1+LmiqziUJ1sMipsKo+jQP614eCi9qjmqhHIgLRgcuOBvsi4g5WUcdcAIZ6qLt5gm2Y3r6rKNVchosU9ZydMUTfjuejDbVwE2fNH5UUnV57fQBxwg9CfX7iFHqKv1bfv5Zviu12paShgWCB12uR3iH\/3lmTJn8K5Xqit3G4eymFaJ5IChdUThBp\/jhQSI2r8sPcZDYSJ\/UZKuFnezFdKhEBd5hMXe8aKAd6ijGDjLARksFuqpi1sS8llC5K1Q+DzktSL\/o64TY4Vuvykiwe\/BAk2SkL9voOtrvU7vfDBcuCPbDJnSBBC0ESpcXeClTBIn6xZ9WaxqoS7sinE\/kUwtWsRd04I7d79\/ouotyNb+mBhTuRsZT12p\/gn4JHXXNUAIpTwchYzGxbfNJ4kxnYBFZWVmvsSqOLFZu1yi5BP3ktA9yhFyWIa5659azRFEKRdXpVHtQVa4IgdhxEqA=",
698 698
     "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
699 699
 }';
700
-		$this->fileAccessHelper
701
-			->expects($this->exactly(2))
702
-			->method('file_get_contents')
703
-			->willReturnMap([
704
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithValidModifiedContent/core/signature.json', $signatureDataFile],
705
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithValidModifiedContent/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
706
-			]);
707
-
708
-		$this->assertSame([], $this->checker->verifyCoreSignature());
709
-	}
710
-
711
-	/**
712
-	 * See inline instruction on how to update the test assets when changing mimetypealiases.dist.json
713
-	 */
714
-	public function testVerifyCoreSignatureWithModifiedMimetypelistSignatureData(): void {
715
-		$shippedMimetypeAliases = (array)json_decode(file_get_contents(\OC::$SERVERROOT . '/resources/config/mimetypealiases.dist.json'));
716
-		$shippedMimetypeNames = (array)json_decode(file_get_contents(\OC::$SERVERROOT . '/resources/config/mimetypenames.dist.json'));
717
-		$allAliases = array_merge($shippedMimetypeAliases, ['my-custom/mimetype' => 'custom']);
718
-		$allMimetypeNames = array_merge($shippedMimetypeNames, ['my-custom/mimetype' => 'Custom Document']);
719
-
720
-		$this->mimeTypeDetector
721
-			->method('getOnlyDefaultAliases')
722
-			->willReturn($shippedMimetypeAliases);
723
-
724
-		$this->mimeTypeDetector
725
-			->method('getAllAliases')
726
-			->willReturn($allAliases);
727
-
728
-		$this->mimeTypeDetector
729
-			->method('getAllNamings')
730
-			->willReturn($allMimetypeNames);
731
-
732
-		$oldMimetypeList = new GenerateMimetypeFileBuilder();
733
-		$all = $this->mimeTypeDetector->getAllAliases();
734
-		$namings = $this->mimeTypeDetector->getAllNamings();
735
-		$newFile = $oldMimetypeList->generateFile($all, $namings);
736
-
737
-		// When updating the mimetype list the test assets need to be updated as well
738
-		// 1. Update core/js/mimetypelist.js with the new generated js by running the test with the next line uncommented:
739
-		// file_put_contents(\OC::$SERVERROOT . '/tests/data/integritycheck/mimetypeListModified/core/js/mimetypelist.js', $newFile);
740
-		// 2. Update signature.json using the following occ command:
741
-		// occ integrity:sign-core --privateKey=./tests/data/integritycheck/core.key --certificate=./tests/data/integritycheck/core.crt --path=./tests/data/integritycheck/mimetypeListModified
742
-		self::assertEquals($newFile, file_get_contents(\OC::$SERVERROOT . '/tests/data/integritycheck/mimetypeListModified/core/js/mimetypelist.js'));
743
-
744
-		$this->serverVersion
745
-			->expects($this->once())
746
-			->method('getChannel')
747
-			->willReturn('stable');
748
-		$this->config
749
-			->expects($this->any())
750
-			->method('getSystemValueBool')
751
-			->with('integrity.check.disabled', false)
752
-			->willReturn(false);
753
-
754
-		$this->environmentHelper
755
-			->expects($this->any())
756
-			->method('getServerRoot')
757
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/mimetypeListModified');
758
-
759
-		$signatureDataFile = file_get_contents(__DIR__ . '/../../data/integritycheck/mimetypeListModified/core/signature.json');
760
-		$this->fileAccessHelper
761
-			->method('file_get_contents')
762
-			->willReturnMap([
763
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/mimetypeListModified/core/signature.json', $signatureDataFile],
764
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/mimetypeListModified/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
765
-			]);
766
-
767
-		$this->assertSame([], $this->checker->verifyCoreSignature());
768
-	}
769
-
770
-	public function testVerifyCoreSignatureWithValidSignatureDataAndNotAlphabeticOrder(): void {
771
-		$this->serverVersion
772
-			->expects($this->once())
773
-			->method('getChannel')
774
-			->willReturn('stable');
775
-		$this->config
776
-			->expects($this->any())
777
-			->method('getSystemValueBool')
778
-			->with('integrity.check.disabled', false)
779
-			->willReturn(false);
780
-
781
-		$this->environmentHelper
782
-			->expects($this->any())
783
-			->method('getServerRoot')
784
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
785
-		$signatureDataFile = '{
700
+        $this->fileAccessHelper
701
+            ->expects($this->exactly(2))
702
+            ->method('file_get_contents')
703
+            ->willReturnMap([
704
+                [\OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithValidModifiedContent/core/signature.json', $signatureDataFile],
705
+                [\OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithValidModifiedContent/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
706
+            ]);
707
+
708
+        $this->assertSame([], $this->checker->verifyCoreSignature());
709
+    }
710
+
711
+    /**
712
+     * See inline instruction on how to update the test assets when changing mimetypealiases.dist.json
713
+     */
714
+    public function testVerifyCoreSignatureWithModifiedMimetypelistSignatureData(): void {
715
+        $shippedMimetypeAliases = (array)json_decode(file_get_contents(\OC::$SERVERROOT . '/resources/config/mimetypealiases.dist.json'));
716
+        $shippedMimetypeNames = (array)json_decode(file_get_contents(\OC::$SERVERROOT . '/resources/config/mimetypenames.dist.json'));
717
+        $allAliases = array_merge($shippedMimetypeAliases, ['my-custom/mimetype' => 'custom']);
718
+        $allMimetypeNames = array_merge($shippedMimetypeNames, ['my-custom/mimetype' => 'Custom Document']);
719
+
720
+        $this->mimeTypeDetector
721
+            ->method('getOnlyDefaultAliases')
722
+            ->willReturn($shippedMimetypeAliases);
723
+
724
+        $this->mimeTypeDetector
725
+            ->method('getAllAliases')
726
+            ->willReturn($allAliases);
727
+
728
+        $this->mimeTypeDetector
729
+            ->method('getAllNamings')
730
+            ->willReturn($allMimetypeNames);
731
+
732
+        $oldMimetypeList = new GenerateMimetypeFileBuilder();
733
+        $all = $this->mimeTypeDetector->getAllAliases();
734
+        $namings = $this->mimeTypeDetector->getAllNamings();
735
+        $newFile = $oldMimetypeList->generateFile($all, $namings);
736
+
737
+        // When updating the mimetype list the test assets need to be updated as well
738
+        // 1. Update core/js/mimetypelist.js with the new generated js by running the test with the next line uncommented:
739
+        // file_put_contents(\OC::$SERVERROOT . '/tests/data/integritycheck/mimetypeListModified/core/js/mimetypelist.js', $newFile);
740
+        // 2. Update signature.json using the following occ command:
741
+        // occ integrity:sign-core --privateKey=./tests/data/integritycheck/core.key --certificate=./tests/data/integritycheck/core.crt --path=./tests/data/integritycheck/mimetypeListModified
742
+        self::assertEquals($newFile, file_get_contents(\OC::$SERVERROOT . '/tests/data/integritycheck/mimetypeListModified/core/js/mimetypelist.js'));
743
+
744
+        $this->serverVersion
745
+            ->expects($this->once())
746
+            ->method('getChannel')
747
+            ->willReturn('stable');
748
+        $this->config
749
+            ->expects($this->any())
750
+            ->method('getSystemValueBool')
751
+            ->with('integrity.check.disabled', false)
752
+            ->willReturn(false);
753
+
754
+        $this->environmentHelper
755
+            ->expects($this->any())
756
+            ->method('getServerRoot')
757
+            ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/mimetypeListModified');
758
+
759
+        $signatureDataFile = file_get_contents(__DIR__ . '/../../data/integritycheck/mimetypeListModified/core/signature.json');
760
+        $this->fileAccessHelper
761
+            ->method('file_get_contents')
762
+            ->willReturnMap([
763
+                [\OC::$SERVERROOT . '/tests/data/integritycheck/mimetypeListModified/core/signature.json', $signatureDataFile],
764
+                [\OC::$SERVERROOT . '/tests/data/integritycheck/mimetypeListModified/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
765
+            ]);
766
+
767
+        $this->assertSame([], $this->checker->verifyCoreSignature());
768
+    }
769
+
770
+    public function testVerifyCoreSignatureWithValidSignatureDataAndNotAlphabeticOrder(): void {
771
+        $this->serverVersion
772
+            ->expects($this->once())
773
+            ->method('getChannel')
774
+            ->willReturn('stable');
775
+        $this->config
776
+            ->expects($this->any())
777
+            ->method('getSystemValueBool')
778
+            ->with('integrity.check.disabled', false)
779
+            ->willReturn(false);
780
+
781
+        $this->environmentHelper
782
+            ->expects($this->any())
783
+            ->method('getServerRoot')
784
+            ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
785
+        $signatureDataFile = '{
786 786
     "hashes": {
787 787
         "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
788 788
         "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
@@ -790,33 +790,33 @@  discard block
 block discarded – undo
790 790
     "signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
791 791
     "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
792 792
 }';
793
-		$this->fileAccessHelper
794
-			->expects($this->exactly(2))
795
-			->method('file_get_contents')
796
-			->willReturnMap([
797
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json', $signatureDataFile],
798
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/app//resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
799
-			]);
800
-
801
-		$this->assertSame([], $this->checker->verifyCoreSignature());
802
-	}
803
-
804
-	public function testVerifyCoreSignatureWithTamperedSignatureData(): void {
805
-		$this->serverVersion
806
-			->expects($this->once())
807
-			->method('getChannel')
808
-			->willReturn('stable');
809
-		$this->config
810
-			->expects($this->any())
811
-			->method('getSystemValueBool')
812
-			->with('integrity.check.disabled', false)
813
-			->willReturn(false);
814
-
815
-		$this->environmentHelper
816
-			->expects($this->any())
817
-			->method('getServerRoot')
818
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/');
819
-		$signatureDataFile = '{
793
+        $this->fileAccessHelper
794
+            ->expects($this->exactly(2))
795
+            ->method('file_get_contents')
796
+            ->willReturnMap([
797
+                [\OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json', $signatureDataFile],
798
+                [\OC::$SERVERROOT . '/tests/data/integritycheck/app//resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
799
+            ]);
800
+
801
+        $this->assertSame([], $this->checker->verifyCoreSignature());
802
+    }
803
+
804
+    public function testVerifyCoreSignatureWithTamperedSignatureData(): void {
805
+        $this->serverVersion
806
+            ->expects($this->once())
807
+            ->method('getChannel')
808
+            ->willReturn('stable');
809
+        $this->config
810
+            ->expects($this->any())
811
+            ->method('getSystemValueBool')
812
+            ->with('integrity.check.disabled', false)
813
+            ->willReturn(false);
814
+
815
+        $this->environmentHelper
816
+            ->expects($this->any())
817
+            ->method('getServerRoot')
818
+            ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/');
819
+        $signatureDataFile = '{
820 820
     "hashes": {
821 821
         "AnotherFile.txt": "tampered",
822 822
         "subfolder\/file.txt": "tampered"
@@ -824,39 +824,39 @@  discard block
 block discarded – undo
824 824
     "signature": "eXesvDm3pkek12xSwMG10y9suRES79Nye3jYNe5KYq1tTUPqRRNgxmMGAfcUro0zpLeAr2YgHeSMWtglblGOW7pmwGVPZ0O1Y4r1fE6jnep0kW+35PLIaqCorIOnCAtSzDNKBhwd1ow3zW2wC0DFouuEkIO8u5Fw28g8E8dp8zEk1xMblNPy+xtWkmYHrVJ\/dQgun1bYOF2ZFtAzatwndTI\/bGsy1i3Wsl+x6HyWKQdq8y8VObtOqKDH7uERBEpB9DHVyKflj1v1gQuEH6BhaRdATc7ee0MiQdGblraIySwYRdfo2d8i82OVKrenMB3SLwyCvDPyQ9iKpTOnSF52ZBqaqSXKM2N\/RAkweeBFQQCwcHhqxvB0cfbyHcbkOLeCZe\/tsh68IxwTiYgzvLfl7sOZ5arnZbzrPpZmB+hfV2omkoJ1tDwOWz9hEmLLNtfo2OxyUH1m0+XFaC+Gbn4WkVDgf7YZkwUcG+Qoa3oKDNMss8MEyZxewl2iDGZcf402dlidHRprlfmXbAYuVQ08\/a0HxIKYPGh\/nsMGmwnO15CWtFpAbhUA\/D5oRjsIxnvXaMDg0iAFpdu\/5Ffsj7g3EPdBkiQHNYK7YU1RRx609eH0bZyiIYHdUPw7ikLupvrebZmELqi3mqDFO99u4eISlxFJlUbUND3L4BtmWTWrKwI=",
825 825
     "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
826 826
 }';
827
-		$this->fileAccessHelper
828
-			->expects($this->exactly(2))
829
-			->method('file_get_contents')
830
-			->willReturnMap([
831
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//core/signature.json', $signatureDataFile],
832
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
833
-			]);
834
-
835
-		$expected = [
836
-			'EXCEPTION' => [
837
-				'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
838
-				'message' => 'Signature could not get verified.',
839
-			]
840
-		];
841
-		$this->assertSame($expected, $this->checker->verifyCoreSignature());
842
-	}
843
-
844
-	public function testVerifyCoreSignatureWithTamperedFiles(): void {
845
-		$this->serverVersion
846
-			->expects($this->once())
847
-			->method('getChannel')
848
-			->willReturn('stable');
849
-		$this->config
850
-			->expects($this->any())
851
-			->method('getSystemValueBool')
852
-			->with('integrity.check.disabled', false)
853
-			->willReturn(false);
854
-
855
-		$this->environmentHelper
856
-			->expects($this->any())
857
-			->method('getServerRoot')
858
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/');
859
-		$signatureDataFile = '{
827
+        $this->fileAccessHelper
828
+            ->expects($this->exactly(2))
829
+            ->method('file_get_contents')
830
+            ->willReturnMap([
831
+                [\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//core/signature.json', $signatureDataFile],
832
+                [\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
833
+            ]);
834
+
835
+        $expected = [
836
+            'EXCEPTION' => [
837
+                'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
838
+                'message' => 'Signature could not get verified.',
839
+            ]
840
+        ];
841
+        $this->assertSame($expected, $this->checker->verifyCoreSignature());
842
+    }
843
+
844
+    public function testVerifyCoreSignatureWithTamperedFiles(): void {
845
+        $this->serverVersion
846
+            ->expects($this->once())
847
+            ->method('getChannel')
848
+            ->willReturn('stable');
849
+        $this->config
850
+            ->expects($this->any())
851
+            ->method('getSystemValueBool')
852
+            ->with('integrity.check.disabled', false)
853
+            ->willReturn(false);
854
+
855
+        $this->environmentHelper
856
+            ->expects($this->any())
857
+            ->method('getServerRoot')
858
+            ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/');
859
+        $signatureDataFile = '{
860 860
     "hashes": {
861 861
         "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
862 862
         "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
@@ -864,54 +864,54 @@  discard block
 block discarded – undo
864 864
     "signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
865 865
     "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
866 866
 }';
867
-		$this->fileAccessHelper
868
-			->expects($this->exactly(2))
869
-			->method('file_get_contents')
870
-			->willReturnMap([
871
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//core/signature.json', $signatureDataFile],
872
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
873
-			]);
874
-
875
-		$expected = [
876
-			'INVALID_HASH' => [
877
-				'AnotherFile.txt' => [
878
-					'expected' => '1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112',
879
-					'current' => '7322348ba269c6d5522efe02f424fa3a0da319a7cd9c33142a5afe32a2d9af2da3a411f086fcfc96ff4301ea566f481dba0960c2abeef3594c4d930462f6584c',
880
-				],
881
-			],
882
-			'FILE_MISSING' => [
883
-				'subfolder/file.txt' => [
884
-					'expected' => '410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b',
885
-					'current' => '',
886
-				],
887
-			],
888
-			'EXTRA_FILE' => [
889
-				'UnecessaryFile' => [
890
-					'expected' => '',
891
-					'current' => 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e',
892
-				],
893
-			],
894
-
895
-		];
896
-		$this->assertSame($expected, $this->checker->verifyCoreSignature());
897
-	}
898
-
899
-	public function testVerifyCoreWithInvalidCertificate(): void {
900
-		$this->serverVersion
901
-			->expects($this->once())
902
-			->method('getChannel')
903
-			->willReturn('stable');
904
-		$this->config
905
-			->expects($this->any())
906
-			->method('getSystemValueBool')
907
-			->with('integrity.check.disabled', false)
908
-			->willReturn(false);
909
-
910
-		$this->environmentHelper
911
-			->expects($this->any())
912
-			->method('getServerRoot')
913
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
914
-		$signatureDataFile = '{
867
+        $this->fileAccessHelper
868
+            ->expects($this->exactly(2))
869
+            ->method('file_get_contents')
870
+            ->willReturnMap([
871
+                [\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//core/signature.json', $signatureDataFile],
872
+                [\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
873
+            ]);
874
+
875
+        $expected = [
876
+            'INVALID_HASH' => [
877
+                'AnotherFile.txt' => [
878
+                    'expected' => '1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112',
879
+                    'current' => '7322348ba269c6d5522efe02f424fa3a0da319a7cd9c33142a5afe32a2d9af2da3a411f086fcfc96ff4301ea566f481dba0960c2abeef3594c4d930462f6584c',
880
+                ],
881
+            ],
882
+            'FILE_MISSING' => [
883
+                'subfolder/file.txt' => [
884
+                    'expected' => '410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b',
885
+                    'current' => '',
886
+                ],
887
+            ],
888
+            'EXTRA_FILE' => [
889
+                'UnecessaryFile' => [
890
+                    'expected' => '',
891
+                    'current' => 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e',
892
+                ],
893
+            ],
894
+
895
+        ];
896
+        $this->assertSame($expected, $this->checker->verifyCoreSignature());
897
+    }
898
+
899
+    public function testVerifyCoreWithInvalidCertificate(): void {
900
+        $this->serverVersion
901
+            ->expects($this->once())
902
+            ->method('getChannel')
903
+            ->willReturn('stable');
904
+        $this->config
905
+            ->expects($this->any())
906
+            ->method('getSystemValueBool')
907
+            ->with('integrity.check.disabled', false)
908
+            ->willReturn(false);
909
+
910
+        $this->environmentHelper
911
+            ->expects($this->any())
912
+            ->method('getServerRoot')
913
+            ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
914
+        $signatureDataFile = '{
915 915
     "hashes": {
916 916
         "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
917 917
         "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
@@ -919,39 +919,39 @@  discard block
 block discarded – undo
919 919
     "signature": "eXesvDm3pkek12xSwMG10y9suRES79Nye3jYNe5KYq1tTUPqRRNgxmMGAfcUro0zpLeAr2YgHeSMWtglblGOW7pmwGVPZ0O1Y4r1fE6jnep0kW+35PLIaqCorIOnCAtSzDNKBhwd1ow3zW2wC0DFouuEkIO8u5Fw28g8E8dp8zEk1xMblNPy+xtWkmYHrVJ\/dQgun1bYOF2ZFtAzatwndTI\/bGsy1i3Wsl+x6HyWKQdq8y8VObtOqKDH7uERBEpB9DHVyKflj1v1gQuEH6BhaRdATc7ee0MiQdGblraIySwYRdfo2d8i82OVKrenMB3SLwyCvDPyQ9iKpTOnSF52ZBqaqSXKM2N\/RAkweeBFQQCwcHhqxvB0cfbyHcbkOLeCZe\/tsh68IxwTiYgzvLfl7sOZ5arnZbzrPpZmB+hfV2omkoJ1tDwOWz9hEmLLNtfo2OxyUH1m0+XFaC+Gbn4WkVDgf7YZkwUcG+Qoa3oKDNMss8MEyZxewl2iDGZcf402dlidHRprlfmXbAYuVQ08\/a0HxIKYPGh\/nsMGmwnO15CWtFpAbhUA\/D5oRjsIxnvXaMDg0iAFpdu\/5Ffsj7g3EPdBkiQHNYK7YU1RRx609eH0bZyiIYHdUPw7ikLupvrebZmELqi3mqDFO99u4eISlxFJlUbUND3L4BtmWTWrKwI=",
920 920
     "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUPYoweUxCPqbDW4ntuh7QvgyqSrgwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIyNDIwNloXDTE2MTEwMzIyNDIwNlowDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJui3nDbjOIjxNnthdBZplphujsN6u8K\r\nQ\/62zAuSwzXVp0+3IMgM\/2sepklVE8YfCyVJ5+SUJqnqHoUWVRVfs8jL0wW6nrHM\r\n\/lsscAguWCee4iAdNOqI9kq4+DUau8J45e62XA9mrAo\/8\/NKzFE2y2WduDoQZcm+\r\n8+dwcUUHXw2jl8dfrmvEMYSqTNDdb4rGmQpeV+dr9BLqr+x03U1Q08qCG9j7mSOz\r\ncvJENjOvC5uzAh5LCuCgxqG4o+mPzB0FtNnwoRRu6IsF3Y3KacRqPc30fB\/iXDn5\r\nBPr14uNxTTYWoZJ1F0tZrLzRbXdjJJOC+dnQurTtXWZ8WjPB1BWQYK7fW6t82mkN\r\n2Qe2xen99gs9nX5yY\/sHM3TKSJdM7AVCEv\/emW3gNjkvWTtRlN\/Nc7X2ckNwXcvo\r\n0yi3fSPjzXpDgLbhp1FzrMlHDn1VzmRT3r8wLByWa\/hsxrJDsBzwunMJYhXhmeKb\r\n3wX0tN\/EUJTWBntpwVOIGnRPD51oBoQUOMaEAq\/kz8PgN181bWZkJbRuf+FWkijQ\r\no+HR2lVF1jWXXst5Uc+s9HN81Uly7X4O9MMg0QxT4+wymtGDs6AOkwMi9rgBTrRB\r\n3tLU3XL2UIwRXgmd8cPtTu\/I6Bm7LdyaYtZ3yJTxRewq3nZdWypqBhD8uhpIYVkf\r\no4bxmGkVAQVTAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAKKAX5EHgU1grODnJ0of\r\nspFpgB1K67YvclNUyuU6NQ6zBJx1\/w1RnM7uxLcxiiWj1BbUhwZQ0ojmEHeUyi6O\r\nGrDVajwhTccDMmja3u5adhEncx65\/H+lD85IPRRkS2qBDssMDdJHhZ0uI+40nI7M\r\nMq1kFjl+6wiuqZXqps66DuLbk45g\/ZlrFIrIo3Ix5vj0OVqwT+gO4LYirJK6KgVS\r\nUttbcEsc\/yKU9ThnM8\/n4m2jstZXfzKPgOsJrQcZrFOtpj+CWmBzVElBSPlDT3Nh\r\nHSgOeTFJ8bQBxj2iG5dLA+JZJQKxyJ1gy2ZtxIJ2GyvLtSe8NUSqvfPWOaAKEUV2\r\ngniytnEFLr+PcD+9EGux6jZNuj6HmtWVThTfD5VGFmtlVU2z71ZRYY0kn6J3mmFc\r\nS2ecEcCUwqG5YNLncEUCyZhC2klWql2SHyGctCEyWWY7ikIDjVzYt2EbcFvLNBnP\r\ntybN1TYHRRZxlug00CCoOE9EZfk46FkZpDvU6KmqJRofkNZ5sj+SffyGcwYwNrDH\r\nKqe8m+9lHf3CRTIDeMu8r2xl1I6M6ZZfjabbmVP9Jd6WN4s6f1FlXDWzhlT1N0Qw\r\nGzJj6xB+SPtS3UV05tBlvbfA4e06D5G9uD7Q8ONcINtMS0xsSJ2oo82AqlpvlF\/q\r\noj7YKHsaTVGA+FxBktZHfoxD\r\n-----END CERTIFICATE-----"
921 921
 }';
922
-		$this->fileAccessHelper
923
-			->expects($this->exactly(2))
924
-			->method('file_get_contents')
925
-			->willReturnMap([
926
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json', $signatureDataFile],
927
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/app//resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
928
-			]);
929
-
930
-		$expected = [
931
-			'EXCEPTION' => [
932
-				'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
933
-				'message' => 'Certificate is not valid.',
934
-			]
935
-		];
936
-		$this->assertSame($expected, $this->checker->verifyCoreSignature());
937
-	}
938
-
939
-	public function testVerifyCoreWithDifferentScope(): void {
940
-		$this->serverVersion
941
-			->expects($this->once())
942
-			->method('getChannel')
943
-			->willReturn('stable');
944
-		$this->config
945
-			->expects($this->any())
946
-			->method('getSystemValueBool')
947
-			->with('integrity.check.disabled', false)
948
-			->willReturn(false);
949
-
950
-		$this->environmentHelper
951
-			->expects($this->any())
952
-			->method('getServerRoot')
953
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
954
-		$signatureDataFile = '{
922
+        $this->fileAccessHelper
923
+            ->expects($this->exactly(2))
924
+            ->method('file_get_contents')
925
+            ->willReturnMap([
926
+                [\OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json', $signatureDataFile],
927
+                [\OC::$SERVERROOT . '/tests/data/integritycheck/app//resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
928
+            ]);
929
+
930
+        $expected = [
931
+            'EXCEPTION' => [
932
+                'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
933
+                'message' => 'Certificate is not valid.',
934
+            ]
935
+        ];
936
+        $this->assertSame($expected, $this->checker->verifyCoreSignature());
937
+    }
938
+
939
+    public function testVerifyCoreWithDifferentScope(): void {
940
+        $this->serverVersion
941
+            ->expects($this->once())
942
+            ->method('getChannel')
943
+            ->willReturn('stable');
944
+        $this->config
945
+            ->expects($this->any())
946
+            ->method('getSystemValueBool')
947
+            ->with('integrity.check.disabled', false)
948
+            ->willReturn(false);
949
+
950
+        $this->environmentHelper
951
+            ->expects($this->any())
952
+            ->method('getServerRoot')
953
+            ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
954
+        $signatureDataFile = '{
955 955
     "hashes": {
956 956
         "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
957 957
         "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
@@ -959,155 +959,155 @@  discard block
 block discarded – undo
959 959
     "signature": "EL49UaSeyMAqyMtqId+tgOhhwgOevPZsRLX4j2blnybAB6fN07z0936JqZV7+eMPiE30Idx+UCY6rCFN531Kqe9vAOCdgtHUSOjjKyKc+lvULESlMb6YQcrZrvDlEMMjzjH49ewG7Ai8sNN6HrRUd9U8ws+ewSkW2DOOBItj\/21RBnkrSt+2AtGXGigEvuTm57HrCYDj8\/lSkumC2GVkjLUHeLOKYo4PRNOr6yP5mED5v7zo66AWvXl2fKv54InZcdxsAk35lyK9DGZbk\/027ZRd0AOHT3LImRLvQ+8EAg3XLlRUy0hOFGgPC+jYonMzgYvsAXAXi2j8LnLJlsLwpFwu1k1B+kZVPMumKZvP9OvJb70EirecXmz62V+Jiyuaq7ne4y7Kp5gKZT\/T8SeZ0lFtCmPfYyzBB0y8s5ldmTTmdVYHs54t\/OCCW82HzQZxnFNPzDTRa8HglsaMKrqPtW59+R4UvRKSWhB8M\/Ah57qgzycvPV4KMz\/FbD4l\/\/9chRKSlCfc2k3b8ZSHNmi+EzCKgJjWIoKdgN1yax94puU8jfn8UW+G7H9Y1Jsf\/jox6QLyYEgtV1vOHY2xLT7fVs2vhyvkN2MNjJnmQ70gFG5Qz2lBz5wi6ZpB+tOfCcpbLxWAkoWoIrmC\/Ilqh7mfmRZ43g5upjkepHNd93ONuY8=",
960 960
     "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEwTCCAqmgAwIBAgIUWv0iujufs5lUr0svCf\/qTQvoyKAwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIyNDk1M1oXDTE2MTEwMzIyNDk1M1owEjEQMA4GA1UEAwwHU29tZUFwcDCCAiIw\r\nDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK8q0x62agGSRBqeWsaeEwFfepMk\r\nF8cAobMMi50qHCv9IrOn\/ZH9l52xBrbIkErVmRjmly0d4JhD8Ymhidsh9ONKYl\/j\r\n+ishsZDM8eNNdp3Ew+fEYVvY1W7mR1qU24NWj0bzVsClI7hvPVIuw7AjfBDq1C5+\r\nA+ZSLSXYvOK2cEWjdxQfuNZwEZSjmA63DUllBIrm35IaTvfuyhU6BW9yHZxmb8+M\r\nw0xDv30D5UkE\/2N7Pa\/HQJLxCR+3zKibRK3nUyRDLSXxMkU9PnFNaPNX59VPgyj4\r\nGB1CFSToldJVPF4pzh7p36uGXZVxs8m3LFD4Ol8mhi7jkxDZjqFN46gzR0r23Py6\r\ndol9vfawGIoUwp9LvL0S7MvdRY0oazLXwClLP4OQ17zpSMAiCj7fgNT661JamPGj\r\nt5O7Zn2wA7I4ddDS\/HDTWCu98Zwc9fHIpsJPgCZ9awoqxi4Mnf7Pk9g5nnXhszGC\r\ncxxIASQKM+GhdzoRxKknax2RzUCwCzcPRtCj8AQT\/x\/mqN3PfRmlnFBNACUw9bpZ\r\nSOoNq2pCF9igftDWpSIXQ38pVpKLWowjjg3DVRmVKBgivHnUnVLyzYBahHPj0vaz\r\ntFtUFRaqXDnt+4qyUGyrT5h5pjZaTcHIcSB4PiarYwdVvgslgwnQzOUcGAzRWBD4\r\n6jV2brP5vFY3g6iPAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBACTY3CCHC+Z28gCf\r\nFWGKQ3wAKs+k4+0yoti0qm2EKX7rSGQ0PHSas6uW79WstC4Rj+DYkDtIhGMSg8FS\r\nHVGZHGBCc0HwdX+BOAt3zi4p7Sf3oQef70\/4imPoKxbAVCpd\/cveVcFyDC19j1yB\r\nBapwu87oh+muoeaZxOlqQI4UxjBlR\/uRSMhOn2UGauIr3dWJgAF4pGt7TtIzt+1v\r\n0uA6FtN1Y4R5O8AaJPh1bIG0CVvFBE58esGzjEYLhOydgKFnEP94kVPgJD5ds9C3\r\npPhEpo1dRpiXaF7WGIV1X6DI\/ipWvfrF7CEy6I\/kP1InY\/vMDjQjeDnJ\/VrXIWXO\r\nyZvHXVaN\/m+1RlETsH7YO\/QmxRue9ZHN3gvvWtmpCeA95sfpepOk7UcHxHZYyQbF\r\n49\/au8j+5tsr4A83xzsT1JbcKRxkAaQ7WDJpOnE5O1+H0fB+BaLakTg6XX9d4Fo7\r\n7Gin7hVWX7pL+JIyxMzME3LhfI61+CRcqZQIrpyaafUziPQbWIPfEs7h8tCOWyvW\r\nUO8ZLervYCB3j44ivkrxPlcBklDCqqKKBzDP9dYOtS\/P4RB1NkHA9+NTvmBpTonS\r\nSFXdg9fFMD7VfjDE3Vnk+8DWkVH5wBYowTAD7w9Wuzr7DumiAULexnP\/Y7xwxLv7\r\n4B+pXTAcRK0zECDEaX3npS8xWzrB\r\n-----END CERTIFICATE-----"
961 961
 }';
962
-		$this->fileAccessHelper
963
-			->expects($this->exactly(2))
964
-			->method('file_get_contents')
965
-			->willReturnMap([
966
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json', $signatureDataFile],
967
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/app//resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
968
-			]);
969
-
970
-		$expected = [
971
-			'EXCEPTION' => [
972
-				'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
973
-				'message' => 'Certificate is not valid for required scope. (Requested: core, current: CN=SomeApp)',
974
-			]
975
-		];
976
-		$this->assertSame($expected, $this->checker->verifyCoreSignature());
977
-	}
978
-
979
-	public function testRunInstanceVerification(): void {
980
-		$this->checker = $this->getMockBuilder('\OC\IntegrityCheck\Checker')
981
-			->setConstructorArgs([
982
-				$this->serverVersion,
983
-				$this->environmentHelper,
984
-				$this->fileAccessHelper,
985
-				$this->appLocator,
986
-				$this->config,
987
-				$this->appConfig,
988
-				$this->cacheFactory,
989
-				$this->appManager,
990
-				$this->mimeTypeDetector,
991
-			])
992
-			->onlyMethods([
993
-				'verifyCoreSignature',
994
-				'verifyAppSignature',
995
-			])
996
-			->getMock();
997
-
998
-		$this->checker
999
-			->expects($this->once())
1000
-			->method('verifyCoreSignature');
1001
-		$this->appManager
1002
-			->expects($this->once())
1003
-			->method('getAllAppsInAppsFolders')
1004
-			->willReturn([
1005
-				'files',
1006
-				'calendar',
1007
-				'contacts',
1008
-				'dav',
1009
-			]);
1010
-		$this->appManager
1011
-			->expects($this->exactly(4))
1012
-			->method('isShipped')
1013
-			->willReturnMap([
1014
-				['files', true],
1015
-				['calendar', false],
1016
-				['contacts', false],
1017
-				['dav', true],
1018
-			]);
1019
-
1020
-		$calls = [
1021
-			'files',
1022
-			'calendar',
1023
-			'dav',
1024
-		];
1025
-		$this->checker
1026
-			->expects($this->exactly(3))
1027
-			->method('verifyAppSignature')
1028
-			->willReturnCallback(function ($app) use (&$calls) {
1029
-				$expected = array_shift($calls);
1030
-				$this->assertSame($expected, $app);
1031
-				return [];
1032
-			});
1033
-		$this->appLocator
1034
-			->expects($this->exactly(2))
1035
-			->method('getAppPath')
1036
-			->willReturnMap([
1037
-				['calendar', '/apps/calendar'],
1038
-				['contacts', '/apps/contacts'],
1039
-			]);
1040
-		$this->fileAccessHelper
1041
-			->expects($this->exactly(2))
1042
-			->method('file_exists')
1043
-			->willReturnMap([
1044
-				['/apps/calendar/appinfo/signature.json', true],
1045
-				['/apps/contacts/appinfo/signature.json', false],
1046
-			]);
1047
-		$this->appConfig
1048
-			->expects($this->once())
1049
-			->method('deleteKey')
1050
-			->with('core', 'oc.integritycheck.checker');
1051
-
1052
-		$this->checker->runInstanceVerification();
1053
-	}
1054
-
1055
-	public function testVerifyAppSignatureWithoutSignatureDataAndCodeCheckerDisabled(): void {
1056
-		$this->serverVersion
1057
-			->expects($this->once())
1058
-			->method('getChannel')
1059
-			->willReturn('stable');
1060
-		$this->config
1061
-			->expects($this->any())
1062
-			->method('getSystemValueBool')
1063
-			->with('integrity.check.disabled', false)
1064
-			->willReturn(true);
1065
-
1066
-		$expected = [];
1067
-		$this->assertSame($expected, $this->checker->verifyAppSignature('SomeApp'));
1068
-	}
1069
-
1070
-	public static function channelDataProvider(): array {
1071
-		return [
1072
-			['stable', true],
1073
-			['git', false],
1074
-		];
1075
-	}
1076
-
1077
-	/**
1078
-	 * @param string $channel
1079
-	 * @param bool $isCodeSigningEnforced
1080
-	 * @dataProvider channelDataProvider
1081
-	 */
1082
-	public function testIsCodeCheckEnforced($channel, $isCodeSigningEnforced): void {
1083
-		$this->serverVersion
1084
-			->expects($this->once())
1085
-			->method('getChannel')
1086
-			->willReturn($channel);
1087
-		$this->config
1088
-			->expects($this->any())
1089
-			->method('getSystemValueBool')
1090
-			->with('integrity.check.disabled', false)
1091
-			->willReturn(false);
1092
-
1093
-		$this->assertSame($isCodeSigningEnforced, $this->checker->isCodeCheckEnforced());
1094
-	}
1095
-
1096
-	/**
1097
-	 * @param string $channel
1098
-	 * @dataProvider channelDataProvider
1099
-	 */
1100
-	public function testIsCodeCheckEnforcedWithDisabledConfigSwitch($channel): void {
1101
-		$this->serverVersion
1102
-			->expects($this->once())
1103
-			->method('getChannel')
1104
-			->willReturn($channel);
1105
-		$this->config
1106
-			->expects($this->any())
1107
-			->method('getSystemValueBool')
1108
-			->with('integrity.check.disabled', false)
1109
-			->willReturn(true);
1110
-
1111
-		$this->assertFalse(self::invokePrivate($this->checker, 'isCodeCheckEnforced'));
1112
-	}
962
+        $this->fileAccessHelper
963
+            ->expects($this->exactly(2))
964
+            ->method('file_get_contents')
965
+            ->willReturnMap([
966
+                [\OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json', $signatureDataFile],
967
+                [\OC::$SERVERROOT . '/tests/data/integritycheck/app//resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
968
+            ]);
969
+
970
+        $expected = [
971
+            'EXCEPTION' => [
972
+                'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
973
+                'message' => 'Certificate is not valid for required scope. (Requested: core, current: CN=SomeApp)',
974
+            ]
975
+        ];
976
+        $this->assertSame($expected, $this->checker->verifyCoreSignature());
977
+    }
978
+
979
+    public function testRunInstanceVerification(): void {
980
+        $this->checker = $this->getMockBuilder('\OC\IntegrityCheck\Checker')
981
+            ->setConstructorArgs([
982
+                $this->serverVersion,
983
+                $this->environmentHelper,
984
+                $this->fileAccessHelper,
985
+                $this->appLocator,
986
+                $this->config,
987
+                $this->appConfig,
988
+                $this->cacheFactory,
989
+                $this->appManager,
990
+                $this->mimeTypeDetector,
991
+            ])
992
+            ->onlyMethods([
993
+                'verifyCoreSignature',
994
+                'verifyAppSignature',
995
+            ])
996
+            ->getMock();
997
+
998
+        $this->checker
999
+            ->expects($this->once())
1000
+            ->method('verifyCoreSignature');
1001
+        $this->appManager
1002
+            ->expects($this->once())
1003
+            ->method('getAllAppsInAppsFolders')
1004
+            ->willReturn([
1005
+                'files',
1006
+                'calendar',
1007
+                'contacts',
1008
+                'dav',
1009
+            ]);
1010
+        $this->appManager
1011
+            ->expects($this->exactly(4))
1012
+            ->method('isShipped')
1013
+            ->willReturnMap([
1014
+                ['files', true],
1015
+                ['calendar', false],
1016
+                ['contacts', false],
1017
+                ['dav', true],
1018
+            ]);
1019
+
1020
+        $calls = [
1021
+            'files',
1022
+            'calendar',
1023
+            'dav',
1024
+        ];
1025
+        $this->checker
1026
+            ->expects($this->exactly(3))
1027
+            ->method('verifyAppSignature')
1028
+            ->willReturnCallback(function ($app) use (&$calls) {
1029
+                $expected = array_shift($calls);
1030
+                $this->assertSame($expected, $app);
1031
+                return [];
1032
+            });
1033
+        $this->appLocator
1034
+            ->expects($this->exactly(2))
1035
+            ->method('getAppPath')
1036
+            ->willReturnMap([
1037
+                ['calendar', '/apps/calendar'],
1038
+                ['contacts', '/apps/contacts'],
1039
+            ]);
1040
+        $this->fileAccessHelper
1041
+            ->expects($this->exactly(2))
1042
+            ->method('file_exists')
1043
+            ->willReturnMap([
1044
+                ['/apps/calendar/appinfo/signature.json', true],
1045
+                ['/apps/contacts/appinfo/signature.json', false],
1046
+            ]);
1047
+        $this->appConfig
1048
+            ->expects($this->once())
1049
+            ->method('deleteKey')
1050
+            ->with('core', 'oc.integritycheck.checker');
1051
+
1052
+        $this->checker->runInstanceVerification();
1053
+    }
1054
+
1055
+    public function testVerifyAppSignatureWithoutSignatureDataAndCodeCheckerDisabled(): void {
1056
+        $this->serverVersion
1057
+            ->expects($this->once())
1058
+            ->method('getChannel')
1059
+            ->willReturn('stable');
1060
+        $this->config
1061
+            ->expects($this->any())
1062
+            ->method('getSystemValueBool')
1063
+            ->with('integrity.check.disabled', false)
1064
+            ->willReturn(true);
1065
+
1066
+        $expected = [];
1067
+        $this->assertSame($expected, $this->checker->verifyAppSignature('SomeApp'));
1068
+    }
1069
+
1070
+    public static function channelDataProvider(): array {
1071
+        return [
1072
+            ['stable', true],
1073
+            ['git', false],
1074
+        ];
1075
+    }
1076
+
1077
+    /**
1078
+     * @param string $channel
1079
+     * @param bool $isCodeSigningEnforced
1080
+     * @dataProvider channelDataProvider
1081
+     */
1082
+    public function testIsCodeCheckEnforced($channel, $isCodeSigningEnforced): void {
1083
+        $this->serverVersion
1084
+            ->expects($this->once())
1085
+            ->method('getChannel')
1086
+            ->willReturn($channel);
1087
+        $this->config
1088
+            ->expects($this->any())
1089
+            ->method('getSystemValueBool')
1090
+            ->with('integrity.check.disabled', false)
1091
+            ->willReturn(false);
1092
+
1093
+        $this->assertSame($isCodeSigningEnforced, $this->checker->isCodeCheckEnforced());
1094
+    }
1095
+
1096
+    /**
1097
+     * @param string $channel
1098
+     * @dataProvider channelDataProvider
1099
+     */
1100
+    public function testIsCodeCheckEnforcedWithDisabledConfigSwitch($channel): void {
1101
+        $this->serverVersion
1102
+            ->expects($this->once())
1103
+            ->method('getChannel')
1104
+            ->willReturn($channel);
1105
+        $this->config
1106
+            ->expects($this->any())
1107
+            ->method('getSystemValueBool')
1108
+            ->with('integrity.check.disabled', false)
1109
+            ->willReturn(true);
1110
+
1111
+        $this->assertFalse(self::invokePrivate($this->checker, 'isCodeCheckEnforced'));
1112
+    }
1113 1113
 }
Please login to merge, or discard this patch.
Spacing   +86 added lines, -86 removed lines patch added patch discarded remove patch
@@ -94,8 +94,8 @@  discard block
 block discarded – undo
94 94
 			->with('NotExistingApp/appinfo')
95 95
 			->willReturn(true);
96 96
 
97
-		$keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.crt');
98
-		$rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.key');
97
+		$keyBundle = file_get_contents(__DIR__.'/../../data/integritycheck/SomeApp.crt');
98
+		$rsaPrivateKey = file_get_contents(__DIR__.'/../../data/integritycheck/SomeApp.key');
99 99
 		$rsa = new RSA();
100 100
 		$rsa->loadKey($rsaPrivateKey);
101 101
 		$x509 = new X509();
@@ -113,13 +113,13 @@  discard block
 block discarded – undo
113 113
 			->method('file_put_contents')
114 114
 			->will($this->throwException(new \Exception('Exception message')));
115 115
 
116
-		$keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.crt');
117
-		$rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.key');
116
+		$keyBundle = file_get_contents(__DIR__.'/../../data/integritycheck/SomeApp.crt');
117
+		$rsaPrivateKey = file_get_contents(__DIR__.'/../../data/integritycheck/SomeApp.key');
118 118
 		$rsa = new RSA();
119 119
 		$rsa->loadKey($rsaPrivateKey);
120 120
 		$x509 = new X509();
121 121
 		$x509->loadX509($keyBundle);
122
-		$this->checker->writeAppSignature(\OC::$SERVERROOT . '/tests/data/integritycheck/app/', $x509, $rsa);
122
+		$this->checker->writeAppSignature(\OC::$SERVERROOT.'/tests/data/integritycheck/app/', $x509, $rsa);
123 123
 	}
124 124
 
125 125
 	public function testWriteAppSignature(): void {
@@ -135,8 +135,8 @@  discard block
 block discarded – undo
135 135
 			->expects($this->once())
136 136
 			->method('file_put_contents')
137 137
 			->with(
138
-				$this->equalTo(\OC::$SERVERROOT . '/tests/data/integritycheck/app//appinfo/signature.json'),
139
-				$this->callback(function ($signature) use ($expectedSignatureFileData) {
138
+				$this->equalTo(\OC::$SERVERROOT.'/tests/data/integritycheck/app//appinfo/signature.json'),
139
+				$this->callback(function($signature) use ($expectedSignatureFileData) {
140 140
 					$expectedArray = json_decode($expectedSignatureFileData, true);
141 141
 					$actualArray = json_decode($signature, true);
142 142
 					$this->assertEquals($expectedArray, $actualArray);
@@ -144,13 +144,13 @@  discard block
 block discarded – undo
144 144
 				})
145 145
 			);
146 146
 
147
-		$keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.crt');
148
-		$rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.key');
147
+		$keyBundle = file_get_contents(__DIR__.'/../../data/integritycheck/SomeApp.crt');
148
+		$rsaPrivateKey = file_get_contents(__DIR__.'/../../data/integritycheck/SomeApp.key');
149 149
 		$rsa = new RSA();
150 150
 		$rsa->loadKey($rsaPrivateKey);
151 151
 		$x509 = new X509();
152 152
 		$x509->loadX509($keyBundle);
153
-		$this->checker->writeAppSignature(\OC::$SERVERROOT . '/tests/data/integritycheck/app/', $x509, $rsa);
153
+		$this->checker->writeAppSignature(\OC::$SERVERROOT.'/tests/data/integritycheck/app/', $x509, $rsa);
154 154
 	}
155 155
 
156 156
 	public function testVerifyAppSignatureWithoutSignatureData(): void {
@@ -188,7 +188,7 @@  discard block
 block discarded – undo
188 188
 			->expects($this->once())
189 189
 			->method('getAppPath')
190 190
 			->with('SomeApp')
191
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
191
+			->willReturn(\OC::$SERVERROOT.'/tests/data/integritycheck/app/');
192 192
 		$signatureDataFile = '{
193 193
     "hashes": {
194 194
         "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
@@ -201,8 +201,8 @@  discard block
 block discarded – undo
201 201
 			->expects($this->exactly(2))
202 202
 			->method('file_get_contents')
203 203
 			->willReturnMap([
204
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/app//appinfo/signature.json', $signatureDataFile],
205
-				['/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
204
+				[\OC::$SERVERROOT.'/tests/data/integritycheck/app//appinfo/signature.json', $signatureDataFile],
205
+				['/resources/codesigning/root.crt', file_get_contents(__DIR__.'/../../data/integritycheck/root.crt')],
206 206
 			]);
207 207
 
208 208
 		$this->assertSame([], $this->checker->verifyAppSignature('SomeApp'));
@@ -223,7 +223,7 @@  discard block
 block discarded – undo
223 223
 			->expects($this->once())
224 224
 			->method('getAppPath')
225 225
 			->with('SomeApp')
226
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
226
+			->willReturn(\OC::$SERVERROOT.'/tests/data/integritycheck/app/');
227 227
 		$signatureDataFile = '{
228 228
     "hashes": {
229 229
         "AnotherFile.txt": "tampered",
@@ -236,8 +236,8 @@  discard block
 block discarded – undo
236 236
 			->expects($this->exactly(2))
237 237
 			->method('file_get_contents')
238 238
 			->willReturnMap([
239
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/app//appinfo/signature.json', $signatureDataFile],
240
-				['/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
239
+				[\OC::$SERVERROOT.'/tests/data/integritycheck/app//appinfo/signature.json', $signatureDataFile],
240
+				['/resources/codesigning/root.crt', file_get_contents(__DIR__.'/../../data/integritycheck/root.crt')],
241 241
 			]);
242 242
 
243 243
 		$expected = [
@@ -264,7 +264,7 @@  discard block
 block discarded – undo
264 264
 			->expects($this->once())
265 265
 			->method('getAppPath')
266 266
 			->with('SomeApp')
267
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/');
267
+			->willReturn(\OC::$SERVERROOT.'/tests/data/integritycheck/appWithInvalidData/');
268 268
 		$signatureDataFile = '{
269 269
     "hashes": {
270 270
         "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
@@ -277,8 +277,8 @@  discard block
 block discarded – undo
277 277
 			->expects($this->exactly(2))
278 278
 			->method('file_get_contents')
279 279
 			->willReturnMap([
280
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//appinfo/signature.json', $signatureDataFile],
281
-				['/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
280
+				[\OC::$SERVERROOT.'/tests/data/integritycheck/appWithInvalidData//appinfo/signature.json', $signatureDataFile],
281
+				['/resources/codesigning/root.crt', file_get_contents(__DIR__.'/../../data/integritycheck/root.crt')],
282 282
 			]);
283 283
 
284 284
 
@@ -333,8 +333,8 @@  discard block
 block discarded – undo
333 333
 			->expects($this->exactly(2))
334 334
 			->method('file_get_contents')
335 335
 			->willReturnMap([
336
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//appinfo/signature.json', $signatureDataFile],
337
-				['/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
336
+				[\OC::$SERVERROOT.'/tests/data/integritycheck/appWithInvalidData//appinfo/signature.json', $signatureDataFile],
337
+				['/resources/codesigning/root.crt', file_get_contents(__DIR__.'/../../data/integritycheck/root.crt')],
338 338
 			]);
339 339
 
340 340
 		$expected = [
@@ -358,7 +358,7 @@  discard block
 block discarded – undo
358 358
 			],
359 359
 
360 360
 		];
361
-		$this->assertSame($expected, $this->checker->verifyAppSignature('SomeApp', \OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/'));
361
+		$this->assertSame($expected, $this->checker->verifyAppSignature('SomeApp', \OC::$SERVERROOT.'/tests/data/integritycheck/appWithInvalidData/'));
362 362
 	}
363 363
 
364 364
 	public function testVerifyAppWithDifferentScope(): void {
@@ -376,7 +376,7 @@  discard block
 block discarded – undo
376 376
 			->expects($this->once())
377 377
 			->method('getAppPath')
378 378
 			->with('SomeApp')
379
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/');
379
+			->willReturn(\OC::$SERVERROOT.'/tests/data/integritycheck/appWithInvalidData/');
380 380
 		$signatureDataFile = '{
381 381
     "hashes": {
382 382
         "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
@@ -389,8 +389,8 @@  discard block
 block discarded – undo
389 389
 			->expects($this->exactly(2))
390 390
 			->method('file_get_contents')
391 391
 			->willReturnMap([
392
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//appinfo/signature.json', $signatureDataFile],
393
-				['/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
392
+				[\OC::$SERVERROOT.'/tests/data/integritycheck/appWithInvalidData//appinfo/signature.json', $signatureDataFile],
393
+				['/resources/codesigning/root.crt', file_get_contents(__DIR__.'/../../data/integritycheck/root.crt')],
394 394
 			]);
395 395
 
396 396
 		$expected = [
@@ -417,7 +417,7 @@  discard block
 block discarded – undo
417 417
 			->expects($this->once())
418 418
 			->method('getAppPath')
419 419
 			->with('SomeApp')
420
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
420
+			->willReturn(\OC::$SERVERROOT.'/tests/data/integritycheck/app/');
421 421
 		$signatureDataFile = '{
422 422
     "hashes": {
423 423
         "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
@@ -430,8 +430,8 @@  discard block
 block discarded – undo
430 430
 			->expects($this->exactly(2))
431 431
 			->method('file_get_contents')
432 432
 			->willReturnMap([
433
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/app//appinfo/signature.json', $signatureDataFile],
434
-				['/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
433
+				[\OC::$SERVERROOT.'/tests/data/integritycheck/app//appinfo/signature.json', $signatureDataFile],
434
+				['/resources/codesigning/root.crt', file_get_contents(__DIR__.'/../../data/integritycheck/root.crt')],
435 435
 			]);
436 436
 
437 437
 		$this->assertSame([], $this->checker->verifyAppSignature('SomeApp'));
@@ -449,11 +449,11 @@  discard block
 block discarded – undo
449 449
 		$this->fileAccessHelper
450 450
 			->expects($this->once())
451 451
 			->method('is_writable')
452
-			->with(__DIR__ . '/core')
452
+			->with(__DIR__.'/core')
453 453
 			->willReturn(true);
454 454
 
455
-		$keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.crt');
456
-		$rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.key');
455
+		$keyBundle = file_get_contents(__DIR__.'/../../data/integritycheck/SomeApp.crt');
456
+		$rsaPrivateKey = file_get_contents(__DIR__.'/../../data/integritycheck/SomeApp.key');
457 457
 		$rsa = new RSA();
458 458
 		$rsa->loadKey($rsaPrivateKey);
459 459
 		$x509 = new X509();
@@ -473,11 +473,11 @@  discard block
 block discarded – undo
473 473
 		$this->fileAccessHelper
474 474
 			->expects($this->once())
475 475
 			->method('is_writable')
476
-			->with(__DIR__ . '/core')
476
+			->with(__DIR__.'/core')
477 477
 			->willReturn(false);
478 478
 
479
-		$keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.crt');
480
-		$rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.key');
479
+		$keyBundle = file_get_contents(__DIR__.'/../../data/integritycheck/SomeApp.crt');
480
+		$rsaPrivateKey = file_get_contents(__DIR__.'/../../data/integritycheck/SomeApp.key');
481 481
 		$rsa = new RSA();
482 482
 		$rsa->loadKey($rsaPrivateKey);
483 483
 		$x509 = new X509();
@@ -497,13 +497,13 @@  discard block
 block discarded – undo
497 497
 		$this->environmentHelper
498 498
 			->expects($this->any())
499 499
 			->method('getServerRoot')
500
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
500
+			->willReturn(\OC::$SERVERROOT.'/tests/data/integritycheck/app/');
501 501
 		$this->fileAccessHelper
502 502
 			->expects($this->once())
503 503
 			->method('file_put_contents')
504 504
 			->with(
505
-				\OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json',
506
-				$this->callback(function ($signature) use ($expectedSignatureFileData) {
505
+				\OC::$SERVERROOT.'/tests/data/integritycheck/app//core/signature.json',
506
+				$this->callback(function($signature) use ($expectedSignatureFileData) {
507 507
 					$expectedArray = json_decode($expectedSignatureFileData, true);
508 508
 					$actualArray = json_decode($signature, true);
509 509
 					$this->assertEquals($expectedArray, $actualArray);
@@ -511,13 +511,13 @@  discard block
 block discarded – undo
511 511
 				})
512 512
 			);
513 513
 
514
-		$keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/core.crt');
515
-		$rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/core.key');
514
+		$keyBundle = file_get_contents(__DIR__.'/../../data/integritycheck/core.crt');
515
+		$rsaPrivateKey = file_get_contents(__DIR__.'/../../data/integritycheck/core.key');
516 516
 		$rsa = new RSA();
517 517
 		$rsa->loadKey($rsaPrivateKey);
518 518
 		$x509 = new X509();
519 519
 		$x509->loadX509($keyBundle);
520
-		$this->checker->writeCoreSignature($x509, $rsa, \OC::$SERVERROOT . '/tests/data/integritycheck/app/');
520
+		$this->checker->writeCoreSignature($x509, $rsa, \OC::$SERVERROOT.'/tests/data/integritycheck/app/');
521 521
 	}
522 522
 
523 523
 	public function testWriteCoreSignatureWithUnmodifiedHtaccess(): void {
@@ -532,13 +532,13 @@  discard block
 block discarded – undo
532 532
 		$this->environmentHelper
533 533
 			->expects($this->any())
534 534
 			->method('getServerRoot')
535
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/htaccessUnmodified/');
535
+			->willReturn(\OC::$SERVERROOT.'/tests/data/integritycheck/htaccessUnmodified/');
536 536
 		$this->fileAccessHelper
537 537
 			->expects($this->once())
538 538
 			->method('file_put_contents')
539 539
 			->with(
540
-				\OC::$SERVERROOT . '/tests/data/integritycheck/htaccessUnmodified//core/signature.json',
541
-				$this->callback(function ($signature) use ($expectedSignatureFileData) {
540
+				\OC::$SERVERROOT.'/tests/data/integritycheck/htaccessUnmodified//core/signature.json',
541
+				$this->callback(function($signature) use ($expectedSignatureFileData) {
542 542
 					$expectedArray = json_decode($expectedSignatureFileData, true);
543 543
 					$actualArray = json_decode($signature, true);
544 544
 					$this->assertEquals($expectedArray, $actualArray);
@@ -546,13 +546,13 @@  discard block
 block discarded – undo
546 546
 				})
547 547
 			);
548 548
 
549
-		$keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/core.crt');
550
-		$rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/core.key');
549
+		$keyBundle = file_get_contents(__DIR__.'/../../data/integritycheck/core.crt');
550
+		$rsaPrivateKey = file_get_contents(__DIR__.'/../../data/integritycheck/core.key');
551 551
 		$rsa = new RSA();
552 552
 		$rsa->loadKey($rsaPrivateKey);
553 553
 		$x509 = new X509();
554 554
 		$x509->loadX509($keyBundle);
555
-		$this->checker->writeCoreSignature($x509, $rsa, \OC::$SERVERROOT . '/tests/data/integritycheck/htaccessUnmodified/');
555
+		$this->checker->writeCoreSignature($x509, $rsa, \OC::$SERVERROOT.'/tests/data/integritycheck/htaccessUnmodified/');
556 556
 	}
557 557
 
558 558
 	public function testWriteCoreSignatureWithInvalidModifiedHtaccess(): void {
@@ -567,8 +567,8 @@  discard block
 block discarded – undo
567 567
 			->expects($this->once())
568 568
 			->method('file_put_contents')
569 569
 			->with(
570
-				\OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithInvalidModifiedContent//core/signature.json',
571
-				$this->callback(function ($signature) use ($expectedSignatureFileData) {
570
+				\OC::$SERVERROOT.'/tests/data/integritycheck/htaccessWithInvalidModifiedContent//core/signature.json',
571
+				$this->callback(function($signature) use ($expectedSignatureFileData) {
572 572
 					$expectedArray = json_decode($expectedSignatureFileData, true);
573 573
 					$actualArray = json_decode($signature, true);
574 574
 					$this->assertEquals($expectedArray, $actualArray);
@@ -576,13 +576,13 @@  discard block
 block discarded – undo
576 576
 				})
577 577
 			);
578 578
 
579
-		$keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/core.crt');
580
-		$rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/core.key');
579
+		$keyBundle = file_get_contents(__DIR__.'/../../data/integritycheck/core.crt');
580
+		$rsaPrivateKey = file_get_contents(__DIR__.'/../../data/integritycheck/core.key');
581 581
 		$rsa = new RSA();
582 582
 		$rsa->loadKey($rsaPrivateKey);
583 583
 		$x509 = new X509();
584 584
 		$x509->loadX509($keyBundle);
585
-		$this->checker->writeCoreSignature($x509, $rsa, \OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithInvalidModifiedContent/');
585
+		$this->checker->writeCoreSignature($x509, $rsa, \OC::$SERVERROOT.'/tests/data/integritycheck/htaccessWithInvalidModifiedContent/');
586 586
 	}
587 587
 
588 588
 	public function testWriteCoreSignatureWithValidModifiedHtaccess(): void {
@@ -597,13 +597,13 @@  discard block
 block discarded – undo
597 597
 		$this->environmentHelper
598 598
 			->expects($this->any())
599 599
 			->method('getServerRoot')
600
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithValidModifiedContent');
600
+			->willReturn(\OC::$SERVERROOT.'/tests/data/integritycheck/htaccessWithValidModifiedContent');
601 601
 		$this->fileAccessHelper
602 602
 			->expects($this->once())
603 603
 			->method('file_put_contents')
604 604
 			->with(
605
-				\OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithValidModifiedContent/core/signature.json',
606
-				$this->callback(function ($signature) use ($expectedSignatureFileData) {
605
+				\OC::$SERVERROOT.'/tests/data/integritycheck/htaccessWithValidModifiedContent/core/signature.json',
606
+				$this->callback(function($signature) use ($expectedSignatureFileData) {
607 607
 					$expectedArray = json_decode($expectedSignatureFileData, true);
608 608
 					$actualArray = json_decode($signature, true);
609 609
 					$this->assertEquals($expectedArray, $actualArray);
@@ -611,13 +611,13 @@  discard block
 block discarded – undo
611 611
 				})
612 612
 			);
613 613
 
614
-		$keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/core.crt');
615
-		$rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/core.key');
614
+		$keyBundle = file_get_contents(__DIR__.'/../../data/integritycheck/core.crt');
615
+		$rsaPrivateKey = file_get_contents(__DIR__.'/../../data/integritycheck/core.key');
616 616
 		$rsa = new RSA();
617 617
 		$rsa->loadKey($rsaPrivateKey);
618 618
 		$x509 = new X509();
619 619
 		$x509->loadX509($keyBundle);
620
-		$this->checker->writeCoreSignature($x509, $rsa, \OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithValidModifiedContent');
620
+		$this->checker->writeCoreSignature($x509, $rsa, \OC::$SERVERROOT.'/tests/data/integritycheck/htaccessWithValidModifiedContent');
621 621
 	}
622 622
 
623 623
 	public function testVerifyCoreSignatureWithoutSignatureData(): void {
@@ -654,7 +654,7 @@  discard block
 block discarded – undo
654 654
 		$this->environmentHelper
655 655
 			->expects($this->any())
656 656
 			->method('getServerRoot')
657
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
657
+			->willReturn(\OC::$SERVERROOT.'/tests/data/integritycheck/app/');
658 658
 		$signatureDataFile = '{
659 659
     "hashes": {
660 660
         "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
@@ -667,8 +667,8 @@  discard block
 block discarded – undo
667 667
 			->expects($this->exactly(2))
668 668
 			->method('file_get_contents')
669 669
 			->willReturnMap([
670
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json', $signatureDataFile],
671
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/app//resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
670
+				[\OC::$SERVERROOT.'/tests/data/integritycheck/app//core/signature.json', $signatureDataFile],
671
+				[\OC::$SERVERROOT.'/tests/data/integritycheck/app//resources/codesigning/root.crt', file_get_contents(__DIR__.'/../../data/integritycheck/root.crt')],
672 672
 			]);
673 673
 
674 674
 		$this->assertSame([], $this->checker->verifyCoreSignature());
@@ -688,7 +688,7 @@  discard block
 block discarded – undo
688 688
 		$this->environmentHelper
689 689
 			->expects($this->any())
690 690
 			->method('getServerRoot')
691
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithValidModifiedContent');
691
+			->willReturn(\OC::$SERVERROOT.'/tests/data/integritycheck/htaccessWithValidModifiedContent');
692 692
 		$signatureDataFile = '{
693 693
     "hashes": {
694 694
         ".htaccess": "7e6a7a4d8ee4f3fbc45dd579407c643471575a9d127d1c75f6d0a49e80766c3c587104b2139ef76d2a4bffce3f45777900605aaa49519c9532909b71e5030227",
@@ -701,8 +701,8 @@  discard block
 block discarded – undo
701 701
 			->expects($this->exactly(2))
702 702
 			->method('file_get_contents')
703 703
 			->willReturnMap([
704
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithValidModifiedContent/core/signature.json', $signatureDataFile],
705
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithValidModifiedContent/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
704
+				[\OC::$SERVERROOT.'/tests/data/integritycheck/htaccessWithValidModifiedContent/core/signature.json', $signatureDataFile],
705
+				[\OC::$SERVERROOT.'/tests/data/integritycheck/htaccessWithValidModifiedContent/resources/codesigning/root.crt', file_get_contents(__DIR__.'/../../data/integritycheck/root.crt')],
706 706
 			]);
707 707
 
708 708
 		$this->assertSame([], $this->checker->verifyCoreSignature());
@@ -712,8 +712,8 @@  discard block
 block discarded – undo
712 712
 	 * See inline instruction on how to update the test assets when changing mimetypealiases.dist.json
713 713
 	 */
714 714
 	public function testVerifyCoreSignatureWithModifiedMimetypelistSignatureData(): void {
715
-		$shippedMimetypeAliases = (array)json_decode(file_get_contents(\OC::$SERVERROOT . '/resources/config/mimetypealiases.dist.json'));
716
-		$shippedMimetypeNames = (array)json_decode(file_get_contents(\OC::$SERVERROOT . '/resources/config/mimetypenames.dist.json'));
715
+		$shippedMimetypeAliases = (array) json_decode(file_get_contents(\OC::$SERVERROOT.'/resources/config/mimetypealiases.dist.json'));
716
+		$shippedMimetypeNames = (array) json_decode(file_get_contents(\OC::$SERVERROOT.'/resources/config/mimetypenames.dist.json'));
717 717
 		$allAliases = array_merge($shippedMimetypeAliases, ['my-custom/mimetype' => 'custom']);
718 718
 		$allMimetypeNames = array_merge($shippedMimetypeNames, ['my-custom/mimetype' => 'Custom Document']);
719 719
 
@@ -739,7 +739,7 @@  discard block
 block discarded – undo
739 739
 		// file_put_contents(\OC::$SERVERROOT . '/tests/data/integritycheck/mimetypeListModified/core/js/mimetypelist.js', $newFile);
740 740
 		// 2. Update signature.json using the following occ command:
741 741
 		// occ integrity:sign-core --privateKey=./tests/data/integritycheck/core.key --certificate=./tests/data/integritycheck/core.crt --path=./tests/data/integritycheck/mimetypeListModified
742
-		self::assertEquals($newFile, file_get_contents(\OC::$SERVERROOT . '/tests/data/integritycheck/mimetypeListModified/core/js/mimetypelist.js'));
742
+		self::assertEquals($newFile, file_get_contents(\OC::$SERVERROOT.'/tests/data/integritycheck/mimetypeListModified/core/js/mimetypelist.js'));
743 743
 
744 744
 		$this->serverVersion
745 745
 			->expects($this->once())
@@ -754,14 +754,14 @@  discard block
 block discarded – undo
754 754
 		$this->environmentHelper
755 755
 			->expects($this->any())
756 756
 			->method('getServerRoot')
757
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/mimetypeListModified');
757
+			->willReturn(\OC::$SERVERROOT.'/tests/data/integritycheck/mimetypeListModified');
758 758
 
759
-		$signatureDataFile = file_get_contents(__DIR__ . '/../../data/integritycheck/mimetypeListModified/core/signature.json');
759
+		$signatureDataFile = file_get_contents(__DIR__.'/../../data/integritycheck/mimetypeListModified/core/signature.json');
760 760
 		$this->fileAccessHelper
761 761
 			->method('file_get_contents')
762 762
 			->willReturnMap([
763
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/mimetypeListModified/core/signature.json', $signatureDataFile],
764
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/mimetypeListModified/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
763
+				[\OC::$SERVERROOT.'/tests/data/integritycheck/mimetypeListModified/core/signature.json', $signatureDataFile],
764
+				[\OC::$SERVERROOT.'/tests/data/integritycheck/mimetypeListModified/resources/codesigning/root.crt', file_get_contents(__DIR__.'/../../data/integritycheck/root.crt')],
765 765
 			]);
766 766
 
767 767
 		$this->assertSame([], $this->checker->verifyCoreSignature());
@@ -781,7 +781,7 @@  discard block
 block discarded – undo
781 781
 		$this->environmentHelper
782 782
 			->expects($this->any())
783 783
 			->method('getServerRoot')
784
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
784
+			->willReturn(\OC::$SERVERROOT.'/tests/data/integritycheck/app/');
785 785
 		$signatureDataFile = '{
786 786
     "hashes": {
787 787
         "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
@@ -794,8 +794,8 @@  discard block
 block discarded – undo
794 794
 			->expects($this->exactly(2))
795 795
 			->method('file_get_contents')
796 796
 			->willReturnMap([
797
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json', $signatureDataFile],
798
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/app//resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
797
+				[\OC::$SERVERROOT.'/tests/data/integritycheck/app//core/signature.json', $signatureDataFile],
798
+				[\OC::$SERVERROOT.'/tests/data/integritycheck/app//resources/codesigning/root.crt', file_get_contents(__DIR__.'/../../data/integritycheck/root.crt')],
799 799
 			]);
800 800
 
801 801
 		$this->assertSame([], $this->checker->verifyCoreSignature());
@@ -815,7 +815,7 @@  discard block
 block discarded – undo
815 815
 		$this->environmentHelper
816 816
 			->expects($this->any())
817 817
 			->method('getServerRoot')
818
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/');
818
+			->willReturn(\OC::$SERVERROOT.'/tests/data/integritycheck/appWithInvalidData/');
819 819
 		$signatureDataFile = '{
820 820
     "hashes": {
821 821
         "AnotherFile.txt": "tampered",
@@ -828,8 +828,8 @@  discard block
 block discarded – undo
828 828
 			->expects($this->exactly(2))
829 829
 			->method('file_get_contents')
830 830
 			->willReturnMap([
831
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//core/signature.json', $signatureDataFile],
832
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
831
+				[\OC::$SERVERROOT.'/tests/data/integritycheck/appWithInvalidData//core/signature.json', $signatureDataFile],
832
+				[\OC::$SERVERROOT.'/tests/data/integritycheck/appWithInvalidData//resources/codesigning/root.crt', file_get_contents(__DIR__.'/../../data/integritycheck/root.crt')],
833 833
 			]);
834 834
 
835 835
 		$expected = [
@@ -855,7 +855,7 @@  discard block
 block discarded – undo
855 855
 		$this->environmentHelper
856 856
 			->expects($this->any())
857 857
 			->method('getServerRoot')
858
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/');
858
+			->willReturn(\OC::$SERVERROOT.'/tests/data/integritycheck/appWithInvalidData/');
859 859
 		$signatureDataFile = '{
860 860
     "hashes": {
861 861
         "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
@@ -868,8 +868,8 @@  discard block
 block discarded – undo
868 868
 			->expects($this->exactly(2))
869 869
 			->method('file_get_contents')
870 870
 			->willReturnMap([
871
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//core/signature.json', $signatureDataFile],
872
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
871
+				[\OC::$SERVERROOT.'/tests/data/integritycheck/appWithInvalidData//core/signature.json', $signatureDataFile],
872
+				[\OC::$SERVERROOT.'/tests/data/integritycheck/appWithInvalidData//resources/codesigning/root.crt', file_get_contents(__DIR__.'/../../data/integritycheck/root.crt')],
873 873
 			]);
874 874
 
875 875
 		$expected = [
@@ -910,7 +910,7 @@  discard block
 block discarded – undo
910 910
 		$this->environmentHelper
911 911
 			->expects($this->any())
912 912
 			->method('getServerRoot')
913
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
913
+			->willReturn(\OC::$SERVERROOT.'/tests/data/integritycheck/app/');
914 914
 		$signatureDataFile = '{
915 915
     "hashes": {
916 916
         "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
@@ -923,8 +923,8 @@  discard block
 block discarded – undo
923 923
 			->expects($this->exactly(2))
924 924
 			->method('file_get_contents')
925 925
 			->willReturnMap([
926
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json', $signatureDataFile],
927
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/app//resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
926
+				[\OC::$SERVERROOT.'/tests/data/integritycheck/app//core/signature.json', $signatureDataFile],
927
+				[\OC::$SERVERROOT.'/tests/data/integritycheck/app//resources/codesigning/root.crt', file_get_contents(__DIR__.'/../../data/integritycheck/root.crt')],
928 928
 			]);
929 929
 
930 930
 		$expected = [
@@ -950,7 +950,7 @@  discard block
 block discarded – undo
950 950
 		$this->environmentHelper
951 951
 			->expects($this->any())
952 952
 			->method('getServerRoot')
953
-			->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
953
+			->willReturn(\OC::$SERVERROOT.'/tests/data/integritycheck/app/');
954 954
 		$signatureDataFile = '{
955 955
     "hashes": {
956 956
         "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
@@ -963,8 +963,8 @@  discard block
 block discarded – undo
963 963
 			->expects($this->exactly(2))
964 964
 			->method('file_get_contents')
965 965
 			->willReturnMap([
966
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json', $signatureDataFile],
967
-				[\OC::$SERVERROOT . '/tests/data/integritycheck/app//resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
966
+				[\OC::$SERVERROOT.'/tests/data/integritycheck/app//core/signature.json', $signatureDataFile],
967
+				[\OC::$SERVERROOT.'/tests/data/integritycheck/app//resources/codesigning/root.crt', file_get_contents(__DIR__.'/../../data/integritycheck/root.crt')],
968 968
 			]);
969 969
 
970 970
 		$expected = [
@@ -1025,7 +1025,7 @@  discard block
 block discarded – undo
1025 1025
 		$this->checker
1026 1026
 			->expects($this->exactly(3))
1027 1027
 			->method('verifyAppSignature')
1028
-			->willReturnCallback(function ($app) use (&$calls) {
1028
+			->willReturnCallback(function($app) use (&$calls) {
1029 1029
 				$expected = array_shift($calls);
1030 1030
 				$this->assertSame($expected, $app);
1031 1031
 				return [];
Please login to merge, or discard this patch.