From 2efc873735f14b8f6150e534b4a1c9ff45cebeb3 Mon Sep 17 00:00:00 2001 From: unglaublicherdude Date: Tue, 11 Jun 2024 17:20:16 +0200 Subject: [PATCH] adds commands for tagging and scanning this commit adds commands for tagging and scanning so the test don't have to rely on a cron run. --- .github/workflows/release-app.yml | 22 +------- appinfo/info.xml | 4 ++ composer.json | 3 +- lib/Command/ScanCommand.php | 81 +++++++++++++++++++++++++++++ lib/Command/TagUnscannedCommand.php | 70 +++++++++++++++++++++++++ tests/functionality-parallel.bats | 19 +++---- tests/functionality-sequential.bats | 34 +++++++----- 7 files changed, 189 insertions(+), 44 deletions(-) create mode 100644 lib/Command/ScanCommand.php create mode 100644 lib/Command/TagUnscannedCommand.php diff --git a/.github/workflows/release-app.yml b/.github/workflows/release-app.yml index 04ddd8cd..660b6531 100644 --- a/.github/workflows/release-app.yml +++ b/.github/workflows/release-app.yml @@ -16,10 +16,12 @@ jobs: name: setup node with: node-version: 20 + - name: Setup BATS uses: mig4/setup-bats@v1 with: bats-version: 1.11.0 + - uses: docker-practice/actions-setup-docker@master - name: install nextcloud env: @@ -33,26 +35,6 @@ jobs: CLIENT_SECRET: ${{ secrets.VAAS_CLIENT_SECRET }} run: bats --no-parallelize-across-files --jobs 3 ./tests - - name: test testuser clean Upload - run: | - STATUS_CODE=$(curl --silent -w "%{http_code}" -u testuser:myfancysecurepassword234 -T /tmp/clean.txt http://127.0.0.1/remote.php/dav/files/testuser/clean.txt) - [[ $STATUS_CODE -ge 200 && $STATUS_CODE -lt 300 ]] || exit 1 - - - name: test testuser pup Upload - run: | - STATUS_CODE=$(curl --silent -w "%{http_code}" -u testuser:myfancysecurepassword234 -T /tmp/pup.exe http://127.0.0.1/remote.php/dav/files/testuser/pup.exe) - [[ $STATUS_CODE -ge 200 && $STATUS_CODE -lt 300 ]] || exit 1 - - - name: test upload when vaas does not function - env: - CLIENT_ID: ${{ secrets.VAAS_CLIENT_ID }} - CLIENT_SECRET: ${{ secrets.VAAS_CLIENT_SECRET }} - run: | - docker exec --user www-data -i nextcloud-container php occ config:app:set gdatavaas clientSecret --value="WRONG_PASSWORD" - STATUS_CODE=$(curl --silent -w "%{http_code}" -u admin:admin -T /tmp/eicar.com.txt http://127.0.0.1/remote.php/dav/files/admin/eicar.com.txt) - [[ $STATUS_CODE -ge 200 && $STATUS_CODE -lt 300 ]] || exit 1 - docker exec --user www-data -i nextcloud-container php occ config:app:set gdatavaas clientSecret --value="$CLIENT_SECRET" - - uses: actions/upload-artifact@master with: name: build-dir diff --git a/appinfo/info.xml b/appinfo/info.xml index 116b4592..da271bd9 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -35,6 +35,10 @@ If you have any questions about scanning, usage or similar, please feel free to OCA\GDataVaas\Settings\VaasAdminSection OCA\GDataVaas\Settings\VaasAdmin + + OCA\GDataVaas\Command\ScanCommand + OCA\GDataVaas\Command\TagUnscannedCommand + diff --git a/composer.json b/composer.json index a4d5e279..9ffd3b09 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,8 @@ "require-dev": { "nextcloud/ocp": "dev-stable28", "psalm/phar": "^5.17.0", - "nextcloud/coding-standard": "^v1.1.1" + "nextcloud/coding-standard": "^v1.1.1", + "symfony/console": "^6.4" }, "scripts": { "lint": "find lib -name \\*.php -not -path './vendor/*' -print0 | xargs -0 -n1 php -l", diff --git a/lib/Command/ScanCommand.php b/lib/Command/ScanCommand.php new file mode 100644 index 00000000..4006abfa --- /dev/null +++ b/lib/Command/ScanCommand.php @@ -0,0 +1,81 @@ +logger = $logger; + $this->tagService = $tagService; + $this->scanService = $scanService; + $this->appConfig = $appConfig; + } + + /** + * @return void + */ + protected function configure() { + $this->setName('gdatavaas:scan'); + $this->setDescription('scan files for malware'); + } + + /** + * @param $argument + * @return void + * @throws \OCP\DB\Exception if the database platform is not supported + */ + protected function execute(InputInterface $input, OutputInterface $output): int { + $unscannedTagIsDisabled = $this->appConfig->getAppValue(self::APP_ID, 'disableUnscannedTag'); + $quantity = $this->appConfig->getAppValue(self::APP_ID, 'scanQueueLength'); + try { + $quantity = intval($quantity); + } catch (Exception) { + $quantity = 5; + } + + $maliciousTag = $this->tagService->getTag(TagService::MALICIOUS); + $pupTag = $this->tagService->getTag(TagService::PUP); + $cleanTag = $this->tagService->getTag(TagService::CLEAN); + $unscannedTag = $this->tagService->getTag(TagService::UNSCANNED); + $wontScanTag = $this->tagService->getTag(TagService::WONT_SCAN); + + if ($unscannedTagIsDisabled) { + $excludedTagIds = [$unscannedTag->getId(), $maliciousTag->getId(), $cleanTag->getId(), $pupTag->getId(), $wontScanTag->getId()]; + $fileIds = $this->tagService->getFileIdsWithoutTags($excludedTagIds, $quantity); + } else { + $fileIds = $this->tagService->getFileIdsWithTag(TagService::UNSCANNED, $quantity, 0); + } + + $this->logger->debug("Scanning files"); + + foreach ($fileIds as $fileId) { + try { + $this->scanService->scanFileById($fileId); + } catch (Exception $e) { + $output->writeln("Failed to scan file with id " . $fileId . ": " . $e->getMessage()); + } + } + + $this->logger->debug("Scanned " . count($fileIds) . " files"); + return 0; + } +} diff --git a/lib/Command/TagUnscannedCommand.php b/lib/Command/TagUnscannedCommand.php new file mode 100644 index 00000000..0e197459 --- /dev/null +++ b/lib/Command/TagUnscannedCommand.php @@ -0,0 +1,70 @@ +appConfig = $appConfig; + $this->tagService = $tagService; + $this->logger = $logger; + } + + /** + * @return void + */ + protected function configure() { + $this->setName('gdatavaas:tag-unscanned'); + $this->setDescription('tags all files without tag from this app as unscanned'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int { + $unscannedTagIsDisabled = $this->appConfig->getAppValue(self::APP_ID, 'disableUnscannedTag'); + if ($unscannedTagIsDisabled) { + $this->tagService->removeTag(TagService::UNSCANNED); + $this->logger->info("Unscanned Tag is disabled, exiting."); + return 0; + } + + $this->logger->debug("Tagging unscanned files"); + + $unscannedTag = $this->tagService->getTag(TagService::UNSCANNED); + $maliciousTag = $this->tagService->getTag(TagService::MALICIOUS); + $pupTag = $this->tagService->getTag(TagService::PUP); + $cleanTag = $this->tagService->getTag(TagService::CLEAN); + $wontScanTag = $this->tagService->getTag(TagService::WONT_SCAN); + + $excludedTagIds = [$unscannedTag->getId(), $maliciousTag->getId(), $cleanTag->getId(), $pupTag->getId(), $wontScanTag->getId()]; + + $fileIds = $this->tagService->getFileIdsWithoutTags($excludedTagIds, 10000); + $this->logger->info("Found " . count($fileIds) . " files without tags from this app"); + + foreach ($fileIds as $fileId) { + if ($this->tagService->hasAnyButUnscannedTag($fileId)) { + continue; + } + $this->tagService->setTag($fileId, TagService::UNSCANNED); + } + + $this->logger->debug("Tagged " . count($fileIds) . " unscanned files"); + + return 0; + } +} diff --git a/tests/functionality-parallel.bats b/tests/functionality-parallel.bats index 6241a336..72113a74 100755 --- a/tests/functionality-parallel.bats +++ b/tests/functionality-parallel.bats @@ -3,29 +3,26 @@ FOLDER_PREFIX=./tmp/functionality-parallel TESTUSER=testuser TESTUSER_PASSWORD=myfancysecurepassword234 +EICAR_STRING='X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' +CLEAN_STRING='nothingwronghere' setup_file() { mkdir -p $FOLDER_PREFIX + curl --output $FOLDER_PREFIX/pup.exe http://amtso.eicar.org/PotentiallyUnwanted.exe docker exec --env OC_PASS=$TESTUSER_PASSWORD --user www-data nextcloud-container php occ user:add $TESTUSER --password-from-env || echo "already exists" docker exec --user www-data -i nextcloud-container php occ config:app:set gdatavaas clientSecret --value="$CLIENT_SECRET" sleep 2 } -setup() { - echo 'nothingwronghere' > $FOLDER_PREFIX/clean.txt - echo 'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' > $FOLDER_PREFIX/eicar.com.txt - curl --output $FOLDER_PREFIX/pup.exe http://amtso.eicar.org/PotentiallyUnwanted.exe -} - @test "test admin eicar Upload" { - RESULT=$(curl --silent -w "%{http_code}" -u admin:admin -T $FOLDER_PREFIX/eicar.com.txt http://127.0.0.1/remote.php/dav/files/admin/functionality-parallel.eicar.com.txt) + RESULT=$(echo $EICAR_STRING | curl --silent -w "%{http_code}" -u admin:admin -T - http://127.0.0.1/remote.php/dav/files/admin/functionality-parallel.eicar.com.txt) echo "Actual: $RESULT" curl --silent -q -u admin:admin -X DELETE http://127.0.0.1/remote.php/dav/files/admin/functionality-parallel.eicar.com.txt [[ "$RESULT" =~ "Upload cannot be completed." ]] } @test "test admin clean upload" { - RESULT=$(curl -w "%{http_code}" -u admin:admin -T $FOLDER_PREFIX/clean.txt http://127.0.0.1/remote.php/dav/files/admin/functionality-parallel.clean.txt) + RESULT=$(echo $CLEAN_STRING | curl -w "%{http_code}" -u admin:admin -T - http://127.0.0.1/remote.php/dav/files/admin/functionality-parallel.clean.txt) echo "Actual: $RESULT" curl --silent -q -u admin:admin -X DELETE http://127.0.0.1/remote.php/dav/files/admin/functionality-parallel.clean.txt [[ $RESULT -ge 200 && $RESULT -lt 300 ]] @@ -39,7 +36,7 @@ setup() { } @test "test testuser eicar Upload" { - RESULT=$(curl --silent -w "%{http_code}" -u $TESTUSER:$TESTUSER_PASSWORD -T ./tmp/functionality-sequential//eicar.com.txt http://127.0.0.1/remote.php/dav/files/$TESTUSER/functionality-parallel.eicar.com.txt) + RESULT=$(echo $EICAR_STRING | curl --silent -w "%{http_code}" -u $TESTUSER:$TESTUSER_PASSWORD -T - http://127.0.0.1/remote.php/dav/files/$TESTUSER/functionality-parallel.eicar.com.txt) echo "Actual: $RESULT" docker exec --user www-data -i nextcloud-container php occ config:app:get gdatavaas clientSecret curl --silent -q -u $TESTUSER:$TESTUSER_PASSWORD -X DELETE http://127.0.0.1/remote.php/dav/files/$TESTUSER/functionality-parallel.eicar.com.txt @@ -47,14 +44,14 @@ setup() { } @test "test testuser clean Upload" { - STATUS_CODE=$(curl --silent -w "%{http_code}" -w "%{http_code}" -u $TESTUSER:$TESTUSER_PASSWORD -T $FOLDER_PREFIX/clean.txt http://127.0.0.1/remote.php/dav/files/$TESTUSER/functionality-parallel.clean.txt) + STATUS_CODE=$(echo $CLEAN_STRING | curl --silent -w "%{http_code}" -u $TESTUSER:$TESTUSER_PASSWORD -T - http://127.0.0.1/remote.php/dav/files/$TESTUSER/functionality-parallel.clean.txt) echo "Actual: $RESULT" curl --silent -q -u $TESTUSER:$TESTUSER_PASSWORD -X DELETE http://127.0.0.1/remote.php/dav/files/$TESTUSER/functionality-parallel.clean.txt [[ $STATUS_CODE -ge 200 && $STATUS_CODE -lt 300 ]] || exit 1 } @test "test testuser pup Upload" { - RESULT=$(curl --silent -w "%{http_code}" -w "%{http_code}" -u $TESTUSER:$TESTUSER_PASSWORD -T $FOLDER_PREFIX/pup.exe http://127.0.0.1/remote.php/dav/files/$TESTUSER/functionality-parallel.pup.exe) + RESULT=$(curl --silent -w "%{http_code}" -u $TESTUSER:$TESTUSER_PASSWORD -T $FOLDER_PREFIX/pup.exe http://127.0.0.1/remote.php/dav/files/$TESTUSER/functionality-parallel.pup.exe) echo "Actual: $RESULT" curl --silent -q -u $TESTUSER:$TESTUSER_PASSWORD -X DELETE http://127.0.0.1/remote.php/dav/files/$TESTUSER/functionality-parallel.pup.exe [[ $RESULT -ge 200 && $RESULT -lt 300 ]] || exit 1 diff --git a/tests/functionality-sequential.bats b/tests/functionality-sequential.bats index d1a5cc49..195e91f7 100644 --- a/tests/functionality-sequential.bats +++ b/tests/functionality-sequential.bats @@ -3,19 +3,18 @@ FOLDER_PREFIX=./tmp/functionality-sequential TESTUSER=testuser TESTUSER_PASSWORD=myfancysecurepassword234 +EICAR_STRING='X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' setup_file() { mkdir -p $FOLDER_PREFIX/ - echo 'nothingwronghere' > $FOLDER_PREFIX/clean.txt - echo 'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' > $FOLDER_PREFIX/eicar.com.txt curl --output $FOLDER_PREFIX/pup.exe http://amtso.eicar.org/PotentiallyUnwanted.exe + docker exec --env OC_PASS=$TESTUSER_PASSWORD --user www-data nextcloud-container php occ user:add $TESTUSER --password-from-env || echo "already exists" BATS_NO_PARALLELIZE_WITHIN_FILE=true } - @test "test upload when vaas does not function" { docker exec --user www-data -i nextcloud-container php occ config:app:set gdatavaas clientSecret --value="WRONG_PASSWORD" - RESULT=$(curl --silent -w "%{http_code}" -u admin:admin -T $FOLDER_PREFIX/eicar.com.txt http://127.0.0.1/remote.php/dav/files/admin/functionality-sequential.eicar.com.txt) + RESULT=$(echo $EICAR_STRING | curl --silent -w "%{http_code}" -u admin:admin -T - http://127.0.0.1/remote.php/dav/files/admin/functionality-sequential.eicar.com.txt) docker exec --user www-data -i nextcloud-container php occ config:app:set gdatavaas clientSecret --value="$CLIENT_SECRET" curl --silent -q -u admin:admin -X DELETE http://127.0.0.1/remote.php/dav/files/admin/functionality-sequential.eicar.com.txt @@ -25,31 +24,42 @@ setup_file() { @test "test croned scan for admin files" { docker exec --user www-data -i nextcloud-container php occ config:app:set gdatavaas clientSecret --value="WRONG_PASSWORD" - curl --silent -w "%{http_code}" -u admin:admin -T $FOLDER_PREFIX/eicar.com.txt http://127.0.0.1/remote.php/dav/files/admin/admin.functionality-sequential.eicar.com.txt + echo $EICAR_STRING | curl --silent -w "%{http_code}" -u admin:admin -T - http://127.0.0.1/remote.php/dav/files/admin/admin.functionality-sequential.eicar.com.txt + curl --silent -w "%{http_code}" -u admin:admin -T $FOLDER_PREFIX/pup.exe http://127.0.0.1/remote.php/dav/files/admin/admin.pup.exe + docker exec --user www-data -i nextcloud-container php occ config:app:set gdatavaas clientSecret --value="$CLIENT_SECRET" - docker exec --user www-data -i nextcloud-container php ./cron.php # tag unscanned - docker exec --user www-data -i nextcloud-container php ./cron.php # actual scan - LOGS=$(docker exec --user www-data -i nextcloud-container cat data/nextcloud.log | egrep "admin.functionality-sequential.eicar.com.txt|Readme.md" ) + docker exec -i --user www-data nextcloud-container php occ gdatavaas:tag-unscanned + docker exec -i --user www-data nextcloud-container php occ gdatavaas:scan + + LOGS=$(docker exec --user www-data -i nextcloud-container cat data/nextcloud.log | egrep "admin.functionality-sequential.eicar.com.txt|Readme.md|admin.pup.exe" ) curl --silent -q -u admin:admin -X DELETE http://127.0.0.1/remote.php/dav/files/admin/admin.functionality-sequential.eicar.com.txt + curl --silent -q -u admin:admin -X DELETE http://127.0.0.1/remote.php/dav/files/admin/admin.pup.exe [[ $LOGS =~ ^.*admin.functionality-sequential.eicar.com.txt.*Verdict:.*Malicious ]] + [[ $LOGS =~ ^.*admin.pup.exe.*Verdict:.*Pup ]] [[ $LOGS =~ ^.*Readme.md.*Verdict:.*Clean ]] } @test "test croned scan for testuser files" { docker exec --user www-data -i nextcloud-container php occ config:app:set gdatavaas clientSecret --value="WRONG_PASSWORD" - curl --silent -w "%{http_code}" -u $TESTUSER:$TESTUSER_PASSWORD -T $FOLDER_PREFIX/eicar.com.txt http://127.0.0.1/remote.php/dav/files/$TESTUSER/$TESTUSER.functionality-sequential.eicar.com.txt + + echo $EICAR_STRING |curl --silent -w "%{http_code}" -u $TESTUSER:$TESTUSER_PASSWORD -T - http://127.0.0.1/remote.php/dav/files/$TESTUSER/$TESTUSER.functionality-sequential.eicar.com.txt + curl --silent -w "%{http_code}" -u $TESTUSER:$TESTUSER_PASSWORD -T $FOLDER_PREFIX/pup.exe http://127.0.0.1/remote.php/dav/files/$TESTUSER/$TESTUSER.pup.exe + docker exec --user www-data -i nextcloud-container php occ config:app:set gdatavaas clientSecret --value="$CLIENT_SECRET" - docker exec --user www-data -i nextcloud-container php ./cron.php # tag unscanned - docker exec --user www-data -i nextcloud-container php ./cron.php # actual scan - LOGS=$(docker exec --user www-data -i nextcloud-container cat data/nextcloud.log | egrep "$TESTUSER.functionality-sequential.eicar.com.txt") + docker exec -i --user www-data nextcloud-container php occ gdatavaas:tag-unscanned + docker exec -i --user www-data nextcloud-container php occ gdatavaas:scan + + LOGS=$(docker exec --user www-data -i nextcloud-container cat data/nextcloud.log | egrep "$TESTUSER.functionality-sequential.eicar.com.txt|$TESTUSER.pup.exe") curl --silent -q -u $TESTUSER:$TESTUSER_PASSWORD -X DELETE http://127.0.0.1/remote.php/dav/files/$TESTUSER/$TESTUSER.functionality-sequential.eicar.com.txt + curl --silent -q -u $TESTUSER:$TESTUSER_PASSWORD -X DELETE http://127.0.0.1/remote.php/dav/files/$TESTUSER/$TESTUSER.pup.exe [[ $LOGS =~ ^.*$TESTUSER.functionality-sequential.eicar.com.txt.*Verdict:.*Malicious ]] + [[ $LOGS =~ ^.*$TESTUSER.pup.exe.*Verdict:.*Pup ]] } tearddown_file() {