// ── §2.7's last rule, given the CI it does not have ─────────────────────── // // `book/02-website-module.md` is explicit that "the website process never opens a // connection to a game server" is the **one boundary rule with no CI behind it**: // an outbound socket is not statically detectable the way an internal `require` // is, so in general the rule is held up by review and by understanding it. // // True of the general case, and not a reason to check nothing. A module can state // a narrower, completely decidable property about **itself**, and this one says: // the shipped server half references no networking primitive at all. Everything // it knows arrives from its own tables, which its sidecar writes. // // Adopted from the kit's acceptance run (`docs/modules/kit-acceptance.md`), where // a reader building a Rust module wrote it unprompted after reading that the rule // had no CI — and observed that for Rust in particular, which ships RCON over // WebSocket, `new WebSocket(rconUrl)` in `boot.js` is about ten lines away. // // ── WHEN YOU ADD A SIDECAR CLIENT, NARROW THIS. DO NOT DELETE IT. ───────── // // Talking to *your sidecar* over HTTP is the expected shape and is not what §2.7 // forbids — the rule is about the **game server**. So the moment your module // grows, say, `server/sidecarClient.js`, this test starts failing correctly and // the fix is to allow that one file: // // const MAY_OPEN_SOCKETS = new Set(['sidecarClient.js']) // // and keep the rest of the tree under the ban. What you get for that is a test // that names the *one* file allowed to reach the network — which is exactly the // file a reviewer should be reading closely, and exactly the place a game-server // URL would appear if the rule were ever broken. // // Scope: SHIPPED code only. `test/` and `scripts/` never run inside core's process. const test = require('node:test') const assert = require('node:assert') const fs = require('node:fs') const path = require('node:path') const SERVER_ROOT = path.resolve(__dirname, '..') const NOT_SHIPPED = new Set(['test', 'scripts', 'node_modules', 'swagger']) /** Every shipped `.js` file under `server/`. */ function shippedFiles(dir = SERVER_ROOT, out = []) { for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { if (entry.isDirectory()) { if (dir === SERVER_ROOT && NOT_SHIPPED.has(entry.name)) continue if (entry.name === 'node_modules') continue shippedFiles(path.join(dir, entry.name), out) } else if (entry.isFile() && entry.name.endsWith('.js')) { out.push(path.join(dir, entry.name)) } } return out } /** * Blank comments, so prose ABOUT the rule does not trip the rule. * * This file is itself the proof that it is needed: the paragraphs above say * "WebSocket" several times. `scripts/checkImports.js` documents hitting exactly * this on its own documentation, and it is the third time in this project's * history that a boundary check has failed on the text explaining it. * * Blanked rather than deleted, so line numbers in a failure still point at the * right line. */ function stripComments(src) { return src .replace(/\/\*[\s\S]*?\*\//g, (m) => m.replace(/[^\n]/g, ' ')) .replace(/^[ \t]*\/\/.*$/gm, '') } // Each is a way a Node process opens a socket. Matched as identifiers, so a // column named `websocket_url` inside a SQL string would not fire. const NETWORKING = [ /\brequire\(\s*['"](?:node:)?(?:net|tls|dgram|http|https|http2)['"]\s*\)/, /\bfrom\s+['"](?:node:)?(?:net|tls|dgram|http|https|http2)['"]/, /\brequire\(\s*['"](?:ws|socket\.io-client|undici|axios|node-fetch|got)['"]\s*\)/, /\bnew\s+WebSocket\b/, /\bfetch\s*\(/, /\bXMLHttpRequest\b/, /\bEventSource\b/, ] test('no shipped file references a networking primitive (§2.7)', () => { const offenders = [] for (const file of shippedFiles()) { const code = stripComments(fs.readFileSync(file, 'utf8')) for (const pattern of NETWORKING) { if (pattern.test(code)) { offenders.push(`${path.relative(SERVER_ROOT, file)} matches ${pattern}`) } } } assert.deepStrictEqual( offenders, [], 'the website process must never open a connection to a game server. If this is ' + 'your sidecar client, allow that one file rather than removing the check — see ' + `the header of this file.\n ${offenders.join('\n ')}`, ) }) test('the check can actually fail — it is pointed at a real violation', () => { // A check that has never been shown to fail is a check nobody knows the state // of. This is the game-server dial the rule exists to stop. const violation = "const socket = new WebSocket('ws://10.0.0.5:28016/' + rconPassword)" assert.ok( NETWORKING.some((p) => p.test(stripComments(violation))), 'the guard would not have caught a direct game-server dial', ) }) test('prose describing the rule does not trip it', () => { const prose = [ '// A game shipping RCON over WebSocket means a module COULD write', "// const s = new WebSocket(url); require('net')", '// in about ten lines. It must not.', 'const x = 1', ].join('\n') for (const pattern of NETWORKING) { assert.ok(!pattern.test(stripComments(prose)), `${pattern} fired on a comment`) } })