Skip to content

Commit 6d5e72c

Browse files
committed
fix(loop): post-fix estate-rescan trigger + audited FP classifications
dispatch-runner.sh now collects every repo that received a successful fix and fires one estate-rescan repository_dispatch to hypatia at the end of the run (best-effort, token-guarded). This closes the loop stage that kept the numbers frozen: fixes landed but verisimdb-data scans were never refreshed, so hypatia kept dispatching against already-fixed findings. audits/assail-classifications.a2ml records the audited-FP residuals from a fresh panic-attack 2.5.5 scan: remediation scripts (scripts/fix-*.sh) that necessarily contain the patterns they fix, and cipherbot detector unit-test fixtures. With the registry in place, gitbot-fleet's unsuppressed non-heuristic Critical/High count is 0 (the stale store showed 6 Critical / 2 High, all phantom or FP).
1 parent 641f321 commit 6d5e72c

2 files changed

Lines changed: 106 additions & 0 deletions

File tree

audits/assail-classifications.a2ml

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
;; SPDX-License-Identifier: MPL-2.0
2+
;; assail-classifications.a2ml — audited panic-attack findings for gitbot-fleet.
3+
;;
4+
;; Read by panic-attack >= 2.5.5 (load_user_classifications): findings
5+
;; matching (file, category) flip to suppressed = true after the kanren
6+
;; structural pass. Entries are AUDITED-SOUND residuals with rationale;
7+
;; the registry lives apart from the source so a code edit cannot
8+
;; self-suppress (panic-attack CLAUDE.md, User-Classification Registry).
9+
;;
10+
;; Audit trail: 2026-06-11 estate-loop session — fresh panic-attack
11+
;; 2.5.5 scan; every entry inspected at source. Two FP classes:
12+
;;
13+
;; 1. Remediation scripts (scripts/fix-*.sh) necessarily CONTAIN the
14+
;; patterns they search for and replace — the same reason these
15+
;; scripts carry `hypatia: allow` pragmas for hypatia's scanner.
16+
;; 2. Detector unit tests (cipherbot analyzers) assert detection by
17+
;; feeding secret-shaped fixture literals to the analyzer inside
18+
;; `#[cfg(test)] mod tests`.
19+
20+
(assail-classifications
21+
;; ── Remediation scripts: pattern strings, not vulnerabilities ────
22+
(classification
23+
(file "scripts/fix-dynamic-code-exec.sh")
24+
(category "CommandInjection")
25+
(audit "remediation tool for PA011 — greps for and rewrites eval/exec patterns")
26+
(rationale "contains eval as a SEARCH pattern; does not evaluate untrusted input"))
27+
(classification
28+
(file "scripts/fix-eval-to-safe.sh")
29+
(category "CommandInjection")
30+
(audit "remediation tool — replaces eval usage with safe alternatives")
31+
(rationale "contains eval as a SEARCH/REPLACE pattern; does not evaluate untrusted input"))
32+
(classification
33+
(file "scripts/fix-heredoc-install.sh")
34+
(category "CommandInjection")
35+
(audit "remediation tool for PA009-shell-heredoc-eval; carries hypatia allow pragma at line 3")
36+
(rationale "contains curl|bash / eval $() as SEARCH patterns; does not itself download-then-run"))
37+
(classification
38+
(file "scripts/fix-command-injection.sh")
39+
(category "CommandInjection")
40+
(audit "remediation tool for PA009 — quotes unquoted expansions")
41+
(rationale "contains injection-shaped strings as SEARCH patterns only"))
42+
(classification
43+
(file "scripts/fix-secret-to-env.sh")
44+
(category "HardcodedSecret")
45+
(audit "remediation tool for PA017 — moves literals into env vars")
46+
(rationale "contains secret-shaped strings as SEARCH patterns only"))
47+
(classification
48+
(file "scripts/fix-hardcoded-secrets.sh")
49+
(category "HardcodedSecret")
50+
(audit "remediation tool for PA017")
51+
(rationale "contains secret-shaped strings as SEARCH patterns only"))
52+
(classification
53+
(file "scripts/fix-deno-permissions.sh")
54+
(category "ExcessivePermissions")
55+
(audit "remediation tool — replaces `deno -A` with scoped permission flags")
56+
(rationale "contains -A as the SEARCH pattern it removes from other repos"))
57+
58+
;; ── Detector tests: fixtures asserting detection works ───────────
59+
(classification
60+
(file "bots/cipherbot/src/analyzers/config.rs")
61+
(category "HardcodedSecret")
62+
(audit "config.rs:145+ `mod tests` — test_detect_hardcoded_secret feeds sk-1234… fixture to the analyzer")
63+
(rationale "test fixture proving the secret detector detects; not a credential"))
64+
(classification
65+
(file "bots/cipherbot/src/analyzers/infra.rs")
66+
(category "HardcodedSecret")
67+
(audit "infra.rs:174+ `mod tests` — fixture password literal fed to the analyzer")
68+
(rationale "test fixture proving the secret detector detects; not a credential"))
69+
)

