feat(agents): security-auditor SAST gate + onboard/audit-delta grafts (verify-loops lot 3)
agents/security-auditor.md: fresh read-only-on-code SAST gate. Pinned rulesets p/security-audit + p/secrets + p/owasp-top-ten (owasp REQUIRED — measured: the 2-ruleset baseline misses SQLi + path-traversal entirely on realistic Flask code), never --config auto, never auto login (BDR-048). Severity map: secrets ERROR → CRITICAL, other ERROR → HIGH (block), WARNING/INFO → reported. gate mode (diff, no Write) vs audit mode (Write only to REPORT, rule-locked). DEGRADED (semgrep absent) still runs the checklist and still blocks — never a vacuous pass (LRN-048). Anti-gaming: a new un-gated nosemgrep suppression is BLOCKING. PROOF mandatory, mute auditor never a PASS, blind (no iteration history), blocks HIGH/CRITICAL only (LRN-047). Grafts: onboard STEP 6 L3a dispatches it in audit mode (report .onboard-audit/semgrep.md) in BOTH gstack branches — complement to cso (cso is a gstack submodule, unmodifiable); synthesis picks it up via the existing .onboard-audit/ sweep. audit-delta security axis runs the SAST pass first, folds findings into the existing gate/fix/re-verify flow. lib/tests/security-auditor.test.sh: 28 structure locks green, shellcheck clean. Behavioral dogfood (fresh agents on a planted fixture): BLOCK(9) on the vuln commit (2 secrets→CRITICAL, semgrep+checklist complementarity — checklist caught the 6 semgrep missed off-context); BLOCK(1) on a new nosemgrep suppression (understood semgrep's 0 was the mask); DEGRADED → BLOCK(7) on grep-detectable secrets with semgrep hidden. FP measured on real repos (faunosteo, game): owasp adds only hygiene findings, contained by diff-scoping. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XpphkdTosUzokBDNG7PToS
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
ff13abfda5
commit
2b297bd44a
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env bash
|
||||
# ============================================================
|
||||
# Structure locks — security-auditor agent + grafts (lot 3)
|
||||
# Deterministic greps on load-bearing doctrine: an edit that
|
||||
# drops one (pinned rulesets, DEGRADED-still-checks, PROOF,
|
||||
# block-HIGH-only, anti-gaming, the two SKILL grafts) reds here.
|
||||
# ============================================================
|
||||
set -u
|
||||
|
||||
REPO="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
AGT="$REPO/agents/security-auditor.md"
|
||||
ONB="$REPO/skills/onboard/SKILL.md"
|
||||
ADL="$REPO/skills/audit-delta/SKILL.md"
|
||||
PASS=0; FAIL=0
|
||||
|
||||
tf() { # tf <label> <file> <fixed-string>
|
||||
if grep -qF -- "$3" "$2" 2>/dev/null; then
|
||||
echo " PASS $1"; PASS=$((PASS+1))
|
||||
else
|
||||
echo " FAIL $1 — missing: $3"; FAIL=$((FAIL+1))
|
||||
fi
|
||||
}
|
||||
tr_() { # tr_ <label> <file> <ERE>
|
||||
if grep -qE -- "$3" "$2" 2>/dev/null; then
|
||||
echo " PASS $1"; PASS=$((PASS+1))
|
||||
else
|
||||
echo " FAIL $1 — no match: $3"; FAIL=$((FAIL+1))
|
||||
fi
|
||||
}
|
||||
tn() { # tn <label> <file> <ERE> (must NOT match)
|
||||
if grep -qE -- "$3" "$2" 2>/dev/null; then
|
||||
echo " FAIL $1 — forbidden match: $3"; FAIL=$((FAIL+1))
|
||||
else
|
||||
echo " PASS $1"; PASS=$((PASS+1))
|
||||
fi
|
||||
}
|
||||
|
||||
echo "── security-auditor.md locks ──"
|
||||
if [ -f "$AGT" ]; then
|
||||
echo " PASS agent exists"; PASS=$((PASS+1))
|
||||
else
|
||||
echo " FAIL agent missing: $AGT"; FAIL=$((FAIL+1))
|
||||
fi
|
||||
tr_ "frontmatter name" "$AGT" "^name: security-auditor$"
|
||||
tr_ "tools incl Write (audit)" "$AGT" "^tools: Read, Grep, Glob, Bash, Write$"
|
||||
tf "verdict grammar" "$AGT" "SECURITY — VERDICT: PASS | BLOCK(n) | ERROR(<reason>)"
|
||||
tf "ruleset security-audit" "$AGT" "p/security-audit"
|
||||
tf "ruleset secrets" "$AGT" "p/secrets"
|
||||
tf "ruleset owasp required" "$AGT" "p/owasp-top-ten"
|
||||
tf "no config auto stated" "$AGT" "never \`--config auto\`"
|
||||
tf "no auto login" "$AGT" "never \`semgrep login\`"
|
||||
tf "secrets to CRITICAL" "$AGT" "p/secrets | CRITICAL"
|
||||
tf "block ERROR threshold only" "$AGT" "blocking threshold is ERROR"
|
||||
tf "medium low reported" "$AGT" "MEDIUM/LOW are REPORTED, never"
|
||||
tf "degraded still checks" "$AGT" "STILL RUN STEP 3"
|
||||
tf "degraded vacuous pass named" "$AGT" "vacuous pass"
|
||||
tf "anti-gaming suppression" "$AGT" "NEW suppression comment"
|
||||
tf "anti-gaming micro-gate" "$AGT" "[gated <date>]"
|
||||
tf "proof mandatory" "$AGT" "\`PROOF\` is MANDATORY"
|
||||
tf "mute never a pass" "$AGT" "NEVER a PASS"
|
||||
tf "write rule-locked audit" "$AGT" "writable path is \`REPORT\`"
|
||||
tf "gate mode write forbidden" "$AGT" "\`Write\` is FORBIDDEN in this mode"
|
||||
tf "blind no history" "$AGT" "NEVER receive iteration history"
|
||||
tf "reverify request first" "$AGT" "re-verify the REQUEST first"
|
||||
tf "max 3 security iters" "$AGT" "Max 3 security iterations"
|
||||
|
||||
echo "── onboard graft locks ──"
|
||||
tf "onboard dispatches auditor" "$ONB" "subagent_type=\"security-auditor\""
|
||||
tf "onboard report path" "$ONB" ".onboard-audit/semgrep.md"
|
||||
tf "onboard verify incl semgrep" "$ONB" "code-clean,cso,semgrep,doc"
|
||||
|
||||
echo "── audit-delta graft locks ──"
|
||||
tf "audit-delta dispatches" "$ADL" "subagent_type=\"security-auditor\""
|
||||
tf "audit-delta semgrep first" "$ADL" "FIRST run the semgrep SAST pass"
|
||||
|
||||
echo ""
|
||||
echo "security-auditor structure locks: $PASS pass, $FAIL fail"
|
||||
[ "$FAIL" -eq 0 ]
|
||||
Reference in New Issue
Block a user