mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-11-23 23:49:08 -06:00
* Security: prevent XSS with storage path template rendering * Security: prevent XSS svg uploads * Security: force attachment disposition for logo * Add suggestions from code review * Improve SVG validation with allowlist for tags and attributes
103 lines
2.2 KiB
Python
103 lines
2.2 KiB
Python
from django.core.exceptions import ValidationError
|
|
from lxml import etree
|
|
|
|
ALLOWED_SVG_TAGS: set[str] = {
|
|
"svg",
|
|
"g",
|
|
"path",
|
|
"rect",
|
|
"circle",
|
|
"ellipse",
|
|
"line",
|
|
"polyline",
|
|
"polygon",
|
|
"text",
|
|
"tspan",
|
|
"defs",
|
|
"linearGradient",
|
|
"radialGradient",
|
|
"stop",
|
|
"clipPath",
|
|
"use",
|
|
"title",
|
|
"desc",
|
|
}
|
|
|
|
ALLOWED_SVG_ATTRIBUTES: set[str] = {
|
|
"id",
|
|
"class",
|
|
"style",
|
|
"d",
|
|
"fill",
|
|
"fill-rule",
|
|
"stroke",
|
|
"stroke-width",
|
|
"stroke-linecap",
|
|
"stroke-linejoin",
|
|
"stroke-miterlimit",
|
|
"stroke-dasharray",
|
|
"stroke-dashoffset",
|
|
"stroke-opacity",
|
|
"transform",
|
|
"x",
|
|
"y",
|
|
"cx",
|
|
"cy",
|
|
"r",
|
|
"rx",
|
|
"ry",
|
|
"width",
|
|
"height",
|
|
"x1",
|
|
"y1",
|
|
"x2",
|
|
"y2",
|
|
"gradientTransform",
|
|
"gradientUnits",
|
|
"offset",
|
|
"stop-color",
|
|
"stop-opacity",
|
|
"clip-path",
|
|
"viewBox",
|
|
"preserveAspectRatio",
|
|
"href",
|
|
"xlink:href",
|
|
"font-family",
|
|
"font-size",
|
|
"font-weight",
|
|
"text-anchor",
|
|
"xmlns",
|
|
"xmlns:xlink",
|
|
}
|
|
|
|
|
|
def reject_dangerous_svg(file):
|
|
"""
|
|
Rejects SVG files that contain dangerous tags or attributes.
|
|
Raises ValidationError if unsafe content is found.
|
|
See GHSA-6p53-hqqw-8j62
|
|
"""
|
|
try:
|
|
parser = etree.XMLParser(resolve_entities=False)
|
|
file.seek(0)
|
|
tree = etree.parse(file, parser)
|
|
root = tree.getroot()
|
|
except etree.XMLSyntaxError:
|
|
raise ValidationError("Invalid SVG file.")
|
|
|
|
for element in root.iter():
|
|
tag = etree.QName(element.tag).localname.lower()
|
|
if tag not in ALLOWED_SVG_TAGS:
|
|
raise ValidationError(f"Disallowed SVG tag: <{tag}>")
|
|
|
|
for attr_name, attr_value in element.attrib.items():
|
|
attr_name_lower = attr_name.lower()
|
|
if attr_name_lower not in ALLOWED_SVG_ATTRIBUTES:
|
|
raise ValidationError(f"Disallowed SVG attribute: {attr_name}")
|
|
|
|
if attr_name_lower in {
|
|
"href",
|
|
"xlink:href",
|
|
} and attr_value.strip().lower().startswith("javascript:"):
|
|
raise ValidationError(f"Disallowed javascript: URI in {attr_name}")
|