Skip to content

Latest commit

 

History

History
172 lines (107 loc) · 5.17 KB

File metadata and controls

172 lines (107 loc) · 5.17 KB

ChainProof Security Audit Report

Generated: 2026-04-18T16:30:33.747Z ChainProof version: 0.1.0 Files scanned: 1

Executive Summary

Severity Count
🔴 Critical 3
🟠 High 2
🟡 Medium 1
🟢 Low 0
🔵 Info 0
⛽ Gas 0
Total 6

⚠️ This contract has critical or high severity findings. Do not deploy to mainnet without addressing these issues.

examples/contracts/VulnerableVault.sol

Scanned with ChainProof AST engine (Slither not available)

Vulnerability Findings

1. 🔴 [CRITICAL] Reentrancy vulnerability

  • ID: CP-107 (SWC-107)
  • Location: Line 41

Description

Function "withdraw" makes an external call before updating state variables. An attacker can re-enter the function before the state is updated, potentially draining funds (e.g. the DAO hack).

Recommendation

Apply the Checks-Effects-Interactions pattern: update all state variables before making any external calls. Alternatively, use OpenZeppelin's ReentrancyGuard modifier.

Affected Code

function withdraw(uint256 amount) external {
        require(balances[msg.sender] >= amount, "Insufficient balance");

        // ❌ External call BEFORE state update — classic reentrancy
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");

        // State updated AFTER call — attacker can re-enter before this line
        balances[msg.sender] -= amount;
        totalDeposited -= amount;
    }

2. 🔴 [CRITICAL] Reentrancy vulnerability

  • ID: CP-107 (SWC-107)
  • Location: Line 59

Description

Function "sendReward" makes an external call before updating state variables. An attacker can re-enter the function before the state is updated, potentially draining funds (e.g. the DAO hack).

Recommendation

Apply the Checks-Effects-Interactions pattern: update all state variables before making any external calls. Alternatively, use OpenZeppelin's ReentrancyGuard modifier.

Affected Code

function sendReward(address recipient) external onlyOwner {
        // ❌ .send() return value ignored — failure is silently swallowed
        recipient.call{value: rewardPool}("");
        rewardPool = 0;
    }

3. 🔴 [CRITICAL] Reentrancy vulnerability

  • ID: CP-107 (SWC-107)
  • Location: Line 66

Description

Function "transferOwnership" makes an external call before updating state variables. An attacker can re-enter the function before the state is updated, potentially draining funds (e.g. the DAO hack).

Recommendation

Apply the Checks-Effects-Interactions pattern: update all state variables before making any external calls. Alternatively, use OpenZeppelin's ReentrancyGuard modifier.

Affected Code

function transferOwnership(address newOwner) external {
        require(tx.origin == owner, "Not owner");
        owner = newOwner;
    }

4. 🟠 [HIGH] Use of tx.origin for authentication

  • ID: CP-115 (SWC-115)
  • Location: Line 29

Description

tx.origin refers to the original external account that initiated the transaction, not the immediate caller. A phishing contract can exploit this to perform unauthorized actions on behalf of the victim.

Recommendation

Replace tx.origin with msg.sender for authorization checks. If you need to distinguish EOAs from contracts, use msg.sender == tx.origin as a secondary check, not the primary guard.

Affected Code

require(tx.origin == owner, "Not owner");

5. 🟠 [HIGH] Use of tx.origin for authentication

  • ID: CP-115 (SWC-115)
  • Location: Line 67

Description

tx.origin refers to the original external account that initiated the transaction, not the immediate caller. A phishing contract can exploit this to perform unauthorized actions on behalf of the victim.

Recommendation

Replace tx.origin with msg.sender for authorization checks. If you need to distinguish EOAs from contracts, use msg.sender == tx.origin as a secondary check, not the primary guard.

Affected Code

require(tx.origin == owner, "Not owner");

6. 🟡 [MEDIUM] Unchecked call return value

  • ID: CP-104 (SWC-104)
  • Location: Line 61

Description

.call() and .send() return a boolean indicating success. Ignoring this return value means failures are silently swallowed, potentially leaving the contract in an inconsistent state.

Recommendation

Always check the return value: (bool success, ) = addr.call{value: amount}(""); require(success, "Transfer failed");. Prefer .transfer() for simple ETH sends if reentrancy is not a concern.

Affected Code

recipient.call{value: rewardPool}("");


This report was generated by ChainProof. It is not a substitute for a manual security audit by a qualified professional.