Merge remote-tracking branch 'origin/next' into audit-policies

# Conflicts:
#	tests/Unit/Policies/GithubAppPolicyTest.php
#	tests/Unit/Policies/SharedEnvironmentVariablePolicyTest.php
This commit is contained in:
Andras Bacsai committed 2026-02-25 18:53:39 +01:00
commit f09bbb4a04
36 files changed
+915 -175

No files matched your search

@@ -165,12 +165,69 @@ it('allows valid health check values via API rules', function () {
expect($validator->fails())->toBeFalse();
});
it('generates CMD healthcheck command directly', function () {
$result = callGenerateHealthcheckCommands([
'health_check_type' => 'cmd',
'health_check_command' => 'pg_isready -U postgres',
]);
expect($result)->toBe('pg_isready -U postgres');
});
it('strips newlines from CMD healthcheck command', function () {
$result = callGenerateHealthcheckCommands([
'health_check_type' => 'cmd',
'health_check_command' => "redis-cli ping\n&& echo pwned",
]);
expect($result)->not->toContain("\n")
->and($result)->toBe('redis-cli ping && echo pwned');
});
it('falls back to HTTP healthcheck when CMD type has empty command', function () {
$result = callGenerateHealthcheckCommands([
'health_check_type' => 'cmd',
'health_check_command' => '',
]);
// Should fall through to HTTP path
expect($result)->toContain('curl -s -X');
});
it('validates healthCheckCommand rejects strings over 1000 characters', function () {
$rules = [
'healthCheckCommand' => 'nullable|string|max:1000',
];
$validator = Validator::make(
['healthCheckCommand' => str_repeat('a', 1001)],
$rules
);
expect($validator->fails())->toBeTrue();
});
it('validates healthCheckCommand accepts strings under 1000 characters', function () {
$rules = [
'healthCheckCommand' => 'nullable|string|max:1000',
];
$validator = Validator::make(
['healthCheckCommand' => 'pg_isready -U postgres'],
$rules
);
expect($validator->fails())->toBeFalse();
});
/**
* Helper: Invokes the private generate_healthcheck_commands() method via reflection.
*/
function callGenerateHealthcheckCommands(array $overrides = []): string
{
$defaults = [
'health_check_type' => 'http',
'health_check_command' => null,
'health_check_method' => 'GET',
'health_check_scheme' => 'http',
'health_check_host' => 'localhost',
@@ -182,6 +239,8 @@ function callGenerateHealthcheckCommands(array $overrides = []): string
$values = array_merge($defaults, $overrides);
$application = Mockery::mock(Application::class)->makePartial();
$application->shouldReceive('getAttribute')->with('health_check_type')->andReturn($values['health_check_type']);
$application->shouldReceive('getAttribute')->with('health_check_command')->andReturn($values['health_check_command']);
$application->shouldReceive('getAttribute')->with('health_check_method')->andReturn($values['health_check_method']);
$application->shouldReceive('getAttribute')->with('health_check_scheme')->andReturn($values['health_check_scheme']);
$application->shouldReceive('getAttribute')->with('health_check_host')->andReturn($values['health_check_host']);
+78 -40
View File
@@ -1,6 +1,5 @@
<?php
use App\Models\GithubApp;
use App\Models\User;
use App\Policies\GithubAppPolicy;
@@ -14,9 +13,12 @@ it('allows any user to view any github apps', function () {
it('allows any user to view system-wide github app', function () {
$user = Mockery::mock(User::class)->makePartial();
$model = Mockery::mock(GithubApp::class)->makePartial();
$model->team_id = 1;
$model->is_system_wide = true;
$model = new class
{
public $team_id = 1;
public $is_system_wide = true;
};
$policy = new GithubAppPolicy;
expect($policy->view($user, $model))->toBeTrue();
@@ -30,9 +32,12 @@ it('allows team member to view non-system-wide github app', function () {
$user = Mockery::mock(User::class)->makePartial();
$user->shouldReceive('getAttribute')->with('teams')->andReturn($teams);
$model = Mockery::mock(GithubApp::class)->makePartial();
$model->team_id = 1;
$model->is_system_wide = false;
$model = new class
{
public $team_id = 1;
public $is_system_wide = false;
};
$policy = new GithubAppPolicy;
expect($policy->view($user, $model))->toBeTrue();
@@ -46,9 +51,12 @@ it('denies non-team member to view non-system-wide github app', function () {
$user = Mockery::mock(User::class)->makePartial();
$user->shouldReceive('getAttribute')->with('teams')->andReturn($teams);
$model = Mockery::mock(GithubApp::class)->makePartial();
$model->team_id = 1;
$model->is_system_wide = false;
$model = new class
{
public $team_id = 1;
public $is_system_wide = false;
};
$policy = new GithubAppPolicy;
expect($policy->view($user, $model))->toBeFalse();
@@ -74,9 +82,12 @@ it('allows user with system access to update system-wide github app', function (
$user = Mockery::mock(User::class)->makePartial();
$user->shouldReceive('canAccessSystemResources')->andReturn(true);
$model = Mockery::mock(GithubApp::class)->makePartial();
$model->team_id = 1;
$model->is_system_wide = true;
$model = new class
{
public $team_id = 1;
public $is_system_wide = true;
};
$policy = new GithubAppPolicy;
expect($policy->update($user, $model))->toBeTrue();
@@ -86,9 +97,12 @@ it('denies user without system access to update system-wide github app', functio
$user = Mockery::mock(User::class)->makePartial();
$user->shouldReceive('canAccessSystemResources')->andReturn(false);
$model = Mockery::mock(GithubApp::class)->makePartial();
$model->team_id = 1;
$model->is_system_wide = true;
$model = new class
{
public $team_id = 1;
public $is_system_wide = true;
};
$policy = new GithubAppPolicy;
expect($policy->update($user, $model))->toBeFalse();
@@ -98,9 +112,12 @@ it('allows team admin to update non-system-wide github app', function () {
$user = Mockery::mock(User::class)->makePartial();
$user->shouldReceive('isAdminOfTeam')->with(1)->andReturn(true);
$model = Mockery::mock(GithubApp::class)->makePartial();
$model->team_id = 1;
$model->is_system_wide = false;
$model = new class
{
public $team_id = 1;
public $is_system_wide = false;
};
$policy = new GithubAppPolicy;
expect($policy->update($user, $model))->toBeTrue();
@@ -110,9 +127,12 @@ it('denies team member to update non-system-wide github app', function () {
$user = Mockery::mock(User::class)->makePartial();
$user->shouldReceive('isAdminOfTeam')->with(1)->andReturn(false);
$model = Mockery::mock(GithubApp::class)->makePartial();
$model->team_id = 1;
$model->is_system_wide = false;
$model = new class
{
public $team_id = 1;
public $is_system_wide = false;
};
$policy = new GithubAppPolicy;
expect($policy->update($user, $model))->toBeFalse();
@@ -122,9 +142,12 @@ it('allows user with system access to delete system-wide github app', function (
$user = Mockery::mock(User::class)->makePartial();
$user->shouldReceive('canAccessSystemResources')->andReturn(true);
$model = Mockery::mock(GithubApp::class)->makePartial();
$model->team_id = 1;
$model->is_system_wide = true;
$model = new class
{
public $team_id = 1;
public $is_system_wide = true;
};
$policy = new GithubAppPolicy;
expect($policy->delete($user, $model))->toBeTrue();
@@ -134,9 +157,12 @@ it('denies user without system access to delete system-wide github app', functio
$user = Mockery::mock(User::class)->makePartial();
$user->shouldReceive('canAccessSystemResources')->andReturn(false);
$model = Mockery::mock(GithubApp::class)->makePartial();
$model->team_id = 1;
$model->is_system_wide = true;
$model = new class
{
public $team_id = 1;
public $is_system_wide = true;
};
$policy = new GithubAppPolicy;
expect($policy->delete($user, $model))->toBeFalse();
@@ -146,9 +172,12 @@ it('allows team admin to delete non-system-wide github app', function () {
$user = Mockery::mock(User::class)->makePartial();
$user->shouldReceive('isAdminOfTeam')->with(1)->andReturn(true);
$model = Mockery::mock(GithubApp::class)->makePartial();
$model->team_id = 1;
$model->is_system_wide = false;
$model = new class
{
public $team_id = 1;
public $is_system_wide = false;
};
$policy = new GithubAppPolicy;
expect($policy->delete($user, $model))->toBeTrue();
@@ -158,9 +187,12 @@ it('denies team member to delete non-system-wide github app', function () {
$user = Mockery::mock(User::class)->makePartial();
$user->shouldReceive('isAdminOfTeam')->with(1)->andReturn(false);
$model = Mockery::mock(GithubApp::class)->makePartial();
$model->team_id = 1;
$model->is_system_wide = false;
$model = new class
{
public $team_id = 1;
public $is_system_wide = false;
};
$policy = new GithubAppPolicy;
expect($policy->delete($user, $model))->toBeFalse();
@@ -169,9 +201,12 @@ it('denies team member to delete non-system-wide github app', function () {
it('denies restore of github app', function () {
$user = Mockery::mock(User::class)->makePartial();
$model = Mockery::mock(GithubApp::class)->makePartial();
$model->team_id = 1;
$model->is_system_wide = false;
$model = new class
{
public $team_id = 1;
public $is_system_wide = false;
};
$policy = new GithubAppPolicy;
expect($policy->restore($user, $model))->toBeFalse();
@@ -180,9 +215,12 @@ it('denies restore of github app', function () {
it('denies force delete of github app', function () {
$user = Mockery::mock(User::class)->makePartial();
$model = Mockery::mock(GithubApp::class)->makePartial();
$model->team_id = 1;
$model->is_system_wide = false;
$model = new class
{
public $team_id = 1;
public $is_system_wide = false;
};
$policy = new GithubAppPolicy;
expect($policy->forceDelete($user, $model))->toBeFalse();
@@ -1,6 +1,5 @@
<?php
use App\Models\SharedEnvironmentVariable;
use App\Models\User;
use App\Policies\SharedEnvironmentVariablePolicy;
@@ -19,8 +18,10 @@ it('allows team member to view their team shared environment variable', function
$user = Mockery::mock(User::class)->makePartial();
$user->shouldReceive('getAttribute')->with('teams')->andReturn($teams);
$model = Mockery::mock(SharedEnvironmentVariable::class)->makePartial();
$model->team_id = 1;
$model = new class
{
public $team_id = 1;
};
$policy = new SharedEnvironmentVariablePolicy;
expect($policy->view($user, $model))->toBeTrue();
@@ -34,8 +35,10 @@ it('denies non-team member to view shared environment variable', function () {
$user = Mockery::mock(User::class)->makePartial();
$user->shouldReceive('getAttribute')->with('teams')->andReturn($teams);
$model = Mockery::mock(SharedEnvironmentVariable::class)->makePartial();
$model->team_id = 2;
$model = new class
{
public $team_id = 2;
};
$policy = new SharedEnvironmentVariablePolicy;
expect($policy->view($user, $model))->toBeFalse();
@@ -61,8 +64,10 @@ it('allows team admin to update shared environment variable', function () {
$user = Mockery::mock(User::class)->makePartial();
$user->shouldReceive('isAdminOfTeam')->with(1)->andReturn(true);
$model = Mockery::mock(SharedEnvironmentVariable::class)->makePartial();
$model->team_id = 1;
$model = new class
{
public $team_id = 1;
};
$policy = new SharedEnvironmentVariablePolicy;
expect($policy->update($user, $model))->toBeTrue();
@@ -72,8 +77,10 @@ it('denies team member to update shared environment variable', function () {
$user = Mockery::mock(User::class)->makePartial();
$user->shouldReceive('isAdminOfTeam')->with(1)->andReturn(false);
$model = Mockery::mock(SharedEnvironmentVariable::class)->makePartial();
$model->team_id = 1;
$model = new class
{
public $team_id = 1;
};
$policy = new SharedEnvironmentVariablePolicy;
expect($policy->update($user, $model))->toBeFalse();
@@ -83,8 +90,10 @@ it('allows team admin to delete shared environment variable', function () {
$user = Mockery::mock(User::class)->makePartial();
$user->shouldReceive('isAdminOfTeam')->with(1)->andReturn(true);
$model = Mockery::mock(SharedEnvironmentVariable::class)->makePartial();
$model->team_id = 1;
$model = new class
{
public $team_id = 1;
};
$policy = new SharedEnvironmentVariablePolicy;
expect($policy->delete($user, $model))->toBeTrue();
@@ -94,8 +103,10 @@ it('denies team member to delete shared environment variable', function () {
$user = Mockery::mock(User::class)->makePartial();
$user->shouldReceive('isAdminOfTeam')->with(1)->andReturn(false);
$model = Mockery::mock(SharedEnvironmentVariable::class)->makePartial();
$model->team_id = 1;
$model = new class
{
public $team_id = 1;
};
$policy = new SharedEnvironmentVariablePolicy;
expect($policy->delete($user, $model))->toBeFalse();
@@ -104,8 +115,10 @@ it('denies team member to delete shared environment variable', function () {
it('denies restore of shared environment variable', function () {
$user = Mockery::mock(User::class)->makePartial();
$model = Mockery::mock(SharedEnvironmentVariable::class)->makePartial();
$model->team_id = 1;
$model = new class
{
public $team_id = 1;
};
$policy = new SharedEnvironmentVariablePolicy;
expect($policy->restore($user, $model))->toBeFalse();
@@ -114,8 +127,10 @@ it('denies restore of shared environment variable', function () {
it('denies force delete of shared environment variable', function () {
$user = Mockery::mock(User::class)->makePartial();
$model = Mockery::mock(SharedEnvironmentVariable::class)->makePartial();
$model->team_id = 1;
$model = new class
{
public $team_id = 1;
};
$policy = new SharedEnvironmentVariablePolicy;
expect($policy->forceDelete($user, $model))->toBeFalse();
@@ -125,8 +140,10 @@ it('allows team admin to manage environment', function () {
$user = Mockery::mock(User::class)->makePartial();
$user->shouldReceive('isAdminOfTeam')->with(1)->andReturn(true);
$model = Mockery::mock(SharedEnvironmentVariable::class)->makePartial();
$model->team_id = 1;
$model = new class
{
public $team_id = 1;
};
$policy = new SharedEnvironmentVariablePolicy;
expect($policy->manageEnvironment($user, $model))->toBeTrue();
@@ -136,8 +153,10 @@ it('denies team member to manage environment', function () {
$user = Mockery::mock(User::class)->makePartial();
$user->shouldReceive('isAdminOfTeam')->with(1)->andReturn(false);
$model = Mockery::mock(SharedEnvironmentVariable::class)->makePartial();
$model->team_id = 1;
$model = new class
{
public $team_id = 1;
};
$policy = new SharedEnvironmentVariablePolicy;
expect($policy->manageEnvironment($user, $model))->toBeFalse();
+1 -1
View File
@@ -24,7 +24,7 @@ it('uses WithoutOverlapping middleware with expireAfter to prevent stale locks',
$expiresAfterProperty->setAccessible(true);
$expiresAfter = $expiresAfterProperty->getValue($overlappingMiddleware);
expect($expiresAfter)->toBe(60)
expect($expiresAfter)->toBe(90)
->and($expiresAfter)->toBeGreaterThan(0, 'expireAfter must be set to prevent stale locks');
// Check releaseAfter is NOT set (we use dontRelease)