mirror of
https://github.com/morluto/rea.git
synced 2026-11-12 09:52:27 +00:00
fix(browser): preserve independently observed storage identities (#10029)
fix(browser): retain partial storage identity evidence Co-authored-by: Cursor Agent <cursoragent@cursor.com>
This commit is contained in:
1 parent
dd928466cb
commit
6151e13835
3 files changed
+77
-16
No files matched your search
@@ -16,14 +16,15 @@ const NO_CACHE_COMMAND = Symbol("unsupported");
|
||||
|
||||
const captureWithCacheResult = async (
|
||||
cacheResult: unknown,
|
||||
storageEntries: unknown = [],
|
||||
): Promise<Awaited<ReturnType<typeof captureStorage>>["value"]> => {
|
||||
const connection = {
|
||||
send(method: string): Promise<unknown> {
|
||||
send(method: string, params: Record<string, unknown>): Promise<unknown> {
|
||||
switch (method) {
|
||||
case "Storage.getUsageAndQuota":
|
||||
return Promise.resolve({ usage: 1, quota: 10 });
|
||||
case "DOMStorage.getDOMStorageItems":
|
||||
return Promise.resolve({ entries: [] });
|
||||
return Promise.resolve({ entries: storageEntries });
|
||||
case "IndexedDB.requestDatabaseNames":
|
||||
return Promise.resolve({ databaseNames: [] });
|
||||
case "Network.getCookies":
|
||||
@@ -40,6 +41,7 @@ const captureWithCacheResult = async (
|
||||
)
|
||||
: Promise.resolve(cacheResult);
|
||||
case "CacheStorage.requestEntries":
|
||||
expect(params.cacheId).toEqual(expect.any(String));
|
||||
return Promise.resolve({ cacheDataEntries: [], returnCount: 0 });
|
||||
default:
|
||||
return Promise.reject(new Error(`Unexpected command: ${method}`));
|
||||
@@ -362,3 +364,57 @@ it("keeps an opaque cookie partition unknown without dropping its value", async
|
||||
});
|
||||
expect(storageComparison(plain, known).status).toBe("changed");
|
||||
});
|
||||
|
||||
it("retains known storage keys and cache names when content or locator metadata is malformed", async () => {
|
||||
const storage = await captureWithCacheResult(
|
||||
{ caches: [{ cacheName: "known-cache" }] },
|
||||
[
|
||||
["known-key", 42],
|
||||
["valid-peer", "recorded"],
|
||||
[42, "unknown identity"],
|
||||
],
|
||||
);
|
||||
expect(storage.local_storage_keys).toEqual(["known-key", "valid-peer"]);
|
||||
expect(storage.session_storage_keys).toEqual(["known-key", "valid-peer"]);
|
||||
expect(storage.cache_names).toEqual(["known-cache"]);
|
||||
expect(storage.fingerprints_complete).toBe(false);
|
||||
const local = storage.content_fingerprints.filter(
|
||||
({ scope }) => scope === "local_storage",
|
||||
);
|
||||
expect(local).toHaveLength(2);
|
||||
expect(local).toContainEqual(
|
||||
expect.objectContaining({ complete: false, value_sha256: null }),
|
||||
);
|
||||
expect(local).toContainEqual(
|
||||
expect.objectContaining({
|
||||
complete: true,
|
||||
value_sha256: expect.any(String),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("retains cookie identity when its content is malformed without inventing a value fingerprint", async () => {
|
||||
const known = await captureCookies([cookieBase]);
|
||||
const unavailable = await captureCookies([
|
||||
{ ...cookieBase, value: 42 },
|
||||
{ ...cookieBase, name: 42 },
|
||||
]);
|
||||
const knownCookie = known.content_fingerprints.find(
|
||||
({ scope }) => scope === "cookie",
|
||||
);
|
||||
if (knownCookie === undefined) throw new Error("Missing known cookie");
|
||||
expect(unavailable.content_fingerprints).toEqual([
|
||||
{
|
||||
scope: "cookie",
|
||||
identity_sha256: knownCookie.identity_sha256,
|
||||
value_sha256: null,
|
||||
complete: false,
|
||||
},
|
||||
]);
|
||||
expect(unavailable.fingerprints_complete).toBe(false);
|
||||
expect(storageComparison(known, unavailable)).toMatchObject({
|
||||
status: "unknown",
|
||||
total_changes: 0,
|
||||
changes: [],
|
||||
});
|
||||
});
|
||||
@@ -83,7 +83,8 @@ export const captureStorage = async (
|
||||
// Like IndexedDB names, a missing, non-array or partly unusable cache
|
||||
// list is an incomplete enumeration rather than an empty one.
|
||||
Array.isArray(cacheField) &&
|
||||
caches.length === cacheField.length;
|
||||
caches.length === cacheField.length &&
|
||||
caches.every(({ id }) => id !== undefined);
|
||||
const keysExclusion = !context.input.include_storage_keys
|
||||
? undefined
|
||||
: local.exclusion === "provider_unavailable" ||
|
||||
@@ -127,7 +128,7 @@ const capturedCaches = (
|
||||
values.flatMap((cache) => {
|
||||
const name = cdpStringValue(cache.cacheName);
|
||||
const id = cdpStringValue(cache.cacheId);
|
||||
return name === undefined || id === undefined ? [] : [{ name, id }];
|
||||
return name === undefined ? [] : [{ name, id }];
|
||||
});
|
||||
|
||||
const storageItems = async (
|
||||
@@ -154,9 +155,14 @@ const storageItems = async (
|
||||
if (!Array.isArray(entry)) return [];
|
||||
const key = cdpStringValue(entry[0]);
|
||||
const value = cdpStringValue(entry[1]);
|
||||
return key === undefined || value === undefined ? [] : [{ key, value }];
|
||||
return key === undefined ? [] : [{ key, value }];
|
||||
});
|
||||
return { items, complete: items.length === entries.length };
|
||||
return {
|
||||
items,
|
||||
complete:
|
||||
items.length === entries.length &&
|
||||
items.every(({ value }) => value !== undefined),
|
||||
};
|
||||
};
|
||||
|
||||
const stringArray = (value: unknown): readonly string[] =>
|
||||
|
||||
@@ -20,14 +20,14 @@ type StorageFingerprint =
|
||||
export interface CapturedStorageItems {
|
||||
readonly items: readonly {
|
||||
readonly key: string;
|
||||
readonly value: string;
|
||||
readonly value: string | undefined;
|
||||
}[];
|
||||
readonly complete: boolean;
|
||||
}
|
||||
|
||||
export interface CapturedCache {
|
||||
readonly name: string;
|
||||
readonly id: string;
|
||||
readonly id: string | undefined;
|
||||
}
|
||||
|
||||
interface StorageFingerprintContext {
|
||||
@@ -106,7 +106,7 @@ const addStorageItems = (
|
||||
scope,
|
||||
identity: item.key,
|
||||
value: item.value,
|
||||
complete: true,
|
||||
complete: item.value !== undefined,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -139,12 +139,7 @@ const addCookies = async (
|
||||
const value = cdpStringValue(cookie.value);
|
||||
const domain = cdpStringValue(cookie.domain);
|
||||
const path = cdpStringValue(cookie.path);
|
||||
if (
|
||||
name === undefined ||
|
||||
value === undefined ||
|
||||
domain === undefined ||
|
||||
path === undefined
|
||||
) {
|
||||
if (name === undefined || domain === undefined || path === undefined) {
|
||||
run.state.complete = false;
|
||||
continue;
|
||||
}
|
||||
@@ -163,7 +158,7 @@ const addCookies = async (
|
||||
...(partitionOpaque ? { partition_opaque: true } : {}),
|
||||
},
|
||||
value,
|
||||
complete: !partitionOpaque,
|
||||
complete: value !== undefined && !partitionOpaque,
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -357,6 +352,10 @@ const addCaches = async (
|
||||
caches: readonly CapturedCache[],
|
||||
): Promise<void> => {
|
||||
for (const cache of caches) {
|
||||
if (cache.id === undefined) {
|
||||
run.state.complete = false;
|
||||
continue;
|
||||
}
|
||||
let skipCount = 0;
|
||||
let returnCount: number | undefined;
|
||||
while (returnCount === undefined || skipCount < returnCount) {
|
||||
|
||||
Reference in new issue
Block a user