Skip to content
This repository was archived by the owner on Feb 5, 2026. It is now read-only.

Conversation

@rsolari
Copy link
Collaborator

@rsolari rsolari commented Dec 31, 2025

Summary

This PR implements critical and high-severity security fixes identified in the Permitter contract audit:

  • C-01 (Critical): Owner DoS via zero caps - Add constructor validation to reject zero caps
  • H-01 (High): TOCTOU race conditions - Implement 1-hour timelock for parameter updates
  • H-02 (High): State invariant violation - Validate new caps against current state
  • H-03 (High): No constructor validation - Reject zero values upfront
  • M-01 (Medium/High): No caller validation - Add authorized caller whitelist

Key Changes

  • Timelock pattern: Replaced instant updates with schedule*() + execute*() functions
  • New state variables for pending updates and execution times
  • Authorized caller parameter added to constructor and factory
  • Comprehensive error handling with custom error types
  • All 92 tests pass (unit, integration, fuzz, exploit verification)

Test Coverage

  • Constructor validation tests
  • Timelock schedule/execute pattern tests
  • State invariant protection tests
  • Caller authorization tests
  • Signer rotation with grace period tests
  • Exploit verification demonstrating fixes work

🤖 Generated with Claude Code

Addresses critical and high severity findings from security audit:
- C-01: Add cap validation in constructor to prevent owner DoS
- H-01: Implement 1-hour timelock for cap/signer updates (schedule/execute pattern)
- H-02: Validate newMaxTotalEth >= totalEthRaised in executeUpdateMaxTotalEth
- H-03: Reject zero caps in constructor validation
- M-01: Add authorizedCaller parameter and validation in validateBid

Key changes:
- Add UPDATE_DELAY constant (1 hour) for timelock protection
- Replace instant update functions with schedule/execute pattern
- Add authorizedCaller to Permitter constructor and factory
- Add pending state variables for scheduled updates
- Add new errors: InvalidCap, CapBelowCurrentAmount, UnauthorizedCaller, UpdateNotScheduled, UpdateTooEarly
- Add new events: CapUpdateScheduled, SignerUpdateScheduled, AuthorizedCallerUpdated
- Add updateAuthorizedCaller function (no timelock for emergency CCA changes)
- Update all test files to use new interface

All 92 tests pass including unit, integration, fuzz, and exploit verification tests.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
uint256 _maxTokensPerBidder,
address _owner
address _owner,
address _authorizedCaller

Check notice

Code scanning / Slither

Missing zero address validation Low

Comment on lines +163 to +180
function executeUpdateMaxTotalEth() external onlyOwner {
if (pendingMaxTotalEthTime == 0) revert UpdateNotScheduled();
if (block.timestamp < pendingMaxTotalEthTime) {
revert UpdateTooEarly(pendingMaxTotalEthTime, block.timestamp);
}
if (pendingMaxTotalEth < totalEthRaised) {
revert CapBelowCurrentAmount(pendingMaxTotalEth, totalEthRaised);
}

uint256 oldCap = maxTotalEth;
maxTotalEth = newMaxTotalEth;
emit CapUpdated(CapType.TOTAL_ETH, oldCap, newMaxTotalEth);
maxTotalEth = pendingMaxTotalEth;

// Clear pending update
pendingMaxTotalEth = 0;
pendingMaxTotalEthTime = 0;

emit CapUpdated(CapType.TOTAL_ETH, oldCap, maxTotalEth);
}

Check notice

Code scanning / Slither

Block timestamp Low

Comment on lines +222 to +236
function executeUpdateTrustedSigner() external onlyOwner {
if (pendingTrustedSignerTime == 0) revert UpdateNotScheduled();
if (block.timestamp < pendingTrustedSignerTime) {
revert UpdateTooEarly(pendingTrustedSignerTime, block.timestamp);
}

address oldSigner = trustedSigner;
trustedSigner = newSigner;
emit SignerUpdated(oldSigner, newSigner);
trustedSigner = pendingTrustedSigner;

// Clear pending update
pendingTrustedSigner = address(0);
pendingTrustedSignerTime = 0;

emit SignerUpdated(oldSigner, trustedSigner);
}

Check notice

Code scanning / Slither

Block timestamp Low

}

/// @inheritdoc IPermitter
function updateAuthorizedCaller(address newCaller) external onlyOwner {

Check notice

Code scanning / Slither

Missing zero address validation Low

- Rename test functions in ExploitTests.t.sol from testFix_/testRemaining_
  to test_ prefix to satisfy scopelint naming convention
- Add test_RevertIf_ExceedsGlobalMaxTokensPerBidder to achieve 100%
  line coverage for Permitter.sol (covers line 131)
- Apply scopelint formatting

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
@github-actions
Copy link

github-actions bot commented Jan 1, 2026

Coverage after merging rsolari/skill-inquiry into main will be

100.00%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
src
   Permitter.sol98.58%91.30%100%100%195, 223
   PermitterFactory.sol100%100%100%100%

@rsolari rsolari merged commit 61f1109 into main Jan 1, 2026
6 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant