CSIRTS // UNIFIED SECURITY ADVISORY FEEDSYS ● ONLINE · POWERED BY INTELFUSIONS.COM

GHSA-4m8q-55qv-9pwp: Kimai: Teamlead authorization bypass in GET /api/timesheets allows reading other users' timesheet records without being teamlead of the target

mediumCVE-2026-52819
Summary GET /api/timesheets?user=<id> (and users[]=<id>) returns the targeted user's timesheet records to any caller that has the view_other_timesheet permission, without verifying that the caller is teamlead of any team containing the target user. The per-record endpoint GET /api/timesheets/{id} correctly enforces this check via TimesheetVoter/RolePermissionManager::checkTeamAccessTimesheet → checkTeamLeadAccess, but the list endpoint only filters projects/customers by team membership and never validates t.user. A ROLE_TEAMLEAD user can therefore enumerate any user's records — including the rate field — as long as those records are on a project with no team scoping (Kimai's default) or on any project that shares any team (membership, not lead) with the requester. Details Root cause: authorization mismatch between the per-record voter and the list endpoint. Per-record path (correct) src/Voter/TimesheetVoter.php:138: if (!$this->permissionManager->checkTeamAccessTimesheet($subject, $user)) { return false; } return $this->permissionManager->hasRolePermission($user, $permission . '_other_timesheet'); checkTeamLeadAccess (RolePermissionManager.php:143-160) requires isTeamleadOf (not just member) one of the target user's teams. The unit test testTeamleadDeniedWhenOnlyPlainMemberOfOwnerTeam (tests/Voter/TimesheetVoterTest.php:253-269) codifies this: *"a TEAMLEAD role with view_other_timesheet must not access another user's timesheet by being a plain team member — they must be the team's teamlead."* List path (vulnerable) src/API/TimesheetController.php:97-119: public function cgetAction(ParamFetcherInterface $paramFetcher, ..., UserRepository $userRepository): Response { $query = new TimesheetQuery(false); $this->prepareQuery($query, $paramFetcher); $seeAll = false; if ($this->isGranted('view_other_timesheet')) { /** @var array<int> $users */ $users = $paramFetcher->get('users'); $userId = $paramFetcher->get('user'); if ('all' === $userId) { $seeAll = true; }

Details

Source
GitHub Security Advisories (INTL · database · site)
Severity
medium
Published
2026-07-13
Last updated
2026-07-13
Exploitation
Not in CISA KEV at last sync

Original advisory: https://github.com/advisories/GHSA-4m8q-55qv-9pwp

Referenced CVEs

CVECSIRTS overviewExternal
CVE-2026-52819coverage & exploitation statusNVD · CVE.org

More from GitHub Security Advisories