Summary
Two key vulnerabilities exist in the LevelOne contract related to review counting and array manipulation
Vulnerability Details
Unlimited Review Exploitation
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");
if (!review) {
studentScore[_student] -= 10;
}
lastReviewTime[_student] = block.timestamp;
}
PoC Test:
function testUnlimitedReviews() public {
vm.prank(principal);
levelOne.addTeacher(teacher);
levelOne.startSession();
for(uint i = 0; i < 10; i++) {
vm.warp(block.timestamp + 1 weeks);
vm.prank(teacher);
levelOne.giveReview(student, false);
}
assert(levelOne.studentScore(student) <= 0);
}
Array Bounds Safety
function removeTeacher(address _teacher) public onlyPrincipal {
uint256 teacherLength = listOfTeachers.length;
for (uint256 n = 0; n < teacherLength; n++) {
if (listOfTeachers[n] == _teacher) {
listOfTeachers[n] = listOfTeachers[teacherLength - 1];
listOfTeachers.pop();
break;
}
}
}
PoC Test:
function testArrayBoundsSafety() public {
vm.prank(principal);
levelOne.removeTeacher(address(0x1));
vm.prank(principal);
levelOne.addTeacher(address(0x1));
levelOne.removeTeacher(address(0x1));
assert(levelOne.getTotalTeachers() == 0);
}
Impact
Unlimited Reviews:
Teachers can give unlimited negative reviews
Student scores can be manipulated beyond intended limits
Breaks the 5-review limit business logic
Could unfairly impact student graduation eligibility
Array Bounds:
Potential for out-of-bounds access
Risk of array manipulation errors
Could lead to contract state inconsistencies
Tools Used
Recommendations
Fix Review Counter:
function giveReview(address _student, bool review) public onlyTeacher {
if (!review) {
studentScore[_student] -= 10;
}
reviewCount[_student]++;
lastReviewTime[_student] = block.timestamp;
}
Add Array Safety:
function removeTeacher(address _teacher) public onlyPrincipal {
if (listOfTeachers.length == 0) revert EmptyTeacherList();
uint256 teacherLength = listOfTeachers.length;
bool found = false;
for (uint256 n = 0; n < teacherLength; n++) {
if (listOfTeachers[n] == _teacher) {
listOfTeachers[n] = listOfTeachers[teacherLength - 1];
listOfTeachers.pop();
found = true;
break;
}
}
require(found, "Teacher not found in list");
}