false); $html = Blade::render(<<<'BLADE' BLADE); expect($html)->toMatch('/]*id="status-trigger"[^>]*\sdisabled(?:[=\s>])/'); }); it('declares gate attributes on form controls with update permission checks', function () { $controlPattern = '/|<\/x-forms\.[^>]+>)/s'; foreach (File::allFiles(resource_path('views')) as $file) { $path = $file->getPathname(); $source = file_get_contents($path); preg_match_all($controlPattern, $source, $controls); foreach ($controls[0] as $control) { if (! preg_match('/can\(\s*[\'\"]update[\'\"]/', $control)) { continue; } expect($control, $path)->toContain('canGate="update"') ->toContain(':canResource='); } } }); it('hides resource action menus when the user cannot manage the resource', function (string $path, string $ability, string $resource, string $prefix) { $source = file_get_contents(resource_path($path)); foreach (['mobile', 'desktop'] as $viewport) { expect($source)->toMatch( "/@can\\('{$ability}', \\$".$resource."\\)[\\s\\S]*?
with([ 'application actions' => ['views/livewire/project/application/heading.blade.php', 'deploy', 'application', 'application'], 'service actions' => ['views/livewire/project/service/heading.blade.php', 'deploy', 'service', 'service'], 'database actions' => ['views/livewire/project/database/heading.blade.php', 'manage', 'database', 'database'], 'server actions' => ['views/livewire/server/navbar.blade.php', 'manageProxy', 'server', 'server'], ]); it('keeps mutable Livewire components behind authorization checks', function (string $path, array $requiredNeedles) { $source = file_get_contents(base_path($path)); foreach ($requiredNeedles as $needle) { expect($source)->toContain($needle); } })->with([ 'storage resources' => [ 'app/Livewire/Storage/Resources.php', ['AuthorizesRequests', "authorize('update'", "authorize('view'"], ], 'postgres init script editor' => [ 'app/Livewire/Project/Database/InitScript.php', ['AuthorizesRequests', "authorize('update'"], ], 'execute container command' => [ 'app/Livewire/Project/Shared/ExecuteContainerCommand.php', ['AuthorizesRequests', "authorize('view'", "authorize('canAccessTerminal'"], ], 'terminal' => [ 'app/Livewire/Project/Shared/Terminal.php', ['AuthorizesRequests', "authorize('view'", "authorize('canAccessTerminal'"], ], ]); it('authorizes every volume backup form control', function (string $path, array $controlPatterns) { $source = file_get_contents(base_path($path)); foreach ($controlPatterns as $controlPattern) { expect($source)->toMatch($controlPattern); } })->with([ 'retention controls' => [ 'resources/views/livewire/project/shared/storages/volume-backups/retention.blade.php', [ '/]*type="submit")(?=[^>]*canGate="update")(?=[^>]*:canResource="\$backup")[^>]*>Save<\/x-forms\.button>/s', '/]*id="retentionAmountLocally")(?=[^>]*canGate="update")(?=[^>]*:canResource="\$backup")[^>]*\/\>/s', '/]*id="retentionDaysLocally")(?=[^>]*canGate="update")(?=[^>]*:canResource="\$backup")[^>]*\/\>/s', '/]*id="retentionMaxStorageLocally")(?=[^>]*canGate="update")(?=[^>]*:canResource="\$backup")[^>]*\/\>/s', '/]*id="retentionAmountS3")(?=[^>]*canGate="update")(?=[^>]*:canResource="\$backup")[^>]*\/\>/s', '/]*id="retentionDaysS3")(?=[^>]*canGate="update")(?=[^>]*:canResource="\$backup")[^>]*\/\>/s', '/]*id="retentionMaxStorageS3")(?=[^>]*canGate="update")(?=[^>]*:canResource="\$backup")[^>]*\/\>/s', ], ], 'S3 controls' => [ 'resources/views/livewire/project/shared/storages/volume-backups/s3.blade.php', [ '/]*type="submit")(?=[^>]*canGate="update")(?=[^>]*:canResource="\$backup")[^>]*>Save<\/x-forms\.button>/s', '/]*wire:click="toggleS3")(?=[^>]*canGate="update")(?=[^>]*:canResource="\$backup")[^>]*>Enable S3<\/x-forms\.button>/s', '/]*wire:click="toggleS3")(?=[^>]*canGate="update")(?=[^>]*:canResource="\$backup")[^>]*>Disable S3<\/x-forms\.button>/s', '/]*id="s3StorageId")(?=[^>]*canGate="update")(?=[^>]*:canResource="\$backup")[^>]*>/s', '/]*id="disableLocalBackup")(?=[^>]*instantSave)(?=[^>]*canGate="update")(?=[^>]*:canResource="\$backup")[^>]*\/\>/s', '/]*id="disableLocalBackup")(?=[^>]*disabled)(?=[^>]*canGate="update")(?=[^>]*:canResource="\$backup")[^>]*\/\>/s', ], ], 'service domains controls' => [ 'resources/views/livewire/project/service/domains.blade.php', [ '/]*canGate="update")(?=[^>]*:canResource="\$service")(?=[^>]*id="newServiceApplicationId")[^>]*>/s', '/]*canGate="update")(?=[^>]*:canResource="\$service")(?=[^>]*id="newDomain")[^>]*>/s', '/]*canGate="update")(?=[^>]*:canResource="\$service")(?=[^>]*wire:click="generateDomain")[^>]*>/s', '/]*canGate="update")(?=[^>]*:canResource="\$service")(?=[^>]*type="submit")[^>]*>/s', '/wire:click="checkAllDns"/s', ], ], 'postgres public access control' => [ 'resources/views/livewire/project/database/postgresql/general.blade.php', [ '/ [ 'resources/views/livewire/project/database/redis/general.blade.php', ['/ [ 'resources/views/livewire/project/database/mongodb/general.blade.php', ['/ [ 'resources/views/livewire/project/database/clickhouse/general.blade.php', ['/ [ 'resources/views/livewire/project/database/mariadb/general.blade.php', ['/ [ 'resources/views/livewire/project/database/dragonfly/general.blade.php', ['/ [ 'resources/views/livewire/project/database/mysql/general.blade.php', ['/ [ 'resources/views/livewire/project/database/keydb/general.blade.php', ['/