Summary
Students who do not meet the cut off score are still able to graduate to level two, which breaks the system invariant.
Vulnerability Details
The test below demonstrates that students who do not meet the minimum cut-off score are still able to graduate when the system upgrades to level two:
function test_student_does_not_meet_cut_off_score_should_not_upgrade() public schoolInSession {
levelTwoImplementation = new LevelTwo();
levelTwoImplementationAddress = address(levelTwoImplementation);
uint256 numberOfStudentsBeforeUpgrade = levelOneProxy.getTotalStudents();
console2.log("Numbers of Students before upgrade: ", numberOfStudentsBeforeUpgrade);
vm.startPrank(alice);
for (uint256 i = 0; i < 4; i++) {
vm.warp(block.timestamp + 1 weeks);
levelOneProxy.giveReview(clara, false);
}
vm.stopPrank();
console2.log("Clara's score:", levelOneProxy.studentScore(clara));
console2.log("Cut off score", levelOneProxy.cutOffScore());
assert(levelOneProxy.studentScore(clara) < levelOneProxy.cutOffScore());
bytes memory data = abi.encodeCall(LevelTwo.graduate, ());
vm.prank(principal);
levelOneProxy.graduateAndUpgrade(levelTwoImplementationAddress, data);
uint256 numberOfStudentsAfterUpgrade = LevelTwo(proxyAddress).getTotalStudents();
console2.log("Numbers of Students after upgrade: ", numberOfStudentsAfterUpgrade);
}
Logs:
Numbers of Students before upgrade: 6
Clara's score: 60
Cut off score 70
Numbers of Students after upgrade: 6
The logs clearly show that Clara's score (60) is below the required cut-off score (70), yet she is still upgraded to level two along with all other students.
Impact
This vulnerability compromises the educational system by:
Allowing students with failing scores to advance inappropriately
Making the scoring system effectively useless
Breaking the core rule that only qualified students should advance
Tools Used
Manual review
Recommendations
Implement score validation in the graduateAndUpgrade() function to expel students who do not meet the minimum cut-off score:
function graduateAndUpgrade(address _levelTwo, bytes memory) public onlyPrincipal {
if (_levelTwo == address(0)) {
revert HH__ZeroAddress();
}
// @audit student does not meet the cut off score should not be upgraded
+ address[] memory studentsToBeExpel = new address[](totalStudents);
+ uint256 count = 0;
+ for (uint256 n = 0; n < totalStudents; n++) {
+ address student = listOfStudents[n];
+ if (studentScore[student] < cutOffScore) {
+ studentsToBeExpel[count] = student;
+ count++;
+ }
+ }
+ for (uint256 n = 0; n < count; n++) {
+ expel(studentsToBeExpel[n]);
+ }
uint256 totalTeachers = listOfTeachers.length;
// ... skip
}