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..f7137f1ea64e --- /dev/null +++ b/test/js/bun/test/mock/mock-module-non-string.test.ts @@ -0,0 +1,18 @@ +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"); +}); + +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); +});