Cannot inject a JavaScript file because of a CSP limitations

I’m having problems injecting a specific library - Mermaid, ever since its build v9.3.0 outlined in details here https://github.com/mermaid-js/mermaid/issues/5378

Essentially the problem is that from v9.3.0 they have introduced something in their build that triggers the following error when I try to inject the library:

Is there a way to somehow tune the CSP specifically employed when you are injecting scripts?

That looks like the Function constructor: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/Function

Which is forbidden since it allows remote code execution.

Use your IDE to look for Function( in the source code of the library. It’s likely that they or one of the theirs dependencies are using it (and probably shouldn’t).

See also:

I noticed that too, but just by looking at it, it turns out that both the v9.2.2 that works, and the v9.3.0 that introduced the change have roughly similar usage of that constructor, and on that line 23 in particular. Of course it’s hard tell with 100% certainty just by looking at a minified code, but the point being is that it seems highly unlikely that the Mermaid team is going to fix their own build now.

Not sure how is that linter issue that you pointed out is related in case I am getting a runtime error?

That linter is used by Firefox addons store to verify your addon when you upload it, so it will give you warning.

You can test it locally by running web-ext lint in your source code folder (you’ll need to have web-ext installed).

Maybe they didn’t used it before so it was trimmed out by Webpack… (are you using Webpack?).

If you are using Webpack, you could use it to alter the created build and remove/replace these constructors.

For example, I’ve wrote a Webpack plugin that does this for some of the forbidden API:

// since many libraries use unsafe forbidden API, we use this plugin to remove it
class FirefoxLinterHotfix {
  apply(compiler) {
    const replacements = [
      // replacing with safe alternative
      {search: `Function('return this')`, replaceValue: `(() => globalThis)`},
      {search: `Function("return this")`, replaceValue: `(() => globalThis)`},
      // these are NOT used in Firefox build, so we replace it with empty function
      {search: `iframeDocument.write`,    replaceValue: `(() => ({}))`},
      {search: `documentClone.write`,     replaceValue: `(() => ({}))`},
    ];
    compiler.hooks.compilation.tap('LinterHotfix', (compilation) => {
      compilation.hooks.processAssets.tap({ name: 'LinterHotfix', stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL }, (assets) => {
        for (const [name, source] of Object.entries(assets)) {
          if (name.endsWith('.js')) {
            let content = source.source();
            const original = content;
            for (const {search, replaceValue} of replacements) {
              if (typeof content === 'string') {
                content = content.replaceAll(search, replaceValue);
              }
            }
            if (content !== original) {
              console.log(chalk.bgGreen.black('\n' +
                name + '\n' +
                replacements.map(({search}) => original.includes(search) ? search : null).filter(Boolean).filter(unique()).join('\n')
              ));
              compilation.updateAsset(name, new webpack.sources.RawSource(content));
            }
          }
        }
      });
    });
  }
}

To use it, simply add “new FirefoxLinterHotfix()” in the “plugins” array.

Ok, so I literally replaced Function("return this") with (() => globalThis) in the minified file and it worked, even though the exact same statement Function("return this") was present in v9.2.2 (the version that worked without issues).

1 Like

I still don’t know if you are using Webpack :slight_smile:, but if you do, there is one more line in my config that could help in this situation:

    node: {
      global: false,    // this will remove Webpack polyfills for NodeJS, like "new Function('return this')()"
    },

But I don’t remember any details, but I think it helped remove some of those function constructors, but eventually I had to write a plugin that removes also those 3rd party one.

Thanks for the config, but no, I am not using Webpack as I am not building my own code base as one giant bundle that includes all of my third-party deps. Instead I have a ‘build’ script that simply npm installs and in some cases rolls up (using rollup) third-party deps (in case they are composed of multiple deps) and stores the outputs into my vendor folder.

The problem is with third-party library builds that I have to literally patch myself during my ‘build’ time, for things that they do not intend to fix, because in that case the source of the bundle cannot be compared and verified by the reviewer. But in any case it will be yet another edge case in my notes to the reviewer.

Oh, that sounds terrible :frowning:.

It’s best to ask the library devs to remove or replace the function constructor.

I’m pretty sure it’s some legacy technology that shouldn’t be used anymore (due to CSP issue) and can be replaced with the “globalThis”.