fix(validation): add input validation for database backup timeout (#9245)

This commit is contained in:
Andras Bacsai
2026-03-30 20:59:37 +02:00
committed by GitHub
3 changed files with 28 additions and 13 deletions
@@ -643,6 +643,7 @@ class DatabasesController extends Controller
'database_backup_retention_amount_s3' => ['type' => 'integer', 'description' => 'Number of backups to retain in S3'],
'database_backup_retention_days_s3' => ['type' => 'integer', 'description' => 'Number of days to retain backups in S3'],
'database_backup_retention_max_storage_s3' => ['type' => 'integer', 'description' => 'Max storage (MB) for S3 backups'],
'timeout' => ['type' => 'integer', 'description' => 'Backup job timeout in seconds (min: 60, max: 36000)', 'default' => 3600],
],
),
)
@@ -679,7 +680,7 @@ class DatabasesController extends Controller
)]
public function create_backup(Request $request)
{
$backupConfigFields = ['save_s3', 'enabled', 'dump_all', 'frequency', 'databases_to_backup', 'database_backup_retention_amount_locally', 'database_backup_retention_days_locally', 'database_backup_retention_max_storage_locally', 'database_backup_retention_amount_s3', 'database_backup_retention_days_s3', 'database_backup_retention_max_storage_s3', 's3_storage_uuid'];
$backupConfigFields = ['save_s3', 'enabled', 'dump_all', 'frequency', 'databases_to_backup', 'database_backup_retention_amount_locally', 'database_backup_retention_days_locally', 'database_backup_retention_max_storage_locally', 'database_backup_retention_amount_s3', 'database_backup_retention_days_s3', 'database_backup_retention_max_storage_s3', 's3_storage_uuid', 'timeout'];
$teamId = getTeamIdFromToken();
if (is_null($teamId)) {
@@ -706,6 +707,7 @@ class DatabasesController extends Controller
'database_backup_retention_amount_s3' => 'integer|min:0',
'database_backup_retention_days_s3' => 'integer|min:0',
'database_backup_retention_max_storage_s3' => 'integer|min:0',
'timeout' => 'integer|min:60|max:36000',
]);
if ($validator->fails()) {
@@ -880,6 +882,7 @@ class DatabasesController extends Controller
'database_backup_retention_amount_s3' => ['type' => 'integer', 'description' => 'Retention amount of the backup in s3'],
'database_backup_retention_days_s3' => ['type' => 'integer', 'description' => 'Retention days of the backup in s3'],
'database_backup_retention_max_storage_s3' => ['type' => 'integer', 'description' => 'Max storage of the backup in S3'],
'timeout' => ['type' => 'integer', 'description' => 'Backup job timeout in seconds (min: 60, max: 36000)', 'default' => 3600],
],
),
)
@@ -909,7 +912,7 @@ class DatabasesController extends Controller
)]
public function update_backup(Request $request)
{
$backupConfigFields = ['save_s3', 'enabled', 'dump_all', 'frequency', 'databases_to_backup', 'database_backup_retention_amount_locally', 'database_backup_retention_days_locally', 'database_backup_retention_max_storage_locally', 'database_backup_retention_amount_s3', 'database_backup_retention_days_s3', 'database_backup_retention_max_storage_s3', 's3_storage_uuid'];
$backupConfigFields = ['save_s3', 'enabled', 'dump_all', 'frequency', 'databases_to_backup', 'database_backup_retention_amount_locally', 'database_backup_retention_days_locally', 'database_backup_retention_max_storage_locally', 'database_backup_retention_amount_s3', 'database_backup_retention_days_s3', 'database_backup_retention_max_storage_s3', 's3_storage_uuid', 'timeout'];
$teamId = getTeamIdFromToken();
if (is_null($teamId)) {
@@ -927,13 +930,14 @@ class DatabasesController extends Controller
'dump_all' => 'boolean',
's3_storage_uuid' => 'string|exists:s3_storages,uuid|nullable',
'databases_to_backup' => 'string|nullable',
'frequency' => 'string|in:every_minute,hourly,daily,weekly,monthly,yearly',
'frequency' => 'string',
'database_backup_retention_amount_locally' => 'integer|min:0',
'database_backup_retention_days_locally' => 'integer|min:0',
'database_backup_retention_max_storage_locally' => 'integer|min:0',
'database_backup_retention_amount_s3' => 'integer|min:0',
'database_backup_retention_days_s3' => 'integer|min:0',
'database_backup_retention_max_storage_s3' => 'integer|min:0',
'timeout' => 'integer|min:60|max:36000',
]);
if ($validator->fails()) {
return response()->json([
@@ -960,6 +964,17 @@ class DatabasesController extends Controller
$this->authorize('update', $database);
// Validate frequency is a valid cron expression
if ($request->filled('frequency')) {
$isValid = validate_cron_expression($request->frequency);
if (! $isValid) {
return response()->json([
'message' => 'Validation failed.',
'errors' => ['frequency' => ['Invalid cron expression or frequency format.']],
], 422);
}
}
if ($request->boolean('save_s3') && ! $request->filled('s3_storage_uuid')) {
return response()->json([
'message' => 'Validation failed.',
+1 -1
View File
@@ -76,7 +76,7 @@ class BackupEdit extends Component
public bool $dumpAll = false;
#[Validate(['required', 'int', 'min:60', 'max:36000'])]
public int $timeout = 3600;
public int|string $timeout = 3600;
public function mount()
{
@@ -81,10 +81,10 @@
@endif
</div>
<div class="flex gap-2">
<x-forms.input label="Frequency" id="frequency" />
<x-forms.input label="Frequency" id="frequency" required />
<x-forms.input label="Timezone" id="timezone" disabled
helper="The timezone of the server where the backup is scheduled to run (if not set, the instance timezone will be used)" />
<x-forms.input label="Timeout" id="timeout" helper="The timeout of the backup job in seconds." />
helper="The timezone of the server where the backup is scheduled to run (if not set, the instance timezone will be used)" required />
<x-forms.input label="Timeout" id="timeout" type="number" min="60" helper="The timeout of the backup job in seconds." required />
</div>
<h3 class="mt-6 mb-2 text-lg font-medium">Backup Retention Settings</h3>
@@ -101,13 +101,13 @@
<div class="flex gap-2">
<x-forms.input label="Number of backups to keep" id="databaseBackupRetentionAmountLocally"
type="number" min="0"
helper="Keeps only the specified number of most recent backups on the server. Set to 0 for unlimited backups." />
helper="Keeps only the specified number of most recent backups on the server. Set to 0 for unlimited backups." required />
<x-forms.input label="Days to keep backups" id="databaseBackupRetentionDaysLocally" type="number"
min="0"
helper="Automatically removes backups older than the specified number of days. Set to 0 for no time limit." />
helper="Automatically removes backups older than the specified number of days. Set to 0 for no time limit." required />
<x-forms.input label="Maximum storage (GB)" id="databaseBackupRetentionMaxStorageLocally"
type="number" min="0"
helper="When total size of all backups in the current backup job exceeds this limit in GB, the oldest backups will be removed. Decimal values are supported (e.g. 0.001 for 1MB). Set to 0 for unlimited storage." />
helper="When total size of all backups in the current backup job exceeds this limit in GB, the oldest backups will be removed. Decimal values are supported (e.g. 0.001 for 1MB). Set to 0 for unlimited storage." required />
</div>
</div>
@@ -117,13 +117,13 @@
<div class="flex gap-2">
<x-forms.input label="Number of backups to keep" id="databaseBackupRetentionAmountS3"
type="number" min="0"
helper="Keeps only the specified number of most recent backups on S3 storage. Set to 0 for unlimited backups." />
helper="Keeps only the specified number of most recent backups on S3 storage. Set to 0 for unlimited backups." required />
<x-forms.input label="Days to keep backups" id="databaseBackupRetentionDaysS3" type="number"
min="0"
helper="Automatically removes S3 backups older than the specified number of days. Set to 0 for no time limit." />
helper="Automatically removes S3 backups older than the specified number of days. Set to 0 for no time limit." required />
<x-forms.input label="Maximum storage (GB)" id="databaseBackupRetentionMaxStorageS3"
type="number" min="0"
helper="When total size of all backups in the current backup job exceeds this limit in GB, the oldest backups will be removed. Decimal values are supported (e.g. 0.5 for 500MB). Set to 0 for unlimited storage." />
helper="When total size of all backups in the current backup job exceeds this limit in GB, the oldest backups will be removed. Decimal values are supported (e.g. 0.5 for 500MB). Set to 0 for unlimited storage." required />
</div>
</div>
@endif