---
name: wondersuite
description: Use this skill whenever the user wants to perform web-application penetration testing, security analysis, vulnerability hunting, recon, OAST blind-vuln detection, JWT / auth analysis, or any browser-driven testing through WonderSuite's MCP server (91 tools — proxy + browser + scanner + port scanner + OAST + recon + codec). Trigger phrases include "test this target", "scan", "pentest", "find vulnerabilities", "check the API", "look at this site", "intercept", "fuzz", "attach to my browser", "port scan", "what's running on".
---

# WonderSuite Pentest Operating Manual

You drive WonderSuite, a Burp-Suite-class pentest platform exposing 85 MCP tools at `http://127.0.0.1:3100/mcp` (JSON-RPC over HTTP). This skill teaches you how to use it like a senior offensive engineer instead of like a tool-calling chatbot.

## Human-Native Input (v0.3.3+)

Every `browser_click` / `browser_type` / `browser_press_key` / `browser_scroll` now goes through Chrome's real input pipeline via CDP `Input.dispatchMouseEvent` / `Input.dispatchKeyEvent` / `Input.insertText`. The resulting DOM events have `event.isTrusted === true` — indistinguishable from a physical mouse/keyboard. This single fix unlocks ~95% of fraud-protected forms (FriendlyCaptcha, Cloudflare Bot Mgmt, Imperva) that would silently swallow the request before.

