Implements explicit UiActionContext contract as requested. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #434
125 lines
5.1 KiB
PHP
125 lines
5.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
function scopedUiActionContextContractChecks(): array
|
|
{
|
|
return [
|
|
'inventory sync header action' => [
|
|
'file' => 'app/Filament/Resources/InventoryItemResource/Pages/ListInventoryItems.php',
|
|
'action' => 'run_inventory_sync',
|
|
],
|
|
'policy sync action factory' => [
|
|
'file' => 'app/Filament/Resources/PolicyResource.php',
|
|
'action' => 'sync',
|
|
'anchor' => 'public static function makeSyncAction',
|
|
],
|
|
'directory group sync header action' => [
|
|
'file' => 'app/Filament/Resources/EntraGroupResource/Pages/ListEntraGroups.php',
|
|
'action' => 'sync_groups',
|
|
],
|
|
'evidence snapshot header action' => [
|
|
'file' => 'app/Filament/Resources/EvidenceSnapshotResource/Pages/ListEvidenceSnapshots.php',
|
|
'action' => 'create_snapshot',
|
|
],
|
|
'evidence snapshot empty-state action' => [
|
|
'file' => 'app/Filament/Resources/EvidenceSnapshotResource.php',
|
|
'action' => 'create_first_snapshot',
|
|
],
|
|
'review pack generation action factory' => [
|
|
'file' => 'app/Filament/Resources/ReviewPackResource.php',
|
|
'action' => 'generate_pack',
|
|
'anchor' => 'public static function generatePackAction',
|
|
],
|
|
'environment review creation action factory' => [
|
|
'file' => 'app/Filament/Resources/EnvironmentReviewResource.php',
|
|
'action' => 'create_review',
|
|
'anchor' => 'public static function makeCreateReviewAction',
|
|
],
|
|
'environment diagnostics bootstrap action' => [
|
|
'file' => 'app/Filament/Pages/EnvironmentDiagnostics.php',
|
|
'action' => 'bootstrapOwner',
|
|
],
|
|
'environment diagnostics merge action' => [
|
|
'file' => 'app/Filament/Pages/EnvironmentDiagnostics.php',
|
|
'action' => 'mergeDuplicateMemberships',
|
|
],
|
|
'restore run create action factory' => [
|
|
'file' => 'app/Filament/Resources/RestoreRunResource.php',
|
|
'action' => 'New restore run',
|
|
'anchor' => 'public static function makeCreateAction',
|
|
],
|
|
'environment dashboard support request action' => [
|
|
'file' => 'app/Filament/Pages/EnvironmentDashboard.php',
|
|
'action' => 'requestSupport',
|
|
'anchor' => 'private function requestSupportAction',
|
|
],
|
|
'environment dashboard support diagnostics action' => [
|
|
'file' => 'app/Filament/Pages/EnvironmentDashboard.php',
|
|
'action' => 'openSupportDiagnostics',
|
|
'anchor' => 'private function openSupportDiagnosticsAction',
|
|
],
|
|
];
|
|
}
|
|
|
|
it('requires explicit UiActionContext for representative risky scoped no-record actions', function (array $check): void {
|
|
$path = base_path($check['file']);
|
|
$source = file_get_contents($path);
|
|
|
|
expect($source)->not->toBeFalse();
|
|
|
|
$source = (string) $source;
|
|
$anchor = $check['anchor'] ?? "Action::make('{$check['action']}'";
|
|
$position = strpos($source, $anchor);
|
|
|
|
expect($position)
|
|
->not->toBeFalse("Could not find {$check['action']} in {$check['file']}.");
|
|
|
|
$slice = substr($source, max(0, (int) $position - 500), 10000);
|
|
|
|
expect($slice)
|
|
->toContain('UiEnforcement::forScopedAction')
|
|
->and($source)
|
|
->toContain('UiActionContext');
|
|
})->with(fn (): array => collect(scopedUiActionContextContractChecks())
|
|
->mapWithKeys(static fn (array $check, string $name): array => [$name => [$check]])
|
|
->all());
|
|
|
|
it('does not reintroduce the guarded no-record scoped action names through UiEnforcement::forAction', function (): void {
|
|
$checks = scopedUiActionContextContractChecks();
|
|
$guardedFiles = collect($checks)->pluck('file')->unique()->values()->all();
|
|
$guardedActionNames = collect($checks)
|
|
->pluck('action')
|
|
->reject(static fn (string $action): bool => $action === 'sync' || $action === 'New restore run')
|
|
->map(static fn (string $action): string => preg_quote($action, '/'))
|
|
->implode('|');
|
|
|
|
$violations = [];
|
|
|
|
foreach ($guardedFiles as $relativePath) {
|
|
$path = base_path($relativePath);
|
|
$source = file_get_contents($path);
|
|
|
|
if (! is_string($source) || ! str_contains($source, 'UiEnforcement::forAction')) {
|
|
continue;
|
|
}
|
|
|
|
$patterns = [
|
|
'/UiEnforcement::forAction\s*\(.{0,1200}Action::make\(\s*[\'"](?:'.$guardedActionNames.')[\'"]/s',
|
|
'/public static function makeSyncAction.{0,1400}UiEnforcement::forAction/s',
|
|
'/public static function makeCreateAction.{0,1400}UiEnforcement::forAction/s',
|
|
];
|
|
|
|
foreach ($patterns as $pattern) {
|
|
if (preg_match($pattern, $source, $match, PREG_OFFSET_CAPTURE) !== 1) {
|
|
continue;
|
|
}
|
|
|
|
$line = substr_count(substr($source, 0, (int) $match[0][1]), "\n") + 1;
|
|
$violations[] = "{$relativePath}:{$line} use UiEnforcement::forScopedAction(..., UiActionContext resolver) for risky no-record scoped actions.";
|
|
}
|
|
}
|
|
|
|
expect($violations)->toBeEmpty(implode("\n", $violations));
|
|
});
|