feat(s3): implemented changes for safety & clarity

This commit is contained in:
Karl Bauer 2025-12-26 14:46:47 +01:00
parent 830762f0ac
commit 062ad02e15
4 changed files with 25 additions and 4 deletions

View file

@ -44,7 +44,7 @@ class Form extends Component
'secret' => 'required|max:255',
'bucket' => 'required|max:255',
'endpoint' => 'required|url|max:255',
'path' => 'nullable|max:255|regex:/^[a-zA-Z0-9\/\-_\.]*$/',
'path' => ['nullable', 'max:255', 'regex:/^[a-zA-Z0-9\/\-_\.]*$/', 'not_regex:/\.\./'],
];
}
@ -68,6 +68,7 @@ class Form extends Component
'endpoint.max' => 'The Endpoint may not be greater than 255 characters.',
'path.max' => 'The Path Prefix may not be greater than 255 characters.',
'path.regex' => 'The Path Prefix may only contain letters, numbers, slashes (/), dashes (-), underscores (_), and dots (.).',
'path.not_regex' => 'The Path Prefix may not contain directory traversal sequences (..).',
]
);
}

View file

@ -213,7 +213,7 @@ function deleteBackupsS3(string|array|null $filenames, S3Storage $s3): void
if (filled($s3->path)) {
$pathPrefix = ltrim($s3->path, '/');
$filenames = array_map(function ($filename) use ($pathPrefix) {
// The filename is the local path (e.g., /var/lib/coolify/backups/databases/...)
// The filename is the backup path (e.g., /data/coolify/backups/databases/...)
// We need to prepend the path prefix to match the S3 key structure
$cleanFilename = ltrim($filename, '/');

View file

@ -42,8 +42,8 @@
</div>
<div class="flex gap-2">
<x-forms.input canGate="update" :canResource="$storage" label="Path Prefix" id="path"
placeholder="e.g., backups/coolify-instance-1"
helper="Optional path prefix for all backups. Useful for storing multiple Coolify instances in one bucket." />
placeholder="e.g., production or instance-1"
helper="Optional prefix added before the standard backup path (/data/coolify/backups/...). Useful for separating multiple Coolify instances in a single bucket." />
</div>
<div class="flex gap-2">
<x-forms.input canGate="update" :canResource="$storage" required type="password" label="Access Key"

View file

@ -91,3 +91,23 @@ test('S3Storage path attribute handles various path formats', function () {
$s3Storage->path = 'my-instance_2024.backups';
expect($s3Storage->path)->toBe('/my-instance_2024.backups');
});
test('S3Storage path attribute handles edge cases', function () {
$s3Storage = new S3Storage;
// Multiple consecutive slashes are preserved (validation should catch this)
$s3Storage->path = 'path//to///backup';
expect($s3Storage->path)->toBe('/path//to///backup');
// Path ending with slash
$s3Storage->path = 'backups/coolify/';
expect($s3Storage->path)->toBe('/backups/coolify/');
// Only whitespace should return null
$s3Storage->path = ' ';
expect($s3Storage->path)->toBeNull();
// Path with dots (valid single dots)
$s3Storage->path = 'path.with.dots';
expect($s3Storage->path)->toBe('/path.with.dots');
});