Description
LibFlow.flow is documented (src/lib/LibFlow.sol:137) as DOES NOT prevent reentrancy attacks — the caller's responsibility. The only concrete caller is Flow.flow (src/concrete/Flow.sol:147), which has nonReentrant. The reentrancy surface has four entry points, each able to call back into the same flow contract during a transfer:
interpreterStore.set — external call to an arbitrary store contract chosen by the flow deployer. A malicious store could re-enter flow.flow(...) from inside set.
- ERC721
safeTransferFrom — invokes onERC721Received on a contract recipient. A malicious recipient could re-enter from the hook.
- ERC1155
safeTransferFrom — invokes onERC1155Received (and onERC1155BatchReceived) on a contract recipient. Same pattern.
- ERC20
safeTransfer / safeTransferFrom — non-reentrant for compliant ERC20s, but ERC777 (which presents an ERC20 interface) invokes tokensToSend on the sender and tokensReceived on the recipient via the ERC1820 registry.
Path 4 is covered by testFlowReentrancyGuardFiresOnTokenCallback in #446 (MaliciousReenteringToken.transferFrom re-enters flow.flow). Paths 1–3 are uncovered.
Required tests
For each of paths 1–3, deploy a minimal helper contract whose callback re-enters flow.flow(...) and assert that the inner call reverts with "ReentrancyGuard: reentrant call" (OZ ReentrancyGuardUpgradeable v4 string).
MaliciousReenteringStore — implements IInterpreterStoreV2.set to re-enter on each call. Assert revert when the flow's store is set to this address and the evaluable returns non-empty kvs.
MaliciousReenteringERC721Recipient — implements IERC721Receiver.onERC721Received to re-enter. Assert revert when an ERC721 transfer targets this address.
MaliciousReenteringERC1155Recipient — implements IERC1155Receiver.onERC1155Received to re-enter. Assert revert when an ERC1155 transfer targets this address.
Each new helper goes in test/concrete/ per project convention. Mutation-test by removing nonReentrant from Flow.flow and confirming each test fails.
Severity
LOW — the reentrancy guard exists and PR #446 demonstrates it works for one path. The remaining three paths are part of the same attack surface and should be locked down equally.
Description
LibFlow.flowis documented (src/lib/LibFlow.sol:137) as DOES NOT prevent reentrancy attacks — the caller's responsibility. The only concrete caller isFlow.flow(src/concrete/Flow.sol:147), which hasnonReentrant. The reentrancy surface has four entry points, each able to call back into the same flow contract during a transfer:interpreterStore.set— external call to an arbitrary store contract chosen by the flow deployer. A malicious store could re-enterflow.flow(...)from insideset.safeTransferFrom— invokesonERC721Receivedon a contract recipient. A malicious recipient could re-enter from the hook.safeTransferFrom— invokesonERC1155Received(andonERC1155BatchReceived) on a contract recipient. Same pattern.safeTransfer/safeTransferFrom— non-reentrant for compliant ERC20s, but ERC777 (which presents an ERC20 interface) invokestokensToSendon the sender andtokensReceivedon the recipient via the ERC1820 registry.Path 4 is covered by
testFlowReentrancyGuardFiresOnTokenCallbackin #446 (MaliciousReenteringToken.transferFromre-entersflow.flow). Paths 1–3 are uncovered.Required tests
For each of paths 1–3, deploy a minimal helper contract whose callback re-enters
flow.flow(...)and assert that the inner call reverts with"ReentrancyGuard: reentrant call"(OZ ReentrancyGuardUpgradeable v4 string).MaliciousReenteringStore— implementsIInterpreterStoreV2.setto re-enter on each call. Assert revert when the flow's store is set to this address and the evaluable returns non-empty kvs.MaliciousReenteringERC721Recipient— implementsIERC721Receiver.onERC721Receivedto re-enter. Assert revert when an ERC721 transfer targets this address.MaliciousReenteringERC1155Recipient— implementsIERC1155Receiver.onERC1155Receivedto re-enter. Assert revert when an ERC1155 transfer targets this address.Each new helper goes in
test/concrete/per project convention. Mutation-test by removingnonReentrantfromFlow.flowand confirming each test fails.Severity
LOW — the reentrancy guard exists and PR #446 demonstrates it works for one path. The remaining three paths are part of the same attack surface and should be locked down equally.