This commit is contained in:
2026-09-20 07:27:29 +01:00
parent dc2cd66b25
commit 8ec44ee0a7
70 changed files with 2657 additions and 128 deletions
+2
View File
@@ -62,6 +62,8 @@ export function setCookies(response: Response, cookies: CookieOptions[]): void {
if (maxAge !== undefined) header += `; Max-Age=${maxAge}`;
if (domain) header += `; Domain=${domain}`;
console.log("header", header);
response.headers.append("Set-Cookie", header);
}
}
+33
View File
@@ -0,0 +1,33 @@
import { describe, expect, test } from "bun:test";
import parsePortList from "./parse-port-list";
describe("parsePortList", () => {
test("treats empty as any port", () => {
expect(parsePortList({ ports: "" })).toEqual({
success: true,
ports: [],
});
expect(parsePortList({})).toEqual({
success: true,
ports: [],
});
});
test("parses singles, lists, and ranges", () => {
expect(parsePortList({ ports: "443" }).ports).toEqual(["443"]);
expect(parsePortList({ ports: "80, 443" }).ports).toEqual([
"80",
"443",
]);
expect(parsePortList({ ports: "8000:8080" }).ports).toEqual([
"8000:8080",
]);
});
test("rejects invalid ports", () => {
expect(parsePortList({ ports: "0" }).success).toBe(false);
expect(parsePortList({ ports: "65536" }).success).toBe(false);
expect(parsePortList({ ports: "8080:8000" }).success).toBe(false);
expect(parsePortList({ ports: "80-443" }).success).toBe(false);
});
});
+96
View File
@@ -0,0 +1,96 @@
type Params = {
ports?: string;
};
export type ParsePortListResult = {
success: boolean;
msg?: string;
ports: string[];
};
function isValidPort({ value }: { value: string }) {
if (!/^\d{1,5}$/.test(value)) {
return false;
}
const port = Number(value);
return port >= 1 && port <= 65535 && String(port) == value;
}
export default function parsePortList({
ports,
}: Params): ParsePortListResult {
if (!ports || !ports.trim()) {
return {
success: true,
ports: [],
};
}
const tokens = ports
.split(",")
.map((token) => token.trim())
.filter(Boolean);
if (!tokens[0]) {
return {
success: true,
ports: [],
};
}
const parsed: string[] = [];
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i];
if (!token) {
continue;
}
if (token.includes(":")) {
const [start, end, extra] = token.split(":");
if (
extra ||
!start ||
!end ||
!isValidPort({ value: start }) ||
!isValidPort({ value: end })
) {
return {
success: false,
msg: `Invalid port range "${token}"`,
ports: [],
};
}
if (Number(start) > Number(end)) {
return {
success: false,
msg: `Invalid port range "${token}"`,
ports: [],
};
}
parsed.push(`${start}:${end}`);
continue;
}
if (!isValidPort({ value: token })) {
return {
success: false,
msg: `Invalid port "${token}"`,
ports: [],
};
}
parsed.push(token);
}
return {
success: true,
ports: parsed,
};
}
+21
View File
@@ -0,0 +1,21 @@
import { describe, expect, test } from "bun:test";
import validateIpv4Cidr from "./validate-ipv4-cidr";
describe("validateIpv4Cidr", () => {
test("accepts a bare IPv4 address", () => {
expect(validateIpv4Cidr({ value: "10.0.0.2" })).toBe(true);
});
test("accepts CIDR notation", () => {
expect(validateIpv4Cidr({ value: "10.0.0.0/24" })).toBe(true);
expect(validateIpv4Cidr({ value: "0.0.0.0/0" })).toBe(true);
expect(validateIpv4Cidr({ value: "192.168.1.1/32" })).toBe(true);
});
test("rejects invalid values", () => {
expect(validateIpv4Cidr({ value: "10.0.0.0/33" })).toBe(false);
expect(validateIpv4Cidr({ value: "10.0.0.0/024" })).toBe(false);
expect(validateIpv4Cidr({ value: "10.0.0.0/24/24" })).toBe(false);
expect(validateIpv4Cidr({ value: "example.com" })).toBe(false);
});
});
+33
View File
@@ -0,0 +1,33 @@
import validateIpv4 from "./validate-ipv4";
type Params = {
value?: string;
};
export default function validateIpv4Cidr({ value }: Params) {
if (!value) {
return false;
}
const [ip, prefix, extra] = value.split("/");
if (extra) {
return false;
}
if (!validateIpv4({ ip })) {
return false;
}
if (prefix == undefined) {
return true;
}
if (!/^\d{1,2}$/.test(prefix)) {
return false;
}
const prefix_value = Number(prefix);
return prefix_value >= 0 && prefix_value <= 32 && String(prefix_value) == prefix;
}
+19
View File
@@ -0,0 +1,19 @@
import { describe, expect, test } from "bun:test";
import validateIpv4 from "./validate-ipv4";
describe("validateIpv4", () => {
test("accepts valid addresses", () => {
expect(validateIpv4({ ip: "10.0.0.2" })).toBe(true);
expect(validateIpv4({ ip: "0.0.0.0" })).toBe(true);
expect(validateIpv4({ ip: "255.255.255.255" })).toBe(true);
});
test("rejects invalid addresses", () => {
expect(validateIpv4({ ip: "" })).toBe(false);
expect(validateIpv4({})).toBe(false);
expect(validateIpv4({ ip: "10.0.0" })).toBe(false);
expect(validateIpv4({ ip: "10.0.0.256" })).toBe(false);
expect(validateIpv4({ ip: "10.0.0.01" })).toBe(false);
expect(validateIpv4({ ip: "10.0.0.2/32" })).toBe(false);
});
});
+35
View File
@@ -0,0 +1,35 @@
type Params = {
ip?: string;
};
export default function validateIpv4({ ip }: Params) {
if (!ip) {
return false;
}
const parts = ip.split(".");
if (parts.length != 4) {
return false;
}
for (let i = 0; i < parts.length; i++) {
const part = parts[i];
if (!part || !/^\d{1,3}$/.test(part)) {
return false;
}
const value = Number(part);
if (value < 0 || value > 255) {
return false;
}
if (String(value) != part) {
return false;
}
}
return true;
}