[eric] browser: don't replay fingerprint-bound clearance tokens into the app partition

This commit is contained in:
ciregenz
2026-07-27 14:57:28 -07:00
parent 514aef45d6
commit c4987b0f13
2 changed files with 25 additions and 1 deletions
@@ -48,6 +48,14 @@ P_GOOGLE_SUFFIXES = ("google.com", "youtube.com")
# seconds, and an entry with no expiry is session-scoped, so it would evaporate on the next quit.
P_CHROMIUM_EPOCH_OFFSET_S = 11644473600
# Anti-bot clearance tokens are bound to the exact user agent and IP that earned them. Our webview
# keeps an "openswarm/" product token in its UA, so a clearance minted by the user's real Chrome can
# never match ours, and replaying a mismatched one reads as token theft: the edge hands back a fresh
# challenge instead of letting us through, which is WORSE than arriving with no clearance at all.
# Everything else in the jar is the actual session, so we carry that and let the edge re-challenge
# us honestly.
P_FINGERPRINT_BOUND = {"cf_clearance", "__cf_bm", "_cfuvid", "datadome", "incap_ses", "reese84"}
class SessionImportResult(BaseModel):
"""What happened, in a shape the caller can branch on without parsing prose."""
@@ -88,7 +96,8 @@ def read_site_records(domain: str) -> List[Dict[str, Any]]:
# A browser we cannot read is a fallback, never a crash: the run just asks the user instead.
logger.info(f"[session-import] read failed for {domain}: {type(exc).__name__}")
return []
return [{**r, "expires": p_unix_expiry(r.get("expires_utc"))} for r in raw]
return [{**r, "expires": p_unix_expiry(r.get("expires_utc"))} for r in raw
if str(r.get("name") or "").lower() not in P_FINGERPRINT_BOUND]
@typechecked
@@ -125,6 +125,21 @@ def test_expiry_is_translated_out_of_chromium_time(monkeypatch):
assert out[1]["expires"] == 0.0, "a session entry must stay session-scoped, not become 1601"
def test_fingerprint_bound_clearance_is_left_behind(monkeypatch):
"""Anti-bot clearance is minted against the UA and IP that earned it, and our webview keeps an
'openswarm/' token in its UA, so a borrowed clearance can never match. Replaying a mismatched
one reads as token theft and gets us challenged HARDER than arriving with none, while the real
session cookies beside it are perfectly portable."""
monkeypatch.setattr(si.browser_cookies, "read_provider_cookie_records", lambda d: [
{"name": "sid", "value": "opaque", "expires_utc": 0},
{"name": "uid", "value": "opaque", "expires_utc": 0},
{"name": "cf_clearance", "value": "opaque", "expires_utc": 0},
{"name": "__cf_bm", "value": "opaque", "expires_utc": 0},
{"name": "datadome", "value": "opaque", "expires_utc": 0},
])
assert sorted(r["name"] for r in si.read_site_records("medium.com")) == ["sid", "uid"]
def test_google_reads_go_through_the_named_sso_scope(monkeypatch):
"""A Gmail borrow must use the reader's named SSO set, never a general sweep of the user's
google entries."""