Every push runs five security jobs: pip-audit, npm audit, bandit, gitleaks, and (manual for now) OWASP ZAP. This doc tells you what to do when one of them fires.
| Severity | Action |
|---|---|
| CRITICAL | Block merge. File issue with severity:critical + area:security. Fix before any other work ships. |
| HIGH | Block merge. File issue with severity:high. Fix in this PR or in a directly follow-on PR (link it). |
| MEDIUM | Do not block. File issue with severity:medium-low. Triage within 1 week. |
| LOW | Accumulate in the monthly security digest issue. |
pip install --upgrade <package> to a non-vulnerable version, or pin a patched range.npm audit fix usually works for transitive. For direct deps, bump explicitly..npmauditrc (create if missing).eval/exec usage (B307), pickle.loads (B301), weak crypto (B303).# nosec) require a one-line justification comment on the same line.git filter-repo or BFG)
3. Force-push requires --force-with-lease, approval from repo owner
4. File an incident issue tagged area:security severity:critical.gitleaksignore only for known-safe false positives (e.g. a fixture that happens to look like a token).The ZAP scan runs automatically via .github/workflows/security-zap.yml:
pr-preview.yml
has deployed one. Skips silently if the preview is unreachable (backend-only
PRs, docs PRs). Posts a sticky comment on the PR with the outcome.https://raxx-app.pages.dev (Antlers staging) and
https://raxx-api-staging.herokuapp.com/ (API staging).app.raxx.app, raxx-console-prod, etc.).
Scanning prod can trigger WAF lockouts and creates synthetic load.Triage flow for ZAP findings:
| ZAP risk level | Workflow action | Human action |
|---|---|---|
| HIGH (riskcode 3) | zap_file_issues.py auto-files a GitHub issue with severity:high + area:security + needs-grooming |
sre-agent triages; routes to feature-developer for code fixes |
| MEDIUM (riskcode 2) | Workflow posts summary in PR comment / step summary | Review manually; file issue if exploitable in this threat model |
| LOW / INFO | Logged in artifact only | No action required; review during monthly digest |
Artifact retention: ZAP HTML + JSON reports are uploaded as run artifacts for 30 days. Download from the Actions run page.
False-positive suppression: Rule overrides live in .zap/rules.tsv. Each
IGNORE entry requires a justification comment. Add new ignores via PR — do not
disable the scan job.
Rule override rationale (see .zap/rules.tsv for full comments):
- 10202 IGNORE — CSRF tokens not applicable to stateless JSON API with header auth
- 10015 IGNORE — Cache-control on CF Pages CDN previews is CDN-controlled, not app-controllable
- 10016 IGNORE — X-XSS-Protection is deprecated in modern browsers; CSP is the correct defense
- 10035 IGNORE — HSTS belongs on origin server config, not on CF Pages-hosted SPA
Hardening follow-ups tracked under Epic #81.
Tune the ruleset, don't disable the job. Adjust bandit config, semgrep rules, or audit ignore lists with a short comment explaining why. The first iteration of CI will catch false positives — fixing the ruleset is the job.
Escalate to the parent epic (#81) as a blocker. Velocity ≠ value if we ship vulnerable code.