diff --git a/packages/playwright-core/src/server/fetch.ts b/packages/playwright-core/src/server/fetch.ts index f5dad1d656..12c67624c8 100644 --- a/packages/playwright-core/src/server/fetch.ts +++ b/packages/playwright-core/src/server/fetch.ts @@ -26,7 +26,7 @@ import zlib from 'zlib'; import type { HTTPCredentials } from '../../types/types'; import { TimeoutSettings } from '../common/timeoutSettings'; import { getUserAgent } from '../utils/userAgent'; -import { assert, createGuid, monotonicTime } from '../utils'; +import { assert, createGuid, isUnderTest, monotonicTime } from '../utils'; import { HttpsProxyAgent, SocksProxyAgent } from '../utilsBundle'; import { BrowserContext, verifyClientCertificates } from './browserContext'; import { CookieStore, domainMatches } from './cookieStore'; @@ -196,7 +196,7 @@ export abstract class APIRequestContext extends SdkObject { ...clientCertificatesToTLSOptions(this._defaultOptions().clientCertificates, requestUrl.toString()), __testHookLookup: (params as any).__testHookLookup, }; - if (process.env.PWTEST_UNSUPPORTED_CUSTOM_CA) + if (process.env.PWTEST_UNSUPPORTED_CUSTOM_CA && isUnderTest()) options.ca = [fs.readFileSync(process.env.PWTEST_UNSUPPORTED_CUSTOM_CA)]; // rejectUnauthorized = undefined is treated as true in Node.js 12. if (params.ignoreHTTPSErrors || defaults.ignoreHTTPSErrors) diff --git a/packages/playwright-core/src/server/socksClientCertificatesInterceptor.ts b/packages/playwright-core/src/server/socksClientCertificatesInterceptor.ts index 5c483e7c30..e8cc30ae49 100644 --- a/packages/playwright-core/src/server/socksClientCertificatesInterceptor.ts +++ b/packages/playwright-core/src/server/socksClientCertificatesInterceptor.ts @@ -21,7 +21,7 @@ import fs from 'fs'; import tls from 'tls'; import stream from 'stream'; import { createSocket } from '../utils/happy-eyeballs'; -import { globToRegex } from '../utils'; +import { globToRegex, isUnderTest } from '../utils'; import type { SocksSocketClosedPayload, SocksSocketDataPayload, SocksSocketRequestedPayload } from '../common/socksProxy'; import { SocksProxy } from '../common/socksProxy'; import type * as channels from '@protocol/channels'; @@ -98,11 +98,14 @@ class SocksProxyConnection { this.internalTLS = internalTLS; internalTLS.on('close', () => this.socksProxy._socksProxy.sendSocketEnd({ uid: this.uid })); - const targetTLS = tls.connect({ + const tlsOptions: tls.ConnectionOptions = { socket: this.target, - rejectUnauthorized: this.socksProxy.contextOptions.ignoreHTTPSErrors === true ? false : true, - ...clientCertificatesToTLSOptions(this.socksProxy.contextOptions.clientCertificates, `https://${this.host}:${this.port}/`), - }); + rejectUnauthorized: !this.socksProxy.ignoreHTTPSErrors, + ...clientCertificatesToTLSOptions(this.socksProxy.clientCertificates, `https://${this.host}:${this.port}/`), + }; + if (process.env.PWTEST_UNSUPPORTED_CUSTOM_CA && isUnderTest()) + tlsOptions.ca = [fs.readFileSync(process.env.PWTEST_UNSUPPORTED_CUSTOM_CA)]; + const targetTLS = tls.connect(tlsOptions); internalTLS.pipe(targetTLS); targetTLS.pipe(internalTLS); @@ -118,13 +121,14 @@ class SocksProxyConnection { internalTLS.on('error', () => closeBothSockets()); targetTLS.on('error', error => { - internalTLS.write('HTTP/1.1 503 Internal Server Error\r\n'); - internalTLS.write('Content-Type: text/html; charset=utf-8\r\n'); const responseBody = 'Playwright client-certificate error: ' + error.message; - internalTLS.write('Content-Length: ' + Buffer.byteLength(responseBody) + '\r\n'); - internalTLS.write('\r\n'); - internalTLS.write(responseBody); - internalTLS.end(); + internalTLS.end([ + 'HTTP/1.1 503 Internal Server Error', + 'Content-Type: text/html; charset=utf-8', + 'Content-Length: ' + Buffer.byteLength(responseBody), + '\r\n', + responseBody, + ].join('\r\n')); closeBothSockets(); }); } @@ -133,10 +137,14 @@ class SocksProxyConnection { export class ClientCertificatesProxy { _socksProxy: SocksProxy; private _connections: Map = new Map(); + ignoreHTTPSErrors: boolean | undefined; + clientCertificates: channels.BrowserNewContextOptions['clientCertificates']; constructor( - public readonly contextOptions: Pick + contextOptions: Pick ) { + this.ignoreHTTPSErrors = contextOptions.ignoreHTTPSErrors; + this.clientCertificates = contextOptions.clientCertificates; this._socksProxy = new SocksProxy(); this._socksProxy.setPattern('*'); this._socksProxy.addListener(SocksProxy.Events.SocksRequested, async (payload: SocksSocketRequestedPayload) => { diff --git a/tests/library/client-certificates.spec.ts b/tests/library/client-certificates.spec.ts index 585a4f5fe3..6cee291f0b 100644 --- a/tests/library/client-certificates.spec.ts +++ b/tests/library/client-certificates.spec.ts @@ -249,6 +249,21 @@ test.describe('browser', () => { await page.close(); }); + test('should have ignoreHTTPSErrors=false by default', async ({ browser, httpsServer, asset }) => { + const page = await browser.newPage({ + clientCertificates: [{ + url: 'https://just-there-that-the-client-certificates-proxy-server-is-getting-launched.com', + certs: [{ + certPath: asset('client-certificates/client/trusted/cert.pem'), + keyPath: asset('client-certificates/client/trusted/key.pem'), + }], + }], + }); + await page.goto(httpsServer.EMPTY_PAGE); + await expect(page.getByText('Playwright client-certificate error')).toBeVisible(); + await page.close(); + }); + test.describe('persistentContext', () => { test('validate input', async ({ launchPersistent }) => { test.slow();