Current section
Files
Jump to
Current section
Files
lib/html_sanitize_ex/scrubber/css.ex
defmodule HtmlSanitizeEx.Scrubber.CSS do
@moduledoc """
Scrub CSS.
"""
def scrub(nil), do: ""
def scrub(text) do
text = String.replace(text, ~r/(\/\*|\*\/|<!--|-->)/, " ")
Regex.replace(~r/([-\w]+)\s*:\s*([^:;]*)/, text, fn _all, a, b ->
case scrub_css(a, b) do
{property, value} -> "#{property}: #{value}"
nil -> ""
end
end)
end
defp scrub_css("azimuth", val), do: validate {"azimuth", scrub_val(val)}
defp scrub_css("background-color", val), do: validate {"background-color", scrub_val(val)}
defp scrub_css("border-bottom-color", val), do: validate {"border-bottom-color", scrub_val(val)}
defp scrub_css("border-collapse", val), do: validate {"border-collapse", scrub_val(val)}
defp scrub_css("border-color", val), do: validate {"border-color", scrub_val(val)}
defp scrub_css("border-left-color", val), do: validate {"border-left-color", scrub_val(val)}
defp scrub_css("border-right-color", val), do: validate {"border-right-color", scrub_val(val)}
defp scrub_css("border-top-color", val), do: validate {"border-top-color", scrub_val(val)}
defp scrub_css("clear", val), do: validate {"clear", scrub_val(val)}
defp scrub_css("color", val), do: validate {"color", scrub_val(val)}
defp scrub_css("cursor", val), do: validate {"cursor", scrub_val(val)}
defp scrub_css("direction", val), do: validate {"direction", scrub_val(val)}
defp scrub_css("display", val), do: validate {"display", scrub_val(val)}
defp scrub_css("elevation", val), do: validate {"elevation", scrub_val(val)}
defp scrub_css("float", val), do: validate {"float", scrub_val(val)}
defp scrub_css("font", val), do: validate {"font", scrub_val(val)}
defp scrub_css("font-family", val), do: validate {"font-family", scrub_val(val)}
defp scrub_css("font-size", val), do: validate {"font-size", scrub_val(val)}
defp scrub_css("font-style", val), do: validate {"font-style", scrub_val(val)}
defp scrub_css("font-variant", val), do: validate {"font-variant", scrub_val(val)}
defp scrub_css("font-weight", val), do: validate {"font-weight", scrub_val(val)}
defp scrub_css("height", val), do: validate {"height", scrub_val(val)}
defp scrub_css("letter-spacing", val), do: validate {"letter-spacing", scrub_val(val)}
defp scrub_css("line-height", val), do: validate {"line-height", scrub_val(val)}
defp scrub_css("overflow", val), do: validate {"overflow", scrub_val(val)}
defp scrub_css("pause", val), do: validate {"pause", scrub_val(val)}
defp scrub_css("pause-after", val), do: validate {"pause-after", scrub_val(val)}
defp scrub_css("pause-before", val), do: validate {"pause-before", scrub_val(val)}
defp scrub_css("pitch", val), do: validate {"pitch", scrub_val(val)}
defp scrub_css("pitch-range", val), do: validate {"pitch-range", scrub_val(val)}
defp scrub_css("richness", val), do: validate {"richness", scrub_val(val)}
defp scrub_css("speak", val), do: validate {"speak", scrub_val(val)}
defp scrub_css("speak-header", val), do: validate {"speak-header", scrub_val(val)}
defp scrub_css("speak-numeral", val), do: validate {"speak-numeral", scrub_val(val)}
defp scrub_css("speak-punctuation", val), do: validate {"speak-punctuation", scrub_val(val)}
defp scrub_css("speech-rate", val), do: validate {"speech-rate", scrub_val(val)}
defp scrub_css("stress", val), do: validate {"stress", scrub_val(val)}
defp scrub_css("text-align", val), do: validate {"text-align", scrub_val(val)}
defp scrub_css("text-decoration", val), do: validate {"text-decoration", scrub_val(val)}
defp scrub_css("text-indent", val), do: validate {"text-indent", scrub_val(val)}
defp scrub_css("unicode-bidi", val), do: validate {"unicode-bidi", scrub_val(val)}
defp scrub_css("vertical-align", val), do: validate {"vertical-align", scrub_val(val)}
defp scrub_css("voice-family", val), do: validate {"voice-family", scrub_val(val)}
defp scrub_css("volume", val), do: validate {"volume", scrub_val(val)}
defp scrub_css("white-space", val), do: validate {"white-space", scrub_val(val)}
defp scrub_css("width", val), do: validate {"width", scrub_val(val)}
defp scrub_css("background", val), do: validate {"background", scrub_val(val)}
defp scrub_css("background-" <> prop, val), do: validate {"background-#{prop}", scrub_val(val)}
defp scrub_css("border", val), do: validate {"border", scrub_val(val)}
defp scrub_css("border-" <> prop, val), do: validate {"border-#{prop}", scrub_val(val)}
defp scrub_css("margin", val), do: validate {"margin", scrub_val(val)}
defp scrub_css("margin-" <> prop, val), do: validate {"margin-#{prop}", scrub_val(val)}
defp scrub_css("padding", val), do: validate {"padding", scrub_val(val)}
defp scrub_css("padding-" <> prop, val), do: validate {"padding-#{prop}", scrub_val(val)}
defp scrub_css(_, _), do: nil
defp validate({_property, ""}), do: nil
defp validate({property, val}), do: {property, val}
defp scrub_val(val) do
val = if String.match?(val, ~r/(\\|&)/), do: "", else: val
Regex.replace(~r/(\S+)/, val, fn _all, a ->
if(allowed_keyword?(a) || measured_unit?(a), do: a, else: "")
end)
end
@allowed_keywords ["auto", "aqua", "black", "block", "blue", "bold", "both", "bottom", "brown", "center", "collapse", "dashed", "dotted", "fuchsia", "gray", "green", "!important", "italic", "left", "lime", "maroon", "medium", "none", "navy", "normal", "nowrap", "olive", "pointer", "purple", "red", "right", "solid", "silver", "teal", "top", "transparent", "underline", "white", "yellow"]
def allowed_keyword?(val) do
Enum.member?(@allowed_keywords, String.downcase(val))
end
defp measured_unit?(val) do
String.match?(val, ~r/\A(#[0-9a-f]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|-?\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)\z/)
end
end