From 6093a1ddb4e016a6158bc47025a3cf2db4c9a41f Mon Sep 17 00:00:00 2001 From: "Aaron K. Clark" Date: Tue, 19 May 2026 06:04:54 -0500 Subject: [PATCH] chore(server): tighten TRUST_PROXY hop-count parsing to strict integer-only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TRUST_PROXY= branch was: ```js } else if (trustProxy && !isNaN(parseInt(trustProxy, 10))) { app.set('trust proxy', parseInt(trustProxy, 10)); } ``` `parseInt` is lenient — `parseInt('1abc', 10)` returns `1`. An operator typo like `TRUST_PROXY=1abc` would silently set the hop count to 1, partially honoring a malformed value instead of falling back to the implicit Express default (no trust). Switch to a strict `/^\d+$/` regex match. Now anything that isn't the literal string `true` or a clean non-negative integer string falls through to the default. Operator typos surface as "X-Forwarded-For not trusted" — a missing-trust observation is debuggable; a partial-trust silent acceptance is not. Tightened the `trustProxy && !isNaN(...)` check to `typeof trustProxy === 'string' && /^\d+$/.test(trustProxy)` for the same reason (the `!isNaN(parseInt(undefined, 10))` short circuit was already handled by the leading `trustProxy &&`, but the explicit type guard is clearer about intent). Co-Authored-By: Claude Opus 4.7 (1M context) --- server.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/server.js b/server.js index ef5ce50..76001b1 100644 --- a/server.js +++ b/server.js @@ -76,10 +76,17 @@ app.use(pinoHttp({ // Operators opt in via TRUST_PROXY (true|false|). Default // false to avoid the security pitfall of trusting X-Forwarded-For // from a non-proxied client. +// +// The hop-count branch uses a strict regex match (^\d+$) rather than +// `parseInt + !isNaN`, because parseInt is lenient — it would happily +// turn `TRUST_PROXY=1abc` (an operator typo) into `1` and silently +// trust one hop. With the regex an invalid value falls through to +// the implicit Express default (no trust) instead of partially +// honoring a malformed setting. const trustProxy = process.env.TRUST_PROXY; if (trustProxy === 'true') { app.set('trust proxy', true); -} else if (trustProxy && !isNaN(parseInt(trustProxy, 10))) { +} else if (typeof trustProxy === 'string' && /^\d+$/.test(trustProxy)) { app.set('trust proxy', parseInt(trustProxy, 10)); }