add unit tests for ssrf guard, version compare, and settings validation

This commit is contained in:
Nystik
2026-06-06 18:29:48 +02:00
parent 7688de599a
commit 04be97e48c
5 changed files with 142 additions and 0 deletions

View File

@@ -183,3 +183,4 @@ router.post("/", async (req, res) => {
});
module.exports = router;
module.exports.isPrivateIp = isPrivateIp;

View File

@@ -0,0 +1,62 @@
import { describe, it, expect } from "vitest";
import { createRequire } from "module";
const require = createRequire(import.meta.url);
const { isPrivateIp } = require("./proxy.js");
describe("isPrivateIp", () => {
it("flags private and link-local IPv4", () => {
for (const ip of [
"0.0.0.0",
"10.0.0.1",
"127.0.0.1",
"169.254.1.1",
"172.16.0.1",
"172.31.255.255",
"192.168.1.1",
"100.64.0.1",
"100.127.255.255",
]) {
expect(isPrivateIp(ip), ip).toBe(true);
}
});
it("allows public IPv4, including range boundaries", () => {
for (const ip of [
"8.8.8.8",
"1.1.1.1",
"172.15.255.255",
"172.32.0.0",
"100.63.255.255",
"100.128.0.0",
"169.253.0.0",
"169.255.0.0",
"11.0.0.1",
"192.169.0.1",
]) {
expect(isPrivateIp(ip), ip).toBe(false);
}
});
it("flags private and link-local IPv6", () => {
for (const ip of ["::1", "::", "fc00::1", "fd12::1", "fe80::1", "feaf::1"]) {
expect(isPrivateIp(ip), ip).toBe(true);
}
});
it("allows public IPv6", () => {
for (const ip of ["2606:4700:4700::1111", "2001:4860:4860::8888"]) {
expect(isPrivateIp(ip), ip).toBe(false);
}
});
it("classifies IPv4-mapped IPv6 by the embedded address", () => {
expect(isPrivateIp("::ffff:127.0.0.1")).toBe(true);
expect(isPrivateIp("::ffff:8.8.8.8")).toBe(false);
});
it("returns false for non-IP input", () => {
expect(isPrivateIp("not-an-ip")).toBe(false);
expect(isPrivateIp("")).toBe(false);
});
});

View File

@@ -94,3 +94,4 @@ router.post("/", (req, res) => {
});
module.exports = router;
module.exports.validate = validate;

View File

@@ -0,0 +1,47 @@
import { describe, it, expect } from "vitest";
import { createRequire } from "module";
const require = createRequire(import.meta.url);
const { validate } = require("./settings.js");
const settings = require("../settings.js");
describe("settings validate", () => {
it("rejects an unknown proxy mode", () => {
expect(() => validate({ proxyMode: "bogus" })).toThrow();
});
it("rejects negative or non-integer numbers", () => {
expect(() => validate({ contentCacheBytes: -1 })).toThrow();
expect(() => validate({ contentCacheBytes: 1.5 })).toThrow();
expect(() => validate({ contentCacheBytes: "5" })).toThrow();
});
it("enforces maxBodyBytes bounds", () => {
expect(() => validate({ maxBodyBytes: 0 })).toThrow();
expect(() =>
validate({ maxBodyBytes: settings.MAX_BODY_BACKSTOP + 1 }),
).toThrow();
expect(validate({ maxBodyBytes: 1048576 })).toEqual({
maxBodyBytes: 1048576,
});
});
it("trims a valid proxy allowlist", () => {
expect(
validate({ proxyAllowlist: [" api.example.com ", "github.com"] }),
).toEqual({ proxyAllowlist: ["api.example.com", "github.com"] });
});
it("rejects a non-array allowlist or an empty entry", () => {
expect(() => validate({ proxyAllowlist: "x" })).toThrow();
expect(() => validate({ proxyAllowlist: ["ok", " "] })).toThrow();
});
it("ignores wsOrigins, which is env-only", () => {
expect(validate({ wsOrigins: ["https://evil.example.com"] })).toEqual({});
});
it("ignores unknown keys", () => {
expect(validate({ bogusKey: 1 })).toEqual({});
});
});

View File

@@ -0,0 +1,31 @@
import { describe, it, expect } from "vitest";
import { createRequire } from "module";
const require = createRequire(import.meta.url);
const { stripBuildMetadata, isNewer } = require("./version.js");
describe("isNewer", () => {
it("is true when latest is strictly newer", () => {
expect(isNewer("0.8.4", "0.8.3")).toBe(true);
expect(isNewer("1.0.0", "0.9.9")).toBe(true);
expect(isNewer("0.9.0", "0.8.9")).toBe(true);
});
it("is false for older or equal, so no downgrade is prompted", () => {
expect(isNewer("0.8.3", "0.8.4")).toBe(false);
expect(isNewer("0.8.4", "0.8.4")).toBe(false);
expect(isNewer("0.9.9", "1.0.0")).toBe(false);
});
it("is false for malformed versions", () => {
expect(isNewer("x", "0.8.4")).toBe(false);
expect(isNewer("0.8", "0.8.4")).toBe(false);
expect(isNewer("1.x.0", "0.8.4")).toBe(false);
});
it("ignores build metadata, so an equal version with a build tag is not newer", () => {
expect(
isNewer(stripBuildMetadata("0.8.4"), stripBuildMetadata("0.8.4+q2fmfox")),
).toBe(false);
});
});