From 0feee550b5eadbd3662bdff807c0e562a206998c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noelia=20Ruiz=20Mart=C3=ADnez?= Date: Sun, 19 May 2024 07:34:19 +0200 Subject: [PATCH] Add support for VirusTotal --- .../workflows/checkAndSubmitAddonMetadata.yml | 66 ++++++++++++++++++- .github/workflows/virusTotalAnalysis.js | 34 ++++++++++ README.md | 2 + 3 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/virusTotalAnalysis.js diff --git a/.github/workflows/checkAndSubmitAddonMetadata.yml b/.github/workflows/checkAndSubmitAddonMetadata.yml index 4883006947b..e8bc2054518 100644 --- a/.github/workflows/checkAndSubmitAddonMetadata.yml +++ b/.github/workflows/checkAndSubmitAddonMetadata.yml @@ -15,7 +15,9 @@ on: issueTitle: required: true type: string - + secrets: + virusTotalApiKey: + required: true jobs: getAddonId: runs-on: windows-latest @@ -225,11 +227,69 @@ jobs: uses: peter-evans/close-issue@v3 with: issue-number: ${{ inputs.issueNumber }} - codeQL-analysis: + + virusTotal-analysis: needs: createPullRequest + runs-on: windows-latest + strategy: + matrix: + python-version: [ 3.11 ] + permissions: + contents: write + pull-requests: write + issues: write + env: + API_KEY: ${{ secrets.virusTotalApiKey }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Download add-on metadata + uses: actions/download-artifact@v4 + with: + name: addonMetadata + - name: Install virusTotal + run: choco install vt-cli + - name: Set Virus Total analysis status + id: setVirusTotalAnalysisStatus + uses: actions/github-script@v7 + with: + script: | + const setVirusTotalAnalysisStatus = require('./.github/workflows/virusTotalAnalysis.js') + setVirusTotalAnalysisStatus({core}) + - name: Upload results + id: uploadResults + if: failure() + uses: actions/upload-artifact@v4 + with: + name: VirusTotal + path: vt.json + overwrite: true + - name: Create pull request + id: cpr + if: failure() + uses: peter-evans/create-pull-request@v6 + with: + add-paths: falsePositiveAddons.json + title: Add false positive add-on (${{ steps.setVirusTotalAnalysisStatus.outputs.addonId }}) + branch: falsePositiveAddon${{ github.event.issue.number }} + commit-message: Add false positive add-on (${{ steps.setVirusTotalAnalysisStatus.outputs.addonId }}) + body: "This add-on needs to be reviewed by NV Access due to VirusTotal analysis failure" + author: github-actions + delete-branch: true + - name: Warn if analysis fails + if: failure() + uses: peter-evans/create-or-update-comment@v4 + with: + issue-number: ${{ inputs.issueNumber }} + body: | + VirusTotal has flagged this add-on as malicious. + You can open this link and [download artifacts containing the results of the analysis](${{ steps.uploadResults.outputs.artifact-url }}). + If you think this is a false positive or want more details, contact NV Access. + codeQL-analysis: + needs: [createPullRequest, virusTotal-analysis] uses: ./.github/workflows/codeql-analysis.yml mergeToMaster: - needs: [getAddonId, createPullRequest, codeQL-analysis] + needs: [getAddonId, createPullRequest, codeQL-analysis, virusTotal-analysis] permissions: contents: write pull-requests: write diff --git a/.github/workflows/virusTotalAnalysis.js b/.github/workflows/virusTotalAnalysis.js new file mode 100644 index 00000000000..c634e4537df --- /dev/null +++ b/.github/workflows/virusTotalAnalysis.js @@ -0,0 +1,34 @@ +module.exports = ({core}) => { + const fs = require('fs'); + const { exec } = require('child_process'); + const addonMetadataContents = fs.readFileSync('addonMetadata.json'); + const addonMetadata = JSON.parse(addonMetadataContents); + const addonId = addonMetadata.addonId; + core.setOutput('addonId', addonId); + const sha256 = addonMetadata.sha256; + // const sha256 = '42335e36a209d39905414f0cbc71aa692338e3bf63efce8bc68d6949d2994ccd'; + const falsePositiveAddonsContents = fs.readFileSync('falsePositiveAddons.json'); + const falsePositiveAddonsData = JSON.parse(falsePositiveAddonsContents); + if (falsePositiveAddonsData[addonId] !== undefined && falsePositiveAddonsData[addonId].includes(sha256)) { + core.info('VirusTotal analysis skipped'); + return; + } + exec(`vt file ${sha256} -k ${process.env.API_KEY} --format json`, (err, stdout, stderr) => { + console.log(stdout); + const vtData = JSON.parse(stdout); + fs.writeFileSync('vt.json', stdout); + const stats = vtData[0]["last_analysis_stats"]; + const malicious = stats.malicious; + if (malicious === 0) { + core.info('VirusTotal analysis succeeded'); + return; + } + if (falsePositiveAddonsData[addonId] === undefined) { + falsePositiveAddonsData[addonId] = []; + } + falsePositiveAddonsData[addonId].push(sha256); + stringified = JSON.stringify(falsePositiveAddonsData, null, 2); + fs.writeFileSync('falsePositiveAddons.json', stringified); + core.setFailed('VirusTotal analysis failed'); + }); +}; diff --git a/README.md b/README.md index 6dc1083900d..6944395b3d7 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ The add-on store includes the following security measures: - The checksum allows NVDA to ensure that add-on releases are immutable. - [Code scanning with CodeQL](https://docs.github.com/en/code-security/code-scanning/introduction-to-code-scanning/about-code-scanning-with-codeql) can detect vulnerabilities in Python and JavaScript code included in submitted add-ons. - NV Access can manage [code scanning alerts](https://docs.github.com/en/code-security/code-scanning/managing-code-scanning-alerts/about-code-scanning-alerts), available from the Code scanning link from the [Security page](https://github.com/nvaccess/addon-datastore/security). +- [Virus Total CLI](https://virustotal.github.io/vt-cli) is used to scan submitted add-ons. +If malicious content is detected, the add-on will need to be manually approved by NV Access and won't be automatically included in the store. ### Human review process / code audit