diff --git a/lib/Adapter/Artax/NetscapeCookieFileJar.php b/lib/Adapter/Artax/NetscapeCookieFileJar.php index fce1a0b..be3d6d1 100644 --- a/lib/Adapter/Artax/NetscapeCookieFileJar.php +++ b/lib/Adapter/Artax/NetscapeCookieFileJar.php @@ -69,7 +69,14 @@ private function parse(string $line): ?ResponseCookie return null; } - if ($line[0] === '#') { + // Handle httponly cookies - lines starting with #HttpOnly_ should be parsed + // See: https://curl.se/docs/http-cookies.html + $isHttpOnly = false; + if (str_starts_with($line, '#HttpOnly_')) { + $line = substr($line, 10); // Remove the '#HttpOnly_' prefix + $isHttpOnly = true; + } elseif ($line[0] === '#') { + // Regular comment line, skip it return null; } @@ -104,6 +111,10 @@ private function parse(string $line): ?ResponseCookie $string .= '; secure'; } + if ($isHttpOnly) { + $string .= '; httponly'; + } + return ResponseCookie::fromHeader($string); } } diff --git a/tests/Integration/Adapter/Artax/NetscapeCookieFileJarTest.php b/tests/Integration/Adapter/Artax/NetscapeCookieFileJarTest.php index f6b3be3..67419d1 100644 --- a/tests/Integration/Adapter/Artax/NetscapeCookieFileJarTest.php +++ b/tests/Integration/Adapter/Artax/NetscapeCookieFileJarTest.php @@ -71,4 +71,41 @@ public function testLoadCookies() (yield $jar->get((new Request('http://127.0.0.1/'))->getUri()))[0]->getValue() ); } + + public function testLoadHttpOnlyCookies() + { + $expire = (new DateTimeImmutable())->modify('+1 day')->format('U'); + + $cookies = <<workspace()->path('httponly_cookies.txt'); + file_put_contents($path, $cookies); + + $jar = new NetscapeCookieFileJar($path); + + $allCookies = $jar->getAll(); + + // Should have 3 cookies total (2 httponly + 1 regular) + $this->assertCount(3, $allCookies); + + // Get cookies for example.com + $exampleCookies = yield $jar->get((new Request('https://example.com/'))->getUri()); + + // Should include the httponly cookie + $cookieNames = array_map(fn ($cookie) => $cookie->getName(), $exampleCookies); + $this->assertContains('session_id', $cookieNames); + $this->assertContains('regular_cookie', $cookieNames); + + // Verify httponly cookie value + $sessionCookie = array_filter($exampleCookies, fn ($cookie) => $cookie->getName() === 'session_id'); + $this->assertEquals('abc123', reset($sessionCookie)->getValue()); + } }