fix(client-certificates): keep ignoreHTTPSErrors false by default (#31760)

This commit is contained in:
Max Schmitt 2024-07-18 22:37:11 +02:00 committed by GitHub
parent 6dbc7b54e8
commit 708def8804
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 37 additions and 14 deletions

View file

@ -26,7 +26,7 @@ import zlib from 'zlib';
import type { HTTPCredentials } from '../../types/types'; import type { HTTPCredentials } from '../../types/types';
import { TimeoutSettings } from '../common/timeoutSettings'; import { TimeoutSettings } from '../common/timeoutSettings';
import { getUserAgent } from '../utils/userAgent'; import { getUserAgent } from '../utils/userAgent';
import { assert, createGuid, monotonicTime } from '../utils'; import { assert, createGuid, isUnderTest, monotonicTime } from '../utils';
import { HttpsProxyAgent, SocksProxyAgent } from '../utilsBundle'; import { HttpsProxyAgent, SocksProxyAgent } from '../utilsBundle';
import { BrowserContext, verifyClientCertificates } from './browserContext'; import { BrowserContext, verifyClientCertificates } from './browserContext';
import { CookieStore, domainMatches } from './cookieStore'; import { CookieStore, domainMatches } from './cookieStore';
@ -196,7 +196,7 @@ export abstract class APIRequestContext extends SdkObject {
...clientCertificatesToTLSOptions(this._defaultOptions().clientCertificates, requestUrl.toString()), ...clientCertificatesToTLSOptions(this._defaultOptions().clientCertificates, requestUrl.toString()),
__testHookLookup: (params as any).__testHookLookup, __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)]; options.ca = [fs.readFileSync(process.env.PWTEST_UNSUPPORTED_CUSTOM_CA)];
// rejectUnauthorized = undefined is treated as true in Node.js 12. // rejectUnauthorized = undefined is treated as true in Node.js 12.
if (params.ignoreHTTPSErrors || defaults.ignoreHTTPSErrors) if (params.ignoreHTTPSErrors || defaults.ignoreHTTPSErrors)

View file

@ -21,7 +21,7 @@ import fs from 'fs';
import tls from 'tls'; import tls from 'tls';
import stream from 'stream'; import stream from 'stream';
import { createSocket } from '../utils/happy-eyeballs'; import { createSocket } from '../utils/happy-eyeballs';
import { globToRegex } from '../utils'; import { globToRegex, isUnderTest } from '../utils';
import type { SocksSocketClosedPayload, SocksSocketDataPayload, SocksSocketRequestedPayload } from '../common/socksProxy'; import type { SocksSocketClosedPayload, SocksSocketDataPayload, SocksSocketRequestedPayload } from '../common/socksProxy';
import { SocksProxy } from '../common/socksProxy'; import { SocksProxy } from '../common/socksProxy';
import type * as channels from '@protocol/channels'; import type * as channels from '@protocol/channels';
@ -98,11 +98,14 @@ class SocksProxyConnection {
this.internalTLS = internalTLS; this.internalTLS = internalTLS;
internalTLS.on('close', () => this.socksProxy._socksProxy.sendSocketEnd({ uid: this.uid })); internalTLS.on('close', () => this.socksProxy._socksProxy.sendSocketEnd({ uid: this.uid }));
const targetTLS = tls.connect({ const tlsOptions: tls.ConnectionOptions = {
socket: this.target, socket: this.target,
rejectUnauthorized: this.socksProxy.contextOptions.ignoreHTTPSErrors === true ? false : true, rejectUnauthorized: !this.socksProxy.ignoreHTTPSErrors,
...clientCertificatesToTLSOptions(this.socksProxy.contextOptions.clientCertificates, `https://${this.host}:${this.port}/`), ...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); internalTLS.pipe(targetTLS);
targetTLS.pipe(internalTLS); targetTLS.pipe(internalTLS);
@ -118,13 +121,14 @@ class SocksProxyConnection {
internalTLS.on('error', () => closeBothSockets()); internalTLS.on('error', () => closeBothSockets());
targetTLS.on('error', error => { 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; const responseBody = 'Playwright client-certificate error: ' + error.message;
internalTLS.write('Content-Length: ' + Buffer.byteLength(responseBody) + '\r\n'); internalTLS.end([
internalTLS.write('\r\n'); 'HTTP/1.1 503 Internal Server Error',
internalTLS.write(responseBody); 'Content-Type: text/html; charset=utf-8',
internalTLS.end(); 'Content-Length: ' + Buffer.byteLength(responseBody),
'\r\n',
responseBody,
].join('\r\n'));
closeBothSockets(); closeBothSockets();
}); });
} }
@ -133,10 +137,14 @@ class SocksProxyConnection {
export class ClientCertificatesProxy { export class ClientCertificatesProxy {
_socksProxy: SocksProxy; _socksProxy: SocksProxy;
private _connections: Map<string, SocksProxyConnection> = new Map(); private _connections: Map<string, SocksProxyConnection> = new Map();
ignoreHTTPSErrors: boolean | undefined;
clientCertificates: channels.BrowserNewContextOptions['clientCertificates'];
constructor( constructor(
public readonly contextOptions: Pick<channels.BrowserNewContextOptions, 'clientCertificates' | 'ignoreHTTPSErrors'> contextOptions: Pick<channels.BrowserNewContextOptions, 'clientCertificates' | 'ignoreHTTPSErrors'>
) { ) {
this.ignoreHTTPSErrors = contextOptions.ignoreHTTPSErrors;
this.clientCertificates = contextOptions.clientCertificates;
this._socksProxy = new SocksProxy(); this._socksProxy = new SocksProxy();
this._socksProxy.setPattern('*'); this._socksProxy.setPattern('*');
this._socksProxy.addListener(SocksProxy.Events.SocksRequested, async (payload: SocksSocketRequestedPayload) => { this._socksProxy.addListener(SocksProxy.Events.SocksRequested, async (payload: SocksSocketRequestedPayload) => {

View file

@ -249,6 +249,21 @@ test.describe('browser', () => {
await page.close(); 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.describe('persistentContext', () => {
test('validate input', async ({ launchPersistent }) => { test('validate input', async ({ launchPersistent }) => {
test.slow(); test.slow();