diff --git a/.devcontainer/postCreateCommands.sh b/.devcontainer/postCreateCommands.sh index 71959e0e..1195c417 100755 --- a/.devcontainer/postCreateCommands.sh +++ b/.devcontainer/postCreateCommands.sh @@ -1,5 +1,8 @@ #!/bin/bash +bash -i -c 'nvm install 20' +bash -i -c 'nvm use 20' + echo "setup php-scoper" composer global require humbug/php-scoper $(composer config home)/vendor/bin/php-scoper completion bash >> $HOME.bash_completion @@ -22,10 +25,9 @@ echo ". /usr/share/bash-completion/bash_completion" >> /home/vscode/.bashrc NEXTCLOUD_VERSION=$(grep -oP -m 1 "[0-9]+\.[0-9]+\.[0-9]+" install.sh) mkdir -p ~/.ssh/ -ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts rm -rf nextcloud-server/ -git clone --depth 1 --recurse-submodules --single-branch --branch v$NEXTCLOUD_VERSION git@github.com:nextcloud/server.git ./nextcloud-server +git clone --depth 1 --recurse-submodules --single-branch --branch v$NEXTCLOUD_VERSION https://github.com/nextcloud/server.git ./nextcloud-server cd nextcloud-server git submodule update --init cd - diff --git a/.vscode/launch.json b/.vscode/launch.json index b78774f5..ddd9285f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -11,7 +11,7 @@ "port": 9080, "pathMappings": { "/var/www/html/": "${workspaceFolder}/nextcloud-server", - "/var/www/html/apps/gdatavaas": "${workspaceFolder}/", + "/var/www/html/apps/gdatavaas": "${workspaceFolder}/build/artifacts/gdatavaas", }, "runtimeArgs": [ "-dxdebug.mode=debug", diff --git a/.vscode/settings.json b/.vscode/settings.json index 74ae131a..02eac3c2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,4 +6,5 @@ "[php]": { "editor.defaultFormatter": "junstyle.php-cs-fixer" }, + "php.suggest.basic": false } \ No newline at end of file diff --git a/Dockerfile.Nextcloud b/Dockerfile.Nextcloud index bde95499..2a7de3cd 100644 --- a/Dockerfile.Nextcloud +++ b/Dockerfile.Nextcloud @@ -31,8 +31,6 @@ RUN echo "error_log = /var/www/html/data/php.log" >> "$PHP_INI_DIR/php.ini" RUN sed -i 's/#LogLevel info ssl:warn/LogLevel debug/g' /etc/apache2/sites-available/000-default.conf COPY xdebug.ini /tmp/xdebug.ini -RUN if [[ "$INSTALL_XDEBUG" == "1" ]]; then \ - install-php-extensions gd xdebug; \ - mv /tmp/xdebug.ini /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini; \ - fi +RUN install-php-extensions gd xdebug; +RUN mv /tmp/xdebug.ini /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini; diff --git a/composer.json b/composer.json index b9573f14..70ccdda1 100644 --- a/composer.json +++ b/composer.json @@ -22,6 +22,7 @@ "autoload": { "psr-4": { "OCP\\": "vendor/nextcloud/ocp/OCP", + "OCA\\Files_Trashbin\\": "nextcloud-server/apps/files_trashbin", "OCA\\GDataVaas\\": "lib" } }, diff --git a/install.sh b/install.sh index 31560fd8..a22cd709 100755 --- a/install.sh +++ b/install.sh @@ -1,7 +1,7 @@ #!/bin/bash set -e -export NEXTCLOUD_VERSION=${1:-30.0.0} +export NEXTCLOUD_VERSION=${1:-30.0.2} export INSTALL_XDEBUG=${2:-1} export XDEBUG_MODE=${XDEBUG_MODE:-develop} diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 29aff2de..741826ba 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -4,25 +4,16 @@ namespace OCA\GDataVaas\AppInfo; -use OC\Files\Filesystem; -use OCA\GDataVaas\AvirWrapper; -use OCA\GDataVaas\CacheEntryListener; use OCA\GDataVaas\Db\DbFileMapper; -use OCA\GDataVaas\Service\MailService; +use OCA\GDataVaas\EventListener\FileEventsListener; use OCA\GDataVaas\Service\TagService; -use OCA\GDataVaas\Service\VerdictService; use OCA\GDataVaas\SystemTag\SystemTagObjectMapperWithoutActivityFactory; -use OCP\Activity\IManager; -use OCP\App\IAppManager; use OCP\AppFramework\App; use OCP\AppFramework\Bootstrap\IBootContext; use OCP\AppFramework\Bootstrap\IBootstrap; use OCP\AppFramework\Bootstrap\IRegistrationContext; use OCP\Collaboration\Resources\LoadAdditionalScriptsEvent; use OCP\EventDispatcher\IEventDispatcher; -use OCP\Files\IHomeStorage; -use OCP\Files\Storage\IStorage; -use OCP\IAppConfig; use OCP\IDBConnection; use OCP\SystemTag\ISystemTagManager; use OCP\SystemTag\ISystemTagObjectMapper; @@ -43,6 +34,7 @@ public function __construct() { $container = $this->getContainer(); $eventDispatcher = $container->get(IEventDispatcher::class); + assert($eventDispatcher instanceof IEventDispatcher); $eventDispatcher->addListener(LoadAdditionalScriptsEvent::class, function () { Util::addScript(self::APP_ID, 'gdatavaas-files-action'); }); @@ -53,9 +45,9 @@ public function __construct() { * @return void */ public function register(IRegistrationContext $context): void { - require_once file_exists(__DIR__.'/../../vendor/scoper-autoload.php') - ? __DIR__.'/../../vendor/scoper-autoload.php' - : __DIR__.'/../../vendor/autoload.php'; + require_once file_exists(__DIR__ . '/../../vendor/scoper-autoload.php') + ? __DIR__ . '/../../vendor/scoper-autoload.php' + : __DIR__ . '/../../vendor/autoload.php'; // Manually register TagService so that we can customize the DI used for $silentTagMapper $context->registerService(TagService::class, function ($c) { @@ -69,50 +61,7 @@ public function register(IRegistrationContext $context): void { return new TagService($logger, $systemTagManager, $standardTagMapper, $silentTagMapper, $dbFileMapper); }, true); - CacheEntryListener::register($context); - - // Util::connection is deprecated, but required ATM by FileSystem::addStorageWrapper - Util::connectHook('OC_Filesystem', 'preSetup', $this, 'setupWrapper'); - } - - /** - * * Add wrapper for local storages - */ - public function setupWrapper(): void { - Filesystem::addStorageWrapper( - 'oc_gdata_vaas', - function (string $mountPoint, IStorage $storage) { - /* - if ($storage->instanceOfStorage(Jail::class)) { - // No reason to wrap jails again - return $storage; - } - */ - - $container = $this->getContainer(); - $verdictService = $container->get(VerdictService::class); - $mailService = $container->get(MailService::class); - $appConfig = $container->get(IAppConfig::class); - // $l10n = $container->get(IL10N::class); - $logger = $container->get(LoggerInterface::class); - $activityManager = $container->get(IManager::class); - $eventDispatcher = $container->get(IEventDispatcher::class); - $appManager = $container->get(IAppManager::class); - return new AvirWrapper([ - 'storage' => $storage, - 'verdictService' => $verdictService, - 'mailService' => $mailService, - 'appConfig' => $appConfig, - //'l10n' => $l10n, - 'logger' => $logger, - 'activityManager' => $activityManager, - 'isHomeStorage' => $storage->instanceOfStorage(IHomeStorage::class), - 'eventDispatcher' => $eventDispatcher, - 'trashEnabled' => $appManager->isEnabledForUser('files_trashbin'), - ]); - }, - 1 - ); + FileEventsListener::register($context); } public function boot(IBootContext $context): void { diff --git a/lib/AvirWrapper.php b/lib/AvirWrapper.php deleted file mode 100644 index fef698f5..00000000 --- a/lib/AvirWrapper.php +++ /dev/null @@ -1,238 +0,0 @@ - - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - -namespace OCA\GDataVaas; - -use Coduo\PHPHumanizer\NumberHumanizer; -use GuzzleHttp\Exception\ServerException; -use OC\Files\Storage\Wrapper\Wrapper; -use OCA\Files_Trashbin\Trash\ITrashManager; -use OCA\GDataVaas\Activity\Provider; -use OCA\GDataVaas\AppInfo\Application; -use OCA\GDataVaas\Service\MailService; -use OCA\GDataVaas\Service\VerdictService; -use OCP\Activity\IManager as ActivityManager; -use OCP\EventDispatcher\IEventDispatcher; -use OCP\Files\EntityTooLargeException; -use OCP\Files\InvalidContentException; -use OCP\Files\NotFoundException; -use OCP\Files\NotPermittedException; -use OCP\IAppConfig; -use OCP\IL10N; -use Psr\Log\LoggerInterface; -use VaasSdk\Exceptions\FileDoesNotExistException; -use VaasSdk\Exceptions\InvalidSha256Exception; -use VaasSdk\Exceptions\TimeoutException; -use VaasSdk\Exceptions\UploadFailedException; -use VaasSdk\Exceptions\VaasAuthenticationException; -use VaasSdk\Message\Verdict; - -class AvirWrapper extends Wrapper { - /** - * Modes that are used for writing - * @var array - */ - private $writingModes = ['r+', 'w', 'w+', 'a', 'a+', 'x', 'x+', 'c', 'c+']; - - protected VerdictService $verdictService; - protected MailService $mailService; - protected IAppConfig $appConfig; - - /** @var IL10N */ - protected $l10n; - - /** @var LoggerInterface */ - protected $logger; - - /** @var ActivityManager */ - protected $activityManager; - - /** @var bool */ - protected $isHomeStorage; - - /** @var bool */ - private $shouldScan = true; - - /** @var bool */ - private $trashEnabled; - - /** - * @param array $parameters - */ - public function __construct($parameters) { - parent::__construct($parameters); - $this->verdictService = $parameters['verdictService']; - $this->mailService = $parameters['mailService']; - $this->appConfig = $parameters['appConfig']; - $this->logger = $parameters['logger']; - $this->activityManager = $parameters['activityManager']; - $this->isHomeStorage = $parameters['isHomeStorage']; - $this->trashEnabled = $parameters['trashEnabled']; - - /** @var IEventDispatcher $eventDispatcher */ - $eventDispatcher = $parameters['eventDispatcher']; - } - - /** - * Asynchronously scan data that are written to the file - * @param string $path - * @param string $mode - * @return resource | false - */ - public function fopen($path, $mode) { - $stream = $this->storage->fopen($path, $mode); - - /* - * Only check when - * - it is a resource - * - it is a writing mode - * - if it is a homestorage it starts with files/ - * - if it is not a homestorage we always wrap (external storages) - */ - if ($this->shouldWrap($path) && is_resource($stream) && $this->isWritingMode($mode)) { - $stream = $this->wrapSteam($path, $stream); - } - return $stream; - } - - public function writeStream(string $path, $stream, ?int $size = null): int { - if ($this->shouldWrap($path)) { - $stream = $this->wrapSteam($path, $stream); - } - return parent::writeStream($path, $stream, $size); - } - - public function rename($source, $target) { - if ($this->shouldWrap($source)) { - // After the upload apps/dav/lib/Connector/Sabre/File.php calls moveFromStorage which calls rename - $this->logger->debug(sprintf('rename(%s, %s)', $source, $target)); - $this->verdictService->onRename($this->getLocalFile($source), $this->getLocalFile($target)); - } - return parent::rename($source, $target); - } - - private function shouldWrap(string $path): bool { - return $this->shouldScan - && (!$this->isHomeStorage - || (strpos($path, 'files/') === 0 - || strpos($path, '/files/') === 0) - ); - } - - private function wrapSteam(string $path, $stream) { - try { - $logger = $this->logger; - return CallbackReadDataWrapper::wrap( - $stream, - null, - null, - function () use ($path, $logger) { - $localPath = $this->getLocalFile($path); - $filesize = $this->filesize($path); - $logger->debug('Closing ' . $localPath . ' with size ' . $filesize); - - if (!$this->verdictService->isAllowedToScan($localPath)) { - return; - } - - if ($filesize > VerdictService::MAX_FILE_SIZE) { - return; - } - - try { - $verdict = $this->verdictService->scan($localPath); - } catch (EntityTooLargeException) { - $this->logger->error("File $localPath is larger than " . NumberHumanizer::binarySuffix(VerdictService::MAX_FILE_SIZE, 'de')); - } catch (FileDoesNotExistException) { - $this->logger->error("File $localPath does not exist on upload"); - } catch (InvalidSha256Exception) { - $this->logger->error("Invalid SHA256 for file $localPath on upload"); - } catch (NotFoundException) { - $this->logger->error("File $localPath not found on upload"); - } catch (NotPermittedException) { - $this->logger->error("Current settings do not permit scanning file $localPath on upload"); - } catch (TimeoutException) { - $this->logger->error("Scanning timed out for file $localPath on upload"); - } catch (UploadFailedException|ServerException) { - $this->logger->error("File $localPath could not be scanned on upload with GData VaaS because there was a temporary upstream server error"); - } catch (VaasAuthenticationException) { - $this->logger->error('Authentication for VaaS scan failed. Please check your credentials.'); - } catch (\Exception $e) { - $this->logger->error('Unexpected error while scanning file ' . $localPath . ' on upload: ' . $e->getMessage()); - } - $logger->debug('Verdict for ' . $localPath . ' is ' . $verdict->Verdict->value); - - if ($verdict->Verdict == Verdict::MALICIOUS) { - $logger->debug('Removing malicious file ' . $localPath); - - //prevent from going to trashbin - if ($this->trashEnabled) { - /** @var ITrashManager $trashManager */ - $trashManager = \OC::$server->query(ITrashManager::class); - $trashManager->pauseTrash(); - } - - $owner = $this->getOwner($path); - $this->unlink($path); - - if ($this->trashEnabled) { - /** @var ITrashManager $trashManager */ - $trashManager = \OC::$server->query(ITrashManager::class); - $trashManager->resumeTrash(); - } - - $this->logger->warning( - 'Infected file deleted. ' . $verdict->Detection - . ' Account: ' . $owner . ' Path: ' . $path, - ['app' => 'gdatavaas'] - ); - - $activity = $this->activityManager->generateEvent(); - $activity->setApp(Application::APP_ID) - ->setSubject(Provider::SUBJECT_VIRUS_DETECTED_UPLOAD, [$verdict->Detection ?? 'no_detection_name']) - ->setMessage(Provider::MESSAGE_FILE_DELETED) - ->setObject('', 0, $path) - ->setAffectedUser($owner) - ->setType(Provider::TYPE_VIRUS_DETECTED); - $this->activityManager->publish($activity); - - if ($this->appConfig->getValueBool(Application::APP_ID, 'sendMailOnVirusUpload')) { - $this->mailService->notifyMaliciousUpload($verdict, $path, $owner, $filesize); - } - - throw new InvalidContentException( - sprintf( - 'Virus %s is detected in the file. Upload cannot be completed.', - $verdict->Detection - ) - ); - } - } - ); - } catch (\Exception $e) { - $this->logger->error($e->getMessage(), ['exception' => $e]); - } - return $stream; - } - - /** - * Checks whether passed mode is suitable for writing - * @param string $mode - * @return bool - */ - private function isWritingMode($mode) { - // Strip unessential binary/text flags - $cleanMode = str_replace( - ['t', 'b'], - ['', ''], - $mode - ); - return in_array($cleanMode, $this->writingModes); - } -} diff --git a/lib/CacheEntryListener.php b/lib/CacheEntryListener.php deleted file mode 100644 index 5130e737..00000000 --- a/lib/CacheEntryListener.php +++ /dev/null @@ -1,54 +0,0 @@ -logger = $logger; - $this->tagService = $tagService; - $this->verdictService = $verdictService; - } - - public static function register(IRegistrationContext $context): void { - $context->registerEventListener(CacheEntryInsertedEvent::class, CacheEntryListener::class); - $context->registerEventListener(CacheEntryUpdatedEvent::class, CacheEntryListener::class); - } - - public function handle(Event $event): void { - $this->logger->debug("CacheEntryListener"); - if (!$event instanceof AbstractCacheEvent) { - return; - } - - $storage = $event->getStorage(); - $path = $event->getPath(); - $fileId = $event->getFileId(); - - $this->logger->debug("GotFields"); - if (self::shouldTag($path) && !$this->tagService->hasAnyVaasTag($fileId)) { - $this->logger->debug("Handling " . get_class($event) . " for " . $path); - - $this->verdictService->tagLastScannedFile($storage->getLocalFile($path), $fileId); - } - } - - private static function shouldTag(string $path): bool { - return str_starts_with($path, 'files/'); - } -} diff --git a/lib/CallbackReadDataWrapper.php b/lib/CallbackReadDataWrapper.php deleted file mode 100644 index b854fe25..00000000 --- a/lib/CallbackReadDataWrapper.php +++ /dev/null @@ -1,60 +0,0 @@ - [ - 'source' => $source, - 'readData' => $read, - 'write' => $write, - 'close' => $close, - 'readDir' => $readDir, - 'preClose' => $preClose - ] - ]); - return Wrapper::wrapSource($source, $context, 'callbackReadData', self::class); - } - - /** - * @return true - */ - protected function open() { - $context = $this->loadContext('callbackReadData'); - - $this->readDataCallback = $context['readData']; - $this->writeCallback = $context['write']; - $this->closeCallback = $context['close']; - $this->readDirCallBack = $context['readDir']; - return true; - } - - public function stream_read($count) { - $result = parent::stream_read($count); - if (is_callable($this->readDataCallback)) { - call_user_func($this->readDataCallback, strlen($result), $result); - } - return $result; - } -} diff --git a/lib/EventListener/FileEventsListener.php b/lib/EventListener/FileEventsListener.php new file mode 100644 index 00000000..f6f91aa5 --- /dev/null +++ b/lib/EventListener/FileEventsListener.php @@ -0,0 +1,117 @@ + */ +class FileEventsListener implements IEventListener { + public function __construct( + private IUserSession $userSession, + private LoggerInterface $logger, + private IConfig $config, + private Server $server, + private IRequest $request, + private VerdictService $verdictService, + private FileService $fileService, + private TagService $tagService, + private IAppConfig $appConfig, + private MailService $mailService, + ) { + } + + public static function register(IRegistrationContext $context): void { + $context->registerEventListener(NodeWrittenEvent::class, self::class); + } + + public function handle(Event $event): void { + if ($event instanceof NodeWrittenEvent) { + $node = $event->getNode(); + if ($node->getType() !== \OCP\Files\FileInfo::TYPE_FILE) { + return; + } + try { + $verdict = $this->verdictService->scanFileById($node->getId()); + } catch (\Exception $e) { + $unscannedTagIsDisabled = $this->appConfig->getValueBool(Application::APP_ID, 'disableUnscannedTag'); + if (!$unscannedTagIsDisabled) { + $this->tagService->setTag($node->getId(), TagService::UNSCANNED, silent: true); + } + $this->logger->error("Failed to scan uploaded file '{$node->getName()}' with ID '{$node->getId()}': {$e->getMessage()}"); + return; + } + + if ($verdict->Verdict->value == TagService::MALICIOUS) { + $this->sendErrorResponse(new VirusFoundException($verdict, $node->getName(), $node->getId())); + $this->fileService->deleteFile($node->getId()); + if ($this->appConfig->getValueBool(Application::APP_ID, 'sendMailOnVirusUpload')) { + $this->mailService->notifyMaliciousUpload($verdict, $node->getPath(), $this->userSession->getUser()->getUID(), $node->getSize()); + } + exit; + } + } + } + + public function generateBody(Exception $ex): mixed { + if ($this->acceptHtml()) { + $templateName = 'exception'; + $renderAs = 'guest'; + $templateName = 'exception'; + } else { + $templateName = 'xml_exception'; + $renderAs = null; + $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); + } + + $debug = $this->config->getSystemValueBool('debug', false); + + $content = new OC_Template('gdatavaas', $templateName, $renderAs); + $content->assign('title', 'Error'); + $content->assign('message', $ex->getMessage()); + $content->assign('remoteAddr', $this->request->getRemoteAddress()); + $content->assign('requestID', $this->request->getId()); + $content->assign('debugMode', $debug); + $content->assign('errorClass', get_class($ex)); + $content->assign('errorMsg', $ex->getMessage()); + $content->assign('errorCode', $ex->getCode()); + $content->assign('file', $ex->getFile()); + $content->assign('line', $ex->getLine()); + $content->assign('exception', $ex); + $contentString = $content->fetchPage(); + return $contentString; + } + + private function acceptHtml(): bool { + foreach (explode(',', $this->request->getHeader('Accept')) as $part) { + $subparts = explode(';', $part); + if (str_ends_with($subparts[0], '/html')) { + return true; + } + } + return false; + } + + private function sendErrorResponse(Exception $ex): void { + $this->server->httpResponse->setBody($this->generateBody($ex)); + $this->server->httpResponse->setStatus(415); + $this->server->sapi->sendResponse($this->server->httpResponse); + } +} diff --git a/lib/Exceptions/VirusFoundException.php b/lib/Exceptions/VirusFoundException.php new file mode 100644 index 00000000..f06c5a37 --- /dev/null +++ b/lib/Exceptions/VirusFoundException.php @@ -0,0 +1,14 @@ +Detection, 415); + } +} \ No newline at end of file diff --git a/lib/Service/FileService.php b/lib/Service/FileService.php index d3b53cbf..f0875a87 100644 --- a/lib/Service/FileService.php +++ b/lib/Service/FileService.php @@ -3,6 +3,8 @@ namespace OCA\GDataVaas\Service; use OCA\GDataVaas\AppInfo\Application; +use OCP\App\IAppManager; +use OCA\Files_Trashbin\Trash\ITrashManager; use OCP\Files\Config\IUserMountCache; use OCP\Files\InvalidPathException; use OCP\Files\IRootFolder; @@ -19,11 +21,15 @@ class FileService { private IRootFolder $rootFolder; private IAppConfig $appConfig; private LoggerInterface $logger; + private IAppManager $appManager; + private ITrashManager $trashManager; - public function __construct(LoggerInterface $logger, IUserMountCache $userMountCache, IRootFolder $rootFolder, IAppConfig $appConfig) { + public function __construct(LoggerInterface $logger, IUserMountCache $userMountCache, IRootFolder $rootFolder, IAppConfig $appConfig, IAppManager $appManager, ITrashManager $trashManager) { $this->userMountCache = $userMountCache; $this->rootFolder = $rootFolder; $this->appConfig = $appConfig; + $this->appManager = $appManager; + $this->trashManager = $trashManager; $this->logger = $logger; } @@ -101,4 +107,18 @@ public function moveFileToQuarantineFolderIfDefined(int $fileId): void { $file->move($quarantine->getPath() . '/' . $file->getName()); $this->logger->info("File " . $file->getName() . " (" . $fileId . ") moved to quarantine folder."); } + + public function deleteFile(int $fileId): void { + $file = $this->getNodeFromFileId($fileId); + $file->unlock(\OCP\Lock\ILockingProvider::LOCK_SHARED); + $trashEnabled = $this->appManager->isEnabledForUser('files_trashbin'); + if ($trashEnabled) { + $this->trashManager->pauseTrash(); + } + $file->delete(); + if ($trashEnabled) { + $this->trashManager->resumeTrash(); + } + $this->logger->info("File " . $file->getName() . " (" . $fileId . ") deleted."); + } } diff --git a/scoper.inc.php b/scoper.inc.php index 06443fad..262dfda6 100644 --- a/scoper.inc.php +++ b/scoper.inc.php @@ -19,7 +19,31 @@ // false, // ), // ); -$excludedFiles = ['templates/admin.php']; +$excludedFiles = [ + 'css/style.css', + 'LICENSES/AGPL-3.0-or-later.txt' +]; +$excludedFolders = array_merge( + array_map( + static fn (SplFileInfo $fileInfo) => $fileInfo->getPathname(), + iterator_to_array( + Finder::create() + ->in('templates') + ->files(), + false, + ), + ), + array_map( + static fn (SplFileInfo $fileInfo) => $fileInfo->getPathname(), + iterator_to_array( + Finder::create() + ->in('src') + ->files(), + false, + ), +)); + +$excludedFiles = array_merge($excludedFiles, $excludedFolders); return [ // The prefix configuration. If a non-null value is used, a random prefix @@ -42,11 +66,27 @@ 'finders' => [ Finder::create() ->files() + ->notName('babel.config.js') + ->notName('compose-install.yaml') + ->notName('composer.local.*') + ->notName('devcontainer.yaml') + ->notName('Dockerfile.Nextcloud') + ->notName('empty-skeleton.config.php') + ->notName('*.sh') + ->notName('Makefile') + ->notName('*.ini') + ->notName('psalm.xml') + ->notName('start-dev-environment*') + ->notName('scoper.inc.php') + ->notName('stylelint.config.js') + ->notName('use-*-vaas.sh') + ->notName('webpack.config.js') + ->notName('xdebug.*') + ->notName('babel.config.js') ->ignoreVCS(true) ->ignoreDotFiles(true) ->exclude([ 'build', - '.devcontainer', 'nextcloud-server', 'tests', 'tmp', @@ -61,7 +101,6 @@ // // For more see: https://github.com/humbug/php-scoper/blob/master/docs/configuration.md#patchers 'exclude-files' => [ - // 'src/an-excluded-file.php', ...$excludedFiles, ], @@ -106,10 +145,12 @@ static function (string $filePath, string $prefix, string $contents): string { 'OC\Files', 'OC\SystemTag', 'Symfony', - 'Icewind' + 'Icewind', + 'Sabre\DAV' ], 'exclude-classes' => [ 'OC', + 'OC_Template' ], 'exclude-functions' => [ ], diff --git a/templates/exception.php b/templates/exception.php new file mode 100644 index 00000000..a6455963 --- /dev/null +++ b/templates/exception.php @@ -0,0 +1,50 @@ +'); + p($e->getTraceAsString()); + print_unescaped(''); + + if ($e->getPrevious() !== null) { + print_unescaped('
'); + print_unescaped('

