// 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;
}
}