Forced workflow failure if scan results are missing (prevents false negatives) Fixed "Fail on critical" step to use calculated counts instead of missing action outputs Added debug logging and file verification for Grype scans Refactored shell scripts to prevent injection vulnerabilities
111 lines
5.1 KiB
Markdown
111 lines
5.1 KiB
Markdown
# Plan: Fix Supply Chain Vulnerability Reporting
|
|
|
|
## Objective
|
|
Fix the `supply-chain-pr.yml` workflow where PR comments report 0 vulnerabilities despite known CVEs, and ensure the workflow correctly fails on critical vulnerabilities.
|
|
|
|
## Context
|
|
The current workflow uses `anchore/scan-action` to scan for vulnerabilities. However, there are potential issues with:
|
|
1. **Output File Handling:** The workflow assumes `results.json` is created, but `anchore/scan-action` with `output-format: json` might not produce this file by default without an explicit `output-file` parameter or capturing output.
|
|
2. **Parsing Logic:** If the file is missing, the `jq` parsing gracefully falls back to 0, masking the error.
|
|
3. **Failure Condition:** The failure step references `${{ steps.grype-scan.outputs.critical_count }}`, which likely does not exist on the `anchore/scan-action` step. It should reference the calculated output from the parsing step.
|
|
|
|
## Research & Diagnosis Steps
|
|
|
|
### 1. Debug Output paths
|
|
We need to verify if `results.json` is actually generated.
|
|
- **Action:** Add a step to list files in the workspace immediately after the scan.
|
|
- **Action:** Add a debug `cat` of the results file if it exists, or header of it.
|
|
|
|
### 2. Verify `anchore/scan-action` behavior
|
|
The `anchore/scan-action` (v7.3.2) documentation suggests that `output-format` is used, but typically it defaults to `results.[format]`. However, explicit `output-file` prevents ambiguity.
|
|
|
|
## Implementation Plan
|
|
|
|
### Phase 1: Robust Path & Debugging
|
|
1. **Explicit Output File:** Modify the `anchore/scan-action` step to explicitly set `output-format: json` AND likely we should try to rely on the default behavior but *check* it.
|
|
*Actually, better practice:* The action supports `output-format` as a list. If we want a file, we usually just look for it.
|
|
*Correction:* We will explicitly check for the file and fail if missing, rather than defaulting to 0.
|
|
2. **List Files:** Add `ls -la` after scan to see exactly what files are created.
|
|
|
|
### Phase 2: Fix Logic Errors
|
|
1. **Update "Fail on critical vulnerabilities" step**:
|
|
- Change `${{ steps.grype-scan.outputs.critical_count }}` to `${{ steps.vuln-summary.outputs.critical_count }}`.
|
|
2. **Robust `jq` parsing**:
|
|
- In `Process vulnerability results`, explicitly check for existence of `results.json` (or whatever the action outputs).
|
|
- If missing, **EXIT 1** instead of setting counts to 0. This forces us to fix the path issue rather than silently passing.
|
|
- Use `tee` or `cat` to print the first few lines of the JSON to stdout for debugging logs.
|
|
|
|
### Phase 3: Validation
|
|
1. Run the workflow on a PR (or simulate via push).
|
|
2. Verify the PR comment shows actual numbers.
|
|
3. Verify the workflow fails if critical vulnerabilities are found (or we can lower the threshold to test).
|
|
|
|
## Detailed Changes
|
|
|
|
### `supply-chain-pr.yml`
|
|
|
|
```yaml
|
|
# ... inside steps ...
|
|
|
|
- name: Scan for vulnerabilities
|
|
if: steps.set-target.outputs.image_name != ''
|
|
uses: anchore/scan-action@7037fa011853d5a11690026fb85feee79f4c946c # v7.3.2
|
|
id: grype-scan
|
|
with:
|
|
sbom: sbom.cyclonedx.json
|
|
fail-build: false
|
|
output-format: json
|
|
# We might need explicit output selection implies asking for 'json' creates 'results.json'
|
|
|
|
- name: Debug Output Files
|
|
if: steps.set-target.outputs.image_name != ''
|
|
run: |
|
|
echo "📂 Listing workspace files:"
|
|
ls -la
|
|
|
|
- name: Process vulnerability results
|
|
if: steps.set-target.outputs.image_name != ''
|
|
id: vuln-summary
|
|
run: |
|
|
# The scan-action output behavior verification
|
|
JSON_RESULT="results.json"
|
|
SARIF_RESULT="results.sarif"
|
|
|
|
# [NEW] Check if scan actually produced output
|
|
if [[ ! -f "$JSON_RESULT" ]]; then
|
|
echo "❌ Error: $JSON_RESULT not found!"
|
|
echo "Available files:"
|
|
ls -la
|
|
exit 1
|
|
fi
|
|
|
|
mv "$JSON_RESULT" grype-results.json
|
|
|
|
# Debug content (head)
|
|
echo "📄 Grype JSON Preview:"
|
|
head -n 20 grype-results.json
|
|
|
|
# ... existing renaming for sarif ...
|
|
|
|
# ... existing jq logic, but remove 'else' block for missing file since we exit above ...
|
|
|
|
# ...
|
|
|
|
- name: Fail on critical vulnerabilities
|
|
if: steps.set-target.outputs.image_name != ''
|
|
run: |
|
|
# [FIX] Use the output from the summary step, NOT the scan step
|
|
CRITICAL_COUNT="${{ steps.vuln-summary.outputs.critical_count }}"
|
|
|
|
if [[ "${CRITICAL_COUNT}" -gt 0 ]]; then
|
|
echo "🚨 Found ${CRITICAL_COUNT} CRITICAL vulnerabilities!"
|
|
echo "Please review the vulnerability report and address critical issues before merging."
|
|
exit 1
|
|
fi
|
|
```
|
|
|
|
### Acceptance Criteria
|
|
- [ ] Workflow "Fail on critical vulnerabilities" uses `steps.vuln-summary.outputs.critical_count`.
|
|
- [ ] `Process vulnerability results` step fails if the scan output file is missing.
|
|
- [ ] Debug logging (ls -la) is present to confirm file placement.
|