Hawk High

First Flight #39
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: low
Valid

Invariant breaking in graduateAndUpgrade() __LevelOne.sol

Summary

The function graduateAndUpgrade breaks the invariant:

“System upgrade cannot take place unless the school’s sessionEnd has been reached.”


Vulnerability Details

The contract currently allows the principal to call graduateAndUpgrade() immediately after startSession() — even though the session period (4 weeks) hasn't elapsed.

There is no check for block.timestamp >= sessionEnd, so the upgrade can happen prematurely.


POC:

This test passes without reverting, proving the bug

function test_fuzz_UpgradeBeforeSessionEndShouldFail(address levelTwo) public {
vm.assume(levelTwo != address(0));
levelOne.initialize(PRINCIPAL, schoolFee, address(usdc));
vm.prank(PRINCIPAL);
levelOne.startSession(50);
vm.prank(PRINCIPAL);
levelOne.graduateAndUpgrade(levelTwo, ""); // ❌ should revert, but doesn't
}
Ran 1 test for test/LevelOne.t.sol:LevelOneTest
[PASS] test_fuzz_UpgradeBeforeSessionEndShouldFail(address) (runs: 257, μ: 176492, ~: 176492)
Traces:
[176492] LevelOneTest::test_fuzz_UpgradeBeforeSessionEndShouldFail(0x4E489a0C8d553D7888dF6c29C71762E76f7EF0b6)
├─ [0] VM::assume(true) [staticcall]
│ └─ ← [Return]
├─ [92624] LevelOne::initialize(principal: [0x6b9470599cb23a06988C6332ABE964d6608A50ca], 1000000000000000000 [1e18], MockUSDC: [0x2e234DAe75C793f67A35089C9d99245E1C58470b])
│ ├─ emit Initialized(version: 1)
│ └─ ← [Stop]
├─ [0] VM::prank(principal: [0x6b9470599cb23a06988C6332ABE964d6608A50ca])
│ └─ ← [Return]
├─ [47258] LevelOne::startSession(50)
│ ├─ emit SchoolInSession(startTime: 1, endTime: 2419201 [2.419e6])
│ └─ ← [Stop]
├─ [0] VM::prank(principal: [0x6b9470599cb23a06988C6332ABE964d6608A50ca])
│ └─ ← [Return]
├─ [18224] LevelOne::graduateAndUpgrade(0x4E489a0C8d553D7888dF6c29C71762E76f7EF0b6, 0x)
│ ├─ [7850] MockUSDC::transfer(principal: [0x6b9470599cb23a06988C6332ABE964d6608A50ca], 0)
│ │ ├─ emit Transfer(from: LevelOne: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], to: principal: [0x6b9470599cb23a06988C6332ABE964d6608A50ca], value: 0)
│ │ └─ ← [Return] true
│ └─ ← [Stop]
└─ ← [Stop]
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 43.52ms (42.23ms CPU time)
Ran 1 test suite in 540.04ms (43.52ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

Impact

Violates a critical time-based invariant of the school system.

Students may be "graduated" without completing their session or reviews.

Can lead to premature payout of wages and inaccurate academic progression logic.


Tools Used

Foundry (Forge)

Fuzz testing


Recommendations

Add a check inside graduateAndUpgrade:

require(block.timestamp >= sessionEnd, "Session has not ended yet");
Ran 1 test for test/LevelOne.t.sol:LevelOneTest
[PASS] test_fuzz_UpgradeBeforeSessionEndShouldFail(address) (runs: 257, μ: 176492, ~: 176492)
Traces:
[176492] LevelOneTest::test_fuzz_UpgradeBeforeSessionEndShouldFail(0xd0E36678b36617E8b66369B2060fF4B1eA81E237)
├─ [0] VM::assume(true) [staticcall]
│ └─ ← [Return]
├─ [92624] LevelOne::initialize(principal: [0x6b9470599cb23a06988C6332ABE964d6608A50ca], 1000000000000000000 [1e18], MockUSDC: [0x2e234DAe75C793f67A35089C9d99245E1C58470b])
│ ├─ emit Initialized(version: 1)
│ └─ ← [Stop]
├─ [0] VM::prank(principal: [0x6b9470599cb23a06988C6332ABE964d6608A50ca])
│ └─ ← [Return]
├─ [47258] LevelOne::startSession(50)
│ ├─ emit SchoolInSession(startTime: 1, endTime: 2419201 [2.419e6])
│ └─ ← [Stop]
├─ [0] VM::prank(principal: [0x6b9470599cb23a06988C6332ABE964d6608A50ca])
│ └─ ← [Return]
├─ [18224] LevelOne::graduateAndUpgrade(0xd0E36678b36617E8b66369B2060fF4B1eA81E237, 0x)
│ ├─ [7850] MockUSDC::transfer(principal: [0x6b9470599cb23a06988C6332ABE964d6608A50ca], 0)
│ │ ├─ emit Transfer(from: LevelOne: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], to: principal: [0x6b9470599cb23a06988C6332ABE964d6608A50ca], value: 0)
│ │ └─ ← [Return] true
│ └─ ← [Stop]
└─ ← [Stop]
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 50.26ms (39.52ms CPU time)
Ran 1 test suite in 630.11ms (50.26ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)
Updates

Lead Judging Commences

yeahchibyke Lead Judge 3 months ago
Submission Judgement Published
Validated
Assigned finding tags:

can graduate without session end

`graduateAndUpgrade()` can be called successfully even when the school session has not ended

yeahchibyke Lead Judge 3 months ago
Submission Judgement Published
Validated
Assigned finding tags:

can graduate without session end

`graduateAndUpgrade()` can be called successfully even when the school session has not ended

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.