Files
OmniRoute/tests/unit/proxySubscription.coreEndpoint.test.ts

106 lines
4.3 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
const mod = await import("../../src/lib/proxySubscription/coreEndpoint.ts");
const {
isLocalCoreEndpointAllowed,
ALLOWED_LOCAL_CORE_HOSTS,
parseLocalCoreEndpoints,
redactCoreEntryForDetail,
} = mod;
test("loopback hosts are allowed", () => {
assert.equal(isLocalCoreEndpointAllowed("socks5://127.0.0.1:1080"), true);
assert.equal(isLocalCoreEndpointAllowed("http://localhost:2080"), true);
});
test("remote hosts are rejected", () => {
assert.equal(isLocalCoreEndpointAllowed("socks5://10.0.0.1:1080"), false);
assert.equal(isLocalCoreEndpointAllowed("http://example.com:2080"), false);
assert.equal(isLocalCoreEndpointAllowed("https://192.168.1.1:443"), false);
});
test("non-proxy schemes on loopback are rejected", () => {
assert.equal(isLocalCoreEndpointAllowed("ftp://127.0.0.1:21"), false);
assert.equal(isLocalCoreEndpointAllowed("file:///tmp/core.sock"), false);
});
test("null / empty / malformed endpoints are rejected", () => {
assert.equal(isLocalCoreEndpointAllowed(null), false);
assert.equal(isLocalCoreEndpointAllowed(""), false);
assert.equal(isLocalCoreEndpointAllowed("not a url"), false);
});
test("allowed host set is loopback-only", () => {
assert.deepEqual([...ALLOWED_LOCAL_CORE_HOSTS].sort(), ["127.0.0.1", "::1", "localhost"].sort());
});
test("parseLocalCoreEndpoints splits one entry per line", () => {
assert.deepEqual(
parseLocalCoreEndpoints(
"socks5://127.0.0.1:1080\nsocks5://127.0.0.1:1081\nhttp://localhost:2080"
),
["socks5://127.0.0.1:1080", "socks5://127.0.0.1:1081", "http://localhost:2080"]
);
});
test("parseLocalCoreEndpoints handles CRLF and drops blank lines", () => {
assert.deepEqual(
parseLocalCoreEndpoints("socks5://127.0.0.1:1080\r\n\r\n \r\nsocks5://127.0.0.1:1081\r\n"),
["socks5://127.0.0.1:1080", "socks5://127.0.0.1:1081"]
);
});
test("parseLocalCoreEndpoints trims each line", () => {
assert.deepEqual(
parseLocalCoreEndpoints(" socks5://127.0.0.1:1080 \n\tsocks5://127.0.0.1:1081\t"),
["socks5://127.0.0.1:1080", "socks5://127.0.0.1:1081"]
);
});
test("parseLocalCoreEndpoints never splits on commas", () => {
// A comma is legitimate inside userinfo; it must survive the split.
assert.deepEqual(parseLocalCoreEndpoints("socks5://user,name:pass@127.0.0.1:1080"), [
"socks5://user,name:pass@127.0.0.1:1080",
]);
// Two URLs on one comma-separated line are a single entry (rejected by the
// loopback gate later, not by the parser).
assert.deepEqual(parseLocalCoreEndpoints("socks5://127.0.0.1:1080, socks5://127.0.0.1:1081"), [
"socks5://127.0.0.1:1080, socks5://127.0.0.1:1081",
]);
});
test("parseLocalCoreEndpoints returns [] for null or blank input", () => {
assert.deepEqual(parseLocalCoreEndpoints(null), []);
assert.deepEqual(parseLocalCoreEndpoints(""), []);
assert.deepEqual(parseLocalCoreEndpoints(" \n \r\n "), []);
});
test("parseLocalCoreEndpoints keeps a port-less entry as-is", () => {
// The default port (8080 per the upsert rule) is applied in the sync loop.
assert.deepEqual(parseLocalCoreEndpoints("socks5://127.0.0.1"), ["socks5://127.0.0.1"]);
});
test("redactCoreEntryForDetail strips userinfo from parseable entries", () => {
const redacted = redactCoreEntryForDetail("socks5://user:pass@127.0.0.1:1080");
assert.ok(!redacted.includes("user"), `userinfo leaked: ${redacted}`);
assert.ok(!redacted.includes("pass"), `password leaked: ${redacted}`);
assert.ok(redacted.includes("127.0.0.1:1080"));
});
test("redactCoreEntryForDetail leaves entries without userinfo unchanged", () => {
assert.equal(redactCoreEntryForDetail("socks5://127.0.0.1:1080"), "socks5://127.0.0.1:1080");
});
test("redactCoreEntryForDetail strips userinfo even from malformed entries", () => {
const redacted = redactCoreEntryForDetail("socks5://user:pass@???");
assert.ok(!redacted.includes("user"), `userinfo leaked: ${redacted}`);
assert.ok(!redacted.includes("pass"), `password leaked: ${redacted}`);
});
test("redactCoreEntryForDetail keeps a @ in the path of malformed entries", () => {
// No authority userinfo here (no scheme, @ after a path slash): kept
// verbatim so the stored detail stays diagnosable.
assert.equal(redactCoreEntryForDetail("not-a-url/path@seg"), "not-a-url/path@seg");
});