Summary
Does not validate whether students met graduation criteria (cutOffScore).
Vulnerability Details
The graduateAndUpgrade() function lacks validation to ensure only students meeting the cutOffScore are carried forward during protocol upgrades. This allows Failed students (score < cutOffScore) to persist post-upgrade.
function graduateAndUpgrade(address _levelTwo, bytes memory) public onlyPrincipal {
if (_levelTwo == address(0)) {
revert HH__ZeroAddress();
}
uint256 totalTeachers = listOfTeachers.length;
uint256 payPerTeacher = (bursary * TEACHER_WAGE) / PRECISION;
uint256 principalPay = (bursary * PRINCIPAL_WAGE) / PRECISION;
@ >
@ >
_authorizeUpgrade(_levelTwo);
for (uint256 n = 0; n < totalTeachers; n++) {
usdc.safeTransfer(listOfTeachers[n], payPerTeacher);
}
usdc.safeTransfer(principal, principalPay);
}
POC
function test_failed_students_get_to_graduate() public schoolInSession {
for (uint week = 1; week <= 4; week++) {
vm.warp(block.timestamp + 1 weeks);
vm.prank(alice);
levelOneProxy.giveReview(clara, true);
vm.prank(alice);
levelOneProxy.giveReview(dan, false);
vm.prank(alice);
levelOneProxy.giveReview(eli, true);
vm.prank(alice);
levelOneProxy.giveReview(fin, false);
vm.prank(alice);
levelOneProxy.giveReview(grey, true);
vm.prank(alice);
levelOneProxy.giveReview(harriet, false);
}
vm.warp(block.timestamp + 4 weeks);
uint256 initialBursary = usdc.balanceOf(address(levelOneProxy));
uint256 initialAliceBalance = usdc.balanceOf(alice);
uint256 initialBobBalance = usdc.balanceOf(bob);
uint256 initialPrincipalBalance = usdc.balanceOf(principal);
levelTwoImplementation = new LevelTwo();
bytes memory data = abi.encodeCall(LevelTwo.graduate, ());
vm.prank(principal);
levelOneProxy.graduateAndUpgrade(address(levelTwoImplementation), data);
LevelTwo levelTwoProxy = LevelTwo(proxyAddress);
assertEq(levelTwoProxy.getTotalStudents(), 6);
}
Impact
Protocol rules to be bypassed, undermining the integrity of the grading system.
Tools Used
Foundry Tests
Recommendations
Add pre-upgrade validation and reset the array
function graduateAndUpgrade(address _levelTwo, bytes memory) public onlyPrincipal {
_validateUpgradeConditions(_levelTwo);
uint256 studentCount = listOfStudents.length;
graduatingCount = 0;
address[] memory newStudentsList = new address[](studentCount);
uint256 newStudentsCount = 0;
for (uint256 i = 0; i < studentCount; i++) {
address student = listOfStudents[i];
if (studentScore[student] >= cutOffScore) {
graduatingCount++;
newStudentsList[newStudentsCount] = student;
newStudentsCount++;
}
}
delete listOfStudents;
for (uint256 i = 0; i < newStudentsCount; i++) {
listOfStudents.push(newStudentsList[i]);
}
_executeUpgradeAndPayments(_levelTwo, graduatingCount);
}