Medium severity5.8NVD Advisory· Published May 8, 2026· Updated May 8, 2026
CVE-2026-42279
CVE-2026-42279
Description
solidtime is an open-source time-tracking app. In version 0.12.0, the PUT /api/v1/organizations/{organization}/time-entries/{timeEntry} API accepts a route-bound timeEntry from another organization when the caller has time-entries:update:all in the URL organization, allowing a known foreign time-entry UUID to be modified and rebound to objects in the caller's organization. This issue has been patched in version 0.12.1.
Affected products
2Patches
1b73aa543fdf5Merge commit from fork
2 files changed · +43 −2
app/Http/Controllers/Api/V1/TimeEntryController.php+2 −2 modified@@ -629,9 +629,9 @@ public function update(Organization $organization, TimeEntry $timeEntry, TimeEnt /** @var Member|null $member */ $member = $request->has('member_id') ? Member::query()->findOrFail($request->input('member_id')) : null; if ($timeEntry->member->user_id === Auth::id() && ($member === null || $member->user_id === Auth::id())) { - $this->checkPermission($organization, 'time-entries:update:own'); + $this->checkPermission($organization, 'time-entries:update:own', $timeEntry); } else { - $this->checkPermission($organization, 'time-entries:update:all'); + $this->checkPermission($organization, 'time-entries:update:all', $timeEntry); } if ($timeEntry->end !== null && $request->has('end') && $request->input('end') === null) {
tests/Unit/Endpoint/Api/V1/TimeEntryEndpointTest.php+41 −0 modified@@ -2490,6 +2490,47 @@ public function test_update_endpoint_fails_if_user_is_not_part_of_time_entry_org $response->assertForbidden(); } + public function test_update_endpoint_fails_if_time_entry_belongs_to_different_organization_than_url_even_with_update_all_permission(): void + { + // Arrange + // Attacker: has `time-entries:update:all` in their own organization (orgA). + $data = $this->createUserWithPermission([ + 'time-entries:update:all', + 'projects:view:all', + ]); + $attackerProject = Project::factory()->forOrganization($data->organization)->create(); + + // Victim: entirely separate organization (orgB). Attacker has NO membership in orgB. + $victimOrgData = $this->createUserWithPermission([], true); + $victimTimeEntry = TimeEntry::factory() + ->forOrganization($victimOrgData->organization) + ->forMember($victimOrgData->ownerMember) + ->create([ + 'description' => 'victim-original', + 'project_id' => null, + 'task_id' => null, + ]); + + Passport::actingAs($data->user); + + // Act: PUT to /organizations/{orgA}/time-entries/{victim_uuid} — URL org is attacker's + // own org, but the route-bound time entry belongs to orgB. + $response = $this->putJson(route('api.v1.time-entries.update', [$data->organization->getKey(), $victimTimeEntry->getKey()]), [ + 'description' => 'attacker-overwrite', + 'project_id' => $attackerProject->getKey(), + ]); + + // Assert: must be rejected. Before the fix this returned 200 and rewrote the + // victim row with attacker's project_id while keeping organization_id = orgB. + $response->assertForbidden(); + $this->assertDatabaseHas(TimeEntry::class, [ + 'id' => $victimTimeEntry->getKey(), + 'organization_id' => $victimOrgData->organization->getKey(), + 'description' => 'victim-original', + 'project_id' => null, + ]); + } + public function test_update_endpoint_fails_if_user_has_no_permission_to_update_time_entries_for_other_users_in_organization(): void { // Arrange
Vulnerability mechanics
AI mechanics synthesis has not run for this CVE yet.
References
3- github.com/solidtime-io/solidtime/commit/b73aa543fdf5b61c37447307ab7277451296832cnvdPatch
- github.com/solidtime-io/solidtime/security/advisories/GHSA-pmf9-pxq9-ccwrnvdExploitVendor Advisory
- github.com/solidtime-io/solidtime/releases/tag/v0.12.1nvdProductRelease Notes
News mentions
0No linked articles in our index yet.