fix(validation): add input validation for resource limit fields (#9238)

This commit is contained in:
Andras Bacsai 2026-03-30 21:04:10 +02:00 committed by GitHub
commit 1efed14169
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -3,6 +3,7 @@
namespace App\Livewire\Project\Shared; namespace App\Livewire\Project\Shared;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Validation\ValidationException;
use Livewire\Component; use Livewire\Component;
class ResourceLimits extends Component class ResourceLimits extends Component
@ -16,24 +17,24 @@ class ResourceLimits extends Component
public ?string $limitsCpuset = null; public ?string $limitsCpuset = null;
public ?int $limitsCpuShares = null; public mixed $limitsCpuShares = null;
public string $limitsMemory; public string $limitsMemory;
public string $limitsMemorySwap; public string $limitsMemorySwap;
public int $limitsMemorySwappiness; public mixed $limitsMemorySwappiness = 0;
public string $limitsMemoryReservation; public string $limitsMemoryReservation;
protected $rules = [ protected $rules = [
'limitsMemory' => 'required|string', 'limitsMemory' => ['required', 'string', 'regex:/^(0|\d+[bBkKmMgG])$/'],
'limitsMemorySwap' => 'required|string', 'limitsMemorySwap' => ['required', 'string', 'regex:/^(0|\d+[bBkKmMgG])$/'],
'limitsMemorySwappiness' => 'required|integer|min:0|max:100', 'limitsMemorySwappiness' => 'required|integer|min:0|max:100',
'limitsMemoryReservation' => 'required|string', 'limitsMemoryReservation' => ['required', 'string', 'regex:/^(0|\d+[bBkKmMgG])$/'],
'limitsCpus' => 'nullable', 'limitsCpus' => ['nullable', 'regex:/^\d*\.?\d+$/'],
'limitsCpuset' => 'nullable', 'limitsCpuset' => ['nullable', 'regex:/^\d+([,-]\d+)*$/'],
'limitsCpuShares' => 'nullable', 'limitsCpuShares' => 'nullable|integer|min:0',
]; ];
protected $validationAttributes = [ protected $validationAttributes = [
@ -46,6 +47,19 @@ class ResourceLimits extends Component
'limitsCpuShares' => 'cpu shares', 'limitsCpuShares' => 'cpu shares',
]; ];
protected $messages = [
'limitsMemory.regex' => 'Maximum Memory Limit must be a number followed by a unit (b, k, m, g). Example: 256m, 1g. Use 0 for unlimited.',
'limitsMemorySwap.regex' => 'Maximum Swap Limit must be a number followed by a unit (b, k, m, g). Example: 256m, 1g. Use 0 for unlimited.',
'limitsMemoryReservation.regex' => 'Soft Memory Limit must be a number followed by a unit (b, k, m, g). Example: 256m, 1g. Use 0 for unlimited.',
'limitsCpus.regex' => 'Number of CPUs must be a number (integer or decimal). Example: 0.5, 2.',
'limitsCpuset.regex' => 'CPU sets must be a comma-separated list of CPU numbers or ranges. Example: 0-2 or 0,1,3.',
'limitsMemorySwappiness.integer' => 'Swappiness must be a whole number between 0 and 100.',
'limitsMemorySwappiness.min' => 'Swappiness must be between 0 and 100.',
'limitsMemorySwappiness.max' => 'Swappiness must be between 0 and 100.',
'limitsCpuShares.integer' => 'CPU Weight must be a whole number.',
'limitsCpuShares.min' => 'CPU Weight must be a positive number.',
];
/** /**
* Sync data between component properties and model * Sync data between component properties and model
* *
@ -57,10 +71,10 @@ private function syncData(bool $toModel = false): void
// Sync TO model (before save) // Sync TO model (before save)
$this->resource->limits_cpus = $this->limitsCpus; $this->resource->limits_cpus = $this->limitsCpus;
$this->resource->limits_cpuset = $this->limitsCpuset; $this->resource->limits_cpuset = $this->limitsCpuset;
$this->resource->limits_cpu_shares = $this->limitsCpuShares; $this->resource->limits_cpu_shares = (int) $this->limitsCpuShares;
$this->resource->limits_memory = $this->limitsMemory; $this->resource->limits_memory = $this->limitsMemory;
$this->resource->limits_memory_swap = $this->limitsMemorySwap; $this->resource->limits_memory_swap = $this->limitsMemorySwap;
$this->resource->limits_memory_swappiness = $this->limitsMemorySwappiness; $this->resource->limits_memory_swappiness = (int) $this->limitsMemorySwappiness;
$this->resource->limits_memory_reservation = $this->limitsMemoryReservation; $this->resource->limits_memory_reservation = $this->limitsMemoryReservation;
} else { } else {
// Sync FROM model (on load/refresh) // Sync FROM model (on load/refresh)
@ -91,7 +105,7 @@ public function submit()
if (! $this->limitsMemorySwap) { if (! $this->limitsMemorySwap) {
$this->limitsMemorySwap = '0'; $this->limitsMemorySwap = '0';
} }
if (is_null($this->limitsMemorySwappiness)) { if ($this->limitsMemorySwappiness === '' || is_null($this->limitsMemorySwappiness)) {
$this->limitsMemorySwappiness = 60; $this->limitsMemorySwappiness = 60;
} }
if (! $this->limitsMemoryReservation) { if (! $this->limitsMemoryReservation) {
@ -103,7 +117,7 @@ public function submit()
if ($this->limitsCpuset === '') { if ($this->limitsCpuset === '') {
$this->limitsCpuset = null; $this->limitsCpuset = null;
} }
if (is_null($this->limitsCpuShares)) { if ($this->limitsCpuShares === '' || is_null($this->limitsCpuShares)) {
$this->limitsCpuShares = 1024; $this->limitsCpuShares = 1024;
} }
@ -112,6 +126,12 @@ public function submit()
$this->syncData(true); $this->syncData(true);
$this->resource->save(); $this->resource->save();
$this->dispatch('success', 'Resource limits updated.'); $this->dispatch('success', 'Resource limits updated.');
} catch (ValidationException $e) {
foreach ($e->validator->errors()->all() as $message) {
$this->dispatch('error', $message);
}
return;
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);
} }