Skip to content

VirusTotal Scan

VirusTotal Scan #95

name: VirusTotal Scan
on:
workflow_dispatch:
schedule:
- cron: "0 17 * * *"
pull_request_target:
types: [opened, synchronize]
permissions:
contents: read # Restrict GITHUB_TOKEN
jobs:
vt-plugin-scan-changes-check:
if: github.event_name == 'pull_request_target'
runs-on: ubuntu-latest
outputs:
plugins: ${{ steps.filter.outputs.plugins }}
non_plugin_files: ${{ steps.filter.outputs.non_plugin_files }}
steps:
- name: Check out repository code
uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.sha }}
- uses: dorny/paths-filter@v4
id: filter
with:
predicate-quantifier: 'every'
filters: |
plugins:
- 'plugins/*.json'
non_plugin_files:
- '**'
- '!plugins/*.json'
- name: Fail if non-plugin files changed
if: github.event_name == 'pull_request_target' && steps.filter.outputs.plugins == 'true' && steps.filter.outputs.non_plugin_files == 'true'
run: |
echo "This PR contains changes to files outside the plugins directory."
echo "Plugin submission PRs must only create/modify plugin manifest files in the plugins directory."
exit 1
vt-plugin-scan:
needs: vt-plugin-scan-changes-check
if: |
always() && (
(github.event_name == 'pull_request_target' && needs.vt-plugin-scan-changes-check.outputs.plugins == 'true' && needs.vt-plugin-scan-changes-check.outputs.non_plugin_files == 'false') ||
github.event_name == 'workflow_dispatch' ||
github.event_name == 'schedule'
)
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v6
# To actually check out and run the plugin downloader from the fork's PR
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.11"
- name: Install dependencies
run: pip install requests
# Avoid repeat downloads which bump the plugins' download count
- name: Restore downloaded plugin cache
uses: actions/cache/restore@v5
# No need to restore cache, PR's plugin is downloaded and scanned each time.
# Plugin dev may repeatedly only update one version
if: github.event_name != 'pull_request_target'
with:
path: |
scan-files/
plugin-download-cache.json
key: plugin-zips-restore-not-used
restore-keys: plugin-zips-
- name: Download plugin submitted in this PR
if: github.event_name == 'pull_request_target'
run: python ci/src/download_plugins.py --mode new --output-dir scan-files
- name: Download all plugins with cache metadata
if: github.event_name != 'pull_request_target'
run: python ci/src/download_plugins.py --output-dir scan-files --cache-meta plugin-download-cache.json
- name: Restore VirusTotal scan cache from previous runs
uses: actions/cache/restore@v5
# Don't use cache, upload/retrieve report from VirustTotal each time for new submissions
if: github.event_name != 'pull_request_target'
with:
path: vt_cache.json
key: vt-cache-restore-not-used
restore-keys: vt-cache-
- name: Run VirusTotal scan
uses: jjw24/virustotal-scanner-action@v1.0.1
with:
api-key: ${{ secrets.VT_API_KEY }}
scan-paths: |
./scan-files/
no-cache: false
cache-path: vt_cache.json
whitelist-path: vt_whitelist.json
report-path: vt_report.json
request-interval-sec: 15
analysis-poll-timeout-sec: 600
download-timeout-sec: 120
max-report-age-days: 30
- name: Save VirusTotal scan cache for future runs
uses: actions/cache/save@v5
# Cache not used for PR plugin submissions
if: always() && github.event_name != 'pull_request_target'
with:
path: vt_cache.json
key: vt-cache-${{ hashFiles('vt_cache.json') }}
- name: Save plugin ZIPs and cache metadata for future runs
uses: actions/cache/save@v5
# Upload even if scan fails
if: always() && github.event_name != 'pull_request_target'
with:
path: |
scan-files/
plugin-download-cache.json
key: plugin-zips-${{ hashFiles('plugin-download-cache.json') }}
- name: Upload VirusTotal scan report as viewable artifact
uses: actions/upload-artifact@v7
if: always()
with:
path: vt_report.json
archive: false
- name: Upload VirusTotal scan cache as viewable artifact
uses: actions/upload-artifact@v7
# Cache not used for PR plugin submissions
if: always() && github.event_name != 'pull_request_target'
with:
path: vt_cache.json
archive: false
- name: Upload plugin download cache metadata as viewable artifact
uses: actions/upload-artifact@v7
# Upload even if scan fails
if: always() && github.event_name != 'pull_request_target'
with:
path: plugin-download-cache.json
archive: false