Skip to content

Commit a739545

Browse files
committed
Scan all add-ons with VT
1 parent 8f72263 commit a739545

File tree

4 files changed

+141
-38
lines changed

4 files changed

+141
-38
lines changed

.github/workflows/checkAndSubmitAddonMetadata.yml

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ jobs:
6666
script: |
6767
const addonId = "${{ steps.getAddonId.outputs.result }}"
6868
return addonId.replace(/[^a-zA-Z0-9]/g, "")
69-
- name: Copy add-on metadata file
69+
- name: Copy add-on metadata file
7070
run: |
7171
Copy-Item ${{ steps.getAddonFileName.outputs.result }} addonMetadata.json
7272
- name: Upload add-on
@@ -155,6 +155,7 @@ jobs:
155155
issues: write
156156
outputs:
157157
pullRequestNumber: ${{ steps.cpr.outputs.pull-request-number }}
158+
addonFileName: ${{ steps.getAddonFileName.outputs.addonFileName }}
158159
steps:
159160
- name: Checkout code
160161
uses: actions/checkout@v4
@@ -247,6 +248,10 @@ jobs:
247248
uses: actions/download-artifact@v4
248249
with:
249250
name: addonMetadata
251+
- name: Install Node.js
252+
uses: actions/setup-node@v2
253+
- name: Install glob
254+
run: npm install glob
250255
- name: Install virusTotal
251256
run: choco install vt-cli
252257
- name: Set Virus Total analysis status
@@ -255,7 +260,7 @@ jobs:
255260
with:
256261
script: |
257262
const setVirusTotalAnalysisStatus = require('./.github/workflows/virusTotalAnalysis.js')
258-
setVirusTotalAnalysisStatus({core})
263+
setVirusTotalAnalysisStatus({core}, "${{ needs.createPullRequest.outputs.getAddonFileName }}")
259264
- name: Upload results
260265
id: uploadResults
261266
if: failure()
@@ -313,7 +318,7 @@ jobs:
313318
commit-message: Add reviewed add-on (${{ needs.getAddonId.outputs.addonId }})
314319
body: |
315320
This add-on needs to be reviewed by NV Access due to analysis failure.
316-
Review ${{ inputs.issueNumber }} for more information.
321+
Review #${{ inputs.issueNumber }} for more information.
317322
author: github-actions <[email protected]>
318323
delete-branch: true
319324
- name: Request to keep issue opened
@@ -340,7 +345,7 @@ jobs:
340345
GH_TOKEN: ${{ github.token }}
341346
run: |
342347
gh pr merge ${{ inputs.issueAuthorName }}${{ inputs.issueNumber }} -b '[Automated] Merged ${{ needs.getAddonId.outputs.addonFileName }} into master (PR #${{ needs.createPullRequest.outputs.pullRequestNumber }})' -m
343-
348+
344349
createReviewComment:
345350
# jq for windows has issues parsing multiline strings (e.g. CRLF),
346351
# use linux instead.
@@ -399,7 +404,7 @@ jobs:
399404
.[\"$addonId\"].discussionId = \"$discussionId\"
400405
| .[\"$addonId\"].discussionUrl = \"$discussionUrl\"
401406
"
402-
407+
403408
mv discussions.json discussions.old.json
404409
jq -e "$jqCode" discussions.old.json > discussions.json
405410
jqExitCode=$?
@@ -420,7 +425,7 @@ jobs:
420425
jqCode="
421426
.[\"reviewUrl\"] = $reviewUrl
422427
"
423-
428+
424429
mv $addonFilename $addonFilename.old.json
425430
jq -e -a "$jqCode" $addonFilename.old.json > $addonFilename
426431
jqExitCode=$?
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
name: Scan all submitted add-ons with Virus Total
2+
3+
on:
4+
workflow_dispatch:
5+
6+
jobs:
7+
virusTotal-analysis:
8+
runs-on: windows-latest
9+
strategy:
10+
matrix:
11+
python-version: [ 3.11 ]
12+
permissions:
13+
contents: read
14+
env:
15+
VT_API_KEY: ${{ secrets.VT_API_KEY }}
16+
steps:
17+
- name: Checkout repository
18+
uses: actions/checkout@v4
19+
with:
20+
ref: ${{ inputs.headRef }}
21+
- name: Install virusTotal
22+
run: choco install vt-cli
23+
- name: Install Node.js
24+
uses: actions/setup-node@v2
25+
- name: Install glob
26+
run: npm install glob
27+
- name: Submit add-ons with VirusTotal
28+
uses: actions/github-script@v7
29+
with:
30+
script: |
31+
const virusTotalSubmit = require('./.github/workflows/virusTotalSubmit.js')
32+
virusTotalSubmit({core}, "./addons/*/*.json")
33+
- name: Set Virus Total analysis status
34+
id: setVirusTotalAnalysisStatus
35+
uses: actions/github-script@v7
36+
with:
37+
script: |
38+
const setVirusTotalAnalysisStatus = require('./.github/workflows/virusTotalAnalysis.js')
39+
setVirusTotalAnalysisStatus({core}, "./addons/*/*.json")
40+
- name: Upload results
41+
id: uploadResults
42+
if: failure()
43+
uses: actions/upload-artifact@v4
44+
with:
45+
name: VirusTotal
46+
path: vt.json
47+
overwrite: true
48+
- name: Upload manual approval
49+
id: uploadManualApproval
50+
if: failure()
51+
uses: actions/upload-artifact@v4
52+
with:
53+
name: manualApproval
54+
path: reviewedAddons.json
55+
overwrite: true
Lines changed: 37 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,43 @@
1-
module.exports = ({core}) => {
1+
const glob = require('glob');
2+
3+
module.exports = ({core}, globPattern) => {
24
const fs = require('fs');
35
const { exec } = require('child_process');
4-
const addonMetadataContents = fs.readFileSync('addonMetadata.json');
5-
const addonMetadata = JSON.parse(addonMetadataContents);
6-
const addonId = addonMetadata.addonId;
7-
core.setOutput('addonId', addonId);
8-
const sha256 = addonMetadata.sha256;
9-
const analysisUrl = `https://www.virustotal.com/gui/file/${sha256}`;
10-
console.log(analysisUrl);
11-
core.setOutput('analysisUrl', analysisUrl);
12-
const reviewedAddonsContents = fs.readFileSync('reviewedAddons.json');
13-
const reviewedAddonsData = JSON.parse(reviewedAddonsContents);
14-
if (reviewedAddonsData[addonId] !== undefined && reviewedAddonsData[addonId].includes(sha256)) {
15-
core.info('VirusTotal analysis skipped');
16-
return;
17-
}
18-
exec(`vt file ${sha256} -k ${process.env.VT_API_KEY} --format json`, (err, stdout, stderr) => {
19-
console.log(`err: ${err}`);
20-
console.log(`stdout: ${stdout}`);
21-
console.log(`stderr: ${stderr}`);
22-
const vtData = JSON.parse(stdout);
23-
fs.writeFileSync('vt.json', stdout);
24-
const stats = vtData[0]["last_analysis_stats"];
25-
const malicious = stats.malicious;
26-
if (malicious === 0) {
27-
core.info('VirusTotal analysis succeeded');
6+
files = glob.globSync(globPattern);
7+
files.forEach(file => {
8+
const addonMetadataContents = fs.readFileSync(file);
9+
const addonMetadata = JSON.parse(addonMetadataContents);
10+
const addonId = addonMetadata.addonId;
11+
core.setOutput('addonId', addonId);
12+
const sha256 = addonMetadata.sha256;
13+
const analysisUrl = `https://www.virustotal.com/gui/file/${sha256}`;
14+
console.log(analysisUrl);
15+
core.setOutput('analysisUrl', analysisUrl);
16+
const reviewedAddonsContents = fs.readFileSync('reviewedAddons.json');
17+
const reviewedAddonsData = JSON.parse(reviewedAddonsContents);
18+
if (reviewedAddonsData[addonId] !== undefined && reviewedAddonsData[addonId].includes(sha256)) {
19+
core.info('VirusTotal analysis skipped');
2820
return;
2921
}
30-
if (reviewedAddonsData[addonId] === undefined) {
31-
reviewedAddonsData[addonId] = [];
32-
}
33-
reviewedAddonsData[addonId].push(sha256);
34-
stringified = JSON.stringify(reviewedAddonsData, null, 2);
35-
fs.writeFileSync('reviewedAddons.json', stringified);
36-
core.setFailed('VirusTotal analysis failed');
22+
exec(`vt file ${sha256} -k ${process.env.VT_API_KEY} --format json`, (err, stdout, stderr) => {
23+
console.log(`err: ${err}`);
24+
console.log(`stdout: ${stdout}`);
25+
console.log(`stderr: ${stderr}`);
26+
const vtData = JSON.parse(stdout);
27+
fs.writeFileSync('vt.json', stdout);
28+
const stats = vtData[0]["last_analysis_stats"];
29+
const malicious = stats.malicious;
30+
if (malicious === 0) {
31+
core.info('VirusTotal analysis succeeded');
32+
return;
33+
}
34+
if (reviewedAddonsData[addonId] === undefined) {
35+
reviewedAddonsData[addonId] = [];
36+
}
37+
reviewedAddonsData[addonId].push(sha256);
38+
stringified = JSON.stringify(reviewedAddonsData, null, 2);
39+
fs.writeFileSync('reviewedAddons.json', stringified);
40+
core.setFailed('VirusTotal analysis failed');
41+
});
3742
});
3843
};

.github/workflows/virusTotalSubmit.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
const glob = require('glob');
2+
3+
module.exports = ({core}, globPattern) => {
4+
const fs = require('fs');
5+
const { exec } = require('child_process');
6+
files = glob.globSync(globPattern);
7+
files.forEach(file => {
8+
const addonMetadataContents = fs.readFileSync(file);
9+
const addonMetadata = JSON.parse(addonMetadataContents);
10+
const addonId = addonMetadata.addonId;
11+
const sha256 = addonMetadata.sha256;
12+
exec(`vt file ${sha256} -k ${process.env.VT_API_KEY} --format json`, (err, stdout, stderr) => {
13+
if (stderr === '' || err === null) {
14+
// File has been scanned before
15+
return;
16+
}
17+
console.log(`err: ${err}`);
18+
console.log(`stdout: ${stdout}`);
19+
console.log(`stderr: ${stderr}`);
20+
// download add-on file
21+
exec(`curl --location --output ${addonId}.nvda-addon "${addonMetadata.URL}"`, (err, stdout, stderr) => {
22+
if (stderr !== '' || err !== null) {
23+
console.log(`err: ${err}`);
24+
console.log(`stdout: ${stdout}`);
25+
console.log(`stderr: ${stderr}`);
26+
core.setFailed('Failed to download add-on file');
27+
return;
28+
}
29+
// scan downloaded file
30+
exec(`vt scan file -k ${process.env.VT_API_KEY} ${addonId}.nvda-addon`, (err, stdout, stderr) => {
31+
console.log(`err: ${err}`);
32+
console.log(`stdout: ${stdout}`);
33+
console.log(`stderr: ${stderr}`);
34+
})
35+
})
36+
});
37+
});
38+
};

0 commit comments

Comments
 (0)