dealing with unsafe-eval and regeneratorruntime

2021-07-15

 | 

~3 min read

 | 

512 words

One of the very first bugs I remember running into was about regeneratorRuntime. At the time I had very little understanding of what the regeneratorRuntime does. Even looking at it today, the regenerator project still confuses me. It seems to be a polyfill for async/await and generator functions to ES5.

Still, the name’s catchy, so when I ran into an unsafe-eval error this morning that I was able to trace back to regeneratorRuntime, I felt like I’d been reunited with an old friend.

This time around, the issue wasn’t whether or not regeneratorRuntime was defined or not, but that something unsafe was happening.

It turns out that if you search the web, this is a common experience. It’s the result of a failsafe that generates a new value through an invocation of Function(), which constitutes an unsafe-eval.

The authors of regeneratorRuntime are also well aware of this problem as they have included a healthy comment detailing the situation:

regenerator-runtime/runtime.js
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)
}

In my case, however, my hands were a bit tied. It wasn’t my configuration that was causing the problem but upstream.

The solution to this problem was to ensure that I never hit that block of code. How? One way would have been to modify my bundler to no longer force strict mode. Per the discussions on the related TSDX issue, this seemed to be discouraged by Rollup.

An alternative exists, however! Avoid the catch altogether by defining, up front, a variable on the globalThis object for regeneratorRuntime.

That’s exactly what I did.

The first step was finding my app’s entry point, which in my case was an App.ts file, and adding the following lines:

App.ts
import Application from '@ember/application';
import Resolver from 'ember-resolver';
import loadInitializers from 'ember-load-initializers';
import config from './config/environment';

+ /**
+  * The globalThis.regeneratorRuntime = undefined addresses a potentially unsafe-eval problem
+  * Source: https://github.com/facebook/regenerator/issues/378#issuecomment-802628326
+  * Date: July 14, 2021
+  */
+ globalThis.regeneratorRuntime = undefined;

export default class App extends Application {
  modulePrefix = config.modulePrefix;
  podModulePrefix = config.podModulePrefix;
  Resolver = Resolver;
}

loadInitializers(App, config.modulePrefix);

With this in place, my app was able to run successfully once again. Importantly, I did not need to modify my Content-Security Policies to allow unsafe-evals or modify my bundler! It may be “misconfigured” (by forcing strict mode), but it’s also the recommended path by Rollup!


Hi there and thanks for reading! My name's Stephen. I live in Chicago with my wife, Kate, and dog, Finn. Want more? See about and get in touch!