Sapper: Update sapper docs to conform with Helmet v4

Created on 6 Aug 2020  路  9Comments  路  Source: sveltejs/sapper

A new major version of Helmet was released a couple days ago and the Sapper documentation about Content Security Policy needs to be adapted to it. One thing that changed for example is that "you can no longer use a function inside a directive, e.g. for nonce generation".

I was not able to set up a working nonce generation for my project to avoid CSP issues, there is also a recent issue that might be relevant: https://github.com/helmetjs/helmet/issues/237

Most helpful comment

I was able to get CSP working after all the related issues were fixed and merged.

I just wanted to leave my helmet configuration here, in case anyone doesn't have the time to figure this all out alone:

app
  .use(
    helmet({
      contentSecurityPolicy: {
        directives: {
          defaultSrc: ["'self'"],
          // Has to be unsafe-eval because %sapper.scripts% uses eval
          // @ts-expect-error
          scriptSrc: ["'self' 'unsafe-eval'", (_req, res) => `'nonce-${res.locals.nonce}'`],
          // Has to be unsafe-inline currently, because svelte-awesome & svelte-image sets inline style
          styleSrc: ["'self' 'unsafe-inline'"],
          // data: needed for svelte-image placeholders and svelte-awesome icons
          imgSrc: ["'self'", 'data:'],
          // localhost:10000 needed by __sapper__ itself
          connectSrc: ["'self'", 'http://localhost:10000'],
        },
      },
    }),
    compression({ threshold: 0 }),
    sirv('static', { dev }),
    sapper.middleware(),
  )

All 9 comments

It sounds to me like this is fixed in Helmet 4.1.0 based off the link above. Please let me know if that's wrong or there are still changes needed to the docs to demonstrate the latest version of Helmet

Yes, Helmet 4.1.0 reintroduced functions for nonce generation, but there are more issues since it is way more strict.

I just tried to make it work again, no chance.

The docs need to adapted to at least set default-src, otherwise running into Error: Content-Security-Policy needs a default-src but none was provided. You can see here in the changelog: https://github.com/helmetjs/helmet/blob/master/CHANGELOG.md#added-1

Other than that, I don't see the nonce being set in the DOM, just <script nonce> without any value.

And this is my console output after having script-src and default-src set:

2020-09-04_22-10

Some errors I can guess how to fix, others not.

I think "Refused to apply the inline style" message would be fixed by https://github.com/sveltejs/sapper/pull/1232. I just did another pass at reviewing that one, so hopefully we can merge it soon

The others look like they're mostly not things that Sapper controls. You might need https://github.com/sveltejs/sapper/pull/1249 for those.

It seems like you'd run into these problems regardless of Helmet version. Are you okay if we leave this closed since there are already other issues tracking those problems?

We can keep it closed, no problem!
Thank you for pushing these CSP issues and pull requests forward, would be great to get the implementation finished up.

I was able to get CSP working after all the related issues were fixed and merged.

I just wanted to leave my helmet configuration here, in case anyone doesn't have the time to figure this all out alone:

app
  .use(
    helmet({
      contentSecurityPolicy: {
        directives: {
          defaultSrc: ["'self'"],
          // Has to be unsafe-eval because %sapper.scripts% uses eval
          // @ts-expect-error
          scriptSrc: ["'self' 'unsafe-eval'", (_req, res) => `'nonce-${res.locals.nonce}'`],
          // Has to be unsafe-inline currently, because svelte-awesome & svelte-image sets inline style
          styleSrc: ["'self' 'unsafe-inline'"],
          // data: needed for svelte-image placeholders and svelte-awesome icons
          imgSrc: ["'self'", 'data:'],
          // localhost:10000 needed by __sapper__ itself
          connectSrc: ["'self'", 'http://localhost:10000'],
        },
      },
    }),
    compression({ threshold: 0 }),
    sirv('static', { dev }),
    sapper.middleware(),
  )

I was able to get CSP working after all the related issues were fixed and merged.

I just wanted to leave my helmet configuration here, in case anyone doesn't have the time to figure this all out alone:

app
  .use(
    helmet({
      contentSecurityPolicy: {
        directives: {
          defaultSrc: ["'self'"],
          // Has to be unsafe-eval because %sapper.scripts% uses eval
          // @ts-expect-error
          scriptSrc: ["'self' 'unsafe-eval'", (_req, res) => `'nonce-${res.locals.nonce}'`],
          // Has to be unsafe-inline currently, because svelte-awesome & svelte-image sets inline style
          styleSrc: ["'self' 'unsafe-inline'"],
          // data: needed for svelte-image placeholders and svelte-awesome icons
          imgSrc: ["'self'", 'data:'],
          // localhost:10000 needed by __sapper__ itself
          connectSrc: ["'self'", 'http://localhost:10000'],
        },
      },
    }),
    compression({ threshold: 0 }),
    sirv('static', { dev }),
    sapper.middleware(),
  )

Hello @mhatvan
Did you test it on safari ?

Nope, Chromium only so far.

I was able to get CSP working after all the related issues were fixed and merged.

I just wanted to leave my helmet configuration here, in case anyone doesn't have the time to figure this all out alone:

app
  .use(
    helmet({
      contentSecurityPolicy: {
        directives: {
          defaultSrc: ["'self'"],
          // Has to be unsafe-eval because %sapper.scripts% uses eval
          // @ts-expect-error
          scriptSrc: ["'self' 'unsafe-eval'", (_req, res) => `'nonce-${res.locals.nonce}'`],
          // Has to be unsafe-inline currently, because svelte-awesome & svelte-image sets inline style
          styleSrc: ["'self' 'unsafe-inline'"],
          // data: needed for svelte-image placeholders and svelte-awesome icons
          imgSrc: ["'self'", 'data:'],
          // localhost:10000 needed by __sapper__ itself
          connectSrc: ["'self'", 'http://localhost:10000'],
        },
      },
    }),
    compression({ threshold: 0 }),
    sirv('static', { dev }),
    sapper.middleware(),
  )

Thanks for this, it really saved me a lot of headaches!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

keyvan-m-sadeghi picture keyvan-m-sadeghi  路  4Comments

mylastore picture mylastore  路  3Comments

Rich-Harris picture Rich-Harris  路  3Comments

Snugug picture Snugug  路  4Comments

matt3224 picture matt3224  路  4Comments