Skip to content

Commit ce53a60

Browse files
committed
feat: add redactCookies() method to HarSanitizer
Add support for redacting cookie values in both requests and responses. - Add redactCookies() method for configuring cookies to redact - Sanitize request cookies by name - Sanitize response cookies by name - Support case-insensitive cookie name matching (default)
1 parent 0bca53d commit ce53a60

2 files changed

Lines changed: 193 additions & 2 deletions

File tree

src/HarSanitizer.php

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ final class HarSanitizer
3131
*/
3232
private array $bodyFieldsToRedact = [];
3333

34+
/**
35+
* @var string[]
36+
*/
37+
private array $cookiesToRedact = [];
38+
3439
private string $redactedValue = self::DEFAULT_REDACTED_VALUE;
3540

3641
private bool $caseSensitive = false;
@@ -74,6 +79,20 @@ public function redactBodyFields(array $fieldNames): self
7479
return $this;
7580
}
7681

82+
/**
83+
* Set cookies that should be redacted.
84+
*
85+
* Applies to both request and response cookies.
86+
*
87+
* @param string[] $cookieNames cookie names to redact
88+
*/
89+
public function redactCookies(array $cookieNames): self
90+
{
91+
$this->cookiesToRedact = $cookieNames;
92+
93+
return $this;
94+
}
95+
7796
/**
7897
* Set the value to use for redacted fields.
7998
*
@@ -125,7 +144,7 @@ private function sanitizeEntry(Entry $entry): void
125144
}
126145

127146
/**
128-
* Sanitize request headers, query params, and body fields.
147+
* Sanitize request headers, query params, body fields, and cookies.
129148
*/
130149
private function sanitizeRequest(Request $request): void
131150
{
@@ -140,10 +159,14 @@ private function sanitizeRequest(Request $request): void
140159
if (!empty($this->bodyFieldsToRedact) && $request->hasPostData()) {
141160
$this->sanitizePostData($request->getPostData());
142161
}
162+
163+
if (!empty($this->cookiesToRedact)) {
164+
$this->sanitizeCookies($request);
165+
}
143166
}
144167

145168
/**
146-
* Sanitize response headers and body fields.
169+
* Sanitize response headers, body fields, and cookies.
147170
*/
148171
private function sanitizeResponse(Response $response): void
149172
{
@@ -154,6 +177,10 @@ private function sanitizeResponse(Response $response): void
154177
if (!empty($this->bodyFieldsToRedact)) {
155178
$this->sanitizeContent($response->getContent());
156179
}
180+
181+
if (!empty($this->cookiesToRedact)) {
182+
$this->sanitizeCookies($response);
183+
}
157184
}
158185

159186
/**
@@ -300,4 +327,16 @@ private function redactArrayFields(mixed $data): mixed
300327

301328
return $data;
302329
}
330+
331+
/**
332+
* Sanitize cookies on a request or response.
333+
*/
334+
private function sanitizeCookies(Request|Response $message): void
335+
{
336+
foreach ($message->getCookies() as $cookie) {
337+
if ($this->shouldRedact($cookie->getName(), $this->cookiesToRedact)) {
338+
$cookie->setValue($this->redactedValue);
339+
}
340+
}
341+
}
303342
}

tests/src/Unit/HarSanitizerTest.php

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use Deviantintegral\Har\Cache;
88
use Deviantintegral\Har\Content;
9+
use Deviantintegral\Har\Cookie;
910
use Deviantintegral\Har\Creator;
1011
use Deviantintegral\Har\Entry;
1112
use Deviantintegral\Har\Har;
@@ -566,6 +567,94 @@ public function testRedactBodyFieldsJsonPreservesSlashesAndUnicode(): void
566567
$this->assertEquals('[REDACTED]', $data['password']);
567568
}
568569

