ability-analysis
Trigger Pattern Always (Aptos Move) - foundational security check - Inject Into Breadth…
Trigger REENTRANCY flag detected (dynamic dispatch, closures, dispatchable FA, function values) - Used by Breadth agents, depth-state-trace
$ npx -y skills add PlamenTSV/plamen --skill reentrancy-analysis --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/reentrancy-analysisContext preview
The summary Claude sees to decide when to auto-load this skill.
Trigger REENTRANCY flag detected (dynamic dispatch, closures, dispatchable FA, function values) - Used by Breadth agents, depth-state-trace
name: "reentrancy-analysis" description: "Trigger REENTRANCY flag detected (dynamic dispatch, closures, dispatchable FA, function values) - Used by Breadth agents, depth-state-trace"
> **Trigger**: REENTRANCY flag detected (dynamic dispatch, closures, dispatchable FA, function values) > **Used by**: Breadth agents, depth-state-trace > **Covers**: Cross-module reentrancy via closures, dispatchable FA hook reentrancy, direct/indirect reentrancy, resource lock gaps
Audit reentrancy vectors in Aptos Move. Historically, Move's linear type system and static dispatch prevented reentrancy. Post Move 2.2, function values (closures) and dispatchable FungibleAsset hooks introduce dynamic dispatch, creating reentrancy surfaces analogous to EVM callbacks but with different mechanics and mitigations.
**Pre Move 2.2**: No dynamic dispatch. All function calls are statically resolved at compile time. Reentrancy was architecturally impossible (no callbacks, no external calls to untrusted code).
**Post Move 2.2**: Two reentrancy vectors exist: 1. **Function values / closures**: `|arg| { body }` syntax allows passing executable code as parameters. A module calling a user-supplied closure can be reentered. 2. **Dispatchable FungibleAsset hooks**: `withdraw`, `deposit`, and `derived_balance` hooks execute external module code during FA operations. This is framework-level dynamic dispatch.
**`#[module_lock]`**: Prevents INDIRECT reentrancy (cross-module reentry into the locked module). Does NOT prevent DIRECT reentrancy (closure calling back into the same module's function within the same execution frame).
Find ALL uses of dynamic dispatch in the audited modules:
**MANDATORY SEARCH**: Grep all `.move` files for: 1. `|` followed by parameter patterns (closure syntax: `|x| { ... }`, `|x, y| { ... }`) 2. Function types in signatures (e.g., `callback: |u64| -> u64`, `FunctionValue`) 3. `move |` (move closures that capture variables) 4. Functions that accept function-typed parameters
| # | Module | Function | Dynamic Dispatch Type | Caller-Controlled? | Reentrancy Risk | |---|--------|----------|----------------------|-------------------|----------------| | 1 | {module} | {func} | Closure parameter | YES/NO | {assess} | | 2 | {module} | {func} | Stored function value | YES/NO | {assess} |
**MANDATORY SEARCH**: Grep for: 1. `dispatchable_fungible_asset` module usage 2. `register_dispatch_functions` or equivalent hook registration 3. `withdraw_with_*`, `deposit_with_*` function patterns 4. `derived_balance` implementations
| # | Module | Hook Type | Registered Function | External Code Executed? | |---|--------|-----------|--------------------|-----------------------| | 1 | {module} | withdraw | {module::withdraw_hook} | YES - at every withdrawal | | 2 | {module} | deposit | {module::deposit_hook} | YES - at every deposit | | 3 | {module} | derived_balance | {module::balance_hook} | YES - at every balance query |
For each module containing dynamic dispatch points:
| Module | Has `#[module_lock]`? | Public Entry Points | Protected by Lock? | Direct Reentry Possible? | |--------|---------------------|--------------------|--------------------|------------------------| | {module} | YES/NO | {list entry/public functions} | YES/NO | {YES if lock present - lock prevents indirect but not direct} |
**CRITICAL DISTINCTION**:
**Check**: For each module WITHOUT `#[module_lock]`: 1. Does it have any dynamic dispatch points (from Step 1)? 2. If YES: cross-module reentrancy is possible - trace all paths.
If the audited module stores data in a third-party resource abstraction:
| Data Structure | Provided By Module | Our Module Uses | Third-Party Lock Protects Us? | |---------------|-------------------|----------------|------------------------------| | SmartTable | aptos_std | YES/NO | NO - their lock protects THEIR invariants, not ours | | Table | aptos_std | YES/NO | NO | | {custom_struct} | {third_party} | YES/NO | NO |
**Pattern**: Module A stores its accounting data in a SmartTable (from `aptos_std`). `aptos_std` may have `#[module_lock]`. But this lock only prevents reentry into `aptos_std` - it does NOT prevent reentry into Module A. An attacker can reenter Module A while Module A's SmartTable operation is in progress.
**Check**: Does the protocol rely on a third-party module's lock for its own reentrancy protection? If YES -> FINDING.
For each dynamic dispatch point identified in Step 1:
| Dispatch Point | State READ Before Dispatch | State MODIFIED Before Dispatch | State Modified AFTER Dispatch | |---------------|--------------------------|------------------------------|------------------------------| | {func:line} | {variables/resources read} | {variables/resources written} | {variables/resources written} |
For each dispatch point where state is modified before dispatch:
1. Function entry: Read state S1 (e.g., user_balance = 100) 2. Modify state: S1 partially updated (e.g., user_balance -= 50, but total_supply not yet updated) 3. Dynamic dispatch: closure/hook executes 4. REENTRY: Attacker calls back into same module 5. Reentrant call reads: S1 (modified) - sees user_balance = 50 6. Reentrant call reads: S2 (NOT yet modified) - sees stale total_supply = 1000 (shou
Autonomous Web3 security auditor for Claude Code and OpenAI Codex CLI. Orchestrates 18-100 AI agents across 40+ phases to produce audit reports with verified PoC exploits — for smart contracts and L1 node-client infrastructure.
Repo: PlamenTSV/plamen
Trigger Pattern Always (Aptos Move) - foundational security check - Inject Into Breadth…
Trigger Pattern Always (Aptos Move) - Move VM aborts on shift = bit width - Inject Into…
Trigger Protocol has privileged roles (admin, operator, governance, resource account owner) -…
Trigger EXTERNAL_LIB flag detected (protocol uses third-party Move dependencies) - Used by…
Trigger Pattern MONETARY_PARAMETER flag (required) - Inject Into Breadth agents (merged via…