Skip to content
This repository has been archived by the owner on Nov 16, 2023. It is now read-only.

Handling when 'unsafe-eval' is not allowed in the site's Content Security Policy #27

Closed
kawwong opened this issue Jan 30, 2019 · 3 comments
Assignees
Labels
bug Something isn't working
Milestone

Comments

@kawwong
Copy link
Contributor

kawwong commented Jan 30, 2019

Navigating to https://github.com causes the extension to error:

Uncaught 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 github.githubassets.com".
@cn894
Copy link
Member

cn894 commented Feb 6, 2019

This is caused by the current CSP, which allows .eval() calls, as well as inlining scripts into the page.

The last .eval() call was removed from the codebase with the removal of MezzuriteInspector.js in #34. However, to be able to remove 'unsafe-eval' from the CSP and have the extension work properly, the code under inject/ and content/ need to change to be compliant with the CSP sans 'unsafe-eval' or we may need to attempt enabling the hashcode based method for allowing inline JS: https://developer.chrome.com/extensions/contentSecurityPolicy#relaxing-inline-script

I am leaning towards the former, but more research is required.

@cn894
Copy link
Member

cn894 commented Feb 7, 2019

Alright. After much sleuthing around, this issue is a whole lot simpler than I originally thought. When inspecting the output of our webpack build, say dist/background.bundle.js, we can see that eval() calls are being made! This is why we are not being CSP compliant in our bundles, even though we are compliant in our source code. See below:

PS C:\Users\caabreut\Source\Repos\Mezzurite-DevTools> Get-Content .\dist\background.bundle.js | Select-String -Pattern eval

eval("__webpack_require__.r(__webpack_exports__);\nfunction _classCallCheck(instance, Constructor) { if (!(instance
instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction
_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor)
descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction
_createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype,
protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nvar
BackgroundController =\n/*#__PURE__*/\nfunction () {\n  function BackgroundController() {\n    _classCallCheck(this,
BackgroundController);\n\n    this.connections = {};\n    this.dataQueue = {};\n  }\n\n
_createClass(BackgroundController, [{\n    key: \"init\",\n    value: function init() {\n      var _this = this;\n\n
   chrome.runtime.onConnect.addListener(function (port) {\n        var devToolsPageListener = function
devToolsPageListener(message, sender, sendResponse) {\n          // The original connection event doesn't include the
tab ID of\n          // the DevTools page, so it needs to be sent explicitly\n          console.log('BG: Received a
message!');\n          console.log(message);\n\n          if (message.action === 'init') {\n
console.log('BG: Received an init message');\n            _this.connections[message.tabId] = port;\n\n
while (_this.dataQueue[message.tabId].length > 0) {\n
_this.connections[message.tabId].postMessage(_this.dataQueue[message.tabId].shift());\n            }\n          }\n
    }; // Listen to messages sent from the DevTools page\n\n\n
port.onMessage.addListener(devToolsPageListener);\n        port.onDisconnect.addListener(function (port) {\n
port.onMessage.removeListener(devToolsPageListener);\n          var tabs = Object.keys(_this.connections);\n\n
 for (var i = 0, len = tabs.length; i < len; i++) {\n            if (_this.connections[tabs[i]] === port) {\n
    delete _this.connections[tabs[i]];\n              break;\n            }\n          }\n        });\n      }); //
Receive message from content scripts and relay them to the devTools page\n      // for the current tab\n\n
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {\n        // Messages from content
scripts should have sender.tab set\n        if (sender.tab != null) {\n          var tabId = sender.tab.id;\n\n
  if (request.action === 'timing') {\n            if (tabId in _this.connections) {\n              while
(_this.dataQueue[tabId].length > 0) {\n
_this.connections[tabId].postMessage(_this.dataQueue[tabId].shift());\n              }\n\n
_this.connections[tabId].postMessage(request);\n            } else {\n              console.log('Tab not found in
connection list.'); // Save data for when it does connect\n\n              if (!(tabId in _this.dataQueue)) {\n
        _this.dataQueue[tabId] = [];\n              }\n\n              _this.dataQueue[tabId].push(request);\n
   }\n          } else if (request.action === 'mezzuriteFound') {\n            _this.dataQueue[tabId] = [];\n
}\n        } else {\n          console.log('sender.tab not defined.');\n        }\n\n        return true;\n      });\n
   }\n  }]);\n\n  return BackgroundController;\n}();\n\n/* harmony default export */ __webpack_exports__[\"default\"]
= (BackgroundController);\n\n//# sourceURL=webpack:///./src/background/BackgroundController.js?");
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var
_BackgroundController__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./BackgroundController */
\"./src/background/BackgroundController.js\");\n\nvar backgroundController = new
_BackgroundController__WEBPACK_IMPORTED_MODULE_0__[\"default\"]();\nbackgroundController.init();\n\n//#
sourceURL=webpack:///./src/background/index.js?");

@cn894
Copy link
Member

cn894 commented Feb 7, 2019

This is caused by Webpack's default source-mapping behavior, which is configured by the devtool field in config: Docs.

Setting it to something else, like source-map, which is coincidentally very helpful for debugging purposes during local development, will prevent Webpack from generating CSP non-compliant code.

Removing that non-compliant generated code will then allow us to remove the 'unsafe-eval' portion of our CSP string in manifest.json, which should resolve this issue.

I'll send out a PR.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants