css_sanitizer.py 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import tinycss2
  2. ALLOWED_CSS_PROPERTIES = frozenset(
  3. (
  4. "azimuth",
  5. "background-color",
  6. "border-bottom-color",
  7. "border-collapse",
  8. "border-color",
  9. "border-left-color",
  10. "border-right-color",
  11. "border-top-color",
  12. "clear",
  13. "color",
  14. "cursor",
  15. "direction",
  16. "display",
  17. "elevation",
  18. "float",
  19. "font",
  20. "font-family",
  21. "font-size",
  22. "font-style",
  23. "font-variant",
  24. "font-weight",
  25. "height",
  26. "letter-spacing",
  27. "line-height",
  28. "overflow",
  29. "pause",
  30. "pause-after",
  31. "pause-before",
  32. "pitch",
  33. "pitch-range",
  34. "richness",
  35. "speak",
  36. "speak-header",
  37. "speak-numeral",
  38. "speak-punctuation",
  39. "speech-rate",
  40. "stress",
  41. "text-align",
  42. "text-decoration",
  43. "text-indent",
  44. "unicode-bidi",
  45. "vertical-align",
  46. "voice-family",
  47. "volume",
  48. "white-space",
  49. "width",
  50. )
  51. )
  52. ALLOWED_SVG_PROPERTIES = frozenset(
  53. (
  54. "fill",
  55. "fill-opacity",
  56. "fill-rule",
  57. "stroke",
  58. "stroke-width",
  59. "stroke-linecap",
  60. "stroke-linejoin",
  61. "stroke-opacity",
  62. )
  63. )
  64. class CSSSanitizer:
  65. def __init__(
  66. self,
  67. allowed_css_properties=ALLOWED_CSS_PROPERTIES,
  68. allowed_svg_properties=ALLOWED_SVG_PROPERTIES,
  69. ):
  70. self.allowed_css_properties = allowed_css_properties
  71. self.allowed_svg_properties = allowed_svg_properties
  72. def sanitize_css(self, style):
  73. """Sanitizes css in style tags"""
  74. parsed = tinycss2.parse_declaration_list(style)
  75. if not parsed:
  76. return ""
  77. new_tokens = []
  78. for token in parsed:
  79. if token.type == "declaration":
  80. if (
  81. token.lower_name in self.allowed_css_properties
  82. or token.lower_name in self.allowed_svg_properties
  83. ):
  84. new_tokens.append(token)
  85. elif token.type in ("comment", "whitespace"):
  86. if new_tokens and new_tokens[-1].type != token.type:
  87. new_tokens.append(token)
  88. # NOTE(willkg): We currently don't handle AtRule or ParseError and
  89. # so both get silently thrown out
  90. if not new_tokens:
  91. return ""
  92. return tinycss2.serialize(new_tokens).strip()