Merge remote-tracking branch 'origin/next' into api-sensitive-data-scrubber

This commit is contained in:
Andras Bacsai
2026-07-02 15:57:43 +02:00
117 changed files with 5114 additions and 1799 deletions
+69 -10
View File
@@ -1312,8 +1312,12 @@ class ServicesController extends Controller
$this->authorize('manageEnvironment', $service);
if ($request->has('key')) {
$request->merge(['key' => ValidationPatterns::normalizeEnvironmentVariableKey((string) $request->key)]);
}
$validator = customApiValidator($request->all(), [
'key' => 'string|required',
'key' => ValidationPatterns::environmentVariableKeyRules(),
'value' => 'string|nullable',
'is_literal' => 'boolean',
'is_multiline' => 'boolean',
@@ -1461,8 +1465,12 @@ class ServicesController extends Controller
$updatedEnvs = collect();
foreach ($bulk_data as $item) {
if (array_key_exists('key', $item)) {
$item['key'] = ValidationPatterns::normalizeEnvironmentVariableKey((string) $item['key']);
}
$validator = customApiValidator($item, [
'key' => 'string|required',
'key' => ValidationPatterns::environmentVariableKeyRules(),
'value' => 'string|nullable',
'is_literal' => 'boolean',
'is_multiline' => 'boolean',
@@ -1580,8 +1588,12 @@ class ServicesController extends Controller
$this->authorize('manageEnvironment', $service);
if ($request->has('key')) {
$request->merge(['key' => ValidationPatterns::normalizeEnvironmentVariableKey((string) $request->key)]);
}
$validator = customApiValidator($request->all(), [
'key' => 'string|required',
'key' => ValidationPatterns::environmentVariableKeyRules(),
'value' => 'string|nullable',
'is_literal' => 'boolean',
'is_multiline' => 'boolean',
@@ -2166,10 +2178,11 @@ class ServicesController extends Controller
'host_path' => ['string', 'nullable', 'regex:'.ValidationPatterns::DIRECTORY_PATH_PATTERN],
'content' => 'string|nullable',
'is_directory' => 'boolean',
'is_host_file' => 'boolean',
'fs_path' => 'string',
]);
$allAllowedFields = ['type', 'resource_uuid', 'name', 'mount_path', 'host_path', 'content', 'is_directory', 'fs_path'];
$allAllowedFields = ['type', 'resource_uuid', 'name', 'mount_path', 'host_path', 'content', 'is_directory', 'is_host_file', 'fs_path'];
$extraFields = array_diff(array_keys($request->all()), $allAllowedFields);
if ($validator->fails() || ! empty($extraFields)) {
$errors = $validator->errors();
@@ -2201,7 +2214,7 @@ class ServicesController extends Controller
], 422);
}
$typeSpecificInvalidFields = array_intersect(['content', 'is_directory', 'fs_path'], array_keys($request->all()));
$typeSpecificInvalidFields = array_intersect(['content', 'is_directory', 'is_host_file', 'fs_path'], array_keys($request->all()));
if (! empty($typeSpecificInvalidFields)) {
return response()->json([
'message' => 'Validation failed.',
@@ -2232,6 +2245,14 @@ class ServicesController extends Controller
}
$isDirectory = $request->boolean('is_directory', false);
$isHostFile = $request->boolean('is_host_file', false);
if ($isDirectory && $isHostFile) {
return response()->json([
'message' => 'Validation failed.',
'errors' => ['is_host_file' => 'Host file mounts cannot also be directory mounts.'],
], 422);
}
if ($isDirectory) {
if (! $request->fs_path) {
@@ -2254,12 +2275,50 @@ class ServicesController extends Controller
'resource_id' => $subResource->id,
'resource_type' => get_class($subResource),
]);
} elseif ($isHostFile) {
if (! $request->fs_path) {
return response()->json([
'message' => 'Validation failed.',
'errors' => ['fs_path' => 'The fs_path field is required for host file mounts.'],
], 422);
}
if ($request->filled('content')) {
return response()->json([
'message' => 'Validation failed.',
'errors' => ['content' => 'Content is not valid for host file mounts.'],
], 422);
}
try {
$fsPath = validateHostFileMountPath($request->fs_path, 'host file source path');
$mountPath = validateFileMountPath($request->mount_path, 'host file destination path');
} catch (\Throwable $e) {
return response()->json([
'message' => 'Validation failed.',
'errors' => ['mount_path' => $e->getMessage()],
], 422);
}
$storage = LocalFileVolume::create([
'fs_path' => $fsPath,
'mount_path' => $mountPath,
'content' => null,
'is_directory' => false,
'is_host_file' => true,
'resource_id' => $subResource->id,
'resource_type' => get_class($subResource),
]);
} else {
$mountPath = str($request->mount_path)->trim()->start('/')->value();
validateShellSafePath($mountPath, 'file storage path');
$fsPath = service_configuration_dir().'/'.$service->uuid.$mountPath;
try {
$mountPath = validateFileMountPath($request->mount_path, 'file storage path');
$fsPath = confineFileMountPath(service_configuration_dir().'/'.$service->uuid, $mountPath, 'file storage path');
} catch (\Throwable $e) {
return response()->json([
'message' => 'Validation failed.',
'errors' => ['mount_path' => $e->getMessage()],
], 422);
}
$storage = LocalFileVolume::create([
'fs_path' => $fsPath,