'); + p($l->t('Previous')); + print_unescaped('

'); + + print_exception($e->getPrevious(), $l); + } +} + +?> +
+

t($_['title'])) ?>

+

t($_['message'])) ?>

+ +

t('Technical details')) ?>

+ + + +
+

t('Trace')) ?>

+ + +
diff --git a/templates/xml_exception.php b/templates/xml_exception.php new file mode 100644 index 00000000..da8f7006 --- /dev/null +++ b/templates/xml_exception.php @@ -0,0 +1,42 @@ +getTraceAsString()); + + if ($e->getPrevious() !== null) { + print_unescaped(''); + print_exception($e->getPrevious(), $l); + print_unescaped(''); + } +} + +print_unescaped('' . "\n"); +?> + + t($_['title'])) ?> + + t($_['message'])) ?> + + + + + + + + + + + + + + + + + + + diff --git a/tests/bats/functionality-parallel.bats b/tests/bats/functionality-parallel.bats index 126d2183..9f72b336 100755 --- a/tests/bats/functionality-parallel.bats +++ b/tests/bats/functionality-parallel.bats @@ -39,7 +39,7 @@ setup_file() { echo "Actual: $RESULT" curl --silent -q -u admin:admin -X DELETE http://$HOSTNAME/remote.php/dav/files/admin/functionality-parallel.eicar.com.txt || echo "file not found" - [[ "$RESULT" =~ "Upload cannot be completed." ]] + [[ "$RESULT" =~ "Virus found" ]] } @test "test admin clean upload" { @@ -77,7 +77,7 @@ setup_file() { echo "Actual: $RESULT" $DOCKER_EXEC_WITH_USER -i nextcloud-container php occ config:app:get gdatavaas clientSecret curl --silent -q -u $TESTUSER:$TESTUSER_PASSWORD -X DELETE http://$HOSTNAME/remote.php/dav/files/$TESTUSER/functionality-parallel.eicar.com.txt || echo "file not found" - [[ "$RESULT" =~ "Upload cannot be completed." ]] + [[ "$RESULT" =~ "Virus found" ]] } @test "test testuser clean Upload" { @@ -100,7 +100,7 @@ setup_file() { docker cp $FOLDER_PREFIX/too-large.dat nextcloud-container:/var/www/html/data/$TESTUSER/files/$TESTUSER.too-large.dat docker exec -i nextcloud-container chown www-data:www-data /var/www/html/data/$TESTUSER/files/$TESTUSER.too-large.dat $DOCKER_EXEC_WITH_USER nextcloud-container php occ files:scan --all - $DOCKER_EXEC_WITH_USER nextcloud-container php occ gdatavaas:tag-unscanned + $DOCKER_EXEC_WITH_USER nextcloud-container php occ gdatavaas:scan $DOCKER_EXEC_WITH_USER nextcloud-container php occ gdatavaas:get-tags-for-file $TESTUSER/files/$TESTUSER.too-large.dat [[ $($DOCKER_EXEC_WITH_USER nextcloud-container php occ gdatavaas:get-tags-for-file $TESTUSER/files/$TESTUSER.too-large.dat | grep "Won't scan") ]]