CVE-2026-52781 Overview
CVE-2026-52781 is a stored cross-site scripting (XSS) vulnerability [CWE-79] in OpenProject, an open-source, web-based project management platform. The HTML sanitizer grants <macro> elements unrestricted data-* attributes through a :data wildcard rule. An authenticated attacker injects data-controller="poll-for-changes" into a work package description, causing Stimulus.js to mount a controller that fetches an attacker-uploaded attachment and passes it to renderStreamMessage(). The result is arbitrary Turbo Stream actions, including redirect_to, executing in every victim's authenticated browser session. The issue is fixed in versions 17.3.3 and 17.4.1.
Critical Impact
Any authenticated user viewing a poisoned work package is redirected to an attacker-controlled server, enabling credential harvesting and session abuse.
Affected Products
- OpenProject versions prior to 17.3.3
- OpenProject 17.4.x versions prior to 17.4.1
- Deployments exposing work package editing to untrusted or low-privilege users
Discovery Timeline
- 2026-06-26 - CVE-2026-52781 published to NVD
- 2026-06-29 - Last updated in NVD database
Technical Details for CVE-2026-52781
Vulnerability Analysis
The vulnerability stems from an overly permissive HTML sanitizer configuration in OpenProject. The sanitizer applies a :data wildcard to <macro> elements, allowing any data-* attribute to pass through unchanged. This directly conflicts with OpenProject's use of Stimulus.js, which treats data-controller attributes as instructions to instantiate JavaScript controllers on matching DOM nodes.
An attacker with permission to edit a work package description injects a <macro> element carrying data-controller="poll-for-changes". When any authenticated user renders the description, Stimulus.js mounts the poll-for-changes controller on the attacker-controlled node. The controller fetches an attachment supplied by the attacker and forwards its contents to renderStreamMessage(), executing Turbo Stream actions such as redirect_to. Victims are then redirected to an attacker-controlled origin without user interaction.
Root Cause
The root cause is an allowlist misconfiguration in the HTML sanitizer. Granting <macro> elements unrestricted data-* attributes fails to account for framework-level attribute semantics. Stimulus.js treats data-controller as an executable binding, converting a stored-content flaw into client-side code execution within the application's trust boundary.
Attack Vector
The attack requires an authenticated account with permission to write work package descriptions. The attacker uploads a malicious attachment containing Turbo Stream markup and then injects a <macro data-controller="poll-for-changes"> element referencing that attachment. When other authenticated users view the work package, their browsers mount the controller and process the attacker-supplied stream, leading to redirection or further Turbo Stream-driven manipulation of the victim's session UI. See the GitHub Security Advisory GHSA-q33w-f822-hg8x for technical details.
Detection Methods for CVE-2026-52781
Indicators of Compromise
- Work package descriptions containing <macro> elements with data-controller attributes, particularly poll-for-changes
- Unexpected attachments referenced by macro elements in work package content
- Outbound HTTP redirects from authenticated OpenProject sessions to unknown external hosts
Detection Strategies
- Query the OpenProject database for work package descriptions matching %<macro%data-controller% to identify injected payloads
- Review web server and reverse proxy logs for anomalous Referer chains originating from /work_packages/ paths and terminating at external domains
- Correlate attachment uploads with subsequent edits that reference those attachments inside <macro> elements
Monitoring Recommendations
- Alert on new or modified work packages containing HTML data-* attributes on non-standard elements
- Monitor Turbo Stream response payloads served from user-uploaded attachment endpoints
- Track authenticated sessions that generate outbound redirects to previously unseen domains shortly after loading a work package
How to Mitigate CVE-2026-52781
Immediate Actions Required
- Upgrade OpenProject to 17.3.3 or 17.4.1 without delay
- Audit existing work package descriptions for <macro> elements carrying data-controller attributes and remove any that are not legitimate
- Rotate session cookies and review authentication logs for suspicious activity following exposure
Patch Information
OpenProject addressed the flaw in versions 17.3.3 and 17.4.1 by tightening the HTML sanitizer allowlist so that <macro> elements no longer receive unrestricted data-* attributes. Administrators should apply the upstream release rather than attempting to patch the sanitizer rules manually. Details are documented in the OpenProject GitHub Security Advisory.
Workarounds
- Restrict work package edit permissions to trusted users until the patch is applied
- Deploy a web application firewall rule that blocks request bodies containing <macro combined with data-controller on OpenProject write endpoints
- Use a strict Content Security Policy that limits outbound navigation and fetch destinations to the OpenProject origin
# Example WAF rule (ModSecurity) blocking injection attempts
SecRule REQUEST_URI "@rx /api/v3/work_packages" \
"phase:2,deny,status:403,id:1052781,\
chain,msg:'OpenProject CVE-2026-52781 macro injection attempt'"
SecRule REQUEST_BODY "@rx <macro[^>]*data-controller" "t:none,t:lowercase"
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

