Hi Astro-Shield Maintainers!
Thanks a lot for building and maintaining Astro-Shield! It has helped me integrating CSP into my static websites.
I recently came across a very specific issue where CSP is at odds with inline style of code syntax highlighting in Astro. Here's the breakdown:
Astro's code syntax highlighting (such as pre-bundled shiki or astro-expressive-code) produces the HTML output where <span>
tokens that contains inline styles.
Here is one example:
<!DOCTYPE html>
<style integrity="sha256-U8bvsLpPRBV22VuGGYvK6SKOwuWuIrN5tvO8jY6oZC8=">.example{font-size:18px}
</style>
<div class="example">Hello!</div>
<pre class="astro-code github-dark"
style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0"
data-language="python"><code><span class="line"><span style="color:#F97583">def</span><span
style="color:#B392F0"> greet</span><span style="color:#E1E4E8">():</span></span>
<span class="line"><span style="color:#79B8FF"> print</span><span
style="color:#E1E4E8">(</span><span style="color:#9ECBFF">"Hello, World!"</span><span
style="color:#E1E4E8">)</span></span>
<span class="line"></span></code></pre>
I use Astro-Shield to generate SRI hashes and also use its Netlify integration to generate _headers
file which, according to my understanding, contains generated CSP headers for each of the pages.
For completeness, my configuration looks like the following:
shield({
securityHeaders: {
enableOnStaticPages: { provider: "netlify" },
contentSecurityPolicy: {
cspDirectives: {
"default-src": "'none'",
"script-src": "'self'",
"style-src": "'self' 'unsafe-inline'",
"img-src": "'self' data:",
"font-src": "'self' data:",
"frame-src": "'self' https://www.youtube-nocookie.com",
"form-action": "'self'",
"frame-ancestors": "'self'",
"base-uri": "'self'",
"worker-src": "'self'",
"manifest-src": "'self'",
"upgrade-insecure-requests": "",
},
},
},
}),
and the generated _headers
(partial):
/example/index.html
content-security-policy: base-uri 'self'; default-src 'none'; font-src 'self' data:; form-action 'self'; frame-ancestors 'self'; frame-src 'self' https://www.youtube-nocookie.com; img-src 'self' data:; manifest-src 'self'; script-src 'none'; style-src 'self' 'sha256-U8bvsLpPRBV22VuGGYvK6SKOwuWuIrN5tvO8jY6oZC8=' 'sha256-xYqUZvluhUpsRy3UdguVrRB9kJjgiW47MPZjSxEi6TI=' 'unsafe-inline'; worker-src 'self'
The only thing to note from the above is this part:
style-src 'self' 'sha256-U8bvsLpPRBV22VuGGYvK6SKOwuWuIrN5tvO8jY6oZC8=' 'sha256-xYqUZvluhUpsRy3UdguVrRB9kJjgiW47MPZjSxEi6TI=' 'unsafe-inline';
Deploying this website (minimal example) on Netlify results in syntax highlighting missing their colors. The browser tells me that 'unsafe-inline'
under style-src
is ignored because SRI hashes (or nonce) exist. That's obvious my website contains <style>
tags in other parts of the page, thus the hash was generated.
Obviously, everything is working as intended. However, I wish that Astro-Shield provides some more granular control via config over how SRI hashes are injected into CSP headers. Particularly
An option to leave
style-src
untouched when generating CSP headers so that my website can use'unsafe-inline'
. For example:shield({ securityHeaders: { enableOnStaticPages: { provider: "netlify" }, contentSecurityPolicy: { cspDirectives: { "default-src": "'none'", "script-src": "'self'", "style-src": "'self' 'unsafe-inline'", "img-src": "'self' data:", "font-src": "'self' data:", "frame-src": "'self' https://www.youtube-nocookie.com", "form-action": "'self'", "frame-ancestors": "'self'", "base-uri": "'self'", "worker-src": "'self'", "manifest-src": "'self'", "upgrade-insecure-requests": "", }, modifyCspDirectives: { // <-- added "script-src": true, "style-src": false, } }, }, }),
I know that 'unsafe-inline'
is not the best settings to use, but using it only for style-src:
but not script-src:
is an acceptable risk for me. (HTTP Observatory also seems to strongly concern more about script-src:
.)
Any tips, suggestions, or workaround is also appreciated.
Pay now to fund the work behind this issue.
Get updates on progress being made.
Maintainer is rewarded once the issue is completed.
You're funding impactful open source efforts
You want to contribute to this effort
You want to get funding like this too