Skip to content

Commit

Permalink
adds commands for tagging and scanning
Browse files Browse the repository at this point in the history
this commit adds commands for tagging and scanning so the test don't have to rely on a cron run.
  • Loading branch information
unglaublicherdude committed Jun 11, 2024
1 parent 1f1638d commit 2efc873
Show file tree
Hide file tree
Showing 7 changed files with 189 additions and 44 deletions.
22 changes: 2 additions & 20 deletions .github/workflows/release-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
Expand Down
4 changes: 4 additions & 0 deletions appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ If you have any questions about scanning, usage or similar, please feel free to
<admin-section>OCA\GDataVaas\Settings\VaasAdminSection</admin-section>
<admin>OCA\GDataVaas\Settings\VaasAdmin</admin>
</settings>
<commands>
<command>OCA\GDataVaas\Command\ScanCommand</command>
<command>OCA\GDataVaas\Command\TagUnscannedCommand</command>
</commands>
<dependencies>
<nextcloud min-version="27" max-version="28"/>
</dependencies>
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
81 changes: 81 additions & 0 deletions lib/Command/ScanCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

namespace OCA\GDataVaas\Command;

use Exception;
use OCA\GDataVaas\Service\TagService;
use OCA\GDataVaas\Service\VerdictService;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\TimedJob;
use OCP\IConfig;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class ScanCommand extends Command {
private const APP_ID = "gdatavaas";

private TagService $tagService;
private VerdictService $scanService;
private IConfig $appConfig;
private LoggerInterface $logger;

public function __construct(LoggerInterface $logger, TagService $tagService, VerdictService $scanService, IConfig $appConfig) {
parent::__construct();

$this->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;
}
}
70 changes: 70 additions & 0 deletions lib/Command/TagUnscannedCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

namespace OCA\GDataVaas\Command;

use OCA\GDataVaas\Service\TagService;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\DB\Exception;
use OCP\IConfig;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;


class TagUnscannedCommand extends Command {
private const APP_ID = "gdatavaas";

private TagService $tagService;
private IConfig $appConfig;
private LoggerInterface $logger;

public function __construct(IConfig $appConfig, TagService $tagService, LoggerInterface $logger) {
parent::__construct();

$this->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;
}
}
19 changes: 8 additions & 11 deletions tests/functionality-parallel.bats
Original file line number Diff line number Diff line change
Expand Up @@ -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 ]]
Expand All @@ -39,22 +36,22 @@ 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
[[ "$RESULT" =~ "Upload cannot be completed." ]]
}

@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
Expand Down
34 changes: 22 additions & 12 deletions tests/functionality-sequential.bats
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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() {
Expand Down

0 comments on commit 2efc873

Please sign in to comment.