$request */ private function scrubEventWithRequest(array $request, ?EventHint $hint = null): ?Event { $event = Event::createEvent(); $event->setRequest($request); return SentryEventScrubber::scrub($event, $hint); } public function test_password_in_request_body_is_scrubbed(): void { $event = $this->scrubEventWithRequest([ 'data' => ['email' => 'a@b.test', 'password' => 'sup3rsecret!'], ]); $this->assertSame('[scrubbed]', $event->getRequest()['data']['password']); $this->assertSame('a@b.test', $event->getRequest()['data']['email']); } public function test_password_confirmation_is_scrubbed(): void { $event = $this->scrubEventWithRequest([ 'data' => ['password_confirmation' => 'p@ss', 'current_password' => 'oldpass'], ]); $this->assertSame('[scrubbed]', $event->getRequest()['data']['password_confirmation']); $this->assertSame('[scrubbed]', $event->getRequest()['data']['current_password']); } public function test_authorization_header_is_scrubbed(): void { $event = $this->scrubEventWithRequest([ 'headers' => ['Authorization' => 'Bearer abc.def.ghi'], ]); $this->assertSame('[scrubbed]', $event->getRequest()['headers']['Authorization']); } public function test_cookie_header_is_scrubbed(): void { $event = $this->scrubEventWithRequest([ 'headers' => ['Cookie' => 'crewli_session=abcd'], ]); $this->assertSame('[scrubbed]', $event->getRequest()['headers']['Cookie']); } public function test_x_impersonation_token_header_is_scrubbed(): void { $event = $this->scrubEventWithRequest([ 'headers' => ['X-Impersonation-Token' => 'imp_token_xyz'], ]); $this->assertSame('[scrubbed]', $event->getRequest()['headers']['X-Impersonation-Token']); } public function test_form_values_payload_is_replaced_wholesale(): void { $event = $this->scrubEventWithRequest([ 'data' => [ 'form_values' => [ 'email' => 'sensitive@example.com', 'dietary' => 'vegan', 'phone' => '+31612345678', ], ], ]); $data = $event->getRequest()['data']; $this->assertSame('[scrubbed_form_values]', $data['form_values']); $serialised = json_encode($data, JSON_THROW_ON_ERROR); $this->assertStringNotContainsString('sensitive@example.com', $serialised); $this->assertStringNotContainsString('vegan', $serialised); $this->assertStringNotContainsString('+31612345678', $serialised); } public function test_token_query_string_is_scrubbed(): void { $event = $this->scrubEventWithRequest([ 'query_string' => 'token=abc123&keep=me', ]); $qs = $event->getRequest()['query_string']; $this->assertStringContainsString('token=%5Bscrubbed%5D', $qs); $this->assertStringContainsString('keep=me', $qs); } public function test_api_key_query_string_is_scrubbed(): void { $event = $this->scrubEventWithRequest([ 'query_string' => 'api_key=xyz&page=2', ]); $qs = $event->getRequest()['query_string']; $this->assertStringContainsString('api_key=%5Bscrubbed%5D', $qs); $this->assertStringContainsString('page=2', $qs); } public function test_iban_in_nested_body_is_scrubbed(): void { $event = $this->scrubEventWithRequest([ 'data' => [ 'profile' => [ 'address' => [ 'iban' => 'NL91ABNA0417164300', 'street' => 'Damrak 1', ], ], ], ]); $address = $event->getRequest()['data']['profile']['address']; $this->assertSame('[scrubbed]', $address['iban']); $this->assertSame('Damrak 1', $address['street']); } public function test_bsn_in_nested_body_is_scrubbed(): void { $event = $this->scrubEventWithRequest([ 'data' => ['kyc' => ['passport_number' => 'NX1234567', 'bsn' => '123456789']], ]); $kyc = $event->getRequest()['data']['kyc']; $this->assertSame('[scrubbed]', $kyc['passport_number']); $this->assertSame('[scrubbed]', $kyc['bsn']); } public function test_send_default_pii_is_false(): void { $this->assertFalse(config('sentry.send_default_pii')); } public function test_validation_exception_is_in_ignore_list(): void { $this->assertContains(ValidationException::class, config('sentry.ignore_exceptions')); } public function test_authentication_exception_is_in_ignore_list(): void { $this->assertContains(AuthenticationException::class, config('sentry.ignore_exceptions')); } public function test_authorization_exception_is_in_ignore_list(): void { $this->assertContains(AuthorizationException::class, config('sentry.ignore_exceptions')); } public function test_http_exception_404_is_dropped_by_scrubber(): void { $event = Event::createEvent(); $hint = EventHint::fromArray(['exception' => new NotFoundHttpException]); $this->assertNull(SentryEventScrubber::scrub($event, $hint)); } public function test_http_exception_500_is_captured(): void { $event = Event::createEvent(); $hint = EventHint::fromArray(['exception' => new HttpException(500, 'boom')]); $this->assertNotNull(SentryEventScrubber::scrub($event, $hint)); } public function test_throwable_from_controller_is_captured(): void { $event = Event::createEvent(); $hint = EventHint::fromArray(['exception' => new RuntimeException('programmer error')]); $this->assertNotNull(SentryEventScrubber::scrub($event, $hint)); } public function test_form_values_replacement_blocks_attempts_to_smuggle_pii(): void { // form_values is a wholesale replace — even if the payload is deeply // nested, the entire branch is wiped so individual keys cannot leak. $event = $this->scrubEventWithRequest([ 'data' => [ 'submission' => [ 'form_values' => [ 'medical' => 'celiac', 'children' => [ ['name' => 'Bobby', 'allergy' => 'peanuts'], ], ], ], ], ]); $serialised = json_encode($event->getRequest()['data'], JSON_THROW_ON_ERROR); $this->assertStringContainsString('[scrubbed_form_values]', $serialised); $this->assertStringNotContainsString('celiac', $serialised); $this->assertStringNotContainsString('Bobby', $serialised); $this->assertStringNotContainsString('peanuts', $serialised); } public function test_cookies_request_field_is_replaced(): void { $event = $this->scrubEventWithRequest([ 'cookies' => ['SESSION' => 'abcd', 'tracking' => 'xyz'], ]); $this->assertSame('[scrubbed]', $event->getRequest()['cookies']); } public function test_max_depth_guard_prevents_unbounded_recursion(): void { $deep = ['v' => 'leaf']; for ($i = 0; $i < 15; $i++) { $deep = ['nest' => $deep]; } $event = $this->scrubEventWithRequest(['data' => $deep]); $serialised = json_encode($event->getRequest()['data'], JSON_THROW_ON_ERROR); $this->assertStringContainsString('[max_depth]', $serialised); } }