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>
This commit is contained in:
przeprogramowani
2026-09-18 17:17:28 +02:00
committed by GitHub
parent da67b5f7bb
commit 2f4ebd3b75
7 changed files with 557 additions and 1 deletions
+4
View File
@@ -7,6 +7,10 @@ prompts, and configs directly into your workspace.
- **Node 20+** — this is the only runtime dependency.
Corporate firewall / proxy allowlist (for security and sysadmin teams):
Polish [docs/wymagania-sieciowe.md](docs/wymagania-sieciowe.md),
English [docs/network-requirements.md](docs/network-requirements.md).
## Install
```bash
+107
View File
@@ -0,0 +1,107 @@
{
"version": 1,
"updated": "2026-09-18",
"description": "Hostnames that 10x-cli source may mention or contact. Keep in sync with docs/wymagania-sieciowe.md and docs/network-requirements.md. tests/network-allowlist.test.ts fails when src/ or those docs introduce an unlisted host.",
"hosts": [
{
"hostname": "10x-toolkit-api.przeprogramowani.workers.dev",
"port": 443,
"protocol": "https",
"calledBy": "cli_process",
"required": true,
"inDocs": true,
"when": ["login", "every_authenticated_command", "doctor"],
"purpose": "Delivery API: auth, membership, catalog, lessons, artifacts, health"
},
{
"hostname": "registry.npmjs.org",
"port": 443,
"protocol": "https",
"calledBy": "cli_process",
"required": true,
"inDocs": true,
"when": ["install", "update_check"],
"purpose": "Public npm registry for @przeprogramowani/10x-cli; doctor version lookup"
},
{
"hostname": "10xbench.ai",
"port": 443,
"protocol": "https",
"calledBy": "cli_process",
"required": false,
"inDocs": true,
"when": ["bench"],
"purpose": "Public leaderboard JSON for `10x bench`"
},
{
"hostname": "github.com",
"port": 443,
"protocol": "https",
"calledBy": "cli_process",
"required": false,
"inDocs": true,
"when": ["bench_kit", "optional_binary_install"],
"purpose": "git clone of 10x-bench-kit; optional GitHub Releases binary download"
},
{
"hostname": "objects.githubusercontent.com",
"port": 443,
"protocol": "https",
"calledBy": "browser",
"required": false,
"inDocs": true,
"when": ["optional_binary_install"],
"purpose": "Unconfirmed GitHub Releases asset CDN"
},
{
"hostname": "release-assets.githubusercontent.com",
"port": 443,
"protocol": "https",
"calledBy": "browser",
"required": false,
"inDocs": true,
"when": ["optional_binary_install"],
"purpose": "Unconfirmed GitHub Releases asset CDN"
},
{
"hostname": "toolkit.przeprogramowani.pl",
"port": 443,
"protocol": "https",
"calledBy": "browser",
"required": true,
"inDocs": true,
"when": ["login_email"],
"purpose": "Magic-link callback opened in the user's browser"
},
{
"hostname": "10xdevs.pl",
"port": 443,
"protocol": "https",
"calledBy": "browser",
"required": false,
"inDocs": true,
"when": ["enrollment_hint"],
"purpose": "Enrollment URL printed in error hints; CLI does not fetch it"
},
{
"hostname": "app.circle.so",
"port": 443,
"protocol": "https",
"calledBy": "browser",
"required": false,
"inDocs": true,
"when": ["login_circle_human"],
"purpose": "Circle web app so the user can open the login DM; CLI process does not connect"
},
{
"hostname": "git-scm.com",
"port": 443,
"protocol": "https",
"calledBy": "documentation_only",
"required": false,
"inDocs": false,
"when": ["bench_kit"],
"purpose": "Install-git hint in a bench-kit error message; CLI does not fetch it"
}
]
}
+146
View File
@@ -0,0 +1,146 @@
# 10x-cli network requirements
Polish: [wymagania-sieciowe.md](wymagania-sieciowe.md).
Machine-readable host list: [network-allowlist.json](network-allowlist.json).
10x-cli (`@przeprogramowani/10x-cli`) signs a 10xDevs course user in and writes lesson files to disk. It does not open a listening port. It does not upload project source code.
---
## Allowlist
Every row: HTTPS, TCP 443, from the device.
### Required for course use
| Host | Port | Protocol | Purpose | Without it |
|------|------|----------|---------|------------|
| `10x-toolkit-api.przeprogramowani.workers.dev` | 443 | HTTPS | Login, token refresh, lesson list and content, `10x doctor` | `10x auth`, `list`, `get`, `sync` fail; `doctor` reports the API unreachable |
| `registry.npmjs.org` | 443 | HTTPS | Install and update `@przeprogramowani/10x-cli` (`npx` / `npm install -g`) | npm install and upgrade fail. `10x doctor` still runs — it skips the version check |
| `toolkit.przeprogramowani.pl` | 443 | HTTPS | Login email link (`/auth/callback`) opened in the browser | Email login (`10x auth`) does not finish after the user clicks the link |
`10x-cli` calls the API only at `10x-toolkit-api.przeprogramowani.workers.dev`. `toolkit.przeprogramowani.pl` is browser-only, for email login.
### Browser — Circle login
| Host | Port | Protocol | Purpose | Without it |
|------|------|----------|---------|------------|
| `10x-toolkit-api.przeprogramowani.workers.dev` | 443 | HTTPS | Approval page from the Circle message | `10x auth --method circle` waits until the link expires |
| `app.circle.so` | 443 | HTTPS | Reading the Circle message that contains the link | The user cannot open the message (the `10x` process does not connect here) |
### Optional
| Host | Port | Protocol | Purpose | Without it |
|------|------|----------|---------|------------|
| `10xbench.ai` | 443 | HTTPS | Model leaderboard (`10x bench`) | `10x bench` fails; lesson download is unchanged |
| `github.com` | 443 | HTTPS | `git clone` of the `10x-bench-kit` template; optional GitHub Releases binary | `10x bench-kit` cannot fetch the template. Course `get` / `sync` unchanged |
| `objects.githubusercontent.com` | 443 | HTTPS | GitHub Releases asset CDN (unconfirmed) | Downloading the standalone binary from Releases may fail |
| `release-assets.githubusercontent.com` | 443 | HTTPS | GitHub Releases asset CDN (unconfirmed) | same |
| `10xdevs.pl` | 443 | HTTPS | Course enrolment site (error-message hint only) | No effect on the CLI |
`10x bench-kit` may also run `git ls-remote` against the `origin` of the repo where the command was invoked — that host is whatever the users git config already uses.
---
## Protocol and TLS
- HTTPS / TLS, TCP 443. Login: `POST`/`GET` and repeated HTTP (every 2 s, up to 5 min). No WebSocket or SSE.
- API and 10xbench timeout: 30 s. `doctor` → API: 5 s. `doctor` → npm: 2 s.
- User-Agent: `10x-cli`.
- The CLI does not listen on any port.
Corporate TLS inspection (custom CA):
| Setting | Fact |
|---------|------|
| `NODE_EXTRA_CA_CERTS` | Node.js appends a PEM CA file. Applies when `10x` runs on Node (npm install). |
| OS certificate store | The CLI does not enable `--use-system-ca`. |
| `HTTPS_PROXY` / `HTTP_PROXY` / `NO_PROXY` | `npm` / `npx` usually honour these. Node 20 `fetch` (CLI minimum) does not unless `NODE_USE_ENV_PROXY=1` (Node 24+). On Node 2022, proxy for `10x auth` / `10x get` is unconfirmed. |
| Standalone binary | Different runtime from Node. `NODE_EXTRA_CA_CERTS` / `HTTPS_PROXY`: unconfirmed. |
If inspection breaks TLS: point `NODE_EXTRA_CA_CERTS` at the corporate root, or disable inspection for the hosts in the tables.
---
## On the device
| Component | Required |
|-----------|----------|
| Node.js ≥ 20 | Yes for npm/npx install |
| npm / npx | Yes for npm install. No for the standalone binary |
| git | `10x bench-kit` only |
| Docker / Podman | Not for the CLI |
| OS | macOS, Linux, Windows |
Session tokens (email, access/refresh JWT, expiry) — not a Circle password:
| OS | Directory | Files | Permissions (POSIX) |
|----|-----------|-------|---------------------|
| macOS / Linux | `$XDG_CONFIG_HOME/10x-cli` or `~/.config/10x-cli` | `auth.json`, `config.json` | directory `0700`, `auth.json` `0600` |
| Windows | `%APPDATA%\10x-cli` | same files | — |
Lesson files are written into the users project directory (for example `.claude/skills/`, `CLAUDE.md`).
---
## Data leaving the device
| Data | When |
|------|------|
| Email address | `10x auth`; stored in `auth.json` |
| JWT access + refresh | Login and every API call (`Authorization: Bearer`) |
| Hostname and a short OS string | Start of `10x auth --method circle` only |
| User-Agent `10x-cli` | Every CLI request |
| Package-version GET (no token) | `10x doctor``registry.npmjs.org` |
| Public leaderboard JSON (no token) | `10x bench` |
Project source code is not uploaded.
---
## AI coding tools (separate allowlist)
10x-cli does not call Anthropic, OpenAI, Google, or OpenRouter. It writes files for the chosen `--tool`. Model traffic is that tools traffic.
| `--tool` | Vendor network requirements |
|----------|------------------------------|
| `claude-code` | [Claude Code — enterprise network](https://docs.claude.com/en/docs/claude-code/network-config) |
| `cursor` | [Cursor — network configuration](https://cursor.com/docs/enterprise/network-configuration) |
| `copilot` | [GitHub Copilot allowlist](https://docs.github.com/copilot/reference/copilot-allowlist-reference) |
| `codex` | [Codex — network](https://developers.openai.com/codex/agent-approvals-security/) — full local-CLI host list: confirm with OpenAI |
| `gemini` | [Gemini Code Assist network access](https://developers.google.com/gemini-code-assist/docs/network-access) — confirm for Gemini CLI |
| `kiro` | [Kiro — firewalls and proxies](https://kiro.dev/docs/web/firewalls/) |
| `devin-desktop` (`windsurf` alias) | [Devin Desktop — domains](https://docs.devin.ai/desktop/troubleshooting/windsurf-common-issues) |
| `generic` | No vendor |
---
## How to verify
```bash
curl -I --max-time 10 https://10x-toolkit-api.przeprogramowani.workers.dev/health
curl -I --max-time 10 https://registry.npmjs.org/@przeprogramowani/10x-cli/latest
curl -I --max-time 10 https://toolkit.przeprogramowani.pl/auth/callback
curl -I --max-time 10 https://10xbench.ai/api/leaderboard.json
curl -I --max-time 10 https://github.com/przeprogramowani/10x-bench-kit
```
TLS handshake matters (HTTP 200 is not required). On the device: `10x doctor`.
| Blocked host | CLI message |
|--------------|-------------|
| API | `Could not reach the 10x-toolkit API.` |
| `doctor`, 5 s timeout | `{url} did not respond within 5s.` |
| `doctor`, other network error | `{url} is unreachable.` |
| npm (does not fail `doctor`) | `10x-cli {version} (update check skipped).` |
| `10x bench` | `Could not reach 10xbench.ai.` |
| `10x bench-kit` | `Could not download the template from https://github.com/przeprogramowani/10x-bench-kit.` |
| bad certificate / intercept | `network_error` plus the runtime message |
Exit codes: `0` success, `1` error, `2` usage, `3` auth required, `4` forbidden, `5` not found. `doctor` with a dead API: **78**.
---
## Keeping this list current
Canonical hosts: [network-allowlist.json](network-allowlist.json). `tests/network-allowlist.test.ts` fails if `src/` or this document / the Polish version introduce a host outside the list. A new host = the same PR: JSON + EN + PL.
+1
View File
@@ -3,6 +3,7 @@
## Requirements
- **Node 20+** — the only runtime dependency on all platforms.
- Corporate network allowlist: Polish [wymagania-sieciowe.md](../wymagania-sieciowe.md), English [network-requirements.md](../network-requirements.md).
## Supported platforms
+146
View File
@@ -0,0 +1,146 @@
# Wymagania sieciowe 10x-cli
English: [network-requirements.md](network-requirements.md).
Machine-readable host list: [network-allowlist.json](network-allowlist.json).
10x-cli (`@przeprogramowani/10x-cli`) loguje użytkownika kursu 10xDevs i zapisuje materiały lekcji na dysk. Nie otwiera portu nasłuchującego. Nie wysyła kodu źródłowego projektu.
---
## Allowlista
Wszystkie wpisy: HTTPS, TCP 443, z urządzenia.
### Wymagane do kursu
| Host | Port | Protokół | Cel | Bez tego |
|------|------|----------|-----|----------|
| `10x-toolkit-api.przeprogramowani.workers.dev` | 443 | HTTPS | Logowanie, odświeżanie tokenu, lista i treść lekcji, `10x doctor` | Nie działa `10x auth`, `list`, `get`, `sync`; `doctor` zgłasza, że API jest nieosiągalne |
| `registry.npmjs.org` | 443 | HTTPS | Instalacja i aktualizacja `@przeprogramowani/10x-cli` (`npx` / `npm install -g`) | Nie da się zainstalować ani zaktualizować pakietu npm. `10x doctor` nadal działa — pomija sprawdzenie wersji |
| `toolkit.przeprogramowani.pl` | 443 | HTTPS | Link z e-maila logowania (`/auth/callback`) otwierany w przeglądarce | Logowanie mailem (`10x auth`) nie dokończy się po kliknięciu linku |
`10x-cli` woła API wyłącznie na `10x-toolkit-api.przeprogramowani.workers.dev`. `toolkit.przeprogramowani.pl` jest tylko dla przeglądarki przy logowaniu mailem.
### Przeglądarka — logowanie Circle
| Host | Port | Protokół | Cel | Bez tego |
|------|------|----------|-----|----------|
| `10x-toolkit-api.przeprogramowani.workers.dev` | 443 | HTTPS | Strona zatwierdzenia z wiadomości Circle | `10x auth --method circle` czeka, aż link wygaśnie |
| `app.circle.so` | 443 | HTTPS | Odczyt wiadomości Circle z linkiem | Użytkownik nie otworzy wiadomości (proces `10x` nie łączy się z tym hostem) |
### Opcjonalne
| Host | Port | Protokół | Cel | Bez tego |
|------|------|----------|-----|----------|
| `10xbench.ai` | 443 | HTTPS | Ranking modeli (`10x bench`) | `10x bench` nie działa; pobieranie lekcji bez zmian |
| `github.com` | 443 | HTTPS | `git clone` szablonu `10x-bench-kit`; opcjonalny binary z GitHub Releases | `10x bench-kit` nie pobierze szablonu. Kurs (`get` / `sync`) bez zmian |
| `objects.githubusercontent.com` | 443 | HTTPS | CDN assetów GitHub Releases (do potwierdzenia) | Pobranie standalone binary z Releases może się nie udać |
| `release-assets.githubusercontent.com` | 443 | HTTPS | CDN assetów GitHub Releases (do potwierdzenia) | j.w. |
| `10xdevs.pl` | 443 | HTTPS | Strona zapisu na kurs (tylko w komunikacie błędu) | Brak wpływu na CLI |
`10x bench-kit` może też wywołać `git ls-remote` wobec `origin` repozytorium, w którym uruchomiono komendę — to dowolny host z konfiguracji git użytkownika.
---
## Protokół i TLS
- HTTPS / TLS, TCP 443. Logowanie: `POST`/`GET` i powtarzany HTTP (co 2 s, do 5 min). Brak WebSocket i SSE.
- Timeout API i 10xbench: 30 s. `doctor` → API: 5 s. `doctor` → npm: 2 s.
- User-Agent: `10x-cli`.
- CLI nie nasłuchuje na żadnym porcie.
Przy korporacyjnym TLS inspection (własny CA):
| Ustawienie | Fakt |
|------------|------|
| `NODE_EXTRA_CA_CERTS` | Node.js doda plik PEM z CA. Działa, gdy `10x` uruchamia Node (instalacja npm). |
| Magazyn CA systemu | CLI nie włącza `--use-system-ca`. |
| `HTTPS_PROXY` / `HTTP_PROXY` / `NO_PROXY` | `npm` / `npx` zwykle je honorują. `fetch` w Node 20 (minimum CLI) ich nie używa bez `NODE_USE_ENV_PROXY=1` (Node 24+). Na Node 2022 proxy dla `10x auth` / `10x get`: do potwierdzenia. |
| Standalone binary | Inny runtime niż Node. `NODE_EXTRA_CA_CERTS` / `HTTPS_PROXY`: do potwierdzenia. |
Jeśli inspection psuje TLS: wskaż korporacyjny root w `NODE_EXTRA_CA_CERTS` albo wyłącz inspection dla hostów z tabeli.
---
## Na urządzeniu
| Składnik | Wymagane |
|----------|----------|
| Node.js ≥ 20 | Tak przy instalacji npm/npx |
| npm / npx | Tak przy instalacji z npm. Nie przy standalone binary |
| git | Tylko `10x bench-kit` |
| Docker / Podman | Nie dla CLI |
| System | macOS, Linux, Windows |
Tokeny sesji (e-mail, access/refresh JWT, data ważności) — nie hasło do Circle:
| System | Katalog | Pliki | Uprawnienia (POSIX) |
|--------|---------|-------|---------------------|
| macOS / Linux | `$XDG_CONFIG_HOME/10x-cli` lub `~/.config/10x-cli` | `auth.json`, `config.json` | katalog `0700`, `auth.json` `0600` |
| Windows | `%APPDATA%\10x-cli` | te same | — |
Materiały lekcji zapisują się w katalogu projektu użytkownika (np. `.claude/skills/`, `CLAUDE.md`).
---
## Dane wychodzące z urządzenia
| Dane | Kiedy |
|------|-------|
| Adres e-mail | `10x auth`; zapis w `auth.json` |
| JWT access + refresh | Logowanie i każde wywołanie API (`Authorization: Bearer`) |
| Hostname i krótki opis OS | Tylko start `10x auth --method circle` |
| User-Agent `10x-cli` | Każde żądanie CLI |
| GET wersji pakietu (bez tokenu) | `10x doctor``registry.npmjs.org` |
| Publiczny JSON rankingu (bez tokenu) | `10x bench` |
Kod źródłowy projektu nie jest wysyłany.
---
## Narzędzia AI (osobna allowlista)
10x-cli nie woła Anthropic, OpenAI, Google ani OpenRouter. Zapisuje pliki pod wybrane `--tool`. Ruch modelu to ruch tego narzędzia.
| `--tool` | Wymagania sieciowe dostawcy |
|----------|------------------------------|
| `claude-code` | [Claude Code — enterprise network](https://docs.claude.com/en/docs/claude-code/network-config) |
| `cursor` | [Cursor — network configuration](https://cursor.com/docs/enterprise/network-configuration) |
| `copilot` | [GitHub Copilot allowlist](https://docs.github.com/copilot/reference/copilot-allowlist-reference) |
| `codex` | [Codex — network](https://developers.openai.com/codex/agent-approvals-security/) — pełna lista hostów lokalnego CLI: do potwierdzenia u OpenAI |
| `gemini` | [Gemini Code Assist network access](https://developers.google.com/gemini-code-assist/docs/network-access) — do potwierdzenia dla Gemini CLI |
| `kiro` | [Kiro — firewalls and proxies](https://kiro.dev/docs/web/firewalls/) |
| `devin-desktop` (alias `windsurf`) | [Devin Desktop — domains](https://docs.devin.ai/desktop/troubleshooting/windsurf-common-issues) |
| `generic` | Brak dostawcy |
---
## Weryfikacja
```bash
curl -I --max-time 10 https://10x-toolkit-api.przeprogramowani.workers.dev/health
curl -I --max-time 10 https://registry.npmjs.org/@przeprogramowani/10x-cli/latest
curl -I --max-time 10 https://toolkit.przeprogramowani.pl/auth/callback
curl -I --max-time 10 https://10xbench.ai/api/leaderboard.json
curl -I --max-time 10 https://github.com/przeprogramowani/10x-bench-kit
```
Ważne jest zestawienie TLS (niekoniecznie HTTP 200). Na urządzeniu: `10x doctor`.
| Blokada | Komunikat CLI |
|---------|----------------|
| API | `Could not reach the 10x-toolkit API.` |
| `doctor`, timeout 5 s | `{url} did not respond within 5s.` |
| `doctor`, inny błąd sieci | `{url} is unreachable.` |
| npm (nie blokuje `doctor`) | `10x-cli {wersja} (update check skipped).` |
| `10x bench` | `Could not reach 10xbench.ai.` |
| `10x bench-kit` | `Could not download the template from https://github.com/przeprogramowani/10x-bench-kit.` |
| zły certyfikat / intercept | `network_error` i komunikat runtime |
Kody wyjścia: `0` sukces, `1` błąd, `2` użycie, `3` brak sesji, `4` brak uprawnień, `5` nie znaleziono. `doctor` przy martwym API: **78**.
---
## Aktualizacja tej listy
Kanoniczne hosty: [network-allowlist.json](network-allowlist.json). Test `tests/network-allowlist.test.ts` failuje, gdy `src/` albo ten dokument / wersja angielska dodadzą hosta spoza listy. Nowy host = ten sam PR: JSON + PL + EN.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@przeprogramowani/10x-cli",
"version": "1.23.0",
"version": "1.23.1",
"description": "Open-source CLI for 10xDevs course content",
"repository": {
"type": "git",
+152
View File
@@ -0,0 +1,152 @@
/**
* 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([]);
});
});