From eb9df84655ccb6388d03a1a4d4fbeef5109bdad4 Mon Sep 17 00:00:00 2001 From: robobun Date: Tue, 24 Mar 2026 18:15:58 +0000 Subject: [PATCH 1/2] Validate mock.module() first argument is a string mock.module() calls toString() on its first argument before checking its type, then passes the result through the module resolver. When a non-string value like SharedArrayBuffer is passed, toString() produces a string like "function SharedArrayBuffer() { [native code] }" which the resolver tries to auto-install as a package, crashing because the package manager's logger allocator is uninitialized in this context. Add an isString() check before the toString() call, matching the validation pattern used by Jest.call() and other Bun APIs. --- src/bun.js/bindings/BunPlugin.cpp | 5 +++++ .../bun/test/mock/mock-module-non-string.test.ts | 14 ++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 test/js/bun/test/mock/mock-module-non-string.test.ts diff --git a/src/bun.js/bindings/BunPlugin.cpp b/src/bun.js/bindings/BunPlugin.cpp index d330fba8f49b..f5abe87b3cc3 100644 --- a/src/bun.js/bindings/BunPlugin.cpp +++ b/src/bun.js/bindings/BunPlugin.cpp @@ -511,6 +511,11 @@ extern "C" JSC_DEFINE_HOST_FUNCTION(JSMock__jsModuleMock, (JSC::JSGlobalObject * return {}; } + if (!callframe->argument(0).isString()) { + scope.throwException(lexicalGlobalObject, JSC::createTypeError(lexicalGlobalObject, "mock(module, fn) requires a module name string"_s)); + return {}; + } + JSC::JSString* specifierString = callframe->argument(0).toString(globalObject); RETURN_IF_EXCEPTION(scope, {}); WTF::String specifier = specifierString->value(globalObject); diff --git a/test/js/bun/test/mock/mock-module-non-string.test.ts b/test/js/bun/test/mock/mock-module-non-string.test.ts new file mode 100644 index 000000000000..ef19d7a2e029 --- /dev/null +++ b/test/js/bun/test/mock/mock-module-non-string.test.ts @@ -0,0 +1,14 @@ +import { expect, mock, test } from "bun:test"; + +test("mock.module throws TypeError for non-string first argument", () => { + expect(() => mock.module(SharedArrayBuffer, () => ({}))).toThrow("mock(module, fn) requires a module name string"); + expect(() => mock.module({}, () => ({}))).toThrow("mock(module, fn) requires a module name string"); + expect(() => mock.module(123, () => ({}))).toThrow("mock(module, fn) requires a module name string"); + expect(() => mock.module(Symbol("test"), () => ({}))).toThrow("mock(module, fn) requires a module name string"); +}); + +test("mock.module still works with valid string argument", async () => { + mock.module("mock-module-non-string-test-fixture", () => ({ default: 42 })); + const m = await import("mock-module-non-string-test-fixture"); + expect(m.default).toBe(42); +}); From c4cc29010b37926a97f7cf0deef0f23510c7751c Mon Sep 17 00:00:00 2001 From: robobun Date: Tue, 24 Mar 2026 20:02:25 +0000 Subject: [PATCH 2/2] Add @ts-expect-error annotations for non-string mock.module calls --- test/js/bun/test/mock/mock-module-non-string.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/js/bun/test/mock/mock-module-non-string.test.ts b/test/js/bun/test/mock/mock-module-non-string.test.ts index ef19d7a2e029..f7137f1ea64e 100644 --- a/test/js/bun/test/mock/mock-module-non-string.test.ts +++ b/test/js/bun/test/mock/mock-module-non-string.test.ts @@ -1,9 +1,13 @@ import { expect, mock, test } from "bun:test"; test("mock.module throws TypeError for non-string first argument", () => { + // @ts-expect-error expect(() => mock.module(SharedArrayBuffer, () => ({}))).toThrow("mock(module, fn) requires a module name string"); + // @ts-expect-error expect(() => mock.module({}, () => ({}))).toThrow("mock(module, fn) requires a module name string"); + // @ts-expect-error expect(() => mock.module(123, () => ({}))).toThrow("mock(module, fn) requires a module name string"); + // @ts-expect-error expect(() => mock.module(Symbol("test"), () => ({}))).toThrow("mock(module, fn) requires a module name string"); });