Skip to content

Commit 6b9e4fb

Browse files
committed
Allow passing csp_nonce_assign_key into router
1 parent 30120c9 commit 6b9e4fb

File tree

6 files changed

+48
-6
lines changed

6 files changed

+48
-6
lines changed

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,26 @@ defmodule MyPhoenixAppWeb.Router do
9797
end
9898
```
9999

100+
### Content Security Policy
101+
102+
Content security policy nonces can be passed into the router to allow usage of strict Content Security Policies throughout an application.
103+
104+
This can be achieved by passing in a `csp_nonce_assign_key` to the `FunWithFlags.UI.Router` forward. Values for the nonces should be set in the Conn assigns before reaching this router.
105+
106+
This an either be a single nonce value, or separate values for script and style tags.
107+
108+
For example:
109+
110+
``` elixir
111+
forward "/", FunWithFlags.UI.Router, namespace: "feature-flags", csp_nonce_assign_key: :my_csp_nonce
112+
```
113+
114+
Or:
115+
116+
``` elixir
117+
forward "/", FunWithFlags.UI.Router, namespace: "feature-flags", csp_nonce_assign_key: %{style: :my_style_nonce, script: :my_script_nonce}
118+
```
119+
100120
## Caveats
101121

102122
While the base `fun_with_flags` library is quite relaxed in terms of valid flag names, group names and actor identifers, this web dashboard extension applies some more restrictive rules.

lib/fun_with_flags/ui/router.ex

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ defmodule FunWithFlags.UI.Router do
3030

3131
@doc false
3232
def call(conn, opts) do
33-
conn = extract_namespace(conn, opts)
33+
conn =
34+
conn
35+
|> extract_namespace(opts)
36+
|> extract_csp_nonce_key(opts)
3437
super(conn, opts)
3538
end
3639

@@ -286,6 +289,16 @@ defmodule FunWithFlags.UI.Router do
286289
Plug.Conn.assign(conn, :namespace, "/" <> ns)
287290
end
288291

292+
defp extract_csp_nonce_key(conn, opts) do
293+
csp_nonce_assign_key =
294+
case opts[:csp_nonce_assign_key] do
295+
nil -> nil
296+
key when is_atom(key) -> %{style: key, script: key}
297+
%{} = keys -> Map.take(keys, [:style, :script])
298+
end
299+
300+
Plug.Conn.put_private(conn, :csp_nonce_assign_key, csp_nonce_assign_key)
301+
end
289302

290303
defp assign_csrf_token(conn, _opts) do
291304
csrf_token = Plug.CSRFProtection.get_csrf_token()

lib/fun_with_flags/ui/templates.ex

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,9 @@ defmodule FunWithFlags.UI.Templates do
8282
|> to_string()
8383
|> URI.encode()
8484
end
85+
86+
def csp_nonce(conn, type) do
87+
csp_nonce_assign_key = conn.private.csp_nonce_assign_key[type]
88+
conn.assigns[csp_nonce_assign_key]
89+
end
8590
end

lib/fun_with_flags/ui/templates/_head.html.eex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
<meta charset="utf-8">
33
<title>FunWithFlags - <%= @title %></title>
44

5-
<link rel="stylesheet" href="<%= path(@conn, "/assets/bootstrap.min.css") %>">
6-
<link rel="stylesheet" href="<%= path(@conn, "/assets/style.css") %>">
5+
<link nonce="<%= csp_nonce(@conn, :style) %>" rel="stylesheet" href="<%= path(@conn, "/assets/bootstrap.min.css") %>">
6+
<link nonce="<%= csp_nonce(@conn, :style) %>" rel="stylesheet" href="<%= path(@conn, "/assets/style.css") %>">
77
</head>

lib/fun_with_flags/ui/templates/details.html.eex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,6 @@
140140
</div>
141141
</div>
142142
</div>
143-
<script type="text/javascript" src="<%= path(@conn, "/assets/details.js") %>"></script>
143+
<script nonce="<%= csp_nonce(@conn, :script) %>" type="text/javascript" src="<%= path(@conn, "/assets/details.js") %>"></script>
144144
</body>
145145
</html>

test/fun_with_flags/ui/templates_test.exs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,12 @@ defmodule FunWithFlags.UI.TemplatesTest do
1212
end
1313

1414
setup do
15-
conn = Plug.Conn.assign(%Plug.Conn{}, :namespace, "/pear")
16-
conn = Plug.Conn.assign(conn, :csrf_token, Plug.CSRFProtection.get_csrf_token())
15+
conn =
16+
%Plug.Conn{}
17+
|> Plug.Conn.assign(:namespace, "/pear")
18+
|> Plug.Conn.put_private(:csp_nonce_assign_key, %{style: :style_key, script: :script_key})
19+
|> Plug.Conn.assign(:csrf_token, Plug.CSRFProtection.get_csrf_token())
20+
1721
{:ok, conn: conn}
1822
end
1923

0 commit comments

Comments
 (0)