diff --git a/lib/html_sanitize_ex/scrubber/html5.ex b/lib/html_sanitize_ex/scrubber/html5.ex
index fb1e3a0..a4c0363 100644
--- a/lib/html_sanitize_ex/scrubber/html5.ex
+++ b/lib/html_sanitize_ex/scrubber/html5.ex
@@ -2123,6 +2123,7 @@ defmodule HtmlSanitizeEx.Scrubber.HTML5 do
Meta.allow_tags_with_style_attributes([
"a",
"blockquote",
+ "body",
"br",
"code",
"del",
@@ -2181,6 +2182,7 @@ defmodule HtmlSanitizeEx.Scrubber.HTML5 do
"sub",
"summary",
"sup",
+ "svg",
"table",
"tbody",
"td",
@@ -2199,6 +2201,51 @@ defmodule HtmlSanitizeEx.Scrubber.HTML5 do
"wbr"
])
+ # ---------- SVG shapes & groups -----------------------------------
+ @shape_attrs ~w(fill stroke stroke-width stroke-linecap stroke-linejoin class transform)
+ @geo_attrs ~w(d x y cx cy r x1 y1 x2 y2 points)
+
+ @svg_ns "http://www.w3.org/2000/svg"
+ @xlink_ns "http://www.w3.org/1999/xlink"
+ @svg_root_attrs ~w(viewbox width height fill stroke stroke-width class role aria-label)
+
+ defp keep(attrs, whitelist) do
+ Enum.filter(attrs, fn {n, _} -> n in whitelist end)
+ |> Enum.filter(fn {n, v} ->
+ (n !== "fill" and n !== "stroke") or safe_colour?(v)
+ end)
+ end
+
+ # Reject gradients / external refs in colour attributes
+ defp safe_colour?(v) do
+ not String.starts_with?(v, "url(") and
+ not String.contains?(v, "javascript:") and
+ byte_size(v) < 64
+ end
+
+ def scrub({"svg", attrs, kids}) do
+ {"svg",
+ Enum.filter(attrs, fn
+ {"xmlns", @svg_ns} -> true
+ {"xmlns:xlink", @xlink_ns} -> true
+ {"fill", v} -> safe_colour?(v)
+ {"stroke", v} -> safe_colour?(v)
+ {n, _} when n in @svg_root_attrs -> true
+ _ -> false
+ end), Enum.map(kids, &scrub/1)}
+ end
+
+ def scrub({"g", attrs, kids}),
+ do: {"g", keep(attrs, @shape_attrs), Enum.map(kids, &scrub/1)}
+
+ def scrub({"path", attrs, _}),
+ do: {"path", keep(attrs, @shape_attrs ++ ["d"]), []}
+
+ for tag <- ~w(rect circle line polyline polygon) do
+ def scrub({unquote(tag), attrs, _kids}),
+ do: {unquote(tag), keep(attrs, @shape_attrs ++ @geo_attrs), []}
+ end
+
# style tags
def scrub({"style", attributes, [text]}) do
diff --git a/test/html5_test.exs b/test/html5_test.exs
index 9bd9662..397bdc5 100644
--- a/test/html5_test.exs
+++ b/test/html5_test.exs
@@ -125,6 +125,45 @@ defmodule HtmlSanitizeExScrubberHTML5Test do
assert input == full_html_sanitize(input)
end
+ test "does not strip valid attributes from svg and shapes" do
+ icon =
+ ~s()
+
+ assert icon == full_html_sanitize(icon)
+ end
+
+ test "strip unsafe colours for fill and stroke in svg" do
+ evil_hero_icon =
+ ~s||
+
+ good_hero_icon =
+ ~s||
+
+ assert good_hero_icon == full_html_sanitize(evil_hero_icon)
+ end
+
+ test "strip unsafe colours for fill and stroke in rect, circle, line, polyline, polygon, path, g" do
+ for tag <- ~w(rect circle line polyline polygon path g) do
+ evil_hero_icon =
+ ~s||
+
+ good_hero_icon =
+ ~s||
+
+ assert good_hero_icon == full_html_sanitize(evil_hero_icon)
+ end
+ end
+
test "make sure a very long URI is truncated before capturing URI scheme" do
input =
"
"