{"record":{"id":"d719d117803441aa","repo":"oven-sh/bun","slug":"security-scanner-must-export-a-scanner-object-wi","errorCode":null,"errorMessage":"Security scanner must export a 'scanner' object with a version property","messagePattern":"Security scanner must export a 'scanner' object with a version property","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/install/PackageManager/scanner-entry.ts","lineNumber":88,"sourceCode":"    }\n\n    sendAndExit({\n      type: \"error\",\n      code: \"MODULE_NOT_FOUND\",\n      module: scannerModuleName,\n    });\n  } else {\n    sendAndExit({\n      type: \"error\",\n      code: \"SCAN_FAILED\",\n      message: error instanceof Error ? error.message : String(error),\n    });\n  }\n}\n\ntry {\n  if (typeof scanner !== \"object\" || scanner === null || typeof scanner.version !== \"string\") {\n    throw new Error(\"Security scanner must export a 'scanner' object with a version property\");\n  }\n\n  if (scanner.version !== \"1\") {\n    sendAndExit({\n      type: \"error\",\n      code: \"INVALID_VERSION\",\n      message: `Security scanner must be version 1, got version ${scanner.version}`,\n    });\n  }\n\n  if (typeof scanner.scan !== \"function\") {\n    throw new Error(`scanner.scan is not a function, got ${typeof scanner.scan}`);\n  }\n\n  const result = await scanner.scan({ packages });\n\n  if (!Array.isArray(result)) {\n    throw new Error(\"Security scanner must return an array of advisories\");","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/oven-sh/bun/blob/8c5296ac459e8252d3cd702f3fbcbb0c249d95d5/src/install/PackageManager/scanner-entry.ts#L70-L106","documentation":"After a successful import, the subprocess requires a named export `scanner` that is an object with a string `version` property. This error is thrown when `import(name).scanner` is not such an object — no `scanner` export, a different export shape (default-only), or a missing/non-string `version` — and it is then reported as SCAN_FAILED with this message.","triggerScenarios":"`.scanner` is undefined (default-only export, or a CJS module whose exports are computed so the interop analysis misses them), or `scanner.version` is absent/undefined/not a string (e.g. the number 1).","commonSituations":"Scanner library renamed its export in a new major version; CJS scanner using `module.exports = buildScanner()` computed at load; version defined as a number; docs/examples exporting from the wrong file.","solutions":["Export the object as a named export: `export const scanner = { version: \"1\", scan }`","Print the actual exports: bun -e 'import(\"m\").then(m => console.log(Object.keys(m)))'","For CJS scanners, assign module.exports.scanner statically instead of computing it","Set version to the string \\\"1\\\""],"exampleFix":"// before\nexport default { version: \"1\", scan };\n\n// after\nexport const scanner = { version: \"1\", scan };","handlingStrategy":"type-guard","validationCode":"const mod = await import(\"@corp/scanner\");\nif (typeof mod.scanner !== \"object\" || mod.scanner === null || typeof mod.scanner.version !== \"string\") {\n  throw new Error(\"scanner package must export { scanner: { version: string, scan: fn } }\");\n}","typeGuard":"function isScanner(value: unknown): value is { version: string; scan: (input: { packages: unknown[] }) => Promise<unknown[]> } {\n  return (\n    typeof value === \"object\" &&\n    value !== null &&\n    typeof (value as { version?: unknown }).version === \"string\" &&\n    typeof (value as { scan?: unknown }).scan === \"function\"\n  );\n}","tryCatchPattern":"try {\n  if (!isScanner(mod.scanner)) throw new Error(\"bad scanner export shape\");\n} catch (e) {\n  // message is the contract text; fix the scanner package\n}","preventionTips":["Export scanner as a static named export","Unit-test the export shape inside the scanner package","Keep version a string"],"tags":["security-scanner","contract","exports","validation"],"backgroundTag":null,"analyzedSha":"8c5296ac459e8252d3cd702f3fbcbb0c249d95d5","analyzedAt":"2026-08-16T08:01:58.794Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}