2

I'm trying to make a chrome extension that receives javascript code from a backend and saves it in localStorage (as base64) so I can later inject it as a content script when the right page is loaded, it does work most of the time except there are a few issues... First issue (not that important) is that I cannot access Chrome APIs (like chrome.storage or chrome.runtime.sendMessage), second issue is that it doesn't inject the correct code to child iframes... because location.href returns the URL of the top webpage and I couldn't find a way to access current URL of iframe within the iframe itself.

This is my code so far:

manifest.json

//....
"content_scripts": [{
    "run_at": "document_end",
    "all_frames": true,
    "matches": [
        "<all_urls>"
    ],
    "js": [
        "src/inject/InjectManager.js"
    ]
}],
//...

InjectManager.js:

// Some functions were not included for brevity
chrome.runtime.sendMessage({ action: "get_supported_urls" }, function(supported_urls) {
    let current_url = window.location.href;

    // Check if we support current_url
    let js_code_to_inject = isWebsiteSupported(supported_urls, current_url); // this function returns string that is javascript code.
    if(js_code_to_inject){
        // Append the code to the body
        let script = document.createElement("script");
        script.type = "text/javascript";
        script.innerHTML = js_code_to_inject;

        document.body.appendChild(script);
    }
});

As you see, I'm kind of trying to recreate what chrome already does in manifest.json's "content_script" section because my javascript code is dynamic.

Note: I know this is not allowed on chrome store and such, this extension is not to be shared with anyone.

Thanks for reading. Any help would be greatly appreciated.

Shryder
  • 61
  • 2
  • 5

1 Answers1

1

I cannot access Chrome APIs (like chrome.storage or chrome.runtime.sendMessage)

Your code currently makes a page script, not a content script. For the latter you need to use chrome.tabs.executeScript (see also content script documentation) in the background script.

location.href returns the URL of the top webpage and I couldn't find a way to access current URL of iframe within the iframe itself.

No, what you're describing simply cannot happen, it would be a doomsday-level violation of URL origin security, so something else is happening. For example, your manifest.json doesn't have match_about_blank meaning InjectManager.js doesn't process the dynamically added about:blank frames at all.


manifest.json:

"content_scripts": [{
  "match_about_blank": true,
  .....................

InjectManager.js:

chrome.runtime.sendMessage({ action: 'inject', data: location.href });

background script:

chrome.runtime.onMessage.addListener(({ action, data }, sender, sendResponse) => {
  if (action === 'inject') {
    chrome.tabs.executeScript(sender.tab.id, {
      code: getInjectionCode(data),
      frameId: sender.frameId,
    });
  }
});

Note that some iframes like javascript: or srcdoc: won't run content scripts in Chrome at all so you'll have to process them directly in InjectManager.js as they cannot be injected by executeScript(). For example you can find all iframes by using document.querySelectorAll('iframe') and create DOM script element inside like you do currently but you'll use frameElement.contentDocument instead of document and of course you'll check whether the iframe's real URL (frameElement.contentWindow.location.href) doesn't start with http because frames can be navigated inside without changing their src attribute on the outside. Do the check inside try/catch because accessing an iframe from a different origin will throw.

wOxxOm
  • 65,848
  • 11
  • 132
  • 136
  • Thanks for answering, this helped a ton. I didn't know executeScript existed, it's now working much better. I have one question regarding your last note though @wOxxOm: I don't want my script to get injected into javascript: or srcdoc: iframes, I only want it to run inside "real" iframes that have actual URLs and I need a way to grab that URL to check whether i want to inject into it or not because currently location.href really is returning same URL as the parent website... – Shryder Mar 05 '20 at 16:22
  • Also another thing to note is that the iframe i'm testing this on is empty and has src='javascript:false', logging frameElement.contentWindow.location.href on that iframe returns parent URL aswell (i would like something that returned javascript:false). Am I making sense? – Shryder Mar 05 '20 at 16:22
  • 1
    The web security is based on URL origins. What you see is a same-origin iframe - all those `javascript:`, `about:`, `srcdoc:` frames that don't have a `sandbox` attribute are running in the same origin as the embedding frame/page which is why their URL is the same. – wOxxOm Mar 05 '20 at 16:27
  • is there a way to check for those kind of frames? I don't want to inject into them... – Shryder Mar 05 '20 at 17:49
  • 1
    Simply don't add `match_about_blank`. Chrome won't inject into `javascript:` anyway. To check whether the iframe is same-origin you can also try to access something that belongs to the parent window: `let isSame; try { isSame = !!window.frameElement } catch (e) {}` – wOxxOm Mar 05 '20 at 18:36