VYPR
Moderate severityNVD Advisory· Published Sep 29, 2022· Updated May 20, 2025

Cross-site Scripting (XSS) - Stored in inventree/inventree

CVE-2022-3355

Description

Cross-site Scripting (XSS) - Stored in GitHub repository inventree/inventree prior to 0.8.3.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Stored XSS in InvenTree prior to 0.8.3 allows attackers to inject arbitrary scripts via unsanitized SVG attachments.

Vulnerability

Description

CVE-2022-3355 is a stored cross-site scripting (XSS) vulnerability affecting InvenTree, an open-source inventory management system. It exists in versions prior to 0.8.3 and stems from insufficient sanitization of SVG file attachments. The official description confirms this as a Stored XSS issue [1].

Exploitation

An attacker can exploit this by uploading a specially crafted SVG file as an attachment to any part of the application (e.g., parts, stock items). The lack of input validation on SVG content allows embedding arbitrary JavaScript. When other users view the attachment, the malicious script executes in their browser context without requiring additional privileges [1][2].

Impact

Successful exploitation enables script execution in the victim's session, potentially leading to session theft, data exfiltration, or other malicious actions. The attack surface is broad since attachments are a common feature in inventory management workflows.

Mitigation

The vulnerability is fixed in InvenTree version 0.8.3, which includes a commit that adds SVG sanitization using the bleach library and an allowlist of elements and attributes [2][4]. Users should upgrade to 0.8.3 or later to mitigate the risk.

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
inventreePyPI
< 0.8.30.8.3

Affected products

1

Patches

1
5a08ef908dd5

