Skip to content

Commit e7b0c3f

Browse files
committed
svelte/1: Add CSP nonce parameters
1 parent 75246ee commit e7b0c3f

2 files changed

Lines changed: 84 additions & 2 deletions

File tree

lib/live_svelte.ex

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,18 @@ defmodule LiveSvelte do
4545
doc: "Class to apply to the Svelte component",
4646
examples: ["my-class", "my-class another-class"]
4747

48+
attr :csp_nonce, :string,
49+
default: nil,
50+
doc: "A Content-Security-Policy nonce for the generated <script> and <style> tags"
51+
52+
attr :csp_script_nonce, :string,
53+
default: nil,
54+
doc: "A Content-Security-Policy nonce for the generated <script> tag"
55+
56+
attr :csp_style_nonce, :string,
57+
default: nil,
58+
doc: "A Content-Security-Policy nonce for the generated <style> tag"
59+
4860
attr :ssr, :boolean,
4961
default: true,
5062
doc: "Whether to render the component via NodeJS on the server",
@@ -124,6 +136,8 @@ defmodule LiveSvelte do
124136

125137
streams_diff = calculate_streams_diff(assigns, init or dead)
126138

139+
csp_attrs = if nonce = assigns.csp_nonce, do: [nonce: nonce]
140+
127141
assigns =
128142
assigns
129143
|> assign(:init, init)
@@ -134,9 +148,17 @@ defmodule LiveSvelte do
134148
|> assign(:use_diff, use_diff)
135149
|> assign(:props_diff, props_diff)
136150
|> assign(:streams_diff, streams_diff)
151+
|> assign(
152+
:csp_script_attrs,
153+
csp_attrs || if(nonce = assigns.csp_script_nonce, do: [nonce: nonce], else: [])
154+
)
155+
|> assign(
156+
:csp_style_attrs,
157+
csp_attrs || if(nonce = assigns.csp_style_nonce, do: [nonce: nonce], else: [])
158+
)
137159

138160
~H"""
139-
<script>
161+
<script {@csp_script_attrs}>
140162
<%= raw(@ssr_render["head"]) %>
141163
</script>
142164
<div
@@ -154,7 +176,7 @@ defmodule LiveSvelte do
154176
>
155177
<div id={"#{@svelte_id}-target"} data-svelte-target>
156178
{raw(@ssr_render["head"])}
157-
<style>
179+
<style {@csp_style_attrs}>
158180
<%= raw(@ssr_render["css"]["code"]) %>
159181
</style>
160182
{raw(@ssr_render["html"])}

test/csp_nonce_test.exs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
defmodule LiveSvelte.CspNonceTest do
2+
use ExUnit.Case, async: true
3+
4+
defp render_html(opts) do
5+
%{
6+
__changed__: nil,
7+
socket: nil,
8+
name: "TestComponent",
9+
id: "test-csp",
10+
key: nil,
11+
props: %{},
12+
ssr: false,
13+
class: nil,
14+
loading: [],
15+
inner_block: [],
16+
csp_nonce: opts[:csp_nonce],
17+
csp_script_nonce: opts[:csp_script_nonce],
18+
csp_style_nonce: opts[:csp_style_nonce]
19+
}
20+
|> LiveSvelte.svelte()
21+
|> Phoenix.HTML.Safe.to_iodata()
22+
|> IO.iodata_to_binary()
23+
end
24+
25+
setup do
26+
%{nonce: System.unique_integer([:positive])}
27+
end
28+
29+
test "no nonce attributes when none specified" do
30+
refute render_html(%{}) =~ "nonce="
31+
end
32+
33+
test "csp_nonce applies to both script and style", %{nonce: nonce} do
34+
html = render_html(%{csp_nonce: nonce})
35+
36+
assert html =~ ~r/<script nonce="#{nonce}">/
37+
assert html =~ ~r/<style nonce="#{nonce}">/
38+
end
39+
40+
test "csp_script_nonce applies only to script", %{nonce: nonce} do
41+
html = render_html(%{csp_script_nonce: nonce})
42+
43+
assert html =~ ~r/<script nonce="#{nonce}">/
44+
refute html =~ ~r/<style nonce=/
45+
end
46+
47+
test "csp_style_nonce applies only to style", %{nonce: nonce} do
48+
html = render_html(%{csp_style_nonce: nonce})
49+
50+
assert html =~ ~r/<style nonce="#{nonce}">/
51+
refute html =~ ~r/<script nonce=/
52+
end
53+
54+
test "separate script and style nonces", %{nonce: nonce} do
55+
html = render_html(%{csp_script_nonce: nonce, csp_style_nonce: nonce * 2})
56+
57+
assert html =~ ~r/<script nonce="#{nonce}">/
58+
assert html =~ ~r/<style nonce="#{nonce * 2}">/
59+
end
60+
end

0 commit comments

Comments
 (0)