Tim
Tim

Reputation: 689

'unsafe-eval' on chrome extension

I am trying to run the following:

chrome.tabs.onCreated.addListener(function (tab){
    if (tab.url.indexOf(".salesforce.com/") != -1 || tab.url.indexOf(".force.com/") != -1) {
        chrome.tabs.executeScript(tab.id, {
            "file": "loadScript.js"
        }, function () {
            console.log("Script Executed .. ");
        });
    } else {
        var wrongTab = chrome.i18n.getMessage("wrongTab");
        console.log(wrongTab);
        alert(wrongTab);
    }
});

Which should (in theory), on page load run the loadScript.js file.... the loadScript.js file is as follows, this should append a file to the running page, not to the background page as it is at the moment:

/* Create a scriipt element in head of HTML and put /soap/ajax/31.0/connection.js in the src  */
var connectJsUrl = "/connection.js";

function loadScript(url, callback) {
    var head = document.getElementsByTagName("head")[0];
    var script = document.createElement("script");
    script.src = url;
    var done = false;
    script.onload = script.onreadystatechange = function() {
        if (!done && (!this.readyState || this.readyState == "loaded" || this.readyState == "complete")) {
            done = true;
            callback();
            script.onload = script.onreadystatechange = null;
            head.removeChild(script);
        }
    };
    head.appendChild(script);
}

loadScript(connectJsUrl, function() {
    console.log("Script Confirmed...")
});

/* Check to see if the file have been appended correctly and works correctly */
var JSFile = "chrome-extension://" + window.location.host + connectJsUrl;
var req = (window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
if (req == null) {
    console.log("Error: XMLHttpRequest failed to initiate.");
};
req.onload = function() {
    try {
        eval(req.responseText);
    } catch (e) {
        console.log("There was an error in the script file.");
    }
};
try {
    req.open("GET", JSFile, true);
    req.send(null);
} catch (e) {
    console.log("Error retrieving data httpReq. Some browsers only accept cross-domain request with HTTP.");
};

I am still a newbie to Chrome Extensions and .js so excuse me if I have made a stupid mistake :)

All I am getting from this is the following: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' chrome-extension-resource:".

Upvotes: 16

Views: 18459

Answers (6)

Steve
Steve

Reputation: 51

Use content script to create invisible element with the following click event handler (someFunction included for completeness):

const someFunction = () => {
    console.log('some text');
}
const div = document.createElement('div');
div.id = 'buttonClickHandler';
div.style.display = 'none';
div.addEventListener('click', () => {
    new Function(div.getAttribute('js'))();
});
document.body.appendChild(div);

On your popup script, set the custom (js) attribute of the div to string representing the code to run and then click it:

const div = document.getElementById('buttonClickHandler');
div.setAttribute('js', 'someFunction();');
div.click();

Manifest v3

"content_security_policy": {
    "extention_pages": "script-src 'self' 'unsafe-inline'"
}

Upvotes: 0

Alexey Vol
Alexey Vol

Reputation: 1930

You build an extension with manifest v3. Target web site has CSP headers in response that protects your extension to run script.

Assume that you want to run some "unsafe" script inside page context script.js:

const foo = new Function('console.log("FOO");');

foo();
  1. NOT working from background/popup:
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
  chrome.scripting.executeScript({
    target: { tabId: tabs[0] },
    files: ['script.js'],
  });
});
  1. Another attempt to run this script immediately via content script:
chrome.scripting.registerContentScripts([
  {
    id: 'script-id',
    matches: ['<all_urls>'],
    runAt: 'document_start',
    js: ['script.js'],
    allFrames: true,
  }
]);
  1. Another attempt to run this script immediately via manifest.json:
{
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "run_at": "document_start",
      "js": ["script.js"],
      "all_frames": true
    }
  ]
}

For all 3 cases as a result you will get:

EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' 'wasm-unsafe-eval' 'inline-speculation-rules' http://localhost:* http://127.0.0.1:*".

WORKING solution is to inject script via content.

  • Add injected script to manifest.json:
{
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "run_at": "document_start",
      "js": ["content.js"],
      "all_frames": true
    }
  ],
  "web_accessible_resources": [
    {
      "matches": ["<all_urls>"],
      "resources": ["script.js"]
    }
  ]
}
  • Inject script.js from content.js:
  const scriptSrc = chrome.runtime.getURL('script.js');
  const scriptTop = document.createElement('script');
  scriptTop.src = scriptSrc;
  documentRoot.appendChild(scriptTop);

Upvotes: 0

Nebula Developments
Nebula Developments

Reputation: 11

After experimenting, I found out that running a web server hosting an html file (that is running your eval) will still work. Then, send the results to the parent window and done.

CLIENT:

var elm = document.createElement('iframe');
elm.src = 'http://localhost:3000'; // I used a basic expressJS server
document.body.appendChild(elm);

SERVER:

const express = require('express');
const socketIO = require('socket.io'); // Optional
const http = require('http');
const path = require('path');


const app = express();
let server = http.createServer(app);
let io = socketIO(server);

const publicPath = path.resolve(__dirname, '../public');
const port = process.env.PORT || 3000;

app.use(express.static(publicPath));

io.on('connection', (client) => {
    console.log('User connected');
});

server.listen(port, (err) => {
    if (err) throw new Error(err);
    console.log(`Server running on port ${port}`);
});

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Test</title>
</head>
<body>
    <script>
        eval(alert('test'))
    </script>

    <!-- Other html.. -->
</body>
</html>



Upvotes: 0

Goutham J.M
Goutham J.M

Reputation: 2194

For Manifest V3

You cannot run code with unsafe eval in manifest v3 , if you are using any bundlers like webpack or vite , you can change the code not to use eval or check package bundle if it contains any eval , here are the list of syntax you are not suppose to use in manifest 3

  • eval()
  • setTimeout()
  • setInterval()
  • Function()
  • setImmediate()
  • execScript()

It is not safe to add content_security_policy with unsafe-eval as site may be prone to XSS attack

But If you are using any wasm code by chance then below config will work to avoid eval for manifest 3

"content_security_policy": {
   "extension_page":"script-src 'self' 'wasm-unsafe-eval'; object-src 'self'"
}

If you are using any iframe add below code also

"content_security_policy": {
   "extension_page":"script-src 'self' 'wasm-unsafe-eval'; object-src 'self'",
   "sandbox":"script-src 'self' 'wasm-unsafe-eval'; object-src 'self'"
}

Upvotes: 7

Imagine Engine
Imagine Engine

Reputation: 67

IMPORTANT

As mentioned before add this to your manifest.json:

"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'"

Make sure you set the "manifest_version" to 2 aka

//this
"manifest_version": 2

Chrome Extentions that work on manifest_version 3 don't support unsafe evals for some security reasons.

Also make sure to reload your extention.

Upvotes: 4

abhilash
abhilash

Reputation: 865

To prevent cross site scripting Google has blocked the eval function.

To solve this add this code to the manifest.json

"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",

Please comment if you need further explanation

Upvotes: 25

Related Questions