570+
public function testRedactCookies(): void
571+
{
572+
$har = $this->createHarWithRequestCookies([
573+
'session_id' => 'abc123',
574+
'tracking' => 'xyz789',
575+
'preferences' => 'dark_mode',
576+
]);
577+
578+
$sanitizer = new HarSanitizer();
579+
$sanitizer->redactCookies(['session_id', 'tracking']);
580+
581+
$sanitized = $sanitizer->sanitize($har);
582+
583+
$cookies = $sanitized->getLog()->getEntries()[0]->getRequest()->getCookies();
584+
$cookieMap = $this->cookiesToMap($cookies);
585+
586+
$this->assertEquals('[REDACTED]', $cookieMap['session_id']);
587+
$this->assertEquals('[REDACTED]', $cookieMap['tracking']);
588+
$this->assertEquals('dark_mode', $cookieMap['preferences']);
589+
}
590+
591+
public function testRedactResponseCookies(): void
592+
{
593+
$har = $this->createHarWithResponseCookies([
594+
'session_id' => 'secret-session',
595+
'auth_token' => 'secret-token',
596+
'locale' => 'en_US',
597+
]);
598+
599+
$sanitizer = new HarSanitizer();
600+
$sanitizer->redactCookies(['session_id', 'auth_token']);
601+
602+
$sanitized = $sanitizer->sanitize($har);
603+
604+
$cookies = $sanitized->getLog()->getEntries()[0]->getResponse()->getCookies();
605+
$cookieMap = $this->cookiesToMap($cookies);
606+
607+
$this->assertEquals('[REDACTED]', $cookieMap['session_id']);
608+
$this->assertEquals('[REDACTED]', $cookieMap['auth_token']);
609+
$this->assertEquals('en_US', $cookieMap['locale']);
610+
}
611+
612+
public function testRedactCookiesCaseInsensitive(): void
613+
{
614+
$har = $this->createHarWithRequestCookies([
615+
'SESSION_ID' => 'secret1',
616+
'Session_Id' => 'secret2',
617+
]);
618+
619+
$sanitizer = new HarSanitizer();
620+
$sanitizer->redactCookies(['session_id']);
621+
622+
$sanitized = $sanitizer->sanitize($har);
623+
624+
$cookies = $sanitized->getLog()->getEntries()[0]->getRequest()->getCookies();
625+
$cookieMap = $this->cookiesToMap($cookies);
626+
627+
$this->assertEquals('[REDACTED]', $cookieMap['SESSION_ID']);
628+
$this->assertEquals('[REDACTED]', $cookieMap['Session_Id']);
629+
}
630+
631+
public function testRedactCookiesFluentInterface(): void
632+
{
633+
$sanitizer = new HarSanitizer();
634+
635+
$result = $sanitizer->redactCookies(['session_id']);
636+
637+
$this->assertSame($sanitizer, $result);
638+
}
639+
640+
public function testRedactCookiesOriginalUnmodified(): void
641+
{
642+
$har = $this->createHarWithRequestCookies([
643+
'session_id' => 'original-value',
644+
]);
645+
646+
$originalValue = $har->getLog()->getEntries()[0]->getRequest()->getCookies()[0]->getValue();
647+
648+
$sanitizer = new HarSanitizer();
649+
$sanitizer->redactCookies(['session_id']);
650+
$sanitizer->sanitize($har);
651+
652+
// Original should be unchanged
653+
$currentValue = $har->getLog()->getEntries()[0]->getRequest()->getCookies()[0]->getValue();
654+
$this->assertEquals($originalValue, $currentValue);
655+
$this->assertEquals('original-value', $currentValue);
656+
}
657+
569658
public function testWithRealFixture(): void
570659
{
571660
$repository = $this->getHarFileRepository();
@@ -879,4 +968,67 @@ private function createHarWithTextResponse(string $text): Har
879968

880969
return $this->createHarWithResponse($response);
881970
}
971+
972+
/**
973+
* @param array<string, string> $cookies
974+
*/
975+
private function createHarWithRequestCookies(array $cookies): Har
976+
{
977+
$cookieObjects = [];
978+
foreach ($cookies as $name => $value) {
979+
$cookie = (new Cookie())->setName($name)->setValue($value);
980+
$cookieObjects[] = $cookie;
981+
}
982+
983+
$request = (new Request())
984+
->setMethod('GET')
985+
->setUrl(new Uri('https://example.com'))
986+
->setHeaders([])
987+
->setCookies($cookieObjects)
988+
->setHttpVersion('HTTP/1.1');
989+
990+
return $this->createHarWithRequest($request);
991+
}
992+
993+
/**
994+
* @param array<string, string> $cookies
995+
*/
996+
private function createHarWithResponseCookies(array $cookies): Har
997+
{
998+
$cookieObjects = [];
999+
foreach ($cookies as $name => $value) {
1000+
$cookie = (new Cookie())->setName($name)->setValue($value);
1001+
$cookieObjects[] = $cookie;
1002+
}
1003+
1004+
$content = (new Content())
1005+
->setSize(0)
1006+
->setCompression(0);
1007+
1008+
$response = (new Response())
1009+
->setStatus(200)
1010+
->setStatusText('OK')
1011+
->setHeaders([])
1012+
->setCookies($cookieObjects)
1013+
->setHttpVersion('HTTP/1.1')
1014+
->setContent($content)
1015+
->setRedirectURL(new Uri(''));
1016+
1017+
return $this->createHarWithResponse($response);
1018+
}
1019+
1020+
/**
1021+
* @param Cookie[] $cookies
1022+
*
1023+
* @return array<string, string>
1024+
*/
1025+
private function cookiesToMap(array $cookies): array
1026+
{
1027+
$map = [];
1028+
foreach ($cookies as $cookie) {
1029+
$map[$cookie->getName()] = $cookie->getValue();
1030+
}
1031+
1032+
return $map;
1033+
}
8821034
}

0 commit comments

Comments
 (0)