Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions lib/phoenix_html.ex
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,64 @@ defmodule Phoenix.HTML do

defp javascript_escape(<<>>, acc), do: acc

@doc ~S"""
Escapes encoded JSON for safe insertion into an HTML script tag.

The given JSON must already be encoded. The characters `<`, `>`, and `&`
are encoded as JSON Unicode escape sequences and the result is marked as
HTML safe.

iex> json = ~s({"name":"</script>"})
iex> json |> json_escape() |> safe_to_string()
"{\"name\":\"\\u003C/script\\u003E\"}"

"""
@spec json_escape(binary) :: safe
def json_escape(data) when is_binary(data) do
{:safe, json_escape(data, 0, data, [])}
end

escapes = [
{?<, "\\u003C"},
{?>, "\\u003E"},
{?&, "\\u0026"}
]

for {match, insert} <- escapes do
defp json_escape(<<unquote(match), rest::bits>>, skip, original, acc) do
json_escape(rest, skip + 1, original, [acc | unquote(insert)])
end
end

defp json_escape(<<_char, rest::bits>>, skip, original, acc) do
json_escape(rest, skip, original, acc, 1)
end

defp json_escape(<<>>, 0, original, _acc) do
original
end

defp json_escape(<<>>, _skip, _original, acc) do
acc
end

for {match, insert} <- escapes do
defp json_escape(<<unquote(match), rest::bits>>, skip, original, acc, len) do
part = binary_part(original, skip, len)
json_escape(rest, skip + len + 1, original, [acc, part | unquote(insert)], 0)
end
end

defp json_escape(<<_char, rest::bits>>, skip, original, acc, len) do
json_escape(rest, skip, original, acc, len + 1)
end

defp json_escape(<<>>, 0, original, _acc, _len), do: original

defp json_escape(<<>>, skip, original, acc, len) do
[acc | binary_part(original, skip, len)]
end

@doc """
Escapes a string for use as a CSS identifier.

Expand Down
12 changes: 12 additions & 0 deletions test/phoenix_html_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@ defmodule Phoenix.HTMLTest do
assert javascript_escape({:safe, ["'Single quote'"]}) == {:safe, "\\'Single quote\\'"}
end

test "json_escape/1" do
assert json_escape("") == {:safe, ""}

assert ~s({"data":"</script><!--&>"})
|> json_escape()
|> safe_to_string() == ~S({"data":"\u003C/script\u003E\u003C!--\u0026\u003E"})

assert ~S({"data":"\"quotes\" and \\slashes"})
|> json_escape()
|> safe_to_string() == ~S({"data":"\"quotes\" and \\slashes"})
end

describe "html_escape" do
test "escapes entities" do
assert html_escape("foo") == {:safe, "foo"}
Expand Down
Loading