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.
@@ -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 scriptNonce = import_crypto.default.randomBytes(22).toString("hex");
537
- let useNonce = true;
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
- if (response.headers[i].name.toLowerCase() === "content-security-policy" || response.headers[i].name.toLowerCase() === "content-security-policy-report-only") {
540
- let cspValue = response.headers[i].value;
541
- const nonceRegex = /script-src[^;]*'nonce-([\w-]+)'/;
542
- const nonceMatch = cspValue.match(nonceRegex);
543
- if (nonceMatch) {
544
- scriptNonce = nonceMatch[1];
545
- } else {
546
- if (/script-src[^;]*'unsafe-inline'/.test(cspValue)) {
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
- break;
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(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#x27;/g, "'").replace(/&#x22;/g, '"').replace(/&nbsp;/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, "&#x27;").replace(/"/g, "&#x22;");
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
- if (useNonce) {
562
- injectionHTML += `<script class="${this._page.delegate.initScriptTag}" nonce="${scriptNonce}" id="${scriptId}" type="text/javascript">document.getElementById("${scriptId}")?.remove();${scriptSource}</script>`;
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
- injectionHTML += `<script class="${this._page.delegate.initScriptTag}" id="${scriptId}" type="text/javascript">document.getElementById("${scriptId}")?.remove();${scriptSource}</script>`;
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
- response.body = injectionHTML + response.body;
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);