wger has Stored XSS via Unescaped License Attribution Fields
Date: 2026-04
Affected: pip/wger
Attack vector: xss
Category: vulnerability-disclosure
Severity: High
CVE: CVE-2026-40353
CVE-2026-40474
CWE: CWE-284
CWE-79
CWE-862
CVSS: 5.4 (CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N)

# Stored XSS via Unescaped License Attribution Fields ## Summary The `AbstractLicenseModel.attribution_link` property in `wger/utils/models.py` constructs HTML strings by directly interpolating user-controlled fields (`license_author`, `license_title`, `license_object_url`, `license_author_url`, `license_derivative_source_url`) without any escaping. The resulting HTML is rendered in the ingredient view template using Django's `|safe` filter, which disables auto-escaping. An authenticated user can create an ingredient with a malicious `license_author` value containing JavaScript, which executes when any user (including unauthenticated visitors) views the ingredient page. ## Severity **High** (CVSS 3.1: ~7.6) - Low-privilege attacker (any authenticated non-temporary user) - Stored XSS — persists in database - Triggers on a public page (no authentication needed to view) - Can steal session cookies, perform actions as other users, redirect to phishing ## CWE CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') ## Affected Components ### Vulnerable Property **File:** `wger/utils/models.py:88-110` ```python @property def attribution_link(self): out = '' if self.license_object_url: out += f'<a href="{self.license_object_url}">{self.license_title}</a>' else: out += self.license_title # NO ESCAPING out += ' by ' if self.license_author_url: out += f'<a href="{self.license_author_url}">{self.l


Mitigations:

Architectural layers (MAESTRO):