147 lines · 4.7 KB · JavaScript
RawDownload
import * as NodeEvents from "node:events";import * as NodeFS from "node:fs";import * as NodeFSP from "node:fs/promises";import * as NodeTimers from "node:timers";import * as NodeURL from "node:url";import * as NodeVM from "node:vm";import { parse } from "acorn"; const expectedDesktopBridgeApis = [  "getClientPlatform",  "getLocalEnvironmentBootstraps",  "pickFolder",];const clerkPasskeysGlobal = "__clerk_internal_electron_passkeys";const preloadExecutionTimeoutMs = 1_000;const desktopPackage = JSON.parse(  NodeFS.readFileSync(new URL("../package.json", import.meta.url), "utf8"),);const electronVersion = desktopPackage.dependencies.electron; const isSyntaxNode = (value) =>  typeof value === "object" && value !== null && "type" in value && typeof value.type === "string"; const inspectBundle = (source) => {  const runtimeImports = [];  const visit = (node) => {    if (node.type === "ImportExpression") {      throw new Error("Desktop preload bundle contains a dynamic import() call");    }     if (node.type === "CallExpression" && node.callee.type === "Identifier") {      if (node.callee.name === "require") {        const [argument] = node.arguments;        if (node.arguments.length !== 1 || argument?.type !== "Literal") {          throw new Error("Desktop preload bundle contains a dynamic require() call");        }        if (typeof argument.value !== "string") {          throw new Error("Desktop preload bundle contains a dynamic require() call");        }        runtimeImports.push(argument.value);      }    }     for (const child of Object.values(node)) {      if (Array.isArray(child)) {        for (const item of child) {          if (isSyntaxNode(item)) visit(item);        }      } else if (isSyntaxNode(child)) {        visit(child);      }    }  };   visit(parse(source, { ecmaVersion: "latest", sourceType: "script" }));  return runtimeImports;}; const createSandboxModules = (exposedGlobals) => {  const ipcRenderer = {    invoke: () => Promise.resolve(undefined),    on: () => undefined,    removeListener: () => undefined,    sendSync: () => undefined,  };  const electron = {    contextBridge: {      exposeInMainWorld: (name, api) => exposedGlobals.set(name, api),    },    ipcRenderer,  };   return new Map([    ["electron", electron],    ["electron/common", electron],    ["electron/renderer", electron],    ["events", NodeEvents.default],    ["node:events", NodeEvents.default],    ["timers", NodeTimers.default],    ["node:timers", NodeTimers.default],    ["url", NodeURL.default],    ["node:url", NodeURL.default],  ]);}; const executeBundle = (source, sandboxModules) => {  const sandboxProcess = {    contextIsolated: true,    // oxlint-disable-next-line t3code/no-global-process-runtime -- This standalone CI verifier supplies the preload's host platform without loading Effect.    platform: process.platform,    versions: { electron: electronVersion },  };  const requireSandboxModule = (moduleName) => {    if (!sandboxModules.has(moduleName)) {      throw new Error(        `Unsupported sandbox module requested during preload execution: ${moduleName}`,      );    }    return sandboxModules.get(moduleName);  };   NodeVM.runInNewContext(    source,    {      process: sandboxProcess,      require: requireSandboxModule,    },    {      filename: "desktop-preload.cjs",      timeout: preloadExecutionTimeoutMs,    },  );}; export const verifyPreloadBundle = (source) => {  const runtimeImports = inspectBundle(source);  const exposedGlobals = new Map();  const sandboxModules = createSandboxModules(exposedGlobals);  const unsupportedImports = [...new Set(runtimeImports)]    .filter((moduleName) => !sandboxModules.has(moduleName))    .toSorted();   if (unsupportedImports.length > 0) {    throw new Error(      `Desktop preload bundle contains unsupported sandbox imports: ${unsupportedImports.join(", ")}`,    );  }   executeBundle(source, sandboxModules);   const desktopBridge = exposedGlobals.get("desktopBridge");  const missingApis = expectedDesktopBridgeApis.filter(    (api) => typeof desktopBridge?.[api] !== "function",  );  if (!exposedGlobals.has("desktopBridge")) missingApis.unshift("desktopBridge exposure");  if (!exposedGlobals.has(clerkPasskeysGlobal)) missingApis.push(`${clerkPasskeysGlobal} exposure`);   if (missingApis.length > 0) {    throw new Error(`Desktop preload bundle is missing executable APIs: ${missingApis.join(", ")}`);  }}; if (process.argv[1] && NodeURL.pathToFileURL(process.argv[1]).href === import.meta.url) {  const preloadUrl = new URL("../dist-electron/preload.cjs", import.meta.url);  const source = await NodeFSP.readFile(preloadUrl, "utf8");  verifyPreloadBundle(source);}