Hawk High

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

All students with or without complete reviews can graduate to level two

Summary

All students can graduate to Level Two without having all 4 reviews; breaking one of the invariants. Reviews also don't increment after each student review in LevelOne::giveReview.

Vulnerability Details

Impact

Breaks system invariants

POC

Paste the following code into LevelOneAndGraduateTest.t.sol

function testStudentCanUpgradeWithoutFourReviews() public {
levelTwoImplementation = new LevelTwo();
levelTwoImplementationAddress = address(levelTwoImplementation);
vm.prank(principal);
levelOneProxy.addTeacher(alice);
vm.startPrank(clara);
usdc.approve(address(levelOneProxy), schoolFees);
levelOneProxy.enroll();
vm.stopPrank();
vm.prank(principal);
levelOneProxy.startSession(70);
vm.warp(block.timestamp + 1 weeks);
for (uint256 i = 0; i < 3; i++) {
vm.prank(alice);
levelOneProxy.giveReview(clara, true);
vm.warp(block.timestamp + 1 weeks);
}
bytes memory data = abi.encodeCall(LevelTwo.graduate, ());
vm.prank(principal);
levelOneProxy.graduateAndUpgrade(levelTwoImplementationAddress, data);
LevelTwo levelTwoProxy = LevelTwo(proxyAddress);
assertEq(levelTwoProxy.getTotalStudents(), levelTwoProxy.getTotalStudents());
}

Tools Used

Manual Review

Recommendations

First add a new error to the list of errors, increment the review count for each student in giveReview , and add a check in graduateAndUpgrade function.

+ error HH__NotAllStudentsHaveFourReviews();
function giveReview(address _student, bool review) public onlyTeacher {
if (!isStudent[_student]) {
revert HH__StudentDoesNotExist();
}
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;
}
// increment student
+ reviewCount[_student] += 1;
// Update last review time
lastReviewTime[_student] = block.timestamp;
emit ReviewGiven(_student, review, studentScore[_student]);
}
function graduateAndUpgrade(address _levelTwo, bytes memory) public onlyPrincipal {
+ uint256 studentsLength = listOfStudents.length;
+ for (uint256 i = 0; i < studentsLength; i++) {
+ if(reviewCount[listOfStudents[i]] < 4) {
+ revert HH__NotAllStudentsHaveFourReviews();
+ }
+ }
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);
}

Updates

Lead Judging Commences

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

cut-off criteria not applied

All students are graduated when the graduation function is called as the cut-off criteria is not applied.

Support

FAQs

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