Strip potentially dangerous tags from SVG files (#12687)

This commit is contained in:
Oliver
2026-08-23 18:50:13 +10:00
committed by GitHub
parent bc98e4bab6
commit f16fb36b08
2 changed files with 27 additions and 26 deletions
+6 -26
View File
@@ -64,10 +64,6 @@ _SVG_ALLOWED_CSS_PROPERTIES = frozenset([
ALLOWED_ELEMENTS_SVG = [ ALLOWED_ELEMENTS_SVG = [
'a', 'a',
'animate',
'animateColor',
'animateMotion',
'animateTransform',
'circle', 'circle',
'defs', 'defs',
'desc', 'desc',
@@ -83,13 +79,11 @@ ALLOWED_ELEMENTS_SVG = [
'marker', 'marker',
'metadata', 'metadata',
'missing-glyph', 'missing-glyph',
'mpath',
'path', 'path',
'polygon', 'polygon',
'polyline', 'polyline',
'radialGradient', 'radialGradient',
'rect', 'rect',
'set',
'stop', 'stop',
'svg', 'svg',
'switch', 'switch',
@@ -98,21 +92,20 @@ ALLOWED_ELEMENTS_SVG = [
'tspan', 'tspan',
'use', 'use',
] ]
# SMIL animation elements ('animate', 'set', etc.) are intentionally excluded: nh3's
# URL-scheme filtering (e.g. stripping `javascript:` from `href`/`xlink:href`) is only
# applied to attributes it recognises as URL-bearing on the element that declares them.
# It does not recognise the `to`/`from`/`values` attributes of animation elements as
# URL-setting, so a `javascript:` URL placed there survives sanitization and is assigned
# to a target element's `href` at render time, bypassing the URL sanitization entirely.
ALLOWED_ATTRIBUTES_SVG = [ ALLOWED_ATTRIBUTES_SVG = [
'accent-height', 'accent-height',
'accumulate',
'additive',
'alphabetic', 'alphabetic',
'arabic-form', 'arabic-form',
'ascent', 'ascent',
'attributeName',
'attributeType',
'baseProfile', 'baseProfile',
'bbox', 'bbox',
'begin',
'by',
'calcMode',
'cap-height', 'cap-height',
'class', 'class',
'color', 'color',
@@ -125,8 +118,6 @@ ALLOWED_ATTRIBUTES_SVG = [
'dy', 'dy',
'descent', 'descent',
'display', 'display',
'dur',
'end',
'fill', 'fill',
'fill-opacity', 'fill-opacity',
'fill-rule', 'fill-rule',
@@ -136,7 +127,6 @@ ALLOWED_ATTRIBUTES_SVG = [
'font-style', 'font-style',
'font-variant', 'font-variant',
'font-weight', 'font-weight',
'from',
'fx', 'fx',
'fy', 'fy',
'g1', 'g1',
@@ -150,9 +140,6 @@ ALLOWED_ATTRIBUTES_SVG = [
'id', 'id',
'ideographic', 'ideographic',
'k', 'k',
'keyPoints',
'keySplines',
'keyTimes',
'lang', 'lang',
'marker-end', 'marker-end',
'marker-mid', 'marker-mid',
@@ -161,8 +148,6 @@ ALLOWED_ATTRIBUTES_SVG = [
'markerUnits', 'markerUnits',
'markerWidth', 'markerWidth',
'mathematical', 'mathematical',
'max',
'min',
'name', 'name',
'offset', 'offset',
'opacity', 'opacity',
@@ -178,11 +163,8 @@ ALLOWED_ATTRIBUTES_SVG = [
'r', 'r',
'refX', 'refX',
'refY', 'refY',
'repeatCount',
'repeatDur',
'requiredExtensions', 'requiredExtensions',
'requiredFeatures', 'requiredFeatures',
'restart',
'rotate', 'rotate',
'rx', 'rx',
'ry', 'ry',
@@ -204,7 +186,6 @@ ALLOWED_ATTRIBUTES_SVG = [
'systemLanguage', 'systemLanguage',
'target', 'target',
'text-anchor', 'text-anchor',
'to',
'transform', 'transform',
'type', 'type',
'u1', 'u1',
@@ -214,7 +195,6 @@ ALLOWED_ATTRIBUTES_SVG = [
'unicode', 'unicode',
'unicode-range', 'unicode-range',
'units-per-em', 'units-per-em',
'values',
'version', 'version',
'viewBox', 'viewBox',
'visibility', 'visibility',
+21
View File
@@ -1678,6 +1678,27 @@ class SanitizerTest(TestCase):
# Test that invalid string is cleaned # Test that invalid string is cleaned
self.assertNotEqual(dangerous_string, sanitize_svg(dangerous_string)) self.assertNotEqual(dangerous_string, sanitize_svg(dangerous_string))
def test_svg_sanitizer_smil_bypass(self):
"""Test that SMIL animation elements cannot be used to smuggle a javascript: URL.
A <set>/<animate>/<animateTransform> element can assign a `javascript:` value to
another element's `href`/`xlink:href` at render time via its `to`/`from`/`values`
attribute. These attributes are not treated as URLs by the sanitizer, so simply
stripping `javascript:` from `href`-like attributes is not sufficient - the
elements themselves must not be permitted.
"""
malicious_string = """<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<a xlink:href="https://example.com">
<set attributeName="xlink:href" to="javascript:alert(document.domain)" />
<text x="10" y="20">Click me</text>
</a>
</svg>"""
cleaned = sanitize_svg(malicious_string)
self.assertNotIn('javascript:', cleaned)
self.assertNotIn('<set', cleaned)
class MagicLoginTest(InvenTreeTestCase): class MagicLoginTest(InvenTreeTestCase):
"""Test magic login token generation.""" """Test magic login token generation."""