Add sanitation for SVG attachments (#3701)

https://github.com/inventree/inventreeMatthias MairSep 23, 2022via ghsa
3 files changed · +95 0
  • InvenTree/InvenTree/models.py+10 0 modified
    @@ -4,6 +4,7 @@
     import os
     import re
     from datetime import datetime
    +from io import BytesIO
     
     from django.conf import settings
     from django.contrib.auth import get_user_model
    @@ -24,6 +25,7 @@
     import InvenTree.helpers
     from common.models import InvenTreeSetting
     from InvenTree.fields import InvenTreeURLField
    +from InvenTree.sanitizer import sanitize_svg
     
     logger = logging.getLogger('inventree')
     
    @@ -383,8 +385,16 @@ def save(self, *args, **kwargs):
                     'link': _('Missing external link'),
                 })
     
    +        if self.attachment.name.lower().endswith('.svg'):
    +            self.attachment.file.file = self.clean_svg(self.attachment)
    +
             super().save(*args, **kwargs)
     
    +    def clean_svg(self, field):
    +        """Sanitize SVG file before saving."""
    +        cleaned = sanitize_svg(field.file.read())
    +        return BytesIO(bytes(cleaned, 'utf8'))
    +
         def __str__(self):
             """Human name for attachment."""
             if self.attachment is not None:
    
  • InvenTree/InvenTree/sanitizer.py+67 0 added
    @@ -0,0 +1,67 @@
    +"""Functions to sanitize user input files."""
    +from bleach import clean
    +from bleach.css_sanitizer import CSSSanitizer
    +
    +ALLOWED_ELEMENTS_SVG = [
    +    'a', 'animate', 'animateColor', 'animateMotion',
    +    'animateTransform', 'circle', 'defs', 'desc', 'ellipse', 'font-face',
    +    'font-face-name', 'font-face-src', 'g', 'glyph', 'hkern',
    +    'linearGradient', 'line', 'marker', 'metadata', 'missing-glyph',
    +    'mpath', 'path', 'polygon', 'polyline', 'radialGradient', 'rect',
    +    'set', 'stop', 'svg', 'switch', 'text', 'title', 'tspan', 'use'
    +]
    +
    +ALLOWED_ATTRIBUTES_SVG = [
    +    'accent-height', 'accumulate', 'additive', 'alphabetic',
    +    'arabic-form', 'ascent', 'attributeName', 'attributeType',
    +    'baseProfile', 'bbox', 'begin', 'by', 'calcMode', 'cap-height',
    +    'class', 'color', 'color-rendering', 'content', 'cx', 'cy', 'd', 'dx',
    +    'dy', 'descent', 'display', 'dur', 'end', 'fill', 'fill-opacity',
    +    'fill-rule', 'font-family', 'font-size', 'font-stretch', 'font-style',
    +    'font-variant', 'font-weight', 'from', 'fx', 'fy', 'g1', 'g2',
    +    'glyph-name', 'gradientUnits', 'hanging', 'height', 'horiz-adv-x',
    +    'horiz-origin-x', 'id', 'ideographic', 'k', 'keyPoints',
    +    'keySplines', 'keyTimes', 'lang', 'marker-end', 'marker-mid',
    +    'marker-start', 'markerHeight', 'markerUnits', 'markerWidth',
    +    'mathematical', 'max', 'min', 'name', 'offset', 'opacity', 'orient',
    +    'origin', 'overline-position', 'overline-thickness', 'panose-1',
    +    'path', 'pathLength', 'points', 'preserveAspectRatio', 'r', 'refX',
    +    'refY', 'repeatCount', 'repeatDur', 'requiredExtensions',
    +    'requiredFeatures', 'restart', 'rotate', 'rx', 'ry', 'slope',
    +    'stemh', 'stemv', 'stop-color', 'stop-opacity',
    +    'strikethrough-position', 'strikethrough-thickness', 'stroke',
    +    'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap',
    +    'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity',
    +    'stroke-width', 'systemLanguage', 'target', 'text-anchor', 'to',
    +    'transform', 'type', 'u1', 'u2', 'underline-position',
    +    'underline-thickness', 'unicode', 'unicode-range', 'units-per-em',
    +    'values', 'version', 'viewBox', 'visibility', 'width', 'widths', 'x',
    +    'x-height', 'x1', 'x2', 'xlink:actuate', 'xlink:arcrole',
    +    'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title',
    +    'xlink:type', 'xml:base', 'xml:lang', 'xml:space', 'xmlns',
    +    'xmlns:xlink', 'y', 'y1', 'y2', 'zoomAndPan', 'style'
    +]
    +
    +
    +def sanitize_svg(file_data: str, strip: bool = True, elements: str = ALLOWED_ELEMENTS_SVG, attributes: str = ALLOWED_ATTRIBUTES_SVG) -> str:
    +    """Sanatize a SVG file.
    +
    +    Args:
    +        file_data (str): SVG as string.
    +        strip (bool, optional): Should invalid elements get removed. Defaults to True.
    +        elements (str, optional): Allowed elements. Defaults to ALLOWED_ELEMENTS_SVG.
    +        attributes (str, optional): Allowed attributes. Defaults to ALLOWED_ATTRIBUTES_SVG.
    +
    +    Returns:
    +        str: Sanitzied SVG file.
    +    """
    +
    +    cleaned = clean(
    +        file_data,
    +        tags=elements,
    +        attributes=attributes,
    +        strip=strip,
    +        strip_comments=strip,
    +        css_sanitizer=CSSSanitizer()
    +    )
    +    return cleaned
    
  • InvenTree/InvenTree/tests.py+18 0 modified
    @@ -23,6 +23,7 @@
     import InvenTree.tasks
     from common.models import InvenTreeSetting
     from common.settings import currency_codes
    +from InvenTree.sanitizer import sanitize_svg
     from part.models import Part, PartCategory
     from stock.models import StockItem, StockLocation
     
    @@ -878,3 +879,20 @@ def test_bacode_hash(self):
     
             for barcode, hash in hashing_tests.items():
                 self.assertEqual(InvenTree.helpers.hash_barcode(barcode), hash)
    +
    +
    +class SanitizerTest(TestCase):
    +    """Simple tests for sanitizer functions."""
    +
    +    def test_svg_sanitizer(self):
    +        """Test that SVGs are sanitized acordingly."""
    +        valid_string = """<svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="svg2" height="400" width="400">{0}
    +        <path id="path1" d="m -151.78571,359.62883 v 112.76373 l 97.068507,-56.04253 V 303.14815 Z" style="fill:#ddbc91;"></path>
    +        </svg>"""
    +        dangerous_string = valid_string.format('<script>alert();</script>')
    +
    +        # Test that valid string
    +        self.assertEqual(valid_string, sanitize_svg(valid_string))
    +
    +        # Test that invalid string is cleanded
    +        self.assertNotEqual(dangerous_string, sanitize_svg(dangerous_string))
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.