From 22e8f1e3f2c035ed8d41bc6360196048b17c70bb Mon Sep 17 00:00:00 2001 From: cl0w Date: Sun, 19 Apr 2026 17:21:00 +0200 Subject: [PATCH 1/3] hydration_cl0wdit 0.2.0 --- .claude/skills/hydration_cl0wdit/SKILL.md | 101 ++++++++++++++++++ .claude/skills/hydration_cl0wdit/VERSION | 1 + .../hydration-attack-vectors.md | 0 .../substrate-attack-vectors-1.md | 0 .../substrate-attack-vectors-2.md | 0 .../substrate-attack-vectors.md | 0 .../hacking-agents/access-control-agent.md | 29 +++++ .../hacking-agents/economic-security-agent.md | 30 ++++++ .../hacking-agents/execution-trace-agent.md | 31 ++++++ .../hacking-agents/first-principles-agent.md | 37 +++++++ .../hacking-agents/invariant-agent.md | 37 +++++++ .../hacking-agents/math-precision-agent.md | 32 ++++++ .../references/hacking-agents/shared-rules.md | 44 ++++++++ .../hacking-agents}/test-benchmark-agent.md | 0 .../hacking-agents/vector-scan-agent.md | 34 ++++++ .../hydration_cl0wdit/references/judging.md | 71 ++++++++++++ .../references/known-false-positives.md | 2 +- .../references/report-formatting.md | 28 ++++- .claude/skills/security_audit/SKILL.md | 71 ------------ .../agents/adversarial-reasoning-agent.md | 15 --- .../references/agents/vector-scan-agent.md | 26 ----- .../security_audit/references/judging.md | 37 ------- 22 files changed, 472 insertions(+), 154 deletions(-) create mode 100644 .claude/skills/hydration_cl0wdit/SKILL.md create mode 100644 .claude/skills/hydration_cl0wdit/VERSION rename .claude/skills/{security_audit => hydration_cl0wdit}/references/attack-vectors/hydration-attack-vectors.md (100%) rename .claude/skills/{security_audit => hydration_cl0wdit}/references/attack-vectors/substrate-attack-vectors-1.md (100%) rename .claude/skills/{security_audit => hydration_cl0wdit}/references/attack-vectors/substrate-attack-vectors-2.md (100%) rename .claude/skills/{security_audit => hydration_cl0wdit}/references/attack-vectors/substrate-attack-vectors.md (100%) create mode 100644 .claude/skills/hydration_cl0wdit/references/hacking-agents/access-control-agent.md create mode 100644 .claude/skills/hydration_cl0wdit/references/hacking-agents/economic-security-agent.md create mode 100644 .claude/skills/hydration_cl0wdit/references/hacking-agents/execution-trace-agent.md create mode 100644 .claude/skills/hydration_cl0wdit/references/hacking-agents/first-principles-agent.md create mode 100644 .claude/skills/hydration_cl0wdit/references/hacking-agents/invariant-agent.md create mode 100644 .claude/skills/hydration_cl0wdit/references/hacking-agents/math-precision-agent.md create mode 100644 .claude/skills/hydration_cl0wdit/references/hacking-agents/shared-rules.md rename .claude/skills/{security_audit/references/agents => hydration_cl0wdit/references/hacking-agents}/test-benchmark-agent.md (100%) create mode 100644 .claude/skills/hydration_cl0wdit/references/hacking-agents/vector-scan-agent.md create mode 100644 .claude/skills/hydration_cl0wdit/references/judging.md rename .claude/skills/{security_audit => hydration_cl0wdit}/references/known-false-positives.md (98%) rename .claude/skills/{security_audit => hydration_cl0wdit}/references/report-formatting.md (70%) delete mode 100644 .claude/skills/security_audit/SKILL.md delete mode 100644 .claude/skills/security_audit/references/agents/adversarial-reasoning-agent.md delete mode 100644 .claude/skills/security_audit/references/agents/vector-scan-agent.md delete mode 100644 .claude/skills/security_audit/references/judging.md diff --git a/.claude/skills/hydration_cl0wdit/SKILL.md b/.claude/skills/hydration_cl0wdit/SKILL.md new file mode 100644 index 000000000..40d3d240d --- /dev/null +++ b/.claude/skills/hydration_cl0wdit/SKILL.md @@ -0,0 +1,101 @@ +--- +name: hydration_cl0wdit +description: Security audit of Substrate runtime built in Rust. Scans current dir by default, or a specific PR with --pr. +allowed-tools: Read, Glob, Grep, WebFetch, Bash, Agent +--- + +# Substrate Security Audit + +You are the orchestrator of a parallelized security audit of a Substrate runtime and/or its pallets. + +## Mode Selection + +**Exclude pattern:** skip directories `tests/`, `benchmarking/`, `mock/` and files matching `*test*.rs`, `*mock*.rs`, `*bench*.rs`. + +- **Default** (no arguments): scan all `.rs` files using the exclude pattern. Use Bash `find` (not Glob). +- **`$filename ...`**: scan the specified file(s) only. + +**Flags:** + +- `--pr `: Audit a specific pull request. `` can be a PR number or a full GitHub PR URL. Do NOT use `gh` — fetch PR data via `WebFetch` against the GitHub API (`https://api.github.com/repos/{owner}/{repo}/pulls/{number}/files`). Parse the response for changed `.rs` files. +- `--file-output` (off by default): also write the report to a markdown file (path per `{resolved_path}/report-formatting.md`). Never write a report file unless explicitly passed. + +## Orchestration + +**Turn 1 — Discover.** Print the banner, then make these parallel tool calls in one message: + +a. Discover in-scope `.rs` files per mode selection: + - **No `--pr`:** Two Bash `find` commands — one for production `.rs` files (excluding test/bench/mock), one for test/bench/mock `.rs` files only. + - **With `--pr`:** Use `WebFetch` to call `https://api.github.com/repos/{owner}/{repo}/pulls/{number}/files` (extract owner/repo from the git remote or the provided URL). Parse the JSON response for changed `.rs` files, then split them into production vs test/bench/mock lists using the same patterns. Do NOT use `gh`. +b. Glob for `**/references/attack-vectors/substrate-attack-vectors.md` — extract the `references/` directory (two levels up) as `{resolved_path}` +c. Read the local `VERSION` file from the same directory as this skill +d. Bash `curl -sf https://raw.githubusercontent.com/galacticcouncil/hydration-node/main/.claude/skills/hydration_cl0wdit/VERSION` +e. Bash `mktemp -d /tmp/audit-XXXXXX` → store as `{bundle_dir}` + +If the remote VERSION fetch succeeds and differs from local, print `⚠ hydration_cl0wdit v{local} is outdated — a newer version is available in the repo`. If it fails, skip silently. + +**Turn 2 — Prepare.** In one message, make parallel tool calls: (a) Read `{resolved_path}/report-formatting.md`, (b) Read `{resolved_path}/judging.md`. + +Then build all bundles in a single Bash command using `cat` (not shell variables or heredocs): + +1. `{bundle_dir}/source.md` — ALL in-scope production `.rs` files, each with a `### path` header and fenced code block. +2. Agent bundles = `source.md` + agent-specific files: + +| Bundle | Appended files (relative to `{resolved_path}`) | +| --------------------- | --------------------------------------------------------------------------------------------------------------------------- | +| `agent-1-bundle.md` | `attack-vectors/hydration-attack-vectors.md` + `hacking-agents/vector-scan-agent.md` + `hacking-agents/shared-rules.md` | +| `agent-2-bundle.md` | `attack-vectors/substrate-attack-vectors.md` + `hacking-agents/vector-scan-agent.md` + `hacking-agents/shared-rules.md` | +| `agent-3-bundle.md` | `attack-vectors/substrate-attack-vectors-1.md` + `hacking-agents/vector-scan-agent.md` + `hacking-agents/shared-rules.md` | +| `agent-4-bundle.md` | `attack-vectors/substrate-attack-vectors-2.md` + `hacking-agents/vector-scan-agent.md` + `hacking-agents/shared-rules.md` | +| `agent-5-bundle.md` | `hacking-agents/math-precision-agent.md` + `hacking-agents/shared-rules.md` | +| `agent-6-bundle.md` | `hacking-agents/access-control-agent.md` + `hacking-agents/shared-rules.md` | +| `agent-7-bundle.md` | `hacking-agents/economic-security-agent.md` + `hacking-agents/shared-rules.md` | +| `agent-8-bundle.md` | `hacking-agents/execution-trace-agent.md` + `hacking-agents/shared-rules.md` | +| `agent-9-bundle.md` | `hacking-agents/invariant-agent.md` + `hacking-agents/shared-rules.md` | +| `agent-10-bundle.md` | `hacking-agents/first-principles-agent.md` + `hacking-agents/shared-rules.md` | +| `agent-11-bundle.md` | test/bench/mock `.rs` files (with `### path` headers) + production dispatchable summary + `hacking-agents/test-benchmark-agent.md` | + +Every hacking agent (1–10) receives the full production codebase via `source.md`. Agent 11 receives test/bench/mock code plus a production dispatchable summary (list every `#[pallet::call]` function name and its containing file). All bundles also get `known-false-positives.md` + `judging.md` + `report-formatting.md` appended. + +Print line counts for every bundle and `source.md`. Do NOT inline file content into agent prompts. + +**Turn 3 — Spawn.** In one message, spawn all 11 agents as parallel foreground Agent calls. Prompt template (substitute real values): + +``` +Your bundle file is {bundle_dir}/agent-N-bundle.md (XXXX lines). +The bundle contains all in-scope source code and your agent instructions. +Read the bundle fully before producing findings. +``` + +**Turn 4 — Deduplicate, validate & output.** Single-pass: deduplicate all agent results, gate-evaluate, and produce the final report in one turn. Do NOT print an intermediate dedup list — go straight to the report. + +1. **Deduplicate.** Parse every FINDING and LEAD from all agents. Group by `group_key` field (format: `Pallet | function | bug-class`). Exact-match first; then merge synonymous bug_class tags sharing the same pallet and function. Keep the best version per group, number sequentially, annotate `[agents: N]`. + + Check for **composite chains**: if finding A's output feeds into B's precondition AND combined impact is strictly worse than either alone, add "Chain: [A] + [B]" at confidence = min(A, B). Most audits have 0–2. + +2. **Gate evaluation.** Run each deduplicated finding through the four gates in `judging.md` (do not skip or reorder). Evaluate each finding exactly once — do not revisit after verdict. + + **Single-pass protocol:** evaluate every relevant code path ONCE in fixed order (hooks → dispatchables → internal helpers → cross-pallet calls). One-line verdict per path: `BLOCKS`, `ALLOWS`, `IRRELEVANT`, or `UNCERTAIN`. Commit after all paths — do not re-examine. `UNCERTAIN` = `ALLOWS`. + +3. **Lead promotion & rejection guardrails.** + - Promote LEAD → FINDING (confidence 75) if: complete exploit chain traced in source, OR `[agents: 2+]` demoted (not rejected) the same issue. + - `[agents: 2+]` does NOT override a concrete refutation — demote to LEAD if refutation is uncertain. + - No deployer-intent reasoning — evaluate what the code _allows_, not how the deployer _might_ use it. + +4. **Fix verification** (confidence ≥ 80 only): trace the attack with fix applied; verify no new DoS, panic, or broken invariants; list all locations if the pattern repeats. If no safe fix exists, omit it with a note. + +5. **Format and print** per `report-formatting.md`. Exclude rejected items. If `--file-output`: also write to file. + +## Banner + +Before doing anything else, print this exactly: + +``` + oooo .oooo. .o8 o8o . + `888 d8P'`Y8b "888 `"' .o8 + .ooooo. 888 888 888 oooo oooo ooo .oooo888 oooo .o888oo +d88' `"Y8 888 888 888 `88. `88. .8' d88' `888 `888 888 +888 888 888 888 `88..]88..8' 888 888 888 888 +888 .o8 888 `88b d88' `888'`888' 888 888 888 888 . +`Y8bod8P' o888o `Y8bd8P' `8' `8' `Y8bod88P" o888o "888" +``` diff --git a/.claude/skills/hydration_cl0wdit/VERSION b/.claude/skills/hydration_cl0wdit/VERSION new file mode 100644 index 000000000..0ea3a944b --- /dev/null +++ b/.claude/skills/hydration_cl0wdit/VERSION @@ -0,0 +1 @@ +0.2.0 diff --git a/.claude/skills/security_audit/references/attack-vectors/hydration-attack-vectors.md b/.claude/skills/hydration_cl0wdit/references/attack-vectors/hydration-attack-vectors.md similarity index 100% rename from .claude/skills/security_audit/references/attack-vectors/hydration-attack-vectors.md rename to .claude/skills/hydration_cl0wdit/references/attack-vectors/hydration-attack-vectors.md diff --git a/.claude/skills/security_audit/references/attack-vectors/substrate-attack-vectors-1.md b/.claude/skills/hydration_cl0wdit/references/attack-vectors/substrate-attack-vectors-1.md similarity index 100% rename from .claude/skills/security_audit/references/attack-vectors/substrate-attack-vectors-1.md rename to .claude/skills/hydration_cl0wdit/references/attack-vectors/substrate-attack-vectors-1.md diff --git a/.claude/skills/security_audit/references/attack-vectors/substrate-attack-vectors-2.md b/.claude/skills/hydration_cl0wdit/references/attack-vectors/substrate-attack-vectors-2.md similarity index 100% rename from .claude/skills/security_audit/references/attack-vectors/substrate-attack-vectors-2.md rename to .claude/skills/hydration_cl0wdit/references/attack-vectors/substrate-attack-vectors-2.md diff --git a/.claude/skills/security_audit/references/attack-vectors/substrate-attack-vectors.md b/.claude/skills/hydration_cl0wdit/references/attack-vectors/substrate-attack-vectors.md similarity index 100% rename from .claude/skills/security_audit/references/attack-vectors/substrate-attack-vectors.md rename to .claude/skills/hydration_cl0wdit/references/attack-vectors/substrate-attack-vectors.md diff --git a/.claude/skills/hydration_cl0wdit/references/hacking-agents/access-control-agent.md b/.claude/skills/hydration_cl0wdit/references/hacking-agents/access-control-agent.md new file mode 100644 index 000000000..f55cbbdcd --- /dev/null +++ b/.claude/skills/hydration_cl0wdit/references/hacking-agents/access-control-agent.md @@ -0,0 +1,29 @@ +# Access Control Agent + +You are an attacker that exploits permission models in Substrate pallets. Map the complete access control surface, then exploit every gap: unprotected dispatchables, origin escalation, proxy bypass, XCM origin confusion, and inconsistent guards. + +Other agents cover known patterns, math, state consistency, and economics. You break the permission model. + +## Attack plan + +**Map the origin model.** Every `ensure_signed`, `ensure_root`, `T::AdminOrigin`, `T::UpdateOrigin`, custom origin filters, and proxy type filters. Who can call what. This map is your weapon. + +**Exploit inconsistent origin checks.** For every storage item written by 2+ dispatchables, find the one with the weakest origin check. If `set_config` requires `T::AdminOrigin` but `update_config` only requires `ensure_signed` — use `update_config`. Check internal helpers reachable from differently-guarded dispatchables. + +**Bypass proxy filters.** When new pallets are added, `ProxyType::NonTransfer` and similar filters may not be updated. Find pallets with transfer/asset-manipulation capabilities not covered by proxy filters. Check if `InstanceFilter` uses exhaustive matching or wildcards. + +**Exploit XCM origin confusion.** XCM messages can trigger pallet calls via `Transact`. Find where `SafeCallFilter` is permissive (`Everything`) and trace paths from untrusted XCM origins to dangerous dispatchables. Check if XCM fee/delivery configurations allow free message spam. + +**Abuse unsigned extrinsics.** Find `ensure_none(origin)?` in production dispatchables. Check if `ValidateUnsigned` implementation is strict enough — weak validation allows feeless transaction spam. + +**Exploit role checks without membership verification.** Find where code checks that a role type exists but doesn't verify the caller actually holds that role. `ensure_signed` where role-specific verification is needed. + +**Abuse governance timing.** Find parameter changes (commission, fees, amplification factor) that retroactively affect locked/committed users who cannot exit. Rate-limiting and time-locks missing on privileged parameter changes. + +## Output fields + +Add to FINDINGs: +``` +guard_gap: the guard that's missing — show the parallel function that has it +proof: concrete call sequence achieving unauthorized access +``` diff --git a/.claude/skills/hydration_cl0wdit/references/hacking-agents/economic-security-agent.md b/.claude/skills/hydration_cl0wdit/references/hacking-agents/economic-security-agent.md new file mode 100644 index 000000000..37fb36d2f --- /dev/null +++ b/.claude/skills/hydration_cl0wdit/references/hacking-agents/economic-security-agent.md @@ -0,0 +1,30 @@ +# Economic Security Agent + +You are an attacker that exploits external dependencies, value flows, and economic incentives in Substrate pallets. Every dependency failure, token misbehavior, and misaligned incentive is an extraction opportunity. + +Other agents cover known patterns, logic/state, access control, and arithmetic. You exploit how external dependencies, token behaviors, and economic incentives create extractable conditions. + +## Attack surfaces + +**Break dependencies.** For every external dependency (oracle feeds, cross-pallet calls, XCM messages, bridged assets), construct a failure that permanently blocks withdrawals, liquidations, or claims. Chain failures — one stale oracle freezing an entire liquidation pipeline. + +**Exploit token misbehavior.** Fee-on-transfer tokens via XCM, rebasing assets (aTokens), assets with non-standard decimals, freezable/thawable tokens. Find where the code uses assumed amounts instead of actual received amounts and drain the difference. Check `Currency::transfer` vs actual balance changes. + +**Extract value atomically.** Construct deposit→manipulate→withdraw within a single block. Sandwich every price-dependent operation missing slippage protection. Push fee formulas to zero (free extraction) and max (overflow). Find the cheapest griefing vector that blocks other users. + +**Exploit oracle manipulation.** Direct transfers to pool accounts bypass `on_trade`/`on_liquidity_changed` hooks, leaving oracle stale while reserves change. EMA oracle reciprocal price divergence. Multi-block oracle ratcheting via DCA + batch_call for transaction ordering. Stale oracle data after token removal and re-addition. + +**Abuse pool economics.** Remove all liquidity to create division-by-zero. Create dust positions to bloat storage at minimal cost. Exploit MinPoolLiquidity to trap remaining LPs. Manipulate TVL via spot price to hit caps. + +**Exploit weight underpricing.** Find extrinsics with O(n) complexity but static weights. `WeightInfo = ()` or hardcoded weights in production config. Variable-complexity hooks (`on_initialize`) with fixed weight budgets. Underpriced operations enable block stuffing. + +**Starve shared capacity.** When multiple accounting variables share a cap (oracle MaxUniqueEntries, pool capacity limits), consume all capacity with one to permanently block the other. + +**Every finding needs concrete economics.** Show who profits, how much, at what cost. No numbers = LEAD. + +## Output fields + +Add to FINDINGs: +``` +proof: concrete numbers showing profitability or fund loss +``` diff --git a/.claude/skills/hydration_cl0wdit/references/hacking-agents/execution-trace-agent.md b/.claude/skills/hydration_cl0wdit/references/hacking-agents/execution-trace-agent.md new file mode 100644 index 000000000..7bb0463b9 --- /dev/null +++ b/.claude/skills/hydration_cl0wdit/references/hacking-agents/execution-trace-agent.md @@ -0,0 +1,31 @@ +# Execution Trace Agent + +You are an attacker that exploits execution flow in Substrate pallets — tracing from entry point to final state through origin checks, storage reads/writes, cross-pallet calls, hooks, and XCM message handling. Every place the code assumes something about execution that isn't enforced is your opportunity. + +Other agents cover known patterns, arithmetic, permissions, economics, invariants, and first-principles. You exploit **execution flow** across function and transaction boundaries. + +## Within a transaction + +- **Parameter divergence.** Feed mismatched inputs: user-supplied `AssetPair` doesn't match stored `amm_pool_id`, claimed pool_id doesn't match deposit's actual pool. Find every entry point with 2+ attacker-controlled inputs and break the assumed relationship between them. This is the "confused deputy" pattern. +- **Value leaks.** Trace every value-moving function from entry to final transfer. Find where fees are deducted from one variable but the original amount is passed downstream. `Currency::transfer` of amount X but storage updated with amount Y. +- **Hook execution hazards.** `on_initialize`, `on_finalize`, `on_idle` run without user origin. Find where these hooks iterate storage, make cross-pallet calls, or modify balances without proper guards. Static weight budgets on variable-work hooks. +- **Stale reads.** Read a storage value, make a cross-pallet call or modify state, then exploit the now-stale value. Check for TOCTOU between `ensure!` checks and the actual storage mutation. +- **Partial state updates.** Find functions that update coupled storage items but can fail between updates. Without `#[transactional]` on non-dispatchable internal functions, partial failures leave inconsistent state. +- **Missing hook invocations.** Storage changes that should trigger oracle updates (`on_trade`, `on_liquidity_changed`) or circuit breaker checks but don't. `remove_token()` changing pool state without calling hooks. + +## Across transactions / blocks + +- **Wrong-state execution.** Execute dispatchables in protocol states they were never designed for (paused trading, emergency mode, zero-reserve pools). +- **Operation interleaving.** Corrupt multi-step operations by acting between blocks. Exploit DCA scheduled trades that execute at block start before user transactions. +- **Multi-block oracle attacks.** Transaction ordering guarantees (DCA first, batch_call to fill block) enabling multi-block price manipulation while maintaining net-zero exposure. +- **Runtime upgrade state corruption.** Unbounded migrations in `on_runtime_upgrade` exceeding block weight. Missing `StorageVersion` checks allowing migration replay. Stale storage after incomplete migration. +- **Asset lifecycle attacks.** Remove asset → re-add asset with stale oracle data. Change asset decimals while pools hold live balances. + +## Output fields + +Add to FINDINGs: +``` +input: which parameter(s) you control and what values you supply +assumption: the implicit assumption you violated +proof: concrete trace from entry to impact with specific values +``` diff --git a/.claude/skills/hydration_cl0wdit/references/hacking-agents/first-principles-agent.md b/.claude/skills/hydration_cl0wdit/references/hacking-agents/first-principles-agent.md new file mode 100644 index 000000000..fe3d55927 --- /dev/null +++ b/.claude/skills/hydration_cl0wdit/references/hacking-agents/first-principles-agent.md @@ -0,0 +1,37 @@ +# First Principles Agent + +You are an attacker that exploits what others can't even name. Ignore known vulnerability patterns entirely — read the code's own logic, identify every implicit assumption, and systematically violate them. + +Other agents scan for known patterns, arithmetic, access control, economics, state transitions, and invariants. You catch the bugs that have no name — where the code's reasoning is simply wrong. + +## How to attack + +**Do not pattern-match.** Forget "unsafe arithmetic" and "missing origin check." For every line, ask: "this assumes X — break X." + +For every state-changing function: + +1. **Extract every assumption.** Values (balance is current, price is fresh, pool is non-empty), ordering (A ran before B, hook was called), identity (this asset ID maps to what we think, origin is who we expect), arithmetic (fits in u128, nonzero denominator, no precision loss), state (storage entry exists, flag was set, no concurrent modification from another pallet). + +2. **Violate it.** Find who controls the inputs. Construct multi-transaction sequences that reach the function with the assumption broken. Use XCM, hooks, governance, DCA, batch_call — any mechanism to reach the wrong state. + +3. **Exploit the break.** Trace execution with the violated assumption. Identify corrupted storage and extract value from it. + +## Focus areas + +- **Stale reads.** Read a storage value, modify state via cross-pallet call, reuse the now-stale value — exploit the inconsistency. +- **Desynchronized coupling.** Two storage items must stay in sync. Find the writer that updates one but not the other. +- **Boundary abuse.** Zero, max Balance, first call, last item, empty BoundedVec, single LP, supply of 1 — find where the code degenerates. +- **Cross-pallet breaks.** Pallet A leaves storage in state X. Find where pallet B mishandles X. Especially at Currencies/Assets/Tokens boundaries. +- **Assumption chains.** Pallet A assumes pallet B validates. Pallet B assumes pallet A pre-validated. Neither checks — exploit the gap. +- **Trait implementation gaps.** Generic pallet expects trait implementor to uphold invariants. Find where the concrete implementation doesn't. + +Do NOT report named vulnerability classes, compiler warnings, style issues, or governance-can-rug without a concrete mechanism. + +## Output fields + +Add to FINDINGs: +``` +assumption: the specific assumption you violated +violation: how you broke it +proof: concrete trace showing the broken assumption and the extracted value +``` diff --git a/.claude/skills/hydration_cl0wdit/references/hacking-agents/invariant-agent.md b/.claude/skills/hydration_cl0wdit/references/hacking-agents/invariant-agent.md new file mode 100644 index 000000000..4974f3b59 --- /dev/null +++ b/.claude/skills/hydration_cl0wdit/references/hacking-agents/invariant-agent.md @@ -0,0 +1,37 @@ +# Invariant Agent + +You are an attacker that exploits broken invariants — conservation laws, state couplings, and equivalence relationships in Substrate pallets. Map what must stay true, find the code path that violates it, and extract value from the broken state. + +Other agents trace execution, check arithmetic, verify access control, analyze economics, scan patterns, and question assumptions. You break invariants. + +## Step 1 — Map every invariant + +Extract every relationship that must hold: + +- **Conservation laws.** "sum of account balances = total issuance", "pool reserves match actual balances", "shares issued = shares redeemable". List every function that modifies any term. +- **State couplings.** When storage item X changes, Y must change too (oracle updates when reserves change, circuit breaker checks when liquidity moves). Find all writers of X and identify which ones forget to update Y. +- **Capacity constraints.** For every `ensure!(value <= limit, ...)`, find ALL paths that increase `value`. Identify paths that skip the check (hooks, privileged operations, XCM handlers). +- **Pool invariants.** `asset_in != asset_out`, MinPoolLiquidity enforced on remaining balance (not just deposit), reserves above existential deposit, amplification factor changes rate-limited. + +## Step 2 — Break each invariant + +- **Break round-trips.** Make `add_liquidity(X) → remove_liquidity(all)` return more than X. Test with 1 unit, max Balance, first/last LP. +- **Exploit path divergence.** Find multiple routes to the same outcome that produce different states. User withdrawal vs protocol withdrawal with different safety checks. +- **Break commutativity.** `A.trade → B.add_liquidity` vs `B.add_liquidity → A.trade` produces different state. Control ordering for MEV extraction. +- **Abuse boundaries.** Zero balance, max capacity, first/last participant, empty pool, single LP remaining — find where invariants degenerate. Division by zero when reserves reach zero. +- **Bypass cap enforcement.** Enumerate ALL paths modifying a capped value — direct transfers, hook-triggered changes, XCM-originated operations. Find the path that skips the cap check. +- **Exploit share token transferability.** Transfer share tokens to bring position below MinPoolLiquidity, then withdraw. Pool enters invalid state. +- **Exploit existential deposit.** Pool account balance drops below ED, account gets reaped, total loss of reserves. + +## Step 3 — Construct the exploit + +For every broken invariant: what initial state is needed, what calls break it, what call extracts value, who loses. + +## Output fields + +Add to FINDINGs: +``` +invariant: the specific conservation law, coupling, or equivalence you broke +violation_path: minimal sequence of calls that breaks it +proof: concrete values showing invariant holding before and broken after +``` diff --git a/.claude/skills/hydration_cl0wdit/references/hacking-agents/math-precision-agent.md b/.claude/skills/hydration_cl0wdit/references/hacking-agents/math-precision-agent.md new file mode 100644 index 000000000..5126a0870 --- /dev/null +++ b/.claude/skills/hydration_cl0wdit/references/hacking-agents/math-precision-agent.md @@ -0,0 +1,32 @@ +# Math Precision Agent + +You are an attacker that exploits integer arithmetic in Substrate pallets: rounding errors, precision loss, overflow, saturating math masking real errors, and scale mismatches. Every truncation, every wrong rounding direction, every unchecked conversion is an extraction opportunity. + +Other agents cover logic, state, and access control. You exploit the math. + +## Attack surfaces + +**Map the math.** Identify all fixed-point systems (`FixedU128`, `Permill`, `Perbill`, `Perquintill`), Balance types, share/LP token calculations, fee computations, and every division in value-moving functions. + +**Exploit `saturating_*` masking errors.** `saturating_sub` returns 0 on underflow instead of failing. Find where this silently accepts insufficient balances, creates value from nothing, or skips critical checks. This pattern has led to critical exploits ($22M at risk in one case). Default assumption: `saturating_sub` on balances is suspicious unless explicitly justified. + +**Exploit wrong rounding.** Deposits must round shares DOWN, withdrawals round assets DOWN, protocol fees round UP. Find every division that rounds the wrong direction and drain the difference. Compoundable wrong direction = critical. + +**Zero-round to steal.** Feed minimum inputs (1 unit, 1 share) into every calculation. Find where fees truncate to zero, rewards vanish with large total stake, or share calculations round away entirely. A ratio truncating to zero flips formulas — exploit it. + +**Amplify truncation.** Find division-before-multiplication chains — `(a / b) * c` where intermediate truncation is amplified by later multiplication. Trace across function boundaries where a truncated return value gets multiplied. Only flag on raw integer types (`u128`, `Balance`), NOT inside `FixedU128` or similar fixed-point wrappers. + +**Overflow intermediates.** For every `a * b / c` on `u128`, construct inputs where `a * b` overflows before the division saves it. Use large Balance values (10^18+ scale). + +**Break type conversions.** `u128 as u64`, `.into()` between Balance/BlockNumber types, `unique_saturated_into` hiding truncation. Construct realistic values that overflow the target type. + +**Exploit checked_* error handling.** Find where `checked_sub` returns `None` but the error path doesn't properly revert or returns a default that corrupts state. + +**Every finding needs concrete numbers.** Walk through the arithmetic with specific values. No numbers = LEAD. + +## Output fields + +Add to FINDINGs: +``` +proof: concrete arithmetic showing the bug with actual numbers +``` diff --git a/.claude/skills/hydration_cl0wdit/references/hacking-agents/shared-rules.md b/.claude/skills/hydration_cl0wdit/references/hacking-agents/shared-rules.md new file mode 100644 index 000000000..8bda53f11 --- /dev/null +++ b/.claude/skills/hydration_cl0wdit/references/hacking-agents/shared-rules.md @@ -0,0 +1,44 @@ +# Shared Scan Rules + +## Reading + +Your bundle has two sections: + +1. **Core source** (inline) — read in parallel chunks (offset + limit), compute offsets from the line count in your prompt. +2. **Peripheral file manifest** — file paths under `# Peripheral Files (read on demand)`. Read only those relevant to your specialty. + +When matching function names, check both public dispatchables (`#[pallet::call]`), hooks (`on_initialize`, `on_finalize`, `on_idle`, `on_runtime_upgrade`), and internal helpers they call. + +## Cross-pallet patterns + +When you find a bug in one pallet, **weaponize that pattern across every other pallet in the bundle.** Search by function name AND by code pattern. Finding unsafe arithmetic in `PalletA::do_transfer` means you check every other pallet's transfer logic — missing a repeat instance is an audit failure. + +After scanning: escalate every finding to its worst exploitable variant (DoS may hide fund theft). Then revisit every function where you found something and attack the other branches. + +## Do not report + +Admin/governance/sudo functions doing admin things. Standard DeFi tradeoffs (MEV, rounding dust). Self-harm-only bugs. "Governance can rug" without a concrete mechanism. Anything a linter or `cargo clippy` would catch. + +## Output + +Return structured blocks only — no preamble, no narration. Exception: vector scan agent outputs its classification block first. + +FINDINGs have concrete, unguarded, exploitable attack paths. LEADs have real code smells with partial paths — default to LEAD over dropping. + +**Every FINDING must have a `proof:` field** — concrete values, traces, or state sequences from the actual code. No proof = LEAD, no exceptions. + +**One vulnerability per item.** Same root cause = one item. Different fixes needed = separate items. + +``` +FINDING | pallet: Name | function: func | bug_class: kebab-tag | group_key: Pallet | function | bug-class +path: caller → extrinsic/hook → internal call → storage mutation → impact +proof: concrete values/trace demonstrating the bug +description: one sentence +fix: one-sentence suggestion + +LEAD | pallet: Name | function: func | bug_class: kebab-tag | group_key: Pallet | function | bug-class +code_smells: what you found +description: one sentence explaining trail and what remains unverified +``` + +The `group_key` enables deduplication: `PalletName | functionName | bug_class`. Agents may add custom fields. diff --git a/.claude/skills/security_audit/references/agents/test-benchmark-agent.md b/.claude/skills/hydration_cl0wdit/references/hacking-agents/test-benchmark-agent.md similarity index 100% rename from .claude/skills/security_audit/references/agents/test-benchmark-agent.md rename to .claude/skills/hydration_cl0wdit/references/hacking-agents/test-benchmark-agent.md diff --git a/.claude/skills/hydration_cl0wdit/references/hacking-agents/vector-scan-agent.md b/.claude/skills/hydration_cl0wdit/references/hacking-agents/vector-scan-agent.md new file mode 100644 index 000000000..fbc59bff9 --- /dev/null +++ b/.claude/skills/hydration_cl0wdit/references/hacking-agents/vector-scan-agent.md @@ -0,0 +1,34 @@ +# Vector Scan Agent + +You are an attacker that exploits known attack vectors from Substrate ecosystem audits. Armed with your vector bundle, grind through every one, find every manifestation in this codebase, and exploit it. + +## How to attack + +For each vector, extract the root cause and hunt ALL manifestations — different pallet names, trait implementations, storage patterns. A "saturating_sub masking insufficient balance" vector applies wherever code uses saturating arithmetic on balances. + +- Construct AND concept both absent → skip +- Guard unambiguously blocks the attack → skip +- No guard, partial guard, or guard that might not cover all paths → investigate and exploit + +For every vector worth investigating, trace the full attack path: confirm reachability from an extrinsic or hook entry point, follow cross-pallet interactions, find the gap that lets you through. + +## Break guards + +A guard only stops you if it blocks ALL paths. Find the way around: +- Reach the same state through a function without the guard +- Feed input values that slip past the `ensure!` check +- Exploit checks positioned after storage mutations (too late) +- Enter through hooks (`on_initialize`, `on_finalize`), XCM handlers, or cross-pallet calls + +## Output gate + +Your response MUST begin with the vector classification block: + +``` +Skip: V1,V2,V5 +Drop: V4,V9 +Investigate: V3,V7 +Total: 7 classified +``` + +Every vector in exactly one category. `Total` matches vector count. After the classification block, output FINDING and LEAD blocks. diff --git a/.claude/skills/hydration_cl0wdit/references/judging.md b/.claude/skills/hydration_cl0wdit/references/judging.md new file mode 100644 index 000000000..88cd55654 --- /dev/null +++ b/.claude/skills/hydration_cl0wdit/references/judging.md @@ -0,0 +1,71 @@ +# Finding Validation + +Every finding passes four sequential gates. Fail any gate → **rejected** or **demoted** to lead. Later gates are not evaluated for failed findings. + +## Gate 1 — Refutation + +Construct the strongest argument that the finding is wrong. Find the guard, check, or constraint that kills the attack — quote the exact line and trace how it blocks the claimed step. + +- Concrete refutation (specific guard blocks exact claimed step) → **REJECTED** (or **DEMOTE** if code smell remains) +- Speculative refutation ("probably wouldn't happen") → **clears**, continue + +## Gate 2 — Reachability + +Prove the vulnerable state exists in a live deployment. + +- Structurally impossible (enforced invariant prevents it) → **REJECTED** +- Requires privileged actions outside normal operation → **DEMOTE** +- Achievable through normal usage or common token/XCM behaviors → **clears**, continue + +## Gate 3 — Trigger + +Prove an unprivileged actor executes the attack. + +- Only trusted roles can trigger (sudo, governance, council, admin origin) → **DEMOTE** +- Costs exceed extraction → **REJECTED** +- Unprivileged actor triggers profitably → **clears**, continue + +## Gate 4 — Impact + +Prove material harm to an identifiable victim. + +- Self-harm only → **REJECTED** +- Dust-level, no compounding → **DEMOTE** +- Material loss to identifiable victim → **CONFIRMED** + +## Confidence + +Start at **100**, deduct: partial attack path **-20**, bounded non-compounding impact **-15**, requires specific (but achievable) state **-10**. Confidence ≥ 80 gets description + fix. Below 80 gets description only. + +## Safe patterns (do not flag) + +- `saturating_*` with explicit justification comment or preceding bounds check +- `.unwrap_or_default()` on storage reads (never panics) +- `.expect("description; qed")` with a sound preceding proof +- `#[pallet::call]` dispatchables without `#[transactional]` (implicit since FRAME v2) +- Hardcoded weights in `benchmarking.rs`, `weights.rs`, or test modules +- `ensure_none` in test/mock modules +- `SafeCallFilter = Everything` for trusted internal XCM origins +- Governance parameter changes that are by-design privileges +- Division-before-multiply inside `FixedU128` or similar fixed-point wrappers +- Bounded storage (`BoundedVec`, `BoundedBTreeMap`) with tight limits and gating deposit + +## Lead promotion + +Before finalizing leads, promote where warranted: + +- **Cross-pallet echo.** Same root cause confirmed as FINDING in one pallet → promote in every pallet where the identical pattern appears. +- **Multi-agent convergence.** 2+ agents flagged same area, lead was demoted (not rejected) → promote to FINDING at confidence 75. +- **Partial-path completion.** Only weakness is incomplete trace but path is reachable and unguarded → promote to FINDING at confidence 75, description only. + +## Leads + +High-signal trails for manual investigation. No confidence score, no fix — title, code smells, and what remains unverified. + +## Known False Positives + +Before scoring, check the finding against `known-false-positives.md` (included in your bundle). If it matches a listed pattern, drop it — do not score or report it. + +## Do Not Report + +Anything a linter, compiler, or `cargo clippy` would dismiss. Admin/governance privileges by design. Missing event emissions. Centralization without exploit path. Implausible preconditions (but fee-on-transfer via XCM, rebasing assets, freezing/thawing ARE plausible for runtimes handling arbitrary assets or XCM messages). diff --git a/.claude/skills/security_audit/references/known-false-positives.md b/.claude/skills/hydration_cl0wdit/references/known-false-positives.md similarity index 98% rename from .claude/skills/security_audit/references/known-false-positives.md rename to .claude/skills/hydration_cl0wdit/references/known-false-positives.md index da9ca478b..05e19675f 100644 --- a/.claude/skills/security_audit/references/known-false-positives.md +++ b/.claude/skills/hydration_cl0wdit/references/known-false-positives.md @@ -96,7 +96,7 @@ Before reporting a finding, check it against this list. If a finding matches a p **Pattern:** Flagging stale oracle state after asset removal and re-addition. -**Why it's wrong (when fixed):** If the runtime has been updated to clear oracle entries on `remove_token()` or reinitialize oracle state on `add_token()`, this is a known-fixed issue. Check for `OracleAccumulatorEntries::remove()` or equivalent cleanup in the remove/add flow before reporting. +**Why it's wrong (when fixed):** If the runtime has been updated to clear oracle entries on `remove_token()` or reinitialize oracle state on `add_token()`, this is a known-fixed issue. Check for oracle accumulator cleanup or equivalent in the remove/add flow before reporting. --- diff --git a/.claude/skills/security_audit/references/report-formatting.md b/.claude/skills/hydration_cl0wdit/references/report-formatting.md similarity index 70% rename from .claude/skills/security_audit/references/report-formatting.md rename to .claude/skills/hydration_cl0wdit/references/report-formatting.md index 1d8ad6f26..d6563b0ad 100644 --- a/.claude/skills/security_audit/references/report-formatting.md +++ b/.claude/skills/hydration_cl0wdit/references/report-formatting.md @@ -55,7 +55,20 @@ When `--file-output` is set, resolve the git repository root via `git rev-parse --- -< ... remaining findings ... > +< ... all above-threshold findings ... > + +--- + +[75] **3. ** + +`pallet_name::function_name` · Confidence: 75 + +**Description** +<The vulnerable code pattern and why it is exploitable, in 1 short sentence> + +--- + +< ... all below-threshold findings (description only, no Fix block) ... > --- @@ -65,9 +78,16 @@ When `--file-output` is set, resolve the git repository root via `git rev-parse |---|---|---| | 1 | [95] | <title> | | 2 | [82] | <title> | -| | | **— Below Confidence Threshold —** | -| 3 | [72] | <title> | -| 4 | [55] | <title> | +| 3 | [75] | <title> | + +--- + +## Leads + +_Vulnerability trails with concrete code smells where the full exploit path could not be completed in one analysis pass. These are not false positives — they are high-signal leads for manual review. Not scored._ + +- **<Title>** — `pallet_name::function_name` — Code smells: <missing guard, unsafe arithmetic, etc.> — <1-2 sentence description of the trail and what remains unverified> +- **<Title>** — `pallet_name::function_name` — Code smells: <...> — <1-2 sentence description> --- diff --git a/.claude/skills/security_audit/SKILL.md b/.claude/skills/security_audit/SKILL.md deleted file mode 100644 index c84daea5f..000000000 --- a/.claude/skills/security_audit/SKILL.md +++ /dev/null @@ -1,71 +0,0 @@ ---- -name: security_audit -description: Security audit of Substrate runtime built in Rust. Scans current dir by default, or a specific PR with --pr. Add --deep for adversarial reasoning. -allowed-tools: Read, Glob, Grep, WebFetch, Bash, Agent ---- - -# Substrate Security Audit - -You are the orchestrator of a parallelized security audit of a Substrate runtime and/or its pallets. Your job is to discover in-scope files, spawn scanning agents, then merge and deduplicate their findings into a single report. - -## Flags - -All flags are combinable (e.g., `--pr 123 --deep --file-output`). - -- `--pr <ref>`: Audit a specific pull request. `<ref>` can be a PR number or a full GitHub PR URL. You will be in the root of the repo. Do NOT use `gh` — fetch PR data via `WebFetch` against the GitHub API (`https://api.github.com/repos/{owner}/{repo}/pulls/{number}/files`). Parse the response to get the list of changed `.rs` files and use those as scope. -- `--deep`: Also spawn Agent 0 (adversarial reasoning, opus model). Slower and more costly. -- `--file-output` (off by default): Also write the report to a markdown file (path per `{resolved_path}/report-formatting.md`). Without this flag, output goes to the terminal only. Never write a report file unless the user explicitly passes `--file-output`. - -## Scope - -**Production code** (Agents 1–4): all `.rs` files EXCLUDING tests, benchmarks, and mocks — directories `tests/`, `benchmarking/`, `mock/` and files matching `*test*.rs`, `*mock*.rs`, `*bench*.rs`. - -**Test/benchmark code** (Agent 5): the inverse — ONLY files in `tests/`, `benchmarking/`, `mock/` or matching `*test*.rs`, `*mock*.rs`, `*bench*.rs`. - -When no `--pr` flag is given, scan the current directory. When `--pr` is given, derive scope from the PR diff. - -## Orchestration - -**Turn 1 — Discover.** Print the banner, then in the same message make parallel tool calls: -- (a) Glob for `**/references/attack-vectors/substrate-attack-vectors.md` and extract the `references/` directory path (two levels up). Use this resolved path as `{resolved_path}` for all subsequent local references. -- (b) Discover in-scope `.rs` files: - - **No `--pr`:** Two Bash `find` commands — one for production `.rs` files (excluding test/bench/mock), one for test/bench/mock `.rs` files only. - - **With `--pr`:** Use `WebFetch` to call `https://api.github.com/repos/{owner}/{repo}/pulls/{number}/files` (extract owner/repo from the git remote or the provided URL). Parse the JSON response for changed `.rs` files, then split them into production vs test/bench/mock lists using the same patterns. Do NOT use `gh`. -**Turn 2 — Prepare.** In a single message, make parallel tool calls: -- (a) Read `{resolved_path}/agents/vector-scan-agent.md` -- (b) Read `{resolved_path}/agents/test-benchmark-agent.md` -- (c) Read `{resolved_path}/report-formatting.md` -- (d) Bash: create five per-agent bundle files in a **single command**: - - `/tmp/audit-agent-1-bundle.md` — all **production** `.rs` files (with `### path` headers and fenced code blocks), then `{resolved_path}/known-false-positives.md`, then `{resolved_path}/judging.md`, then `{resolved_path}/report-formatting.md`, then `{resolved_path}/attack-vectors/hydration-attack-vectors.md`. - - `/tmp/audit-agent-2-bundle.md` — all **production** `.rs` files (same as agent 1), then `{resolved_path}/known-false-positives.md`, then `{resolved_path}/judging.md`, then `{resolved_path}/report-formatting.md`, then `{resolved_path}/attack-vectors/substrate-attack-vectors.md`. - - `/tmp/audit-agent-3-bundle.md` — all **production** `.rs` files (same as agent 1), then `{resolved_path}/known-false-positives.md`, then `{resolved_path}/judging.md`, then `{resolved_path}/report-formatting.md`, then `{resolved_path}/attack-vectors/substrate-attack-vectors-1.md`. - - `/tmp/audit-agent-4-bundle.md` — all **production** `.rs` files (same as agent 1), then `{resolved_path}/known-false-positives.md`, then `{resolved_path}/judging.md`, then `{resolved_path}/report-formatting.md`, then `{resolved_path}/attack-vectors/substrate-attack-vectors-2.md`. - - `/tmp/audit-agent-5-bundle.md` — all **test/bench/mock** `.rs` files (with `### path` headers and fenced code blocks), then a **summary of production dispatchables** (list every `#[pallet::call]` function name and its containing file from the production file list), then `{resolved_path}/known-false-positives.md`, then `{resolved_path}/judging.md`, then `{resolved_path}/report-formatting.md`. - - Print line counts for all five bundles. - -Every vector-scan agent receives the full production codebase — only the attack-vectors file differs. Agent 5 receives test/bench/mock code plus a production dispatchable summary. Do NOT read or inline any file content into agent prompts — the bundle files replace that entirely. - -**Turn 3 — Spawn.** In a single message, spawn all agents as parallel foreground Agent tool calls (do NOT use `run_in_background`). Always spawn Agents 1–5. Only spawn Agent 0 when `--deep` is set. - -- **Agent 1** (Hydration vectors) — spawn with `model: "sonnet"`. Prompt must contain the full text of `vector-scan-agent.md` (read in Turn 2, paste into prompt). After the instructions, add: `Your bundle file is /tmp/audit-agent-1-bundle.md (XXXX lines).` (substitute the real line count). -- **Agent 2** (Substrate & Rust vectors) — spawn with `model: "sonnet"`. Same as Agent 1 but: `Your bundle file is /tmp/audit-agent-2-bundle.md (XXXX lines).` -- **Agent 3** (Runtime config, XCM & weight vectors) — spawn with `model: "sonnet"`. Same as Agent 1 but: `Your bundle file is /tmp/audit-agent-3-bundle.md (XXXX lines).` -- **Agent 4** (DeFi, access control & crypto vectors) — spawn with `model: "sonnet"`. Same as Agent 1 but: `Your bundle file is /tmp/audit-agent-4-bundle.md (XXXX lines).` -- **Agent 5** (Test & benchmark) — spawn with `model: "sonnet"`. Prompt must contain the full text of `test-benchmark-agent.md` (read in Turn 2, paste into prompt). After the instructions, add: `Your bundle file is /tmp/audit-agent-5-bundle.md (XXXX lines).` -- **Agent 0** (adversarial reasoning, `--deep` only) — spawn with `model: "opus"`. Receives the in-scope production `.rs` file paths and the instruction: your reference directory is `{resolved_path}`. Read `{resolved_path}/agents/adversarial-reasoning-agent.md` for your full instructions. - -**Turn 4 — Report.** Merge all agent results (up to 6 agents): deduplicate by root cause (keep the higher-confidence version), sort by confidence highest-first, re-number sequentially, and insert the **Below Confidence Threshold** separator row. Print findings directly — do not re-draft or re-describe them. Use report-formatting.md (read in Turn 2) for the scope table and output structure. If `--file-output` is set, write the report to a file (path per report-formatting.md) and print the path. - -## Banner - -Before doing anything else, print this exactly: - -``` - oooo .oooo. .o8 o8o . - `888 d8P'`Y8b "888 `"' .o8 - .ooooo. 888 888 888 oooo oooo ooo .oooo888 oooo .o888oo -d88' `"Y8 888 888 888 `88. `88. .8' d88' `888 `888 888 -888 888 888 888 `88..]88..8' 888 888 888 888 -888 .o8 888 `88b d88' `888'`888' 888 888 888 888 . -`Y8bod8P' o888o `Y8bd8P' `8' `8' `Y8bod88P" o888o "888" -``` diff --git a/.claude/skills/security_audit/references/agents/adversarial-reasoning-agent.md b/.claude/skills/security_audit/references/agents/adversarial-reasoning-agent.md deleted file mode 100644 index 18bcbf190..000000000 --- a/.claude/skills/security_audit/references/agents/adversarial-reasoning-agent.md +++ /dev/null @@ -1,15 +0,0 @@ -# Adversarial Reasoning Agent Instructions - -You are an adversarial security researcher trying to exploit this Substrate runtime and its pallets. There are bugs here — find them. Your goal is to find every way to steal funds, lock funds, grief users, or break invariants. Do not give up. If your first pass finds nothing, assume you missed something and look again from a different angle. - -## Critical Output Rule - -You communicate results back ONLY through your final text response. Do not output findings during analysis. Collect all findings internally and include them ALL in your final response message. Your final response IS the deliverable. Do NOT write any files — no report files, no output files. Your only job is to return findings as text. - -## Workflow - -1. Read all in-scope `.rs` files, plus `judging.md` and `report-formatting.md` from the reference directory provided in your prompt, in a single parallel batch. Do not use any attack vector reference files. -2. Reason freely about the code — look for logic errors, unsafe storage access patterns, origin/permission gaps, arithmetic overflows, unbounded iterations, weight underestimation, cross-pallet interaction flaws, hook execution hazards (`on_initialize` running without user origin), XCM handler exploits, and any other vulnerability you can construct a concrete attack path for. For each potential finding, apply the FP gate from `judging.md` immediately (three checks). If any check fails → drop and move on without elaborating. Only if all three pass → trace the full attack path, apply score deductions, and format the finding. -3. Your final response message MUST contain every finding **already formatted per `report-formatting.md`** — indicator + bold numbered title, location · confidence line, **Description** with one-sentence explanation, and **Fix** with diff block (omit fix for findings below 80 confidence). Use placeholder sequential numbers (the main agent will re-number). -4. Do not output findings during analysis — compile them all and return them together as your final response. -5. If you find NO findings, respond with "No findings." diff --git a/.claude/skills/security_audit/references/agents/vector-scan-agent.md b/.claude/skills/security_audit/references/agents/vector-scan-agent.md deleted file mode 100644 index 18d9d477c..000000000 --- a/.claude/skills/security_audit/references/agents/vector-scan-agent.md +++ /dev/null @@ -1,26 +0,0 @@ -# Vector Scan Agent Instructions - -You are a security auditor scanning Substrate pallets and Rust runtime code for vulnerabilities. There are bugs here — your job is to find every way to steal funds, lock funds, grief users, or break invariants. Do not accept "no findings" easily. - -## Critical Output Rule - -You communicate results back ONLY through your final text response. Do not output findings during analysis. Collect all findings internally and include them ALL in your final response message. Your final response IS the deliverable. Do NOT write any files — no report files, no output files. Your only job is to return findings as text. - -## Workflow - -1. Read your bundle file in **parallel 1000-line chunks** on your first turn. The line count is in your prompt — compute the offsets and issue all Read calls at once (e.g., for a 5000-line file: `Read(file, limit=1000)`, `Read(file, offset=1000, limit=1000)`, `Read(file, offset=2000, limit=1000)`, `Read(file, offset=3000, limit=1000)`, `Read(file, offset=4000, limit=1000)`). Do NOT read without a limit. These are your ONLY file reads — do NOT read any other file after this step. -2. **Triage pass.** For each vector, classify into three tiers: - - **Skip** — the named construct AND underlying concept are both absent (e.g., ERC721 vectors when there are no NFTs or cross-chain asset transfers at all; "ERC777 reentrancy" when there are no EVM precompiles or cross-contract calls). - - **Borderline** — the named construct is absent but the underlying vulnerability concept could manifest through a different mechanism in this codebase (e.g., "unbounded decoding" when a pallet accepts user-supplied `Vec<T>` but doesn't use `BoundedVec` or `decode_with_depth_limit`; "stale cached balance" when the code caches cross-pallet reserve state without re-reading). - - **Survive** — the construct or pattern is clearly present (e.g., "unsafe arithmetic" when `+`/`-` operators are used on Balance types without `checked_*` or `saturating_*` wrappers). - Output all three tiers — every vector must appear in exactly one: `Skip: V1, V2 ...`, `Surviving: V3, V16 ...`, `Borderline: V8, V22 ...`. End with `Total: N classified` and verify it matches your vector count. Borderline vectors get a 1-sentence relevance check: only promote if you can (a) name the specific function where the concept manifests AND (b) describe in one sentence how the exploit would work; otherwise drop. -3. **Deep pass.** Only for surviving vectors. Use this **structured one-liner format** for each vector's analysis — do NOT write free-form paragraphs: - ``` - V15: path: transfer() → do_transfer() → Balance::set() without saturating_sub | guard: none | verdict: CONFIRM [85] - V22: path: withdraw() → T::Currency::withdraw() | guard: ensure_signed + ensure!(amount <= free_balance) | verdict: DROP (FP gate 3: guarded) - ``` - For each vector: trace the call chain from external entry point (dispatchable extrinsic via `#[pallet::call]`, hook like `on_initialize`/`on_finalize`/`on_idle`, inherent, or XCM handler) to the vulnerable line — check every origin check (`ensure_signed`, `ensure_root`, custom origin), weight annotation (`#[pallet::weight]`), transactional wrapper (`#[transactional]` or `with_transaction`), and state guard. Consider alternate manifestations, not just the literal construct named. Confirm the path involves a state-changing entry point (not a non-dispatchable helper or `#[pallet::getter]` function). If no match or FP conditions fully apply → DROP in one line (never reconsider). If match → apply the FP gate from `judging.md` (three checks). If any check fails → DROP in one line. Only if all three pass → write CONFIRM with score deductions, then expand into the formatted finding below. **Budget: ≤1 line per dropped vector, ≤3 lines per confirmed vector before its formatted finding.** -4. **Composability check.** Only if you have 2+ confirmed findings: do any two compound (e.g., missing weight + unbounded iteration = block stuffing DoS; origin bypass + unsafe arithmetic = total fund drain)? If so, note the interaction in the higher-confidence finding's description. -5. Your final response message MUST contain every finding **already formatted per `report-formatting.md`** — indicator + bold numbered title, location · confidence line, **Description** with one-sentence explanation, and **Fix** with diff block (omit fix for findings below 80 confidence). Use placeholder sequential numbers (the main agent will re-number). -6. Do not output findings during analysis — compile them all and return them together as your final response. -7. **Hard stop.** After the deep pass, STOP — do not re-examine eliminated vectors, scan outside your assigned vector set, or "revisit"/"reconsider" anything. Output your formatted findings, or "No findings." if none survive. diff --git a/.claude/skills/security_audit/references/judging.md b/.claude/skills/security_audit/references/judging.md deleted file mode 100644 index e8d9732ad..000000000 --- a/.claude/skills/security_audit/references/judging.md +++ /dev/null @@ -1,37 +0,0 @@ -# Finding Validation - -Each finding passes a false-positive gate, then gets a confidence score (how certain you are it is real). - -## FP Gate - -Every finding must pass all three checks. If any check fails, drop the finding — do not score or report it. - -1. You can trace a concrete attack path: caller → extrinsic/hook → internal call chain → storage mutation → loss/impact. Evaluate what the code _allows_, not what the deployer _might configure_. -2. The entry point is reachable by the attacker (check origin requirements: `ensure_signed`, `ensure_root`, custom origin filters, `T::AdminOrigin`, governance-gated calls). -3. No existing guard already prevents the attack (`ensure!`, `frame_support::transactional`, `checked_*`/`saturating_*` arithmetic, `BoundedVec` limits, weight metering, etc.). - -## Confidence Score - -Confidence measures certainty that the finding is real and exploitable — not how severe it is. Every finding that passes the FP gate starts at **100**. - -**Deductions (apply all that fit):** - -- Privileged caller required (sudo, governance, admin origin, council) → **-25**. -- Attack path is partial (general idea is sound but cannot write exact caller → extrinsic → storage mutation → outcome) → **-20**. -- Impact is self-contained (only affects the attacker's own funds/state, no spillover to other users) → **-15**. - -Confidence indicator: `[score]` (e.g., `[95]`, `[75]`, `[60]`). - -Findings below the confidence threshold (default 75) are still included in the report table but do not get a **Fix** section — description only. - -## Known False Positives - -Before scoring, check the finding against `known-false-positives.md` (included in your bundle). If it matches a listed pattern, drop it — do not score or report it. - -## Do Not Report - -- Anything a linter, compiler (`cargo clippy`), or seasoned Rust developer would dismiss — INFO-level notes, minor style issues, naming conventions, missing doc comments. -- Sudo/root/governance can set parameters, pause pallets, or upgrade runtime — these are by-design privileges, not vulnerabilities. -- Missing event emissions or insufficient logging. -- Centralization observations without a concrete exploit path (e.g., "sudo could drain treasury" with no specific mechanism beyond inherent trust assumptions). -- Theoretical issues requiring implausible preconditions (e.g., compromised collator/validator set, >50% token supply held by attacker, corrupted WASM blob). Note: common token behaviors (fee-on-transfer via XCM, rebasing assets, freezing/thawing) and cross-chain message manipulation are NOT implausible — if the runtime handles arbitrary assets or XCM messages, these are valid attack surfaces. From 5c3d0af9070543f5d4c1b34a7164a3826ae62d3c Mon Sep 17 00:00:00 2001 From: cl0w <v@lery.dev> Date: Sun, 19 Apr 2026 17:25:46 +0200 Subject: [PATCH 2/3] add skill version to report template --- .claude/skills/hydration_cl0wdit/references/report-formatting.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.claude/skills/hydration_cl0wdit/references/report-formatting.md b/.claude/skills/hydration_cl0wdit/references/report-formatting.md index d6563b0ad..c907a3ed8 100644 --- a/.claude/skills/hydration_cl0wdit/references/report-formatting.md +++ b/.claude/skills/hydration_cl0wdit/references/report-formatting.md @@ -15,6 +15,7 @@ When `--file-output` is set, resolve the git repository root via `git rev-parse | Field | Value | |---|---| +| **Skill version** | hydration_cl0wdit vX.Y.Z | | **Mode** | ALL / default / filename | | **In-scope files** | `pallet_foo/src/lib.rs` · `pallet_bar/src/lib.rs`<br>`runtime/src/lib.rs` | <!-- list every file, 2-3 per line --> | **Confidence threshold (1–100)** | N | From 51d63ad7dfdeac4e09cdaae61fc08b3a43a9f187 Mon Sep 17 00:00:00 2001 From: cl0w <v@lery.dev> Date: Sun, 19 Apr 2026 17:39:24 +0200 Subject: [PATCH 3/3] dont write interim reports --- .../hydration_cl0wdit/references/hacking-agents/shared-rules.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.claude/skills/hydration_cl0wdit/references/hacking-agents/shared-rules.md b/.claude/skills/hydration_cl0wdit/references/hacking-agents/shared-rules.md index 8bda53f11..5a4bcfc49 100644 --- a/.claude/skills/hydration_cl0wdit/references/hacking-agents/shared-rules.md +++ b/.claude/skills/hydration_cl0wdit/references/hacking-agents/shared-rules.md @@ -21,6 +21,8 @@ Admin/governance/sudo functions doing admin things. Standard DeFi tradeoffs (MEV ## Output +**Do NOT write any files.** No report files, no output files. Return findings as text in your final response only. The orchestrator handles all file output. + Return structured blocks only — no preamble, no narration. Exception: vector scan agent outputs its classification block first. FINDINGs have concrete, unguarded, exploitable attack paths. LEADs have real code smells with partial paths — default to LEAD over dropping.