Files
2026-07-13 13:39:12 +08:00

55 lines
2.2 KiB
JavaScript

/**
* Best-effort build of the TPROXY IP_TRANSPARENT native addon so the production
* build can copy `build/Release/transparent.node` into the standalone bundle
* (assembleStandalone's NATIVE_ASSET_ENTRIES). Called from build-next-isolated.mjs
* before the standalone is assembled.
*
* IP_TRANSPARENT is Linux-only, so this is a no-op everywhere else. A missing C
* toolchain is NOT fatal — the TPROXY capture mode degrades gracefully when the
* addon is absent (transparentSocket.ts returns "unavailable"). Every effectful
* seam (platform/run/exists) is injectable so the decision logic is unit-testable.
*
* Hard Rule #13: the command + args are a fixed allowlist (no interpolation of
* external/runtime values); `cwd` is derived from `projectRoot`, never user input.
*/
import { execFileSync } from "node:child_process";
import { existsSync } from "node:fs";
import path from "node:path";
/**
* @param {string} projectRoot
* @param {{ platform?: string, run?: (cmd:string, args:string[], cwd:string) => void,
* exists?: (p:string) => boolean }} [opts]
* @returns {{ built: boolean, reason?: string }}
*/
export function buildTproxyNative(projectRoot, opts = {}) {
const platform = opts.platform ?? process.platform;
const run = opts.run ?? defaultRun;
const exists = opts.exists ?? existsSync;
if (platform !== "linux") {
return { built: false, reason: "non-linux host (IP_TRANSPARENT is Linux-only)" };
}
const nativeDir = path.join(projectRoot, "src", "mitm", "tproxy", "native");
if (!exists(path.join(nativeDir, "binding.gyp"))) {
return { built: false, reason: "native sources absent (binding.gyp not found)" };
}
const out = path.join(nativeDir, "build", "Release", "transparent.node");
try {
run("npx", ["--yes", "node-gyp", "rebuild"], nativeDir);
} catch (err) {
return { built: false, reason: `toolchain/build failed: ${err?.message ?? String(err)}` };
}
if (!exists(out)) {
return { built: false, reason: "node-gyp produced no transparent.node" };
}
return { built: true };
}
/** @type {(cmd: string, args: string[], cwd: string) => void} */
function defaultRun(cmd, args, cwd) {
execFileSync(cmd, args, { cwd, stdio: "inherit" });
}