From bb2bd94a5020c89e99b9943b53759760ba1807d5 Mon Sep 17 00:00:00 2001 From: Ryan Duguid Date: Sun, 1 Nov 2026 10:52:37 +1100 Subject: [PATCH 1/2] ci: apply .snyk ignores to the Snyk SBOM tests snyk sbom test ignores .snyk, so an advisory ignored there would still fail the job. Each SBOM is now tested with --json; findings whose ID .snyk ignores for every path with an expiry still to come are dropped, every finding is printed as open or ignored, and any open one fails the job. A Snyk error (exit above 1), empty output or JSON without a vulnerabilities list still fails it. Without a .snyk nothing is ignored. --- .github/workflows/snyk.yml | 50 +++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/.github/workflows/snyk.yml b/.github/workflows/snyk.yml index c47b333..4c43469 100644 --- a/.github/workflows/snyk.yml +++ b/.github/workflows/snyk.yml @@ -54,6 +54,11 @@ jobs: - uses: taiki-e/install-action@1e29709c0ec63babbe8f266397a465f29a825f91 # v2.87.31 with: tool: cargo-cyclonedx@0.5.9 + # `snyk test` applies .snyk itself; `snyk sbom test` does not, so its ignores + # are applied here: a finding is dropped when .snyk ignores its ID for every + # path ('*') with an `expires` still to come. Without a .snyk nothing is + # ignored. Snyk exiting above 1 (an error), or output the filter cannot + # read, fails the job. - name: Test each dependency set env: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} @@ -64,6 +69,49 @@ jobs: if [ -z "$SNYK_TOKEN" ]; then echo "::error::The SNYK_TOKEN repository secret is not set, so nothing was scanned." fi + # {"": ""} for each ID .snyk ignores everywhere. + ignored='{}' + if [ -f .snyk ]; then + ignored=$(yq -o=json '.ignore // {}' .snyk | jq -c ' + with_entries(.value = ([.value[] | .["*"]? | .expires? // empty + | select((sub("\\.[0-9]+"; "") | fromdateiso8601) > now)] | max)) + | with_entries(select(.value != null))') + fi + report=$RUNNER_TEMP/snyk.json + # Tests one SBOM and prints each finding as open or ignored; fails on an + # open finding or a result it cannot read. + sbom_test() { + local code=0 findings open + echo "== $1" + snyk sbom test --experimental --json --file="$1" > "$report" || code=$? + if [ "$code" -gt 1 ] || [ ! -s "$report" ]; then + cat "$report"; echo + echo "::error::snyk sbom test failed on $1 (exit $code)." + return 1 + fi + # One line per vulnerable path, `open` or `ignored`. + if ! findings=$(jq -r --argjson ignored "$ignored" ' + if type == "array" then .[] else . end + | if (.vulnerabilities | type) == "array" then .vulnerabilities[] + else error("no vulnerabilities list in the Snyk output") end + | $ignored[.id] as $until + | "\(if $until then "ignored" else "open" end) [\(.severity // "?" | ascii_upcase)] \(.title) (\(.id)) in \(.packageName)@\(.version) via \(.from // [] | join(" > "))\(if $until then ", ignored until \($until)" else "" end)" + ' "$report"); then + cat "$report"; echo + echo "::error::Could not read the Snyk results for $1." + return 1 + fi + [ -z "$findings" ] || printf '%s\n' "$findings" + open=$(printf '%s\n' "$findings" | grep -c '^open ' || true) + if [ "$open" -gt 0 ]; then + echo "::error::$open open Snyk finding(s) in $1." + return 1 + elif [ "$code" -eq 1 ] && [ -z "$findings" ]; then + cat "$report"; echo + echo "::error::Snyk failed $1 without listing a vulnerability." + return 1 + fi + } status=0 while IFS=: read -r kind target; do [ -n "$kind" ] || continue @@ -82,7 +130,7 @@ jobs: || { echo "::error::Cannot list the SBOMs for $target"; status=1; continue; } [ -s "$list" ] || { echo "::error::cargo cyclonedx wrote no SBOM for $target"; status=1; continue; } while IFS= read -r -d '' sbom; do - [ -z "$SNYK_TOKEN" ] || snyk sbom test --experimental --file="$sbom" || status=1 + [ -z "$SNYK_TOKEN" ] || sbom_test "$sbom" || status=1 done < "$list" ;; *) echo "::error::Unknown dependency set $kind:$target"; status=1 ;; esac From 2786f8efc9e5677fedaf45357d903e83767fc93c Mon Sep 17 00:00:00 2001 From: Ryan Duguid Date: Sun, 1 Nov 2026 12:14:56 +1100 Subject: [PATCH 2/2] ci: fail the Snyk job when its output holds no result An empty JSON array or whitespace-only output produced no findings and passed. The filter now reads every JSON document and fails unless at least one result is present; an empty vulnerabilities list still passes. --- .github/workflows/snyk.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/snyk.yml b/.github/workflows/snyk.yml index 4c43469..fef8f82 100644 --- a/.github/workflows/snyk.yml +++ b/.github/workflows/snyk.yml @@ -90,8 +90,9 @@ jobs: return 1 fi # One line per vulnerable path, `open` or `ignored`. - if ! findings=$(jq -r --argjson ignored "$ignored" ' - if type == "array" then .[] else . end + if ! findings=$(jq -rn --argjson ignored "$ignored" ' + [inputs | if type == "array" then .[] else . end] + | if length == 0 then error("no result in the Snyk output") else .[] end | if (.vulnerabilities | type) == "array" then .vulnerabilities[] else error("no vulnerabilities list in the Snyk output") end | $ignored[.id] as $until