ci: auto-file GitHub issue when periodic security-scan finds a regression (#73)#75
Merged
Conversation
…sion (#73) Extend `.github/workflows/security-scan.yml` so a red cron run gains an owner via a labelled GitHub issue instead of scrolling past in Actions. - Switch both scanners to JSON output (`format: json` for Trivy, `-json` for govulncheck) so the rendered issue body can name the primary CVE/vuln id and affected component without fragile table parsing. Policy flags (severity, ignore-unfixed, vuln-type, exit-code) are unchanged — same definition of a finding as PR-time. - Add per-scanner `if: failure()` render + upload-artifact steps that pre-bake an `issue-title.txt` + `issue-body.md` pair and hand them off as `regression-image-scan` / `regression-govulncheck` artifacts. - Add a `file-issue` job (`needs: [image-scan, govulncheck]`, `if: failure()`) that downloads the artifacts and posts via `gh issue create --body-file` (never `--body "<...>"`). `gh issue list --search "in:title \"<full-title>\""` provides best-effort dedup against open `security-sensitive` issues. - `issues: write` is scoped to the `file-issue` job ONLY — the workflow-level block stays `contents: read` and the scanner jobs keep their belt-and-suspenders `contents: read` (AC #3). - Add workflow-level `concurrency: security-scan` (deferred from #72) with `cancel-in-progress: false` so overlapping scheduled/manual runs queue rather than truncate artifacts mid-upload. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Contributor
Author
Code Review: #73Decision: PASS Security-sensitive checklist
AC verification
Security-goggles pass on the diff
Findings
SummaryThree jobs, two trust tiers, clean privilege split. The artifact-handoff design correctly keeps |
- docs/knowledge/codebase/73.md — implementation summary, patterns established (privilege split via artifact handoff, --body-file for machine-generated bodies, job-level permission widening, deterministic-title dedup, shopt -s nullglob), verification flows, and lessons learned (passive→active signal conversion, shell-argument expansion as threat surface, lockstep-via-comment). - docs/knowledge/features/docker-image.md § CI image scanning — now names the file-issue job, the artifact-handoff privilege split, and the dedup mechanism. - docs/threat-model.md § Supply chain — cites #73 alongside #72 as the actionability layer on the periodic re-scan control. - docs/knowledge/INDEX.md — extends the docker-image entry with the #73 summary.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Extend
.github/workflows/security-scan.ymlso a failing daily cron run opens a labelled GitHub issue instead of just producing a red row in the Actions list.trivy.json,govulncheck.json) so the body can be machine-rendered without table-column regex fragility. Policy flags (severity, ignore-unfixed, vuln-type, exit-code) are unchanged — same definition of a finding as PR-time.if: failure()post-steps: ajq-based renderer that writesissue-title.txt+issue-body.md, and anactions/upload-artifact@v5step (guarded byhashFiles(...) != ''so scanner-infrastructure failures with no parseable findings produce no artifact and no spurious issue).file-issuejob (needs: [image-scan, govulncheck],if: failure()) downloads the artifacts viaactions/download-artifact@v5, runs a dedupgh issue list --search "in:title \"<full-title>\""against opensecurity-sensitiveissues, and posts viagh issue create --body-file(NEVER--body "<...>", so CVE-derived content never passes through shell-argument expansion).issues: writeis granted at the job level onfile-issueonly — the workflow-level block stayscontents: readand the scanner jobs keep their belt-and-suspenderscontents: read(AC relay: connection registry — server-id → binary + server-id → [phone] thread-safe maps #3).concurrency: security-scanwithcancel-in-progress: false(the block deferred from relay: periodic security-scan cron workflow (re-runs Trivy + govulncheck against main) #72) so overlapping scheduled/manual runs queue rather than truncate artifacts mid-upload.Issue
Closes #73. Architecture spec:
docs/specs/architecture/73-auto-issue-on-security-regression.md.Testing
This is a CI workflow change — no Go code touched, no unit tests added. The spec's testing strategy is operator-driven against the deployed workflow:
maingreen path (AC relay: WS upgrade for /v1/server — accept binary connection, validate headers, claim server-id #4 first half). After merge, hit "Run workflow" on the Actions tab againstmain. Expect: both scanners green,file-issueskipped (failure()false), no issue filed.verify/73-vulnerable-state), pin the Dockerfile base image to a digest with a known fixable CRITICAL/HIGH CVE (or add a temporarygolang.org/x/text v0.3.7import reachable frommain()to surface aGO-...advisory).gh workflow run security-scan.yml --ref verify/73-vulnerable-state. Expect: the failing scanner job goes red,file-issueruns, one issue is filed with the deterministic title,security-sensitivelabel, body containing the CVE/vuln id + component + workflow run URL. Trigger a second run on the same branch; expectduplicate suppressed for: <title>in thefile-issuelog and no second issue. Close the test issue, delete the branch.pull_request:trigger to observe a green run on this PR, then remove before merge.Architecture compliance
if: failure()post-steps render an issue body containing scanner output excerpt, primary CVE/vuln id, affected dependency/image-layer, and workflow run URL.--label security-sensitiveset ongh issue create.gh issue list --search "in:title \"<full-title>\"" --label security-sensitive --state open— option (a) from the AC, with a deterministic title pattern (security-scan regression: <id> in <component>) that also makes option-b dedup-by-eye work as a backstop.issues: writelives ONLY on thefile-issuejob'spermissions:block. Workflow-level remainscontents: read. Scanner jobs unchanged.file-issueskipped). Vulnerable-state recipes documented in the spec for the verification run.Security-review section in the architect spec covers trust boundaries (CVE-DB content →
gh issue create --body-file), token scoping (github.token, job-scoped permissions), adversarial scenarios (markdown injection bounded by GitHub's renderer; concurrent runs coalesced; dedup false-positive/-negative both accepted).🤖 Generated with Claude Code