@@ -156,6 +156,40 @@ test.describe('fetch', () => {
156156 await request . dispose ( ) ;
157157 } ) ;
158158
159+ test ( 'should not leak client certificate to cross-origin redirect target' , async ( { playwright, startCCServer, asset } ) => {
160+ const targetURL = await startCCServer ( ) ;
161+
162+ // Standalone HTTPS server (not cert-required) that 302-redirects to the cert-required server.
163+ const redirectServer = createHttpsServer ( {
164+ key : fs . readFileSync ( asset ( 'client-certificates/server/server_key.pem' ) ) ,
165+ cert : fs . readFileSync ( asset ( 'client-certificates/server/server_cert.pem' ) ) ,
166+ } , ( req , res ) => {
167+ res . writeHead ( 302 , { Location : targetURL } ) ;
168+ res . end ( ) ;
169+ } ) ;
170+ await new Promise < void > ( f => redirectServer . listen ( 0 , '127.0.0.1' , ( ) => f ( ) ) ) ;
171+ const redirectAddr = redirectServer . address ( ) as net . AddressInfo ;
172+ // Use 'localhost' for the redirect origin so it differs from the target's '127.0.0.1' origin
173+ // even though both resolve to the loopback interface and share the same trusted CA.
174+ const redirectURL = `https://localhost:${ redirectAddr . port } /redir` ;
175+
176+ const request = await playwright . request . newContext ( {
177+ ignoreHTTPSErrors : true ,
178+ // Cert is scoped to the redirect *origin*, not the target — it must not follow the redirect.
179+ clientCertificates : [ {
180+ origin : new URL ( redirectURL ) . origin ,
181+ certPath : asset ( 'client-certificates/client/trusted/cert.pem' ) ,
182+ keyPath : asset ( 'client-certificates/client/trusted/key.pem' ) ,
183+ } ] ,
184+ } ) ;
185+ const response = await request . get ( redirectURL ) ;
186+ expect ( response . url ( ) ) . toBe ( targetURL ) ;
187+ expect ( response . status ( ) ) . toBe ( 401 ) ;
188+ expect ( await response . text ( ) ) . toContain ( 'you need to provide a client certificate' ) ;
189+ await request . dispose ( ) ;
190+ await new Promise < void > ( f => redirectServer . close ( ( ) => f ( ) ) ) ;
191+ } ) ;
192+
159193 test ( 'pass with trusted client certificates in pfx format' , async ( { playwright, startCCServer, asset } ) => {
160194 const serverURL = await startCCServer ( ) ;
161195 const request = await playwright . request . newContext ( {
0 commit comments