@@ -465,6 +465,68 @@ describe("performMatrixRequest", () => {
465465 expect ( result . text ) . toBe ( payload ) ;
466466 expect ( result . buffer . toString ( "utf8" ) ) . toBe ( payload ) ;
467467 } ) ;
468+
469+ it ( "rejects malformed homeserver URLs with a stable error" , async ( ) => {
470+ await expect (
471+ performMatrixRequest ( {
472+ homeserver : "http://matrix-user:matrix-fixture@invalid host" ,
473+ accessToken : "token" ,
474+ method : "GET" ,
475+ endpoint : "/_matrix/client/v3/account/whoami" ,
476+ timeoutMs : 5000 ,
477+ ssrfPolicy : { allowPrivateNetwork : true } ,
478+ } ) ,
479+ ) . rejects . toThrow ( new Error ( "Invalid URL" ) ) ;
480+ } ) ;
481+
482+ it ( "rejects malformed redirect Location headers with a stable error" , async ( ) => {
483+ const server = http . createServer ( ( _req , res ) => {
484+ res . writeHead ( 302 , { location : "http://invalid host/path" } ) ;
485+ res . end ( ) ;
486+ } ) ;
487+ await new Promise < void > ( ( resolve ) => {
488+ server . listen ( 0 , "127.0.0.1" , resolve ) ;
489+ } ) ;
490+ const { port } = server . address ( ) as { port : number } ;
491+
492+ try {
493+ await expect (
494+ performMatrixRequest ( {
495+ homeserver : `http://127.0.0.1:${ port } ` ,
496+ accessToken : "token" ,
497+ method : "GET" ,
498+ endpoint : "/_matrix/client/v3/account/whoami" ,
499+ timeoutMs : 5000 ,
500+ ssrfPolicy : { allowPrivateNetwork : true } ,
501+ } ) ,
502+ ) . rejects . toThrow ( new Error ( "Invalid URL" ) ) ;
503+ } finally {
504+ await new Promise < void > ( ( resolve ) => {
505+ server . close ( ( ) => resolve ( ) ) ;
506+ } ) ;
507+ }
508+ } ) ;
509+
510+ it ( "does not reflect credential-bearing malformed homeserver URLs in errors" , async ( ) => {
511+ const secretPass = "matrix-fixture" ;
512+ const malformed = `http://matrix-user:${ secretPass } @${ [ "invalid" , "host" ] . join ( " " ) } ` ;
513+
514+ try {
515+ await performMatrixRequest ( {
516+ homeserver : malformed ,
517+ accessToken : "token" ,
518+ method : "GET" ,
519+ endpoint : "/_matrix/client/v3/account/whoami" ,
520+ timeoutMs : 5000 ,
521+ ssrfPolicy : { allowPrivateNetwork : true } ,
522+ } ) ;
523+ throw new Error ( "expected performMatrixRequest to reject malformed homeserver URL" ) ;
524+ } catch ( error ) {
525+ const message = error instanceof Error ? error . message : String ( error ) ;
526+ expect ( message ) . toBe ( "Invalid URL" ) ;
527+ expect ( message ) . not . toContain ( secretPass ) ;
528+ }
529+ } ) ;
468530} ) ;
469531
470532describe ( "createMatrixGuardedFetch" , ( ) => {
@@ -550,6 +612,14 @@ describe("createMatrixGuardedFetch", () => {
550612 expect ( runtimeFetch ) . toHaveBeenCalledTimes ( 1 ) ;
551613 expect ( runtimeFetch . mock . calls . at ( 0 ) ?. [ 0 ] ) . toBe ( url ) ;
552614 } ) ;
615+
616+ it ( "rejects malformed request URLs with a stable error" , async ( ) => {
617+ const guardedFetch = createMatrixGuardedFetch ( {
618+ ssrfPolicy : { allowPrivateNetwork : true } ,
619+ } ) ;
620+
621+ await expect ( guardedFetch ( "not-a-url" ) ) . rejects . toThrow ( new Error ( "Invalid URL" ) ) ;
622+ } ) ;
553623} ) ;
554624
555625describe ( "matrix transport streaming OOM guard — real HTTP server without Content-Length" , ( ) => {
0 commit comments