svelte/1: Add CSP nonce parameters

This commit is contained in:
Christian Wesselhoeft 2026-04-10 12:58:49 -06:00
parent 75246ee285
commit e7b0c3ff04
No known key found for this signature in database
2 changed files with 84 additions and 2 deletions

View file

@ -45,6 +45,18 @@ defmodule LiveSvelte do
doc: "Class to apply to the Svelte component",
examples: ["my-class", "my-class another-class"]
attr :csp_nonce, :string,
default: nil,
doc: "A Content-Security-Policy nonce for the generated <script> and <style> tags"
attr :csp_script_nonce, :string,
default: nil,
doc: "A Content-Security-Policy nonce for the generated <script> tag"
attr :csp_style_nonce, :string,
default: nil,
doc: "A Content-Security-Policy nonce for the generated <style> tag"
attr :ssr, :boolean,
default: true,
doc: "Whether to render the component via NodeJS on the server",
@ -124,6 +136,8 @@ defmodule LiveSvelte do
streams_diff = calculate_streams_diff(assigns, init or dead)
csp_attrs = if nonce = assigns.csp_nonce, do: [nonce: nonce]
assigns =
assigns
|> assign(:init, init)
@ -134,9 +148,17 @@ defmodule LiveSvelte do
|> assign(:use_diff, use_diff)
|> assign(:props_diff, props_diff)
|> assign(:streams_diff, streams_diff)
|> assign(
:csp_script_attrs,
csp_attrs || if(nonce = assigns.csp_script_nonce, do: [nonce: nonce], else: [])
)
|> assign(
:csp_style_attrs,
csp_attrs || if(nonce = assigns.csp_style_nonce, do: [nonce: nonce], else: [])
)
~H"""
<script>
<script {@csp_script_attrs}>
<%= raw(@ssr_render["head"]) %>
</script>
<div
@ -154,7 +176,7 @@ defmodule LiveSvelte do
>
<div id={"#{@svelte_id}-target"} data-svelte-target>
{raw(@ssr_render["head"])}
<style>
<style {@csp_style_attrs}>
<%= raw(@ssr_render["css"]["code"]) %>
</style>
{raw(@ssr_render["html"])}

60
test/csp_nonce_test.exs Normal file
View file

@ -0,0 +1,60 @@
defmodule LiveSvelte.CspNonceTest do
use ExUnit.Case, async: true
defp render_html(opts) do
%{
__changed__: nil,
socket: nil,
name: "TestComponent",
id: "test-csp",
key: nil,
props: %{},
ssr: false,
class: nil,
loading: [],
inner_block: [],
csp_nonce: opts[:csp_nonce],
csp_script_nonce: opts[:csp_script_nonce],
csp_style_nonce: opts[:csp_style_nonce]
}
|> LiveSvelte.svelte()
|> Phoenix.HTML.Safe.to_iodata()
|> IO.iodata_to_binary()
end
setup do
%{nonce: System.unique_integer([:positive])}
end
test "no nonce attributes when none specified" do
refute render_html(%{}) =~ "nonce="
end
test "csp_nonce applies to both script and style", %{nonce: nonce} do
html = render_html(%{csp_nonce: nonce})
assert html =~ ~r/<script nonce="#{nonce}">/
assert html =~ ~r/<style nonce="#{nonce}">/
end
test "csp_script_nonce applies only to script", %{nonce: nonce} do
html = render_html(%{csp_script_nonce: nonce})
assert html =~ ~r/<script nonce="#{nonce}">/
refute html =~ ~r/<style nonce=/
end
test "csp_style_nonce applies only to style", %{nonce: nonce} do
html = render_html(%{csp_style_nonce: nonce})
assert html =~ ~r/<style nonce="#{nonce}">/
refute html =~ ~r/<script nonce=/
end
test "separate script and style nonces", %{nonce: nonce} do
html = render_html(%{csp_script_nonce: nonce, csp_style_nonce: nonce * 2})
assert html =~ ~r/<script nonce="#{nonce}">/
assert html =~ ~r/<style nonce="#{nonce * 2}">/
end
end