mocha: content_security_policy - EvalError: Refused to evaluate a string

The problem: I’ve had to add the following line to a chrome extension manifest file in order for the latest version of Mocha to work: "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self';",

Without this line, mocha scripts fails to run with the following error:

mocha.js:13165 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 'self' blob: filesystem:".

    at Function (<anonymous>)
    at mocha.js:13165
    at createCommonjsModule (mocha.js:16)
    at mocha.js:12456
    at mocha.js:4
    at mocha.js:5

The mentioned workaround was found here: https://stackoverflow.com/a/48047578 but ideally we don’t want to meddle with the content_security_policy

The error seems related to the following piece of code in mocha.js :

	  // In sloppy mode, unbound `this` refers to the global object, fallback to
	  // Function constructor if we're in global strict mode. That is sadly a form
	  // of indirect eval which violates Content Security Policy.
	  function () {
	    return this;
	  }() || Function("return this")());

This looks like a regression in the newer versions of Mocha as this issue doesn’t occur after downgrading to Mocha 8.1.3 .

What I’ve tried: Experimented with different devtool settings including "cheap-module-source-map" and false

How I’m running Mocha: Slightly differently because of testing a chrome extension with a specific page. But the core idea works in other projects and shouldn’t be related to the described error.

mocha.setup("bdd");
mocha.timeout(8000);
// set up all the relevant tests
mocha.run()

How I’m working around this at the moment: Downgraded to Mocha 8.1.3

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 2
  • Comments: 17 (15 by maintainers)

Most upvoted comments

I don’t know the root cause, but we introduced it in https://github.com/mochajs/mocha/commit/12db9dbc9dd3b0ec31c182d0e41a6ec615735401 after I tracked it via git bisect.

@goatonabicycle @snoack thanks for your support. I’m willing to solve this issue, but you have to take the lead. I lack the knowledge and will have to ask questions.

Unfortunately we haven’t dropped the support of IE11. But we started using modern Javascript and transpiling since Mocha v8.1.0. So I’m surprised that v8.1.3 is working as expected. Whatever we will change, Mocha has to keep supporting lovely IE11.

If it’s acceptable to drop support for Internet Explorer 11 […]

I’m afraid no.

Since 12db9db is the first commit that made use of async/await this change caused the regression here.

This commit landed in v8.2.0, and would explain why v8.1.3 is still working. I will have to check wether we haven’t used async/await before.

Do you have any other suggestions, please? @outsideris ?

This issue keeps us for a while from updating to any version of Mocha newer than 8.1.3, and I assume that applies to everyone who uses Mocha to test a browser extension. Right now, we could as a workaround relax the content-security-policy in the extension’s manifest.json. But we don’t want to run our tests in an environment that is unnecessarily different from the production environment. Even more importantly, with manifest v3, this will no longer be an option. Consequently, if this issue is not being addressed, it means that Mocha is no longer a solution for testing browser extensions, and anyone using it for this purpose should start looking for alternatives.

This weekend, probably.

Thanks @dfkaye for your feedback. @snoack your proposition sounds very promising, I will check your PR 👍 .

In case we want to build a second bundle for modern non-IE11 browsers

There seems to be another option. If we update regenerator-runtime to the latest version, it uses a different approach to set a variable in the global scope:

try {
  regeneratorRuntime = runtime;
} catch (accidentalStrictMode) {
  // This module should not be running in strict mode, so the above
  // assignment should always work unless something is misconfigured. Just
  // in case runtime.js accidentally runs in strict mode, we can escape
  // strict mode using a global Function call. This could conceivably fail
  // if a Content Security Policy forbids using Function, but in that case
  // the proper solution is to fix the accidental strict mode problem. If
  // you've misconfigured your bundler to force strict mode and applied a
  // CSP to forbid Function, and you're not willing to fix either of those
  // problems, please detail your unique predicament in a GitHub issue.
  Function("r", "regeneratorRuntime = r")(runtime);
}

This will still fail in strict mode without unsafe eval. But now we can just add var regeneratorRuntime; to the top of the generated code (with Rollup’s output.intro setting). That way regeneratorRuntime = runtime is no longer an implicit definition of a global variable (which fails in strict mode), but it becomes just a reassignment of an existing variable which never fails, and so the code in the catch block (which requires unsafe eval) is never reached.

This will give us a bundle that will work both on Internet Explorer 11, and in environments that don’t allow unsafe eval.

As @goatonabicycle concluded above, the offender here is regenerator-runtime. Babel automatically includes regenerator-runtime into the generated code when transpiling any generator functions or async functions. Since 12db9db is the first commit that made use of async/await this change caused the regression here.

If it’s acceptable to drop support for Internet Explorer 11 (which is the only browser listed in .browserslistrc that doesn’t have native generator support), all it would take to fix this bug is telling Babel to just not transpile generator functions:

diff --git a/rollup.config.js b/rollup.config.js
index 4b4f3494..5fae0247 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -43,5 +43,6 @@ const config = {
               version: 3,
               proposals: false
-            }
+            },
+            exclude: ["transform-regenerator"]
           }
         ]

Note that async functions will still work on browser that lack native support as long as they support generators.

@juergba, what do you think?

Hello.

So, this issue was investigated with an eye on seeing how to help out or at least get some direction.

Like @outsideris mentioned, a git bisect shows that commit https://github.com/mochajs/mocha/commit/12db9dbc9dd3b0ec31c182d0e41a6ec615735401 does introduce the code block mentioned above (searching for “sloppy” in the build output to quickly find it)

It seems like that block of code gets indirectly introduced by regenerator-runtime. Which seems to tie in with the global setup stuff mentioned in this release: https://github.com/mochajs/mocha/releases/tag/v8.2.0

Relevant to this discussion is also this post which seems to very closely mirror this exact issue we’re experiencing.

@boneskull Sorry for the random ping but would you perhaps be able to add some insight into this?

Thank you so much!