From 3ed6f7bf6cd0fe21e0829f49a539066a9f8828c5 Mon Sep 17 00:00:00 2001 From: clawedassistant26 <307253840+clawedassistant26@users.noreply.github.com> Date: Mon, 3 Aug 2026 18:22:06 +0000 Subject: [PATCH] fix(site): reject a --proxy port outside 1-65535 instead of silently misconfiguring `moshcode site --proxy ` validated the port with `^\d+$`, which accepts 0 and out-of-range numbers. --proxy 0 is falsy downstream, so the site silently dropped to a static root; --proxy 99999 wrote a proxy_pass to a port that cannot exist. Both exited 0 and reported the site as live. Validate the port as 1-65535, matching the range every other port takes here (console --port, dns --port/--parking-port). Adds a regression test plus a control that a real port still installs the reverse proxy unchanged. Co-Authored-By: Claude Opus 4.8 --- src/serve.mjs | 13 ++++++++++--- test/serve.test.mjs | 31 +++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/serve.mjs b/src/serve.mjs index 3c02436..13ababe 100644 --- a/src/serve.mjs +++ b/src/serve.mjs @@ -385,9 +385,16 @@ export async function serveCommand(args = [], out = console.log, deps = {}) { const flag = (f) => { const at = rest.indexOf(f); return at >= 0 ? rest[at + 1] : undefined; }; const proxy = flag("--proxy"); - if (proxy !== undefined && !/^\d+$/.test(proxy)) { - out("moshcode site: --proxy takes a port"); - return 1; + if (proxy !== undefined) { + // `^\d+$` alone let 0 and out-of-range numbers through: --proxy 0 is + // falsy downstream, so the site silently drops to a static root, and + // --proxy 99999 writes a proxy_pass to a port that cannot exist. Both + // were reported as live. Ports are 1-65535 everywhere else here. + const port = /^\d+$/.test(proxy) ? Number(proxy) : NaN; + if (!Number.isSafeInteger(port) || port < 1 || port > 65535) { + out(`moshcode site: --proxy needs a decimal integer from 1 to 65535, got ${JSON.stringify(proxy)}`); + return 1; + } } const root = flag("--root") || `/srv/${name}`; const choice = await chooseTemplate(rest); diff --git a/test/serve.test.mjs b/test/serve.test.mjs index 3c73ff0..51bf017 100644 --- a/test/serve.test.mjs +++ b/test/serve.test.mjs @@ -140,6 +140,37 @@ test("a name that is not a Moshpit name is refused before anything else", async } }); +test("a --proxy port that is not 1-65535 is refused, not silently misconfigured", async () => { + // 0 is falsy downstream, so it dropped to a static root; 99999 wrote a + // proxy_pass to a port that cannot exist. Both used to exit 0 and write. + for (const bad of ["0", "99999", "70000", "-1", "abc", ""]) { + const lines = []; + let wrote = false; + const code = await serveCommand(["blue.eggs", "--proxy", bad, "--install"], (l) => lines.push(l), { + detect: async () => "nginx", + write: async () => { wrote = true; }, + mkdir: async () => {}, + copy: async () => {}, + runner: async () => ({ ok: true }), + }); + assert.equal(code, 1, `--proxy ${bad} should be refused`); + assert.equal(wrote, false, `--proxy ${bad} must not write a config`); + assert.ok(lines.some((l) => /--proxy needs a decimal integer from 1 to 65535/.test(l)), `--proxy ${bad} message`); + } + // Control: a real port still installs a reverse proxy, unchanged. + const lines = []; + let content = null; + const code = await serveCommand(["blue.eggs", "--proxy", "3000", "--install"], (l) => lines.push(l), { + detect: async () => "nginx", + write: async (_p, c) => { content = c; }, + mkdir: async () => {}, + copy: async () => {}, + runner: async () => ({ ok: true }), + }); + assert.equal(code, 0); + assert.match(content, /proxy_pass http:\/\/127\.0\.0\.1:3000;/); +}); + test("a fresh site is seeded, because an empty root is a 404 that reads as broken", async () => { // The moment someone is trying to tell "the name resolved" from "the install // failed" is exactly the moment an empty root answers 404 and refuses to