11import assert from 'node:assert/strict' ;
22import { describe , it } from 'node:test' ;
33import { computePathnameFromDomain } from '../../../dist/core/i18n/domain.js' ;
4- import type { SSRManifestI18n } from '../../../dist/core/app/types.js' ;
4+ import type { SSRManifest , SSRManifestI18n } from '../../../dist/core/app/types.js' ;
55import type { RoutingStrategies } from '../../../dist/core/app/common.js' ;
66import type { Locales } from '../../../dist/types/public/config.js' ;
7- import { defaultLogger , SpyLogger } from '../test-utils.ts' ;
7+ import { defaultLogger } from '../test-utils.ts' ;
88
99interface I18nOverrides {
1010 strategy ?: RoutingStrategies ;
@@ -38,6 +38,7 @@ function run(
3838 i18n ?: SSRManifestI18n | undefined ;
3939 base ?: string ;
4040 trailingSlash ?: 'always' | 'never' | 'ignore' ;
41+ allowedDomains ?: SSRManifest [ 'allowedDomains' ] ;
4142 logger ?: typeof defaultLogger ;
4243 } = { } ,
4344) {
@@ -46,9 +47,18 @@ function run(
4647 const i18n = 'i18n' in opts ? opts . i18n : makeI18n ( ) ;
4748 const base = opts . base ?? '/' ;
4849 const trailingSlash = opts . trailingSlash ?? 'ignore' ;
50+ const allowedDomains = opts . allowedDomains ?? [ { hostname : 'example.fr' } ] ;
4951 const logger = opts . logger ?? defaultLogger ;
5052 const [ request , parsedUrl ] = reqAndUrl ( url , headers ) ;
51- return computePathnameFromDomain ( request , parsedUrl , i18n , base , trailingSlash , logger ) ;
53+ return computePathnameFromDomain (
54+ request ,
55+ parsedUrl ,
56+ i18n ,
57+ base ,
58+ trailingSlash ,
59+ allowedDomains ,
60+ logger ,
61+ ) ;
5262}
5363
5464describe ( 'computePathnameFromDomain' , ( ) => {
@@ -90,10 +100,60 @@ describe('computePathnameFromDomain', () => {
90100 ) ;
91101 } ) ;
92102
103+ it ( 'ignores X-Forwarded-Host when allowedDomains is not configured' , ( ) => {
104+ assert . equal (
105+ run (
106+ 'https://example.com/about' ,
107+ { Host : 'example.com' , 'X-Forwarded-Host' : 'example.fr' } ,
108+ { allowedDomains : [ ] } ,
109+ ) ,
110+ undefined ,
111+ ) ;
112+ } ) ;
113+
114+ it ( 'ignores X-Forwarded-Host when it does not match allowedDomains' , ( ) => {
115+ assert . equal (
116+ run (
117+ 'https://example.com/about' ,
118+ { Host : 'example.com' , 'X-Forwarded-Host' : 'example.fr' } ,
119+ { allowedDomains : [ { hostname : 'example.com' } ] } ,
120+ ) ,
121+ undefined ,
122+ ) ;
123+ } ) ;
124+
93125 it ( 'falls back to the Host header when X-Forwarded-Host is absent' , ( ) => {
94126 assert . equal ( run ( 'https://example.fr/about' , { Host : 'example.fr' } ) , '/fr/about' ) ;
95127 } ) ;
96128
129+ it ( 'ignores the Host header when it does not match configured allowedDomains' , ( ) => {
130+ assert . equal (
131+ run (
132+ 'https://example.fr/about' ,
133+ { Host : 'example.fr' } ,
134+ { allowedDomains : [ { hostname : 'example.com' } ] } ,
135+ ) ,
136+ undefined ,
137+ ) ;
138+ } ) ;
139+
140+ it ( 'uses the Host header without validation when allowedDomains is not configured' , ( ) => {
141+ assert . equal (
142+ run ( 'https://example.fr/about' , { Host : 'example.fr' } , { allowedDomains : [ ] } ) ,
143+ '/fr/about' ,
144+ ) ;
145+ } ) ;
146+
147+ it ( 'uses the first value from a comma-separated X-Forwarded-Host header' , ( ) => {
148+ assert . equal (
149+ run ( 'https://example.fr/about' , {
150+ 'X-Forwarded-Host' : 'example.fr, proxy.internal' ,
151+ 'X-Forwarded-Proto' : 'https, http' ,
152+ } ) ,
153+ '/fr/about' ,
154+ ) ;
155+ } ) ;
156+
97157 it ( 'strips a port from the forwarded host before matching' , ( ) => {
98158 assert . equal (
99159 run ( 'https://example.fr/about' , {
@@ -186,13 +246,7 @@ describe('computePathnameFromDomain', () => {
186246 ) ;
187247 } ) ;
188248
189- it ( 'logs an error and returns undefined when the host cannot be parsed as a URL' , ( ) => {
190- const logger = new SpyLogger ( ) ;
191- const result = run ( 'https://example.fr/about' , { 'X-Forwarded-Host' : '[' } , { logger } ) ;
192- assert . equal ( result , undefined ) ;
193- assert . ok (
194- logger . logs . some ( ( entry ) => entry . level === 'error' && entry . label === 'router' ) ,
195- 'expected a router error to be logged' ,
196- ) ;
249+ it ( 'ignores a forwarded host that cannot be parsed as a URL' , ( ) => {
250+ assert . equal ( run ( 'https://example.fr/about' , { 'X-Forwarded-Host' : '[' } ) , undefined ) ;
197251 } ) ;
198252} ) ;
0 commit comments