diff --git a/lib/makeup/formatters/html/html_formatter.ex b/lib/makeup/formatters/html/html_formatter.ex index 7e9938c..254057c 100644 --- a/lib/makeup/formatters/html/html_formatter.ex +++ b/lib/makeup/formatters/html/html_formatter.ex @@ -53,9 +53,28 @@ defmodule Makeup.Formatters.HTML.HTMLFormatter do defp escape_for(c) when is_integer(c) and c >= 128, do: << c :: utf8 >> defp escape_for(string) when is_binary(string) do - string - |> to_charlist() - |> Enum.map(&escape_for/1) + escape_binary(string, string, 0, 0, []) + end + + for {char, replacement} <- [ + {?&, "&"}, + {?<, "<"}, + {?>, ">"}, + {?", """}, + {?', "'"} + ] do + defp escape_binary(<>, original, skip, len, acc) do + part = binary_part(original, skip, len) + escape_binary(rest, original, skip + len + 1, 0, [acc, part, unquote(replacement)]) + end + end + + defp escape_binary(<<_char, rest::binary>>, original, skip, len, acc) do + escape_binary(rest, original, skip, len + 1, acc) + end + + defp escape_binary(<<>>, original, skip, len, acc) do + [acc | binary_part(original, skip, len)] end defp escape(iodata) when is_list(iodata) do diff --git a/test/html_formatter_test.exs b/test/html_formatter_test.exs index 2577321..d4fa86c 100644 --- a/test/html_formatter_test.exs +++ b/test/html_formatter_test.exs @@ -74,6 +74,17 @@ defmodule MakeupTest.Lexer.HTMLFormatterTest do end end + test "a binary value is escaped, preserving safe characters and utf8" do + value = "a < b & \"c\" é 日" + + assert HTMLFormatter.format_as_binary([{:string, %{}, value}]) == + ~S[
] <>
+             ~S[] <>
+             ~S[a < b & "c" é 日] <>
+             ~S[] <>
+             ~S[
] + end + test "group ids are encoded before being written to data attributes" do html = HTMLFormatter.format_as_binary([