Skip to content

Commit 75ff91d

Browse files
authored
fix(pwa): validate config port (#65)
1 parent be753cf commit 75ff91d

2 files changed

Lines changed: 65 additions & 2 deletions

File tree

apps/pwa/src/config.mjs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,26 @@ function loadEnv() {
2323
}
2424
loadEnv();
2525

26-
const origin = (process.env.PUBLIC_ORIGIN || `http://localhost:${process.env.PORT || 8080}`).replace(/\/+$/, "");
26+
function readPort(value = process.env.PORT) {
27+
const raw = value === undefined || value === null || String(value).trim() === "" ? "8080" : String(value).trim();
28+
if (!/^\d+$/.test(raw)) {
29+
throw new Error(`PORT must be a decimal integer, got ${JSON.stringify(value)}`);
30+
}
31+
const port = Number(raw);
32+
if (!Number.isSafeInteger(port) || port < 0 || port > 65535) {
33+
throw new Error(`PORT must be between 0 and 65535, got ${JSON.stringify(value)}`);
34+
}
35+
return port;
36+
}
37+
38+
const port = readPort();
39+
const origin = (process.env.PUBLIC_ORIGIN || `http://localhost:${port}`).trim().replace(/\/+$/, "");
2740
const rpID = new URL(origin).hostname;
2841

2942
export const config = {
3043
root: ROOT,
3144
env: process.env.NODE_ENV || "development",
32-
port: Number(process.env.PORT || 8080),
45+
port,
3346
origin,
3447
// WebAuthn relying party = this host.
3548
rpID,

apps/pwa/test/config-port.test.mjs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import assert from "node:assert/strict";
2+
import { spawnSync } from "node:child_process";
3+
import test from "node:test";
4+
5+
const CONFIG = new URL("../src/config.mjs", import.meta.url);
6+
7+
function loadConfig(env) {
8+
return spawnSync(process.execPath, [
9+
"--input-type=module",
10+
"-e",
11+
`import(${JSON.stringify(CONFIG.href)})
12+
.then(({ config }) => {
13+
console.log(JSON.stringify({ port: config.port, origin: config.origin, rpID: config.rpID }));
14+
})
15+
.catch((err) => {
16+
console.error(err.message);
17+
process.exit(1);
18+
});`,
19+
], {
20+
env: {
21+
...process.env,
22+
PUBLIC_ORIGIN: "",
23+
PORT: "",
24+
...env,
25+
},
26+
encoding: "utf8",
27+
});
28+
}
29+
30+
test("config trims PORT before building the fallback origin", () => {
31+
const res = loadConfig({ PORT: "3000 " });
32+
assert.equal(res.status, 0, res.stderr);
33+
assert.deepEqual(JSON.parse(res.stdout), {
34+
port: 3000,
35+
origin: "http://localhost:3000",
36+
rpID: "localhost",
37+
});
38+
});
39+
40+
test("config rejects a non-integer PORT before building the fallback origin", () => {
41+
const res = loadConfig({ PORT: "abc" });
42+
assert.equal(res.status, 1);
43+
assert.match(res.stderr, /PORT must be a decimal integer/);
44+
});
45+
46+
test("config rejects a PORT outside the TCP range", () => {
47+
const res = loadConfig({ PORT: "65536" });
48+
assert.equal(res.status, 1);
49+
assert.match(res.stderr, /PORT must be between 0 and 65535/);
50+
});

0 commit comments

Comments
 (0)