Commit graph

16 commits

Author SHA1 Message Date
28553d86f7 feat(secrev): compliance-drift Plane-1 Tier-1 checker (ALARM-only)
Read-only org compliance checker on the shared substrate (no re-clone; scans existing mirrors). Checklist grounded in handbook/github-standards: kebab repo name, README, CI/CD, Dependabot config+alerts, tracked-.env secrets, branch protection, merge settings; handbook exceptions (docs-only, compliance-exempt) honored. Mode-600 reports, ALARM-only (clean=silent). Includes planted-drift canary (asserts 6). Review fixes folded in: branch-protection + dependabot are status-code-aware (only a real 404 is drift; transient API failure -> skip, no false alarm); secrets-committed fires only on secret-shaped values (not benign config). NOT scheduled (provisioning gated).
2026-06-18 14:06:31 -04:00
1a1facd945 refactor(secrev): factor shared sweep substrate out of nightly_sweep.sh (Plane-1 Phase 0)
Extract discovery/mirror/budget-ledger/rotation/Slack-ALARM(redaction)/canary into security-review/lib/sweep_substrate.sh (sourceable, bash, stdlib only); nightly_sweep.sh (415->362) now sources it. Zero behavior change proven: shellcheck -x clean, offline two-tier dry-run byte-identical before/after, token discipline (read-only PAT, REST-only, no gh CLI, origin scrubbed) preserved, review.sh untouched. Revert point: 73e35f3. Foundation both planes' scheduled side reuses.
2026-06-18 14:06:31 -04:00
b0d8b842e5 security-review: surgically exclude only cdk.out asset bundles from checkov (A2)
The prior exclusion (--skip-path cdk.out) stopped the CDK-repo stall but also
silenced checkov on cdk.out/<stack>.template.json — the actual deploy artifact —
losing real S3/IAM IaC coverage (CKV_AWS_53-56, CKV_AWS_111). Switch to skipping
only the cdk.out asset.<hash>/ dependency bundles (the stall cause) so the
synthesized templates are still scanned.

