InstanceSettings::query()->create(['id' => 0])); // Attacker: Team A $this->userA = User::factory()->create(); $this->teamA = Team::factory()->create(); $this->userA->teams()->attach($this->teamA, ['role' => 'owner']); $this->serverA = Server::factory()->create(['team_id' => $this->teamA->id]); $this->projectA = Project::factory()->create(['team_id' => $this->teamA->id]); $this->environmentA = Environment::factory()->create(['project_id' => $this->projectA->id]); $this->destinationA = StandaloneDocker::factory()->create([ 'server_id' => $this->serverA->id, 'name' => 'dest-a-'.fake()->unique()->word(), 'network' => 'coolify-a-'.fake()->unique()->word(), ]); $this->applicationA = Application::factory()->create([ 'environment_id' => $this->environmentA->id, 'destination_id' => $this->destinationA->id, 'destination_type' => StandaloneDocker::class, ]); // A second usable destination on Team A's own server, used for positive-path tests. $this->serverA2 = Server::factory()->create(['team_id' => $this->teamA->id]); $this->destinationA2 = StandaloneDocker::factory()->create([ 'server_id' => $this->serverA2->id, 'name' => 'dest-a2-'.fake()->unique()->word(), 'network' => 'coolify-a2-'.fake()->unique()->word(), ]); // Victim: Team B $this->userB = User::factory()->create(); $this->teamB = Team::factory()->create(); $this->userB->teams()->attach($this->teamB, ['role' => 'owner']); $this->serverB = Server::factory()->create(['team_id' => $this->teamB->id]); $this->destinationB = StandaloneDocker::factory()->create([ 'server_id' => $this->serverB->id, 'name' => 'dest-b-'.fake()->unique()->word(), 'network' => 'coolify-b-'.fake()->unique()->word(), ]); // Act as attacker (Team A) $this->actingAs($this->userA); session(['currentTeam' => $this->teamA]); }); describe('Destination::addServer GHSA-j395-3pqh-9r5g', function () { test('cannot attach another team\'s server + network to own application', function () { try { Livewire::test(Destination::class, ['resource' => $this->applicationA]) ->call('addServer', $this->destinationB->id, $this->serverB->id); } catch (Throwable $e) { // handleError on ModelNotFoundException calls abort(404); pivot assertion is source of truth. } expect($this->applicationA->fresh()->additional_networks)->toHaveCount(0); expect($this->applicationA->fresh()->additional_servers)->toHaveCount(0); }); test('cannot attach own network paired with another team\'s server', function () { try { Livewire::test(Destination::class, ['resource' => $this->applicationA]) ->call('addServer', $this->destinationA2->id, $this->serverB->id); } catch (Throwable $e) { } expect($this->applicationA->fresh()->additional_networks)->toHaveCount(0); }); test('cannot attach another team\'s network paired with own server', function () { try { Livewire::test(Destination::class, ['resource' => $this->applicationA]) ->call('addServer', $this->destinationB->id, $this->serverA2->id); } catch (Throwable $e) { } expect($this->applicationA->fresh()->additional_networks)->toHaveCount(0); }); test('can attach own team\'s server + network to own application', function () { Livewire::test(Destination::class, ['resource' => $this->applicationA]) ->call('addServer', $this->destinationA2->id, $this->serverA2->id); $additional = $this->applicationA->fresh()->additional_networks; expect($additional)->toHaveCount(1); expect($additional->first()->id)->toBe($this->destinationA2->id); expect($additional->first()->pivot->server_id)->toBe($this->serverA2->id); }); }); describe('Destination::promote GHSA-j395-3pqh-9r5g', function () { test('cannot promote another team\'s network as the application\'s main destination', function () { $originalDestinationId = $this->applicationA->destination_id; try { Livewire::test(Destination::class, ['resource' => $this->applicationA]) ->call('promote', $this->destinationB->id, $this->serverB->id); } catch (Throwable $e) { } expect($this->applicationA->fresh()->destination_id)->toBe($originalDestinationId); }); });