From c2124427397254bbff51020e08b2a5592f848e81 Mon Sep 17 00:00:00 2001 From: Sachu Shaji Abraham Date: Thu, 23 Jul 2026 00:44:31 +0530 Subject: [PATCH] ci: replace invalid osv-scanner severity flags osv-scanner v2.3.8 has no --severity flag, so #9331 broke the audit step with 'flag provided but not defined: -severity'. Replace it with a JSON output scan plus a composite-action gate that fails only on findings with CVSS >= 7.0 (HIGH/CRITICAL), which is what #9331 intended. Medium/low findings are logged as warnings and no longer block releases. TICKET: VL-7134 TICKET: VL-7134 --- .github/actions/osv-severity-gate/action.yml | 78 ++++++++++++++++++++ .github/workflows/npmjs-release.yml | 14 +++- .github/workflows/publish.yml | 14 +++- 3 files changed, 102 insertions(+), 4 deletions(-) create mode 100644 .github/actions/osv-severity-gate/action.yml diff --git a/.github/actions/osv-severity-gate/action.yml b/.github/actions/osv-severity-gate/action.yml new file mode 100644 index 0000000000..49fbd61637 --- /dev/null +++ b/.github/actions/osv-severity-gate/action.yml @@ -0,0 +1,78 @@ +name: OSV severity gate +description: > + Fails the build only when osv-scanner results contain a vulnerability at or + above the CVSS threshold (default 7.0, i.e. HIGH/CRITICAL). osv-scanner has + no severity filter of its own and exits non-zero on ANY finding, so the scan + step must run with `continue-on-error: true`, output JSON to a file, and let + this gate decide pass/fail. + +inputs: + scan-outcome: + description: Outcome of the osv-scanner step (`steps..outcome`) + required: true + results-file: + description: Path to the osv-scanner JSON results file + required: false + default: osv-results.json + cvss-threshold: + description: Minimum CVSS score that fails the build + required: false + default: "7.0" + +runs: + using: composite + steps: + - name: Evaluate scan results + shell: bash + env: + SCAN_OUTCOME: ${{ inputs.scan-outcome }} + RESULTS_FILE: ${{ inputs.results-file }} + CVSS_THRESHOLD: ${{ inputs.cvss-threshold }} + run: | + set -euo pipefail + + # osv-scanner exit codes: 0 = no findings, 1 = findings, >1 = scan error. + if [[ "$SCAN_OUTCOME" == "success" ]]; then + echo "osv-scanner found no vulnerabilities." + rm -f "$RESULTS_FILE" + exit 0 + fi + + if ! jq -e . "$RESULTS_FILE" >/dev/null 2>&1; then + echo "::error::osv-scanner failed without producing valid JSON results ($RESULTS_FILE). This is a scan error (e.g. bad flags or network failure), not a vulnerability finding. Check the audit step logs." + exit 1 + fi + + echo "Vulnerability findings (max CVSS per advisory group):" + jq -r ' + .results[]?.packages[]? + | .package as $p + | .groups[]? + | " \($p.name)@\($p.version) CVSS \(.max_severity // "" | if . == "" then "unscored" else . end) \(.ids | join(", "))" + ' "$RESULTS_FILE" + + TOTAL=$(jq '[.results[]?.packages[]?.groups[]?] | length' "$RESULTS_FILE") + UNSCORED=$(jq '[.results[]?.packages[]?.groups[]? | select((.max_severity // "") == "")] | length' "$RESULTS_FILE") + BLOCKING=$(jq --argjson t "$CVSS_THRESHOLD" ' + [.results[]?.packages[]?.groups[]? + | .max_severity // "" + | select(. != "") + | tonumber + | select(. >= $t)] + | length + ' "$RESULTS_FILE") + + # Keep the working tree clean for later git/publish steps. + rm -f "$RESULTS_FILE" + + if [[ "$UNSCORED" -gt 0 ]]; then + echo "::warning::$UNSCORED advisory group(s) have no CVSS score and were not counted against the threshold. Review them manually." + fi + + if [[ "$BLOCKING" -gt 0 ]]; then + echo "::error::$BLOCKING of $TOTAL advisory group(s) at or above CVSS $CVSS_THRESHOLD (HIGH/CRITICAL). Failing the release. Fix the dependency or add a justified exclusion to osv-scanner.toml." + exit 1 + fi + + echo "$TOTAL advisory group(s) found, none at or above CVSS $CVSS_THRESHOLD. Not blocking the release." + echo "::warning::$TOTAL medium/low advisory group(s) present — see log above. Track remediation in WCN-1550." diff --git a/.github/workflows/npmjs-release.yml b/.github/workflows/npmjs-release.yml index b862c711d1..53c9a7983a 100644 --- a/.github/workflows/npmjs-release.yml +++ b/.github/workflows/npmjs-release.yml @@ -266,15 +266,25 @@ jobs: run: | yarn install --frozen-lockfile + # osv-scanner has no severity filter and exits non-zero on ANY finding, + # so run it with continue-on-error and let the severity gate below + # decide pass/fail based on CVSS score (fails on HIGH/CRITICAL only). - name: Audit Dependencies + id: osv-scan + continue-on-error: true uses: google/osv-scanner-action/osv-scanner-action@9a498708959aeaef5ef730655706c5a1df1edbc2 # v2.3.8 with: scan-args: |- --config=osv-scanner.toml - --severity=HIGH - --severity=CRITICAL + --format=json + --output-file=osv-results.json ./ + - name: Enforce Vulnerability Severity Threshold + uses: ./.github/actions/osv-severity-gate + with: + scan-outcome: ${{ steps.osv-scan.outcome }} + - name: Run dependency check run: | yarn check-deps diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 419182cf49..a6de32846c 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -36,15 +36,25 @@ jobs: - name: Install BitGoJS run: sfw yarn install --with-frozen-lockfile + # osv-scanner has no severity filter and exits non-zero on ANY finding, + # so run it with continue-on-error and let the severity gate below + # decide pass/fail based on CVSS score (fails on HIGH/CRITICAL only). - name: Audit Dependencies + id: osv-scan + continue-on-error: true uses: google/osv-scanner-action/osv-scanner-action@9a498708959aeaef5ef730655706c5a1df1edbc2 # v2.3.8 with: scan-args: |- --config=osv-scanner.toml - --severity=HIGH - --severity=CRITICAL + --format=json + --output-file=osv-results.json ./ + - name: Enforce Vulnerability Severity Threshold + uses: ./.github/actions/osv-severity-gate + with: + scan-outcome: ${{ steps.osv-scan.outcome }} + - name: Set Environment Variable for Alpha if: github.ref != 'refs/heads/master' # only publish changes if on feature branches run: |