feat(audit): weekly CodeRabbit Deep Scan audit that files findings as issues
Summary
Adds a weekly audit (Thursday 17:01 UTC) that reads CodeRabbit Deep Scan findings from GET /v1/security/scans/code?format=sarif&state=all and keeps one issue per finding, keyed on findingFingerprint, in the repo that owns the code. These findings currently reach neither the SonarCloud nor the CodeQL audit.
Standard: standards/ci-standards.md § CodeRabbit Audit.
Design choices (approved by the repo owner before this PR)
| Choice | Decision |
|---|---|
| Public issue content | Fingerprint, finding ID, severity, repo. No category, CWE, subsystem, path, line, description or call stack: every org repo is public and these findings can describe exploit chains |
dev-lead | Never added by the audit. Issues get needs-verification; a person confirms the finding and adds dev-lead. The API exposes no verification result to gate on |
| Severity threshold | Every severity is filed (MIN_SEVERITY defaults to low) |
Behavior worth reviewing
- Routing: a finding located in a caller stub (no
steps:, calling one org repo's reusable workflow) goes to that repo. Call-stack frames are not used, because CodeRabbit guarantees neither their order nor their format. - Closing: only on an explicit
fixedordismissed. A finding absent from the export stays open and is counted, because a partial-coverage scan omits findings it did not reach. - Fail closed: a failed read, an export missing expected identity fields, an unreadable stub, or an unlistable repo fails the run with no issue writes. API error bodies are never logged.
- Artifact: the counts-only
summary.md.
Test plan
-
bats test/scripts/coderabbit-audit/: 56 pass, including stubbed end-to-end runs with canary strings in every sensitive field - Mutation checks: leaking finding text to logs or issue text, adding
dev-lead, closing on absence, passing the key in argv, and filing duplicates each fail a test -
shellcheck,actionlint,check-cron-timing.sh,gitleaks - Dry run over two real UI exports with
curlstubbed: parses and routes, no issue writes - First keyed
workflow_dispatchwithdry_run: true
Not yet verified
No API key exists yet, so the live endpoint has not been called. Check these on the first keyed dry run:
- The API's SARIF matches the UI export's fields. A mismatch fails the run with no writes.
state=allreturnsfixedanddismissedfindings. The sample exports contain onlyopen.- The key works on this plan. The endpoint page says all plans qualify.
findingFingerprintis stable across scans. Every sampled finding has only been seen in one scan. If it is not stable, each scan files duplicates, visible as a rising "absent from this export" count.
Before merge
- Create a non-agentic CodeRabbit API key with
security:readand add it as theCODERABBIT_API_KEYrepository secret oncombsmith/.github. - Set a recurring Deep Scan schedule per repo in CodeRabbit. The API only reads results, and scans are usage-billed.
The first non-dry run files about 72 issues based on current exports.
🤖 Generated with Claude Code
Problem
See the linked issue.
Risk
Medium — changes GitHub Actions workflow behavior, which is exercised only post-merge; verify via the affected workflow runs.
Test plan
Tests added/updated: test/scripts/coderabbit-audit/audit-run.bats,test/scripts/coderabbit-audit/pure-helpers.bats. Verification: bash scripts/dev-lead-lint.sh (shellcheck --severity=warning) ran pre-commit; the bats suite runs in CI.
Rollback
Revert this PR. No non-revertible side effects (no tags, migrations, or external state).
Monitoring
Watch the affected workflow run(s) in the Actions tab and this PR's Lint check for regressions.