Add ValidProxyConfigFilename rule for dynamic proxy config validation (#7544)

This commit is contained in:
Andras Bacsai
2025-12-09 16:32:41 +01:00
committed by GitHub
3 changed files with 261 additions and 2 deletions
@@ -4,6 +4,7 @@ namespace App\Livewire\Server\Proxy;
use App\Enums\ProxyTypes;
use App\Models\Server;
use App\Rules\ValidProxyConfigFilename;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Livewire\Component;
use Symfony\Component\Yaml\Yaml;
@@ -38,11 +39,11 @@ class NewDynamicConfiguration extends Component
try {
$this->authorize('update', $this->server);
$this->validate([
'fileName' => 'required',
'fileName' => ['required', new ValidProxyConfigFilename],
'value' => 'required',
]);
// Validate filename to prevent command injection
// Additional security validation to prevent command injection
validateShellSafePath($this->fileName, 'proxy configuration filename');
if (data_get($this->parameters, 'server_uuid')) {
+73
View File
@@ -0,0 +1,73 @@
<?php
namespace App\Rules;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
class ValidProxyConfigFilename implements ValidationRule
{
/**
* Reserved filenames that cannot be used.
*/
private const RESERVED_FILENAMES = [
'coolify.yaml',
'coolify.yml',
'Caddyfile',
];
/**
* Run the validation rule.
*
* Validates proxy configuration filename:
* - Must be 1-255 characters
* - No path separators (/, \) to prevent path traversal
* - Cannot start with a dot (hidden files)
* - Only alphanumeric characters, dashes, underscores, and dots allowed
* - Must have a basename before any extension
* - Cannot use reserved filenames
*/
public function validate(string $attribute, mixed $value, Closure $fail): void
{
if (empty($value)) {
return;
}
$filename = trim($value);
// Check length (filesystem limit is typically 255 bytes)
if (strlen($filename) > 255) {
$fail('The :attribute must not exceed 255 characters.');
return;
}
// Check for path separators (prevent path traversal)
if (str_contains($filename, '/') || str_contains($filename, '\\')) {
$fail('The :attribute cannot contain path separators.');
return;
}
// Check for hidden files (starting with dot)
if (str_starts_with($filename, '.')) {
$fail('The :attribute cannot start with a dot (hidden files not allowed).');
return;
}
// Check for valid characters only: alphanumeric, dashes, underscores, dots
if (! preg_match('/^[a-zA-Z0-9._-]+$/', $filename)) {
$fail('The :attribute may only contain letters, numbers, dashes, underscores, and dots.');
return;
}
// Check for reserved filenames (case-sensitive for coolify.yaml/yml, case-insensitive check not needed as Caddyfile is exact)
if (in_array($filename, self::RESERVED_FILENAMES, true)) {
$fail('The :attribute uses a reserved filename.');
return;
}
}
}