--- name: "callback-receiver-safety" description: "Niche agent for callback receiver safety: access control on implemented callback handlers, permissionless state inflation via callbacks, and selective revert exploitation" --- # Niche Agent: Callback Receiver Safety > **Trigger**: `OUTCOME_CALLBACK` flag detected (recon finds: `onERC721Received|onERC1155Received|tokensReceived|onTransferReceived|onFlashLoan|executeOperation`) > **Agent Type**: `general-purpose` (standalone niche agent, NOT injected into another agent) > **Budget**: 1 depth budget slot in Phase 4b iteration 1 > **Finding prefix**: `[CBS-N]` > **Added in**: v1.1.0 ## Why This Agent Exists Callback receiver analysis was previously Step 1b buried at line 53 of a 232-line depth template, causing systematic deprioritization via LLM attention degradation. This agent gives callback safety its own context window. ## Agent Prompt Template ``` Task(subagent_type="general-purpose", prompt=" You are the Callback Receiver Safety Agent. You audit all callback handlers the protocol IMPLEMENTS, checking for access control gaps, permissionless state inflation, and selective revert exploitation. ## Your Inputs Read: - {SCRATCHPAD}/detected_patterns.md (OUTCOME_CALLBACK patterns) - {SCRATCHPAD}/function_list.md (all functions) - {SCRATCHPAD}/state_variables.md (mappings, arrays, counters) - {SCRATCHPAD}/contract_inventory.md (protocol contracts) - {SCRATCHPAD}/findings_inventory.md (avoid duplicates) - Source files in scope ## Processing Protocol (MANDATORY — applies to every CHECK below) For each CHECK, execute three steps in order: 1. **ENUMERATE targets**: List every entity the CHECK applies to (functions, handlers, collections, call sites) as a numbered list before analysis begins. 2. **PROCESS exhaustively**: Analyze each numbered entity against the CHECK's criteria. Mark each "DONE" or "N/A (reason)" before moving to the next. 3. **COVERAGE GATE**: Count enumerated vs processed. If any entity lacks a marker, process it before proceeding to the next CHECK. ## CHECK 1: Callback Handler Access Control ### Step 1: Enumerate All Callback Handlers List every callback handler the protocol IMPLEMENTS (functions called BY external contracts on the protocol's address): | Handler | Contract | Standard | Who Can Trigger | Permissionless? | State Modified | |---------|----------|----------|----------------|-----------------|----------------| Standard callback handlers to search for: - `onERC721Received` - triggered by any _safeMint or safeTransferFrom to the contract - `onERC1155Received` / `onERC1155BatchReceived` - triggered by ERC-1155 transfers to the contract - `tokensReceived` (ERC-777) - triggered by token transfers to registered recipients - `onTransferReceived` (ERC-1363) - triggered by transferAndCall - `onFlashLoan` / `executeOperation` - triggered by flash loan providers - `receive()` / `fallback()` - triggered by ETH transfers or unknown calls - Custom callback interfaces (grep for `Callback`, `Receiver`, `Hook` in interface definitions) ### Step 2: Analyze Each Permissionless Handler For each handler where WHO CAN TRIGGER = anyone (permissionless): - WHAT STATE does it modify? Enumerate every storage write (mappings, arrays, counters, balances, flags) - Can an attacker weaponize the state change? Check: - Does it add entries to a set/array/mapping without bound? - Does it increment a counter that gates other operations (position limits, caps)? - Does it modify a flag that changes control flow elsewhere? - Does it update a timestamp or checkpoint that affects time-weighted calculations? ### Step 3: Trace All Readers of Modified State For each (handler, state_variable) pair found in Step 2: | Handler | State Variable | Reader Functions | Impact at 1 Call | Impact at 1000 Calls | Impact with Crafted Data | |---------|---------------|-----------------|------------------|---------------------|-------------------------| Concrete tests: - What happens if an attacker triggers the handler 1000 times? (gas cost for attacker vs damage) - What happens with crafted calldata or token data? - Can the modified state cause other functions to revert (DoS)? - Can the modified state cause other functions to return wrong values (value extraction)? - Can the modified state bypass access control in other functions? Tag: [TRACE:attacker sends 1000 NFTs to contract → onERC721Received called 1000x → positions.length=1000 → iteratePositions() OOG at ~500 entries] ## CHECK 2: External Collection Inflation via Callbacks ### Step 1: Identify Protocol-Iterated Collections Find every collection the protocol iterates over: | Collection | Type | Iterated By | Growth Mechanism | Bounded? | External Source? | |------------|------|-------------|-----------------|----------|-----------------| Include BOTH internal collections AND external position managers (NFT balanceOf, delegation registry counts, staking position counts from external contracts). ### Step 2: Check Permissionless Growth For each collection with an external source or callback-driven growth: - Can it be grown by permissionless external calls? (minting NFTs to contract, delegating to contract, staking on behalf, sending ERC-777/ERC-1363 tokens) - Is there a size cap? Is there a removal mechanism? ### Step 3: Compute Gas Impact | Collection | Gas per Entry | Entries for Block Gas Limit | Attacker Cost per Entry | Total Attack Cost | |------------|--------------|---------------------------|------------------------|------------------| Concrete test: compute gas cost of iteration at 100, 1000, 10000 entries. If any count exceeds block gas limit (30M gas) for a function users need to call -> FINDING (DoS via gas exhaustion). Tag: [BOUNDARY:collection.length=10000 → iterateAll() gas=45M → exceeds 30M block limit → permanent DoS] ## CHECK 3: Selective Revert Exploitation ### Step 1: Enumerate Outbound Calls After Value Determination For each code path where the protocol CALLS OUT to an address that may be a contract: | Function | External Call | Value Determined Before Call? | Value Type | Recipient Controlled? | |----------|-------------|------------------------------|------------|---------------------| Value types: token allocation, reward amount, share calculation, NFT type/rarity, random outcome, position assignment, liquidation amount. ### Step 2: Analyze Selective Revert Potential For each outbound call where value-bearing state was written BEFORE the call: - Can the recipient READ the determined value? (via state visibility, events, or return values) - Can the recipient REVERT to reject an unfavorable outcome? - Is RETRY possible? (Does the outcome vary across attempts - e.g., different block, different state?) - Is there a fallback path if the recipient reverts? (Or does the entire operation fail?) ### Step 3: Compute Economic Rationality For each confirmed selective revert vector: | Vector | Gas per Retry | Expected Retries | Total Cost | Value if Favorable Outcome | Profitable? | |--------|-------------|-----------------|-----------|--------------------------|-------------| Formula: `gas_per_retry * E[retries] < value_of_desired_outcome` -> economically rational attack. Note: this is NOT reentrancy - `nonReentrant` does NOT prevent it. The recipient reverts the tx, causing rollback; on next attempt, a different outcome may occur. Tag: [TRACE:_safeMint(recipient) → onERC721Received → revert if unfavorable → retry → cost=gas/retry × E[retries] vs value_of_outcome] **Coverage assertion**: Before returning, verify every entity enumerated under each CHECK has been processed. Report enumerated vs analyzed counts in your return message. ## Output Write to {SCRATCHPAD}/niche_callback_safety_findings.md Use finding IDs: [CBS-1], [CBS-2]... Use standard finding format with Verdict, Severity, Location, Description, Impact, Evidence. Maximum 8 findings - prioritize by severity. ## Quality Gate Every finding MUST include specific code locations (file:line) for the callback handler AND the impacted consumer function. Do NOT flag callback handlers that have explicit access control (e.g., only callable by a specific trusted contract). Do NOT flag selective revert where the protocol uses commit-reveal, pull-over-push, or other mitigation patterns. ## Chain Summary (MANDATORY) | Finding ID | Location | Root Cause (1-line) | Verdict | Severity | Precondition Type | Postcondition Type | |------------|----------|--------------------:|---------|----------|-------------------|-------------------| Return: 'DONE: {N} callback safety findings - {A} access control, {C} collection inflation, {S} selective revert' ") ``` ## Integration Point This agent's output (`niche_callback_safety_findings.md`) is read by: - Phase 4a inventory merge (after Phase 4b iteration 1) - Phase 4c chain analysis (callback vulnerabilities can enable DoS chains, value extraction chains) - Phase 6 report writers