diff --git a/config/firewall.php b/config/firewall.php index d3675728..cdcac1dc 100644 --- a/config/firewall.php +++ b/config/firewall.php @@ -418,7 +418,10 @@ return [ 'inputs' => [ 'only' => [], // i.e. 'first_name' - 'except' => [], // i.e. 'password' + // Livewire cryptographically signs this serialized component + // state. Its large JSON payload produces false positives; user + // updates and every other request field remain protected. + 'except' => ['snapshot'], ], 'patterns' => [ diff --git a/tests/Unit/Firewall/LivewireXssExclusionTest.php b/tests/Unit/Firewall/LivewireXssExclusionTest.php new file mode 100644 index 00000000..eb44af06 --- /dev/null +++ b/tests/Unit/Firewall/LivewireXssExclusionTest.php @@ -0,0 +1,43 @@ + true]); + } + + public function test_signed_livewire_snapshot_is_excluded_from_xss_scanning(): void + { + $middleware = new Xss(); + $middleware->prepare(Request::create('/1/livewire/update', 'POST')); + + $this->assertFalse($middleware->match( + $middleware->getPatterns()[4], + ['components' => [['snapshot' => '{"data":{"product_name":""}}']]], + )); + } + + public function test_livewire_user_updates_and_regular_fields_remain_protected(): void + { + $middleware = new Xss(); + $middleware->prepare(Request::create('/1/livewire/update', 'POST')); + + $this->assertSame(1, $middleware->match( + $middleware->getPatterns()[4], + ['components' => [['updates' => ['name' => '']]]], + )); + $this->assertSame(1, $middleware->match( + $middleware->getPatterns()[4], + ['name' => ''], + )); + } +}