Skip to content

Commit bb47a27

Browse files
authored
fix: allow Plausible inline init script via CSP nonce (#182)
1 parent 13a32a6 commit bb47a27

2 files changed

Lines changed: 29 additions & 7 deletions

File tree

lib/flick_web/components/layouts/root.html.heex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<!-- Privacy-friendly analytics by Plausible -->
1515
<script async src="https://plausible.io/js/pa-9IEM5uoRIpI0OwkV4Qqkv.js">
1616
</script>
17-
<script>
17+
<script nonce={@csp_nonce}>
1818
window.plausible=window.plausible||function(){(plausible.q=plausible.q||[]).push(arguments)},plausible.init=plausible.init||function(i){plausible.o=i||{}};
1919
plausible.init()
2020
</script>

lib/flick_web/router.ex

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,26 @@ defmodule FlickWeb.Router do
1212
plug :put_root_layout, html: {FlickWeb.Layouts, :root}
1313
plug :protect_from_forgery
1414

15-
# Tailwind uses SVG data URLs for icons,
16-
# so we need to allow them with `img-src`.
17-
# https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
15+
# Our Content Security Policy. Notes:
16+
#
17+
# - `img-src` allows `data:` URLs because Tailwind uses SVG data URLs for
18+
# icons.
19+
# - `style-src` allows `'unsafe-inline'` to avoid web console issues with
20+
# Phoenix Storybook.
1821
#
19-
# To avoid web console issues with Phoenix Storybook we've added
20-
# `style-src 'self' 'unsafe-inline'` which feels unfortunate and
21-
# might be reconsidered.
22+
# `put_csp_nonce` below augments this static policy with a per-request nonce
23+
# so the inline Plausible analytics `<script>` in the root layout is allowed
24+
# without opening `script-src` up to `'unsafe-inline'`. We keep the full
25+
# policy here (rather than building it entirely in the plug) so Sobelow's
26+
# `Config.CSP` check can still verify a CSP is present.
27+
#
28+
# https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
2229
plug :put_secure_browser_headers, %{
2330
"content-security-policy" =>
2431
"default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self' https://plausible.io; connect-src 'self' https://plausible.io"
2532
}
33+
34+
plug :put_csp_nonce
2635
end
2736

2837
pipeline :admin do
@@ -33,6 +42,19 @@ defmodule FlickWeb.Router do
3342
plug :accepts, ["json"]
3443
end
3544

45+
# Generates a per-request nonce, assigns it to the conn (so the root layout can
46+
# stamp it onto the inline Plausible `<script>`), and splices it into the
47+
# `script-src` directive of the CSP header set by `put_secure_browser_headers`.
48+
defp put_csp_nonce(conn, _opts) do
49+
nonce = 18 |> :crypto.strong_rand_bytes() |> Base.encode64()
50+
51+
conn
52+
|> assign(:csp_nonce, nonce)
53+
|> update_resp_header("content-security-policy", "", fn csp ->
54+
String.replace(csp, "script-src ", "script-src 'nonce-#{nonce}' ")
55+
end)
56+
end
57+
3658
scope "/" do
3759
storybook_assets()
3860
end

0 commit comments

Comments
 (0)