record('outbound api call', [ 'authorization' => 'Bearer irdb_svc_ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', ]); $out = $processor($record); self::assertSame('***', $out->context['authorization']); } public function testFormattedOutputDoesNotLeakBearerToken(): void { $processor = new SecretScrubbingProcessor(); $record = $this->record('outbound', [ 'headers' => ['Authorization' => 'Bearer irdb_svc_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'], ]); $out = $processor($record); $line = (new JsonFormatter())->format($out); self::assertStringNotContainsString('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', $line); self::assertStringContainsString('***', $line); } public function testLocalAdminPasswordHashKeyScrubbed(): void { $processor = new SecretScrubbingProcessor(); $record = $this->record('config', [ 'LOCAL_ADMIN_PASSWORD_HASH' => '$argon2id$v=19$abc$def', 'OIDC_CLIENT_SECRET' => 'oidc-secret', ]); $out = $processor($record); self::assertSame('***', $out->context['LOCAL_ADMIN_PASSWORD_HASH']); self::assertSame('***', $out->context['OIDC_CLIENT_SECRET']); } public function testNonSensitiveLeftAlone(): void { $processor = new SecretScrubbingProcessor(); $record = $this->record('search ok', ['count' => 42, 'q' => '203.0.113.42']); $out = $processor($record); self::assertSame(42, $out->context['count']); self::assertSame('203.0.113.42', $out->context['q']); } public function testRawJwtInValueIsScrubbed(): void { // SEC_REVIEW F65: JWTs anchored on `eyJ` (base64url of `{"`) // are scrubbed regardless of the key they're logged under. $processor = new SecretScrubbingProcessor(); $jwt = 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI3In0.qj8u_Mt1ZyT5PfksI91X4Aaa'; $record = $this->record('oidc id_token captured', ['jwt' => $jwt]); $out = $processor($record); self::assertSame('eyJ***', $out->context['jwt']); } public function testShortBearerIsScrubbed(): void { // SEC_REVIEW F65: Bearer floor lowered from {20,} to {8,}. $processor = new SecretScrubbingProcessor(); $record = $this->record('outbound', ['auth' => 'Bearer abcd1234']); $out = $processor($record); self::assertSame('Bearer ***', $out->context['auth']); } /** * @param array $context */ private function record(string $message, array $context): LogRecord { return new LogRecord( datetime: new \DateTimeImmutable(), channel: 'test', level: Level::Info, message: $message, context: $context, ); } }