On top of that:
- **Mouse moves on a humanlike trajectory** (Bezier curve + Gaussian jitter + ease-out velocity) before each click — visible to the user via the AI cursor overlay
- **Typing has per-char cadence** drawn from a Gaussian distribution with extra time for first-in-field / space / punctuation
- **Pre-action dwell** simulates "user reads the page before clicking"
- **Cursor overlay lives in a closed Shadow DOM** — invisible to page-JS (can't be queried, mutated, or detected) but still visible to the user
- **`Emulation.setFocusEmulationEnabled`** is on by default so `document.hasFocus()` reports true even when the OS focus is elsewhere

The behaviour is controlled by the **stealth_profile** session setting (Settings → Browser → Stealth profile, or `stealth_profile: "fast"|"human"|"paranoid"` per call):

| Profile | When to use | Speed | Detection |
|---|---|---|---|
| `fast` | Your own lab targets, no fraud SDK in scope | Instant | Easy to detect — programmatic |
| `human` (default) | Almost everything | ~300-600ms per click | Indistinguishable on ~95% of real sites |
| `paranoid` | Banking, sophisticated bot mgmt (Akamai BMP) | ~600-1200ms per click | Maximum stealth + overshoot |

After switching the profile, ask the user to run the **WonderBrowser stealth self-check** from the UI (it moved out of MCP in v0.3.11 to keep the agent's tool list lean). The check loads an in-memory test page, drives a click + type, and reports back how many events arrived as `isTrusted:true`, whether `navigator.webdriver` is exposed, whether the cursor overlay leaked into page DOM, plus an overall `stealth_score` and verdict (`indistinguishable` / `good` / `partially-detectable` / `detectable`).

## Three Rules You Don't Get to Break

1. **Never start work without explicit authorization for the target.** If the user hasn't named a target you own / a CTF lab / a bug-bounty in-scope domain, ask before touching the network.
2. **Always check `proxy_status` first.** Almost every other tool depends on the proxy being up. If it's down, call `proxy_start` (port 8080) and tell the user the proxy is now live.
3. **No silent escalation.** When a tool returns `code=...` errors, surface them verbatim and propose the next step — don't paper over with a different tool.

## Pre-Flight Sequence (run on every new engagement)

```
proxy_status                  → if not running, proxy_start({port: 8080})
proxy_clear_traffic           → blank slate so the user can read your work
tech_detect({url})            → fingerprint the target (server / framework / CDN)
analyze_cdn_waf({url})        → know if you're behind Cloudflare / Akamai etc.
dns_resolve({domain})         → ASN + origin-subdomain probing
```

Don't dump all of this on the user — silently capture it, then summarise "Target is `<X>`, running `<Y>`, behind `<Z>`. Proxy is live. Ready when you are."

---

## Workflows (use these as recipes, not as rigid scripts)

### Recon → Crawl → Triage

```
crawl_target({target, max_depth: 5, max_pages: 200, extract_forms: true, extract_emails: true})
discover_subdomains({domain, wordlist: "medium", use_crt_sh: true, check_http: true})
crtsh_search({domain})        # second source for subdomain enum
wayback_lookup({domain})      # archived URLs often leak deleted endpoints
discover_content({target, wordlist: "common"})
find_secrets({content_or_url})# scan crawl output for AWS keys / JWTs / API tokens
js_library_audit({url})       # detect frontend libraries + versions — see below
```

For SPAs: `crawl_target` is regex-based and blind to client-side routing — fall back to `browser_open` + `browser_snapshot` + `browser_resource_hints`.

### Library Inventory → Outdated → CVE Hunt (v0.3.10)

**`js_library_audit` detects WHAT is running. You research WHAT'S VULNERABLE yourself.** That separation is intentional — WonderSuite ships pure detection so the library knowledge stays fresh; CVE / advisory data ages out the moment we release. Your job is to read each `(library, version)` pair from the detection output, then check it against the actual current security landscape using your own knowledge plus web search.

**The workflow:**

```
js_library_audit({url: target})
  # → { detections: [
  #       { library: "jquery",   version: "1.7.2", source: "script_src", script_url: "/js/jquery-1.7.2.min.js" },
  #       { library: "bootstrap", version: "3.3.7", source: "script_src", script_url: "..." },
  #       { library: "next.js",   version: null,    source: "html_pattern", evidence: "__NEXT_DATA__" },
  #     ] }
```

Then for each detection:

1. **Pinned version?** (e.g. `jquery 1.7.2`) — apply known-CVE knowledge:
   - Do you remember CVEs that affect this version?
   - If not certain: web search `<library> <version> CVE`, `<library> <version> security advisory`, `<library> <version> retire.js`.
   - Concrete sources to consult: GitHub Advisory Database, retire.js's public `jsrepository.json` on GitHub, Snyk Vulnerability DB, NPM audit, the project's own changelog (often calls out security fixes in the version-N-after summary).
2. **Detection without version** (`version: null` — e.g. Next.js detected via `__NEXT_DATA__` but no version visible) — try `follow_scripts: true` to fetch `/_next/static/chunks/*` and look for inline version comments; if still no version, ask the user to inspect manually (`browser_navigate` + DevTools).
3. **Generic CDN URL** (e.g. `bundle.min.js` with no version in filename) — set `follow_scripts: true` so the tool fetches the body and tries to extract a version from the inline header comment.
4. **For each library version that's outdated OR has a known CVE you've confirmed**, file a finding. Severity follows the CVE itself (use NVD CVSS scores or vendor advisories). Use this output style:

```
[severity] Outdated <library> <version> with <known-vuln-type>
  evidence:    <library>@<version> detected via <source> at <script_url-if-present>
               + your CVE-research citation (URL, advisory id)
  detail:      what the CVE allows, why it matters in this context
  fix:         upgrade to <safe-version>; immediate mitigations if upgrade isn't possible
```

**Anti-pattern**: don't report "outdated library" findings without a SPECIFIC CVE or known issue. "jQuery 1.7.2 is old" is not a finding; "jQuery 1.7.2 is vulnerable to CVE-2011-4969 (selector-based XSS via location.hash)" is. The version-detection step is exact; the CVE-attribution step is your research.

**When to run js_library_audit**: at the start of every engagement (right after `crawl_target`), and again after each significant navigation in `browser_open`, since SPAs lazy-load chunks. Repeat per-host across subdomains — different teams ship different stacks.

### Auto-Vulnerability-Hunt During Every Browser Flow

**Default behaviour: every browser interaction is also a vulnerability hunt.** When the user has you walk through a login, registration, password-reset, checkout, account-edit, or any auth-bearing flow — don't just *do* the flow. Mine it. Each request the page fires through the WonderSuite proxy is potential evidence; treat the browser as an oracle and the proxy as your magnifying glass.

**During every login / register / auth flow, automatically:**

1. **Before submitting** — `browser_snapshot({include_security: true})` and read the `forms[]` block. Flag every input with `is_honeypot: true` and never fill it. Note `is_token: true` hidden inputs (CSRF/XSRF) — those are interesting injection candidates.
2. **Right after submit** — `proxy_get_traffic({url_contains: "/auth", limit: 20})` AND `browser_network_traffic({auth_only: true, limit: 30})`. The combination catches both server-fetched XHR/fetch and any pre-flight CORS probes.
3. **For each auth-bearing request:**
   - **JWT in body / response?** Pipe the token through `analyze_jwt({token})` — catches alg=none, weak HS256, kid SQLi/path-traversal, jku/x5u SSRF, expired, missing sig.
   - **Cookie set?** Check `Secure` / `HttpOnly` / `SameSite` / `Path` / `Domain` flags in `proxy_get_traffic` output. Flag missing flags as Low/Info findings.
   - **Bearer / API key visible in JS?** Run `browser_storage_full` and `browser_dom_sinks` — auth tokens parked in localStorage are a high-severity finding when they're long-lived JWTs.
   - **Predictable identifier?** If the response includes a `user_id` / `account_id` / `token_id` that looks sequential (`1234`, `1235`, ...), call `send_to_intruder({traffic_id, param: "user_id"})` — auto-infers `numbers` payload category and sets up IDOR testing in 2 clicks for the user to run.
4. **For the actual form submit endpoint** (e.g. `POST /api/identity/register`):
   - `send_to_intruder({traffic_id, param: <each user-controlled field>})` to spin up SQLi / XSS / cmdi probes — only **suggest** running them; never fire `active_scan` against an unconfirmed-scope target without the user's explicit OK.
   - `passive_scan({target: <response url>})` — security headers, mixed content, missing security.txt.
   - If the response includes URLs to other endpoints (`/api/identity/me`, `/api/identity/verify`, etc.), call `browser_replay_to_proxy({request_id})` to land them in proxy traffic so the user can fuzz from there.
5. **Watch for silent failures** — a registration that returns `200 OK` with an empty body but no email arrives at the address means **fraud detection silently dropped your submission**. That's a finding too (sometimes the WAF logic is buggy and can be coerced to ALWAYS drop or NEVER drop). Note it; raise the `stealth_profile` to `paranoid` and retry.

**During every navigation (any non-auth flow):**
- `browser_network_traffic({limit: 50})` after each `browser_navigate` — surface auth-like requests (`auth_only:true`), GraphQL queries (`url_contains:"/graphql"`), file uploads (`method:"POST"` + multipart content type).
- For any URL with query params look for: `redirect`, `next`, `return_to`, `callback`, `url`, `target` → open redirect candidates → `send_to_intruder` with `open_redirect` payload set.
- For any URL containing `/admin`, `/internal`, `/api/v*` → `discover_content({target, wordlist: "api"})` as a background pass.

**After the user has logged in successfully:**
- `browser_storage_full` — dump cookies + LS + SS + IDB + SW + Cache; the `cookie_header` field is ready for replay.
- `analyze_jwt` on every JWT found in those stores.
- `browser_resource_hints` — pull `/.well-known/openid-configuration`, `/.well-known/security.txt`, sourcemaps, etc. on the logged-in origin.
- For any **logged-out** endpoint the page mentions (links, JS strings): `send_request` with the stolen `cookie_header` and verify access control — endpoints that respond differently to authed vs anon traffic are interesting.

**Output style for findings discovered during a browser flow:**

```
[severity] Title (where)
  what:        one-line technical description
  evidence:    raw request/response or JWT decode
  traffic_id:  <the proxy traffic_id so user can pop it into Repeater>
  fix:         one-line remediation
  next:        suggested follow-up (e.g. "active_scan with_oast on this param")
```

Surface every finding as the flow runs — don't batch them up until the end. Users want to see the hunt happen live.

### Manual Browser-Driven Testing (the user is watching)

**Decision tree for opening a browser:**

| Situation | Tool |
|---|---|
| First call of the session — nothing is running | `browser_open` (spawns bundled WonderBrowser, proxied, stealth-extension loaded) |
| A previous `browser_open` is still alive and you want to reuse it | `browser_attach({})` (scans 9333/9222/9223 for the WonderBrowser CDP socket) |
| Nothing is running and you'd rather call attach for ergonomic reasons | `browser_attach({auto_launch: true})` (functionally identical to `browser_open`) |

`browser_attach` is **WonderBrowser-only** by design. It will refuse to drive a system Chrome / Edge / Brave even if one happens to be listening on a CDP port — the user's daily-driver browsers carry their real cookies, extensions and logged-in sessions, and we don't touch those from MCP. If you see `code=NOT_WONDERBROWSER`, call `browser_open` instead.

If the user says "attach to my browser" expecting their personal Chrome session: gently explain that WonderSuite intentionally drives an isolated bundled browser to keep their real session safe, then offer to `browser_open` a fresh WonderBrowser they can log into separately (logins there persist in the isolated profile across sessions).

**Working with the page:**

```
browser_snapshot({include_security: true})  # FIRST CALL every time the page changes — returns a11y tree with ref=eN IDs
browser_click({ref, includeSnapshot: true}) # click + fresh snap in one call
browser_type({ref, text})                   # use ref or selector, NOT positional coords
browser_fill_form({values: [{ref, value}, ...], submit: true})
browser_press_key({key: "Enter"})
browser_scroll({direction: "down", amount: 800})
browser_wait_for({action: "selector", selector: ".result", timeout_ms: 10000})
browser_screenshot({quality: 80, return_base64: false})  # returns disk path, NOT 4 MB of base64
```

After any user-triggered action: snapshot again. Refs are tied to the snapshot and go stale on DOM change (you'll get `code=STALE_REF`).

**Recon while you're in the page:**

```
browser_storage_full        # dump cookies + localStorage + sessionStorage + IndexedDB
browser_console({})         # read page errors + CSP violations (we auto-capture them)
browser_dom_sinks           # find inline-script innerHTML/eval/document.write — DOM-XSS surface
browser_network_traffic     # list everything the page has fetched since browser_open
browser_replay_to_proxy({request_id})  # take a browser request → fire it through the Repeater → land in proxy traffic
browser_resource_hints      # robots.txt + sitemap.xml + .well-known/* + script srcs + sourceMappingURLs
```

`browser_replay_to_proxy` is the killer feature: pick any auth-bearing request the page made, push it into the proxy where you can fuzz it.

### OAST Blind-Vuln Hunt

WonderSuite's OAST listeners (DNS / HTTP / SMTP) run in-process — no external server like Burp Collaborator needed. As of v0.3.11 the standalone `oast_*` MCP tools are **no longer exposed to the agent** (they were dropping ~8 entries into the tool list and most agents lost the workflow in the noise). The functionality is intact and available two ways:

1. **Through `active_scan({with_oast: true})`** — the recommended path. The scanner generates correlation IDs, fires blind_cmdi / blind_ssrf / blind_sqli_dns / log4shell payloads, and reports OAST callbacks back in the findings list. One tool call covers the whole blind-vuln chain:
   ```
   active_scan({target, with_oast: true})       # blind probes + scanner findings, all in one
   ```
2. **Through the OAST UI panel** — start listeners, view live interactions, generate one-off payloads. Useful when you want to inject manually via `browser_*` or `send_request`. Ask the user to drive the UI; the agent observes results via `proxy_get_traffic` (since blind payloads injected by you go through the local proxy).

If you need the raw OAST MCP surface back (single payloads, manual polling), it's still in the Rust source — un-comment the dispatch + tool_definitions entries in `mcp/handlers/mod.rs` and `mcp/mod.rs`. The v0.3.11 cut was a context-budget decision, not a deprecation.

### Auth + JWT Analysis

```
analyze_jwt({token})        # checks alg=none, HS-key-confusion, weak kid, jku/x5u SSRF, expired, empty sig
smart_decode({input})       # auto-unwraps base64→URL→hex→JWT layered encodings
```

For session theft testing: `browser_storage_full` after login → `send_request` with stolen cookie → compare scopes.

### SQLi / XSS / Injection Hunting

Two paths:

**Active scanner (automated):**
```
active_scan({target, modes: ["sqli_error", "sqli_time", "xss_reflected", "ssti", "lfi", "open_redirect"]})
active_scan({target, with_oast: true})   # adds blind variants
```

**Manual via Intruder (precise, recommended for one juicy param):**
```
proxy_get_traffic({url_contains: "/api/v1"})       # find the request you want
send_to_intruder({traffic_id, param: "user_id"})   # auto-infers category from param name (user_id → sqli, q → xss, redirect → open_redirect, path → lfi, cmd → cmdi)
# returns a pre-configured fuzz_request that you can edit or run as-is
fuzz_request({...})                                # Sniper / Battering Ram / Pitchfork / Cluster Bomb modes
```

### Race Conditions

```
race_request({url, method, body, count: 50})    # fires N requests through a barrier sync — exposes TOCTOU
```

### HTTP Smuggling / Protocol Tricks

```
raw_tcp_send({host, port, payload_hex})        # CL.TE / TE.CL / TE.TE smuggling, custom protocol fuzzing
h2_send_request({...})                          # HTTP/2 specific behaviors, protocol downgrade
mtls_send_request({url, p12_path, p12_password})# mTLS endpoints
```

### Templates (pre-canned probes — Burp-style enumeration)

The Templates UI in the WonderSuite app holds ~110 curated probes. The agent doesn't drive Templates directly via MCP — they're a user-facing tab. If the user asks "check this target against templates", they want to click the Templates Run-all button in the UI, not have you call MCP.

### Generating the Final Report

```
generate_report({format: "markdown" | "json", scan_id?})
```

---

## Tool Reference (91 tools)

### HTTP / Send Request (the workhorses)

- **`send_request`** — the primary HTTP tool. Use for ANY ad-hoc request. Bypasses proxy interception (still logged in `get_traffic_log`).
- **`get_traffic_log`** — read everything `send_request` has fired this session.
- **`raw_tcp_send`** — raw bytes over TCP/TLS for smuggling tests.
- **`mtls_send_request`** — HTTP with client certificate (PKCS12).
- **`h2_send_request`** — HTTP/2-specific.
- **`race_request`** — N concurrent requests via barrier sync.

### Proxy (15 tools — Burp-Suite core)

- **`proxy_start` / `proxy_stop` / `proxy_status`** — lifecycle. Default port 8080.
- **`proxy_toggle_intercept`** — turn interception on/off. Combine with `get_intercepted` + `forward_intercepted` to MITM individual requests.
- **`proxy_get_traffic`** — list captured requests. Filter by `url_contains`, `method`, `status`, `mime_contains`. Returns `traffic_id` per entry — pipe into `send_to_repeater` / `send_to_intruder`.
- **`proxy_search_traffic`** — search bodies + headers.
- **`proxy_get_statistics`** — counters (requests, bytes, connections, uptime).
- **`proxy_clear_traffic`** — wipe.
- **`proxy_export_traffic`** — JSON or HAR.
- **`proxy_add_match_replace` / `proxy_get_match_replace` / `proxy_remove_match_replace`** — regex rewrite rules on in-flight traffic.
- **`proxy_add_interception_rule` / `proxy_remove_interception_rule`** — selective interception (intercept only matching).
- **`proxy_add_tls_passthrough`** — skip MITM for a host (useful for cert-pinned apps).
- **`proxy_set_upstream`** — chain through an upstream proxy.
- **`proxy_get_websocket_messages`** — captured WS frames.
- **`proxy_annotate_traffic`** — notes + color highlights on entries (Burp-style).

### Interception flow

```
proxy_toggle_intercept({enabled: true})
# user does something in browser
get_intercepted                                  # list pending
forward_intercepted({id, modified_raw?})         # forward (optionally mutated) or drop
```

### Browser (24 tools)

**Lifecycle:**
- `browser_open` — spawn bundled WonderBrowser, proxied, with our extension loaded.
- `browser_attach` — reuse an already-running WonderBrowser (port-scans 9333/9222/9223). `auto_launch:true` spawns a fresh one (= `browser_open`). WonderBrowser-only; rejects system Chrome with `code=NOT_WONDERBROWSER`.
- `browser_close` — drop session. For attached browsers doesn't kill the user's process.
- `browser_navigate({url, wait_until: "load"|"domcontentloaded"|"networkidle"})`.
- `browser_tabs({action: "list"|"new"|"close"})`.

**Page state:**
- `browser_snapshot({include_security})` — **PRIMARY primitive**, call before every action that needs refs.
- `browser_get_outer_html({ref})` — single element, not whole page.
- `browser_screenshot({quality, return_base64, full_page})` — saves JPEG to `~/.wondersuite/screenshots/`, returns path.
- `browser_storage_full` — auth-state dump.
- `browser_console` — page errors + CSP violations (we hook `securitypolicyviolation`).
- `browser_dom_sinks` — DOM-XSS surface enumeration.
- `browser_network_traffic({url_contains, method, status, auth_only, limit})` — every request the page has made.
- `browser_resource_hints` — robots.txt + sitemap + .well-known + script srcs + sourceMappingURLs.

**Input — all CDP-native, `isTrusted:true` events (v0.3.3+):**
- `browser_click({ref, includeSnapshot, stealth_profile?})` — humanlike Bezier trajectory → real mousedown/mouseup → click.
- `browser_type({ref, text, clear, stealth_profile?})` — click-into-field + per-char `Input.insertText` with Gaussian cadence.
- `browser_fill_form({values: [{ref|selector|name, value}], form_ref?, submit_ref?, submit: true, stealth_profile?})` — ref-targeted fields go through the humanlike path; selector/name fall back to JS setter.
- `browser_press_key({key, modifiers?})` — real `Input.dispatchKeyEvent(keyDown→keyUp)`. Modifier mask: 1=Alt 2=Ctrl 4=Meta 8=Shift.
- `browser_scroll({direction, amount, ref?, stealth_profile?})` — CDP `mouseWheel` event at cursor position. Real wheel events.
- `browser_select_option({ref, value})`.
- `browser_set_file_input({ref, files: [absolute_paths]})`.

The `stealth_profile` param is optional per call (`fast` | `human` | `paranoid`); without it, uses the session default from Settings.

**Escape hatches:**
- `browser_evaluate({code, await_promise})` — run arbitrary JS in the page's main world. Use sparingly.
- `browser_wait_for({action, selector|text|url, timeout_ms})` — synchronisation.
- `browser_replay_to_proxy({request_id})` — push a CDP-captured request into the proxy's Repeater.

**Diagnostic (UI-only as of v0.3.11):** the stealth-self-test moved out of the MCP surface — open the WonderBrowser settings panel and click "Run stealth check". It reports `isTrusted` counts per event type, `navigator.webdriver` state, cursor-overlay leakage check, and an overall score.

**AI cursor (v0.3.11 fix):** lives on `documentElement`, **only in the top frame** (sub-frames like auth-widget iframes no longer spawn ghost cursors). Updates via CDP-driven `__ws_cursor_animate` calls; back-to-back animations cancel each other via a generation token. Self-heals via MutationObserver + 1.5 s polling.

### Scanner (active + passive)

- **`passive_scan({target})`** — security headers, cookie flags, mixed content, info leaks.
- **`active_scan({target, modes, with_oast})`** — error + time-based SQLi, reflected XSS, SSTI, LFI, open redirect, plus blind variants when `with_oast:true`.
- **`fuzz_request({...})`** — Burp-Intruder. Modes: `sniper`, `battering_ram`, `pitchfork`, `cluster_bomb`. Payload categories: `sqli`, `xss`, `lfi`, `cmdi`, `ssrf`, `xxe`, `ssti`, `open_redirect`, `auth`, `numbers`, `custom`.
- **`payload_manager`** — manage wordlists from SecLists / PayloadsAllTheThings.
- **`generate_report({format})`** — final report from accumulated findings.

### OAST (not in MCP surface as of v0.3.11)

The 8 `oast_*` tools were removed from the agent's tool list in v0.3.11 to trim the context budget. OAST functionality is still 100% available — drive it via `active_scan({with_oast: true})` (recommended; the scanner generates correlation IDs and reports callbacks back in its findings list) or ask the user to operate the OAST UI panel and observe results via `proxy_get_traffic`.

### Recon / OSINT

- **`crawl_target`** — static BFS crawler. JS-blind. For SPAs use the browser.
- **`discover_subdomains({domain, wordlist, use_crt_sh, check_http})`** — DNS bruteforce + CT logs.
- **`discover_content({target, wordlist})`** — directory bruteforce. Wordlists: `common`, `admin`, `api`, `backup`, `medium`.
- **`find_secrets({content_or_url})`** — leaked keys / JWTs / passwords pattern match.
- **`crtsh_search`**, **`wayback_lookup`**, **`whois_lookup`**, **`asn_lookup`** — OSINT.
- **`favicon_hash({url})`** — Murmur3 hash for Shodan/FOFA/ZoomEye pivots.
- **`reverse_ip_lookup({ip})`** — PTR records.
- **`hackertarget_lookup`**, **`ip_geolocation`** — no-API-key OSINT.
- **`js_link_finder({js_url})`** — extract endpoints/paths/parameters from JS bundles.
- **`graphql_introspect({url})`** — discover GraphQL schema (works against many "disabled introspection" servers).
- **`dns_resolve`**, **`tech_detect`**, **`analyze_cdn_waf`** — basics.

### Port Recon (5 tools — v0.3.7)

In-process port scanner with **three real engines**: TCP Connect (no admin), TCP SYN (raw sockets, requires CAP_NET_RAW / root / Npcap), and UDP (no admin, response-based detection). Service detection runs in-process against **187 probes + 12k match patterns** from nmap's canonical `nmap-service-probes` file — no nmap subprocess. Adaptive concurrency via Little's Law (`in_flight = pps × RTT`).

- **`port_scan({target, ports, mode, timing, service_detect, intensity, max_wait_ms})`** — scan a single host. `ports`: `"top-100"` (default), `"top-1000"`, `"80,443"`, `"1-1024"`, `"all"`. `mode`: `"connect" | "syn" | "udp"` (defaults `connect`). `timing`: T0 paranoid → T6 ludicrous (default T3). Returns `{scan_id, total_probes, targets_resolved, ports_count, summary}` — summary has `total_open`, `by_service` histogram, and a 50-item sample.
- **`port_scan_range({targets: [...], ports, mode, exclude_cdn, max_hosts, max_wait_ms})`** — scan multiple hosts (CIDR, range, hostnames). `exclude_cdn` drops known CDN ranges. **Be considerate** — `/24` × top-1000 = ~256k probes. SYN mode is much faster than connect for large ranges.
- **`service_detect({host, port, intensity, timeout_ms})`** — surgical service detection on a known-open port. Runs the full nmap-service-probes pipeline: NULL banner read → port-relevant active probes by rarity ≤ intensity → fallback chain. Returns `{service: {name, product, version, banner, tls_cn, tls_san, info, hostname, os, device, cpe[]}}`.
- **`banner_grab({host, port, max_bytes, timeout_ms, prefer_send?})`** — raw banner read. No probe synthesis. Returns banner string if printable + hex either way. Use for custom protocols.
- **`port_scan_results({scan_id, offset, limit, open_only})`** — paginated drill-down for a previously-issued scan_id.

Scan-mode decision:
```
Quick triage, no admin               → mode="connect"
Large subnet, speed + stealth, admin → mode="syn"  (needs CAP_NET_RAW / root / Npcap)
DNS / SNMP / NTP / IPMI / SIP recon  → mode="udp"
```

Tool-by-tool decision:
```
Single host, fast triage           → port_scan(target, ports="top-100")
Subnet sweep                       → port_scan_range(targets, exclude_cdn=true)
Already know port open, want svc   → service_detect(host, port)
Raw bytes for custom regex match   → banner_grab(host, port, prefer_send="...")
Drill into a previous scan's full result set → port_scan_results(scan_id, offset, limit)
```

Privilege model:
- **Linux SYN**: needs `CAP_NET_RAW`. Tell the user: `sudo setcap cap_net_raw,cap_net_admin=+eip /path/to/wondersuite`. Without it the engine **gracefully falls back to TCP connect** so the scan still produces results.
- **macOS SYN**: needs `sudo` to run WonderSuite. No `setcap` equivalent.
- **Windows SYN**: uses **bundled WinDivert** (LGPLv3, EV-signed, ~140 KB). First SYN scan triggers a single UAC prompt to install the kernel driver as a system service. No external download. HVCI / Memory Integrity must be disabled for the driver to load — the UI surfaces this clearly and falls back to TCP connect when HVCI is enforced.
- **UDP**: no admin needed. Without raw ICMP we can't distinguish `closed` from `open|filtered` (same limitation as nmap UDP without root).

Auto-finding hooks: when `service_detect` reports an `auth_required:false` on a sensitive service (Redis, Mongo, Memcached, anonymous FTP, Elasticsearch) — emit a finding via the scanner-finding event and mention it explicitly. Look for `product` strings matching known CVE-prone software (old OpenSSH, old nginx, etc.) and cross-reference against findings.

### Codec / Crypto

- **`encode` / `decode`** — base64 / URL / hex.
- **`hash`** — MD5 / SHA1 / SHA256 / SHA512.
- **`smart_decode`** — auto-detect multi-layered encoding.
- **`analyze_jwt`** — JWT vuln check (alg-none, HS-key-confusion, jku/x5u SSRF, weak kid, empty sig, expired).

### Repeater / Intruder bridge

- **`send_to_repeater({traffic_id})`** — pop a traffic entry into the Repeater tab.
- **`send_to_intruder({traffic_id, param})`** — auto-builds a `fuzz_request` config; param-name heuristics infer payload category.

### WebSocket

- **`websocket_connect`** — raw WS ops (connect / send / receive / close / list). For testing chat-style / GraphQL-over-WS endpoints.

### Bambda

- **`bambda_filter({data, expression})`** — Burp-style filter on captured traffic. `field operator value` syntax.

---

## Error Codes You Will See

| code | meaning | what to do |
|---|---|---|
| `PROXY_DOWN` | proxy isn't running | `proxy_start({port: 8080})` then retry |
| `NOT_OPEN` | no browser session | `browser_open` or `browser_attach` |
| `ALREADY_OPEN` | session exists | `browser_close` first if you want a fresh one |
| `STALE_REF` | element ref no longer in DOM | call `browser_snapshot` again |
| `WAIT_TIMEOUT` | `browser_wait_for` gave up | inspect with `browser_snapshot` to see why selector/text never appeared |
| `CDP_LOST` | CDP socket died and reconnect failed | `browser_close` + retry from scratch |
| `ATTACH_FAILED` | no WonderBrowser CDP responder on the scanned ports | call again with `auto_launch:true` or just use `browser_open` |
| `NOT_WONDERBROWSER` | found a CDP server but it's a system Chrome / Edge / Brave | call `browser_open` instead — `browser_attach` refuses to drive user-owned browsers |
| `NO_APP_HANDLE` | MCP browser module not initialized | restart the WonderSuite app |

When the user reports a tool error, paste the full `code=… hint=…` instead of paraphrasing — that's what teaches them the recovery path.

---

## Anti-Patterns (avoid these)

- **Don't poll `browser_snapshot` in a loop** — call it once after each meaningful state change. Snapshots are large.
- **Don't paste base64 screenshots into chat.** `browser_screenshot` already saves to disk and returns the path. Quote the path; if the user wants to see it, they can open the file.
- **Don't use `browser_evaluate` for what a typed tool already does.** Click via `browser_click`, fill via `browser_fill_form`. Evaluate is a last resort.
- **Don't run `active_scan` on production sites you don't own.** Confirm scope with the user before any noisy tool (active scan, fuzz, race, intruder).
- **Don't call `send_request` to "test" if the proxy works.** `proxy_status` exists.
- **Don't drop intercepted requests silently** — if interception is on and you forget about pending requests, the user's browser hangs. `get_intercepted` regularly, `forward_intercepted` decisively.
- **Don't try to drive the user's daily-driver Chrome.** `browser_attach` is intentionally WonderBrowser-only — touching the user's real Chrome profile (cookies, extensions, accounts) is not supported. If the user expects that, explain the safety boundary and offer to spin up a bundled WonderBrowser they can log into separately.

---

## Browser MCP Edge Cases You Will Hit

- **Cursor disappears**: it shouldn't — the overlay self-heals via MutationObserver and a 1.5s polling fallback. If it really vanishes, the page may have `document.documentElement.replaceWith(...)` — the next `browser_snapshot` or any browser action will re-inject.
- **Scrolling not visible**: we use a 700ms rAF animation (`__ws_cursor_animate_scroll`) instead of CSS `behavior:smooth`. If you're inside a custom scroll container, pass `ref` to `browser_scroll` so we animate that element specifically.
- **CDP "closed connection" error**: the session detects this and reconnects to the same port automatically. If the second attempt fails (browser process gone) you'll see `code=CDP_LOST` — close + reopen.
- **Snapshots feel slow**: pass `include_security: false` to skip the security checks pass.
- **Forms with honeypots**: `browser_snapshot` flags each form's hidden / off-screen / suspicious-name fields with `is_honeypot: true` + `honeypot_reason`. Don't fill those. They're tarpits.
- **Captchas / 2FA**: MCP browser defaults to visible mode (Settings → Browser → MCP headless). Tell the user "I see a captcha; can you solve it?" and they can interact directly with the same browser window.
- **Bundled-browser-only features**: TLS impersonation (Chrome JA3/JA4) and the stealth extension are loaded for every WonderBrowser session — both `browser_open` and `browser_attach({auto_launch:true})` get them. `browser_attach({})` reuses an existing WonderBrowser session, so those features are also active.

---

## When To Ask Vs. When To Act

Act without asking:
- Read-only recon (`tech_detect`, `dns_resolve`, `whois_lookup`, etc.)
- `proxy_start` if the proxy is down and the user clearly wants to proxy traffic
- `browser_snapshot` after any action
- `send_request` for one-off probes the user explicitly asked for

Ask first:
- Anything noisy: `active_scan`, `fuzz_request`, `race_request`, `discover_content`, `discover_subdomains`
- Anything destructive in interception: dropping requests, match-replace rules
- Spawning a system browser via `browser_attach({auto_launch:true})` — let the user know you're about to open a new Chrome window
- Mode choice when ambiguous: "isolated profile or your real profile (Chrome must be closed)?"

---

## Reporting Style

When you finish a sweep, emit one of these per finding:

```
[severity] Title (where)
  what:   one-line technical description
  evidence: link or paste the curl/raw request/response that proves it
  fix:    one-line remediation
  next:   suggested follow-up tool (e.g. "active_scan with sqli_time on this param")
```

Severity scale: `critical | high | medium | low | info`. Use `info` for things like "Spring Boot Actuator /health is exposed" — interesting context, not exploitable on its own.

---

## One-Liner You Can Steal

> "I'm wired into a local WonderSuite MCP server (91 tools — proxy / browser / scanner / port scanner / OAST / recon / codec). Tell me a target you have permission to test, and I'll start with a passive sweep before anything noisy."

That's the right opening line for any new engagement.
