Major architectural improvements:
- Merged download and restore into single atomic operation
- Eliminated separate S3DownloadFinished event (redundant)
- Files now transfer directly: S3 → helper container → server → database container
- Removed download progress tracking in favor of unified restore progress
UI/UX improvements:
- Unified restore method selection with visual cards
- Consistent "File Information" display between local and S3 restore
- Single slide-over for all restore operations (removed separate S3 download monitor)
- Better visual feedback with loading states
Security enhancements:
- Added isSafeTmpPath() helper for path traversal protection
- URL decode validation to catch encoded attacks
- Canonical path resolution to prevent symlink attacks
- Comprehensive path validation in all cleanup events
Cleanup improvements:
- S3RestoreJobFinished now handles all cleanup (helper container + all temp files)
- RestoreJobFinished uses new isSafeTmpPath() validation
- CoolifyTask dispatches cleanup events even on job failure
- All cleanup uses non-throwing commands (2>/dev/null || true)
Other improvements:
- S3 storage policy authorization on Show component
- Storage Form properly syncs is_usable state after test
- Removed debug code and improved error handling
- Better command organization and documentation
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
61 lines
1.9 KiB
PHP
61 lines
1.9 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Security tests for RestoreJobFinished event to ensure it uses secure path validation.
|
|
*/
|
|
describe('RestoreJobFinished event security', function () {
|
|
it('validates that safe paths pass validation', function () {
|
|
$validPaths = [
|
|
'/tmp/restore-backup.sql',
|
|
'/tmp/restore-script.sh',
|
|
'/tmp/database-dump-'.uniqid().'.sql',
|
|
];
|
|
|
|
foreach ($validPaths as $path) {
|
|
expect(isSafeTmpPath($path))->toBeTrue();
|
|
}
|
|
});
|
|
|
|
it('validates that malicious paths fail validation', function () {
|
|
$maliciousPaths = [
|
|
'/tmp/../etc/passwd',
|
|
'/tmp/foo/../../etc/shadow',
|
|
'/etc/sensitive-file',
|
|
'/var/www/config.php',
|
|
'/tmp/../../../root/.ssh/id_rsa',
|
|
];
|
|
|
|
foreach ($maliciousPaths as $path) {
|
|
expect(isSafeTmpPath($path))->toBeFalse();
|
|
}
|
|
});
|
|
|
|
it('rejects URL-encoded path traversal attempts', function () {
|
|
$encodedTraversalPaths = [
|
|
'/tmp/%2e%2e/etc/passwd',
|
|
'/tmp/foo%2f%2e%2e%2f%2e%2e/etc/shadow',
|
|
urlencode('/tmp/../etc/passwd'),
|
|
];
|
|
|
|
foreach ($encodedTraversalPaths as $path) {
|
|
expect(isSafeTmpPath($path))->toBeFalse();
|
|
}
|
|
});
|
|
|
|
it('handles edge cases correctly', function () {
|
|
// Too short
|
|
expect(isSafeTmpPath('/tmp'))->toBeFalse();
|
|
expect(isSafeTmpPath('/tmp/'))->toBeFalse();
|
|
|
|
// Null/empty
|
|
expect(isSafeTmpPath(null))->toBeFalse();
|
|
expect(isSafeTmpPath(''))->toBeFalse();
|
|
|
|
// Null byte injection
|
|
expect(isSafeTmpPath("/tmp/file.sql\0../../etc/passwd"))->toBeFalse();
|
|
|
|
// Valid edge cases
|
|
expect(isSafeTmpPath('/tmp/x'))->toBeTrue();
|
|
expect(isSafeTmpPath('/tmp/very/deeply/nested/path/to/file.sql'))->toBeTrue();
|
|
});
|
|
});
|