зеркало из https://github.com/mozilla/kitsune.git
remove SVG scrubbing (#5860)
This commit is contained in:
Родитель
c9e07b1917
Коммит
9cda6858f1
|
@ -21,7 +21,7 @@ RUN set -xe \
|
|||
&& apt-get install -y --no-install-recommends \
|
||||
gettext build-essential \
|
||||
libxml2-dev libxslt1-dev zlib1g-dev git \
|
||||
libjpeg-dev libcairo2-dev libffi-dev libssl-dev libxslt1.1 \
|
||||
libjpeg-dev libffi-dev libssl-dev libxslt1.1 \
|
||||
optipng postgresql zip \
|
||||
# python
|
||||
&& python -m venv /venv \
|
||||
|
@ -103,7 +103,7 @@ COPY --chown=kitsune:kitsune . .
|
|||
RUN apt-get update && \
|
||||
apt-get upgrade -y && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
libcairo2 libxslt1.1 optipng postgresql && \
|
||||
libxslt1.1 optipng postgresql && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN mkdir /app/media && chown kitsune:kitsune /app/media
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
from pathlib import Path
|
||||
|
||||
from cairosvg import svg2svg
|
||||
from django import forms
|
||||
from django.contrib.auth.models import User
|
||||
from django.core import validators
|
||||
|
@ -86,7 +85,7 @@ class MultiUsernameField(forms.Field):
|
|||
|
||||
class ImagePlusField(forms.ImageField):
|
||||
"""
|
||||
Same as django.forms.ImageField but with support for SVG images as well.
|
||||
Same as django.forms.ImageField but with support for trusted SVG images as well.
|
||||
"""
|
||||
|
||||
default_validators = [
|
||||
|
@ -98,7 +97,7 @@ class ImagePlusField(forms.ImageField):
|
|||
def to_python(self, data):
|
||||
"""
|
||||
Check that the file-upload field data contains an image that
|
||||
Pillow supports or a valid SVG image.
|
||||
Pillow supports or an SVG image (assumed to be trusted).
|
||||
"""
|
||||
try:
|
||||
return super().to_python(data)
|
||||
|
@ -108,38 +107,4 @@ class ImagePlusField(forms.ImageField):
|
|||
):
|
||||
raise
|
||||
|
||||
def scrub(svg_as_bytes):
|
||||
"""
|
||||
Accepts an SVG file as bytes and returns a safe version of that
|
||||
SVG file as bytes.
|
||||
"""
|
||||
try:
|
||||
return svg2svg(bytestring=svg_as_bytes)
|
||||
except Exception as exc:
|
||||
# CairoSVG doesn't recognize it as an SVG image.
|
||||
msg = _("Invalid or unsupported SVG image: {reason}")
|
||||
raise ValidationError(
|
||||
msg.format(reason=str(exc)),
|
||||
code="invalid_svg_image",
|
||||
) from exc
|
||||
|
||||
if hasattr(data, "read"):
|
||||
# This is typically an instance of a sub-class of UploadedFile,
|
||||
# which shouldn't be closed, otherwise it will be deleted.
|
||||
data.seek(0)
|
||||
try:
|
||||
scrubbed = scrub(data.read())
|
||||
finally:
|
||||
# The read pointer is expected to point to the start of the file.
|
||||
data.seek(0)
|
||||
try:
|
||||
# Over-write the image with its scrubbed version.
|
||||
data.truncate()
|
||||
data.write(scrubbed)
|
||||
finally:
|
||||
# The read pointer is expected to point to the start of the file.
|
||||
data.seek(0)
|
||||
else:
|
||||
data["content"] = scrub(data["content"])
|
||||
|
||||
return data
|
||||
|
|
|
@ -105,22 +105,6 @@ class ImagePlusFieldTestCases(TestCase):
|
|||
data = self.get_uploaded_file("stuff.svg")
|
||||
self.assertEqual(field.clean(data), data)
|
||||
|
||||
def test_svg_image_with_unsafe_file(self):
|
||||
"""Test for the case when the uploaded file is unsafe."""
|
||||
field = ImagePlusField()
|
||||
data = self.get_uploaded_file(
|
||||
"stuff.svg",
|
||||
content=b"""
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
|
||||
<script>alert('This is an unsafe SVG file!');</script>
|
||||
<rect x="10" y="10" width="80" height="80" fill="blue" />
|
||||
</svg>""",
|
||||
)
|
||||
self.assertEqual(field.clean(data), data)
|
||||
content = data.read()
|
||||
self.assertIn(b'<svg xmlns="http://www.w3.org/2000/svg"', content)
|
||||
self.assertNotIn(b"<script>", content)
|
||||
|
||||
def test_svg_image_without_proper_extension(self):
|
||||
"""SVG images without an "svg" extension should be considered invalid."""
|
||||
field = ImagePlusField()
|
||||
|
@ -131,17 +115,3 @@ class ImagePlusFieldTestCases(TestCase):
|
|||
|
||||
self.assertTrue(hasattr(arm.exception, "code"))
|
||||
self.assertEqual(arm.exception.code, "invalid_image")
|
||||
|
||||
def test_invalid_svg_image(self):
|
||||
"""Invalid SVG images should raise a validation error."""
|
||||
field = ImagePlusField()
|
||||
data = self.get_uploaded_file(
|
||||
"stuff.svg", content=b"""<svg xmlns="http://www.w3.org/2000/svg"></svg>"""
|
||||
)
|
||||
|
||||
with self.assertRaises(ValidationError) as arm:
|
||||
field.clean(data)
|
||||
|
||||
self.assertTrue(hasattr(arm.exception, "code"))
|
||||
self.assertEqual(arm.exception.code, "invalid_svg_image")
|
||||
self.assertIn("The SVG size is undefined", str(arm.exception))
|
||||
|
|
|
@ -317,47 +317,6 @@ files = [
|
|||
{file = "cachetools-5.3.2.tar.gz", hash = "sha256:086ee420196f7b2ab9ca2db2520aca326318b68fe5ba8bc4d49cca91add450f2"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cairocffi"
|
||||
version = "1.6.1"
|
||||
description = "cffi-based cairo bindings for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "cairocffi-1.6.1-py3-none-any.whl", hash = "sha256:aa78ee52b9069d7475eeac457389b6275aa92111895d78fbaa2202a52dac112e"},
|
||||
{file = "cairocffi-1.6.1.tar.gz", hash = "sha256:78e6bbe47357640c453d0be929fa49cd05cce2e1286f3d2a1ca9cbda7efdb8b7"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
cffi = ">=1.1.0"
|
||||
|
||||
[package.extras]
|
||||
doc = ["sphinx", "sphinx_rtd_theme"]
|
||||
test = ["flake8", "isort", "numpy", "pikepdf", "pytest"]
|
||||
xcb = ["xcffib (>=1.4.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "cairosvg"
|
||||
version = "2.7.1"
|
||||
description = "A Simple SVG Converter based on Cairo"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
{file = "CairoSVG-2.7.1-py3-none-any.whl", hash = "sha256:8a5222d4e6c3f86f1f7046b63246877a63b49923a1cd202184c3a634ef546b3b"},
|
||||
{file = "CairoSVG-2.7.1.tar.gz", hash = "sha256:432531d72347291b9a9ebfb6777026b607563fd8719c46ee742db0aef7271ba0"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
cairocffi = "*"
|
||||
cssselect2 = "*"
|
||||
defusedxml = "*"
|
||||
pillow = "*"
|
||||
tinycss2 = "*"
|
||||
|
||||
[package.extras]
|
||||
doc = ["sphinx", "sphinx-rtd-theme"]
|
||||
test = ["flake8", "isort", "pytest"]
|
||||
|
||||
[[package]]
|
||||
name = "celery"
|
||||
version = "5.2.7"
|
||||
|
@ -752,25 +711,6 @@ files = [
|
|||
{file = "cssselect-1.2.0.tar.gz", hash = "sha256:666b19839cfaddb9ce9d36bfe4c969132c647b92fc9088c4e23f786b30f1b3dc"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cssselect2"
|
||||
version = "0.7.0"
|
||||
description = "CSS selectors for Python ElementTree"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "cssselect2-0.7.0-py3-none-any.whl", hash = "sha256:fd23a65bfd444595913f02fc71f6b286c29261e354c41d722ca7a261a49b5969"},
|
||||
{file = "cssselect2-0.7.0.tar.gz", hash = "sha256:1ccd984dab89fc68955043aca4e1b03e0cf29cad9880f6e28e3ba7a74b14aa5a"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
tinycss2 = "*"
|
||||
webencodings = "*"
|
||||
|
||||
[package.extras]
|
||||
doc = ["sphinx", "sphinx_rtd_theme"]
|
||||
test = ["flake8", "isort", "pytest"]
|
||||
|
||||
[[package]]
|
||||
name = "cssutils"
|
||||
version = "2.9.0"
|
||||
|
@ -824,17 +764,6 @@ files = [
|
|||
{file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "defusedxml"
|
||||
version = "0.7.1"
|
||||
description = "XML bomb protection for Python stdlib modules"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
files = [
|
||||
{file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"},
|
||||
{file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dennis"
|
||||
version = "1.1.0"
|
||||
|
@ -2991,8 +2920,6 @@ files = [
|
|||
{file = "psycopg2-2.9.9-cp310-cp310-win_amd64.whl", hash = "sha256:426f9f29bde126913a20a96ff8ce7d73fd8a216cfb323b1f04da402d452853c3"},
|
||||
{file = "psycopg2-2.9.9-cp311-cp311-win32.whl", hash = "sha256:ade01303ccf7ae12c356a5e10911c9e1c51136003a9a1d92f7aa9d010fb98372"},
|
||||
{file = "psycopg2-2.9.9-cp311-cp311-win_amd64.whl", hash = "sha256:121081ea2e76729acfb0673ff33755e8703d45e926e416cb59bae3a86c6a4981"},
|
||||
{file = "psycopg2-2.9.9-cp312-cp312-win32.whl", hash = "sha256:d735786acc7dd25815e89cc4ad529a43af779db2e25aa7c626de864127e5a024"},
|
||||
{file = "psycopg2-2.9.9-cp312-cp312-win_amd64.whl", hash = "sha256:a7653d00b732afb6fc597e29c50ad28087dcb4fbfb28e86092277a559ae4e693"},
|
||||
{file = "psycopg2-2.9.9-cp37-cp37m-win32.whl", hash = "sha256:5e0d98cade4f0e0304d7d6f25bbfbc5bd186e07b38eac65379309c4ca3193efa"},
|
||||
{file = "psycopg2-2.9.9-cp37-cp37m-win_amd64.whl", hash = "sha256:7e2dacf8b009a1c1e843b5213a87f7c544b2b042476ed7755be813eaf4e8347a"},
|
||||
{file = "psycopg2-2.9.9-cp38-cp38-win32.whl", hash = "sha256:ff432630e510709564c01dafdbe996cb552e0b9f3f065eb89bdce5bd31fabf4c"},
|
||||
|
@ -4865,4 +4792,4 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"]
|
|||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.11"
|
||||
content-hash = "9fe0f9faf7b1a91e8d82b1f49f2ecf391badae9aaf13889649165d0718c8fdb0"
|
||||
content-hash = "4b195c7ed47a191a50009675cdde566b34ee23e080a9d94343781a6083e44b53"
|
||||
|
|
|
@ -88,7 +88,6 @@ psycopg2 = "^2.9.9"
|
|||
mkdocs = "^1.5.3"
|
||||
mkdocs-material = "^9.5.3"
|
||||
dockerflow = "^2022.8.0"
|
||||
cairosvg = "^2.7.1"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
ipdb = "^0.13.11"
|
||||
|
|
Загрузка…
Ссылка в новой задаче