Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/server.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/ajax/update.php7
-rw-r--r--core/command/integrity/signapp.php98
-rw-r--r--core/command/integrity/signcore.php98
-rw-r--r--core/command/upgrade.php10
-rw-r--r--core/js/integritycheck-failed-notification.js38
-rw-r--r--core/js/setupchecks.js14
-rw-r--r--core/js/tests/specs/setupchecksSpec.js7
-rw-r--r--core/register_command.php8
-rw-r--r--lib/private/integritycheck/checker.php449
-rw-r--r--lib/private/integritycheck/exceptions/invalidsignatureexception.php30
-rw-r--r--lib/private/integritycheck/helpers/applocator.php56
-rw-r--r--lib/private/integritycheck/helpers/environmenthelper.php39
-rw-r--r--lib/private/integritycheck/helpers/fileaccesshelper.php61
-rw-r--r--lib/private/integritycheck/iterator/excludefilebynamefilteriterator.php58
-rw-r--r--lib/private/integritycheck/iterator/excludefoldersbypathfilteriterator.php51
-rw-r--r--lib/private/server.php31
-rw-r--r--lib/private/templatelayout.php15
-rw-r--r--lib/private/updater.php15
-rw-r--r--resources/codesigning/core.crt28
-rw-r--r--resources/codesigning/core.key51
-rw-r--r--resources/codesigning/root.crt28
-rw-r--r--resources/codesigning/root.key51
-rw-r--r--settings/application.php9
-rw-r--r--settings/controller/checksetupcontroller.php81
-rw-r--r--settings/routes.php2
-rw-r--r--tests/data/integritycheck/SomeApp.crt28
-rw-r--r--tests/data/integritycheck/SomeApp.key51
-rw-r--r--tests/data/integritycheck/app/AnotherFile.txt1
-rw-r--r--tests/data/integritycheck/app/subfolder/file.txt1
-rw-r--r--tests/data/integritycheck/appWithInvalidData/AnotherFile.txt1
-rw-r--r--tests/data/integritycheck/appWithInvalidData/UnecessaryFile0
-rw-r--r--tests/data/integritycheck/core.crt28
-rw-r--r--tests/data/integritycheck/core.key51
-rw-r--r--tests/data/integritycheck/root.crt28
-rw-r--r--tests/data/integritycheck/root.key51
-rw-r--r--tests/lib/command/integrity/SignAppTest.php247
-rw-r--r--tests/lib/command/integrity/SignCoreTest.php194
-rw-r--r--tests/lib/integritycheck/checkertest.php671
-rw-r--r--tests/lib/integritycheck/helpers/AppLocatorTest.php51
-rw-r--r--tests/lib/integritycheck/helpers/EnvironmentHelperTest.php32
-rw-r--r--tests/lib/integritycheck/helpers/FileAccessHelperTest.php43
-rw-r--r--tests/lib/updater.php11
-rw-r--r--tests/settings/controller/CheckSetupControllerTest.php449
43 files changed, 3262 insertions, 11 deletions
diff --git a/core/ajax/update.php b/core/ajax/update.php
index 7da9b71b751..a5c1f79e3ea 100644
--- a/core/ajax/update.php
+++ b/core/ajax/update.php
@@ -47,6 +47,7 @@ if (OC::checkUpgrade(false)) {
$updater = new \OC\Updater(
\OC::$server->getHTTPHelper(),
$config,
+ \OC::$server->getIntegrityCodeChecker(),
$logger
);
$incompatibleApps = [];
@@ -108,6 +109,12 @@ if (OC::checkUpgrade(false)) {
$updater->listen('\OC\Updater', 'resetLogLevel', function ($logLevel, $logLevelName) use($eventSource, $l) {
$eventSource->send('success', (string)$l->t('Reset log level to "%s"', [ $logLevelName ]));
});
+ $updater->listen('\OC\Updater', 'startCheckCodeIntegrity', function () use($eventSource, $l) {
+ $eventSource->send('success', (string)$l->t('Starting code integrity check'));
+ });
+ $updater->listen('\OC\Updater', 'finishedCheckCodeIntegrity', function () use($eventSource, $l) {
+ $eventSource->send('success', (string)$l->t('Finished code integrity check'));
+ });
try {
$updater->upgrade();
diff --git a/core/command/integrity/signapp.php b/core/command/integrity/signapp.php
new file mode 100644
index 00000000000..83a7972068f
--- /dev/null
+++ b/core/command/integrity/signapp.php
@@ -0,0 +1,98 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Integrity;
+
+use OC\IntegrityCheck\Checker;
+use OC\IntegrityCheck\Helpers\FileAccessHelper;
+use phpseclib\Crypt\RSA;
+use phpseclib\File\X509;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * Class SignApp
+ *
+ * @package OC\Core\Command\Integrity
+ */
+class SignApp extends Command {
+ /** @var Checker */
+ private $checker;
+ /** @var FileAccessHelper */
+ private $fileAccessHelper;
+
+ /**
+ * @param Checker $checker
+ * @param FileAccessHelper $fileAccessHelper
+ */
+ public function __construct(Checker $checker,
+ FileAccessHelper $fileAccessHelper) {
+ parent::__construct(null);
+ $this->checker = $checker;
+ $this->fileAccessHelper = $fileAccessHelper;
+ }
+
+ protected function configure() {
+ $this
+ ->setName('integrity:sign-app')
+ ->setDescription('Sign app using a private key.')
+ ->addOption('appId', null, InputOption::VALUE_REQUIRED, 'Application to sign')
+ ->addOption('privateKey', null, InputOption::VALUE_REQUIRED, 'Path to private key to use for signing')
+ ->addOption('certificate', null, InputOption::VALUE_REQUIRED, 'Path to certificate to use for signing');
+ }
+
+ /**
+ * {@inheritdoc }
+ */
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $appId = $input->getOption('appId');
+ $privateKeyPath = $input->getOption('privateKey');
+ $keyBundlePath = $input->getOption('certificate');
+ if(is_null($appId) || is_null($privateKeyPath) || is_null($keyBundlePath)) {
+ $output->writeln('--appId, --privateKey and --certificate are required.');
+ return null;
+ }
+
+ $privateKey = $this->fileAccessHelper->file_get_contents($privateKeyPath);
+ $keyBundle = $this->fileAccessHelper->file_get_contents($keyBundlePath);
+
+ if($privateKey === false) {
+ $output->writeln(sprintf('Private key "%s" does not exists.', $privateKeyPath));
+ return null;
+ }
+
+ if($keyBundle === false) {
+ $output->writeln(sprintf('Certificate "%s" does not exists.', $keyBundlePath));
+ return null;
+ }
+
+ $rsa = new RSA();
+ $rsa->loadKey($privateKey);
+ $x509 = new X509();
+ $x509->loadX509($keyBundle);
+ $x509->setPrivateKey($rsa);
+ $this->checker->writeAppSignature($appId, $x509, $rsa);
+
+ $output->writeln('Successfully signed "'.$appId.'"');
+ }
+}
diff --git a/core/command/integrity/signcore.php b/core/command/integrity/signcore.php
new file mode 100644
index 00000000000..4d097ad4f96
--- /dev/null
+++ b/core/command/integrity/signcore.php
@@ -0,0 +1,98 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command\Integrity;
+
+use OC\IntegrityCheck\Checker;
+use OC\IntegrityCheck\Helpers\EnvironmentHelper;
+use OC\IntegrityCheck\Helpers\FileAccessHelper;
+use phpseclib\Crypt\RSA;
+use phpseclib\File\X509;
+use Symfony\Component\Console\Command\Command;
+use OCP\IConfig;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * Class SignCore
+ *
+ * @package OC\Core\Command\Integrity
+ */
+class SignCore extends Command {
+ /** @var Checker */
+ private $checker;
+ /** @var FileAccessHelper */
+ private $fileAccessHelper;
+
+ /**
+ * @param Checker $checker
+ * @param FileAccessHelper $fileAccessHelper
+ */
+ public function __construct(Checker $checker,
+ FileAccessHelper $fileAccessHelper) {
+ parent::__construct(null);
+ $this->checker = $checker;
+ $this->fileAccessHelper = $fileAccessHelper;
+ }
+
+ protected function configure() {
+ $this
+ ->setName('integrity:sign-core')
+ ->setDescription('Sign core using a private key.')
+ ->addOption('privateKey', null, InputOption::VALUE_REQUIRED, 'Path to private key to use for signing')
+ ->addOption('certificate', null, InputOption::VALUE_REQUIRED, 'Path to certificate to use for signing');
+ }
+
+ /**
+ * {@inheritdoc }
+ */
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $privateKeyPath = $input->getOption('privateKey');
+ $keyBundlePath = $input->getOption('certificate');
+ if(is_null($privateKeyPath) || is_null($keyBundlePath)) {
+ $output->writeln('--privateKey and --certificate are required.');
+ return null;
+ }
+
+ $privateKey = $this->fileAccessHelper->file_get_contents($privateKeyPath);
+ $keyBundle = $this->fileAccessHelper->file_get_contents($keyBundlePath);
+
+ if($privateKey === false) {
+ $output->writeln(sprintf('Private key "%s" does not exists.', $privateKeyPath));
+ return null;
+ }
+
+ if($keyBundle === false) {
+ $output->writeln(sprintf('Certificate "%s" does not exists.', $keyBundlePath));
+ return null;
+ }
+
+ $rsa = new RSA();
+ $rsa->loadKey($privateKey);
+ $x509 = new X509();
+ $x509->loadX509($keyBundle);
+ $x509->setPrivateKey($rsa);
+ $this->checker->writeCoreSignature($x509, $rsa);
+
+ $output->writeln('Successfully signed "core"');
+ }
+}
diff --git a/core/command/upgrade.php b/core/command/upgrade.php
index d1a7c09c30c..9031e284f85 100644
--- a/core/command/upgrade.php
+++ b/core/command/upgrade.php
@@ -53,6 +53,7 @@ class Upgrade extends Command {
/**
* @param IConfig $config
+ * @param ILogger $logger
*/
public function __construct(IConfig $config, ILogger $logger) {
parent::__construct();
@@ -122,9 +123,12 @@ class Upgrade extends Command {
}
$self = $this;
- $updater = new Updater(\OC::$server->getHTTPHelper(),
- $this->config,
- $this->logger);
+ $updater = new Updater(
+ \OC::$server->getHTTPHelper(),
+ $this->config,
+ \OC::$server->getIntegrityCodeChecker(),
+ $this->logger
+ );
$updater->setSimulateStepEnabled($simulateStepEnabled);
$updater->setUpdateStepEnabled($updateStepEnabled);
diff --git a/core/js/integritycheck-failed-notification.js b/core/js/integritycheck-failed-notification.js
new file mode 100644
index 00000000000..5bc758d54cb
--- /dev/null
+++ b/core/js/integritycheck-failed-notification.js
@@ -0,0 +1,38 @@
+/**
+ * @author Lukas Reschke
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ * This gets only loaded if the integrity check has failed and then shows a notification
+ */
+$(document).ready(function(){
+ var text = t(
+ 'core',
+ '<a href="{docUrl}">There were problems with the code integrity check. More information…</a>',
+ {
+ docUrl: OC.generateUrl('/settings/admin#security-warning')
+ }
+ );
+
+ OC.Notification.showHtml(
+ text,
+ {
+ isHTML: true
+ }
+ );
+});
+
diff --git a/core/js/setupchecks.js b/core/js/setupchecks.js
index 6e2058d54fc..5ac1945da73 100644
--- a/core/js/setupchecks.js
+++ b/core/js/setupchecks.js
@@ -104,6 +104,20 @@
type: OC.SetupChecks.MESSAGE_TYPE_WARNING
});
}
+ if(!data.hasPassedCodeIntegrityCheck) {
+ messages.push({
+ msg: t(
+ 'core',
+ 'Some files have not passed the integrity check. Further information on how to resolve this issue can be found in our <a href="{docLink}">documentation</a>. (<a href="{codeIntegrityDownloadEndpoint}">List of invalid files…</a> / <a href="{rescanEndpoint}">Rescan…</a>)',
+ {
+ docLink: data.codeIntegrityCheckerDocumentation,
+ codeIntegrityDownloadEndpoint: OC.generateUrl('/settings/integrity/failed'),
+ rescanEndpoint: OC.generateUrl('/settings/integrity/rescan?requesttoken={requesttoken}', {'requesttoken': OC.requestToken})
+ }
+ ),
+ type: OC.SetupChecks.MESSAGE_TYPE_ERROR
+ });
+ }
} else {
messages.push({
msg: t('core', 'Error occurred while checking server setup'),
diff --git a/core/js/tests/specs/setupchecksSpec.js b/core/js/tests/specs/setupchecksSpec.js
index 8dd2214621a..4bad893cf37 100644
--- a/core/js/tests/specs/setupchecksSpec.js
+++ b/core/js/tests/specs/setupchecksSpec.js
@@ -75,6 +75,7 @@ describe('OC.SetupChecks tests', function() {
memcacheDocs: 'https://doc.owncloud.org/server/go.php?to=admin-performance',
forwardedForHeadersWorking: true,
isCorrectMemcachedPHPModuleInstalled: true,
+ hasPassedCodeIntegrityCheck: true,
})
);
@@ -109,6 +110,7 @@ describe('OC.SetupChecks tests', function() {
memcacheDocs: 'https://doc.owncloud.org/server/go.php?to=admin-performance',
forwardedForHeadersWorking: true,
isCorrectMemcachedPHPModuleInstalled: true,
+ hasPassedCodeIntegrityCheck: true,
})
);
@@ -145,6 +147,7 @@ describe('OC.SetupChecks tests', function() {
isMemcacheConfigured: true,
forwardedForHeadersWorking: true,
isCorrectMemcachedPHPModuleInstalled: true,
+ hasPassedCodeIntegrityCheck: true,
})
);
@@ -178,6 +181,7 @@ describe('OC.SetupChecks tests', function() {
isMemcacheConfigured: true,
forwardedForHeadersWorking: true,
isCorrectMemcachedPHPModuleInstalled: true,
+ hasPassedCodeIntegrityCheck: true,
})
);
@@ -206,6 +210,7 @@ describe('OC.SetupChecks tests', function() {
isMemcacheConfigured: true,
forwardedForHeadersWorking: true,
isCorrectMemcachedPHPModuleInstalled: false,
+ hasPassedCodeIntegrityCheck: true,
})
);
@@ -234,6 +239,7 @@ describe('OC.SetupChecks tests', function() {
forwardedForHeadersWorking: false,
reverseProxyDocs: 'https://docs.owncloud.org/foo/bar.html',
isCorrectMemcachedPHPModuleInstalled: true,
+ hasPassedCodeIntegrityCheck: true,
})
);
@@ -283,6 +289,7 @@ describe('OC.SetupChecks tests', function() {
forwardedForHeadersWorking: true,
phpSupported: {eol: true, version: '5.4.0'},
isCorrectMemcachedPHPModuleInstalled: true,
+ hasPassedCodeIntegrityCheck: true,
})
);
diff --git a/core/register_command.php b/core/register_command.php
index 4044d2d200c..16dda55878e 100644
--- a/core/register_command.php
+++ b/core/register_command.php
@@ -32,6 +32,14 @@ $application->add(new OC\Core\Command\Check(\OC::$server->getConfig()));
$infoParser = new \OC\App\InfoParser(\OC::$server->getHTTPHelper(), \OC::$server->getURLGenerator());
$application->add(new OC\Core\Command\App\CheckCode($infoParser));
$application->add(new OC\Core\Command\L10n\CreateJs());
+$application->add(new \OC\Core\Command\Integrity\SignApp(
+ \OC::$server->getIntegrityCodeChecker(),
+ new \OC\IntegrityCheck\Helpers\FileAccessHelper()
+));
+$application->add(new \OC\Core\Command\Integrity\SignCore(
+ \OC::$server->getIntegrityCodeChecker(),
+ new \OC\IntegrityCheck\Helpers\FileAccessHelper()
+));
if (\OC::$server->getConfig()->getSystemValue('installed', false)) {
$application->add(new OC\Core\Command\App\Disable());
diff --git a/lib/private/integritycheck/checker.php b/lib/private/integritycheck/checker.php
new file mode 100644
index 00000000000..edfe6b082e7
--- /dev/null
+++ b/lib/private/integritycheck/checker.php
@@ -0,0 +1,449 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\IntegrityCheck;
+
+use OC\IntegrityCheck\Exceptions\InvalidSignatureException;
+use OC\IntegrityCheck\Helpers\AppLocator;
+use OC\IntegrityCheck\Helpers\EnvironmentHelper;
+use OC\IntegrityCheck\Helpers\FileAccessHelper;
+use OC\Integritycheck\Iterator\ExcludeFileByNameFilterIterator;
+use OC\IntegrityCheck\Iterator\ExcludeFoldersByPathFilterIterator;
+use OCP\App\IAppManager;
+use OCP\ICache;
+use OCP\ICacheFactory;
+use OCP\IConfig;
+use phpseclib\Crypt\RSA;
+use phpseclib\File\X509;
+
+/**
+ * Class Checker handles the code signing using X.509 and RSA. ownCloud ships with
+ * a public root certificate certificate that allows to issue new certificates that
+ * will be trusted for signing code. The CN will be used to verify that a certificate
+ * given to a third-party developer may not be used for other applications. For
+ * example the author of the application "calendar" would only receive a certificate
+ * only valid for this application.
+ *
+ * @package OC\IntegrityCheck
+ */
+class Checker {
+ const CACHE_KEY = 'oc.integritycheck.checker';
+ /** @var EnvironmentHelper */
+ private $environmentHelper;
+ /** @var AppLocator */
+ private $appLocator;
+ /** @var FileAccessHelper */
+ private $fileAccessHelper;
+ /** @var IConfig */
+ private $config;
+ /** @var ICache */
+ private $cache;
+ /** @var IAppManager */
+ private $appManager;
+
+ /**
+ * @param EnvironmentHelper $environmentHelper
+ * @param FileAccessHelper $fileAccessHelper
+ * @param AppLocator $appLocator
+ * @param IConfig $config
+ * @param ICacheFactory $cacheFactory
+ * @param IAppManager $appManager
+ */
+ public function __construct(EnvironmentHelper $environmentHelper,
+ FileAccessHelper $fileAccessHelper,
+ AppLocator $appLocator,
+ IConfig $config = null,
+ ICacheFactory $cacheFactory,
+ IAppManager $appManager = null) {
+ $this->environmentHelper = $environmentHelper;
+ $this->fileAccessHelper = $fileAccessHelper;
+ $this->appLocator = $appLocator;
+ $this->config = $config;
+ $this->cache = $cacheFactory->create(self::CACHE_KEY);
+ $this->appManager = $appManager;
+ }
+
+ /**
+ * Enumerates all files belonging to the folder. Sensible defaults are excluded.
+ *
+ * @param string $folderToIterate
+ * @return \RecursiveIteratorIterator
+ */
+ private function getFolderIterator($folderToIterate) {
+ $dirItr = new \RecursiveDirectoryIterator(
+ $folderToIterate,
+ \RecursiveDirectoryIterator::SKIP_DOTS
+ );
+ $excludeGenericFilesIterator = new ExcludeFileByNameFilterIterator($dirItr);
+ $excludeFoldersIterator = new ExcludeFoldersByPathFilterIterator($excludeGenericFilesIterator);
+
+ return new \RecursiveIteratorIterator(
+ $excludeFoldersIterator,
+ \RecursiveIteratorIterator::SELF_FIRST
+ );
+ }
+
+ /**
+ * Returns an array of ['filename' => 'SHA512-hash-of-file'] for all files found
+ * in the iterator.
+ *
+ * @param \RecursiveIteratorIterator $iterator
+ * @param string $path
+ * @return array Array of hashes.
+ */
+ private function generateHashes(\RecursiveIteratorIterator $iterator,
+ $path) {
+ $hashes = [];
+
+ $baseDirectoryLength = strlen($path);
+ foreach($iterator as $filename => $data) {
+ /** @var \DirectoryIterator $data */
+ if($data->isDir()) {
+ continue;
+ }
+
+ $relativeFileName = substr($filename, $baseDirectoryLength);
+
+ // Exclude signature.json files in the appinfo and root folder
+ if($relativeFileName === '/appinfo/signature.json') {
+ continue;
+ }
+ // Exclude signature.json files in the appinfo and core folder
+ if($relativeFileName === '/core/signature.json') {
+ continue;
+ }
+
+ $hashes[$relativeFileName] = hash_file('sha512', $filename);
+ }
+ return $hashes;
+ }
+
+ /**
+ * Creates the signature data
+ *
+ * @param array $hashes
+ * @param X509 $certificate
+ * @param RSA $privateKey
+ * @return string
+ */
+ private function createSignatureData(array $hashes,
+ X509 $certificate,
+ RSA $privateKey) {
+ ksort($hashes);
+
+ $privateKey->setSignatureMode(RSA::SIGNATURE_PSS);
+ $privateKey->setMGFHash('sha512');
+ $signature = $privateKey->sign(json_encode($hashes));
+
+ return [
+ 'hashes' => $hashes,
+ 'signature' => base64_encode($signature),
+ 'certificate' => $certificate->saveX509($certificate->currentCert),
+ ];
+ }
+
+ /**
+ * Write the signature of the specified app
+ *
+ * @param string $appId
+ * @param X509 $certificate
+ * @param RSA $privateKey
+ * @throws \Exception
+ */
+ public function writeAppSignature($appId,
+ X509 $certificate,
+ RSA $privateKey) {
+ $path = $this->appLocator->getAppPath($appId);
+ $iterator = $this->getFolderIterator($path);
+ $hashes = $this->generateHashes($iterator, $path);
+ $signature = $this->createSignatureData($hashes, $certificate, $privateKey);
+ $this->fileAccessHelper->file_put_contents(
+ $path . '/appinfo/signature.json',
+ json_encode($signature, JSON_PRETTY_PRINT)
+ );
+ }
+
+ /**
+ * Write the signature of core
+ *
+ * @param X509 $certificate
+ * @param RSA $rsa
+ */
+ public function writeCoreSignature(X509 $certificate,
+ RSA $rsa) {
+ $iterator = $this->getFolderIterator($this->environmentHelper->getServerRoot());
+ $hashes = $this->generateHashes($iterator, $this->environmentHelper->getServerRoot());
+ $signatureData = $this->createSignatureData($hashes, $certificate, $rsa);
+ $this->fileAccessHelper->file_put_contents(
+ $this->environmentHelper->getServerRoot() . '/core/signature.json',
+ json_encode($signatureData, JSON_PRETTY_PRINT)
+ );
+ }
+
+ /**
+ * Verifies the signature for the specified path.
+ *
+ * @param string $signaturePath
+ * @param string $basePath
+ * @param string $certificateCN
+ * @return array
+ * @throws InvalidSignatureException
+ * @throws \Exception
+ */
+ private function verify($signaturePath, $basePath, $certificateCN) {
+ $signatureData = json_decode($this->fileAccessHelper->file_get_contents($signaturePath), true);
+ if(!is_array($signatureData)) {
+ throw new InvalidSignatureException('Signature data not found.');
+ }
+
+ $expectedHashes = $signatureData['hashes'];
+ ksort($expectedHashes);
+ $signature = base64_decode($signatureData['signature']);
+ $certificate = $signatureData['certificate'];
+
+ // Check if certificate is signed by ownCloud Root Authority
+ $x509 = new \phpseclib\File\X509();
+ $rootCertificatePublicKey = $this->fileAccessHelper->file_get_contents($this->environmentHelper->getServerRoot().'/resources/codesigning/root.crt');
+ $x509->loadCA($rootCertificatePublicKey);
+ $x509->loadX509($certificate);
+ if(!$x509->validateSignature()) {
+ throw new InvalidSignatureException('Certificate is not valid.');
+ }
+ // Verify if certificate has proper CN. "core" CN is always trusted.
+ if($x509->getDN(X509::DN_OPENSSL)['CN'] !== $certificateCN && $x509->getDN(X509::DN_OPENSSL)['CN'] !== 'core') {
+ throw new InvalidSignatureException(
+ sprintf('Certificate is not valid for required scope. (Requested: %s, current: %s)', $certificateCN, $x509->getDN(true))
+ );
+ }
+
+ // Check if the signature of the files is valid
+ $rsa = new \phpseclib\Crypt\RSA();
+ $rsa->loadKey($x509->currentCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']);
+ $rsa->setSignatureMode(RSA::SIGNATURE_PSS);
+ $rsa->setMGFHash('sha512');
+ if(!$rsa->verify(json_encode($expectedHashes), $signature)) {
+ throw new InvalidSignatureException('Signature could not get verified.');
+ }
+
+ // Compare the list of files which are not identical
+ $currentInstanceHashes = $this->generateHashes($this->getFolderIterator($basePath), $basePath);
+ $differencesA = array_diff($expectedHashes, $currentInstanceHashes);
+ $differencesB = array_diff($currentInstanceHashes, $expectedHashes);
+ $differences = array_unique(array_merge($differencesA, $differencesB));
+ $differenceArray = [];
+ foreach($differences as $filename => $hash) {
+ // Check if file should not exist in the new signature table
+ if(!array_key_exists($filename, $expectedHashes)) {
+ $differenceArray['EXTRA_FILE'][$filename]['expected'] = '';
+ $differenceArray['EXTRA_FILE'][$filename]['current'] = $hash;
+ continue;
+ }
+
+ // Check if file is missing
+ if(!array_key_exists($filename, $currentInstanceHashes)) {
+ $differenceArray['FILE_MISSING'][$filename]['expected'] = $expectedHashes[$filename];
+ $differenceArray['FILE_MISSING'][$filename]['current'] = '';
+ continue;
+ }
+
+ // Check if hash does mismatch
+ if($expectedHashes[$filename] !== $currentInstanceHashes[$filename]) {
+ $differenceArray['INVALID_HASH'][$filename]['expected'] = $expectedHashes[$filename];
+ $differenceArray['INVALID_HASH'][$filename]['current'] = $currentInstanceHashes[$filename];
+ continue;
+ }
+
+ // Should never happen.
+ throw new \Exception('Invalid behaviour in file hash comparison experienced. Please report this error to the developers.');
+ }
+
+ return $differenceArray;
+ }
+
+ /**
+ * Whether the code integrity check has passed successful or not
+ *
+ * @return bool
+ */
+ public function hasPassedCheck() {
+ $results = $this->getResults();
+ if(empty($results)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * @return array
+ */
+ public function getResults() {
+ $cachedResults = $this->cache->get(self::CACHE_KEY);
+ if(!is_null($cachedResults)) {
+ return json_decode($cachedResults, true);
+ }
+
+ return json_decode($this->config->getAppValue('core', self::CACHE_KEY, '{}'), true);
+ }
+
+ /**
+ * Stores the results in the app config as well as cache
+ *
+ * @param string $scope
+ * @param array $result
+ */
+ private function storeResults($scope, array $result) {
+ $resultArray = $this->getResults();
+ unset($resultArray[$scope]);
+ if(!empty($result)) {
+ $resultArray[$scope] = $result;
+ }
+ $this->config->setAppValue('core', self::CACHE_KEY, json_encode($resultArray));
+ $this->cache->set(self::CACHE_KEY, json_encode($resultArray));
+ }
+
+
+ /**
+ * Verify the signature of $appId. Returns an array with the following content:
+ * [
+ * 'FILE_MISSING' =>
+ * [
+ * 'filename' => [
+ * 'expected' => 'expectedSHA512',
+ * 'current' => 'currentSHA512',
+ * ],
+ * ],
+ * 'EXTRA_FILE' =>
+ * [
+ * 'filename' => [
+ * 'expected' => 'expectedSHA512',
+ * 'current' => 'currentSHA512',
+ * ],
+ * ],
+ * 'INVALID_HASH' =>
+ * [
+ * 'filename' => [
+ * 'expected' => 'expectedSHA512',
+ * 'current' => 'currentSHA512',
+ * ],
+ * ],
+ * ]
+ *
+ * Array may be empty in case no problems have been found.
+ *
+ * @param string $appId
+ * @return array
+ */
+ public function verifyAppSignature($appId) {
+ try {
+ $path = $this->appLocator->getAppPath($appId);
+ $result = $this->verify(
+ $path . '/appinfo/signature.json',
+ $path,
+ $appId
+ );
+ } catch (\Exception $e) {
+ $result = [
+ 'EXCEPTION' => [
+ 'class' => get_class($e),
+ 'message' => $e->getMessage(),
+ ],
+ ];
+ }
+ $this->storeResults($appId, $result);
+
+ return $result;
+ }
+
+ /**
+ * Verify the signature of core. Returns an array with the following content:
+ * [
+ * 'FILE_MISSING' =>
+ * [
+ * 'filename' => [
+ * 'expected' => 'expectedSHA512',
+ * 'current' => 'currentSHA512',
+ * ],
+ * ],
+ * 'EXTRA_FILE' =>
+ * [
+ * 'filename' => [
+ * 'expected' => 'expectedSHA512',
+ * 'current' => 'currentSHA512',
+ * ],
+ * ],
+ * 'INVALID_HASH' =>
+ * [
+ * 'filename' => [
+ * 'expected' => 'expectedSHA512',
+ * 'current' => 'currentSHA512',
+ * ],
+ * ],
+ * ]
+ *
+ * Array may be empty in case no problems have been found.
+ *
+ * @return array
+ */
+ public function verifyCoreSignature() {
+ try {
+ $result = $this->verify(
+ $this->environmentHelper->getServerRoot() . '/core/signature.json',
+ $this->environmentHelper->getServerRoot(),
+ 'core'
+ );
+ } catch (\Exception $e) {
+ $result = [
+ 'EXCEPTION' => [
+ 'class' => get_class($e),
+ 'message' => $e->getMessage(),
+ ],
+ ];
+ }
+ $this->storeResults('core', $result);
+
+ return $result;
+ }
+
+ /**
+ * Verify the core code of the instance as well as all applicable applications
+ * and store the results.
+ */
+ public function runInstanceVerification() {
+ $this->verifyCoreSignature();
+ $appIds = $this->appLocator->getAllApps();
+ foreach($appIds as $appId) {
+ // If an application is shipped a valid signature is required
+ $isShipped = $this->appManager->isShipped($appId);
+ $appNeedsToBeChecked = false;
+ if ($isShipped) {
+ $appNeedsToBeChecked = true;
+ } elseif ($this->fileAccessHelper->file_exists($this->appLocator->getAppPath($appId) . '/appinfo/signature.json')) {
+ // Otherwise only if the application explicitly ships a signature.json file
+ $appNeedsToBeChecked = true;
+ }
+
+ if($appNeedsToBeChecked) {
+ $this->verifyAppSignature($appId);
+ }
+ }
+ }
+}
diff --git a/lib/private/integritycheck/exceptions/invalidsignatureexception.php b/lib/private/integritycheck/exceptions/invalidsignatureexception.php
new file mode 100644
index 00000000000..9e05e5884f5
--- /dev/null
+++ b/lib/private/integritycheck/exceptions/invalidsignatureexception.php
@@ -0,0 +1,30 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\IntegrityCheck\Exceptions;
+
+/**
+ * Class InvalidSignatureException is thrown in case the signature of the hashes
+ * cannot be properly validated. This indicates that either files
+ *
+ * @package OC\IntegrityCheck\Exceptions
+ */
+class InvalidSignatureException extends \Exception {}
diff --git a/lib/private/integritycheck/helpers/applocator.php b/lib/private/integritycheck/helpers/applocator.php
new file mode 100644
index 00000000000..b732cb80893
--- /dev/null
+++ b/lib/private/integritycheck/helpers/applocator.php
@@ -0,0 +1,56 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\IntegrityCheck\Helpers;
+
+/**
+ * Class AppLocator provides a non-static helper for OC_App::getPath($appId)
+ * it is not possible to use IAppManager at this point as IAppManager has a
+ * dependency on a running ownCloud.
+ *
+ * @package OC\IntegrityCheck\Helpers
+ */
+class AppLocator {
+ /**
+ * Provides \OC_App::getAppPath($appId)
+ *
+ * @param string $appId
+ * @return string
+ * @throws \Exception If the app cannot be found
+ */
+ public function getAppPath($appId) {
+ $path = \OC_App::getAppPath($appId);
+ if($path === false) {
+
+ throw new \Exception('App not found');
+ }
+ return $path;
+ }
+
+ /**
+ * Providers \OC_App::getAllApps()
+ *
+ * @return array
+ */
+ public function getAllApps() {
+ return \OC_App::getAllApps();
+ }
+}
diff --git a/lib/private/integritycheck/helpers/environmenthelper.php b/lib/private/integritycheck/helpers/environmenthelper.php
new file mode 100644
index 00000000000..d7747dbb966
--- /dev/null
+++ b/lib/private/integritycheck/helpers/environmenthelper.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\IntegrityCheck\Helpers;
+
+/**
+ * Class EnvironmentHelper provides a non-static helper for access to static
+ * variables such as \OC::$SERVERROOT.
+ *
+ * @package OC\IntegrityCheck\Helpers
+ */
+class EnvironmentHelper {
+ /**
+ * Provides \OC::$SERVERROOT
+ *
+ * @return string
+ */
+ public function getServerRoot() {
+ return \OC::$SERVERROOT;
+ }
+}
diff --git a/lib/private/integritycheck/helpers/fileaccesshelper.php b/lib/private/integritycheck/helpers/fileaccesshelper.php
new file mode 100644
index 00000000000..23f592122dc
--- /dev/null
+++ b/lib/private/integritycheck/helpers/fileaccesshelper.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\IntegrityCheck\Helpers;
+
+/**
+ * Class FileAccessHelper provides a helper around file_get_contents and
+ * file_put_contents
+ *
+ * @package OC\IntegrityCheck\Helpers
+ */
+class FileAccessHelper {
+ /**
+ * Wrapper around file_get_contents($filename, $data)
+ *
+ * @param string $filename
+ * @return string|false
+ */
+ public function file_get_contents($filename) {
+ return file_get_contents($filename);
+ }
+
+ /**
+ * Wrapper around file_exists($filename)
+ *
+ * @param string $filename
+ * @return bool
+ */
+ public function file_exists($filename) {
+ return file_exists($filename);
+ }
+
+ /**
+ * Wrapper around file_put_contents($filename, $data)
+ *
+ * @param string $filename
+ * @param $data
+ * @return int|false
+ */
+ public function file_put_contents($filename, $data) {
+ return file_put_contents($filename, $data);
+ }
+}
diff --git a/lib/private/integritycheck/iterator/excludefilebynamefilteriterator.php b/lib/private/integritycheck/iterator/excludefilebynamefilteriterator.php
new file mode 100644
index 00000000000..c75554a7cc9
--- /dev/null
+++ b/lib/private/integritycheck/iterator/excludefilebynamefilteriterator.php
@@ -0,0 +1,58 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\IntegrityCheck\Iterator;
+
+/**
+ * Class ExcludeFileByNameFilterIterator provides a custom iterator which excludes
+ * entries with the specified file name from the file list.
+ *
+ * @package OC\Integritycheck\Iterator
+ */
+class ExcludeFileByNameFilterIterator extends \RecursiveFilterIterator {
+ /**
+ * Array of excluded file names. Those are not scanned by the integrity checker.
+ * This is used to exclude files which administrators could upload by mistakes
+ * such as .DS_Store files.
+ *
+ * @var array
+ */
+ private $excludedFilenames = [
+ '.DS_Store', // Mac OS X
+ 'Thumbs.db', // Microsoft Windows
+ '.directory', // Dolphin (KDE)
+ ];
+
+ /**
+ * @return bool
+ */
+ public function accept() {
+ if($this->isDir()) {
+ return true;
+ }
+
+ return !in_array(
+ $this->current()->getFilename(),
+ $this->excludedFilenames,
+ true
+ );
+ }
+}
diff --git a/lib/private/integritycheck/iterator/excludefoldersbypathfilteriterator.php b/lib/private/integritycheck/iterator/excludefoldersbypathfilteriterator.php
new file mode 100644
index 00000000000..43f19475862
--- /dev/null
+++ b/lib/private/integritycheck/iterator/excludefoldersbypathfilteriterator.php
@@ -0,0 +1,51 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\IntegrityCheck\Iterator;
+
+class ExcludeFoldersByPathFilterIterator extends \RecursiveFilterIterator {
+ private $excludedFolders = [];
+
+ public function __construct(\RecursiveIterator $iterator) {
+ parent::__construct($iterator);
+
+ $appFolders = \OC::$APPSROOTS;
+ foreach($appFolders as $key => $appFolder) {
+ $appFolders[$key] = rtrim($appFolder['path'], '/');
+ }
+
+ $this->excludedFolders = array_merge([
+ rtrim(\OC::$server->getConfig()->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data'), '/'),
+ rtrim(\OC::$SERVERROOT.'/themes', '/'),
+ ], $appFolders);
+ }
+
+ /**
+ * @return bool
+ */
+ public function accept() {
+ return !in_array(
+ $this->current()->getPathName(),
+ $this->excludedFolders,
+ true
+ );
+ }
+}
diff --git a/lib/private/server.php b/lib/private/server.php
index de3324d2cce..fe0d9dcf55d 100644
--- a/lib/private/server.php
+++ b/lib/private/server.php
@@ -51,6 +51,10 @@ use OC\Files\Node\HookConnector;
use OC\Files\Node\Root;
use OC\Files\View;
use OC\Http\Client\ClientService;
+use OC\IntegrityCheck\Checker;
+use OC\IntegrityCheck\Helpers\AppLocator;
+use OC\IntegrityCheck\Helpers\EnvironmentHelper;
+use OC\IntegrityCheck\Helpers\FileAccessHelper;
use OC\Lock\DBLockingProvider;
use OC\Lock\MemcacheLockingProvider;
use OC\Lock\NoopLockingProvider;
@@ -409,6 +413,26 @@ class Server extends SimpleContainer implements IServerContainer {
$this->registerService('TrustedDomainHelper', function ($c) {
return new TrustedDomainHelper($this->getConfig());
});
+ $this->registerService('IntegrityCodeChecker', function (Server $c) {
+ // IConfig and IAppManager requires a working database. This code
+ // might however be called when ownCloud is not yet setup.
+ if(\OC::$server->getSystemConfig()->getValue('installed', false)) {
+ $config = $c->getConfig();
+ $appManager = $c->getAppManager();
+ } else {
+ $config = null;
+ $appManager = null;
+ }
+
+ return new Checker(
+ new EnvironmentHelper(),
+ new FileAccessHelper(),
+ new AppLocator(),
+ $config,
+ $c->getMemCacheFactory(),
+ $appManager
+ );
+ });
$this->registerService('Request', function ($c) {
if (isset($this['urlParams'])) {
$urlParams = $this['urlParams'];
@@ -1094,6 +1118,13 @@ class Server extends SimpleContainer implements IServerContainer {
}
/**
+ * @return \OC\IntegrityCheck\Checker
+ */
+ public function getIntegrityCodeChecker() {
+ return $this->query('IntegrityCodeChecker');
+ }
+
+ /**
* @return \OC\Session\CryptoWrapper
*/
public function getSessionCryptoWrapper() {
diff --git a/lib/private/templatelayout.php b/lib/private/templatelayout.php
index f5974128b73..1a6a07ddc9f 100644
--- a/lib/private/templatelayout.php
+++ b/lib/private/templatelayout.php
@@ -78,8 +78,12 @@ class OC_TemplateLayout extends OC_Template {
// Update notification
if($this->config->getSystemValue('updatechecker', true) === true &&
OC_User::isAdminUser(OC_User::getUser())) {
- $updater = new \OC\Updater(\OC::$server->getHTTPHelper(),
- \OC::$server->getConfig(), \OC::$server->getLogger());
+ $updater = new \OC\Updater(
+ \OC::$server->getHTTPHelper(),
+ \OC::$server->getConfig(),
+ \OC::$server->getIntegrityCodeChecker(),
+ \OC::$server->getLogger()
+ );
$data = $updater->check();
if(isset($data['version']) && $data['version'] != '' and $data['version'] !== Array()) {
@@ -96,8 +100,13 @@ class OC_TemplateLayout extends OC_Template {
$this->assign('updateAvailable', false); // Update check is disabled
}
- // Add navigation entry
+ // Code integrity notification
+ $integrityChecker = \OC::$server->getIntegrityCodeChecker();
+ if(!$integrityChecker->hasPassedCheck()) {
+ \OCP\Util::addScript('core', 'integritycheck-failed-notification');
+ }
+ // Add navigation entry
$this->assign( 'application', '');
$this->assign( 'appid', $appId );
$navigation = OC_App::getNavigation();
diff --git a/lib/private/updater.php b/lib/private/updater.php
index 1ff80863737..366ad2555a8 100644
--- a/lib/private/updater.php
+++ b/lib/private/updater.php
@@ -34,6 +34,8 @@
namespace OC;
use OC\Hooks\BasicEmitter;
+use OC\IntegrityCheck\Checker;
+use OC\IntegrityCheck\Storage;
use OC_App;
use OC_Installer;
use OC_Util;
@@ -61,6 +63,9 @@ class Updater extends BasicEmitter {
/** @var IConfig */
private $config;
+ /** @var Checker */
+ private $checker;
+
/** @var bool */
private $simulateStepEnabled;
@@ -81,14 +86,17 @@ class Updater extends BasicEmitter {
/**
* @param HTTPHelper $httpHelper
* @param IConfig $config
+ * @param Checker $checker
* @param ILogger $log
*/
public function __construct(HTTPHelper $httpHelper,
IConfig $config,
+ Checker $checker,
ILogger $log = null) {
$this->httpHelper = $httpHelper;
$this->log = $log;
$this->config = $config;
+ $this->checker = $checker;
$this->simulateStepEnabled = true;
$this->updateStepEnabled = true;
}
@@ -335,6 +343,13 @@ class Updater extends BasicEmitter {
//Invalidate update feed
$this->config->setAppValue('core', 'lastupdatedat', 0);
+ // Check for code integrity on the stable channel
+ if(\OC_Util::getChannel() === 'stable') {
+ $this->emit('\OC\Updater', 'startCheckCodeIntegrity');
+ $this->checker->runInstanceVerification();
+ $this->emit('\OC\Updater', 'finishedCheckCodeIntegrity');
+ }
+
// only set the final version if everything went well
$this->config->setSystemValue('version', implode('.', \OC_Util::getVersion()));
}
diff --git a/resources/codesigning/core.crt b/resources/codesigning/core.crt
new file mode 100644
index 00000000000..475a59bddce
--- /dev/null
+++ b/resources/codesigning/core.crt
@@ -0,0 +1,28 @@
+-----BEGIN CERTIFICATE-----
+MIIEvjCCAqagAwIBAgIUc/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF
+BQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw
+MzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ
+KoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s
+iOf4RwPXR6SE9bWZEm/b72SfWk//J6AbrD8WiOzBuT/ODy6k5T1arEdHO+Pux0W1
+MxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr/xolP3oD+eLbShPcblhdS
+VtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0
+klnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5/2riAzIssMFSCarWCx0AKYb54+d
+xLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77
+H87KFhYW8tKFFvF1V3AHl/sFQ9tDHaxM9Y0pZ2jPp/ccdiqnmdkBxBDqsiRvHvVB
+Cn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO/wAtd2vUW8UFiq
+s2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ/zrM0
+i8nfCFwTxWRxp3H9KoECzO/zS5R5KIS7s3/wq/w9T2Ie4rcecgXwDizwnn0C/aKc
+bDIjujpL1s9HO05pcD/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ
+Q238lC+A/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2
+AvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji
+oNCXUbExC/0iCPUqdHZIVb+Lc/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd
+9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb
+H+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th/55
+cf3Fovj6JJgbb9XFxrdnsOsDOu/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX
+uVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO//+TJtXRbyNgsf
+oMRZGi8DLGU2SGEAHcRH/QZHq/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1
+0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI/XUxEWOa2F
+K2EqhErgMK/N07U1JJJay5tYZRtvkGq46oP/5kQG8hYST0MDK6VihJoPpvCmAm4E
+pEYKQ96x6A4EH9Y9mZlYozH/eqmxPbTK8n89/p7Ydun4rI+B2iiLnY8REWWy6+UQ
+V204fGUkJqW5CrKy3P3XvY9X
+-----END CERTIFICATE----- \ No newline at end of file
diff --git a/resources/codesigning/core.key b/resources/codesigning/core.key
new file mode 100644
index 00000000000..4a588d47356
--- /dev/null
+++ b/resources/codesigning/core.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY----- \ No newline at end of file
diff --git a/resources/codesigning/root.crt b/resources/codesigning/root.crt
new file mode 100644
index 00000000000..539bd9a199f
--- /dev/null
+++ b/resources/codesigning/root.crt
@@ -0,0 +1,28 @@
+-----BEGIN CERTIFICATE-----
+MIIE1DCCArygAwIBAgIUFgEnT7tUWNgEKfbMiRTOm3Z8figwDQYJKoZIhvcNAQEF
+BQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMCAXDTE1MTEw
+MzIxMDMzMloYDzk5OTkxMjMxMjM1OTU5WjAjMSEwHwYDVQQKDBhvd25DbG91ZCBD
+b2RlIFNpZ25pbmcgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCt
+rYEwiwpuhzRNx1L2SntkL/zalA7OkwEcsBn4Ysw47nEdvp648AVohT7d+U7+DWix
+O06xvlAJUhqYTXX7EG9n+mBnG+TIMq2zlei0Jj3uq1pEZE9elfGZael2uc8gRXMZ
+YFmSlTvzexqvfK4B3DwoZaMaWJecEO9iyuUyzHMBpE8bHtGDOUGy/oTO9WASbtl9
+Rfk38VLLV6csCPwKjii6Q3YZ+AYU0YqLg22BwZlqlTexUjWAmVIqaoxmSEuKa41X
+nuAIHfP65oiob86s4IvJXVr3r7NjdF2IJ/ZrwmjGKaWgSZKcoZBPLArZJeJiIQ4Y
+KzLZATwba5dWswV0mbDj8eP2BG+02NVKxylOBbeoBESnahZeZ5nJ5XKZr+ErJAUW
+b417fEnYaQNBlNnijjkqXaDipmTktUfnv4lm30sUAgho6I3Ga7gQrFPzKg9V4j2S
++LOTc1HmJOnR6Kfttx+yAHYLKtvV5yIMMpz+rZ2X5g/N2GdgleZj5VU9nmKzTPeG
+x6V+dBaIkqNe8/AXaVnxt9KSb03Q/+CFjKTNDtEN5fNJuXS0+h+oop6nhpktM2i7
+gCpxeLNEaQaeoxR5093VN00oOJOYBvQoVGEDftEwdG6dWbTZsIykBF7aK+p8DMy9
+tCdc2GnGMEuFlUNA9ucv2Rv7IcuPdspnK31CZoMNKwIDAQABMA0GCSqGSIb3DQEB
+BQUAA4ICAQBROWec0HOsnLPN40gkBQ62mNBUJgcAr5K2eMIEMSRFRD2ldEVOvmCO
+u1Q62umy9tiSRiFQTcG1J9k0zlOjy/hfpBl2G1Zce0OoEeuNkH7c0W/idHSloWRZ
+YlK3tVJD6DzY6s9VbO6e/ncecNsXkirkWp/cvMYquH2d4OmSl0/hW0VMdxOCLxkA
+xbW+3Dh05u7tgKVRD67/GRvLtg+xHaOJqiOh3MpMaHy+6xT5Fd5K2QC0pcGtZuqF
+EnnfdeUI/Dy76yQE8pBfjaUFf3TS1n1E6kun1Nkf0X4pvwi8W1goLsPu5sWDNNga
+1RGYj0o5OdIo27qebfmu76WX0fNNd47VabtzNV+W7Msj0yeZg+hxAtAvs0ZEyJh+
+4biWsv+ALSlqz4sSdoOVGUEBdnkUrWN19lou62ix9vTmuCrVEA3TuZA3PR0+hqqQ
+/A/DcmWwxWYKyaBgxwHc/nGo1qWrDh22P5Rp7++Zw3kOCY6QmeJkAiHFs6Crw4ub
+HKVMw3fV5H9oiUFjadPZoCU51uXKX4YRqKxWJ5djlp4r1GCEQHyxngTsmH3komnw
+kh4LsEQRqdhuT0A4sZN7CenMJfQiFqupL7RVSycJFQpgzwVFmOzjCVT4PT/W5ARv
+9YtqEkvyoRTwErwuN/FIVvhWnIP/C69Z1/T/nXyj86P2G7PMgnchIQ==
+-----END CERTIFICATE-----
diff --git a/resources/codesigning/root.key b/resources/codesigning/root.key
new file mode 100644
index 00000000000..557b0a26e3a
--- /dev/null
+++ b/resources/codesigning/root.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
diff --git a/settings/application.php b/settings/application.php
index c876065fffa..f9ee0121db0 100644
--- a/settings/application.php
+++ b/settings/application.php
@@ -40,6 +40,7 @@ use OC\Settings\Middleware\SubadminMiddleware;
use \OCP\AppFramework\App;
use OCP\IContainer;
use \OCP\Util;
+use OC\Server;
/**
* @package OC\Settings
@@ -154,7 +155,8 @@ class Application extends App {
$c->query('ClientService'),
$c->query('URLGenerator'),
$c->query('Util'),
- $c->query('L10N')
+ $c->query('L10N'),
+ $c->query('Checker')
);
});
@@ -240,5 +242,10 @@ class Application extends App {
$container->registerService('CertificateManager', function(IContainer $c){
return $c->query('ServerContainer')->getCertificateManager();
});
+ $container->registerService('Checker', function(IContainer $c) {
+ /** @var Server $server */
+ $server = $c->query('ServerContainer');
+ return $server->getIntegrityCodeChecker();
+ });
}
}
diff --git a/settings/controller/checksetupcontroller.php b/settings/controller/checksetupcontroller.php
index 491821b44c5..5a77049479d 100644
--- a/settings/controller/checksetupcontroller.php
+++ b/settings/controller/checksetupcontroller.php
@@ -26,8 +26,12 @@
namespace OC\Settings\Controller;
use GuzzleHttp\Exception\ClientException;
+use OC\AppFramework\Http;
+use OC\IntegrityCheck\Checker;
use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\DataDisplayResponse;
use OCP\AppFramework\Http\DataResponse;
+use OCP\AppFramework\Http\RedirectResponse;
use OCP\Http\Client\IClientService;
use OCP\IConfig;
use OCP\IL10N;
@@ -49,6 +53,8 @@ class CheckSetupController extends Controller {
private $urlGenerator;
/** @var IL10N */
private $l10n;
+ /** @var Checker */
+ private $checker;
/**
* @param string $AppName
@@ -58,6 +64,7 @@ class CheckSetupController extends Controller {
* @param IURLGenerator $urlGenerator
* @param \OC_Util $util
* @param IL10N $l10n
+ * @param Checker $checker
*/
public function __construct($AppName,
IRequest $request,
@@ -65,13 +72,15 @@ class CheckSetupController extends Controller {
IClientService $clientService,
IURLGenerator $urlGenerator,
\OC_Util $util,
- IL10N $l10n) {
+ IL10N $l10n,
+ Checker $checker) {
parent::__construct($AppName, $request);
$this->config = $config;
$this->clientService = $clientService;
$this->util = $util;
$this->urlGenerator = $urlGenerator;
$this->l10n = $l10n;
+ $this->checker = $checker;
}
/**
@@ -248,6 +257,72 @@ class CheckSetupController extends Controller {
}
/**
+ * @return RedirectResponse
+ */
+ public function rescanFailedIntegrityCheck() {
+ $this->checker->runInstanceVerification();
+ return new RedirectResponse(
+ $this->urlGenerator->linkToRoute('settings_admin')
+ );
+ }
+
+ /**
+ * @NoCSRFRequired
+ * @return DataResponse
+ */
+ public function getFailedIntegrityCheckFiles() {
+ $completeResults = $this->checker->getResults();
+
+ if(!empty($completeResults)) {
+ $formattedTextResponse = 'Technical information
+=====================
+The following list covers which files have failed the integrity check. Please read
+the previous linked documentation to learn more about the errors and how to fix
+them.
+
+Results
+=======
+';
+ foreach($completeResults as $context => $contextResult) {
+ $formattedTextResponse .= "- $context\n";
+
+ foreach($contextResult as $category => $result) {
+ $formattedTextResponse .= "\t- $category\n";
+ if($category !== 'EXCEPTION') {
+ foreach ($result as $key => $results) {
+ $formattedTextResponse .= "\t\t- $key\n";
+ }
+ } else {
+ foreach ($result as $key => $results) {
+ $formattedTextResponse .= "\t\t- $results\n";
+ }
+ }
+
+ }
+ }
+
+ $formattedTextResponse .= '
+Raw output
+==========
+';
+ $formattedTextResponse .= print_r($completeResults, true);
+ } else {
+ $formattedTextResponse = 'No errors have been found.';
+ }
+
+
+ $response = new DataDisplayResponse(
+ $formattedTextResponse,
+ Http::STATUS_OK,
+ [
+ 'Content-Type' => 'text/plain',
+ ]
+ );
+
+ return $response;
+ }
+
+ /**
* @return DataResponse
*/
public function check() {
@@ -263,7 +338,9 @@ class CheckSetupController extends Controller {
'phpSupported' => $this->isPhpSupported(),
'forwardedForHeadersWorking' => $this->forwardedForHeadersWorking(),
'reverseProxyDocs' => $this->urlGenerator->linkToDocs('admin-reverse-proxy'),
- 'isCorrectMemcachedPHPModuleInstalled' => $this->isCorrectMemcachedPHPModuleInstalled()
+ 'isCorrectMemcachedPHPModuleInstalled' => $this->isCorrectMemcachedPHPModuleInstalled(),
+ 'hasPassedCodeIntegrityCheck' => $this->checker->hasPassedCheck(),
+ 'codeIntegrityCheckerDocumentation' => $this->urlGenerator->linkToDocs('admin-code-integrity'),
]
);
}
diff --git a/settings/routes.php b/settings/routes.php
index 95ad234c699..6b6b0150168 100644
--- a/settings/routes.php
+++ b/settings/routes.php
@@ -53,6 +53,8 @@ $application->registerRoutes($this, [
['name' => 'LogSettings#getEntries', 'url' => '/settings/admin/log/entries', 'verb' => 'GET'],
['name' => 'LogSettings#download', 'url' => '/settings/admin/log/download', 'verb' => 'GET'],
['name' => 'CheckSetup#check', 'url' => '/settings/ajax/checksetup', 'verb' => 'GET'],
+ ['name' => 'CheckSetup#getFailedIntegrityCheckFiles', 'url' => '/settings/integrity/failed', 'verb' => 'GET'],
+ ['name' => 'CheckSetup#rescanFailedIntegrityCheck', 'url' => '/settings/integrity/rescan', 'verb' => 'GET'],
['name' => 'Certificate#addPersonalRootCertificate', 'url' => '/settings/personal/certificate', 'verb' => 'POST'],
['name' => 'Certificate#removePersonalRootCertificate', 'url' => '/settings/personal/certificate/{certificateIdentifier}', 'verb' => 'DELETE'],
]
diff --git a/tests/data/integritycheck/SomeApp.crt b/tests/data/integritycheck/SomeApp.crt
new file mode 100644
index 00000000000..a9bd4cd8a7c
--- /dev/null
+++ b/tests/data/integritycheck/SomeApp.crt
@@ -0,0 +1,28 @@
+-----BEGIN CERTIFICATE-----
+MIIEwTCCAqmgAwIBAgIUWv0iujufs5lUr0svCf/qTQvoyKAwDQYJKoZIhvcNAQEF
+BQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw
+MzIyNDk1M1oXDTE2MTEwMzIyNDk1M1owEjEQMA4GA1UEAwwHU29tZUFwcDCCAiIw
+DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK8q0x62agGSRBqeWsaeEwFfepMk
+F8cAobMMi50qHCv9IrOn/ZH9l52xBrbIkErVmRjmly0d4JhD8Ymhidsh9ONKYl/j
++ishsZDM8eNNdp3Ew+fEYVvY1W7mR1qU24NWj0bzVsClI7hvPVIuw7AjfBDq1C5+
+A+ZSLSXYvOK2cEWjdxQfuNZwEZSjmA63DUllBIrm35IaTvfuyhU6BW9yHZxmb8+M
+w0xDv30D5UkE/2N7Pa/HQJLxCR+3zKibRK3nUyRDLSXxMkU9PnFNaPNX59VPgyj4
+GB1CFSToldJVPF4pzh7p36uGXZVxs8m3LFD4Ol8mhi7jkxDZjqFN46gzR0r23Py6
+dol9vfawGIoUwp9LvL0S7MvdRY0oazLXwClLP4OQ17zpSMAiCj7fgNT661JamPGj
+t5O7Zn2wA7I4ddDS/HDTWCu98Zwc9fHIpsJPgCZ9awoqxi4Mnf7Pk9g5nnXhszGC
+cxxIASQKM+GhdzoRxKknax2RzUCwCzcPRtCj8AQT/x/mqN3PfRmlnFBNACUw9bpZ
+SOoNq2pCF9igftDWpSIXQ38pVpKLWowjjg3DVRmVKBgivHnUnVLyzYBahHPj0vaz
+tFtUFRaqXDnt+4qyUGyrT5h5pjZaTcHIcSB4PiarYwdVvgslgwnQzOUcGAzRWBD4
+6jV2brP5vFY3g6iPAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBACTY3CCHC+Z28gCf
+FWGKQ3wAKs+k4+0yoti0qm2EKX7rSGQ0PHSas6uW79WstC4Rj+DYkDtIhGMSg8FS
+HVGZHGBCc0HwdX+BOAt3zi4p7Sf3oQef70/4imPoKxbAVCpd/cveVcFyDC19j1yB
+Bapwu87oh+muoeaZxOlqQI4UxjBlR/uRSMhOn2UGauIr3dWJgAF4pGt7TtIzt+1v
+0uA6FtN1Y4R5O8AaJPh1bIG0CVvFBE58esGzjEYLhOydgKFnEP94kVPgJD5ds9C3
+pPhEpo1dRpiXaF7WGIV1X6DI/ipWvfrF7CEy6I/kP1InY/vMDjQjeDnJ/VrXIWXO
+yZvHXVaN/m+1RlETsH7YO/QmxRue9ZHN3gvvWtmpCeA95sfpepOk7UcHxHZYyQbF
+49/au8j+5tsr4A83xzsT1JbcKRxkAaQ7WDJpOnE5O1+H0fB+BaLakTg6XX9d4Fo7
+7Gin7hVWX7pL+JIyxMzME3LhfI61+CRcqZQIrpyaafUziPQbWIPfEs7h8tCOWyvW
+UO8ZLervYCB3j44ivkrxPlcBklDCqqKKBzDP9dYOtS/P4RB1NkHA9+NTvmBpTonS
+SFXdg9fFMD7VfjDE3Vnk+8DWkVH5wBYowTAD7w9Wuzr7DumiAULexnP/Y7xwxLv7
+4B+pXTAcRK0zECDEaX3npS8xWzrB
+-----END CERTIFICATE----- \ No newline at end of file
diff --git a/tests/data/integritycheck/SomeApp.key b/tests/data/integritycheck/SomeApp.key
new file mode 100644
index 00000000000..ce59eb1b86f
--- /dev/null
+++ b/tests/data/integritycheck/SomeApp.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY----- \ No newline at end of file
diff --git a/tests/data/integritycheck/app/AnotherFile.txt b/tests/data/integritycheck/app/AnotherFile.txt
new file mode 100644
index 00000000000..cf85999ddfc
--- /dev/null
+++ b/tests/data/integritycheck/app/AnotherFile.txt
@@ -0,0 +1 @@
+Another file with some Content.
diff --git a/tests/data/integritycheck/app/subfolder/file.txt b/tests/data/integritycheck/app/subfolder/file.txt
new file mode 100644
index 00000000000..69cd4e0afaf
--- /dev/null
+++ b/tests/data/integritycheck/app/subfolder/file.txt
@@ -0,0 +1 @@
+A file with some content.
diff --git a/tests/data/integritycheck/appWithInvalidData/AnotherFile.txt b/tests/data/integritycheck/appWithInvalidData/AnotherFile.txt
new file mode 100644
index 00000000000..c73a4892418
--- /dev/null
+++ b/tests/data/integritycheck/appWithInvalidData/AnotherFile.txt
@@ -0,0 +1 @@
+TAMPERED FILE. \ No newline at end of file
diff --git a/tests/data/integritycheck/appWithInvalidData/UnecessaryFile b/tests/data/integritycheck/appWithInvalidData/UnecessaryFile
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/tests/data/integritycheck/appWithInvalidData/UnecessaryFile
diff --git a/tests/data/integritycheck/core.crt b/tests/data/integritycheck/core.crt
new file mode 100644
index 00000000000..475a59bddce
--- /dev/null
+++ b/tests/data/integritycheck/core.crt
@@ -0,0 +1,28 @@
+-----BEGIN CERTIFICATE-----
+MIIEvjCCAqagAwIBAgIUc/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF
+BQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw
+MzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ
+KoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s
+iOf4RwPXR6SE9bWZEm/b72SfWk//J6AbrD8WiOzBuT/ODy6k5T1arEdHO+Pux0W1
+MxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr/xolP3oD+eLbShPcblhdS
+VtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0
+klnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5/2riAzIssMFSCarWCx0AKYb54+d
+xLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77
+H87KFhYW8tKFFvF1V3AHl/sFQ9tDHaxM9Y0pZ2jPp/ccdiqnmdkBxBDqsiRvHvVB
+Cn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO/wAtd2vUW8UFiq
+s2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ/zrM0
+i8nfCFwTxWRxp3H9KoECzO/zS5R5KIS7s3/wq/w9T2Ie4rcecgXwDizwnn0C/aKc
+bDIjujpL1s9HO05pcD/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ
+Q238lC+A/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2
+AvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji
+oNCXUbExC/0iCPUqdHZIVb+Lc/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd
+9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb
+H+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th/55
+cf3Fovj6JJgbb9XFxrdnsOsDOu/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX
+uVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO//+TJtXRbyNgsf
+oMRZGi8DLGU2SGEAHcRH/QZHq/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1
+0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI/XUxEWOa2F
+K2EqhErgMK/N07U1JJJay5tYZRtvkGq46oP/5kQG8hYST0MDK6VihJoPpvCmAm4E
+pEYKQ96x6A4EH9Y9mZlYozH/eqmxPbTK8n89/p7Ydun4rI+B2iiLnY8REWWy6+UQ
+V204fGUkJqW5CrKy3P3XvY9X
+-----END CERTIFICATE----- \ No newline at end of file
diff --git a/tests/data/integritycheck/core.key b/tests/data/integritycheck/core.key
new file mode 100644
index 00000000000..4a588d47356
--- /dev/null
+++ b/tests/data/integritycheck/core.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY----- \ No newline at end of file
diff --git a/tests/data/integritycheck/root.crt b/tests/data/integritycheck/root.crt
new file mode 100644
index 00000000000..539bd9a199f
--- /dev/null
+++ b/tests/data/integritycheck/root.crt
@@ -0,0 +1,28 @@
+-----BEGIN CERTIFICATE-----
+MIIE1DCCArygAwIBAgIUFgEnT7tUWNgEKfbMiRTOm3Z8figwDQYJKoZIhvcNAQEF
+BQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMCAXDTE1MTEw
+MzIxMDMzMloYDzk5OTkxMjMxMjM1OTU5WjAjMSEwHwYDVQQKDBhvd25DbG91ZCBD
+b2RlIFNpZ25pbmcgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCt
+rYEwiwpuhzRNx1L2SntkL/zalA7OkwEcsBn4Ysw47nEdvp648AVohT7d+U7+DWix
+O06xvlAJUhqYTXX7EG9n+mBnG+TIMq2zlei0Jj3uq1pEZE9elfGZael2uc8gRXMZ
+YFmSlTvzexqvfK4B3DwoZaMaWJecEO9iyuUyzHMBpE8bHtGDOUGy/oTO9WASbtl9
+Rfk38VLLV6csCPwKjii6Q3YZ+AYU0YqLg22BwZlqlTexUjWAmVIqaoxmSEuKa41X
+nuAIHfP65oiob86s4IvJXVr3r7NjdF2IJ/ZrwmjGKaWgSZKcoZBPLArZJeJiIQ4Y
+KzLZATwba5dWswV0mbDj8eP2BG+02NVKxylOBbeoBESnahZeZ5nJ5XKZr+ErJAUW
+b417fEnYaQNBlNnijjkqXaDipmTktUfnv4lm30sUAgho6I3Ga7gQrFPzKg9V4j2S
++LOTc1HmJOnR6Kfttx+yAHYLKtvV5yIMMpz+rZ2X5g/N2GdgleZj5VU9nmKzTPeG
+x6V+dBaIkqNe8/AXaVnxt9KSb03Q/+CFjKTNDtEN5fNJuXS0+h+oop6nhpktM2i7
+gCpxeLNEaQaeoxR5093VN00oOJOYBvQoVGEDftEwdG6dWbTZsIykBF7aK+p8DMy9
+tCdc2GnGMEuFlUNA9ucv2Rv7IcuPdspnK31CZoMNKwIDAQABMA0GCSqGSIb3DQEB
+BQUAA4ICAQBROWec0HOsnLPN40gkBQ62mNBUJgcAr5K2eMIEMSRFRD2ldEVOvmCO
+u1Q62umy9tiSRiFQTcG1J9k0zlOjy/hfpBl2G1Zce0OoEeuNkH7c0W/idHSloWRZ
+YlK3tVJD6DzY6s9VbO6e/ncecNsXkirkWp/cvMYquH2d4OmSl0/hW0VMdxOCLxkA
+xbW+3Dh05u7tgKVRD67/GRvLtg+xHaOJqiOh3MpMaHy+6xT5Fd5K2QC0pcGtZuqF
+EnnfdeUI/Dy76yQE8pBfjaUFf3TS1n1E6kun1Nkf0X4pvwi8W1goLsPu5sWDNNga
+1RGYj0o5OdIo27qebfmu76WX0fNNd47VabtzNV+W7Msj0yeZg+hxAtAvs0ZEyJh+
+4biWsv+ALSlqz4sSdoOVGUEBdnkUrWN19lou62ix9vTmuCrVEA3TuZA3PR0+hqqQ
+/A/DcmWwxWYKyaBgxwHc/nGo1qWrDh22P5Rp7++Zw3kOCY6QmeJkAiHFs6Crw4ub
+HKVMw3fV5H9oiUFjadPZoCU51uXKX4YRqKxWJ5djlp4r1GCEQHyxngTsmH3komnw
+kh4LsEQRqdhuT0A4sZN7CenMJfQiFqupL7RVSycJFQpgzwVFmOzjCVT4PT/W5ARv
+9YtqEkvyoRTwErwuN/FIVvhWnIP/C69Z1/T/nXyj86P2G7PMgnchIQ==
+-----END CERTIFICATE-----
diff --git a/tests/data/integritycheck/root.key b/tests/data/integritycheck/root.key
new file mode 100644
index 00000000000..557b0a26e3a
--- /dev/null
+++ b/tests/data/integritycheck/root.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
diff --git a/tests/lib/command/integrity/SignAppTest.php b/tests/lib/command/integrity/SignAppTest.php
new file mode 100644
index 00000000000..b7c34585c5c
--- /dev/null
+++ b/tests/lib/command/integrity/SignAppTest.php
@@ -0,0 +1,247 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+namespace Test\Command\Integrity;
+
+use OC\Core\Command\Integrity\SignApp;
+use OC\IntegrityCheck\Checker;
+use OC\IntegrityCheck\Helpers\FileAccessHelper;
+use Test\TestCase;
+
+class SignAppTest extends TestCase {
+ /** @var Checker */
+ private $checker;
+ /** @var SignApp */
+ private $signApp;
+ /** @var FileAccessHelper */
+ private $fileAccessHelper;
+
+ public function setUp() {
+ parent::setUp();
+ $this->checker = $this->getMockBuilder('\OC\IntegrityCheck\Checker')
+ ->disableOriginalConstructor()->getMock();
+ $this->fileAccessHelper = $this->getMockBuilder('\OC\IntegrityCheck\Helpers\FileAccessHelper')
+ ->disableOriginalConstructor()->getMock();
+ $this->signApp = new SignApp(
+ $this->checker,
+ $this->fileAccessHelper
+ );
+ }
+
+ public function testExecuteWithMissingAppId() {
+ $inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
+ $outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
+
+ $inputInterface
+ ->expects($this->at(0))
+ ->method('getOption')
+ ->with('appId')
+ ->will($this->returnValue(null));
+ $inputInterface
+ ->expects($this->at(1))
+ ->method('getOption')
+ ->with('privateKey')
+ ->will($this->returnValue('PrivateKey'));
+ $inputInterface
+ ->expects($this->at(2))
+ ->method('getOption')
+ ->with('certificate')
+ ->will($this->returnValue('Certificate'));
+
+ $outputInterface
+ ->expects($this->at(0))
+ ->method('writeln')
+ ->with('--appId, --privateKey and --certificate are required.');
+
+ $this->invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface]);
+ }
+
+ public function testExecuteWithMissingPrivateKey() {
+ $inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
+ $outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
+
+ $inputInterface
+ ->expects($this->at(0))
+ ->method('getOption')
+ ->with('appId')
+ ->will($this->returnValue('AppId'));
+ $inputInterface
+ ->expects($this->at(1))
+ ->method('getOption')
+ ->with('privateKey')
+ ->will($this->returnValue(null));
+ $inputInterface
+ ->expects($this->at(2))
+ ->method('getOption')
+ ->with('certificate')
+ ->will($this->returnValue('Certificate'));
+
+ $outputInterface
+ ->expects($this->at(0))
+ ->method('writeln')
+ ->with('--appId, --privateKey and --certificate are required.');
+
+ $this->invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface]);
+ }
+
+ public function testExecuteWithMissingCertificate() {
+ $inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
+ $outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
+
+ $inputInterface
+ ->expects($this->at(0))
+ ->method('getOption')
+ ->with('appId')
+ ->will($this->returnValue('AppId'));
+ $inputInterface
+ ->expects($this->at(1))
+ ->method('getOption')
+ ->with('privateKey')
+ ->will($this->returnValue('privateKey'));
+ $inputInterface
+ ->expects($this->at(2))
+ ->method('getOption')
+ ->with('certificate')
+ ->will($this->returnValue(null));
+
+ $outputInterface
+ ->expects($this->at(0))
+ ->method('writeln')
+ ->with('--appId, --privateKey and --certificate are required.');
+
+ $this->invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface]);
+ }
+
+ public function testExecuteWithNotExistingPrivateKey() {
+ $inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
+ $outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
+
+ $inputInterface
+ ->expects($this->at(0))
+ ->method('getOption')
+ ->with('appId')
+ ->will($this->returnValue('AppId'));
+ $inputInterface
+ ->expects($this->at(1))
+ ->method('getOption')
+ ->with('privateKey')
+ ->will($this->returnValue('privateKey'));
+ $inputInterface
+ ->expects($this->at(2))
+ ->method('getOption')
+ ->with('certificate')
+ ->will($this->returnValue('certificate'));
+
+ $this->fileAccessHelper
+ ->expects($this->at(0))
+ ->method('file_get_contents')
+ ->with('privateKey')
+ ->will($this->returnValue(false));
+
+ $outputInterface
+ ->expects($this->at(0))
+ ->method('writeln')
+ ->with('Private key "privateKey" does not exists.');
+
+ $this->invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface]);
+ }
+
+ public function testExecuteWithNotExistingCertificate() {
+ $inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
+ $outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
+
+ $inputInterface
+ ->expects($this->at(0))
+ ->method('getOption')
+ ->with('appId')
+ ->will($this->returnValue('AppId'));
+ $inputInterface
+ ->expects($this->at(1))
+ ->method('getOption')
+ ->with('privateKey')
+ ->will($this->returnValue('privateKey'));
+ $inputInterface
+ ->expects($this->at(2))
+ ->method('getOption')
+ ->with('certificate')
+ ->will($this->returnValue('certificate'));
+
+ $this->fileAccessHelper
+ ->expects($this->at(0))
+ ->method('file_get_contents')
+ ->with('privateKey')
+ ->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/core.key'));
+ $this->fileAccessHelper
+ ->expects($this->at(1))
+ ->method('file_get_contents')
+ ->with('certificate')
+ ->will($this->returnValue(false));
+
+ $outputInterface
+ ->expects($this->at(0))
+ ->method('writeln')
+ ->with('Certificate "certificate" does not exists.');
+
+ $this->invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface]);
+ }
+
+ public function testExecute() {
+ $inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
+ $outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
+
+ $inputInterface
+ ->expects($this->at(0))
+ ->method('getOption')
+ ->with('appId')
+ ->will($this->returnValue('AppId'));
+ $inputInterface
+ ->expects($this->at(1))
+ ->method('getOption')
+ ->with('privateKey')
+ ->will($this->returnValue('privateKey'));
+ $inputInterface
+ ->expects($this->at(2))
+ ->method('getOption')
+ ->with('certificate')
+ ->will($this->returnValue('certificate'));
+
+ $this->fileAccessHelper
+ ->expects($this->at(0))
+ ->method('file_get_contents')
+ ->with('privateKey')
+ ->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/core.key'));
+ $this->fileAccessHelper
+ ->expects($this->at(1))
+ ->method('file_get_contents')
+ ->with('certificate')
+ ->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/core.crt'));
+
+ $this->checker
+ ->expects($this->once())
+ ->method('writeAppSignature');
+
+ $outputInterface
+ ->expects($this->at(0))
+ ->method('writeln')
+ ->with('Successfully signed "AppId"');
+
+ $this->invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface]);
+ }
+}
diff --git a/tests/lib/command/integrity/SignCoreTest.php b/tests/lib/command/integrity/SignCoreTest.php
new file mode 100644
index 00000000000..885c6fc664e
--- /dev/null
+++ b/tests/lib/command/integrity/SignCoreTest.php
@@ -0,0 +1,194 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+namespace Test\Command\Integrity;
+
+use OC\Core\Command\Integrity\SignCore;
+use OC\IntegrityCheck\Checker;
+use OC\IntegrityCheck\Helpers\FileAccessHelper;
+use Test\TestCase;
+
+class SignCoreTest extends TestCase {
+ /** @var Checker */
+ private $checker;
+ /** @var SignCore */
+ private $signCore;
+ /** @var FileAccessHelper */
+ private $fileAccessHelper;
+
+ public function setUp() {
+ parent::setUp();
+ $this->checker = $this->getMockBuilder('\OC\IntegrityCheck\Checker')
+ ->disableOriginalConstructor()->getMock();
+ $this->fileAccessHelper = $this->getMockBuilder('\OC\IntegrityCheck\Helpers\FileAccessHelper')
+ ->disableOriginalConstructor()->getMock();
+ $this->signCore = new SignCore(
+ $this->checker,
+ $this->fileAccessHelper
+ );
+ }
+
+ public function testExecuteWithMissingPrivateKey() {
+ $inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
+ $outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
+
+ $inputInterface
+ ->expects($this->at(0))
+ ->method('getOption')
+ ->with('privateKey')
+ ->will($this->returnValue(null));
+ $inputInterface
+ ->expects($this->at(1))
+ ->method('getOption')
+ ->with('certificate')
+ ->will($this->returnValue('Certificate'));
+
+ $outputInterface
+ ->expects($this->at(0))
+ ->method('writeln')
+ ->with('--privateKey and --certificate are required.');
+
+ $this->invokePrivate($this->signCore, 'execute', [$inputInterface, $outputInterface]);
+ }
+
+ public function testExecuteWithMissingCertificate() {
+ $inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
+ $outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
+
+ $inputInterface
+ ->expects($this->at(0))
+ ->method('getOption')
+ ->with('privateKey')
+ ->will($this->returnValue('privateKey'));
+ $inputInterface
+ ->expects($this->at(1))
+ ->method('getOption')
+ ->with('certificate')
+ ->will($this->returnValue(null));
+
+ $outputInterface
+ ->expects($this->at(0))
+ ->method('writeln')
+ ->with('--privateKey and --certificate are required.');
+
+ $this->invokePrivate($this->signCore, 'execute', [$inputInterface, $outputInterface]);
+ }
+
+ public function testExecuteWithNotExistingPrivateKey() {
+ $inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
+ $outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
+
+ $inputInterface
+ ->expects($this->at(0))
+ ->method('getOption')
+ ->with('privateKey')
+ ->will($this->returnValue('privateKey'));
+ $inputInterface
+ ->expects($this->at(1))
+ ->method('getOption')
+ ->with('certificate')
+ ->will($this->returnValue('certificate'));
+
+ $this->fileAccessHelper
+ ->expects($this->at(0))
+ ->method('file_get_contents')
+ ->with('privateKey')
+ ->will($this->returnValue(false));
+
+ $outputInterface
+ ->expects($this->at(0))
+ ->method('writeln')
+ ->with('Private key "privateKey" does not exists.');
+
+ $this->invokePrivate($this->signCore, 'execute', [$inputInterface, $outputInterface]);
+ }
+
+ public function testExecuteWithNotExistingCertificate() {
+ $inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
+ $outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
+
+ $inputInterface
+ ->expects($this->at(0))
+ ->method('getOption')
+ ->with('privateKey')
+ ->will($this->returnValue('privateKey'));
+ $inputInterface
+ ->expects($this->at(1))
+ ->method('getOption')
+ ->with('certificate')
+ ->will($this->returnValue('certificate'));
+
+ $this->fileAccessHelper
+ ->expects($this->at(0))
+ ->method('file_get_contents')
+ ->with('privateKey')
+ ->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/core.key'));
+ $this->fileAccessHelper
+ ->expects($this->at(1))
+ ->method('file_get_contents')
+ ->with('certificate')
+ ->will($this->returnValue(false));
+
+ $outputInterface
+ ->expects($this->at(0))
+ ->method('writeln')
+ ->with('Certificate "certificate" does not exists.');
+
+ $this->invokePrivate($this->signCore, 'execute', [$inputInterface, $outputInterface]);
+ }
+
+ public function testExecute() {
+ $inputInterface = $this->getMock('\Symfony\Component\Console\Input\InputInterface');
+ $outputInterface = $this->getMock('\Symfony\Component\Console\Output\OutputInterface');
+
+ $inputInterface
+ ->expects($this->at(0))
+ ->method('getOption')
+ ->with('privateKey')
+ ->will($this->returnValue('privateKey'));
+ $inputInterface
+ ->expects($this->at(1))
+ ->method('getOption')
+ ->with('certificate')
+ ->will($this->returnValue('certificate'));
+
+ $this->fileAccessHelper
+ ->expects($this->at(0))
+ ->method('file_get_contents')
+ ->with('privateKey')
+ ->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/core.key'));
+ $this->fileAccessHelper
+ ->expects($this->at(1))
+ ->method('file_get_contents')
+ ->with('certificate')
+ ->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/core.crt'));
+
+ $this->checker
+ ->expects($this->once())
+ ->method('writeCoreSignature');
+
+ $outputInterface
+ ->expects($this->at(0))
+ ->method('writeln')
+ ->with('Successfully signed "core"');
+
+ $this->invokePrivate($this->signCore, 'execute', [$inputInterface, $outputInterface]);
+ }
+}
diff --git a/tests/lib/integritycheck/checkertest.php b/tests/lib/integritycheck/checkertest.php
new file mode 100644
index 00000000000..17117a4274b
--- /dev/null
+++ b/tests/lib/integritycheck/checkertest.php
@@ -0,0 +1,671 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace Test\IntegrityCheck;
+
+use OC\IntegrityCheck\Checker;
+use OC\Memcache\NullCache;
+use phpseclib\Crypt\RSA;
+use phpseclib\File\X509;
+use Test\TestCase;
+use OC\IntegrityCheck\Helpers\EnvironmentHelper;
+use OC\IntegrityCheck\Helpers\FileAccessHelper;
+use OC\IntegrityCheck\Helpers\AppLocator;
+use OCP\IConfig;
+use OCP\ICacheFactory;
+use OCP\App\IAppManager;
+
+class CheckerTest extends TestCase {
+ /** @var EnvironmentHelper */
+ private $environmentHelper;
+ /** @var AppLocator */
+ private $appLocator;
+ /** @var Checker */
+ private $checker;
+ /** @var FileAccessHelper */
+ private $fileAccessHelper;
+ /** @var IConfig */
+ private $config;
+ /** @var ICacheFactory */
+ private $cacheFactory;
+ /** @var IAppManager */
+ private $appManager;
+
+ public function setUp() {
+ parent::setUp();
+ $this->environmentHelper = $this->getMock('\OC\IntegrityCheck\Helpers\EnvironmentHelper');
+ $this->fileAccessHelper = $this->getMock('\OC\IntegrityCheck\Helpers\FileAccessHelper');
+ $this->appLocator = $this->getMock('\OC\IntegrityCheck\Helpers\AppLocator');
+ $this->config = $this->getMock('\OCP\IConfig');
+ $this->cacheFactory = $this->getMock('\OCP\ICacheFactory');
+ $this->appManager = $this->getMock('\OCP\App\IAppManager');
+
+ $this->cacheFactory
+ ->expects($this->any())
+ ->method('create')
+ ->with('oc.integritycheck.checker')
+ ->will($this->returnValue(new NullCache()));
+
+ $this->checker = new Checker(
+ $this->environmentHelper,
+ $this->fileAccessHelper,
+ $this->appLocator,
+ $this->config,
+ $this->cacheFactory,
+ $this->appManager
+ );
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage Directory name must not be empty.
+ */
+ public function testWriteAppSignatureOfNotExistingApp() {
+ $keyBundle = file_get_contents(__DIR__ .'/../../data/integritycheck/SomeApp.crt');
+ $rsaPrivateKey = file_get_contents(__DIR__ .'/../../data/integritycheck/SomeApp.key');
+ $rsa = new RSA();
+ $rsa->loadKey($rsaPrivateKey);
+ $x509 = new X509();
+ $x509->loadX509($keyBundle);
+ $this->checker->writeAppSignature('NotExistingApp', $x509, $rsa);
+ }
+
+ public function testWriteAppSignature() {
+ $expectedSignatureFileData = '{
+ "hashes": {
+ "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
+ "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
+ },
+ "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=",
+ "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-----"
+}';
+ $this->appLocator
+ ->expects($this->once())
+ ->method('getAppPath')
+ ->with('SomeExistingApp')
+ ->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/app/'));
+ $this->fileAccessHelper
+ ->expects($this->once())
+ ->method('file_put_contents')
+ ->with(
+ \OC::$SERVERROOT . '/tests/data/integritycheck/app//appinfo/signature.json',
+ $expectedSignatureFileData
+ );
+
+ $keyBundle = file_get_contents(__DIR__ .'/../../data/integritycheck/SomeApp.crt');
+ $rsaPrivateKey = file_get_contents(__DIR__ .'/../../data/integritycheck/SomeApp.key');
+ $rsa = new RSA();
+ $rsa->loadKey($rsaPrivateKey);
+ $x509 = new X509();
+ $x509->loadX509($keyBundle);
+ $this->checker->writeAppSignature('SomeExistingApp', $x509, $rsa);
+ }
+
+ public function testVerifyAppSignatureWithoutSignatureData() {
+ $expected = [
+ 'EXCEPTION' => [
+ 'class' => 'OC\IntegrityCheck\Exceptions\InvalidSignatureException',
+ 'message' => 'Signature data not found.',
+ ],
+ ];
+ $this->assertSame($expected, $this->checker->verifyAppSignature('SomeApp'));
+ }
+
+ public function testVerifyAppSignatureWithValidSignatureData() {
+ $this->appLocator
+ ->expects($this->once())
+ ->method('getAppPath')
+ ->with('SomeApp')
+ ->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/app/'));
+ $signatureDataFile = '{
+ "hashes": {
+ "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
+ "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
+ },
+ "signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
+ "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-----"
+}';
+ $this->fileAccessHelper
+ ->expects($this->at(0))
+ ->method('file_get_contents')
+ ->with(
+ \OC::$SERVERROOT . '/tests/data/integritycheck/app//appinfo/signature.json'
+ )
+ ->will($this->returnValue($signatureDataFile));
+ $this->fileAccessHelper
+ ->expects($this->at(1))
+ ->method('file_get_contents')
+ ->with(
+ '/resources/codesigning/root.crt'
+ )
+ ->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
+
+ $this->assertSame([], $this->checker->verifyAppSignature('SomeApp'));
+ }
+
+ public function testVerifyAppSignatureWithTamperedSignatureData() {
+ $this->appLocator
+ ->expects($this->once())
+ ->method('getAppPath')
+ ->with('SomeApp')
+ ->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/app/'));
+ $signatureDataFile = '{
+ "hashes": {
+ "AnotherFile.txt": "tampered",
+ "subfolder\/file.txt": "tampered"
+ },
+ "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=",
+ "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-----"
+}';
+ $this->fileAccessHelper
+ ->expects($this->at(0))
+ ->method('file_get_contents')
+ ->with(
+ \OC::$SERVERROOT . '/tests/data/integritycheck/app//appinfo/signature.json'
+ )
+ ->will($this->returnValue($signatureDataFile));
+ $this->fileAccessHelper
+ ->expects($this->at(1))
+ ->method('file_get_contents')
+ ->with(
+ '/resources/codesigning/root.crt'
+ )
+ ->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
+
+ $expected = [
+ 'EXCEPTION' => [
+ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
+ 'message' => 'Signature could not get verified.',
+ ],
+ ];
+ $this->assertEquals($expected, $this->checker->verifyAppSignature('SomeApp'));
+ }
+
+ public function testVerifyAppSignatureWithTamperedFiles() {
+ $this->appLocator
+ ->expects($this->once())
+ ->method('getAppPath')
+ ->with('SomeApp')
+ ->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/'));
+ $signatureDataFile = '{
+ "hashes": {
+ "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
+ "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
+ },
+ "signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
+ "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-----"
+}';
+ $this->fileAccessHelper
+ ->expects($this->at(0))
+ ->method('file_get_contents')
+ ->with(
+ \OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//appinfo/signature.json'
+ )
+ ->will($this->returnValue($signatureDataFile));
+ $this->fileAccessHelper
+ ->expects($this->at(1))
+ ->method('file_get_contents')
+ ->with(
+ '/resources/codesigning/root.crt'
+ )
+ ->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
+
+
+ $expected = [
+ 'INVALID_HASH' => [
+ 'AnotherFile.txt' => [
+ 'expected' => '1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112',
+ 'current' => '7322348ba269c6d5522efe02f424fa3a0da319a7cd9c33142a5afe32a2d9af2da3a411f086fcfc96ff4301ea566f481dba0960c2abeef3594c4d930462f6584c',
+ ],
+ ],
+ 'FILE_MISSING' => [
+ 'subfolder/file.txt' => [
+ 'expected' => '410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b',
+ 'current' => '',
+ ],
+ ],
+ 'EXTRA_FILE' => [
+ 'UnecessaryFile' => [
+ 'expected' => '',
+ 'current' => 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e',
+ ],
+ ],
+
+ ];
+ $this->assertSame($expected, $this->checker->verifyAppSignature('SomeApp'));
+ }
+
+ public function testVerifyAppWithDifferentScope() {
+ $this->appLocator
+ ->expects($this->once())
+ ->method('getAppPath')
+ ->with('SomeApp')
+ ->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/'));
+ $signatureDataFile = '{
+ "hashes": {
+ "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
+ "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
+ },
+ "signature": "eXesvDm3pkek12xSwMG10y9suRES79Nye3jYNe5KYq1tTUPqRRNgxmMGAfcUro0zpLeAr2YgHeSMWtglblGOW7pmwGVPZ0O1Y4r1fE6jnep0kW+35PLIaqCorIOnCAtSzDNKBhwd1ow3zW2wC0DFouuEkIO8u5Fw28g8E8dp8zEk1xMblNPy+xtWkmYHrVJ\/dQgun1bYOF2ZFtAzatwndTI\/bGsy1i3Wsl+x6HyWKQdq8y8VObtOqKDH7uERBEpB9DHVyKflj1v1gQuEH6BhaRdATc7ee0MiQdGblraIySwYRdfo2d8i82OVKrenMB3SLwyCvDPyQ9iKpTOnSF52ZBqaqSXKM2N\/RAkweeBFQQCwcHhqxvB0cfbyHcbkOLeCZe\/tsh68IxwTiYgzvLfl7sOZ5arnZbzrPpZmB+hfV2omkoJ1tDwOWz9hEmLLNtfo2OxyUH1m0+XFaC+Gbn4WkVDgf7YZkwUcG+Qoa3oKDNMss8MEyZxewl2iDGZcf402dlidHRprlfmXbAYuVQ08\/a0HxIKYPGh\/nsMGmwnO15CWtFpAbhUA\/D5oRjsIxnvXaMDg0iAFpdu\/5Ffsj7g3EPdBkiQHNYK7YU1RRx609eH0bZyiIYHdUPw7ikLupvrebZmELqi3mqDFO99u4eISlxFJlUbUND3L4BtmWTWrKwI=",
+ "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-----"
+}';
+ $this->fileAccessHelper
+ ->expects($this->at(0))
+ ->method('file_get_contents')
+ ->with(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//appinfo/signature.json')
+ ->will($this->returnValue($signatureDataFile));
+ $this->fileAccessHelper
+ ->expects($this->at(1))
+ ->method('file_get_contents')
+ ->with(
+ '/resources/codesigning/root.crt'
+ )
+ ->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
+
+ $expected = [
+ 'EXCEPTION' => [
+ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
+ 'message' => 'Certificate is not valid for required scope. (Requested: SomeApp, current: CN=AnotherScope)',
+ ],
+ ];
+ $this->assertSame($expected, $this->checker->verifyAppSignature('SomeApp'));
+ }
+
+ public function testVerifyAppWithDifferentScopeAndAlwaysTrustedCore() {
+ $this->appLocator
+ ->expects($this->once())
+ ->method('getAppPath')
+ ->with('SomeApp')
+ ->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/app/'));
+ $signatureDataFile = '{
+ "hashes": {
+ "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
+ "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
+ },
+ "signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
+ "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-----"
+}';
+ $this->fileAccessHelper
+ ->expects($this->at(0))
+ ->method('file_get_contents')
+ ->with(\OC::$SERVERROOT . '/tests/data/integritycheck/app//appinfo/signature.json')
+ ->will($this->returnValue($signatureDataFile));
+ $this->fileAccessHelper
+ ->expects($this->at(1))
+ ->method('file_get_contents')
+ ->with(
+ '/resources/codesigning/root.crt'
+ )
+ ->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
+
+ $this->assertSame([], $this->checker->verifyAppSignature('SomeApp'));
+ }
+
+ public function testWriteCoreSignature() {
+ $expectedSignatureFileData = '{
+ "hashes": {
+ "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
+ "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
+ },
+ "signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
+ "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-----"
+}';
+ $this->environmentHelper
+ ->expects($this->any())
+ ->method('getServerRoot')
+ ->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/app/'));
+ $this->fileAccessHelper
+ ->expects($this->once())
+ ->method('file_put_contents')
+ ->with(
+ \OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json',
+ $expectedSignatureFileData
+ );
+
+ $keyBundle = file_get_contents(__DIR__ .'/../../data/integritycheck/core.crt');
+ $rsaPrivateKey = file_get_contents(__DIR__ .'/../../data/integritycheck/core.key');
+ $rsa = new RSA();
+ $rsa->loadKey($rsaPrivateKey);
+ $x509 = new X509();
+ $x509->loadX509($keyBundle);
+ $this->checker->writeCoreSignature($x509, $rsa);
+ }
+
+ public function testVerifyCoreSignatureWithoutSignatureData() {
+ $expected = [
+ 'EXCEPTION' => [
+ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
+ 'message' => 'Signature data not found.',
+ ],
+ ];
+ $this->assertSame($expected, $this->checker->verifyCoreSignature());
+ }
+
+ public function testVerifyCoreSignatureWithValidSignatureData() {
+ $this->environmentHelper
+ ->expects($this->any())
+ ->method('getServerRoot')
+ ->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/app/'));
+ $signatureDataFile = '{
+ "hashes": {
+ "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
+ "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
+ },
+ "signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
+ "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-----"
+}';
+ $this->fileAccessHelper
+ ->expects($this->at(0))
+ ->method('file_get_contents')
+ ->with(
+ \OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json'
+ )
+ ->will($this->returnValue($signatureDataFile));
+ $this->fileAccessHelper
+ ->expects($this->at(1))
+ ->method('file_get_contents')
+ ->with(
+ \OC::$SERVERROOT . '/tests/data/integritycheck/app//resources/codesigning/root.crt'
+ )
+ ->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
+
+ $this->assertSame([], $this->checker->verifyCoreSignature());
+ }
+
+ public function testVerifyCoreSignatureWithValidSignatureDataAndNotAlphabeticOrder() {
+ $this->environmentHelper
+ ->expects($this->any())
+ ->method('getServerRoot')
+ ->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/app/'));
+ $signatureDataFile = '{
+ "hashes": {
+ "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
+ "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
+ },
+ "signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
+ "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-----"
+}';
+ $this->fileAccessHelper
+ ->expects($this->at(0))
+ ->method('file_get_contents')
+ ->with(
+ \OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json'
+ )
+ ->will($this->returnValue($signatureDataFile));
+ $this->fileAccessHelper
+ ->expects($this->at(1))
+ ->method('file_get_contents')
+ ->with(
+ \OC::$SERVERROOT . '/tests/data/integritycheck/app//resources/codesigning/root.crt'
+ )
+ ->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
+
+ $this->assertSame([], $this->checker->verifyCoreSignature());
+ }
+
+ public function testVerifyCoreSignatureWithTamperedSignatureData() {
+ $this->environmentHelper
+ ->expects($this->any())
+ ->method('getServerRoot')
+ ->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/'));
+ $signatureDataFile = '{
+ "hashes": {
+ "AnotherFile.txt": "tampered",
+ "subfolder\/file.txt": "tampered"
+ },
+ "signature": "eXesvDm3pkek12xSwMG10y9suRES79Nye3jYNe5KYq1tTUPqRRNgxmMGAfcUro0zpLeAr2YgHeSMWtglblGOW7pmwGVPZ0O1Y4r1fE6jnep0kW+35PLIaqCorIOnCAtSzDNKBhwd1ow3zW2wC0DFouuEkIO8u5Fw28g8E8dp8zEk1xMblNPy+xtWkmYHrVJ\/dQgun1bYOF2ZFtAzatwndTI\/bGsy1i3Wsl+x6HyWKQdq8y8VObtOqKDH7uERBEpB9DHVyKflj1v1gQuEH6BhaRdATc7ee0MiQdGblraIySwYRdfo2d8i82OVKrenMB3SLwyCvDPyQ9iKpTOnSF52ZBqaqSXKM2N\/RAkweeBFQQCwcHhqxvB0cfbyHcbkOLeCZe\/tsh68IxwTiYgzvLfl7sOZ5arnZbzrPpZmB+hfV2omkoJ1tDwOWz9hEmLLNtfo2OxyUH1m0+XFaC+Gbn4WkVDgf7YZkwUcG+Qoa3oKDNMss8MEyZxewl2iDGZcf402dlidHRprlfmXbAYuVQ08\/a0HxIKYPGh\/nsMGmwnO15CWtFpAbhUA\/D5oRjsIxnvXaMDg0iAFpdu\/5Ffsj7g3EPdBkiQHNYK7YU1RRx609eH0bZyiIYHdUPw7ikLupvrebZmELqi3mqDFO99u4eISlxFJlUbUND3L4BtmWTWrKwI=",
+ "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-----"
+}';
+ $this->fileAccessHelper
+ ->expects($this->at(0))
+ ->method('file_get_contents')
+ ->with(
+ \OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//core/signature.json'
+ )
+ ->will($this->returnValue($signatureDataFile));
+ $this->fileAccessHelper
+ ->expects($this->at(1))
+ ->method('file_get_contents')
+ ->with(
+ \OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//resources/codesigning/root.crt'
+ )
+ ->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
+
+ $expected = [
+ 'EXCEPTION' => [
+ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
+ 'message' => 'Signature could not get verified.',
+ ]
+ ];
+ $this->assertSame($expected, $this->checker->verifyCoreSignature());
+ }
+
+ public function testVerifyCoreSignatureWithTamperedFiles() {
+ $this->environmentHelper
+ ->expects($this->any())
+ ->method('getServerRoot')
+ ->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/'));
+ $signatureDataFile = '{
+ "hashes": {
+ "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
+ "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
+ },
+ "signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
+ "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-----"
+}';
+ $this->fileAccessHelper
+ ->expects($this->at(0))
+ ->method('file_get_contents')
+ ->with(
+ \OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//core/signature.json'
+ )
+ ->will($this->returnValue($signatureDataFile));
+ $this->fileAccessHelper
+ ->expects($this->at(1))
+ ->method('file_get_contents')
+ ->with(
+ \OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//resources/codesigning/root.crt'
+ )
+ ->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
+
+ $expected = [
+ 'INVALID_HASH' => [
+ 'AnotherFile.txt' => [
+ 'expected' => '1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112',
+ 'current' => '7322348ba269c6d5522efe02f424fa3a0da319a7cd9c33142a5afe32a2d9af2da3a411f086fcfc96ff4301ea566f481dba0960c2abeef3594c4d930462f6584c',
+ ],
+ ],
+ 'FILE_MISSING' => [
+ 'subfolder/file.txt' => [
+ 'expected' => '410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b',
+ 'current' => '',
+ ],
+ ],
+ 'EXTRA_FILE' => [
+ 'UnecessaryFile' => [
+ 'expected' => '',
+ 'current' => 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e',
+ ],
+ ],
+
+ ];
+ $this->assertSame($expected, $this->checker->verifyCoreSignature());
+ }
+
+ public function testVerifyCoreWithInvalidCertificate() {
+ $this->environmentHelper
+ ->expects($this->any())
+ ->method('getServerRoot')
+ ->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/app/'));
+ $signatureDataFile = '{
+ "hashes": {
+ "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
+ "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
+ },
+ "signature": "eXesvDm3pkek12xSwMG10y9suRES79Nye3jYNe5KYq1tTUPqRRNgxmMGAfcUro0zpLeAr2YgHeSMWtglblGOW7pmwGVPZ0O1Y4r1fE6jnep0kW+35PLIaqCorIOnCAtSzDNKBhwd1ow3zW2wC0DFouuEkIO8u5Fw28g8E8dp8zEk1xMblNPy+xtWkmYHrVJ\/dQgun1bYOF2ZFtAzatwndTI\/bGsy1i3Wsl+x6HyWKQdq8y8VObtOqKDH7uERBEpB9DHVyKflj1v1gQuEH6BhaRdATc7ee0MiQdGblraIySwYRdfo2d8i82OVKrenMB3SLwyCvDPyQ9iKpTOnSF52ZBqaqSXKM2N\/RAkweeBFQQCwcHhqxvB0cfbyHcbkOLeCZe\/tsh68IxwTiYgzvLfl7sOZ5arnZbzrPpZmB+hfV2omkoJ1tDwOWz9hEmLLNtfo2OxyUH1m0+XFaC+Gbn4WkVDgf7YZkwUcG+Qoa3oKDNMss8MEyZxewl2iDGZcf402dlidHRprlfmXbAYuVQ08\/a0HxIKYPGh\/nsMGmwnO15CWtFpAbhUA\/D5oRjsIxnvXaMDg0iAFpdu\/5Ffsj7g3EPdBkiQHNYK7YU1RRx609eH0bZyiIYHdUPw7ikLupvrebZmELqi3mqDFO99u4eISlxFJlUbUND3L4BtmWTWrKwI=",
+ "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-----"
+}';
+ $this->fileAccessHelper
+ ->expects($this->at(0))
+ ->method('file_get_contents')
+ ->with(
+ \OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json'
+ )
+ ->will($this->returnValue($signatureDataFile));
+ $this->fileAccessHelper
+ ->expects($this->at(1))
+ ->method('file_get_contents')
+ ->with(
+ \OC::$SERVERROOT . '/tests/data/integritycheck/app//resources/codesigning/root.crt'
+ )
+ ->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
+
+ $expected = [
+ 'EXCEPTION' => [
+ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
+ 'message' => 'Certificate is not valid.',
+ ]
+ ];
+ $this->assertSame($expected, $this->checker->verifyCoreSignature());
+ }
+
+ public function testVerifyCoreWithDifferentScope() {
+ $this->environmentHelper
+ ->expects($this->any())
+ ->method('getServerRoot')
+ ->will($this->returnValue(\OC::$SERVERROOT . '/tests/data/integritycheck/app/'));
+ $signatureDataFile = '{
+ "hashes": {
+ "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
+ "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
+ },
+ "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=",
+ "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-----"
+}';
+ $this->fileAccessHelper
+ ->expects($this->at(0))
+ ->method('file_get_contents')
+ ->with(
+ \OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json'
+ )
+ ->will($this->returnValue($signatureDataFile));
+ $this->fileAccessHelper
+ ->expects($this->at(1))
+ ->method('file_get_contents')
+ ->with(
+ \OC::$SERVERROOT . '/tests/data/integritycheck/app//resources/codesigning/root.crt'
+ )
+ ->will($this->returnValue(file_get_contents(__DIR__ .'/../../data/integritycheck/root.crt')));
+
+ $expected = [
+ 'EXCEPTION' => [
+ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
+ 'message' => 'Certificate is not valid for required scope. (Requested: core, current: CN=SomeApp)',
+ ]
+ ];
+ $this->assertSame($expected, $this->checker->verifyCoreSignature());
+ }
+
+ public function testRunInstanceVerification() {
+ $this->checker = $this->getMockBuilder('\OC\IntegrityCheck\Checker')
+ ->setConstructorArgs([
+ $this->environmentHelper,
+ $this->fileAccessHelper,
+ $this->appLocator,
+ $this->config,
+ $this->cacheFactory,
+ $this->appManager,
+ ])
+ ->setMethods([
+ 'verifyCoreSignature',
+ 'verifyAppSignature',
+ ])
+ ->getMock();
+
+ $this->checker
+ ->expects($this->at(0))
+ ->method('verifyCoreSignature');
+ $this->appLocator
+ ->expects($this->at(0))
+ ->Method('getAllApps')
+ ->will($this->returnValue([
+ 'files',
+ 'calendar',
+ 'contacts',
+ 'dav',
+ ]));
+ $this->appManager
+ ->expects($this->at(0))
+ ->method('isShipped')
+ ->with('files')
+ ->will($this->returnValue(true));
+ $this->checker
+ ->expects($this->at(1))
+ ->method('verifyAppSignature')
+ ->with('files');
+ $this->appManager
+ ->expects($this->at(1))
+ ->method('isShipped')
+ ->with('calendar')
+ ->will($this->returnValue(false));
+ $this->appLocator
+ ->expects($this->at(1))
+ ->method('getAppPath')
+ ->with('calendar')
+ ->will($this->returnValue('/apps/calendar'));
+ $this->fileAccessHelper
+ ->expects($this->at(0))
+ ->method('file_exists')
+ ->with('/apps/calendar/appinfo/signature.json')
+ ->will($this->returnValue(true));
+ $this->checker
+ ->expects($this->at(2))
+ ->method('verifyAppSignature')
+ ->with('calendar');
+ $this->appManager
+ ->expects($this->at(2))
+ ->method('isShipped')
+ ->with('contacts')
+ ->will($this->returnValue(false));
+ $this->appLocator
+ ->expects($this->at(2))
+ ->method('getAppPath')
+ ->with('contacts')
+ ->will($this->returnValue('/apps/contacts'));
+ $this->fileAccessHelper
+ ->expects($this->at(1))
+ ->method('file_exists')
+ ->with('/apps/contacts/appinfo/signature.json')
+ ->will($this->returnValue(false));
+ $this->appManager
+ ->expects($this->at(3))
+ ->method('isShipped')
+ ->with('dav')
+ ->will($this->returnValue(true));
+ $this->checker
+ ->expects($this->at(3))
+ ->method('verifyAppSignature')
+ ->with('dav');
+
+ $this->checker->runInstanceVerification();
+ }
+
+}
diff --git a/tests/lib/integritycheck/helpers/AppLocatorTest.php b/tests/lib/integritycheck/helpers/AppLocatorTest.php
new file mode 100644
index 00000000000..d0083298b22
--- /dev/null
+++ b/tests/lib/integritycheck/helpers/AppLocatorTest.php
@@ -0,0 +1,51 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace Test\IntegrityCheck\Helpers;
+
+use OC\IntegrityCheck\Helpers\AppLocator;
+use Test\TestCase;
+
+class AppLocatorTest extends TestCase {
+ /** @var AppLocator */
+ private $locator;
+
+ public function setUp() {
+ parent::setUp();
+ $this->locator = new AppLocator();
+ }
+
+ public function testGetAppPath() {
+ $this->assertSame(\OC_App::getAppPath('files'), $this->locator->getAppPath('files'));
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage App not found
+ */
+ public function testGetAppPathNotExistentApp() {
+ $this->locator->getAppPath('aTotallyNotExistingApp');
+ }
+
+ public function testGetAllApps() {
+ $this->assertSame(\OC_App::getAllApps(), $this->locator->getAllApps());
+ }
+}
diff --git a/tests/lib/integritycheck/helpers/EnvironmentHelperTest.php b/tests/lib/integritycheck/helpers/EnvironmentHelperTest.php
new file mode 100644
index 00000000000..4a292188e75
--- /dev/null
+++ b/tests/lib/integritycheck/helpers/EnvironmentHelperTest.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace Test\IntegrityCheck\Factories;
+
+use OC\IntegrityCheck\Helpers\EnvironmentHelper;
+use Test\TestCase;
+
+class EnvironmentHelperTest extends TestCase {
+ public function testGetServerRoot() {
+ $factory = new EnvironmentHelper();
+ $this->assertSame(\OC::$SERVERROOT, $factory->getServerRoot());
+ }
+}
diff --git a/tests/lib/integritycheck/helpers/FileAccessHelperTest.php b/tests/lib/integritycheck/helpers/FileAccessHelperTest.php
new file mode 100644
index 00000000000..740b14e61c4
--- /dev/null
+++ b/tests/lib/integritycheck/helpers/FileAccessHelperTest.php
@@ -0,0 +1,43 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+namespace Test\IntegrityCheck\Helpers;
+
+use OC\IntegrityCheck\Helpers\FileAccessHelper;
+use Test\TestCase;
+
+class FileAccessHelperTest extends TestCase {
+ /** @var FileAccessHelper */
+ private $fileAccessHelper;
+
+ public function setUp() {
+ parent::setUp();
+ $this->fileAccessHelper = new FileAccessHelper();
+ }
+
+ public function testReadAndWrite() {
+ $tempManager = \OC::$server->getTempManager();
+ $filePath = $tempManager->getTemporaryFile();
+ $data = 'SomeDataGeneratedByIntegrityCheck';
+
+ $this->fileAccessHelper->file_put_contents($filePath, $data);
+ $this->assertSame($data, $this->fileAccessHelper->file_get_contents($filePath));
+ }
+}
diff --git a/tests/lib/updater.php b/tests/lib/updater.php
index 1651fe1759d..14ae3db3276 100644
--- a/tests/lib/updater.php
+++ b/tests/lib/updater.php
@@ -24,6 +24,7 @@ namespace OC;
use OCP\IConfig;
use OCP\ILogger;
+use OC\IntegrityCheck\Checker;
class UpdaterTest extends \Test\TestCase {
/** @var IConfig */
@@ -34,6 +35,8 @@ class UpdaterTest extends \Test\TestCase {
private $logger;
/** @var Updater */
private $updater;
+ /** @var Checker */
+ private $checker;
public function setUp() {
parent::setUp();
@@ -46,10 +49,14 @@ class UpdaterTest extends \Test\TestCase {
$this->logger = $this->getMockBuilder('\\OCP\\ILogger')
->disableOriginalConstructor()
->getMock();
+ $this->checker = $this->getMockBuilder('\OC\IntegrityCheck\Checker')
+ ->disableOriginalConstructor()
+ ->getMock();
$this->updater = new Updater(
$this->httpHelper,
$this->config,
+ $this->checker,
$this->logger
);
}
@@ -158,11 +165,11 @@ class UpdaterTest extends \Test\TestCase {
*
* @param string $oldVersion
* @param string $newVersion
+ * @param string $allowedVersion
* @param bool $result
*/
public function testIsUpgradePossible($oldVersion, $newVersion, $allowedVersion, $result) {
- $updater = new Updater($this->httpHelper, $this->config, $this->logger);
- $this->assertSame($result, $updater->isUpgradePossible($oldVersion, $newVersion, $allowedVersion));
+ $this->assertSame($result, $this->updater->isUpgradePossible($oldVersion, $newVersion, $allowedVersion));
}
public function testCheckInCache() {
diff --git a/tests/settings/controller/CheckSetupControllerTest.php b/tests/settings/controller/CheckSetupControllerTest.php
index ebba18de5fd..08d4135fa12 100644
--- a/tests/settings/controller/CheckSetupControllerTest.php
+++ b/tests/settings/controller/CheckSetupControllerTest.php
@@ -21,7 +21,10 @@
namespace OC\Settings\Controller;
+use OCP\AppFramework\Http;
+use OCP\AppFramework\Http\DataDisplayResponse;
use OCP\AppFramework\Http\DataResponse;
+use OCP\AppFramework\Http\RedirectResponse;
use OCP\Http\Client\IClientService;
use OCP\IConfig;
use OCP\IL10N;
@@ -29,6 +32,7 @@ use OCP\IRequest;
use OCP\IURLGenerator;
use OC_Util;
use Test\TestCase;
+use OC\IntegrityCheck\Checker;
/**
* Mock version_compare
@@ -63,6 +67,8 @@ class CheckSetupControllerTest extends TestCase {
private $util;
/** @var IL10N */
private $l10n;
+ /** @var Checker */
+ private $checker;
public function setUp() {
parent::setUp();
@@ -86,6 +92,8 @@ class CheckSetupControllerTest extends TestCase {
->will($this->returnCallback(function($message, array $replace) {
return vsprintf($message, $replace);
}));
+ $this->checker = $this->getMockBuilder('\OC\IntegrityCheck\Checker')
+ ->disableOriginalConstructor()->getMock();
$this->checkSetupController = $this->getMockBuilder('\OC\Settings\Controller\CheckSetupController')
->setConstructorArgs([
'settings',
@@ -95,6 +103,7 @@ class CheckSetupControllerTest extends TestCase {
$this->urlGenerator,
$this->util,
$this->l10n,
+ $this->checker,
])
->setMethods(['getCurlVersion'])->getMock();
}
@@ -351,6 +360,8 @@ class CheckSetupControllerTest extends TestCase {
'forwardedForHeadersWorking' => true,
'reverseProxyDocs' => 'reverse-proxy-doc-link',
'isCorrectMemcachedPHPModuleInstalled' => true,
+ 'hasPassedCodeIntegrityCheck' => null,
+ 'codeIntegrityCheckerDocumentation' => null,
]
);
$this->assertEquals($expected, $this->checkSetupController->check());
@@ -366,6 +377,7 @@ class CheckSetupControllerTest extends TestCase {
$this->urlGenerator,
$this->util,
$this->l10n,
+ $this->checker
])
->setMethods(null)->getMock();
@@ -596,4 +608,441 @@ class CheckSetupControllerTest extends TestCase {
->will($this->returnValue([]));
$this->assertSame('', $this->invokePrivate($this->checkSetupController, 'isUsedTlsLibOutdated'));
}
+
+ public function testRescanFailedIntegrityCheck() {
+ $this->checker
+ ->expects($this->once())
+ ->method('runInstanceVerification');
+ $this->urlGenerator
+ ->expects($this->once())
+ ->method('linkToRoute')
+ ->with('settings_admin')
+ ->will($this->returnValue('/admin'));
+
+ $expected = new RedirectResponse('/admin');
+ $this->assertEquals($expected, $this->checkSetupController->rescanFailedIntegrityCheck());
+ }
+
+ public function testGetFailedIntegrityCheckFilesWithNoErrorsFound() {
+ $this->checker
+ ->expects($this->once())
+ ->method('getResults')
+ ->will($this->returnValue([]));
+
+ $expected = new DataDisplayResponse(
+ 'No errors have been found.',
+ Http::STATUS_OK,
+ [
+ 'Content-Type' => 'text/plain',
+ ]
+ );
+ $this->assertEquals($expected, $this->checkSetupController->getFailedIntegrityCheckFiles());
+ }
+
+ public function testGetFailedIntegrityCheckFilesWithSomeErrorsFound() {
+ $this->checker
+ ->expects($this->once())
+ ->method('getResults')
+ ->will($this->returnValue(array ( 'core' => array ( 'EXTRA_FILE' => array('/testfile' => array()), 'INVALID_HASH' => array ( '/.idea/workspace.xml' => array ( 'expected' => 'f1c5e2630d784bc9cb02d5a28f55d6f24d06dae2a0fee685f3c2521b050955d9d452769f61454c9ddfa9c308146ade10546cfa829794448eaffbc9a04a29d216', 'current' => 'ce08bf30bcbb879a18b49239a9bec6b8702f52452f88a9d32142cad8d2494d5735e6bfa0d8642b2762c62ca5be49f9bf4ec231d4a230559d4f3e2c471d3ea094', ), '/lib/private/integritycheck/checker.php' => array ( 'expected' => 'c5a03bacae8dedf8b239997901ba1fffd2fe51271d13a00cc4b34b09cca5176397a89fc27381cbb1f72855fa18b69b6f87d7d5685c3b45aee373b09be54742ea', 'current' => '88a3a92c11db91dec1ac3be0e1c87f862c95ba6ffaaaa3f2c3b8f682187c66f07af3a3b557a868342ef4a271218fe1c1e300c478e6c156c5955ed53c40d06585', ), '/settings/controller/checksetupcontroller.php' => array ( 'expected' => '3e1de26ce93c7bfe0ede7c19cb6c93cadc010340225b375607a7178812e9de163179b0dc33809f451e01f491d93f6f5aaca7929685d21594cccf8bda732327c4', 'current' => '09563164f9904a837f9ca0b5f626db56c838e5098e0ccc1d8b935f68fa03a25c5ec6f6b2d9e44a868e8b85764dafd1605522b4af8db0ae269d73432e9a01e63a', ), ), ), 'bookmarks' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'dav' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'encryption' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'external' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'federation' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'files' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'files_antivirus' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'files_drop' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'files_external' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'files_pdfviewer' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'files_sharing' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'files_trashbin' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'files_versions' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'files_videoviewer' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'firstrunwizard' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'gitsmart' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'logreader' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature could not get verified.', ), ), 'password_policy' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'provisioning_api' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'sketch' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'threatblock' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'two_factor_auth' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'user_ldap' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), 'user_shibboleth' => array ( 'EXCEPTION' => array ( 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ), ), )));
+
+ $expected = new DataDisplayResponse(
+ 'Technical information
+=====================
+The following list covers which files have failed the integrity check. Please read
+the previous linked documentation to learn more about the errors and how to fix
+them.
+
+Results
+=======
+- core
+ - EXTRA_FILE
+ - /testfile
+ - INVALID_HASH
+ - /.idea/workspace.xml
+ - /lib/private/integritycheck/checker.php
+ - /settings/controller/checksetupcontroller.php
+- bookmarks
+ - EXCEPTION
+ - OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ - Signature data not found.
+- dav
+ - EXCEPTION
+ - OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ - Signature data not found.
+- encryption
+ - EXCEPTION
+ - OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ - Signature data not found.
+- external
+ - EXCEPTION
+ - OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ - Signature data not found.
+- federation
+ - EXCEPTION
+ - OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ - Signature data not found.
+- files
+ - EXCEPTION
+ - OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ - Signature data not found.
+- files_antivirus
+ - EXCEPTION
+ - OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ - Signature data not found.
+- files_drop
+ - EXCEPTION
+ - OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ - Signature data not found.
+- files_external
+ - EXCEPTION
+ - OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ - Signature data not found.
+- files_pdfviewer
+ - EXCEPTION
+ - OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ - Signature data not found.
+- files_sharing
+ - EXCEPTION
+ - OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ - Signature data not found.
+- files_trashbin
+ - EXCEPTION
+ - OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ - Signature data not found.
+- files_versions
+ - EXCEPTION
+ - OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ - Signature data not found.
+- files_videoviewer
+ - EXCEPTION
+ - OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ - Signature data not found.
+- firstrunwizard
+ - EXCEPTION
+ - OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ - Signature data not found.
+- gitsmart
+ - EXCEPTION
+ - OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ - Signature data not found.
+- logreader
+ - EXCEPTION
+ - OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ - Signature could not get verified.
+- password_policy
+ - EXCEPTION
+ - OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ - Signature data not found.
+- provisioning_api
+ - EXCEPTION
+ - OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ - Signature data not found.
+- sketch
+ - EXCEPTION
+ - OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ - Signature data not found.
+- threatblock
+ - EXCEPTION
+ - OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ - Signature data not found.
+- two_factor_auth
+ - EXCEPTION
+ - OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ - Signature data not found.
+- user_ldap
+ - EXCEPTION
+ - OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ - Signature data not found.
+- user_shibboleth
+ - EXCEPTION
+ - OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ - Signature data not found.
+
+Raw output
+==========
+Array
+(
+ [core] => Array
+ (
+ [EXTRA_FILE] => Array
+ (
+ [/testfile] => Array
+ (
+ )
+
+ )
+
+ [INVALID_HASH] => Array
+ (
+ [/.idea/workspace.xml] => Array
+ (
+ [expected] => f1c5e2630d784bc9cb02d5a28f55d6f24d06dae2a0fee685f3c2521b050955d9d452769f61454c9ddfa9c308146ade10546cfa829794448eaffbc9a04a29d216
+ [current] => ce08bf30bcbb879a18b49239a9bec6b8702f52452f88a9d32142cad8d2494d5735e6bfa0d8642b2762c62ca5be49f9bf4ec231d4a230559d4f3e2c471d3ea094
+ )
+
+ [/lib/private/integritycheck/checker.php] => Array
+ (
+ [expected] => c5a03bacae8dedf8b239997901ba1fffd2fe51271d13a00cc4b34b09cca5176397a89fc27381cbb1f72855fa18b69b6f87d7d5685c3b45aee373b09be54742ea
+ [current] => 88a3a92c11db91dec1ac3be0e1c87f862c95ba6ffaaaa3f2c3b8f682187c66f07af3a3b557a868342ef4a271218fe1c1e300c478e6c156c5955ed53c40d06585
+ )
+
+ [/settings/controller/checksetupcontroller.php] => Array
+ (
+ [expected] => 3e1de26ce93c7bfe0ede7c19cb6c93cadc010340225b375607a7178812e9de163179b0dc33809f451e01f491d93f6f5aaca7929685d21594cccf8bda732327c4
+ [current] => 09563164f9904a837f9ca0b5f626db56c838e5098e0ccc1d8b935f68fa03a25c5ec6f6b2d9e44a868e8b85764dafd1605522b4af8db0ae269d73432e9a01e63a
+ )
+
+ )
+
+ )
+
+ [bookmarks] => Array
+ (
+ [EXCEPTION] => Array
+ (
+ [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ [message] => Signature data not found.
+ )
+
+ )
+
+ [dav] => Array
+ (
+ [EXCEPTION] => Array
+ (
+ [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ [message] => Signature data not found.
+ )
+
+ )
+
+ [encryption] => Array
+ (
+ [EXCEPTION] => Array
+ (
+ [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ [message] => Signature data not found.
+ )
+
+ )
+
+ [external] => Array
+ (
+ [EXCEPTION] => Array
+ (
+ [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ [message] => Signature data not found.
+ )
+
+ )
+
+ [federation] => Array
+ (
+ [EXCEPTION] => Array
+ (
+ [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ [message] => Signature data not found.
+ )
+
+ )
+
+ [files] => Array
+ (
+ [EXCEPTION] => Array
+ (
+ [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ [message] => Signature data not found.
+ )
+
+ )
+
+ [files_antivirus] => Array
+ (
+ [EXCEPTION] => Array
+ (
+ [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ [message] => Signature data not found.
+ )
+
+ )
+
+ [files_drop] => Array
+ (
+ [EXCEPTION] => Array
+ (
+ [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ [message] => Signature data not found.
+ )
+
+ )
+
+ [files_external] => Array
+ (
+ [EXCEPTION] => Array
+ (
+ [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ [message] => Signature data not found.
+ )
+
+ )
+
+ [files_pdfviewer] => Array
+ (
+ [EXCEPTION] => Array
+ (
+ [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ [message] => Signature data not found.
+ )
+
+ )
+
+ [files_sharing] => Array
+ (
+ [EXCEPTION] => Array
+ (
+ [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ [message] => Signature data not found.
+ )
+
+ )
+
+ [files_trashbin] => Array
+ (
+ [EXCEPTION] => Array
+ (
+ [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ [message] => Signature data not found.
+ )
+
+ )
+
+ [files_versions] => Array
+ (
+ [EXCEPTION] => Array
+ (
+ [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ [message] => Signature data not found.
+ )
+
+ )
+
+ [files_videoviewer] => Array
+ (
+ [EXCEPTION] => Array
+ (
+ [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ [message] => Signature data not found.
+ )
+
+ )
+
+ [firstrunwizard] => Array
+ (
+ [EXCEPTION] => Array
+ (
+ [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ [message] => Signature data not found.
+ )
+
+ )
+
+ [gitsmart] => Array
+ (
+ [EXCEPTION] => Array
+ (
+ [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ [message] => Signature data not found.
+ )
+
+ )
+
+ [logreader] => Array
+ (
+ [EXCEPTION] => Array
+ (
+ [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ [message] => Signature could not get verified.
+ )
+
+ )
+
+ [password_policy] => Array
+ (
+ [EXCEPTION] => Array
+ (
+ [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ [message] => Signature data not found.
+ )
+
+ )
+
+ [provisioning_api] => Array
+ (
+ [EXCEPTION] => Array
+ (
+ [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ [message] => Signature data not found.
+ )
+
+ )
+
+ [sketch] => Array
+ (
+ [EXCEPTION] => Array
+ (
+ [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ [message] => Signature data not found.
+ )
+
+ )
+
+ [threatblock] => Array
+ (
+ [EXCEPTION] => Array
+ (
+ [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ [message] => Signature data not found.
+ )
+
+ )
+
+ [two_factor_auth] => Array
+ (
+ [EXCEPTION] => Array
+ (
+ [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ [message] => Signature data not found.
+ )
+
+ )
+
+ [user_ldap] => Array
+ (
+ [EXCEPTION] => Array
+ (
+ [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ [message] => Signature data not found.
+ )
+
+ )
+
+ [user_shibboleth] => Array
+ (
+ [EXCEPTION] => Array
+ (
+ [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
+ [message] => Signature data not found.
+ )
+
+ )
+
+)
+',
+ Http::STATUS_OK,
+ [
+ 'Content-Type' => 'text/plain',
+ ]
+ );
+ $this->assertEquals($expected, $this->checkSetupController->getFailedIntegrityCheckFiles());
+ }
}