@@ -2,7 +2,7 @@ import fs from "node:fs";
22import os from "node:os" ;
33import path from "node:path" ;
44
5- import { afterEach , beforeEach , describe , expect , it } from "vitest" ;
5+ import { afterEach , beforeEach , describe , expect , it , vi } from "vitest" ;
66
77import {
88 DEFAULT_REDIRECT_PATH ,
@@ -13,6 +13,7 @@ import {
1313 googleOAuthConfigured ,
1414 protectedApiPath ,
1515 protectedPagePath ,
16+ resolveConfiguredPlatformUrl ,
1617 resolvePlatformUrl ,
1718 sanitizeRedirectPath ,
1819 verifyCredentials ,
@@ -36,6 +37,7 @@ afterEach(() => {
3637 delete process . env . DIFFAUDIT_SHARED_USERNAME ;
3738 delete process . env . DIFFAUDIT_SHARED_PASSWORD ;
3839 delete process . env . DIFFAUDIT_PLATFORM_URL ;
40+ vi . unstubAllEnvs ( ) ;
3941 fs . rmSync ( tempDir , { recursive : true , force : true } ) ;
4042} ) ;
4143
@@ -66,6 +68,19 @@ describe("auth route helpers", () => {
6668 expect ( sanitizeRedirectPath ( "//evil.example/path" ) ) . toBe ( DEFAULT_REDIRECT_PATH ) ;
6769 } ) ;
6870
71+ it ( "rejects redirect targets that can be normalized into external origins" , ( ) => {
72+ expect ( sanitizeRedirectPath ( "/\\\\evil.example/path" ) ) . toBe ( DEFAULT_REDIRECT_PATH ) ;
73+ expect ( sanitizeRedirectPath ( "/%5C%5Cevil.example/path" ) ) . toBe ( DEFAULT_REDIRECT_PATH ) ;
74+ expect ( sanitizeRedirectPath ( "/%5c%5cevil.example/path" ) ) . toBe ( DEFAULT_REDIRECT_PATH ) ;
75+ } ) ;
76+
77+ it ( "rejects redirect targets with whitespace or control characters" , ( ) => {
78+ expect ( sanitizeRedirectPath ( " /workspace" ) ) . toBe ( DEFAULT_REDIRECT_PATH ) ;
79+ expect ( sanitizeRedirectPath ( "/workspace " ) ) . toBe ( DEFAULT_REDIRECT_PATH ) ;
80+ expect ( sanitizeRedirectPath ( "/\t/evil.example" ) ) . toBe ( DEFAULT_REDIRECT_PATH ) ;
81+ expect ( sanitizeRedirectPath ( "/%09/evil.example" ) ) . toBe ( DEFAULT_REDIRECT_PATH ) ;
82+ } ) ;
83+
6984 it ( "protects only the workspace routes and not the marketing pages" , ( ) => {
7085 expect ( protectedPagePath ( "/" ) ) . toBe ( false ) ;
7186 expect ( protectedPagePath ( "/trial" ) ) . toBe ( false ) ;
@@ -95,17 +110,49 @@ describe("auth route helpers", () => {
95110 ) . toBe ( true ) ;
96111 } ) ;
97112
98- it ( "resolves oauth public URL from the request when configured URL is bind-only" , ( ) => {
113+ it ( "rejects bind-only configured platform URLs in production" , ( ) => {
114+ vi . stubEnv ( "NODE_ENV" , "production" ) ;
99115 process . env . DIFFAUDIT_PLATFORM_URL = "http://0.0.0.0:3000" ;
100116
101117 const request = new Request ( "http://127.0.0.1:3000/api/auth/google" , {
102118 headers : {
103- host : "diffaudit.example.test" ,
119+ host : "attacker.example.test" ,
120+ "x-forwarded-host" : "attacker.example.test" ,
104121 "x-forwarded-proto" : "https" ,
105122 } ,
106123 } ) ;
107124
108- expect ( resolvePlatformUrl ( request ) ) . toBe ( "https://diffaudit.example.test" ) ;
125+ expect ( resolvePlatformUrl ( request ) ) . toBeNull ( ) ;
126+ expect ( resolveConfiguredPlatformUrl ( ) ) . toBeNull ( ) ;
127+ } ) ;
128+
129+ it ( "rejects unconfigured production platform URLs instead of trusting forwarded hosts" , ( ) => {
130+ vi . stubEnv ( "NODE_ENV" , "production" ) ;
131+
132+ const request = new Request ( "https://internal.invalid/api/auth/google" , {
133+ headers : {
134+ host : "attacker.example.test" ,
135+ "x-forwarded-host" : "attacker.example.test" ,
136+ "x-forwarded-proto" : "https" ,
137+ } ,
138+ } ) ;
139+
140+ expect ( resolvePlatformUrl ( request ) ) . toBeNull ( ) ;
141+ } ) ;
142+
143+ it ( "keeps localhost origin fallback for local development only" , ( ) => {
144+ vi . stubEnv ( "NODE_ENV" , "development" ) ;
145+
146+ const request = new Request ( "http://127.0.0.1:3000/api/auth/google" , {
147+ headers : {
148+ host : "attacker.example.test" ,
149+ "x-forwarded-host" : "attacker.example.test" ,
150+ "x-forwarded-proto" : "https" ,
151+ } ,
152+ } ) ;
153+
154+ expect ( resolvePlatformUrl ( request ) ) . toBe ( "http://127.0.0.1:3000" ) ;
155+ expect ( resolveConfiguredPlatformUrl ( ) ) . toBe ( "http://localhost:3000" ) ;
109156 } ) ;
110157
111158 it ( "prefers a valid configured public platform URL for oauth redirects" , ( ) => {
0 commit comments