Files
diegosouzapw--omniroute/tests/unit/inspector-system-proxy.test.ts
2026-07-13 13:39:12 +08:00

225 lines
7.3 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import os from "node:os";
import {
__setExec,
apply,
revert,
type ExecFileFn,
} from "../../src/mitm/inspector/systemProxyConfig.ts";
interface Call {
file: string;
args: string[];
}
function makeRecorder(stdoutByCmd: Record<string, string> = {}): {
calls: Call[];
exec: ExecFileFn;
} {
const calls: Call[] = [];
const exec: ExecFileFn = async (file, args) => {
calls.push({ file, args });
const key = `${file} ${args.join(" ")}`;
for (const [pattern, out] of Object.entries(stdoutByCmd)) {
if (key.includes(pattern)) return { stdout: out, stderr: "" };
}
return { stdout: "", stderr: "" };
};
return { calls, exec };
}
test("macOS apply uses execFile array-form and captures previous state", async (t) => {
const orig = os.platform;
(os as { platform: () => NodeJS.Platform }).platform = () => "darwin" as NodeJS.Platform;
t.after(() => {
(os as { platform: () => NodeJS.Platform }).platform = orig;
});
const { calls, exec } = makeRecorder({
"-getwebproxy Wi-Fi": "Enabled: Yes\nServer: 10.0.0.1\nPort: 8888\n",
"-getsecurewebproxy Wi-Fi": "Enabled: No\nServer:\nPort: 0\n",
});
const restore = __setExec(exec);
t.after(restore);
const result = await apply(8080);
assert.equal(result.platform, "macos");
// All calls must use array args, never a single shell string
for (const c of calls) {
assert.ok(Array.isArray(c.args));
// file is bare command name, no spaces / pipes / redirects
assert.ok(!c.file.includes(" "));
assert.ok(!c.file.includes(";"));
assert.ok(!c.file.includes("|"));
}
const setCall = calls.find((c) => c.args.includes("-setwebproxy"));
assert.ok(setCall);
assert.deepEqual(setCall.args, ["-setwebproxy", "Wi-Fi", "127.0.0.1", "8080"]);
const prev = result.previousState as { platform: string; http: { enabled: boolean } };
assert.equal(prev.platform, "macos");
assert.equal(prev.http.enabled, true);
});
test("macOS revert restores prior server when http was enabled", async (t) => {
const orig = os.platform;
(os as { platform: () => NodeJS.Platform }).platform = () => "darwin" as NodeJS.Platform;
t.after(() => {
(os as { platform: () => NodeJS.Platform }).platform = orig;
});
const { calls, exec } = makeRecorder();
const restore = __setExec(exec);
t.after(restore);
await revert({
platform: "macos",
service: "Wi-Fi",
http: { enabled: true, host: "10.0.0.1", port: "8888" },
https: { enabled: false, host: "", port: "" },
});
const restoreCall = calls.find((c) => c.args.includes("-setwebproxy"));
assert.ok(restoreCall);
assert.deepEqual(restoreCall.args, ["-setwebproxy", "Wi-Fi", "10.0.0.1", "8888"]);
// https disabled previously → revert should turn it off
const offCall = calls.find((c) => c.args.includes("-setsecurewebproxystate"));
assert.ok(offCall);
assert.deepEqual(offCall.args, ["-setsecurewebproxystate", "Wi-Fi", "off"]);
});
test("Linux apply uses gsettings with array args", async (t) => {
const orig = os.platform;
(os as { platform: () => NodeJS.Platform }).platform = () => "linux" as NodeJS.Platform;
t.after(() => {
(os as { platform: () => NodeJS.Platform }).platform = orig;
});
const { calls, exec } = makeRecorder({
"get org.gnome.system.proxy mode": "'none'\n",
"get org.gnome.system.proxy.http host": "''\n",
});
const restore = __setExec(exec);
t.after(restore);
const result = await apply(9090);
assert.equal(result.platform, "linux");
const setMode = calls.find(
(c) => c.args[0] === "set" && c.args[1] === "org.gnome.system.proxy" && c.args[2] === "mode"
);
assert.ok(setMode);
assert.deepEqual(setMode.args, ["set", "org.gnome.system.proxy", "mode", "manual"]);
const setHost = calls.find(
(c) =>
c.args[0] === "set" &&
c.args[1] === "org.gnome.system.proxy.http" &&
c.args[2] === "host"
);
assert.ok(setHost);
assert.deepEqual(setHost.args, ["set", "org.gnome.system.proxy.http", "host", "127.0.0.1"]);
// port string is passed as own arg (no shell interpolation)
const setPort = calls.find(
(c) =>
c.args[0] === "set" &&
c.args[1] === "org.gnome.system.proxy.http" &&
c.args[2] === "port"
);
assert.ok(setPort);
assert.equal(setPort.args[3], "9090");
});
test("Linux revert restores recorded gnomeMode", async (t) => {
const orig = os.platform;
(os as { platform: () => NodeJS.Platform }).platform = () => "linux" as NodeJS.Platform;
t.after(() => {
(os as { platform: () => NodeJS.Platform }).platform = orig;
});
const { calls, exec } = makeRecorder();
const restore = __setExec(exec);
t.after(restore);
await revert({
platform: "linux",
gnomeMode: "'auto'",
httpHost: "old.host",
httpPort: "1234",
httpsHost: "",
httpsPort: "",
});
const restoreMode = calls.find(
(c) => c.args[0] === "set" && c.args[1] === "org.gnome.system.proxy" && c.args[2] === "mode"
);
assert.ok(restoreMode);
assert.equal(restoreMode.args[3], "'auto'");
});
test("Windows apply passes proxyArg as single arg, no shell interpolation", async (t) => {
const orig = os.platform;
(os as { platform: () => NodeJS.Platform }).platform = () => "win32" as NodeJS.Platform;
t.after(() => {
(os as { platform: () => NodeJS.Platform }).platform = orig;
});
const { calls, exec } = makeRecorder({
"winhttp show proxy": "Direct access (no proxy server).",
});
const restore = __setExec(exec);
t.after(restore);
const result = await apply(7777);
assert.equal(result.platform, "windows");
const setCall = calls.find((c) => c.args.join(" ") === "winhttp set proxy 127.0.0.1:7777");
assert.ok(setCall);
assert.equal(setCall.file, "netsh");
// Argument is one literal token — no embedded spaces, semicolons, pipes
const proxyArg = setCall.args[setCall.args.length - 1];
assert.equal(proxyArg, "127.0.0.1:7777");
});
test("Windows revert calls netsh winhttp reset proxy", async (t) => {
const orig = os.platform;
(os as { platform: () => NodeJS.Platform }).platform = () => "win32" as NodeJS.Platform;
t.after(() => {
(os as { platform: () => NodeJS.Platform }).platform = orig;
});
const { calls, exec } = makeRecorder();
const restore = __setExec(exec);
t.after(restore);
await revert({ platform: "windows", netshOutput: "" });
const resetCall = calls.find((c) => c.args.join(" ") === "winhttp reset proxy");
assert.ok(resetCall);
});
test("apply throws sanitized error when exec fails", async (t) => {
const orig = os.platform;
(os as { platform: () => NodeJS.Platform }).platform = () => "darwin" as NodeJS.Platform;
t.after(() => {
(os as { platform: () => NodeJS.Platform }).platform = orig;
});
const exec: ExecFileFn = async () => {
throw new Error("ENOENT: /usr/bin/networksetup");
};
const restore = __setExec(exec);
t.after(restore);
await assert.rejects(() => apply(8080), (err: Error) => {
// sanitizeErrorMessage strips paths; assert we still get an Error
assert.ok(err instanceof Error);
assert.ok(err.message.length > 0);
return true;
});
});
test("revert no-ops for unknown platform payload", async (t) => {
const { calls, exec } = makeRecorder();
const restore = __setExec(exec);
t.after(restore);
await revert(null);
await revert({ platform: "freebsd" });
assert.equal(calls.length, 0);
});