patchright-core 1.55.1 → 1.55.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/reinstall_chrome_beta_mac.sh +1 -1
- package/bin/reinstall_chrome_stable_mac.sh +1 -1
- package/bin/reinstall_msedge_beta_mac.sh +1 -1
- package/bin/reinstall_msedge_dev_mac.sh +1 -1
- package/bin/reinstall_msedge_stable_mac.sh +1 -1
- package/browsers.json +4 -4
- package/lib/client/locator.js +5 -5
- package/lib/generated/injectedScriptSource.js +1 -1
- package/lib/server/browserContext.js +1 -3
- package/lib/server/chromium/chromiumSwitches.js +0 -1
- package/lib/server/chromium/crNetworkManager.js +146 -27
- package/lib/server/chromium/crServiceWorker.js +10 -0
- package/lib/server/deviceDescriptorsSource.json +54 -54
- package/lib/server/frames.js +17 -13
- package/lib/vite/htmlReport/index.html +1 -1
- package/lib/vite/traceViewer/assets/{codeMirrorModule-Di48jgWx.js → codeMirrorModule-DsmF_DpA.js} +1 -1
- package/lib/vite/traceViewer/assets/{defaultSettingsView-szBn8781.js → defaultSettingsView-Cd59AFK5.js} +3 -3
- package/lib/vite/traceViewer/{index.DQvXoPLL.js → index.BivEwfRr.js} +1 -1
- package/lib/vite/traceViewer/index.html +2 -2
- package/lib/vite/traceViewer/{uiMode.dBV3oN9h.js → uiMode.BDvz7Y9W.js} +1 -1
- package/lib/vite/traceViewer/uiMode.html +2 -2
- package/package.json +1 -1
|
@@ -127,9 +127,7 @@ class BrowserContext extends import_instrumentation.SdkObject {
|
|
|
127
127
|
`);
|
|
128
128
|
}
|
|
129
129
|
if (this._options.serviceWorkers === "block")
|
|
130
|
-
await this.addInitScript(`navigator.serviceWorker.register = async () => { }
|
|
131
|
-
if (navigator.serviceWorker) navigator.serviceWorker.register = async () => { console.warn('Service Worker registration blocked by Playwright'); };
|
|
132
|
-
`);
|
|
130
|
+
await this.addInitScript(void 0, `navigator.serviceWorker.register = async () => { };`);
|
|
133
131
|
if (this._options.permissions)
|
|
134
132
|
await this.grantPermissions(this._options.permissions);
|
|
135
133
|
}
|
|
@@ -73,7 +73,6 @@ const chromiumSwitches = (assistantMode, channel) => [
|
|
|
73
73
|
"--disable-search-engine-choice-screen",
|
|
74
74
|
// Edge can potentially restart on Windows (msRelaunchNoCompatLayer) which looses its file descriptors (stdout/stderr) and CDP (3/4). Disable until fixed upstream.
|
|
75
75
|
"--edge-skip-compat-layer-relaunch",
|
|
76
|
-
assistantMode ? "" : "--enable-automation",
|
|
77
76
|
"--disable-blink-features=AutomationControlled"
|
|
78
77
|
].filter(Boolean);
|
|
79
78
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -533,45 +533,91 @@ class RouteImpl {
|
|
|
533
533
|
if (!allInjections.includes(binding)) allInjections.push(binding);
|
|
534
534
|
}
|
|
535
535
|
if (isTextHtml && allInjections.length) {
|
|
536
|
-
let
|
|
537
|
-
let
|
|
536
|
+
let useNonce = false;
|
|
537
|
+
let scriptNonce = null;
|
|
538
|
+
if (response.isBase64) {
|
|
539
|
+
response.isBase64 = false;
|
|
540
|
+
response.body = Buffer.from(response.body, "base64").toString("utf-8");
|
|
541
|
+
}
|
|
542
|
+
const cspHeaderNames = ["content-security-policy", "content-security-policy-report-only"];
|
|
538
543
|
for (let i = 0; i < response.headers.length; i++) {
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
const
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
useNonce = false;
|
|
548
|
-
} else {
|
|
549
|
-
const scriptSrcRegex = /(script-src[^;]*)(;|$)/;
|
|
550
|
-
const newCspValue = cspValue.replace(scriptSrcRegex, `$1 'nonce-${scriptNonce}'$2`);
|
|
551
|
-
response.headers[i].value = newCspValue;
|
|
544
|
+
const headerName = response.headers[i].name.toLowerCase();
|
|
545
|
+
if (cspHeaderNames.includes(headerName)) {
|
|
546
|
+
const originalCsp = response.headers[i].value || "";
|
|
547
|
+
if (!useNonce) {
|
|
548
|
+
const nonceMatch = originalCsp.match(/script-src[^;]*'nonce-([^'"\s;]+)'/i);
|
|
549
|
+
if (nonceMatch && nonceMatch[1]) {
|
|
550
|
+
scriptNonce = nonceMatch[1];
|
|
551
|
+
useNonce = true;
|
|
552
552
|
}
|
|
553
553
|
}
|
|
554
|
-
|
|
554
|
+
const fixedCsp = this._fixCSP(originalCsp, scriptNonce);
|
|
555
|
+
response.headers[i].value = fixedCsp;
|
|
555
556
|
}
|
|
556
557
|
}
|
|
558
|
+
if (typeof response.body === "string" && response.body.length) {
|
|
559
|
+
response.body = response.body.replace(
|
|
560
|
+
/<meta[^>]*http-equiv=(?:"|')?Content-Security-Policy(?:"|')?[^>]*>/gi,
|
|
561
|
+
(match) => {
|
|
562
|
+
const contentMatch = match.match(/content=(?:"|')([^"']*)(?:"|')/i);
|
|
563
|
+
if (contentMatch && contentMatch[1]) {
|
|
564
|
+
let originalCsp = contentMatch[1];
|
|
565
|
+
originalCsp = originalCsp.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/"/g, '"').replace(/ /g, " ").replace(/&#(d+);/g, (match2, dec) => String.fromCharCode(dec)).replace(/&#x([0-9a-fA-F]+);/g, (match2, hex) => String.fromCharCode(parseInt(hex, 16)));
|
|
566
|
+
if (!useNonce) {
|
|
567
|
+
const nonceMatch = originalCsp.match(/script-src[^;]*'nonce-([^'"\s;]+)'/i);
|
|
568
|
+
if (nonceMatch && nonceMatch[1]) {
|
|
569
|
+
scriptNonce = nonceMatch[1];
|
|
570
|
+
useNonce = true;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
const fixedCsp = this._fixCSP(originalCsp, scriptNonce);
|
|
574
|
+
const encodedCsp = fixedCsp.replace(/'/g, "'").replace(/"/g, """);
|
|
575
|
+
return match.replace(contentMatch[1], encodedCsp);
|
|
576
|
+
}
|
|
577
|
+
return match;
|
|
578
|
+
}
|
|
579
|
+
);
|
|
580
|
+
}
|
|
557
581
|
let injectionHTML = "";
|
|
558
582
|
allInjections.forEach((script) => {
|
|
559
583
|
let scriptId = import_crypto.default.randomBytes(22).toString("hex");
|
|
560
584
|
let scriptSource = script.source || script;
|
|
561
|
-
|
|
562
|
-
|
|
585
|
+
const nonceAttr = useNonce ? `nonce="${scriptNonce}"` : "";
|
|
586
|
+
injectionHTML += `<script class="${this._page.delegate.initScriptTag}" ${nonceAttr} id="${scriptId}" type="text/javascript">document.getElementById("${scriptId}")?.remove();${scriptSource}</script>`;
|
|
587
|
+
});
|
|
588
|
+
const lower = response.body.toLowerCase();
|
|
589
|
+
const headStartIndex = lower.indexOf("<head");
|
|
590
|
+
if (headStartIndex !== -1) {
|
|
591
|
+
const headEndTagIndex = lower.indexOf("</head>", headStartIndex);
|
|
592
|
+
if (headEndTagIndex !== -1) {
|
|
593
|
+
const headOpenEnd = response.body.indexOf(">", headStartIndex) + 1;
|
|
594
|
+
const headContent = response.body.slice(headOpenEnd, headEndTagIndex);
|
|
595
|
+
const headContentLower = headContent.toLowerCase();
|
|
596
|
+
const firstScriptIndex = headContentLower.indexOf("<script");
|
|
597
|
+
if (firstScriptIndex !== -1) {
|
|
598
|
+
const insertPosition = headOpenEnd + firstScriptIndex;
|
|
599
|
+
response.body = response.body.slice(0, insertPosition) + injectionHTML + response.body.slice(insertPosition);
|
|
600
|
+
} else {
|
|
601
|
+
response.body = response.body.slice(0, headEndTagIndex) + injectionHTML + response.body.slice(headEndTagIndex);
|
|
602
|
+
}
|
|
563
603
|
} else {
|
|
564
|
-
|
|
604
|
+
const headStartTagEnd = response.body.indexOf(">", headStartIndex) + 1;
|
|
605
|
+
response.body = response.body.slice(0, headStartTagEnd) + injectionHTML + response.body.slice(headStartTagEnd);
|
|
565
606
|
}
|
|
566
|
-
});
|
|
567
|
-
if (response.isBase64) {
|
|
568
|
-
response.isBase64 = false;
|
|
569
|
-
response.body = Buffer.from(response.body, "base64").toString("utf-8");
|
|
570
|
-
}
|
|
571
|
-
if (/^<!DOCTYPE[sS]*?>/i.test(response.body)) {
|
|
572
|
-
response.body = response.body.replace(/^<!DOCTYPE[sS]*?>/i, (match) => `${match}${injectionHTML}`);
|
|
573
607
|
} else {
|
|
574
|
-
|
|
608
|
+
const doctypeIndex = lower.indexOf("<!doctype");
|
|
609
|
+
if (doctypeIndex === 0) {
|
|
610
|
+
const doctypeEnd = response.body.indexOf(">", doctypeIndex) + 1;
|
|
611
|
+
response.body = response.body.slice(0, doctypeEnd) + injectionHTML + response.body.slice(doctypeEnd);
|
|
612
|
+
} else {
|
|
613
|
+
const htmlIndex = lower.indexOf("<html");
|
|
614
|
+
if (htmlIndex !== -1) {
|
|
615
|
+
const htmlTagEnd = response.body.indexOf(">", htmlIndex) + 1;
|
|
616
|
+
response.body = response.body.slice(0, htmlTagEnd) + `<head>${injectionHTML}</head>` + response.body.slice(htmlTagEnd);
|
|
617
|
+
} else {
|
|
618
|
+
response.body = injectionHTML + response.body;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
575
621
|
}
|
|
576
622
|
}
|
|
577
623
|
this._fulfilled = true;
|
|
@@ -597,6 +643,79 @@ class RouteImpl {
|
|
|
597
643
|
});
|
|
598
644
|
});
|
|
599
645
|
}
|
|
646
|
+
_fixCSP(csp, scriptNonce) {
|
|
647
|
+
if (!csp || typeof csp !== "string") return csp;
|
|
648
|
+
const directives = csp.split(";").map((d) => d.trim()).filter((d) => d && d.length > 0);
|
|
649
|
+
const fixedDirectives = [];
|
|
650
|
+
let hasScriptSrc = false;
|
|
651
|
+
for (let directive of directives) {
|
|
652
|
+
if (!directive.trim()) continue;
|
|
653
|
+
const parts = directive.trim().split(/s+/);
|
|
654
|
+
if (parts.length === 0) continue;
|
|
655
|
+
const directiveName = parts[0].toLowerCase();
|
|
656
|
+
const directiveValues = parts.slice(1);
|
|
657
|
+
switch (directiveName) {
|
|
658
|
+
case "script-src":
|
|
659
|
+
hasScriptSrc = true;
|
|
660
|
+
let values = [...directiveValues];
|
|
661
|
+
if (scriptNonce && !values.some((v) => v.includes(`nonce-${scriptNonce}`))) {
|
|
662
|
+
values.push(`'nonce-${scriptNonce}'`);
|
|
663
|
+
}
|
|
664
|
+
if (!values.includes("'unsafe-eval'")) {
|
|
665
|
+
values.push("'unsafe-eval'");
|
|
666
|
+
}
|
|
667
|
+
fixedDirectives.push(`script-src ${values.join(" ")}`);
|
|
668
|
+
break;
|
|
669
|
+
case "style-src":
|
|
670
|
+
let styleValues = [...directiveValues];
|
|
671
|
+
if (!styleValues.includes("'unsafe-inline'")) {
|
|
672
|
+
styleValues.push("'unsafe-inline'");
|
|
673
|
+
}
|
|
674
|
+
fixedDirectives.push(`style-src ${styleValues.join(" ")}`);
|
|
675
|
+
break;
|
|
676
|
+
case "img-src":
|
|
677
|
+
let imgValues = [...directiveValues];
|
|
678
|
+
if (!imgValues.includes("data:") && !imgValues.includes("*")) {
|
|
679
|
+
imgValues.push("data:");
|
|
680
|
+
}
|
|
681
|
+
fixedDirectives.push(`img-src ${imgValues.join(" ")}`);
|
|
682
|
+
break;
|
|
683
|
+
case "font-src":
|
|
684
|
+
let fontValues = [...directiveValues];
|
|
685
|
+
if (!fontValues.includes("data:") && !fontValues.includes("*")) {
|
|
686
|
+
fontValues.push("data:");
|
|
687
|
+
}
|
|
688
|
+
fixedDirectives.push(`font-src ${fontValues.join(" ")}`);
|
|
689
|
+
break;
|
|
690
|
+
case "connect-src":
|
|
691
|
+
let connectValues = [...directiveValues];
|
|
692
|
+
const hasWs = connectValues.some((v) => v.includes("ws:") || v.includes("wss:") || v === "*");
|
|
693
|
+
if (!hasWs) {
|
|
694
|
+
connectValues.push("ws:", "wss:");
|
|
695
|
+
}
|
|
696
|
+
fixedDirectives.push(`connect-src ${connectValues.join(" ")}`);
|
|
697
|
+
break;
|
|
698
|
+
case "frame-ancestors":
|
|
699
|
+
let frameAncestorValues = [...directiveValues];
|
|
700
|
+
if (frameAncestorValues.includes("'none'")) {
|
|
701
|
+
frameAncestorValues = ["'self'"];
|
|
702
|
+
}
|
|
703
|
+
fixedDirectives.push(`frame-ancestors ${frameAncestorValues.join(" ")}`);
|
|
704
|
+
break;
|
|
705
|
+
default:
|
|
706
|
+
fixedDirectives.push(directive);
|
|
707
|
+
break;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
if (!hasScriptSrc) {
|
|
711
|
+
if (scriptNonce) {
|
|
712
|
+
fixedDirectives.push(`script-src 'self' 'unsafe-eval' 'nonce-${scriptNonce}'`);
|
|
713
|
+
} else {
|
|
714
|
+
fixedDirectives.push(`script-src 'self' 'unsafe-eval'`);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
return fixedDirectives.join("; ");
|
|
718
|
+
}
|
|
600
719
|
async _networkRequestIntercepted(event) {
|
|
601
720
|
if (event.resourceType !== "Document") {
|
|
602
721
|
return;
|
|
@@ -64,6 +64,16 @@ class CRServiceWorker extends import_page.Worker {
|
|
|
64
64
|
session.on("Inspector.targetReloadedAfterCrash", () => {
|
|
65
65
|
session._sendMayFail("Runtime.runIfWaitingForDebugger", {});
|
|
66
66
|
});
|
|
67
|
+
session._sendMayFail("Runtime.evaluate", {
|
|
68
|
+
expression: "globalThis",
|
|
69
|
+
serializationOptions: { serialization: "idOnly" }
|
|
70
|
+
}).then((globalThis) => {
|
|
71
|
+
if (globalThis && globalThis.result) {
|
|
72
|
+
var globalThisObjId = globalThis.result.objectId;
|
|
73
|
+
var executionContextId = parseInt(globalThisObjId.split(".")[1], 10);
|
|
74
|
+
this.createExecutionContext(new import_crExecutionContext.CRExecutionContext(session, { id: executionContextId }));
|
|
75
|
+
}
|
|
76
|
+
});
|
|
67
77
|
}
|
|
68
78
|
didClose() {
|
|
69
79
|
this._networkManager?.removeSession(this._session);
|