import tinycss2 | |
ALLOWED_CSS_PROPERTIES = frozenset( | |
( | |
"azimuth", | |
"background-color", | |
"border-bottom-color", | |
"border-collapse", | |
"border-color", | |
"border-left-color", | |
"border-right-color", | |
"border-top-color", | |
"clear", | |
"color", | |
"cursor", | |
"direction", | |
"display", | |
"elevation", | |
"float", | |
"font", | |
"font-family", | |
"font-size", | |
"font-style", | |
"font-variant", | |
"font-weight", | |
"height", | |
"letter-spacing", | |
"line-height", | |
"overflow", | |
"pause", | |
"pause-after", | |
"pause-before", | |
"pitch", | |
"pitch-range", | |
"richness", | |
"speak", | |
"speak-header", | |
"speak-numeral", | |
"speak-punctuation", | |
"speech-rate", | |
"stress", | |
"text-align", | |
"text-decoration", | |
"text-indent", | |
"unicode-bidi", | |
"vertical-align", | |
"voice-family", | |
"volume", | |
"white-space", | |
"width", | |
) | |
) | |
ALLOWED_SVG_PROPERTIES = frozenset( | |
( | |
"fill", | |
"fill-opacity", | |
"fill-rule", | |
"stroke", | |
"stroke-width", | |
"stroke-linecap", | |
"stroke-linejoin", | |
"stroke-opacity", | |
) | |
) | |
class CSSSanitizer: | |
def __init__( | |
self, | |
allowed_css_properties=ALLOWED_CSS_PROPERTIES, | |
allowed_svg_properties=ALLOWED_SVG_PROPERTIES, | |
): | |
self.allowed_css_properties = allowed_css_properties | |
self.allowed_svg_properties = allowed_svg_properties | |
def sanitize_css(self, style): | |
"""Sanitizes css in style tags""" | |
parsed = tinycss2.parse_declaration_list(style) | |
if not parsed: | |
return "" | |
new_tokens = [] | |
for token in parsed: | |
if token.type == "declaration": | |
if ( | |
token.lower_name in self.allowed_css_properties | |
or token.lower_name in self.allowed_svg_properties | |
): | |
new_tokens.append(token) | |
elif token.type in ("comment", "whitespace"): | |
if new_tokens and new_tokens[-1].type != token.type: | |
new_tokens.append(token) | |
# NOTE(willkg): We currently don't handle AtRule or ParseError and | |
# so both get silently thrown out | |
if not new_tokens: | |
return "" | |
return tinycss2.serialize(new_tokens).strip() | |