From ca906e604797cd65c9277a4069e6a82295b29c8f Mon Sep 17 00:00:00 2001 From: Philo Hermans Date: Fri, 23 Jan 2026 12:07:21 +0100 Subject: [PATCH] Add #[Locked] attributes to prevent client-side property tampering This hardens the Modal component against Livewire hydration attacks (CVE-2025-54068). While the vulnerability was fixed in Livewire 3.6.4, adding #[Locked] to $activeComponent and $components provides defense-in-depth by preventing any client-side manipulation of these properties via the updates mechanism. The test was updated to use proper server-side methods instead of directly setting properties, which is exactly what #[Locked] prevents. Co-Authored-By: Claude Opus 4.5 --- src/Modal.php | 3 +++ tests/LivewireModalTest.php | 12 +++--------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/Modal.php b/src/Modal.php index 4d16461..9b14c76 100644 --- a/src/Modal.php +++ b/src/Modal.php @@ -8,14 +8,17 @@ use Illuminate\Support\Collection; use Illuminate\Support\Reflector; use Illuminate\View\View; +use Livewire\Attributes\Locked; use Livewire\Component; use Livewire\Mechanisms\ComponentRegistry; use ReflectionClass; class Modal extends Component { + #[Locked] public ?string $activeComponent; + #[Locked] public array $components = []; public function resetState(): void diff --git a/tests/LivewireModalTest.php b/tests/LivewireModalTest.php index 41c9470..76a3ca5 100644 --- a/tests/LivewireModalTest.php +++ b/tests/LivewireModalTest.php @@ -72,15 +72,9 @@ public function testModalReset(): void Livewire::component('demo-modal', DemoModal::class); Livewire::test(Modal::class) - ->dispatch('openModal', 'demo-modal') - ->set('components', [ - 'some-component' => [ - 'name' => 'demo-modal', - 'arguments' => ['bar'], - 'modalAttributes' => [], - ], - ]) - ->set('activeComponent', 'some-component') + ->dispatch('openModal', component: 'demo-modal', arguments: ['message' => 'Test']) + ->assertNotSet('activeComponent', null) + ->assertNotSet('components', []) ->call('resetState') // Verify properties are reset ->assertSet('activeComponent', null)