From 96606f511455e5b3af1eb6aa14fc9e895f3e9efb Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Sat, 31 Oct 2026 01:18:12 +0800 Subject: [PATCH] test(process): strengthen macOS token parser boundaries --- .../darwinProcessRunTokenReader.test.ts | 15 +++++++-- .../fixtures/processRunTokenParserProbe.swift | 33 ++++++++++++++++++- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/tests/boundary/process/darwinProcessRunTokenReader.test.ts b/tests/boundary/process/darwinProcessRunTokenReader.test.ts index e68bc04008..c9ca7ca9cf 100644 --- a/tests/boundary/process/darwinProcessRunTokenReader.test.ts +++ b/tests/boundary/process/darwinProcessRunTokenReader.test.ts @@ -1312,6 +1312,11 @@ it.skipIf(!onDarwin)( duplicateTokenFailsClosed: true, finalAssignmentArgumentRead: true, missingAppleBoundaryFailsClosed: true, + malformedBuffersRejected: true, + malformedObservation: { + state: "unavailable", + reason: "malformed_procargs", + }, nonAsciiPathRead: true, realTokenRead: true, reservedArgumentIgnored: true, @@ -1441,7 +1446,9 @@ it.skipIf(!onDarwin)( const host = createSystemProcessOwnershipHost("darwin"); const children: Awaited>[] = []; try { - const executables = [join(root, "n"), join(root, "node")]; + const executables = Array.from({ length: 8 }, (_, index) => + join(root, "n".repeat(index + 1)), + ); for (const executable of executables) await symlink(process.execPath, executable); for (let index = 0; index < executables.length; index += 1) { @@ -1467,8 +1474,10 @@ it.skipIf(!onDarwin)( }); const observations = await host.runTokens?.(entries); expect(children.map(({ pid }) => observations?.get(pid))).toEqual([ - { state: "readable", runId: "synthetic-padding-0" }, - { state: "readable", runId: "synthetic-padding-1" }, + ...executables.map((_, index) => ({ + state: "readable", + runId: `synthetic-padding-${String(index)}`, + })), { state: "readable" }, ]); } finally { diff --git a/tests/fixtures/processRunTokenParserProbe.swift b/tests/fixtures/processRunTokenParserProbe.swift index db046ea095..010f8a62d3 100644 --- a/tests/fixtures/processRunTokenParserProbe.swift +++ b/tests/fixtures/processRunTokenParserProbe.swift @@ -123,9 +123,35 @@ struct ProcessRunTokenParserProbe { do { _ = try parseProcessEnvironment(in: truncatedArgv) truncatedArgvFailsClosed = false - } catch { + } catch RunTokenReadError.invalidBuffer { truncatedArgvFailsClosed = true } + var invalidPadding = fixture(arguments: ["x"], environment: [], executablePath: "x") + invalidPadding[6] = 1 // argc (4 bytes), path and its NUL precede the padding. + var unterminated = fixture(arguments: ["x"], environment: [], executablePath: "x") + unterminated.removeLast() + let malformedBuffers: [[UInt8]] = [ + [0, 0, 0], // Short argc. + [255, 255, 255, 255], // Negative argc. + [0, 0, 0, 0, 120], // Unterminated executable path. + invalidPadding, + unterminated, + ] + let malformedBuffersRejected = malformedBuffers.allSatisfy { bytes in + do { + _ = try parseProcessEnvironment(in: bytes) + return false + } catch RunTokenReadError.invalidBuffer { + return true + } catch { + return false + } + } + let malformedObservation = processRunTokenObservation( + pid: 1, + parse: .failure(.invalidBuffer), + codeSigningFlags: { 0 } + ) let duplicateTokenFailsClosed: Bool do { _ = try parseProcessEnvironment(in: fixture( @@ -228,6 +254,11 @@ struct ProcessRunTokenParserProbe { "emptyLaterArgumentRead": emptyLaterArgument.runId == "empty-argument-token", "alternatePaddingRead": alternatePadding.runId == "alternate-padding-token", "truncatedArgvFailsClosed": truncatedArgvFailsClosed, + "malformedBuffersRejected": malformedBuffersRejected, + "malformedObservation": [ + "state": malformedObservation.state, + "reason": malformedObservation.reason as Any? ?? NSNull(), + ], "duplicateTokenFailsClosed": duplicateTokenFailsClosed, "missingAppleBoundaryFailsClosed": missingAppleBoundaryFailsClosed, "emptyArgv0Read": emptyArgv0Read.runId == "empty-argv0-token",