forge-guard 0.3.4

Pre-deployment smart contract auditing framework for Foundry
Documentation
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * @title VulnerableToken
 * @notice Sample contract with intentionally vulnerable patterns for forge-audit CI testing.
 *         DO NOT USE IN PRODUCTION — this is for testing/demo purposes only.
 */
contract VulnerableToken {

    // ── Reentrancy vulnerability: CEI violation ──
    mapping(address => uint256) public balances;
    address public owner;

    event Withdrawal(address indexed user, uint256 amount);
    event Transfer(address indexed from, address indexed to, uint256 amount);

    constructor() {
        owner = msg.sender;
    }

    /// @dev DEPRECATED: This function has a reentrancy vulnerability (CEI violation).
    ///      State write happens AFTER the external call.
    function withdraw(uint256 amount) public {
        require(balances[msg.sender] >= amount, "Insufficient balance");
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");
        balances[msg.sender] -= amount; // <-- CEI violation: state write after external call
        emit Withdrawal(msg.sender, amount);
    }

    // ── Missing access control on sensitive function ──
    /// @dev Anyone can call this, no onlyOwner modifier
    function mint(address to, uint256 amount) public {
        balances[to] += amount;
        emit Transfer(address(0), to, amount);
    }

    // ── tx.origin usage ──
    function transferAll(address to) public {
        require(tx.origin == owner, "Not owner via tx.origin");
        payable(to).transfer(address(this).balance);
    }

    // ── Unsafe assembly ──
    function rawTransfer(address to, uint256 amount) public returns (bool) {
        bool success;
        assembly {
            success := call(gas(), to, amount, 0, 0, 0, 0)
        }
        return success;
    }

    // ── Timestamp dependency ──
    uint256 public lastAction;
    function timedAction() public {
        require(block.timestamp >= lastAction + 1 days, "Too soon");
        lastAction = block.timestamp;
    }

    // ── Unbounded loop (potential DoS) ──
    address[] public holders;

    function distribute() public {
        for (uint256 i = 0; i < holders.length; i++) {
            payable(holders[i]).transfer(1 ether);
        }
    }

    // ── Unsafe casting ──
    function unsafeCast(uint256 value) public pure returns (uint64) {
        return uint64(value); // truncation without check
    }

    // ── Function with underscore naming ──
    function unsafe_admin_action() public { // naming convention violation
        owner = msg.sender;
    }

    // ── Missing event emission on state change ──
    address public admin;
    function setAdmin(address newAdmin) public { // no event emitted
        admin = newAdmin;
    }
}