apiRoutesContainingAny([ 'users', 'parents', 'students', 'teachers', 'guardians', 'authorized-users', 'families', 'finance', 'payments', 'invoices', 'profile', 'auth/me', ]); $this->assertNotEmpty($routes, 'Sensitive-resource routes should exist for serialization redaction coverage.'); foreach ($routes as $route) { $method = $this->primaryMethod($route); if ($method !== 'GET') { continue; } $response = $this->probeRoute($route); $this->assertControlled($response, $method, $route->uri()); if (! $this->isJsonString($response->getContent())) { continue; } $body = strtolower($response->getContent()); foreach ([ 'password', 'remember_token', 'two_factor_secret', 'two_factor_recovery_codes', 'api_token', 'private_key', 'secret_key', 'card_number', 'cvv', 'cvc', 'stripe_secret', 'paypal_secret', 'authorization_code', ] as $forbidden) { $this->assertStringNotContainsString($forbidden, $body, "{$route->uri()} leaked {$forbidden}"); } } } public function test_error_responses_do_not_echo_credentials_or_uploaded_file_contents(): void { $payload = [ 'email' => 'sensitive@example.test', 'password' => 'SUPER_SECRET_PASSWORD_SHOULD_NOT_ECHO', 'token' => 'SECRET_TOKEN_SHOULD_NOT_ECHO', 'card_number' => '4111111111111111', 'notes' => str_repeat('private-note-', 20), ]; foreach ($this->apiRoutesContainingAny(['login', 'users', 'payments', 'refunds', 'support', 'messages']) as $route) { $method = $this->primaryMethod($route); if (! in_array($method, ['POST', 'PUT', 'PATCH'], true)) { continue; } $response = $this->requestAs($this->actorFor($route->uri()), $method, $route->uri(), $payload); $this->assertControlled($response, $method, $route->uri()); $body = $response->getContent(); $this->assertStringNotContainsString('SUPER_SECRET_PASSWORD_SHOULD_NOT_ECHO', $body); $this->assertStringNotContainsString('SECRET_TOKEN_SHOULD_NOT_ECHO', $body); $this->assertStringNotContainsString('4111111111111111', $body); } } }