Merge remote-tracking branch 'origin/next' into audit-policies

This commit is contained in:
Andras Bacsai
2026-05-11 21:13:57 +02:00
259 changed files with 12779 additions and 3172 deletions
@@ -88,9 +88,15 @@ class General extends Component
return [
'name' => ValidationPatterns::nameRules(),
'description' => ValidationPatterns::descriptionRules(),
'postgresUser' => 'required',
'postgresPassword' => 'required',
'postgresDb' => 'required',
'postgresUser' => ValidationPatterns::databaseIdentifierRules(
enforcePattern: $this->postgresUser !== $this->database->postgres_user,
),
'postgresPassword' => ValidationPatterns::databasePasswordRules(
enforcePattern: $this->postgresPassword !== $this->database->postgres_password,
),
'postgresDb' => ValidationPatterns::databaseIdentifierRules(
enforcePattern: $this->postgresDb !== $this->database->postgres_db,
),
'postgresInitdbArgs' => 'nullable',
'postgresHostAuthMethod' => 'nullable',
'postgresConf' => 'nullable',
@@ -114,9 +120,9 @@ class General extends Component
ValidationPatterns::portMappingMessages(),
[
'name.required' => 'The Name field is required.',
'postgresUser.required' => 'The Postgres User field is required.',
'postgresPassword.required' => 'The Postgres Password field is required.',
'postgresDb.required' => 'The Postgres Database field is required.',
...ValidationPatterns::databaseIdentifierMessages('postgresUser', 'Postgres User'),
...ValidationPatterns::databasePasswordMessages('postgresPassword', 'Postgres Password'),
...ValidationPatterns::databaseIdentifierMessages('postgresDb', 'Postgres Database'),
'image.required' => 'The Docker Image field is required.',
'publicPort.integer' => 'The Public Port must be an integer.',
'publicPort.min' => 'The Public Port must be at least 1.',
@@ -361,9 +367,14 @@ class General extends Component
if ($oldScript && $oldScript['filename'] !== $script['filename']) {
try {
// Validate and escape filename to prevent command injection
validateShellSafePath($oldScript['filename'], 'init script filename');
$old_file_path = "$configuration_dir/docker-entrypoint-initdb.d/{$oldScript['filename']}";
// New filename is user-supplied — must be safe before accepting the rename.
validateFilenameSafe($script['filename'], 'init script filename');
// Old filename may be a legacy value written before this validation existed.
// basename() scopes the rm to the initdb.d directory; escapeshellarg() contains
// any remaining shell-metachars. No validator — don't block cleanup of legacy rows.
$old_filename = basename($oldScript['filename']);
$old_file_path = "$configuration_dir/docker-entrypoint-initdb.d/{$old_filename}";
$escapedOldPath = escapeshellarg($old_file_path);
$delete_command = "rm -f {$escapedOldPath}";
instant_remote_process([$delete_command], $this->server);
@@ -407,9 +418,11 @@ class General extends Component
$configuration_dir = database_configuration_dir().'/'.$container_name;
try {
// Validate and escape filename to prevent command injection
validateShellSafePath($script['filename'], 'init script filename');
$file_path = "$configuration_dir/docker-entrypoint-initdb.d/{$script['filename']}";
// Allow deletion of legacy rows with unsafe filenames so operators can clean up.
// basename() scopes the rm to the initdb.d directory; escapeshellarg() keeps the
// shell invocation safe regardless of the stored value.
$safe_filename = basename($script['filename']);
$file_path = "$configuration_dir/docker-entrypoint-initdb.d/{$safe_filename}";
$escapedPath = escapeshellarg($file_path);
$command = "rm -f {$escapedPath}";
@@ -446,8 +459,8 @@ class General extends Component
]);
try {
// Validate filename to prevent command injection
validateShellSafePath($this->new_filename, 'init script filename');
// Validate filename to prevent path traversal and command injection
validateFilenameSafe($this->new_filename, 'init script filename');
} catch (Exception $e) {
$this->dispatch('error', $e->getMessage());