mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-11-23 23:49:08 -06:00
Merge commit from fork
* 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
This commit is contained in:
102
src/paperless/validators.py
Normal file
102
src/paperless/validators.py
Normal file
@@ -0,0 +1,102 @@
|
||||
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}")
|
||||
Reference in New Issue
Block a user