benwinding / command-pal

The hackable command palette for the web, inspired by Visual Studio Code.
https://benwinding.github.io/command-pal/docs
MIT License
75 stars 8 forks source link

Sadly CSP need unsafe-nonce to work #13

Open rouilj opened 1 year ago

rouilj commented 1 year ago

Very nice JS library you have here.

I spent the weekend working with it and I like it. Unfortunately I have to change the content security policy to include unsafe-inline to make it work. The diff of my CSP is:

-       "style-src 'self' 'nonce-{nonce}'; "
+       "style-src 'self' 'unsafe-inline'; "

sadly this is kind of deal breaker. I did some searching for svelte and CSP and it seems they did have some issues with it in the past, but they were resolved. It's possible that rebuilding with updated dependencies will fix this. However I am not steeped enough in svelte, npm etc. to try it myself.

Any chance you can look into this and see if it is easily fixable?

rouilj commented 1 year ago

I have been able to run this in dev mode using http://localhost:5005/cp-advanced/local-dev without unsafe-inline by adding the following hashes to the Content-Security-Policy header style-src value.

'sha256-gwaXl9ypsP5M+Pl7IPC4TdkehJgQRvkKmcYSf6NCydU=' 
'sha256-uBf9jI60SnIb3YTm/PeNRoEUOwpLyiOgSOmdm9XR8v8=' 
'sha256-RpNJcpPx5tmGIdPUg0nsHLh99/fW3Gh85m5Kr+izRG8='
'sha256-niGZxRuNLAMQ7wdnrrAJe1tl+OBjlL3f2JEim7bTuAs='
which are used for the following script ids: Script id sha256 sum
svelte-1qhguwm-style sha256-gwaXl9ypsP5M+Pl7IPC4TdkehJgQRvkKmcYSf6NCydU=
svelte-bvn01s-style sha256-uBf9jI60SnIb3YTm/PeNRoEUOwpLyiOgSOmdm9XR8v8=
svelte-1ary9cc-style sha256-RpNJcpPx5tmGIdPUg0nsHLh99/fW3Gh85m5Kr+izRG8=
svelte-ty8x5i-style sha256-niGZxRuNLAMQ7wdnrrAJe1tl+OBjlL3f2JEim7bTuAs=

As an example the header looks like:

Content-Security-Policy:  style-src 'self'  'nonce-123456789112345678921234567890' 'sha256-gwaXl9ypsP5M+Pl7IPC4TdkehJgQRvkKmcYSf6NCydU=' 'sha256-uBf9jI60SnIb3YTm/PeNRoEUOwpLyiOgSOmdm9XR8v8=' 'sha256-RpNJcpPx5tmGIdPUg0nsHLh99/fW3Gh85m5Kr+izRG8=' 'sha256-niGZxRuNLAMQ7wdnrrAJe1tl+OBjlL3f2JEim7bTuAs='

I got the sha256 hashes to print out by adding:

 <script>
      async function hashFromString(name, string) {
      const hash = await crypto.subtle.digest("SHA-256", (new TextEncoder()).encode(string))
      csp = "sha256-" + btoa(String.fromCharCode(...new Uint8Array(hash)));
      console.log(name, csp);
      return csp;
     }
 </script>

at the top of the head section. I found the code above on stack overflow.

To invoke this I modified the append() function in node_modules/svelte/internal/index.mjs to read:

function append(target, node) {
    if ( node.tagName === "STYLE") { hashFromString(node.id, node.innerText)} // added
    target.appendChild(node);
}

I don't know how static these script tags are. Hopefully there is some way to produce and publish the sha256 (sha384 or sha512) hashes as part of the build procedure so they can be published with new releases.

All the svelte examples for setting a style nonce seem to be associated with running svelte it server side. In my case, I am just loading this bundle as a widget and run it client side. Substituting the nonce into the bundle so it can label the style tags seems fragile if doable at all. Also I didn't find an easy way to export the style tags as files that I could just include them using using link tags.

So this may be as good as I can do. I would like to be proved wrong or get an idea of how often the contents change but ...

I'll try this in production and see if these hashes work.

Also I used the following serve.json in the public folder to enable CSP headers:

{
  "headers": [
      {
      "source" : "**/*.html",
      "headers" : [{
              "key" : "Content-Security-Policy",
              "value" : "style-src 'self' 'nonce-123456789112345678921234567890' 'sha256-gwaXl9ypsP5M+Pl7IPC4TdkehJgQRvkKmcYSf6NCydU=' 'sha256-uBf9jI60SnIb3YTm/PeNRoEUOwpLyiOgSOmdm9XR8v8=' 'sha256-RpNJcpPx5tmGIdPUg0nsHLh99/fW3Gh85m5Kr+izRG8=' 'sha256-niGZxRuNLAMQ7wdnrrAJe1tl+OBjlL3f2JEim7bTuAs='"
      }]
      }
  ]
}
rouilj commented 1 year ago

Looks like running npm run build changed all the hashes even though the source hadn't changed. The id's for the hashes remained the same.

rouilj commented 1 year ago

Ugh. Apparently the styles change based on the device. On my android phone I get:

style ID hash
svelte-y4bnn4-style sha256-RrMEicWJlkAPGj4SrFXQJ6sTabOSDFMeB+nD5PjyQ0U=
svelte-1x199rl-style sha256-qnY5wXoZJyTquy8rurowWuPfgN54qzcbs+XgC/hwRrM=
svelte-1dpfclk-style sha256-21OKiJm2F8mOisLGzb5olwSBrd1ZiNYD9x0vL76xtR8=
svelte-tippst-style sha256-rW4lVf+jYeIHsGTLJDMkvXrdsPze/jkkxaaswC44Bp4=

I only have one device to test with. I wonder if IOS or another type of device gets different styles.

This is definitely a showstopper 8-(. Stupid svelte.

[ edit: this might be a caching issue even though remote debugging said the command-pal.js was not cached.]

rouilj commented 1 year ago

Supposedly adding:

    <svelte:options css="external"/>

at the top of each .svelte file is supposed to externalize the styles into a file.

However:

command-pal$ npm run build
> command-pal@0.2.7 build
> rollup -c
src/main.js → public/build/bundle.js...
[!] (plugin svelte) ValidationError: <svelte:options> unknown attribute src/App.svelte
1: <svelte:options tag="my-foobar" immutable={true} css="external"/>

As you can see it accepted the tag and immutable attributes without issue. Not sure what's happening here. Is the version of svelte maybe too old to support the css attribute?

This is the preferred way to load the styles:

Also I found out that there is a cssHash attribute. It can be used to define a function that returns the hash placed after the svelte-##### class name. It takes 4 arguments: hash, css, name, and filename. In theory the hash name can be made constant by returning name IIUC. This would make applying external CSS easier I think as we can predict that

   button.svelte-MobileButton svg {...}

provides enough specificity to override existing styles in the css.

Quips, comments, evasions, questions, or answers?