Skip to content

Commit e482b2b

Browse files
Merge pull request #39 from elementalsouls/harden-recon
Engine: map-first deterministic recon + skill-arsenal categorization
2 parents 987b22d + bf01b73 commit e482b2b

7 files changed

Lines changed: 1227 additions & 112 deletions

File tree

engine/README.md

Lines changed: 63 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,80 @@
1-
# Engagement engine (v0)
1+
# Engagement engine
22

3-
The eval harness proved the gap to "automate 80% of the grind" isn't the *skills*
4-
(the base model already exploits standard classes) — it's an **autonomous engagement
5-
system**: scope-safe, stateful, multi-step, false-positive-disciplined. This is that
6-
system's v0 skeleton.
3+
A **decision-support engine**, not an autonomous robot. It maps a target's attack surface
4+
deterministically, **categorizes it against the skill arsenal** (which `hunt-*` skill applies
5+
where), and **shows you each stage live** — so you spend your expert 20% where the 80%
6+
automation points. It does **not** try to test everything itself; active hunting is opt-in.
77

8-
## Design principle
9-
**Control flow is deterministic code; only hunting/verifying is the LLM.** Scope,
10-
state, dispatch, ranking, dedup, and reporting are Python — so a long unattended run
11-
is *safe* (can't go out of scope), *auditable* (everything on disk), and *resumable*.
8+
## Design principles
9+
1. **Deterministic-first.** Breadth (recon, parameter discovery, secret scanning, service
10+
fingerprint, categorization) is plain Python + passive tooling — **`$0`, no agents, no
11+
rate-limit burn**. The LLM is reserved for the few judgment calls in the opt-in hunt.
12+
2. **Map-first, hunting opt-in.** A default run stops at the **arsenal map** and hands off to
13+
you. Spend agents only when you choose (`--hunt`).
14+
3. **Show every stage.** Each phase logs what it found, live, so you always know where to focus.
15+
4. **Curl-first.** Active testing uses `curl`; Burp MCP is optional (OOB/blind/fuzzing only).
16+
5. **Safe by construction.** Scope is a deterministic allowlist; agents run **read-only by
17+
default** with hard rules-of-engagement; a deterministic scope-audit flags any out-of-scope PoC.
1218

1319
```
14-
scope ─▶ recon ─▶ rank ─▶ hunt ─▶ validate ─▶ report
15-
│ │
16-
└ discoveries filtered to in-scope hosts
17-
└ deterministic allowlist; no agent is ever dispatched at an out-of-scope target
18-
│ └ adversarial verifier rejects false positives
19-
└ one focused hunt agent per ranked (url, param, class)
20+
DEFAULT (deterministic, $0, no agents) │ OPT-IN (--hunt)
21+
recon ───▶ rank ───▶ map ──────────────────────────────▶hunt ─▶ validate ─▶ report
22+
│ └ surface → skill arsenal + │ │ └ adversarial verifier (read-only)
23+
│ │ curl-first probes (arsenal.md) │ └ curl-first agent per ranked (url,param,class), skills-off
24+
└ class-weight priority (secrets boosted)
25+
└ service/tech + JS endpoints + JS secrets + ALL params (gau + katana), categorized
2026
```
27+
> **OSINT (subdomain/asset enum) is a *separate* concern** — handled by Claude-OSINT, not the
28+
> engine. The engine takes the in-scope seed target(s) and goes straight to deterministic recon.
2129
2230
| Piece | What it is |
2331
|---|---|
2432
| `scope.py` | deterministic allowlist (apex/wildcard/CIDR/regex; deny-wins; default-deny). Enforced at recon **and** hunt. |
25-
| `state.py` | persistent, resumable engagement store (`state.json` + `evidence/` + `engine.log` + `report.md`). |
26-
| `agent.py` | headless `claude -p` dispatch (skills + Burp MCP) + JSON extraction. |
27-
| `engine.py` | the orchestrator: phases, scope enforcement, ranking, candidate→confirm flow, report. |
33+
| `recon.py` | **deterministic recon** — per target: service/tech (JS markers), JS-bundle endpoint mining, **JS secret scanning** (AWS/GCP/Anthropic/OpenAI/Slack/GitHub/Stripe/EmailJS… keys, redacted), and **all input parameters** from two independent sources — `gau` (passive historical) + `katana` (active live crawl). Noise-filtered, scope-filtered, multi-class categorized. No LLM in the find-step. |
34+
| `skill_map.py` | **arsenal categorization** — maps each `(endpoint\|param, class)` → the specific `hunt-*` skill(s) **actually installed** in `~/.claude/skills`, plus a curl-first starter probe. Tech-stack skills (`hunt-nextjs`, `hunt-nodejs`…) mapped from the fingerprint. |
35+
| `osint.py` | **separate/optional** — per-target service/tech probe (PD `httpx-toolkit`) reused by recon. Its subdomain-enum path is *not* in the engine flow (that's Claude-OSINT). |
36+
| `state.py` | persistent, resumable engagement store (`state.json` + `evidence/` + `engine.log` + `arsenal.md` + `report.md`). |
37+
| `agent.py` | headless `claude -p` dispatch + JSON extraction. **Skills OFF by default** (eval: ~0 capability gain, saves ~12–15k tokens/agent). Used only for the opt-in hunt/validate. |
38+
| `engine.py` | the orchestrator: phases, scope enforcement, ranking, the map, parallel hunt/validate, candidate→confirm, report. |
39+
40+
## The map (the default deliverable)
41+
`map` writes `arsenal.md` and logs every target live — for each endpoint/parameter: the attack
42+
classes, the exact `hunt-*` skill(s) to open, and a ready-to-run curl. Example line:
43+
```
44+
map: redirect_to https://t/wp-login.php?redirect_to=FUZZ ssrf→hunt-ssrf lfi→hunt-lfi open-redirect→hunt-open-redirect
45+
```
2846

2947
## Run
3048
```bash
31-
cp engine/burp-mcp.json.example engine/burp-mcp.json # set your mcp-proxy jar path
49+
# DEFAULT — deterministic map only ($0, no agents). Stops for you with arsenal.md:
50+
python3 engine/engine.py --scope my-engagement.json
51+
# scope file = {name, in_scope, out_of_scope, seeds}; output in ~/.bughunter-engagements/<name>/
3252

33-
# dry-run the whole flow with canned agent output (no agents, no budget) — proves the wiring:
34-
python3 engine/engine.py --scope engine/engagement.example.json --base /tmp/eng --mock
53+
# OPT-IN — auto-test the mapped surface with agents (read-only, curl-first, parallel):
54+
cp engine/burp-mcp.json.example engine/burp-mcp.json # only if you want Burp for OOB/blind
55+
python3 engine/engine.py --scope my-engagement.json --hunt --parallel 3 --max-hunts 12
56+
python3 engine/engine.py --scope my-engagement.json --hunt --allow-intrusive # permit state-changing PoCs (off by default)
3557

36-
# live (needs Burp running + claude budget); scope file = {name, in_scope, out_of_scope, seeds}:
37-
python3 engine/engine.py --scope my-engagement.json --max-hunts 8
38-
python3 engine/engine.py --scope my-engagement.json --phases hunt,validate,report # resume later phases
58+
# dry-run the whole wiring with canned output (no agents, no budget):
59+
python3 engine/engine.py --scope engine/engagement.example.json --base /tmp/eng --mock --hunt
60+
61+
# standalone recon (deterministic, no engine state):
62+
python3 engine/recon.py https://target/ target.com
3963
```
40-
Engagement state + report land in `~/.bughunter-engagements/<name>/` (outside the repo).
41-
42-
## Honest status (v0)
43-
- **Deterministic backbone: built, unit-tested, and mock-validated end-to-end** (scope-drop of
44-
out-of-scope hosts, rank, candidate→confirm, FP-rejection at validate, report, resume).
45-
- **Live agent phases: wired but not yet run** — blocked on Burp + claude budget (rate-limited at
46-
build time). The same `claude -p` + Burp MCP path is already proven by the eval harness.
47-
- **v0 limitations (the road ahead):** recon is single-seed and agent-driven (no subdomain enum /
48-
multi-host sweep yet); no chaining/escalation between findings; ranking is a static class-weight
49-
heuristic; reporting is a deterministic template (not yet the report skills). These are the next
50-
increments toward a system that does a real multi-host engagement unattended.
64+
Key flags: `--hunt` (opt into agents) · `--allow-intrusive` (default OFF = read-only) ·
65+
`--parallel N` (concurrent agents, default 3) · `--phases a,b,c` (explicit override).
66+
67+
## Safety
68+
- **Read-only by default** — hunt/validate agents are told: in-scope hosts only (never
69+
third-party, even to prove a finding), and no state-changing actions (no emails/writes/
70+
deletes/cache-purge, don't exercise exposed creds). `--allow-intrusive` lifts this.
71+
- **Deterministic scope-audit** — every confirmed finding is checked for out-of-scope hosts in
72+
its PoC; any hit is flagged (⚠) in the report for review.
73+
- **Calibrated severity** — the adversarial validator's severity (not the hunt's raw guess) is
74+
what's stored.
5175

5276
## Why this and not more skills
53-
The measured result (`eval/`): skills add ~0 capability on benchmarkable tasks because the model
54-
already has them. The leverage is here — turning that raw capability into a *safe, stateful,
55-
self-verifying* engagement loop. This is the part that doesn't exist for free in the base model.
77+
The measured result (`eval/`): skills add ~0 capability on benchmarkable tasks — the base model
78+
already exploits standard classes, and skills cost ~12–15k tokens/agent to load. The leverage is
79+
here: turning that raw capability into a **safe, deterministic, skill-routing, operator-in-the-
80+
loop** engine. Breadth is free and visible; you bring the judgment.

engine/agent.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
])
2222

2323

24-
def run_agent(task, skills_on=True, model="claude-sonnet-4-6", max_turns=40, timeout=600):
24+
def run_agent(task, skills_on=False, model="claude-sonnet-4-6", max_turns=40, timeout=600):
25+
# skills OFF by default: the eval showed they add ~0 capability but cost ~12-15k tokens/agent.
2526
cmd = ["claude", "-p", task,
2627
"--mcp-config", MCP_CONFIG, "--strict-mcp-config",
2728
"--permission-mode", "bypassPermissions",

0 commit comments

Comments
 (0)