Files
przeprogramowani__10x-cli/tests/network-allowlist.test.ts
T
przeprogramowani 2f4ebd3b75 docs: wymagania sieciowe dla urządzeń służbowych (allowlist PL+EN) (#53)
* docs: add corporate network allowlist for security/sysadmin teams

Polish sysadmin spec plus a machine-readable host list and a source-scan
test so new CLI destinations cannot land undocumented.

* chore(release): prepare v1.23.1

* docs: rewrite corporate allowlist in sysadmin language (PL+EN)

Plain host/port/protocol tables, matching English document, and an
allowlist test that covers both language files.

* docs: drop localhost from the corporate network allowlist

Public DNS names only; the source scan skips IP literals and
single-label hosts so local-dev URLs stay out of the sysadmin spec.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-09-18 17:17:28 +02:00

153 lines
4.7 KiB
TypeScript

/**
* Fails when src/ or the sysadmin docs mention a hostname that is not listed
* in docs/network-allowlist.json. Hosts with inDocs: true must appear in both
* the Polish and English documents.
*/
import { describe, expect, it } from "bun:test";
import { readdirSync, readFileSync, statSync } from "node:fs";
import { join, relative, resolve } from "node:path";
const ROOT = resolve(import.meta.dir, "..");
const SRC = join(ROOT, "src");
const ALLOWLIST_PATH = join(ROOT, "docs/network-allowlist.json");
const DOC_PATHS = [
"docs/wymagania-sieciowe.md",
"docs/network-requirements.md",
] as const;
const HOST_RE = /https?:\/\/([a-zA-Z0-9.-]+)/g;
/** Vendor documentation links in the AI-tool table — not 10x-cli destinations. */
const VENDOR_DOC_HOSTS = new Set([
"docs.claude.com",
"cursor.com",
"docs.github.com",
"developers.openai.com",
"developers.google.com",
"kiro.dev",
"docs.devin.ai",
]);
interface AllowlistHost {
hostname: string;
inDocs?: boolean;
}
interface AllowlistFile {
version: number;
hosts: AllowlistHost[];
}
function walkTsFiles(dir: string): string[] {
const out: string[] = [];
for (const name of readdirSync(dir)) {
const path = join(dir, name);
const st = statSync(path);
if (st.isDirectory()) out.push(...walkTsFiles(path));
else if (name.endsWith(".ts")) out.push(path);
}
return out;
}
/** Drop block comments, then line comments that are not inside http(s)://. */
function stripComments(source: string): string {
const withoutBlocks = source.replace(/\/\*[\s\S]*?\*\//g, " ");
return withoutBlocks
.split("\n")
.map((line) => {
let i = 0;
while (i < line.length) {
if (line.startsWith("https://", i)) {
i += "https://".length;
continue;
}
if (line.startsWith("http://", i)) {
i += "http://".length;
continue;
}
if (line[i] === "/" && line[i + 1] === "/") return line.slice(0, i);
i += 1;
}
return line;
})
.join("\n");
}
function hostsInText(text: string): Set<string> {
const found = new Set<string>();
HOST_RE.lastIndex = 0;
let match: RegExpExecArray | null;
while ((match = HOST_RE.exec(text)) !== null) {
const host = match[1]?.replace(/\.+$/, "");
if (host) found.add(host);
}
return found;
}
/** Allowlist only public DNS names (a dot, not an IP literal). */
function isPublicHostname(host: string): boolean {
if (!host.includes(".")) return false;
if (/^\d{1,3}(?:\.\d{1,3}){3}$/.test(host)) return false;
return true;
}
function hostsInSource(): Map<string, string[]> {
const found = new Map<string, string[]>();
for (const file of walkTsFiles(SRC)) {
const text = stripComments(readFileSync(file, "utf8"));
for (const host of hostsInText(text)) {
if (!isPublicHostname(host)) continue;
const rel = relative(ROOT, file);
const list = found.get(host) ?? [];
if (!list.includes(rel)) list.push(rel);
found.set(host, list);
}
}
return found;
}
describe("docs/network-allowlist.json", () => {
const raw = JSON.parse(readFileSync(ALLOWLIST_PATH, "utf8")) as AllowlistFile;
const listed = new Set(raw.hosts.map((h) => h.hostname));
const inDocs = raw.hosts.filter((h) => h.inDocs).map((h) => h.hostname);
const docTexts = Object.fromEntries(
DOC_PATHS.map((path) => [path, readFileSync(join(ROOT, path), "utf8")]),
);
it("is a versioned host list", () => {
expect(raw.version).toBe(1);
expect(raw.hosts.length).toBeGreaterThan(0);
expect(listed.has("10x-toolkit-api.przeprogramowani.workers.dev")).toBe(true);
expect(listed.has("registry.npmjs.org")).toBe(true);
});
it("lists every public hostname referenced from src/", () => {
const missing: string[] = [];
for (const [host, files] of hostsInSource()) {
if (!listed.has(host)) missing.push(`${host} (${files.join(", ")})`);
}
expect(missing).toEqual([]);
});
it("lists every non-vendor hostname referenced from both sysadmin docs", () => {
const missing: string[] = [];
for (const path of DOC_PATHS) {
const text = docTexts[path] ?? "";
for (const host of hostsInText(text)) {
if (!isPublicHostname(host) || VENDOR_DOC_HOSTS.has(host)) continue;
if (!listed.has(host)) missing.push(`${host} (${path})`);
}
}
expect(missing).toEqual([]);
});
it("requires inDocs hosts in both the Polish and English documents", () => {
const missing: string[] = [];
for (const path of DOC_PATHS) {
const text = docTexts[path] ?? "";
for (const host of inDocs) {
if (!text.includes(host)) missing.push(`${host} missing from ${path}`);
}
}
expect(missing).toEqual([]);
});
});