The Problem
The cookie jar has two entrances, and they handle the domain differently.
Network.setCookies stores the domain unchanged, including the leading dot. The Set-Cookie path strips the dot before constructing the key. If the same cookie is sent via both paths, it ends up under two different keys. Both match during lookup, and the request carries the name twice with different values.
This affects every imported session. Playwright's storageState writes domain cookies with the dot. Any cookie that the page later renews via Set-Cookie with the Domain= attribute thus becomes a duplicate entry instead of replacing the old one.
Environment
- obscura
main, commit 94374e638cda7084ecab9cfb08b100efee8f9a04
- Build: render, without stealth (
cargo build --release -p obscura-cli --bins --features render)
- Linux x86_64 (WSL2)
Reproduction
A cookie is sent via CDP with a dot, after which the server sets the same name via Set-Cookie with the Domain= attribute.
Network.setCookies with [{name: "sid", value: "one", domain: ".example.com", path: "/"}]
- Load a page at
https://app.example.com/, whose response contains Set-Cookie: sid=two; Domain=example.com; Path=/
- The
Cookie header of the next request to app.example.com
Expected: sid=two, once.
Observed: sid=one; sid=two, the name appears twice.
Demonstrated directly in the jar, without a server, as a test case:
let jar = CookieJar::new();
let url = Url::parse("https://app.example.com/").unwrap();
jar.set_cookies_from_cdp(vec![CookieInfo {
name: "sid".into(), value: "one".into(),
domain: ".example.com".into(), path: "/".into(),
secure: false, http_only: false, same_site: String::new(), expires: None,
}]);
jar.set_cookie("sid=two; Domain=example.com; Path=/", &url);
// Expects 1, but actually returns 2
assert_eq!(jar.get_all_cookies().iter().filter(|c| c.name == "sid").count(), 1);
Chromium for Comparison
Chromium 151.0.7922.34, headless, same sequence using the same CDP methods. The response with the Set-Cookie header was generated offline so that it would be processed by Chromium's own parser.
https://app.example.com/ -> 1 entry domain=.example.com value=two
https://example.com/ -> 1 entry domain=.example.com value=two
Chromium keeps one cookie. The Set-Cookie with the Domain= attribute also creates a domain cookie and replaces the imported one.
What is explicitly NOT the error case
Two consecutive Network.setCookies calls, one for .example.com and one for example.com, are not evidence of the issue. Chromium keeps two cookies there, and that is correct: in CDP, the leading dot encodes the domain cookie, while the dotless form encodes a host-only cookie. According to RFC 6265 5.3, these are different cookies.
Measured:
https://app.example.com/ -> 1 domain=.example.com value=one
https://example.com/ -> 2 domain=example.com value=two
domain=.example.com value=one
The issue, therefore, is not that obscura retains the dot, but rather that the two entrances are inconsistent.
Side Note
The three deletion paths already work around this inconsistency: set_cookies_from_cdp, delete_cookie, and delete_cookies_filtered each try three different spellings of the domain to find a match. If you canonicalize during insertion, this becomes unnecessary.
Separately: set_cookies_from_cdp sets host_only: false for every import. obscura therefore does not map the host-only flag from CDP at all. This is a separate gap and not part of this report.
The Problem
The cookie jar has two entrances, and they handle the domain differently.
Network.setCookiesstores the domain unchanged, including the leading dot. TheSet-Cookiepath strips the dot before constructing the key. If the same cookie is sent via both paths, it ends up under two different keys. Both match during lookup, and the request carries the name twice with different values.This affects every imported session. Playwright's
storageStatewrites domain cookies with the dot. Any cookie that the page later renews viaSet-Cookiewith theDomain=attribute thus becomes a duplicate entry instead of replacing the old one.Environment
main, commit94374e638cda7084ecab9cfb08b100efee8f9a04cargo build --release -p obscura-cli --bins --features render)Reproduction
A cookie is sent via CDP with a dot, after which the server sets the same name via
Set-Cookiewith theDomain=attribute.Network.setCookieswith[{name: "sid", value: "one", domain: ".example.com", path: "/"}]https://app.example.com/, whose response containsSet-Cookie: sid=two; Domain=example.com; Path=/Cookieheader of the next request toapp.example.comExpected:
sid=two, once.Observed:
sid=one; sid=two, the name appears twice.Demonstrated directly in the jar, without a server, as a test case:
Chromium for Comparison
Chromium 151.0.7922.34, headless, same sequence using the same CDP methods. The response with the
Set-Cookieheader was generated offline so that it would be processed by Chromium's own parser.Chromium keeps one cookie. The
Set-Cookiewith theDomain=attribute also creates a domain cookie and replaces the imported one.What is explicitly NOT the error case
Two consecutive
Network.setCookiescalls, one for.example.comand one forexample.com, are not evidence of the issue. Chromium keeps two cookies there, and that is correct: in CDP, the leading dot encodes the domain cookie, while the dotless form encodes a host-only cookie. According to RFC 6265 5.3, these are different cookies.Measured:
The issue, therefore, is not that obscura retains the dot, but rather that the two entrances are inconsistent.
Side Note
The three deletion paths already work around this inconsistency:
set_cookies_from_cdp,delete_cookie, anddelete_cookies_filteredeach try three different spellings of the domain to find a match. If you canonicalize during insertion, this becomes unnecessary.Separately:
set_cookies_from_cdpsetshost_only: falsefor every import. obscura therefore does not map the host-only flag from CDP at all. This is a separate gap and not part of this report.