Skip to content

Commit 84c5540

Browse files
committed
ci(fix): CVE gate now feeds osv a recognized .cdx.json via -L and fails closed (was silently scanning nothing) (#4)
1 parent 29e1133 commit 84c5540

1 file changed

Lines changed: 33 additions & 5 deletions

File tree

.github/workflows/ci.yml

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,23 +95,51 @@ jobs:
9595
mkdir -p "$OSV_SCANNER_LOCAL_DB_CACHE_DIRECTORY"
9696
# outputName uses ${project.version}; resolve the actual emitted file.
9797
SBOM=$(ls target/*-bom.json | head -n1)
98-
echo "Scanning SBOM: $SBOM"
98+
if [ -z "${SBOM:-}" ] || [ ! -f "$SBOM" ]; then
99+
echo "::error::CycloneDX SBOM not found under target/*-bom.json — cannot run the CVE gate."
100+
exit 1
101+
fi
102+
# osv-scanner detects SBOM format from the FILENAME; '<name>-bom.json'
103+
# is not a recognized CycloneDX name (it silently parses nothing), so
104+
# copy to a recognized '.cdx.json' name before scanning.
105+
SBOM_CDX="$RUNNER_TEMP/bom.cdx.json"
106+
cp "$SBOM" "$SBOM_CDX"
107+
echo "Scanning SBOM: $SBOM (as $SBOM_CDX)"
99108
# --offline-vulnerabilities: never contacts the live feed during scan.
100109
# --download-offline-databases: refresh the cached DB if cache missed.
101-
# osv-scanner exits 1 on ANY finding; we ignore its exit code and gate
102-
# ourselves on CVSS severity from the JSON instead.
110+
# -L: lockfile/SBOM input (replaces the deprecated --sbom).
111+
set +e
103112
osv-scanner scan \
104113
--offline-vulnerabilities \
105114
--download-offline-databases \
106115
--format json \
107-
--sbom "$SBOM" > osv-results.json || true
116+
-L "$SBOM_CDX" > osv-results.json 2> osv-stderr.txt
117+
OSV_EXIT=$?
118+
set -e
119+
cat osv-stderr.txt || true
120+
# FAIL CLOSED: osv-scanner exits 0 (no vulns) or 1 (vulns found) on a
121+
# real scan; any other exit code, a parse error, or a missing results
122+
# object means NOTHING was scanned — the gate must fail, never pass on
123+
# a non-scan (the bug this replaces let a non-scan pass green).
124+
if [ "$OSV_EXIT" != "0" ] && [ "$OSV_EXIT" != "1" ]; then
125+
echo "::error::osv-scanner did not complete a scan (exit $OSV_EXIT) — failing closed."
126+
exit 1
127+
fi
128+
if grep -qiE "Failed to parse|invalid SBOM" osv-stderr.txt; then
129+
echo "::error::osv-scanner could not ingest the SBOM — failing closed."
130+
exit 1
131+
fi
132+
if ! jq -e 'has("results")' osv-results.json >/dev/null 2>&1; then
133+
echo "::error::osv-scanner produced no results object — failing closed."
134+
exit 1
135+
fi
108136
# Gate: max_severity is a CVSS base-score string per vulnerability
109137
# group. >= 7.0 == HIGH or CRITICAL (CVSS v3). Anything below is
110138
# non-blocking per security.md. An unscored group (null or "") is
111139
# coerced to 0 so a missing CVSS never crashes or trips the gate.
112140
SEV='((.max_severity // "0") | if . == "" then "0" else . end | tonumber)'
113141
HIGH=$(jq "[.results[]?.packages[]?.groups[]? | ${SEV} | select(. >= 7.0)] | length" osv-results.json)
114-
echo "HIGH/CRITICAL findings (CVSS >= 7.0): ${HIGH}"
142+
echo "HIGH/CRITICAL findings (CVSS >= 7.0): ${HIGH:-0}"
115143
jq -r ".results[]?.packages[]? | .package as \$p | .groups[]? | ${SEV} as \$s | select(\$s >= 7.0) | \" BLOCK \(\$p.name)@\(\$p.version) CVSS=\(.max_severity) \(.ids | join(\",\"))\"" osv-results.json || true
116144
if [ "${HIGH:-0}" -gt 0 ]; then
117145
echo "::error::${HIGH} HIGH/CRITICAL dependency vulnerabilit(ies) found — failing per security policy."

0 commit comments

Comments
 (0)