From 92f715d9f30bc1a7eae58f50994bd59e05abbb13 Mon Sep 17 00:00:00 2001 From: Tomas Zijdemans Date: Fri, 24 Jul 2026 13:46:53 +0200 Subject: [PATCH 1/3] fix(cbor): stop leaking inherited keys into encoded objects --- cbor/_common_encode.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/cbor/_common_encode.ts b/cbor/_common_encode.ts index b038bd86eb4a..d6cd4096c582 100644 --- a/cbor/_common_encode.ts +++ b/cbor/_common_encode.ts @@ -51,13 +51,13 @@ export function calcEncodingSize(x: CborType): number { for (const y of x) size += calcEncodingSize(y[0]) + calcEncodingSize(y[1]); return size; } - let pairs = 0; + // Iterate own keys only, matching encodeObject's pair count. + const keys = Object.keys(x); let size = 0; - for (const y in x) { - ++pairs; + for (const y of keys) { size += calcHeaderSize(y.length) + y.length + calcEncodingSize(x[y]); } - return size + calcHeaderSize(pairs); + return size + calcHeaderSize(keys.length); } export function encode( @@ -217,8 +217,11 @@ function encodeObject( offset: number, ): number { output[offset] = 0b101_00000; - offset = encodeHeader(0b101_00000, Object.keys(input).length, output, offset); - for (const key in input) { + // Iterate the same own keys the header counts; for-in would also visit + // inherited enumerable keys and desync the pair count. + const keys = Object.keys(input); + offset = encodeHeader(0b101_00000, keys.length, output, offset); + for (const key of keys) { offset = encodeString(key, output, offset); offset = encode(input[key], output, offset); } From 5292a9e4487c293a21a968f6c9c25b4480dcf40f Mon Sep 17 00:00:00 2001 From: Tomas Zijdemans Date: Fri, 24 Jul 2026 13:50:20 +0200 Subject: [PATCH 2/3] fix code comment --- cbor/_common_encode.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cbor/_common_encode.ts b/cbor/_common_encode.ts index d6cd4096c582..b1a2be923743 100644 --- a/cbor/_common_encode.ts +++ b/cbor/_common_encode.ts @@ -51,7 +51,7 @@ export function calcEncodingSize(x: CborType): number { for (const y of x) size += calcEncodingSize(y[0]) + calcEncodingSize(y[1]); return size; } - // Iterate own keys only, matching encodeObject's pair count. + // Must iterate the same keys as encodeObject(). const keys = Object.keys(x); let size = 0; for (const y of keys) { @@ -217,8 +217,7 @@ function encodeObject( offset: number, ): number { output[offset] = 0b101_00000; - // Iterate the same own keys the header counts; for-in would also visit - // inherited enumerable keys and desync the pair count. + // Must iterate the same keys as calcObjectEncodingSize(). const keys = Object.keys(input); offset = encodeHeader(0b101_00000, keys.length, output, offset); for (const key of keys) { From 72aa2e8ce2a442b36c24eda17599ba831e056802 Mon Sep 17 00:00:00 2001 From: Tomas Zijdemans Date: Thu, 30 Jul 2026 19:27:45 +0200 Subject: [PATCH 3/3] fix stale comment reference and add regression test for inherited keys Co-Authored-By: Claude Fable 5 --- cbor/_common_encode.ts | 2 +- cbor/encode_cbor_test.ts | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/cbor/_common_encode.ts b/cbor/_common_encode.ts index b1a2be923743..08123023a817 100644 --- a/cbor/_common_encode.ts +++ b/cbor/_common_encode.ts @@ -217,7 +217,7 @@ function encodeObject( offset: number, ): number { output[offset] = 0b101_00000; - // Must iterate the same keys as calcObjectEncodingSize(). + // Must iterate the same keys as calcEncodingSize(). const keys = Object.keys(input); offset = encodeHeader(0b101_00000, keys.length, output, offset); for (const key of keys) { diff --git a/cbor/encode_cbor_test.ts b/cbor/encode_cbor_test.ts index 0e71085be55d..06c1f05cf612 100644 --- a/cbor/encode_cbor_test.ts +++ b/cbor/encode_cbor_test.ts @@ -440,6 +440,21 @@ Deno.test("encodeCbor() encoding objects", () => { // Can't test the next two bracket up due to JavaScript limitations. }); +Deno.test("encodeCbor() ignores inherited enumerable properties", () => { + const input: { [k: string]: CborType } = Object.assign( + Object.create({ inherited: 1 }), + { own: 2 }, + ); + assertEquals( + encodeCbor(input), + new Uint8Array([ + 0b101_00001, + ...encodeCbor("own"), + ...encodeCbor(2), + ]), + ); +}); + Deno.test("encodeCbor() encoding CborTag()", () => { const bytes = new Uint8Array(random(0, 24)).map((_) => random(0, 256)); assertEquals(