CVE-2026-52819
Kimai: Teamlead authorization bypass in GET /api/timesheets allows reading other users' timesheet records without being teamlead of the target
Kimai is an open-source time tracking application. Prior to 2.57.0, the GET /api/timesheets list endpoint accepts user and users[] target identifiers from a caller with view_other_timesheet but does not apply access_user or verify that a ROLE_TEAMLEAD requester leads a team containing each target user. TimesheetController::cgetAction() adds the resolved users directly to the query while project and customer filtering still permits records on unscoped projects or projects sharing ordinary team membership, allowing a teamlead to retrieve another user's descriptions, timing data, tags, rate, and internalRate even though GET /api/timesheets/{id} would deny access through TimesheetVoter. This issue is fixed in version 2.57.0.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- Low
- Integrity
- None
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
6.3
Kimai: Teamlead authorization bypass in GET /api/timesheets allows reading other users' timesheet records without being teamlead of the target
Kimai is an open-source time tracking application. Prior to 2.57.0, the GET /api/timesheets list endpoint accepts user and users[] target identifiers from a caller with view_other_timesheet but does not apply access_user or verify that a ROLE_TEAMLEAD requester leads a team containing each target user. TimesheetController::cgetAction() adds the resolved users directly to the query while project and customer filtering still permits records on unscoped projects or projects sharing ordinary team membership, allowing a teamlead to retrieve another user's descriptions, timing data, tags, rate, and internalRate even though GET /api/timesheets/{id} would deny access through TimesheetVoter. This issue is fixed in version 2.57.0.
- Record
CVE-2026-52819- CVSS 4.0
- 6.3 Medium
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N - Weakness
- CWE-863
- Updated
- 2026-11-02
EUVDEUVD-2026-78398
6.3
Kimai is an open-source time tracking application. Prior to 2.57.0, the GET /api/timesheets list endpoint accepts user and users[] target identifiers from a caller with view_other_timesheet but does not apply access_user or verify that a ROLE_TEAMLEAD requester leads a team containing each target user. TimesheetController::cgetAction() adds the resolved users directly to the query while project and customer filtering still permits records on unscoped projects or projects sharing ordinary team membership, allowing a teamlead to retrieve another user's descriptions, timing data, tags, rate, and internalRate even though GET /api/timesheets/{id} would deny access through TimesheetVoter. This issue is fixed in version 2.57.0.
- Record
EUVD-2026-78398- CVSS 4.0
- 6.3 Medium
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-4m8q-55qv-9pwp
6.3
Kimai: Teamlead authorization bypass in GET /api/timesheets allows reading other users' timesheet records without being teamlead of the target
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_timesheetmust 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;
} elseif (\is_string($userId) && $userId !== '') {
$users[] = (int) $userId;
}
if (!$seeAll) {
foreach ($userRepository->findByIds($users) as $user) {
$query->addUser($user); // <-- no teamlead-of-target check
}
}
}
...
config/packages/kimai.yaml:96,115 grants TIMESHEET_OTHER (which contains view_other_timesheet) to ROLE_TEAMLEAD, so the gate at line 103 passes for any teamlead. The user= / users[]= IDs are pushed straight into the query.
Net effect
For any victim bob who:
- has at least one team that the requester
aliceis not teamlead of (so the voter denies per-record access), AND - has timesheets either on a project with no team (Kimai's default), or on a project that shares any team with
alice(membership, not lead)
alice is denied via GET /api/timesheets/{id} but receives bob's records via GET /api/timesheets?user=<bob_id>.
Disclosed fields in the collection response include description, begin, end, duration, billable, exported, tags, rate, internalRate, plus project/activity/user IDs (Default/Collection serializer groups, Timesheet.php:164-173). rate is financial data that the per-record voter is supposed to gate via the separate view_rate_other_timesheet permission.
Why other proposed mitigations don't apply
- The
view_other_timesheetIsGrantedon the route is the only authorization layer in the list path; ROLE_TEAMLEAD has it globally. prepareQueryonly setscurrentUser, not authorization (BaseApiController.php:68-71).- The serializer does not filter
rateper caller — it is a staticDefault-group property. - Recent commit 20c7b03 "Re-usable ACL checks on teams" hardened the voter side but left the list endpoint unchanged.
*A PoC was…
- Record
GHSA-4m8q-55qv-9pwp- CVSS 4.0
- 6.3 Medium
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N - Weakness
- CWE-863
- Updated
- 2026-11-02
NVD
6.3
Kimai is an open-source time tracking application. Prior to 2.57.0, the GET /api/timesheets list endpoint accepts user and users[] target identifiers from a caller with view_other_timesheet but does not apply access_user or verify that a ROLE_TEAMLEAD requester leads a team containing each target user. TimesheetController::cgetAction() adds the resolved users directly to the query while project and customer filtering still permits records on unscoped projects or projects sharing ordinary team membership, allowing a teamlead to retrieve another user's descriptions, timing data, tags, rate, and internalRate even though GET /api/timesheets/{id} would deny access through TimesheetVoter. This issue is fixed in version 2.57.0.
- Record
CVE-2026-52819- CVSS 4.0
- 6.3 Medium
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - Weakness
- CWE-863
- Updated
- 2026-11-02
OSV
6.3
Kimai: Teamlead authorization bypass in GET /api/timesheets allows reading other users' timesheet records without being teamlead of the target
Kimai is an open-source time tracking application. Prior to 2.57.0, the GET /api/timesheets list endpoint accepts user and users[] target identifiers from a caller with view_other_timesheet but does not apply access_user or verify that a ROLE_TEAMLEAD requester leads a team containing each target user. TimesheetController::cgetAction() adds the resolved users directly to the query while project and customer filtering still permits records on unscoped projects or projects sharing ordinary team membership, allowing a teamlead to retrieve another user's descriptions, timing data, tags, rate, and internalRate even though GET /api/timesheets/{id} would deny access through TimesheetVoter. This issue is fixed in version 2.57.0.
- Record
CVE-2026-52819- CVSS 4.0
- 6.3 Medium
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N - Weakness
- CWE-863
- Updated
- 2026-11-02
OSVGHSA-4m8q-55qv-9pwp
6.3
Kimai: Teamlead authorization bypass in GET /api/timesheets allows reading other users' timesheet records without being teamlead of the target
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_timesheetmust 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;
} elseif (\is_string($userId) && $userId !== '') {
$users[] = (int) $userId;
}
if (!$seeAll) {
foreach ($userRepository->findByIds($users) as $user) {
$query->addUser($user); // <-- no teamlead-of-target check
}
}
}
...
config/packages/kimai.yaml:96,115 grants TIMESHEET_OTHER (which contains view_other_timesheet) to ROLE_TEAMLEAD, so the gate at line 103 passes for any teamlead. The user= / users[]= IDs are pushed straight into the query.
Net effect
For any victim bob who:
- has at least one team that the requester
aliceis not teamlead of (so the voter denies per-record access), AND - has timesheets either on a project with no team (Kimai's default), or on a project that shares any team with
alice(membership, not lead)
alice is denied via GET /api/timesheets/{id} but receives bob's records via GET /api/timesheets?user=<bob_id>.
Disclosed fields in the collection response include description, begin, end, duration, billable, exported, tags, rate, internalRate, plus project/activity/user IDs (Default/Collection serializer groups, Timesheet.php:164-173). rate is financial data that the per-record voter is supposed to gate via the separate view_rate_other_timesheet permission.
Why other proposed mitigations don't apply
- The
view_other_timesheetIsGrantedon the route is the only authorization layer in the list path; ROLE_TEAMLEAD has it globally. prepareQueryonly setscurrentUser, not authorization (BaseApiController.php:68-71).- The serializer does not filter
rateper caller — it is a staticDefault-group property. - Recent commit 20c7b03 "Re-usable ACL checks on teams" hardened the voter side but left the list endpoint unchanged.
*A PoC was…
- Record
GHSA-4m8q-55qv-9pwp- CVSS 4.0
- 6.3 Medium
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N - Weakness
- CWE-863
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
kimai/kimaiPackagist |
through 2.56.0 | 2.57.0 |
kimai/kimaiPackagist |
before 2.57.0, plus listed versions 0.10.20.30.40.5 Show 45 moreShow less0.60.6.10.70.80.8.10.91.01.0.11.11.101.10.11.10.21.111.11.11.121.131.141.14.11.14.21.14.31.151.15.11.15.21.15.31.15.41.15.51.15.61.161.16.11.16.101.16.21.16.31.16.41.16.51.16.61.16.71.16.81.16.91.171.17.11.181.18.11.18.21.191.19.1 |
2.57.0 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-52819 is a medium vulnerability (CVSS 6.3) in Kimai, published July 13, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.50%, the 42nd percentile. Fixed in kimai/kimai 2.57.0.
References
github.com/kimai/kimai/security/advisories/GHSA-4m8q-55qv-9pwp
github.com/kimai/kimai/pull/5929
github.com/kimai/kimai/commit/976d38e8a4485a1c923ee7b5841849e91a06e849
github.com/kimai/kimai/releases/tag/2.57.0
kimai.org/en/security/ghsa-4m8q-55qv-9pwp
github.com/advisories/GHSA-4m8q-55qv-9pwp
github.com/CVEProject/cvelistV5/tree/main/cves/2026/52xxx/CVE-2026-52819.json
nvd.nist.gov/vuln/detail/CVE-2026-52819
github.com/kimai/kimai
Left out here: 152 versions and the rest of 2 source descriptions. The JSON record has them.