100 lines
3.8 KiB
TypeScript
100 lines
3.8 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import express from "express";
|
|
import { applyTrustProxy, parseTrustProxyEnv } from "../middleware/trust-proxy.js";
|
|
|
|
function appWithEnv(raw: string | undefined): express.Express {
|
|
const app = express();
|
|
applyTrustProxy(app, parseTrustProxyEnv(raw));
|
|
return app;
|
|
}
|
|
|
|
describe("parseTrustProxyEnv", () => {
|
|
it("unset leaves Express at its safe default (trust nothing)", () => {
|
|
// Express's default trust-proxy setting is `false`. We verify the
|
|
// setting is unchanged by comparing against a vanilla express()
|
|
// instance that never had `applyTrustProxy` called on it.
|
|
const baseline = express().get("trust proxy");
|
|
const app = appWithEnv(undefined);
|
|
expect(parseTrustProxyEnv(undefined)).toBeUndefined();
|
|
expect(app.get("trust proxy")).toBe(baseline);
|
|
});
|
|
|
|
it("empty / false / 0 are treated as unset", () => {
|
|
expect(parseTrustProxyEnv("")).toBeUndefined();
|
|
expect(parseTrustProxyEnv("false")).toBeUndefined();
|
|
expect(parseTrustProxyEnv("0")).toBeUndefined();
|
|
const baseline = express().get("trust proxy");
|
|
expect(appWithEnv("0").get("trust proxy")).toBe(baseline);
|
|
});
|
|
|
|
it("'true' yields boolean true and sets app accordingly", () => {
|
|
expect(parseTrustProxyEnv("true")).toBe(true);
|
|
expect(appWithEnv("true").get("trust proxy")).toBe(true);
|
|
});
|
|
|
|
it("positive integer is parsed as a number", () => {
|
|
expect(parseTrustProxyEnv("2")).toBe(2);
|
|
expect(appWithEnv("2").get("trust proxy")).toBe(2);
|
|
});
|
|
|
|
it("'01' throws (strict integer, no leading zeros)", () => {
|
|
expect(() => parseTrustProxyEnv("01")).toThrow(/invalid integer/);
|
|
});
|
|
|
|
it("integer with internal whitespace throws", () => {
|
|
// A value like "1 2" (digits + whitespace + digits) is clearly not
|
|
// a single int and not a subnet list either — must be rejected.
|
|
// This is distinct from " 2 " (surrounding whitespace), which the
|
|
// outer `raw.trim()` accepts; see the next test for that contract.
|
|
// The parser happens to reach the subnet-token path for "1 2"
|
|
// (the inner-whitespace integer guard only fires when the whole
|
|
// string is `^\s*\d+\s*$`), so we match the unrecognized-token
|
|
// error rather than the "invalid integer" branch.
|
|
expect(() => parseTrustProxyEnv("1 2")).toThrow(/unrecognized token "1 2"/);
|
|
});
|
|
|
|
it("integer with surrounding whitespace is accepted (trimmed)", () => {
|
|
// The parser intentionally trims the *outer* value before matching,
|
|
// so " 2 " is equivalent to "2". Locking this in so the contract
|
|
// doesn't drift relative to the "internal whitespace throws" case.
|
|
expect(parseTrustProxyEnv(" 2 ")).toBe(2);
|
|
expect(appWithEnv(" 2 ").get("trust proxy")).toBe(2);
|
|
});
|
|
|
|
it("'loopback' yields a single-element array", () => {
|
|
const v = parseTrustProxyEnv("loopback");
|
|
expect(v).toEqual(["loopback"]);
|
|
const app = appWithEnv("loopback");
|
|
expect(app.get("trust proxy")).toEqual(["loopback"]);
|
|
});
|
|
|
|
it("'loopback,uniquelocal' yields a 2-element array", () => {
|
|
expect(parseTrustProxyEnv("loopback,uniquelocal")).toEqual([
|
|
"loopback",
|
|
"uniquelocal",
|
|
]);
|
|
});
|
|
|
|
it("IPv4 CIDR is accepted", () => {
|
|
expect(parseTrustProxyEnv("10.0.0.0/8")).toEqual(["10.0.0.0/8"]);
|
|
});
|
|
|
|
it("mixed IPv4 + IPv6 CIDR list is accepted with whitespace tolerance", () => {
|
|
expect(parseTrustProxyEnv(" 10.0.0.0/8 , fd00::/8 ")).toEqual([
|
|
"10.0.0.0/8",
|
|
"fd00::/8",
|
|
]);
|
|
});
|
|
|
|
it("'bogus' throws with a helpful message naming the bad token", () => {
|
|
expect(() => parseTrustProxyEnv("bogus")).toThrow(/bogus/);
|
|
expect(() => parseTrustProxyEnv("bogus")).toThrow(/loopback/);
|
|
});
|
|
|
|
it("partial-garbage list throws on the bad token, not silently dropped", () => {
|
|
expect(() => parseTrustProxyEnv("loopback,not-a-cidr")).toThrow(
|
|
/not-a-cidr/,
|
|
);
|
|
});
|
|
});
|