55 lines
2.2 KiB
JavaScript
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" });
|
|
}
|