Attack Test
Hit a running system over HTTP as an attacker. Do not read ACs to make them
pass — ask whether skipping a step / forging proof / swapping identity /
abusing idempotency still moves money or mutates protected state.
Every finding must carry:
- reproduce — request sequence another session can replay cold
- impact — money-move / authz bypass / info disclosure / DoS state
- fix — where to change + the invariant to enforce (not "add validation")
Confirmed findings hand off to the BUG flow. Do not patch production code
inside this skill unless the user explicitly asks to fix after the report.
When to use
- "try hack", "attack-test", "can we bypass…", "transfer without bio/OTP?"
- After a money / authz / session / idempotency feature is up on local or SIT
- Before merging an MR that touches confirm, settle, challenge, authz, webhook
When NOT to use
| Job |
Go elsewhere |
| Latent defects in green code without firing HTTP |
bug-hunter |
| Audit whether a gate/coverage number can go red |
falsifying |
| Diff-only review |
code-review |
| Write e2e that prove ACs |
e2e-playwright |
| No running stack / no contract yet |
stand the stack up + read knowledge/api first |
Ground rules
- Live HTTP only — never mark HACKED from a static code read. Need status + body.
- Happy path first — if the root flow is broken, do not conclude the attack was blocked.
- No exploit kits / malware — only the target system's APIs the user pointed at (local/SIT/this project).
- Stay in scope — sentinel/test customers only; never thrash production data.
- Evidence = request/response — every finding cites status + app error code or settle field.
- Fix must point somewhere — file/function/missing check, or mark
[INFERENCE] if source was not opened.
- Never invent wire fields — pull names from compose / bruno / e2e / api-spec / knowledge.
Inputs (collect before firing)
| Required |
Example |
| Base URL |
http://localhost:8080 |
| Target flow |
transfer biometric, payment confirm, … |
| Normal endpoint order |
verify → accept → result → confirm |
| ≥ 2 test identities |
owner / other customer (header or token) |
| Protected value |
settle money, change destination, unlock session |
| Auth scheme for that env |
X-Customer-ID, bearer, mTLS, … |
Missing inputs → stop and ask. Do not invent.
Process
1) Baseline (happy path)
- Confirm stack health.
- Walk the normal flow until a real success side effect (e.g. confirm returns
core_reference_number).
- Record IDs:
transaction_id, token, session, idempotency key.
If baseline fails → stop attacking; report BASELINE_FAIL.
2) Build the attack matrix (minimum)
Cut against the real flow. Cover at least these six groups:
| Group |
Question |
| Skip-step |
Skip challenge/OTP/approve — does the final action still succeed? |
| Forge-proof |
Fake proof (random UUID, forged success status, empty signature) — accepted? |
| Replay |
Reuse token/session/idem key after success or after fail |
| Confused deputy / IDOR |
Identity B uses A's ids (tx, session, order) |
| Tamper body |
Change amount/destination/customer on a late step |
| State abuse |
Confirm after fail, double settle, parallel confirm |
Add domain-specific cases when present: webhook re-delivery, two-tab race, expired session, mass assignment.
3) Fire and record
Each case keeps:
case:
actor: owner|other|anonymous
steps: ...
expect_blocked: true|false
http_status:
app_code:
side_effect: none|settled|leaked_fields|state_changed
evidence: (trim body — never dump long secrets)
4) Classify results
| Label |
Condition |
| HACKED |
Valuable outcome without meeting the protection conditions (money moved, excess privilege, wrong unlock) |
| INFO_LEAK |
No replayed action, but read another party's or settle data |
| BLOCKED |
Rejected and no bad side effect |
| TRUST_BOUNDARY |
Passed because design trusts an upstream (e.g. BFF) — name it; do not fake a product bug |
| BASELINE_FAIL |
Happy path never succeeded |
5) Report format (mandatory)
Emit the report in this shape (outer fence is documentation only — do not nest live fences when writing the real report):
# Attack Test — <flow> @ <env>
## Baseline
- happy path: PASS|FAIL
- evidence: ...
## Findings
### F1 — <short title> [<HACKED|INFO_LEAK|...>]
**Impact:** ...
**Reproduce:**
1. ...
2. ...
POST /...
Header: ...
Body: ...
→ <status> <code>
**Why it works:** <1–3 lines; cite code if opened>
**Fix:**
- enforce: <invariant, e.g. session.CustomerID == caller>
- where: <path>:<symbol> or layer (usecase confirm gate)
- tests: proposed unit case + negative e2e name
- residual: <if any, e.g. still trusts BFF>
## Blocked (summary table)
| case | result |
|---|---|
| ... | BLOCKED |
## Out of scope / not tested
- ...
6) Minimum fix guidance by type
| Kind |
Direction |
| Skip-step |
Server-side state machine; final action checks state, not only request fields |
| Forge-proof |
Proof issued by server/upstream; client may send only a server-bound handle |
| IDOR |
Every read/consume asserts resource.Owner == caller |
| Idempotency leak |
Idem key lookup scoped by caller/owner before returning a cached body |
| Tamper amount |
Lock amount/destination into session at verify; late steps reject overrides |
| Replay |
Single-use consume (GETDEL); explicit state transitions |
Never propose bare "add validation" without naming the invariant.
Depth levels
| Level |
When |
What |
| smoke |
Need it fast |
baseline + skip-step + forge-proof on the final action |
| standard (default) |
Normal ask |
+ two-identity IDOR + replay + tamper body + idempotency |
| deep |
Before money release |
+ paired-request race + expired/TTL + sibling resource + webhook |
Output rules
- Match the user's language (default Thai if the user wrote Thai).
- Short tables; the report alone must be enough to replay requests.
- Never label HACKED from static reading without an HTTP fire.
- Untested cases go under not tested — never guess.
- Post to MR/Jira only when asked; finding body still needs reproduce + fix.
- Hand HACKED / INFO_LEAK to BUG flow (
diagnosing-bugs → tdd repro) unless the user asked to fix now.
Red flags (skill is broken)
- Only happy path ran, then "looks safe"
- Finding without reproduce
- Finding without fix / floating fix
- Conclusion from code without HTTP
- Real production customers without explicit permission
Verification (before stop)
Rationalizations
| Thought |
Reality |
| "Code checks the state, so skip-step is fine" |
Prove it with HTTP. Untested checks are claims. |
| "403 on one call means the flow is safe" |
Try the final action directly; middle-step 403 is not the prize. |
| "I'll mark HACKED from the missing if" |
Static read → candidate. Live settle/leak → finding. |
| "Fix: add validation" |
Name the invariant and the gate that must enforce it. |
| "No second identity handy — skip IDOR" |
Then list IDOR under not tested. Do not imply it passed. |
| "Production data is fine, I'm careful" |
Sentinel only. Full stop. |
1---2name: attack-test3description: Fires abuse/hack paths over HTTP against a running stack (local/SIT) after the happy path works. Hunts money-moves, authz bypass, proof forge, and idempotency leaks; reports each finding with reproduce steps plus a fix that names the file/check to enforce. Use when the user says try hack, attack-test, probe the flow, security probe HTTP, or asks whether skipping step X still transfers or completes a protected action. Not for static latent hunts (`bug-hunter`), gate audits (`falsifying`), diff review (`code-review`), or AC-driven e2e (`e2e-playwright`).4---56# Attack Test78Hit a **running** system over HTTP as an attacker. Do not read ACs to make them9pass — ask whether **skipping a step / forging proof / swapping identity /10abusing idempotency** still moves money or mutates protected state.1112Every finding **must** carry:13141. **reproduce** — request sequence another session can replay cold152. **impact** — money-move / authz bypass / info disclosure / DoS state163. **fix** — where to change + the invariant to enforce (not "add validation")1718Confirmed findings hand off to the **BUG flow**. Do not patch production code19inside this skill unless the user explicitly asks to fix after the report.2021## When to use2223- "try hack", "attack-test", "can we bypass…", "transfer without bio/OTP?"24- After a money / authz / session / idempotency feature is up on local or SIT25- Before merging an MR that touches confirm, settle, challenge, authz, webhook2627## When NOT to use2829| Job | Go elsewhere |30|---|---|31| Latent defects in green code without firing HTTP | `bug-hunter` |32| Audit whether a gate/coverage number can go red | `falsifying` |33| Diff-only review | `code-review` |34| Write e2e that prove ACs | `e2e-playwright` |35| No running stack / no contract yet | stand the stack up + read knowledge/api first |3637## Ground rules38391. **Live HTTP only** — never mark HACKED from a static code read. Need status + body.402. **Happy path first** — if the root flow is broken, do not conclude the attack was blocked.413. **No exploit kits / malware** — only the target system's APIs the user pointed at (local/SIT/this project).424. **Stay in scope** — sentinel/test customers only; never thrash production data.435. **Evidence = request/response** — every finding cites status + app error code or settle field.446. **Fix must point somewhere** — file/function/missing check, or mark `[INFERENCE]` if source was not opened.457. **Never invent wire fields** — pull names from compose / bruno / e2e / api-spec / knowledge.4647## Inputs (collect before firing)4849| Required | Example |50|---|---|51| Base URL | `http://localhost:8080` |52| Target flow | transfer biometric, payment confirm, … |53| Normal endpoint order | verify → accept → result → confirm |54| ≥ 2 test identities | owner / other customer (header or token) |55| Protected value | settle money, change destination, unlock session |56| Auth scheme for that env | `X-Customer-ID`, bearer, mTLS, … |5758Missing inputs → stop and ask. Do not invent.5960## Process6162### 1) Baseline (happy path)63641. Confirm stack health.652. Walk the normal flow until a real success side effect (e.g. confirm returns `core_reference_number`).663. Record IDs: `transaction_id`, token, session, idempotency key.6768If baseline fails → stop attacking; report **BASELINE_FAIL**.6970### 2) Build the attack matrix (minimum)7172Cut against the real flow. Cover at least these six groups:7374| Group | Question |75|---|---|76| **Skip-step** | Skip challenge/OTP/approve — does the final action still succeed? |77| **Forge-proof** | Fake proof (random UUID, forged success status, empty signature) — accepted? |78| **Replay** | Reuse token/session/idem key after success or after fail |79| **Confused deputy / IDOR** | Identity B uses A's ids (tx, session, order) |80| **Tamper body** | Change amount/destination/customer on a late step |81| **State abuse** | Confirm after fail, double settle, parallel confirm |8283Add domain-specific cases when present: webhook re-delivery, two-tab race, expired session, mass assignment.8485### 3) Fire and record8687Each case keeps:8889```text90case:91actor: owner|other|anonymous92steps: ...93expect_blocked: true|false94http_status:95app_code:96side_effect: none|settled|leaked_fields|state_changed97evidence: (trim body — never dump long secrets)98```99100### 4) Classify results101102| Label | Condition |103|---|---|104| **HACKED** | Valuable outcome without meeting the protection conditions (money moved, excess privilege, wrong unlock) |105| **INFO_LEAK** | No replayed action, but read another party's or settle data |106| **BLOCKED** | Rejected and no bad side effect |107| **TRUST_BOUNDARY** | Passed because design trusts an upstream (e.g. BFF) — name it; do not fake a product bug |108| **BASELINE_FAIL** | Happy path never succeeded |109110### 5) Report format (mandatory)111112Emit the report in this shape (outer fence is documentation only — do not nest live fences when writing the real report):113114````markdown115# Attack Test — <flow> @ <env>116117## Baseline118- happy path: PASS|FAIL119- evidence: ...120121## Findings122123### F1 — <short title> [<HACKED|INFO_LEAK|...>]124**Impact:** ...125**Reproduce:**1261. ...1272. ...128129 POST /...130 Header: ...131 Body: ...132 → <status> <code>133134**Why it works:** <1–3 lines; cite code if opened>135**Fix:**136- enforce: <invariant, e.g. session.CustomerID == caller>137- where: <path>:<symbol> or layer (usecase confirm gate)138- tests: proposed unit case + negative e2e name139- residual: <if any, e.g. still trusts BFF>140141## Blocked (summary table)142143| case | result |144|---|---|145| ... | BLOCKED |146147## Out of scope / not tested148149- ...150````151152### 6) Minimum fix guidance by type153154| Kind | Direction |155|---|---|156| Skip-step | Server-side state machine; final action checks **state**, not only request fields |157| Forge-proof | Proof issued by server/upstream; client may send only a server-bound handle |158| IDOR | Every read/consume asserts `resource.Owner == caller` |159| Idempotency leak | Idem key lookup scoped by caller/owner before returning a cached body |160| Tamper amount | Lock amount/destination into session at verify; late steps reject overrides |161| Replay | Single-use consume (GETDEL); explicit state transitions |162163Never propose bare "add validation" without naming the **invariant**.164165## Depth levels166167| Level | When | What |168|---|---|---|169| **smoke** | Need it fast | baseline + skip-step + forge-proof on the final action |170| **standard** (default) | Normal ask | + two-identity IDOR + replay + tamper body + idempotency |171| **deep** | Before money release | + paired-request race + expired/TTL + sibling resource + webhook |172173## Output rules174175- Match the user's language (default Thai if the user wrote Thai).176- Short tables; the report alone must be enough to replay requests.177- Never label HACKED from static reading without an HTTP fire.178- Untested cases go under **not tested** — never guess.179- Post to MR/Jira only when asked; finding body still needs reproduce + fix.180- Hand HACKED / INFO_LEAK to BUG flow (`diagnosing-bugs` → `tdd` repro) unless the user asked to fix now.181182## Red flags (skill is broken)183184- Only happy path ran, then "looks safe"185- Finding without reproduce186- Finding without fix / floating fix187- Conclusion from code without HTTP188- Real production customers without explicit permission189190## Verification (before stop)191192- [ ] Baseline passed and recorded (or BASELINE_FAIL stopped the run)193- [ ] Standard groups covered (or depth stated as smoke/deep)194- [ ] Every finding has reproduce + impact + fix195- [ ] HACKED / INFO_LEAK / TRUST_BOUNDARY split cleanly196- [ ] Blocked cases summarized in a table197- [ ] not tested listed explicitly198199## Rationalizations200201| Thought | Reality |202|---|---|203| "Code checks the state, so skip-step is fine" | Prove it with HTTP. Untested checks are claims. |204| "403 on one call means the flow is safe" | Try the final action directly; middle-step 403 is not the prize. |205| "I'll mark HACKED from the missing if" | Static read → candidate. Live settle/leak → finding. |206| "Fix: add validation" | Name the invariant and the gate that must enforce it. |207| "No second identity handy — skip IDOR" | Then list IDOR under not tested. Do not imply it passed. |208| "Production data is fine, I'm careful" | Sentinel only. Full stop. |