feat: enforce live supply chain provenance gates
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Live CI supply-chain gate. Unlike the deterministic local denylist gate, this
|
||||
# obtains current advisory data and scans the built images. Missing scanners are
|
||||
# an error in strict CI; no "scanner unavailable => green" path exists.
|
||||
#
|
||||
# Usage: live-supply-chain-scan.sh <report-dir> [image-ref ...]
|
||||
# Required tools: npm, osv-scanner, trivy (when CASAN_REQUIRE_LIVE_SCANNERS=1)
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR/casan-paths.sh"
|
||||
ROOT="$CASAN_APP_ROOT"
|
||||
OUT="${1:-}"; shift || true
|
||||
[[ -n "$OUT" ]] || { echo "LIVE_SCAN_USAGE report-dir [image-ref ...]" >&2; exit 64; }
|
||||
mkdir -p "$OUT"
|
||||
|
||||
fail() { echo "LIVE_SUPPLY_CHAIN_FAIL $*" >&2; exit 1; }
|
||||
require() { command -v "$1" >/dev/null 2>&1 || fail "tool_missing name=$1"; }
|
||||
require npm
|
||||
if [[ "${CASAN_REQUIRE_LIVE_SCANNERS:-1}" == "1" ]]; then
|
||||
require osv-scanner
|
||||
require trivy
|
||||
fi
|
||||
|
||||
[[ -f "$ROOT/package-lock.json" ]] || fail "package_lock_missing"
|
||||
cd "$ROOT"
|
||||
|
||||
# npm audit exits non-zero for findings at/above the requested level. Preserve
|
||||
# JSON evidence even on failure without converting a failed scan into success.
|
||||
set +e
|
||||
npm audit --omit=dev --audit-level=high --json > "$OUT/npm-audit.json" 2> "$OUT/npm-audit.stderr"
|
||||
audit_rc=$?
|
||||
set -e
|
||||
[[ "$audit_rc" -eq 0 ]] || fail "npm_audit_high_or_critical rc=$audit_rc report=$OUT/npm-audit.json"
|
||||
|
||||
npm sbom --sbom-format cyclonedx --package-lock-only --omit=dev > "$OUT/npm.cyclonedx.json" \
|
||||
|| fail "npm_sbom_failed"
|
||||
python3 - "$OUT/npm.cyclonedx.json" <<'PY' || exit 1
|
||||
import json, sys
|
||||
d = json.load(open(sys.argv[1]))
|
||||
assert d.get("bomFormat") == "CycloneDX", d
|
||||
PY
|
||||
|
||||
if command -v osv-scanner >/dev/null 2>&1; then
|
||||
osv-scanner scan source --recursive --format json --output-file "$OUT/osv-source.json" "$ROOT" \
|
||||
|| fail "osv_source_findings_or_scan_error report=$OUT/osv-source.json"
|
||||
fi
|
||||
if command -v trivy >/dev/null 2>&1; then
|
||||
trivy fs --scanners vuln,secret,misconfig --severity HIGH,CRITICAL --exit-code 1 --format json --output "$OUT/trivy-source.json" "$ROOT" \
|
||||
|| fail "trivy_source_findings_or_scan_error report=$OUT/trivy-source.json"
|
||||
fi
|
||||
|
||||
for image in "$@"; do
|
||||
safe="$(printf '%s' "$image" | tr '/:@' '___')"
|
||||
if command -v osv-scanner >/dev/null 2>&1; then
|
||||
osv-scanner scan image --format json --output-file "$OUT/osv-image-${safe}.json" "$image" \
|
||||
|| fail "osv_image_findings_or_scan_error image=$image"
|
||||
fi
|
||||
if command -v trivy >/dev/null 2>&1; then
|
||||
trivy image --severity HIGH,CRITICAL --exit-code 1 --format json --output "$OUT/trivy-image-${safe}.json" "$image" \
|
||||
|| fail "trivy_image_findings_or_scan_error image=$image"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "LIVE_SUPPLY_CHAIN_PASS sbom=$OUT/npm.cyclonedx.json images=$#"
|
||||
Reference in New Issue
Block a user