- --skip-path 'cdk\.out/asset\.' anchors to cdk.out so a source file literally
  named asset.* is not also excluded; keeps cdk.out/*.template.json scanned.
- venv/dist/build kept as bare names (match anywhere); .venv/.aws-sam escaped.

cfn-lint + semgrep already prune these trees (prior commit). gitleaks runs in
git-mode and respects .gitignore, so cdk.out is already skipped there.

Verified: shellcheck clean; synthetic cdk.out test confirms the stack template is
scanned while asset.* is skipped; the orchestrator's own --scanners-only gate
still exits 0 with suppressions (inert on non-CDK repos: A1==A2 findings here).
2026-06-17 17:06:05 -04:00
c6ecc621be Prune generated/vendored trees from the scanners
cfn-lint, semgrep, and checkov were scanning synthesized/vendored output
(cdk.out, node_modules, .venv/venv, .aws-sam, dist, build). On CDK repos this
explodes the find/xargs arg list and stalls the scan, and flagging synthesized
templates is wrong. Prune those trees in the cfn-lint find, and pass
--exclude / --skip-path to semgrep / checkov.
2026-06-17 14:55:41 -04:00
dbe2f8c186 Raise nightly agentic budget $20 -> $120 for full per-night coverage
First-run data (2026-06-17) showed the $20 ceiling covered only the
canary + 5 of ~22 scannable repos before pausing the rotation, leaving
16 repos un-deep-scanned that night. Raise TOTAL_BUDGET_USD default to
$120 so every repo gets a deep agentic pass each night (~22 x ~$5 +
canary, with headroom). Spend draws on the Max subscription pool; the
per-target cap ($12) and round-robin rotation are unchanged, so this is
a ceiling raise, not a per-repo cost change. Updates README, DEPLOY, and
the systemd Environment example to match.
2026-06-17 13:18:46 -04:00
15a16f8b88 Tune nightly defaults from first-run data
First full VM run: canary recall 18 with the expanded Node/.NET corpus, and a real
agentic repo cost ~$3.50 (vs the $1.4 testbed). Raise CANARY_FLOOR 10->14 (catches a
language-blindness recall collapse to ~9 while keeping margin under 18) and MAX_CYCLE_NIGHTS
4->6 (at ~4-6 agentic repos/night a full 22-repo rotation takes ~4-5 nights; 4 would
false-fire the coverage alarm). Both stay env-overridable and re-tunable as data accrues.
2026-06-16 18:02:57 -04:00
f4bb2bce8a Make scan_scanners return 0 so a passing repo doesn't trip set -e
scan_scanners communicates results via globals (T1_BLOCK/T1_CRIT/T1_HIGH); its last
statement was a bare [ $rc -eq 1 ] && T1_BLOCK=1. On a PASS (rc=0) that test is false,
so the function returned non-zero and set -e killed the whole sweep at the first passing
repo in tier 1 (right after the unbound-variable fix let it get that far). Add an explicit
return 0. Audited the rest of the tier1/tier2/summary path; scan_agentic and the others
already end on a zero-status command.
2026-06-16 16:43:35 -04:00
6d65c54b58 Fix unbound-variable crash in the nightly sweep tier-1 loop
scan_scanners referenced ${slug} inside the SAME local statement that defines it
(local target=... slug=$2 result_json=...${slug}...). bash expands the local's
arguments before the builtin assigns them, so under set -u ${slug} is unbound and
the sweep died right after the canary, before tier 1 ever ran. Split the local so
slug exists first. Fix the same latent self-reference in mirror_repo (dir=...$name),
which only worked by accident because the discovery loop left a global $name.
2026-06-16 16:00:21 -04:00
c217c5656d Add machine-level suppressions to the repo-sourced hooks
The live global pre-push hook was hand-edited to resolve suppressions from a
machine-level file (${SH_SECURITY_SUPPRESSIONS_DIR:-~/.config/sea-haven/security-review}/
<repo-basename>/suppressions.json) kept out of repo history, falling back to a
repo-local .security-review/suppressions.json. The repo-sourced hooks lacked it, so
install-hooks.sh --global would overwrite the live hook and lose the feature.

Port the prefer-machine/fallback-repo-local block into hooks/pre-push, align
hooks/pre-commit to the same (else a machine-suppressed finding passes at push but
blocks at commit), and document the path + SH_SECURITY_SUPPRESSIONS_DIR override +
basename-collision caveat in the README.
2026-06-16 15:33:43 -04:00
f4dd72ced9 Add headless detector fan-out + proof-or-kill verifier runner
run_headless.py is the Path B engine the nightly sweep calls: the 6 fresh-context
detectors + proof-or-kill verifier from /sh-security-review, run unattended via the
Claude Agent SDK on subscription OAuth (pops ANTHROPIC_API_KEY so the API key can't
silently win). Read-only tools, hermetic, per-call + total budget caps, fails toward
over-reporting. Emits the finding schema that review.sh --agent-findings consumes.
2026-06-16 15:00:10 -04:00
a05afb5ebc Update security-review docs for two-tier auto-discovery
Rewrite README + DEPLOY-R720 for the clean-clone mirror model, the read-only GH_TOKEN
PAT recipe, the global hook installer, and the no-CI-by-design decision.
2026-06-16 15:00:00 -04:00
537e83975b Rewrite nightly sweep as two-tier clean-clone auto-discovery
Replace the opt-in sweep-targets allowlist with zero-wiring discovery: enumerate org
repos via the GitHub REST API (curl + read-only GH_TOKEN, no gh dependency) and mirror
each as a shallow clean clone (git clone --depth=1, default branch from the API) into
~/repo-mirrors. Scanning server-side clones keeps local .env secrets out of scope.

Tier 1 runs deterministic scanners over every repo nightly ($0 Claude); tier 2 runs the
agentic pass over a budget-bounded round-robin rotation with a persistent cycle pointer,
so the draw on the shared Max limits stays bounded and coverage never goes silently
incomplete (COVERAGE ALARM if the rotation falls behind). Skip = committed marker or
central list (marker-skips logged). Redact secrets from Slack; reports mode 600. Raise
the systemd timeout to 6h for the longer two-tier run.
2026-06-16 15:00:00 -04:00
a3ab3f5f40 Make security-review hooks and skill installable from the repo
The global pre-push hook, the /sh-security-review prompt, and finding.schema.json
previously lived only in ~/.config/git and ~/.claude (untracked) — unreproducible.
Source them here: add hooks/pre-push, rewrite install-hooks.sh with a --global mode
(lays down both hooks, sets core.hooksPath, links skill+schema into ~/.claude) and a
per-repo mode. Align pre-commit with pre-push (honor skip marker + suppressions). Add
semgrep p/javascript so the scanners cover the org's Node/.NET repos.
2026-06-16 15:00:00 -04:00
18412c7482 Remove parked security-review CI drafts
CI was swapped for the global git hooks + nightly VM sweep (solo dev), so
the parked ci/*.yml and CI-BACKSTOP-NOTES.md were dead weight — a defective
workflow in-tree is a foot-gun. Recover from history if the team grows.
2026-06-16 14:59:42 -04:00
f90f759e12 Tune checkov severity, wire npm audit, add CI backstop + R720 runbook
checkov: high-signal exposure/access checks -> high, best-practice noise -> low
(was 51 undifferentiated mediums). npm audit wired for Node dep CVEs. Report
collapses the low/info tail to a count. Adds ci/security-review.yml (PR backstop)
and DEPLOY-R720.md (Phase 3 host runbook).
2026-06-15 15:57:34 -04:00
7e5ce1f5b2 Add security-review gate (review.sh + scanners + pre-commit hook)
Trigger-agnostic pure-code gate that merges deterministic-scanner findings
(semgrep/gitleaks/checkov/cfn-lint/pip-audit) with agent findings from
/sh-security-review, dedups, applies justification-required suppressions, and
makes the block decision (exit 1 on confirmed critical/high). Phase 2 of the
Sea Haven security-review agent; Path B (CI/headless) wiring lands in Phase 3.
2026-06-15 15:52:15 -04:00