fix(browser): keep opaque cookie partitions unaligned (#7203)

Keep cookies with opaque partitions distinct from unpartitioned identities while retaining value hashes and incomplete comparison coverage. Preserve known partition hashes and use the existing shared CLI/MCP fingerprint owner.

Validated with 63 source and 57 storage boundary tests, builds/static checks, and real Chrome156 CLI/MCP controls. Live opaque-cookie emission was not reproduced; its protocol regression is explicit. Hosted CI's unrelated failures were reproduced against exact main a905a71e3; seven fail-fast-cancelled shards remain unverified; Greptile's sole existing-main finding was scoped and documented. Human readiness remains unasserted; bypass authorized by the requester.

Co-authored-by: gensokyo-dev <[email protected]>
This commit is contained in:
Rudy Mizrahi Celekliandgensokyo-dev authored and GitHub committed 2026-10-27 10:16:15 +08:00
1 parent f53c256886
commit 6749bd090d
2 files changed
+126 -1

No files matched your search

+119
View File
@@ -1,6 +1,13 @@
import { expect, it } from "vitest";
import { inspectWebPageInputSchema } from "../domain/browserObservation.js";
import type { WebPageInspection } from "../domain/browserObservationSchemas.js";
import {
browserCaptureComparisonInputSchema,
compareBrowserCaptures,
} from "../domain/browserCaptureComparison.js";
import { webCaptureDiffSchema } from "../domain/webCaptureDiffSchemas.js";
import { passiveNetworkCapture } from "../../tests/fixtures/passiveNetworkCapture.js";
import { captureStorage } from "./CdpCaptureStorage.js";
import { CdpCommandRejection } from "./CdpCommandRejection.js";
import type { CdpConnection } from "./CdpConnection.js";
@@ -243,3 +250,115 @@ it.each(["indexed_db", "cache"] as const)(
expect(value.fingerprints_complete).toBe(false);
},
);
const cookieBase = {
name: "fixture",
value: "same",
domain: "example.test",
path: "/",
expires: -1,
size: 11,
httpOnly: false,
secure: false,
session: true,
sameSite: "Lax",
};
const captureCookies = async (
cookies: readonly Record<string, unknown>[],
): Promise<WebPageInspection["storage"]> => {
const replies: Record<string, unknown> = {
"Storage.getUsageAndQuota": { usage: 0, quota: 1000 },
"DOMStorage.getDOMStorageItems": { entries: [] },
"IndexedDB.requestDatabaseNames": { databaseNames: [] },
"CacheStorage.requestCacheNames": { caches: [] },
"Network.getCookies": { cookies },
};
const connection = {
async send(method: string): Promise<unknown> {
if (!(method in replies)) throw new Error(method);
return replies[method];
},
} as unknown as CdpConnection;
const { value } = await captureStorage(
{
connection,
sessionId: undefined,
input: inspectWebPageInputSchema.parse({
cdp_endpoint: "http://127.0.0.1:9222",
target_id: "fixture-page",
include_storage_keys: true,
include_storage_fingerprints: true,
}),
},
"https://example.test/",
[],
);
return value;
};
const storageComparison = (
before: WebPageInspection["storage"],
after: WebPageInspection["storage"],
) => {
const inspection = (storage: WebPageInspection["storage"]) => {
const capture = passiveNetworkCapture();
capture.storage = storage;
return capture;
};
return webCaptureDiffSchema.parse(
compareBrowserCaptures(
browserCaptureComparisonInputSchema.parse({
before: { inspection: inspection(before) },
after: { inspection: inspection(after) },
}),
),
).dimensions.storage;
};
it("keeps an opaque cookie partition unknown without dropping its value", async () => {
const plain = await captureCookies([cookieBase]);
const opaque = await captureCookies([
{ ...cookieBase, partitionKeyOpaque: true },
]);
const known = await captureCookies([
{
...cookieBase,
partitionKey: {
topLevelSite: "https://top.example.test",
hasCrossSiteAncestor: false,
},
partitionKeyOpaque: false,
},
]);
const cookieFingerprint = (storage: WebPageInspection["storage"]) => {
const fingerprint = storage.content_fingerprints.find(
(item) => item.scope === "cookie",
);
expect(fingerprint).toBeDefined();
return fingerprint!;
};
const plainCookie = cookieFingerprint(plain);
const opaqueCookie = cookieFingerprint(opaque);
const knownCookie = cookieFingerprint(known);
expect(opaque.fingerprints_complete).toBe(false);
expect(opaqueCookie).toMatchObject({
complete: false,
value_sha256: plainCookie.value_sha256,
});
expect(opaqueCookie.identity_sha256).not.toBe(plainCookie.identity_sha256);
expect(known.fingerprints_complete).toBe(true);
expect(knownCookie).toMatchObject({ complete: true });
expect(knownCookie.identity_sha256).not.toBe(plainCookie.identity_sha256);
expect(storageComparison(plain, opaque)).toMatchObject({
status: "unknown",
total_changes: 0,
changes: [],
});
expect(storageComparison(opaque, opaque)).toMatchObject({
status: "unknown",
total_changes: 0,
changes: [],
});
expect(storageComparison(plain, known).status).toBe("changed");
});
+7 -1
View File
@@ -148,6 +148,11 @@ const addCookies = async (
run.state.complete = false;
continue;
}
// CDP Cookie.partitionKeyOpaque means the partition exists but is not
// serializable. A missing partitionKey means unpartitioned, so hashing
// that absence would align an unresolved partition with a different cookie.
// https://chromedevtools.github.io/devtools-protocol/tot/Network/#type-Cookie
const partitionOpaque = cookie.partitionKeyOpaque === true;
addFingerprint(run.state, {
scope: "cookie",
identity: {
@@ -155,9 +160,10 @@ const addCookies = async (
path,
name,
partition_key: cookie.partitionKey ?? null,
...(partitionOpaque ? { partition_opaque: true } : {}),
},
value,
complete: cookie.partitionKeyOpaque !== true,
complete: !partitionOpaque,
});
}
};