Skip to content

Commit 9cac9f3

Browse files
matthewpematipicoflorian-lefebvre
authoredMar 18, 2025··
Handle bad values in x-forwarded-host (#13428)
* Handle bad values in x-forwarded-host If a bad value is provide by this header, we simply ignore it and fallback to the host provided by the host header (if there is one). * Add changeset * Update packages/astro/src/core/app/node.ts Co-authored-by: Florian Lefebvre <contact@florian-lefebvre.dev> --------- Co-authored-by: Emanuele Stoppa <my.burning@gmail.com> Co-authored-by: Florian Lefebvre <contact@florian-lefebvre.dev>
1 parent 65f7e09 commit 9cac9f3

File tree

3 files changed

+35
-5
lines changed

3 files changed

+35
-5
lines changed
 

‎.changeset/true-moose-report.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'astro': patch
3+
---
4+
5+
Prevent bad value in x-forwarded-host from crashing request

‎packages/astro/src/core/app/node.ts

+19-5
Original file line numberDiff line numberDiff line change
@@ -75,19 +75,27 @@ export class NodeApp extends App {
7575
// We need to handle it here and parse the header correctly.
7676
// @example "https, http,http" => "http"
7777
const forwardedProtocol = getFirstForwardedValue(req.headers['x-forwarded-proto']);
78-
const protocol = forwardedProtocol ?? (isEncrypted ? 'https' : 'http');
78+
const providedProtocol = (isEncrypted ? 'https' : 'http');
79+
const protocol = forwardedProtocol ?? providedProtocol;
7980

8081
// @example "example.com,www2.example.com" => "example.com"
8182
const forwardedHostname = getFirstForwardedValue(req.headers['x-forwarded-host']);
82-
const hostname = forwardedHostname ?? req.headers.host ?? req.headers[':authority'];
83+
const providedHostname = req.headers.host ?? req.headers[':authority'];
84+
const hostname = forwardedHostname ?? providedHostname;
8385

8486
// @example "443,8080,80" => "443"
8587
const port = getFirstForwardedValue(req.headers['x-forwarded-port']);
8688

87-
const portInHostname = typeof hostname === 'string' && /:\d+$/.test(hostname);
88-
const hostnamePort = portInHostname ? hostname : `${hostname}${port ? `:${port}` : ''}`;
89+
let url: URL;
90+
try {
91+
const hostnamePort = getHostnamePort(hostname, port);
92+
url = new URL(`${protocol}://${hostnamePort}${req.url}`);
93+
} catch {
94+
// Fallback to the provided hostname and port
95+
const hostnamePort = getHostnamePort(providedHostname, port);
96+
url = new URL(`${providedProtocol}://${hostnamePort}`);
97+
}
8998

90-
const url = `${protocol}://${hostnamePort}${req.url}`;
9199
const options: RequestInit = {
92100
method: req.method || 'GET',
93101
headers: makeRequestHeaders(req),
@@ -161,6 +169,12 @@ export class NodeApp extends App {
161169
}
162170
}
163171

172+
function getHostnamePort(hostname: string | string[] | undefined, port?: string): string {
173+
const portInHostname = typeof hostname === 'string' && /:\d+$/.test(hostname);
174+
const hostnamePort = portInHostname ? hostname : `${hostname}${port ? `:${port}` : ''}`;
175+
return hostnamePort;
176+
}
177+
164178
function makeRequestHeaders(req: NodeRequest): Headers {
165179
const headers = new Headers();
166180
for (const [name, value] of Object.entries(req.headers)) {

‎packages/astro/test/units/app/node.test.js

+11
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,17 @@ describe('NodeApp', () => {
8686
});
8787
assert.equal(result.url, 'https://example.com/');
8888
});
89+
90+
it('bad values are ignored and fallback to host header', () => {
91+
const result = NodeApp.createRequest({
92+
...mockNodeRequest,
93+
headers: {
94+
host: 'example.com',
95+
'x-forwarded-host': ':123'
96+
},
97+
});
98+
assert.equal(result.url, 'https://example.com/');
99+
});
89100
});
90101

91102
describe('x-forwarded-proto', () => {

0 commit comments

Comments
 (0)
Please sign in to comment.