Summary
System upgrade can occur if any student has not gotten 4 reviews.
Vulnerability Details
This vulnerability stems from two missing checks/updates:
The giveReview() function fails to increment the reviewCount mapping for students when a review is given. This means the reviewCount for every student remains at 0, regardless of how many reviews they actually receive.
The graduateAndUpgrade() function does not include a check to ensure that all students have accumulated the necessary 4 reviews before the upgrade process is allowed to proceed.
In order to get the review count from students, implement this geter in contract LevelOne
function getStudentReviewCount(address _student) external view returns (uint256) {
return reviewCount[_student];
}
Then below test demonstrates system can graduate to level two with review count zero.
function test_can_graduate_without_review() public schoolInSession {
levelTwoImplementation = new LevelTwo();
levelTwoImplementationAddress = address(levelTwoImplementation);
console2.log("Clara's review count: ", levelOneProxy.getStudentReviewCount(clara));
assertEq(levelOneProxy.getStudentReviewCount(clara), 0);
bytes memory data = abi.encodeCall(LevelTwo.graduate, ());
vm.prank(principal);
levelOneProxy.graduateAndUpgrade(levelTwoImplementationAddress, data);
}
Impact
Bypasses Graduation Prerequisite: Students can advance to the next level without fulfilling the required review criteria.
Renders Review System Meaningless: The review mechanism fails as a mandatory gating factor for progression.
Tools Used
Recommendations
Implement the missing update in giveReview() and add a check in graduateAndUpgrade() to enforce the requirement that all students must have exactly 4 reviews before graduation can occur.
function giveReview(address _student, bool review) public onlyTeacher {
if (!isStudent[_student]) {
revert HH__StudentDoesNotExist();
}
+ reviewCount[_student] += 1;
// @audit did not update reviewCount, reviewCount will always less than 5
require(reviewCount[_student] < 5, "Student review count exceeded!!!");
require(block.timestamp >= lastReviewTime[_student] + reviewTime, "Reviews can only be given once per week");
// where `false` is a bad review and true is a good review
if (!review) {
studentScore[_student] -= 10; // q only bad review will affect score, good review will not change anything
}
// Update last review time
lastReviewTime[_student] = block.timestamp;
emit ReviewGiven(_student, review, studentScore[_student]);
}
function graduateAndUpgrade(address _levelTwo, bytes memory) public onlyPrincipal {
if (_levelTwo == address(0)) {
revert HH__ZeroAddress();
}
// @audit should check if all stduents got 4 reviews
+ uint256 totalStudents = listOfStudents.length;
+ for (uint256 n = 0; n < totalStudents; n++) {
+ if (reviewCount[listOfStudents[n]] != 4) {
+ revert HH__DoesNotReviewAllStudents();
+ }
+ }
// ..skip
}