VYPR
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

2

Patches

1
b73aa543fdf5

Merge commit from fork

https://github.com/solidtime-io/solidtimeGregor VostrakApr 21, 2026via nvd-ref
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

News mentions

0

No linked articles in our index yet.