How to set Content Security Policy for useGLTFs

Hi guys,

I want to load a 3d model from aws S3 bucket using useGLTF, it is working well when I run it in local. The code I use is:

  const url = `api/v1/test/models/${name}`;
  const { nodes, materials } = useGLTF(url);

but when I run build file in local, it gives me the compileError like this

Uncaught (in promise) CompileError: WebAssembly.instantiate(): Refused to compile or instantiate WebAssembly module because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self'"
 


I manually added unsafe-eval in meta tag like this

<html lang="en">
  <head>
    <meta http-equiv="Content-Security-Policy" content="default-src 'self';base-uri 'self';block-all-mixed-content;font-src 'self' https: data:;form-action 'self';frame-ancestors 'self';img-src 'self' data:;object-src 'none';script-src 'self' 'unsafe-eval';script-src-attr 'none';style-src 'self' https: 'unsafe-inline';upgrade-insecure-requests">
    <meta charset="utf-8" />
........

And npm run build, I checked index.thml file in build file, unsafe-eval is there , same with the above code. but when I run build file , it still gives me the same error. So UseGLTF can’t load the model. I was wondering how I could solve this Content Security Policy for useGLTF. Thanks in advance.

I think the error messages contain the information you’ll need here. Your CSP must include:

  • script-src 'self' 'unsafe-eval';
  • connect-src 'self' https://www.gstatic.com/;

Thank you so much, I will try again

Could you show me where I should add this CSP. I added this in meta tag in index.html, build it again, but it still no unsafe-eval. and I try to use the code below:

tconst { override } = require("customize-cra");
const cspHtmlWebpackPlugin = require("csp-html-webpack-plugin");

const cspConfigPolicy = {
  "default-src": "'self'",
  "base-uri": "'self'",
  "object-src": "'none'",
  "script-src": "'self' 'unsafe-eval'",
  "style-src": "'self' https://fonts.googleapis.com",
  "font-src": "'self' https: data:",
  "connect-src": "'self' https://www.gstatic.com",
};

function addCspHtmlWebpackPlugin(config) {
  if (process.env.NODE_ENV === "production") {
    config.plugins.push(new cspHtmlWebpackPlugin(cspConfigPolicy));
  }

  return config;
}

module.exports = {
  webpack: override(addCspHtmlWebpackPlugin),
};

it gives me the new error and csp does not contain unsafe-eval.


Thank you so mcuh.

The error above has a different message, and mentions different rules that must be added to your CSP:

  • style-src 'self' 'unsafe-inline';

In general when adding a CSP, you’ll get a lot of these errors, and you need to read each one to see what is missing.

The later screenshot is of the HTTP Response Headers — if you’re using a <meta /> tag for the CSP then it will not appear in that area, only in the HTML. You can choose whether to specify the CSP with HTTP Response Headers or with a <meta /> tag.

Thank you for your reply.
I added it to CSP

const cspConfigPolicy = {
  "default-src": "'self'",
  "base-uri": "'self'",
  "object-src": "'none'",
  "script-src": "'self' 'unsafe-eval'",
  // "style-src": "'self' https://fonts.googleapis.com 'unsafe-inline'",
  "style-src": "'self' 'unsafe-inline'",
  //"font-src": "'self' https: data:",
  "connect-src": "'self' https://www.gstatic.com",
};

it gives the error

And I even tried to delete google font but the style-src always gives me different errors :sob:.

Hello everyone,

I am trying to use useGLTF for an ecommerce. (Shopify Hydrogen).

Apparently adding unsafe-inline / unsafe-eval to the CSP makes it vulnerable to XSS attacks.

How could we possibly use useGLTF with a safe CSP please ?

You’ll have to identify what code or URL breaks the CSP – it could be any number of causes. If you try loading a glTF model without draco compression, does that work?

Thank you very much for replying. :slightly_smiling_face:

Yes, when I disable Draco and MeshOptDecoder, this indeed work. But, especially in case of an e-commerce that makes it pretty much obsolete without any compression. I am a beginner so might miss something …

While I don’t think it’s a framework specific thing, I did open a discussion on the hydrogen repo’ just in case it can bring more context: Fails to load 3D model / Content Security Policy · Shopify/hydrogen · Discussion #1313 · GitHub

These decoders require WebAssembly, and it looks like a CSP will block WebAssembly unless unsafe-eval is enabled. There’s a proposal to improve how CSP handles this but I’m not sure of the status on that proposal.

In the meantime, it’s theoretically possible to compile Draco or Meshopt decoders (from source C/C++) to JavaScript, but these projects are outside the scope of the three.js project, and I don’t know how easy or difficult that would be.

Finally, the Meshopt library includes a slower plain-JS reference decoder:

It should be possible to use that instead of the WASM decoder. In plain three.js you’d do that with the loader.setMeshoptDecoder( ... ) function, I’m not sure about useGLTF.

1 Like

This was incredibly helpful. Thank you a lot for your inputs.

As you said, I did implement the apparently slower MeshOptDecoder in plain JS.
I also had to add this line somewhere in my useGLTF hook to use only the JS Draco decoder.
“dracoLoader.setDecoderConfig({type: ‘js’}); // Use JavaScript implementation”

This got rid of the unsafe-eval problem, but introduced a new one related to the CSP:

“Refused to create a worker from ‘blob:’ because it violates the following Content Security Policy directive: “default-src ‘self’ ‘nonce-8472a3a8e8533297d4807fe73f0fda79’ ”. Note that ‘worker-src’ was not explicitly set, so ‘default-src’ is used as a fallback.”
“Refused to create a worker from ‘blob:http://localhost:3000/f5a17f0e-3c0b-4584-a35a-1d310442d4ee’ because it violates the following Content Security Policy directive: “default-src ‘self’ ‘nonce-8472a3a8e8533297d4807fe73f0fda79’ https://cdn.shopify.com https://shopify.com”. Note that ‘worker-src’ was not explicitly set, so ‘default-src’ is used as a fallback.”

It is pointing the DRACOLoader.js file which apparently create a worker. (Not sure I understand all of this fully, if any.)
“const worker2 = new Worker(this.workerSourceURL);”

One way to get rid of this is to add: " workerSrc: [‘blob:’] " to the CSP but we are getting back to the risks it introduces when we put this line in the Content Security Policy.

Still fuzzy for me at this point, I’m a bit of a newbie but I am now trying to figure out how to make it work without the " workerSrc: [‘blob:’] " in the CSP.