test(reentrancy): ERC1155-callback reentrancy regression for both vault types - #325
Conversation
…r both vaults Adds fuzz-backed regression tests for the reentrancy surface documented in issue #316 (rows 1/2/7 of the master call-site table): a malicious receiver that re-enters vault.deposit from inside onERC1155Received is rejected by the nonReentrant guard; the outer deposit reverts with ReentrancyGuardReentrantCall. Positive-control test confirms the same setup deposits successfully when the receiver is not reentrant. Covers OffchainAssetReceiptVault and ERC20PriceOracleReceiptVault. Closes #316. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 28 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
WalkthroughAdds Solidity reentrancy test doubles and regression suites for ERC20, oracle, receipt callback, authorizer, certification, share transfer, ETH refund, confiscation, and payment-mint flows. ChangesReentrancy regression tests
Estimated code review effort: 4 (Complex) | ~45 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
🤖 ai:vetter |
|
Rework note (human): incomplete coverage of #316. The issues test-coverage checklist enumerates ~7 distinct reentry categories; this PR covers only the ERC1155 deposit-callback rows (1/2/7, per the PR body). Still uncovered: asset-ERC20 transfer hooks (rows 8/9 — the asset mock bypasses them), payment-token reentry (row 19), ETH-refund |
…ocks Extends the ERC1155-callback reentrancy regression suite (rows 1/2/7) to the remaining attacker-controllable reentry rows of the issue #316 call-site table, each exercised against a REAL reentrant dependency rather than an always-succeed mock: - Rows 8/9: ReentrantAsset — a genuine hooked ERC20 vault asset whose transferFrom (_beforeDeposit pull) and transfer (_afterWithdraw push) re-enter vault.deposit; both revert on the shared nonReentrant guard. - Row 11: EthRefundReenterDepositor — re-enters deposit from receive() when the post-guard ETH refund lands; both deposits land independently under distinct receipt ids (benign, per the audit; hardening in #315). - Rows 12/13: ReentrantAuthorizer — swapped authorizer re-enters deposit from the authorizeReceiptTransfer3 and _afterDeposit authorize call sites; both revert on the guard held by _deposit. - Row 14: CertifyObserverAuthorizer — observes from inside the CERTIFY authorize callback that certifiedUntil is already written (certify is unguarded; effects-before-interaction is the protection). - Row 15: ReentrantShareTransferAuthorizer — re-enters transferFrom in the pre-balance-write authorize window; the double-move underflows with ERC20InsufficientBalance, proving no overspend. - Row 16: ReentrantConfiscator — re-enters confiscateReceipt from the ERC1155 acceptance callback; reverts on the nonReentrant guard. - Row 19: payment-mint with ReentrantAsset as the payment token; the reentrant payment pull reverts on the guard, supply cap intact. - Row 20: ReentrantOracle — re-enters deposit from the pre-guard _nextId price read; each deposit locks its own fresh price/receipt id. Every test is mutation-validated: removing the guard under test (or reordering the protected effect) makes exactly the claiming tests fail, restoring makes them pass. Co-Authored-By: Claude <noreply@anthropic.com>
|
🤖 ai:producer Producer note: rework executed — rows covered (each against a REAL reentrant dependency, all mutation-validated):
Rows found inapplicable: none. Mutation validation (7 mutants, each killed by exactly the claiming tests, restored after): Local suite: 392 passed, 1 pre-existing env-only failure ( |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.pre-commit-config.yaml:
- Line 1: The pre-commit configuration is an environment-specific symlink into
the Nix store and is not portable. Replace the `.pre-commit-config.yaml` symlink
with the actual repository configuration, or add setup logic that generates the
symlink locally while keeping a portable config tracked in the repository.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 35e762fd-3b6d-4d82-8aaa-7a6ef1e6c38d
📒 Files selected for processing (11)
.pre-commit-config.yamltest/concrete/CertifyObserverAuthorizer.soltest/concrete/EthRefundReenterDepositor.soltest/concrete/ReentrantAsset.soltest/concrete/ReentrantAuthorizer.soltest/concrete/ReentrantConfiscator.soltest/concrete/ReentrantOracle.soltest/concrete/ReentrantShareTransferAuthorizer.soltest/src/concrete/vault/ERC20PriceOracleReceiptVault.reentrant.t.soltest/src/concrete/vault/OffchainAssetReceiptVault.reentrant.t.soltest/src/concrete/vault/OffchainAssetReceiptVaultPaymentMint.reentrant.t.sol
Co-Authored-By: Claude <noreply@anthropic.com>
The dev shell rewrote the tracked symlink to a machine-local nix store path; this diff never intended to touch it. Restored to main's target. Co-Authored-By: Claude <noreply@anthropic.com>
|
🤖 ai:vetter |
…rsion The branch predates main's rain-factory 0.1.1 -> 0.1.5 bump. Every other file already imports through the 0.1.5 prefix; this one still used 0.1.1, which soldeer no longer installs, so the whole suite failed to compile.
…1.5 exposes rain-factory 0.1.5 removed `clone()` in favour of `cloneDeterministic()` with an explicit salt. The factory is constructed fresh inside this helper, so salt 0 cannot collide -- matching the sibling construct test in test/src/concrete/authorize/.
|
Reviewed 6ebc80e: approve Merge-updated and fixed two version-drift breaks before merging. Both were introduced by the branch being five weeks stale, not by the tests themselves. What I fixed, and how I nearly missed itThe first CI run after the merge-update showed one red check, and my initial read — "every The actual error, caused by this branch:
rain-factory 0.1.5 removed Now the red really is environmental, demonstrated rather than assertedOn the final run: 0 The testsTwelve functions across three files, driven by seven purpose-built attacker contracts — and every fixture is referenced, none is scaffolding. The body claims "rows 1/2/7" of #316's table and badly understates itself; the suite reaches rows 8, 9, 11, 12, 14, 15 and 20 as well. The best property is that the test names pin the audit's reasoning, not merely "does not revert". Row 14's mitigation is "
Scope of
|
Summary
Adds fuzz-backed reentrancy regression tests covering the ERC1155 acceptance-callback surface documented in issue #316 (rows 1/2/7 of the master reentrancy call-site table).
OffchainAssetReceiptVault.reentrant.t.sol:testDepositReentrancyGuardFires— a malicious receiver that re-entersvault.depositfrom insideonERC1155Receivedis blocked by thenonReentrantguard; the outer deposit reverts withReentrancyGuardReentrantCall.testDepositSucceedsWithNonReentrantReceiver— positive control confirming the setup is valid.ERC20PriceOracleReceiptVault.reentrant.t.sol: same two tests for the oracle vault variant, exercising the base_beforeDepositERC20 transfer path in addition to the receipt callback.Both tests run 256 fuzz rounds locally (all pass).
No production code changes — test-only addition.
Closes #316
Test plan
forge test --match-path "test/src/concrete/vault/*reentrant*"— 4/4 pass, 256 fuzz rounds each🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Tests