From 64442e54b86273b20da4a1f4bf2744b43f2e7e02 Mon Sep 17 00:00:00 2001 From: Benjamin Young Date: Fri, 10 Oct 2025 14:37:58 -0400 Subject: [PATCH 01/10] Add npm audit and list output to OSV PR comments. --- .github/workflows/osv-scanner-pr.yaml | 65 ++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/.github/workflows/osv-scanner-pr.yaml b/.github/workflows/osv-scanner-pr.yaml index f4d6a14..157ce8c 100644 --- a/.github/workflows/osv-scanner-pr.yaml +++ b/.github/workflows/osv-scanner-pr.yaml @@ -100,12 +100,75 @@ jobs: uses: juliangruber/read-file-action@b549046febe0fe86f8cb4f93c24e284433f9ab58 # v1.1.7 with: path: new-results.md - - name: Add OSV comment + + # Run npm audit and npm list to build up additional report explanations + - name: npm audit + id: npm_audit + run: | + echo 'result=$(npm audit --json)' >> $GITHUB_OUTPUT + continue-on-error: true + - name: npm audit + id: audit_report + run: | + AUDIT=$(echo "${{ toJson(steps.npm_audit.outputs.result) }}" | jq -r ' + "| Severity | Name | Version | Fix Available |", + "| --- | --- | --- | --- |", + (.vulnerabilities | to_entries | .[] | + "| \(.value.severity) | \(.key) | \(.value.range) | \(.value.fixAvailable.version) |" + )') + # Use a random delimiter to capture the multi-line output + delimiter=$(openssl rand -hex 8) + echo "result<<$delimiter" >> $GITHUB_OUTPUT + echo "$AUDIT" >> $GITHUB_OUTPUT + echo "$delimiter" >> $GITHUB_OUTPUT + continue-on-error: true + - name: npm list vulnerable dependencies + id: list_report + run: | + DEPS=$(echo "${{ toJSON(steps.npm_audit.outputs.result) }}" | jq -r '[.vulnerabilities | to_entries[] | .key] | join(" ")') + LIST=$(npm list $DEPS --json | jq -r ' + def walk_tree(prefix): + to_entries | map( + "\(prefix)\(.key)@\(.value.version)\n" + + if .value.dependencies then + (.value.dependencies | walk_tree(" " + prefix)) + else + "" + end + ) | join(""); + .dependencies | walk_tree("* ")') + # Use a random delimiter to capture the multi-line output + delimiter=$(openssl rand -hex 8) + echo "result<<$delimiter" >> $GITHUB_OUTPUT + echo "$LIST" >> $GITHUB_OUTPUT + echo "$delimiter" >> $GITHUB_OUTPUT + continue-on-error: true + + # Combine OSV, npm audit, and npm list output into a single comment + - name: Add a comment containing OSV, npm audit, and npm list output uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b # v3.0.1 with: message: | ## Vulnerability results from base branch ${{ steps.old.outputs.content }} + ### npm audit + + ${{ steps.audit_report.outputs.result }} + + ### npm list + + ${{ steps.list_report.outputs.result }} + + --- + ## Vulnerability results from current PR branch ${{ steps.new.outputs.content }} + + ### npm audit + + ${{ steps.audit_report.outputs.result }} + + ### npm list + + ${{ steps.list_report.outputs.result }} From 8d54bd866578f9d1be5ba0cc7952f17f37d70027 Mon Sep 17 00:00:00 2001 From: Benjamin Young Date: Fri, 10 Oct 2025 14:52:33 -0400 Subject: [PATCH 02/10] Combine audit and list reports; refactoring. This is headed toward being a shared GitHub Action, so it can be run on both the old code and the new/latest code (without copying and pasting...). --- .github/workflows/osv-scanner-pr.yaml | 33 +++++++++++++++------------ 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/.github/workflows/osv-scanner-pr.yaml b/.github/workflows/osv-scanner-pr.yaml index 157ce8c..d060a73 100644 --- a/.github/workflows/osv-scanner-pr.yaml +++ b/.github/workflows/osv-scanner-pr.yaml @@ -143,6 +143,22 @@ jobs: echo "$LIST" >> $GITHUB_OUTPUT echo "$delimiter" >> $GITHUB_OUTPUT continue-on-error: true + - name: combine npm audit/list reports + id: combined_report + run: | + # Use a random delimiter to capture the multi-line output + delimiter=$(openssl rand -hex 8) + echo "result<<$delimiter" >> $GITHUB_OUTPUT + echo " + ### npm audit + + ${{ steps.audit_report.outputs.result }} + + ### npm list + + ${{ steps.list_report.outputs.result }} + " >> $GITHUB_OUTPUT + echo "$delimiter" >> $GITHUB_OUTPUT # Combine OSV, npm audit, and npm list output into a single comment - name: Add a comment containing OSV, npm audit, and npm list output @@ -152,23 +168,10 @@ jobs: ## Vulnerability results from base branch ${{ steps.old.outputs.content }} - ### npm audit - - ${{ steps.audit_report.outputs.result }} - - ### npm list - - ${{ steps.list_report.outputs.result }} - + ${{ steps.combined_report.outputs.result }} --- ## Vulnerability results from current PR branch ${{ steps.new.outputs.content }} - ### npm audit - - ${{ steps.audit_report.outputs.result }} - - ### npm list - - ${{ steps.list_report.outputs.result }} + ${{ steps.combined_report.outputs.result }} From 7a382a6f38228bffc30f3be07e2b839248a37705 Mon Sep 17 00:00:00 2001 From: Benjamin Young Date: Fri, 10 Oct 2025 15:18:32 -0400 Subject: [PATCH 03/10] Add shared action for npm audit/list reporting. --- .github/actions/npm-audit-list/action.yaml | 68 ++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 .github/actions/npm-audit-list/action.yaml diff --git a/.github/actions/npm-audit-list/action.yaml b/.github/actions/npm-audit-list/action.yaml new file mode 100644 index 0000000..68a0c21 --- /dev/null +++ b/.github/actions/npm-audit-list/action.yaml @@ -0,0 +1,68 @@ +name: Output npm audit and list results as Markdown +description: > + A reusable action to output `npm audit` and a filtered `npm list` (based on + the audit results) in Markdown. +runs: + using: composite + steps: + # Run `npm audit` and collect as JSON + - name: npm audit + id: npm_audit + run: | + echo 'result=$(npm audit --json)' >> $GITHUB_OUTPUT + continue-on-error: true + # Use `jq` to create a Markdown table of the findings + - name: npm audit + id: audit_report + run: | + AUDIT=$(echo "${{ toJson(steps.npm_audit.outputs.result) }}" | jq -r ' + "| Severity | Name | Version | Fix Available |", + "| --- | --- | --- | --- |", + (.vulnerabilities | to_entries | .[] | + "| \(.value.severity) | \(.key) | \(.value.range) | \(.value.fixAvailable.version) |" + )') + # Use a random delimiter to capture the multi-line output + delimiter=$(openssl rand -hex 8) + echo "result<<$delimiter" >> $GITHUB_OUTPUT + echo "$AUDIT" >> $GITHUB_OUTPUT + echo "$delimiter" >> $GITHUB_OUTPUT + continue-on-error: true + # Generate a tree/list based on the vulnerable package names + - name: npm list vulnerable dependencies + id: list_report + run: | + DEPS=$(echo "${{ toJSON(steps.npm_audit.outputs.result) }}" | jq -r '[.vulnerabilities | to_entries[] | .key] | join(" ")') + LIST=$(npm list $DEPS --json | jq -r ' + def walk_tree(prefix): + to_entries | map( + "\(prefix)\(.key)@\(.value.version)\n" + + if .value.dependencies then + (.value.dependencies | walk_tree(" " + prefix)) + else + "" + end + ) | join(""); + .dependencies | walk_tree("* ")') + # Use a random delimiter to capture the multi-line output + delimiter=$(openssl rand -hex 8) + echo "result<<$delimiter" >> $GITHUB_OUTPUT + echo "$LIST" >> $GITHUB_OUTPUT + echo "$delimiter" >> $GITHUB_OUTPUT + continue-on-error: true + # Combine the two Markdown reports into one for use elsewhere + - name: combine npm audit/list reports + id: combined_report + run: | + # Use a random delimiter to capture the multi-line output + delimiter=$(openssl rand -hex 8) + echo "result<<$delimiter" >> $GITHUB_OUTPUT + echo " + ### npm audit + + ${{ steps.audit_report.outputs.result }} + + ### npm list + + ${{ steps.list_report.outputs.result }} + " >> $GITHUB_OUTPUT + echo "$delimiter" >> $GITHUB_OUTPUT From 325a12e8844d1e9664e69208f46b47760d1a8ca3 Mon Sep 17 00:00:00 2001 From: Benjamin Young Date: Fri, 10 Oct 2025 15:19:05 -0400 Subject: [PATCH 04/10] Use new shared audit/list action. This also now generates reports for the old and new code separately. --- .github/workflows/osv-scanner-pr.yaml | 70 +++++---------------------- 1 file changed, 11 insertions(+), 59 deletions(-) diff --git a/.github/workflows/osv-scanner-pr.yaml b/.github/workflows/osv-scanner-pr.yaml index d060a73..0549233 100644 --- a/.github/workflows/osv-scanner-pr.yaml +++ b/.github/workflows/osv-scanner-pr.yaml @@ -68,6 +68,12 @@ jobs: uses: juliangruber/read-file-action@b549046febe0fe86f8cb4f93c24e284433f9ab58 # v1.1.7 with: path: old-results.md + # Run npm audit and npm list to build up additional report explanations + - name: Generate npm audit and list report + id: old_audit_list_report + uses: ./.github/actions/npm-audit-list + + # --- run the same on the current branch --- - name: "Checkout current branch" # Use -f in case any changes were made by osv-scanner (there should be no changes) run: | @@ -102,63 +108,9 @@ jobs: path: new-results.md # Run npm audit and npm list to build up additional report explanations - - name: npm audit - id: npm_audit - run: | - echo 'result=$(npm audit --json)' >> $GITHUB_OUTPUT - continue-on-error: true - - name: npm audit - id: audit_report - run: | - AUDIT=$(echo "${{ toJson(steps.npm_audit.outputs.result) }}" | jq -r ' - "| Severity | Name | Version | Fix Available |", - "| --- | --- | --- | --- |", - (.vulnerabilities | to_entries | .[] | - "| \(.value.severity) | \(.key) | \(.value.range) | \(.value.fixAvailable.version) |" - )') - # Use a random delimiter to capture the multi-line output - delimiter=$(openssl rand -hex 8) - echo "result<<$delimiter" >> $GITHUB_OUTPUT - echo "$AUDIT" >> $GITHUB_OUTPUT - echo "$delimiter" >> $GITHUB_OUTPUT - continue-on-error: true - - name: npm list vulnerable dependencies - id: list_report - run: | - DEPS=$(echo "${{ toJSON(steps.npm_audit.outputs.result) }}" | jq -r '[.vulnerabilities | to_entries[] | .key] | join(" ")') - LIST=$(npm list $DEPS --json | jq -r ' - def walk_tree(prefix): - to_entries | map( - "\(prefix)\(.key)@\(.value.version)\n" + - if .value.dependencies then - (.value.dependencies | walk_tree(" " + prefix)) - else - "" - end - ) | join(""); - .dependencies | walk_tree("* ")') - # Use a random delimiter to capture the multi-line output - delimiter=$(openssl rand -hex 8) - echo "result<<$delimiter" >> $GITHUB_OUTPUT - echo "$LIST" >> $GITHUB_OUTPUT - echo "$delimiter" >> $GITHUB_OUTPUT - continue-on-error: true - - name: combine npm audit/list reports - id: combined_report - run: | - # Use a random delimiter to capture the multi-line output - delimiter=$(openssl rand -hex 8) - echo "result<<$delimiter" >> $GITHUB_OUTPUT - echo " - ### npm audit - - ${{ steps.audit_report.outputs.result }} - - ### npm list - - ${{ steps.list_report.outputs.result }} - " >> $GITHUB_OUTPUT - echo "$delimiter" >> $GITHUB_OUTPUT + - name: Generate npm audit and list report + id: new_audit_list_report + uses: ./.github/actions/npm-audit-list # Combine OSV, npm audit, and npm list output into a single comment - name: Add a comment containing OSV, npm audit, and npm list output @@ -168,10 +120,10 @@ jobs: ## Vulnerability results from base branch ${{ steps.old.outputs.content }} - ${{ steps.combined_report.outputs.result }} + ${{ steps.old_audit_list_report.outputs.result }} --- ## Vulnerability results from current PR branch ${{ steps.new.outputs.content }} - ${{ steps.combined_report.outputs.result }} + ${{ steps.new_audit_list_report.outputs.result }} From 19ba15d84aea6b8e2879fac4ed18d5fd6cb958e9 Mon Sep 17 00:00:00 2001 From: Benjamin Young Date: Fri, 10 Oct 2025 15:26:23 -0400 Subject: [PATCH 05/10] Continue past the initial shared action call. This action is new to the repo and it will fail on the old branch, so we are continuing on error in that case. --- .github/workflows/osv-scanner-pr.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/osv-scanner-pr.yaml b/.github/workflows/osv-scanner-pr.yaml index 0549233..30b1e4e 100644 --- a/.github/workflows/osv-scanner-pr.yaml +++ b/.github/workflows/osv-scanner-pr.yaml @@ -72,6 +72,7 @@ jobs: - name: Generate npm audit and list report id: old_audit_list_report uses: ./.github/actions/npm-audit-list + continue-on-error: true # this action may not exist in the repo yet... # --- run the same on the current branch --- - name: "Checkout current branch" From 6e8fdf581d58e204fab684d31200bd2cb94d39b4 Mon Sep 17 00:00:00 2001 From: Benjamin Young Date: Fri, 10 Oct 2025 15:29:41 -0400 Subject: [PATCH 06/10] Set `shell` to `bash` in shared action. --- .github/actions/npm-audit-list/action.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/actions/npm-audit-list/action.yaml b/.github/actions/npm-audit-list/action.yaml index 68a0c21..31a8f3e 100644 --- a/.github/actions/npm-audit-list/action.yaml +++ b/.github/actions/npm-audit-list/action.yaml @@ -8,12 +8,14 @@ runs: # Run `npm audit` and collect as JSON - name: npm audit id: npm_audit + shell: bash run: | echo 'result=$(npm audit --json)' >> $GITHUB_OUTPUT continue-on-error: true # Use `jq` to create a Markdown table of the findings - name: npm audit id: audit_report + shell: bash run: | AUDIT=$(echo "${{ toJson(steps.npm_audit.outputs.result) }}" | jq -r ' "| Severity | Name | Version | Fix Available |", @@ -30,6 +32,7 @@ runs: # Generate a tree/list based on the vulnerable package names - name: npm list vulnerable dependencies id: list_report + shell: bash run: | DEPS=$(echo "${{ toJSON(steps.npm_audit.outputs.result) }}" | jq -r '[.vulnerabilities | to_entries[] | .key] | join(" ")') LIST=$(npm list $DEPS --json | jq -r ' @@ -52,6 +55,7 @@ runs: # Combine the two Markdown reports into one for use elsewhere - name: combine npm audit/list reports id: combined_report + shell: bash run: | # Use a random delimiter to capture the multi-line output delimiter=$(openssl rand -hex 8) From 4ae8602bb7c1d59ee365f8b06c95624897069ab3 Mon Sep 17 00:00:00 2001 From: Benjamin Young Date: Sat, 11 Oct 2025 07:18:22 -0400 Subject: [PATCH 07/10] Use --package-lock-only on npm list. Otherwise we have to download the entire node_modules/ just to check the same data. --- .github/actions/npm-audit-list/action.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/npm-audit-list/action.yaml b/.github/actions/npm-audit-list/action.yaml index 31a8f3e..98a0e7b 100644 --- a/.github/actions/npm-audit-list/action.yaml +++ b/.github/actions/npm-audit-list/action.yaml @@ -35,7 +35,7 @@ runs: shell: bash run: | DEPS=$(echo "${{ toJSON(steps.npm_audit.outputs.result) }}" | jq -r '[.vulnerabilities | to_entries[] | .key] | join(" ")') - LIST=$(npm list $DEPS --json | jq -r ' + LIST=$(npm list $DEPS --json --package-lock-only | jq -r ' def walk_tree(prefix): to_entries | map( "\(prefix)\(.key)@\(.value.version)\n" + From 9cf1201c345ed08b31196f3f2a7912da2efa9ab2 Mon Sep 17 00:00:00 2001 From: Benjamin Young Date: Sat, 11 Oct 2025 07:18:47 -0400 Subject: [PATCH 08/10] Fix spacing in shared action. --- .github/actions/npm-audit-list/action.yaml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/actions/npm-audit-list/action.yaml b/.github/actions/npm-audit-list/action.yaml index 98a0e7b..9e379aa 100644 --- a/.github/actions/npm-audit-list/action.yaml +++ b/.github/actions/npm-audit-list/action.yaml @@ -60,13 +60,12 @@ runs: # Use a random delimiter to capture the multi-line output delimiter=$(openssl rand -hex 8) echo "result<<$delimiter" >> $GITHUB_OUTPUT - echo " - ### npm audit + echo "### npm audit - ${{ steps.audit_report.outputs.result }} + ${{ steps.audit_report.outputs.result }} - ### npm list + ### npm list - ${{ steps.list_report.outputs.result }} + ${{ steps.list_report.outputs.result }} " >> $GITHUB_OUTPUT echo "$delimiter" >> $GITHUB_OUTPUT From 4c95459435444081863a2028e5f8e877c7f831c6 Mon Sep 17 00:00:00 2001 From: Benjamin Young Date: Sat, 11 Oct 2025 07:42:08 -0400 Subject: [PATCH 09/10] Set explicit output variable on shared action. Local tests did not need this...but apparently the runner in the cloud does. --- .github/actions/npm-audit-list/action.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/actions/npm-audit-list/action.yaml b/.github/actions/npm-audit-list/action.yaml index 9e379aa..276cfd2 100644 --- a/.github/actions/npm-audit-list/action.yaml +++ b/.github/actions/npm-audit-list/action.yaml @@ -2,6 +2,10 @@ name: Output npm audit and list results as Markdown description: > A reusable action to output `npm audit` and a filtered `npm list` (based on the audit results) in Markdown. +outputs: + result: + description: Combined npm audit/list reports + value: ${{ steps.combined_report.outputs.result }} runs: using: composite steps: From dc52911f20adab9be44e157e88a9e5995b3952d0 Mon Sep 17 00:00:00 2001 From: Benjamin Young Date: Sat, 11 Oct 2025 16:28:29 -0400 Subject: [PATCH 10/10] Add license header to shared action. --- .github/actions/npm-audit-list/action.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/actions/npm-audit-list/action.yaml b/.github/actions/npm-audit-list/action.yaml index 276cfd2..7625359 100644 --- a/.github/actions/npm-audit-list/action.yaml +++ b/.github/actions/npm-audit-list/action.yaml @@ -1,3 +1,6 @@ +# Copyright 2025 Digital Bazaar, Inc. +# SPDX-License-Identifier: BSD-3-Clause + name: Output npm audit and list results as Markdown description: > A reusable action to output `npm audit` and a filtered `npm list` (based on