[ '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)); });