5

I'm trying to access the contentDocument of a cross origin iframe using Runtime.evaluate. As far as I understand the docs this should be possible by creating an executionContext with universal access using Page.createIsolatedWorld + grantUniveralAccess: true [1] and passing the returned executionContextId to Runtime.evaluate as contextId.

Any ideas?

Given a chromium process started with chromium-browser --user-data-dir=/tmp/headless --remote-debugging-port=9000 [2].

// See [3] for full code
const frameId = /* frameId of our page with origin localhost:9000 */
function execute(command, args) { /* ... send and receive on websocket */ }

const {executionContextId} = await execute("Page.createIsolatedWorld", {
  frameId: frameId, 
  grantUniveralAccess: true // NOT grantUniversalAccess. Typo in devtools protocol itself [4].
})

// fails with:
// Access to fetch at 'http://example.com/' from origin 'http://localhost:9000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled. 
await execute("Runtime.evaluate", {
  awaitPromise: true, 
  expression: `fetch("http://example.com").then(r => r.text())`, 
  contextId: executionContextId
})

// fails with:
// Uncaught DOMException: Blocked a frame with origin "http://localhost:9000" from accessing a cross-origin frame.
execute("Runtime.evaluate", {
  awaitPromise: true,
  expression: `
    new Promise((resolve, reject) => {
      const iframe = document.createElement("iframe");
      iframe.src = "http://example.com"
      iframe.onload = () => resolve(iframe)
      iframe.onerror = reject;
      document.body.append(iframe)
    }).then(iframe => iframe.contentWindow.document)`,    
  contextId: executionContextId
})


[1] I would have expected universal access to allow me to acess cross origin resources the same way the --disable-web-security flag does - which internally grants universal access

  if (!frame_->GetSettings()->GetWebSecurityEnabled()) {
    // Web security is turned off. We should let this document access
    // every other document. This is used primary by testing harnesses for
    // web sites.
    origin->GrantUniversalAccess();

[2] Running head-full for easier debugging (e.g. seeing the full cors error only printed to the console) - running with --headless doesn't work either.

[3]

  const targets = await fetch("http://localhost:9000/json").then(r => r.json());
  const tab = targets.filter(t => t.type === "page")[0];
  let counter = 0, commands = {}; 
  const w = new WebSocket(tab.webSocketDebuggerUrl);
  await new Promise(resolve => { w.onopen = resolve; })
  w.onmessage = event => {
    const json = JSON.parse(event.data)
    if (commands[json.id]) commands[json.id](json);
    else console.log(json); // event
  }
  
  function execute(method, params) {
    return new Promise((resolve, reject) => {
      const id = counter++;
      commands[id] = ({result, error}) => {
        console.log(method, params, result, error)
        if (error) reject(error);
        else resolve(result);
        // delete commands[id];
      };
      w.send(JSON.stringify({method, id, params}));
    });
  }
  window.execute = execute;
  window.frameId = tab.id;

[4] The correct parameter name is grantUniveralAccess (no s in univeral). Easily validated by passing a value with an incorrect type (expects a bool)

// fails with:
// Failed to deserialize params.grantUniveralAccess - BINDINGS: bool value expected at position 69
await execute("Page.createIsolatedWorld", {frameId, grantUniveralAccess: "true"})
Wes
  • 3,978
  • 4
  • 24
  • 45
Niklas Fasching
  • 1,326
  • 11
  • 15
  • asked yesterday? lol we are working on the same thing! i haven't tried yet but i think we are missing a S (univerSalAccess) – Wes Jan 01 '21 at 00:50
  • small world :). did you see [4] though? I'm pretty certain we're not missing an s and there's a typo in the api – Niklas Fasching Jan 01 '21 at 22:07
  • 1
    sorry for the late answer. the ShouldAllowAccessTo functions here are the ones used for loading frames, fetch, xmlhttp, etc. https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/renderer/bindings/core/v8/binding_security.h;l=52;drc=35804f92097794e21d85b142d6f1096285867e3c and LocalDomWindow (accessing_window) is the Execution Context. I am trying to figure out what that variable is set to from e.g. https://source.chromium.org/chromium/chromium/src/+/master:v8/src/inspector/v8-runtime-agent-impl.cc;l=111;drc=c32c285dc034eef1c6bb53775afac99716826d51 – Wes Jan 08 '21 at 06:57
  • this method is invoked on the isolated world with universal access, https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/renderer/platform/weborigin/security_origin.cc;l=328;drc=38a9bbda8911f7330c528b6df10aae5dea26e608;bpv=1;bpt=1 so, universal access is required on the requesting context only, not the opposite, or on both the execution contexts. so it should work... but it doesn't..... – Wes Jan 09 '21 at 02:30
  • Maybe it's really a bug - I opened a ticket in the chromium bug tracker https://bugs.chromium.org/p/chromium/issues/detail?id=1164675. Let's see what they say. – Niklas Fasching Jan 09 '21 at 15:52
  • they're looking into it :) – Niklas Fasching Jan 14 '21 at 13:21

0 Answers0