Files
OmniRoute/tests/unit/tproxy-capture-manager.test.ts
Diego Rodrigues de Sa e Souza 4c6cb45a6a feat(mitm): TPROXY capture-mode manager (decrypt 4a/N) (#4208)
Singleton lifecycle around the decrypt-capable transparent listener (#4200):
builds the dynamic CA (#4173), gates on native-addon availability, holds the
single running handle, counts interceptions, and exposes start / stop / status
for a local-only route (4b) to drive.

The OS trust-store mechanism is deliberately not baked in: installCa / uninstallCa
are injected by the caller (the route / VPS e2e provides the real installer, which
needs its own trust-store slot so it does not clobber the static MITM cert). The
manager therefore never mutates the trust store itself and stays unit-testable
without root or the native addon.

- startCaptureMode: rejects when unavailable or already running; builds the CA;
  wires the injected installCa/uninstallCa through to the decrypt listener.
- stopCaptureMode: stops the handle (close + uninstall CA + revert), idempotent.
- getCaptureStatus: running / available / startedAt / interceptCount / onPort.

Route + real trust-store installer + Traffic Inspector UI tab + VPS
e2e-with-decrypt + addon prebuilds follow (4b+).
2026-06-18 19:21:16 -03:00

123 lines
4.2 KiB
TypeScript

/**
* Fase 3 / Epic A — TPROXY capture-mode manager (decrypt 4a/N).
*
* The manager is the singleton lifecycle a (local-only) route drives: it gates on
* native-addon availability, builds the dynamic CA, wires the injected trust-store
* installer through to the decrypt listener, counts interceptions, and reports
* status. All effectful seams are injected so this is testable without root or the
* native addon. The real intercept/decrypt path was proven in #4169/#4179/#4200.
*/
import test from "node:test";
import assert from "node:assert/strict";
import {
startCaptureMode,
stopCaptureMode,
getCaptureStatus,
__resetCaptureManager,
type StartCaptureModeOptions,
} from "../../src/mitm/tproxy/captureManager.ts";
const CFG = { dport: 443, mark: 0x2333, onPort: 8443, routeTable: 233, bypassMark: 0x539 };
interface CapturedStart {
cfg?: unknown;
options?: {
decrypt?: { installCa: (p: string) => Promise<void>; uninstallCa: () => Promise<void> };
onIntercept?: (info: { destIp: string; destPort: number }) => void;
};
stopped: boolean;
}
/** A fake `startTproxyCapture` that records what it was handed and returns a
* handle whose stop() flips a flag. */
function fakeStart(rec: CapturedStart) {
return async (cfg: unknown, options: CapturedStart["options"]) => {
rec.cfg = cfg;
rec.options = options;
return {
cfg: cfg as never,
server: {} as never,
stop: async () => {
rec.stopped = true;
},
};
};
}
function baseOptions(over: Partial<StartCaptureModeOptions> = {}): StartCaptureModeOptions {
return {
cfg: CFG,
installCa: async () => {},
uninstallCa: async () => {},
...over,
};
}
test.afterEach(() => __resetCaptureManager());
test("startCaptureMode rejects when the native addon is unavailable", async () => {
await assert.rejects(
() => startCaptureMode(baseOptions({ deps: { isAvailable: () => false } })),
/native addon|CAP_NET_ADMIN/
);
});
test("startCaptureMode wires the injected CA installer into the decrypt listener", async () => {
const rec: CapturedStart = { stopped: false };
const installCa = async () => {};
const uninstallCa = async () => {};
const status = await startCaptureMode(
baseOptions({
installCa,
uninstallCa,
deps: { isAvailable: () => true, startTproxyCapture: fakeStart(rec) as never },
})
);
assert.equal(status.running, true);
assert.equal(status.onPort, 8443);
assert.equal(rec.options?.decrypt?.installCa, installCa, "installCa is passed through to decrypt");
assert.equal(rec.options?.decrypt?.uninstallCa, uninstallCa, "uninstallCa is passed through");
});
test("startCaptureMode refuses to start a second concurrent session", async () => {
const rec: CapturedStart = { stopped: false };
const deps = { isAvailable: () => true, startTproxyCapture: fakeStart(rec) as never };
await startCaptureMode(baseOptions({ deps }));
await assert.rejects(() => startCaptureMode(baseOptions({ deps })), /already running/);
});
test("stopCaptureMode stops the handle and clears the running state", async () => {
const rec: CapturedStart = { stopped: false };
await startCaptureMode(
baseOptions({ deps: { isAvailable: () => true, startTproxyCapture: fakeStart(rec) as never } })
);
const status = await stopCaptureMode();
assert.equal(rec.stopped, true, "the handle's stop() was invoked");
assert.equal(status.running, false);
});
test("stopCaptureMode is a no-op when nothing is running", async () => {
const status = await stopCaptureMode();
assert.equal(status.running, false);
});
test("getCaptureStatus counts interceptions reported by the listener", async () => {
const rec: CapturedStart = { stopped: false };
await startCaptureMode(
baseOptions({
deps: { isAvailable: () => true, startTproxyCapture: fakeStart(rec) as never },
})
);
rec.options?.onIntercept?.({ destIp: "1.2.3.4", destPort: 443 });
rec.options?.onIntercept?.({ destIp: "5.6.7.8", destPort: 443 });
const status = getCaptureStatus();
assert.equal(status.running, true);
assert.equal(status.interceptCount, 2);
});
test("getCaptureStatus reports not-running when idle", () => {
const status = getCaptureStatus();
assert.equal(status.running, false);
assert.equal(typeof status.available, "boolean");
});