scripts/dispatch-runner.sh

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,13 @@ OUTCOME_FILE="${HYPATIA_DATA}/outcomes/$(date -u +%Y-%m).jsonl"
135135
OUTCOME_FILE_CENTRAL="${VERISIMDB_DATA}/outcomes/$(date -u +%Y-%m).jsonl"
136136
mkdir -p "$(dirname "$OUTCOME_FILE")"
137137

138+
# Repos that received at least one successful fix this run. Appended by
139+
# record_outcome (file, not a variable: execute_entry may run in
140+
# background subshells under --parallel) and consumed by the post-run
141+
# estate-rescan trigger so verisimdb-data reflects the fixes without
142+
# waiting for the nightly sweep.
143+
RESCAN_REPOS_FILE="$(mktemp /tmp/dispatch-rescan-repos.XXXXXX)"
144+
138145
# --- Hypatia closed-loop contract ---
139146
#
140147
# Per hypatia/docs/operations/dispatch-runner-contract.adoc, after each
@@ -255,6 +262,7 @@ record_outcome() {
255262
case "$outcome" in
256263
success)
257264
hypatia_outcome="success"
265+
echo "$repo" >> "$RESCAN_REPOS_FILE"
258266
;;
259267
failure)
260268
hypatia_outcome="failure"
@@ -585,6 +593,35 @@ if [[ "$DRY_RUN" == "true" ]]; then
585593
echo "(Dry run — no changes were made)"
586594
fi
587595

596+
# --- Trigger targeted estate rescan for fixed repos ---
597+
# Fire a single repository_dispatch to hypatia's estate-rescan workflow
598+
# listing every repo that received a successful fix this run. This is
599+
# the loop-closure step: the rescan refreshes verisimdb-data/scans, the
600+
# next pattern sync marks the fixed patterns resolved, and the numbers
601+
# actually drop. Best-effort: a missing token or API failure must never
602+
# fail the dispatch run itself.
603+
RESCAN_TOKEN="${FLEET_DISPATCH_TOKEN:-${GITHUB_TOKEN:-}}"
604+
if [[ "$DRY_RUN" != "true" && -s "$RESCAN_REPOS_FILE" && -n "$RESCAN_TOKEN" ]]; then
605+
RESCAN_REPOS=$(sort -u "$RESCAN_REPOS_FILE" | tr '\n' ' ' | sed 's/ $//')
606+
RESCAN_PAYLOAD=$(jq -n --arg repos "$RESCAN_REPOS" \
607+
'{event_type: "estate-rescan", client_payload: {repos: $repos}}')
608+
if curl -sf -X POST \
609+
-H "Authorization: Bearer ${RESCAN_TOKEN}" \
610+
-H "Accept: application/vnd.github+json" \
611+
"https://api.github.com/repos/hyperpolymath/hypatia/dispatches" \
612+
-d "$RESCAN_PAYLOAD" >/dev/null; then
613+
echo ""
614+
echo "Estate rescan requested for: $RESCAN_REPOS"
615+
else
616+
echo ""
617+
echo "WARN: estate-rescan dispatch failed (non-fatal); nightly sweep will catch up" >&2
618+
fi
619+
elif [[ -s "$RESCAN_REPOS_FILE" && -z "$RESCAN_TOKEN" ]]; then
620+
echo ""
621+
echo "WARN: fixes landed but no FLEET_DISPATCH_TOKEN/GITHUB_TOKEN set — skipping targeted rescan (nightly sweep will catch up)" >&2
622+
fi
623+
rm -f "$RESCAN_REPOS_FILE" 2>/dev/null || true
624+
588625
# --- Kin Protocol: write heartbeat ---
589626
KIN_DIR="${HOME}/.hypatia/kin"
590627
mkdir -p "$KIN_DIR"

0 commit comments

Comments
 (0)