coolify/tests/Feature/TeamPolicyTest.php
Andras Bacsai 336fa0c714 fix: critical privilege escalation in team invitation system
This commit addresses a critical security vulnerability where low-privileged
users (members) could invite high-privileged users (admins/owners) to teams,
allowing them to escalate their own privileges through password reset.

Root Causes Fixed:
1. TeamPolicy authorization checks were commented out, allowing all team
   members to manage invitations instead of just admins/owners
2. Missing role elevation checks in InviteLink component allowed members
   to invite users with higher privileges

Security Fixes:

1. app/Policies/TeamPolicy.php
   - Uncommented and enforced authorization checks for:
     * update() - Only admins/owners can update team settings
     * delete() - Only admins/owners can delete teams
     * manageMembers() - Only admins/owners can manage team members
     * viewAdmin() - Only admins/owners can view admin panel
     * manageInvitations() - Only admins/owners can manage invitations

2. app/Livewire/Team/InviteLink.php
   - Added explicit role elevation checks to prevent:
     * Members from inviting admins or owners
     * Admins from inviting owners (defense-in-depth)
   - Validates that inviter has sufficient privileges for target role

Test Coverage:

1. tests/Feature/TeamPolicyTest.php
   - 24 comprehensive tests covering all policy methods
   - Tests for owner, admin, member, and non-member access
   - Specific tests for the privilege escalation vulnerability

2. tests/Feature/TeamInvitationPrivilegeEscalationTest.php
   - 11 tests covering all role elevation scenarios
   - Tests member → admin/owner escalation (blocked)
   - Tests admin → owner escalation (blocked)
   - Tests valid invitation paths for each role

Impact:
- Prevents privilege escalation attacks
- Protects all Coolify instances from unauthorized access
- Enforces proper role hierarchy in team management

References:
- Identified by Aikido AI whitebox pentest service
- CVE: Pending assignment
- Severity: Critical

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-15 11:42:25 +02:00

137 lines
4.6 KiB
PHP

<?php
use App\Models\Team;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
beforeEach(function () {
// Create a team with owner, admin, and member
$this->team = Team::factory()->create();
$this->owner = User::factory()->create();
$this->admin = User::factory()->create();
$this->member = User::factory()->create();
$this->team->members()->attach($this->owner->id, ['role' => 'owner']);
$this->team->members()->attach($this->admin->id, ['role' => 'admin']);
$this->team->members()->attach($this->member->id, ['role' => 'member']);
});
describe('update permission', function () {
test('owner can update team', function () {
expect($this->owner->can('update', $this->team))->toBeTrue();
});
test('admin can update team', function () {
expect($this->admin->can('update', $this->team))->toBeTrue();
});
test('member cannot update team', function () {
expect($this->member->can('update', $this->team))->toBeFalse();
});
test('non-team member cannot update team', function () {
$outsider = User::factory()->create();
expect($outsider->can('update', $this->team))->toBeFalse();
});
});
describe('delete permission', function () {
test('owner can delete team', function () {
expect($this->owner->can('delete', $this->team))->toBeTrue();
});
test('admin can delete team', function () {
expect($this->admin->can('delete', $this->team))->toBeTrue();
});
test('member cannot delete team', function () {
expect($this->member->can('delete', $this->team))->toBeFalse();
});
test('non-team member cannot delete team', function () {
$outsider = User::factory()->create();
expect($outsider->can('delete', $this->team))->toBeFalse();
});
});
describe('manageMembers permission', function () {
test('owner can manage members', function () {
expect($this->owner->can('manageMembers', $this->team))->toBeTrue();
});
test('admin can manage members', function () {
expect($this->admin->can('manageMembers', $this->team))->toBeTrue();
});
test('member cannot manage members', function () {
expect($this->member->can('manageMembers', $this->team))->toBeFalse();
});
test('non-team member cannot manage members', function () {
$outsider = User::factory()->create();
expect($outsider->can('manageMembers', $this->team))->toBeFalse();
});
});
describe('viewAdmin permission', function () {
test('owner can view admin panel', function () {
expect($this->owner->can('viewAdmin', $this->team))->toBeTrue();
});
test('admin can view admin panel', function () {
expect($this->admin->can('viewAdmin', $this->team))->toBeTrue();
});
test('member cannot view admin panel', function () {
expect($this->member->can('viewAdmin', $this->team))->toBeFalse();
});
test('non-team member cannot view admin panel', function () {
$outsider = User::factory()->create();
expect($outsider->can('viewAdmin', $this->team))->toBeFalse();
});
});
describe('manageInvitations permission (privilege escalation fix)', function () {
test('owner can manage invitations', function () {
expect($this->owner->can('manageInvitations', $this->team))->toBeTrue();
});
test('admin can manage invitations', function () {
expect($this->admin->can('manageInvitations', $this->team))->toBeTrue();
});
test('member cannot manage invitations (SECURITY FIX)', function () {
// This test verifies the privilege escalation vulnerability is fixed
// Previously, members could see and manage admin invitations
expect($this->member->can('manageInvitations', $this->team))->toBeFalse();
});
test('non-team member cannot manage invitations', function () {
$outsider = User::factory()->create();
expect($outsider->can('manageInvitations', $this->team))->toBeFalse();
});
});
describe('view permission', function () {
test('owner can view team', function () {
expect($this->owner->can('view', $this->team))->toBeTrue();
});
test('admin can view team', function () {
expect($this->admin->can('view', $this->team))->toBeTrue();
});
test('member can view team', function () {
expect($this->member->can('view', $this->team))->toBeTrue();
});
test('non-team member cannot view team', function () {
$outsider = User::factory()->create();
expect($outsider->can('view', $this->team))->toBeFalse();
});
});