_structuredClone dispatches platform-object hooks as hook(value, seen), but the CryptoKey hook took only `src` and never touched the memo, and hook branches return before the generic seen.set. So one CryptoKey referenced twice in a graph cloned into two distinct objects (and re-registered its key material each time), where structuredClone preserves reference identity within a graph. Consult and populate `seen` in the hook. Adds a parity test asserting a key reached twice clones to one shared object that crypto.subtle still accepts.
This commit is contained in:
1 parent
891d850d19
commit
8698afc065
2 files changed
+41
-1
No files matched your search
@@ -207,3 +207,38 @@ async fn structured_clone_rejects_functions_and_symbols() {
|
|||||||
assert_eq!(val["fn"], "DataCloneError", "functions must not clone");
|
assert_eq!(val["fn"], "DataCloneError", "functions must not clone");
|
||||||
assert_eq!(val["sym"], "DataCloneError", "symbols must not clone");
|
assert_eq!(val["sym"], "DataCloneError", "symbols must not clone");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// structuredClone preserves reference identity within one graph, including for
|
||||||
|
// platform objects cloned through a hook (issue #423). The CryptoKey hook
|
||||||
|
// ignored the `seen` map _structuredClone hands it, so one key referenced twice
|
||||||
|
// came back as two distinct objects.
|
||||||
|
#[tokio::test(flavor = "current_thread")]
|
||||||
|
async fn structured_clone_preserves_cryptokey_identity() {
|
||||||
|
let (mut ctx, sid) = setup().await;
|
||||||
|
let v = eval(
|
||||||
|
&mut ctx,
|
||||||
|
2,
|
||||||
|
r#"(async () => {
|
||||||
|
const key = await crypto.subtle.importKey(
|
||||||
|
"raw", new Uint8Array(32),
|
||||||
|
{ name: "HMAC", hash: "SHA-256" }, true, ["sign"]
|
||||||
|
);
|
||||||
|
const clone = structuredClone({ a: key, b: key });
|
||||||
|
// The shared clone must still be usable by crypto.subtle.
|
||||||
|
const sig = await crypto.subtle.sign("HMAC", clone.a, new TextEncoder().encode("abc"));
|
||||||
|
return JSON.stringify({
|
||||||
|
shared: clone.a === clone.b,
|
||||||
|
distinctFromSource: clone.a !== key,
|
||||||
|
tag: clone.a[Symbol.toStringTag],
|
||||||
|
sigLen: new Uint8Array(sig).length,
|
||||||
|
});
|
||||||
|
})()"#,
|
||||||
|
&sid,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let val = serde_json::from_str::<Value>(v["result"]["value"].as_str().unwrap()).unwrap();
|
||||||
|
assert_eq!(val["shared"], true, "one CryptoKey reached twice must clone to one shared object");
|
||||||
|
assert_eq!(val["distinctFromSource"], true, "clone must not alias the source key");
|
||||||
|
assert_eq!(val["tag"], "CryptoKey");
|
||||||
|
assert_eq!(val["sigLen"], 32, "the shared clone must remain usable by crypto.subtle");
|
||||||
|
}
|
||||||
Vendored
+6
-1
@@ -7094,8 +7094,13 @@ if (!globalThis.crypto.subtle) {
|
|||||||
// _structuredClone via Symbol.toStringTag ("CryptoKey"); registered lazily
|
// _structuredClone via Symbol.toStringTag ("CryptoKey"); registered lazily
|
||||||
// because structuredClone is defined before this block (issue #389).
|
// because structuredClone is defined before this block (issue #389).
|
||||||
globalThis.__obscura_clone_hooks = globalThis.__obscura_clone_hooks || {};
|
globalThis.__obscura_clone_hooks = globalThis.__obscura_clone_hooks || {};
|
||||||
globalThis.__obscura_clone_hooks["CryptoKey"] = function (src) {
|
// `seen` is the clone memo _structuredClone hands every hook. Populate it so
|
||||||
|
// one key reached twice in a graph clones to one shared object (and its key
|
||||||
|
// material is registered once), matching structuredClone's identity rules.
|
||||||
|
globalThis.__obscura_clone_hooks["CryptoKey"] = function (src, seen) {
|
||||||
|
if (seen && seen.has(src)) return seen.get(src);
|
||||||
const copy = makeKey(src.type, src.extractable, src.algorithm, src.usages, keyBytes(src));
|
const copy = makeKey(src.type, src.extractable, src.algorithm, src.usages, keyBytes(src));
|
||||||
|
if (seen) seen.set(src, copy);
|
||||||
return copy;
|
return copy;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user