address feedback

This commit is contained in:
Max Schmitt 2024-08-05 13:53:53 +02:00
parent 2adc16bca2
commit 15dedb78f4
4 changed files with 20 additions and 20 deletions

View file

@ -22,7 +22,7 @@ import fs from 'fs';
import tls from 'tls'; import tls from 'tls';
import stream from 'stream'; import stream from 'stream';
import { createSocket, createTLSSocket } from '../utils/happy-eyeballs'; import { createSocket, createTLSSocket } from '../utils/happy-eyeballs';
import { ManualPromise, rewriteErrorMessage } from '../utils'; import { escapeHTML, ManualPromise, rewriteErrorMessage } 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';
@ -152,7 +152,9 @@ class SocksProxyConnection {
const handleError = (error: Error) => { const handleError = (error: Error) => {
error = rewriteOpenSSLErrorIfNeeded(error); error = rewriteOpenSSLErrorIfNeeded(error);
debugLogger.log('client-certificates', `error when connecting to target: ${error.message.replaceAll('\n', ' ')}`); debugLogger.log('client-certificates', `error when connecting to target: ${error.message.replaceAll('\n', ' ')}`);
const responseBody = 'Playwright client-certificate error: ' + error.message.replaceAll('\n', '<br>'); const responseBody = escapeHTML('Playwright client-certificate error: ' + error.message)
.replaceAll('\n', ' <br>')
.replaceAll(/(https?:\/\/[^\s]+)/g, '<a href="$1" target="_blank">$1</a>');
if (internalTLS?.alpnProtocol === 'h2') { if (internalTLS?.alpnProtocol === 'h2') {
// This method is available only in Node.js 20+ // This method is available only in Node.js 20+
if ('performServerHandshake' in http2) { if ('performServerHandshake' in http2) {
@ -304,11 +306,8 @@ export function rewriteOpenSSLErrorIfNeeded(error: Error): Error {
return error; return error;
return rewriteErrorMessage(error, [ return rewriteErrorMessage(error, [
'Unsupported TLS certificate.', 'Unsupported TLS certificate.',
'Node.js (OpenSSL) has deprecated certifiates with the security algorithm of the given client-certifiate.', 'Most likely, the security algorithm of the given certificate was deprecated by OpenSSL.',
'To fix this issue, you need to modernise the certificates by running the following command:', 'For more details, see https://github.com/openssl/openssl/blob/master/README-PROVIDERS.md#the-legacy-provider',
'openssl pkcs12 -in oldPfxFile.pfx -nodes -legacy -out decryptedPfxFile.tmp', 'You could probably modernize the certificate by following the steps at https://github.com/nodejs/node/issues/40672#issuecomment-1243648223',
'openssl pkcs12 -in decryptedPfxFile.tmp -export -out newPfxFile.pfx',
'Then, you can use the newPfxFile.pfx in your client-certificates configuration.',
'For more information, please refer to OpenSSL: https://github.com/openssl/openssl/blob/master/README-PROVIDERS.md#the-legacy-provider',
].join('\n')); ].join('\n'));
} }

View file

@ -132,3 +132,11 @@ export function escapeRegExp(s: string) {
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
} }
const escaped = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', '\'': '&#39;' };
export function escapeHTMLAttribute(s: string): string {
return s.replace(/[&<>"']/ug, char => (escaped as any)[char]);
}
export function escapeHTML(s: string): string {
return s.replace(/[&<]/ug, char => (escaped as any)[char]);
}

View file

@ -14,6 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { escapeHTMLAttribute, escapeHTML } from '@isomorphic/stringUtils';
import type { FrameSnapshot, NodeNameAttributesChildNodesSnapshot, NodeSnapshot, RenderedFrameSnapshot, ResourceSnapshot, SubtreeReferenceSnapshot } from '@trace/snapshot'; import type { FrameSnapshot, NodeNameAttributesChildNodesSnapshot, NodeSnapshot, RenderedFrameSnapshot, ResourceSnapshot, SubtreeReferenceSnapshot } from '@trace/snapshot';
function isNodeNameAttributesChildNodesSnapshot(n: NodeSnapshot): n is NodeNameAttributesChildNodesSnapshot { function isNodeNameAttributesChildNodesSnapshot(n: NodeSnapshot): n is NodeNameAttributesChildNodesSnapshot {
@ -57,7 +58,7 @@ export class SnapshotRenderer {
// Old snapshotter was sending lower-case. // Old snapshotter was sending lower-case.
if (parentTag === 'STYLE' || parentTag === 'style') if (parentTag === 'STYLE' || parentTag === 'style')
return rewriteURLsInStyleSheetForCustomProtocol(n); return rewriteURLsInStyleSheetForCustomProtocol(n);
return escapeText(n); return escapeHTML(n);
} }
if (!(n as any)._string) { if (!(n as any)._string) {
@ -106,7 +107,7 @@ export class SnapshotRenderer {
attrValue = 'link://' + value; attrValue = 'link://' + value;
else if (attr.toLowerCase() === 'href' || attr.toLowerCase() === 'src' || attr === kCurrentSrcAttribute) else if (attr.toLowerCase() === 'href' || attr.toLowerCase() === 'src' || attr === kCurrentSrcAttribute)
attrValue = rewriteURLForCustomProtocol(value); attrValue = rewriteURLForCustomProtocol(value);
builder.push(' ', attrName, '="', escapeAttribute(attrValue), '"'); builder.push(' ', attrName, '="', escapeHTMLAttribute(attrValue), '"');
} }
builder.push('>'); builder.push('>');
for (const child of children) for (const child of children)
@ -193,14 +194,6 @@ export class SnapshotRenderer {
} }
const autoClosing = new Set(['AREA', 'BASE', 'BR', 'COL', 'COMMAND', 'EMBED', 'HR', 'IMG', 'INPUT', 'KEYGEN', 'LINK', 'MENUITEM', 'META', 'PARAM', 'SOURCE', 'TRACK', 'WBR']); const autoClosing = new Set(['AREA', 'BASE', 'BR', 'COL', 'COMMAND', 'EMBED', 'HR', 'IMG', 'INPUT', 'KEYGEN', 'LINK', 'MENUITEM', 'META', 'PARAM', 'SOURCE', 'TRACK', 'WBR']);
const escaped = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', '\'': '&#39;' };
function escapeAttribute(s: string): string {
return s.replace(/[&<>"']/ug, char => (escaped as any)[char]);
}
function escapeText(s: string): string {
return s.replace(/[&<]/ug, char => (escaped as any)[char]);
}
function snapshotNodes(snapshot: FrameSnapshot): NodeSnapshot[] { function snapshotNodes(snapshot: FrameSnapshot): NodeSnapshot[] {
if (!(snapshot as any)._nodes) { if (!(snapshot as any)._nodes) {

View file

@ -200,7 +200,7 @@ test.describe('fetch', () => {
await request.dispose(); await request.dispose();
}); });
test('should pass with matching certificates in legacy pfx format', async ({ playwright, startCCServer, asset, browserName }) => { test('should fail with matching certificates in legacy pfx format', async ({ playwright, startCCServer, asset, browserName }) => {
const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && process.platform === 'darwin' }); const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && process.platform === 'darwin' });
const request = await playwright.request.newContext({ const request = await playwright.request.newContext({
ignoreHTTPSErrors: true, ignoreHTTPSErrors: true,
@ -317,7 +317,7 @@ test.describe('browser', () => {
await page.close(); await page.close();
}); });
test('should pass with matching certificates in legacy pfx format', async ({ browser, startCCServer, asset, browserName }) => { test('should fail with matching certificates in legacy pfx format', async ({ browser, startCCServer, asset, browserName }) => {
const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && process.platform === 'darwin' }); const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && process.platform === 'darwin' });
const page = await browser.newPage({ const page = await browser.newPage({
ignoreHTTPSErrors: true, ignoreHTTPSErrors: true,