@@ -48,13 +48,19 @@ export const legacyBitstreamURLRedirectGuard: CanActivateFn = (
4848 getFirstCompletedRemoteData ( ) ,
4949 map ( ( rd : RemoteData < Bitstream > ) => {
5050 if ( rd . hasSucceeded && ! rd . hasNoContent ) {
51- // Harden namespace handling: trim slashes, neutralize absolute-like values, enforce root-relative path
5251 let nameSpace = ( appConfig . ui . nameSpace || '' ) . replace ( / ^ \/ + | \/ + $ / g, '' ) ;
53- // Neutralize any absolute-like values (http:, https:, //)
54- if ( nameSpace . startsWith ( 'http:' ) || nameSpace . startsWith ( 'https:' ) || nameSpace . startsWith ( '//' ) ) {
52+ // Neutralize any absolute-like values (http:, https:, //, protocol-relative URLs, or invalid characters)
53+ const allowedNamespaceRegex = / ^ [ a - z A - Z 0 - 9 _ - ] + $ / ;
54+ // Check for absolute URLs, protocol-relative URLs, or invalid characters
55+ if (
56+ nameSpace . startsWith ( 'http:' ) ||
57+ nameSpace . startsWith ( 'https:' ) ||
58+ nameSpace . startsWith ( '//' ) ||
59+ nameSpace . match ( / ^ \/ [ ^ \/ ] / ) || // protocol-relative URL: single slash followed by non-slash
60+ ! allowedNamespaceRegex . test ( nameSpace )
61+ ) {
5562 nameSpace = '' ;
5663 }
57- // Always build path starting with /
5864 const redirectPath = nameSpace ? `/${ nameSpace } /bitstreams/${ rd . payload . uuid } /download` : `/bitstreams/${ rd . payload . uuid } /download` ;
5965 const redirectUrl = new URL ( redirectPath , serverHardRedirectService . getCurrentOrigin ( ) ) . href ;
6066 serverHardRedirectService . redirect ( redirectUrl , 301 ) ;
0 commit comments