From 10407fb62409d8fc84e6e33c865bb644b2b072da Mon Sep 17 00:00:00 2001 From: ShuaixinHuang <1836656514@qq.com> Date: Sat, 7 Nov 2026 01:29:22 +0800 Subject: [PATCH] fix(dotnet): reject invalid UTF-8 metadata version strings (#9156) * test(process): align symlink fixture with filesystem checkpoints * test(frida): assert the CLI spawn Evidence contract * test(mcp): use an unsupported analysis-view parent * test(browser): separate source-location line bounds from parity * fix(dotnet): reject invalid UTF-8 metadata version strings * test(dotnet): preserve valid multibyte metadata version text --- src/dotnet/ManagedMetadataHeaps.test.ts | 18 ++++++++++++++++++ src/dotnet/ManagedMetadataLayout.ts | 14 +++++++++++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/dotnet/ManagedMetadataHeaps.test.ts b/src/dotnet/ManagedMetadataHeaps.test.ts index 469f547b34..5ad03227f0 100644 --- a/src/dotnet/ManagedMetadataHeaps.test.ts +++ b/src/dotnet/ManagedMetadataHeaps.test.ts @@ -39,6 +39,24 @@ describe("managed metadata heaps", () => { const layout = readManagedMetadataLayout(bytes, 0, bytes.length); expect(layout.streamNames).toEqual(["#GUID", "#Strings", "#~"]); expect(layout.blob).toBeNull(); + expect(layout.version).toBe("v4.0.30319"); + + bytes.fill(0, 16, 16 + version.length); + Buffer.from("v😀é", "utf8").copy(bytes, 16); + expect(readManagedMetadataLayout(bytes, 0, bytes.length).version).toBe( + "v😀é", + ); + + bytes[16] = 0xff; + expect(() => readManagedMetadataLayout(bytes, 0, bytes.length)).toThrow( + expect.objectContaining({ + issue: expect.objectContaining({ + code: "invalid-metadata-root", + scope: "metadata.version", + offset: 16, + }), + }), + ); }); it("accepts the empty blob entry when an empty #Blob stream is omitted", () => { diff --git a/src/dotnet/ManagedMetadataLayout.ts b/src/dotnet/ManagedMetadataLayout.ts index cf4025b6f4..d114e6cd77 100644 --- a/src/dotnet/ManagedMetadataLayout.ts +++ b/src/dotnet/ManagedMetadataLayout.ts @@ -1,3 +1,5 @@ +import { isUtf8 } from "node:buffer"; + import { attachManagedDecodeBudget, ManagedDecodeBudget, @@ -241,9 +243,15 @@ const readMetadataStreams = ( const zero = versionBytes.indexOf(0); const decodedVersionLength = zero < 0 ? versionBytes.length : zero; budget.reserveString(decodedVersionLength, rootOffset + 16); - const version = versionBytes - .subarray(0, decodedVersionLength) - .toString("utf8"); + const versionText = versionBytes.subarray(0, decodedVersionLength); + if (!isUtf8(versionText)) + throw managedFailure( + "invalid-metadata-root", + "metadata.version", + "Metadata version is not valid UTF-8", + rootOffset + 16, + ); + const version = versionText.toString("utf8"); let cursor = align4(16 + versionLength); requireRange(bytes, rootOffset, cursor, 4, "metadata.stream-count"); const streamCount = bytes.readUInt16LE(cursor + 2);