Improper Neutralization of Invalid Characters in Data Attribute Names in org.xwiki.commons:xwiki-commons-xml
Description
XWiki Commons XML HTML sanitizer allows XSS via invalid data attributes in versions 14.6-rc-1 to 14.10.3.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
XWiki Commons XML HTML sanitizer allows XSS via invalid data attributes in versions 14.6-rc-1 to 14.10.3.
The HTML sanitizer in org.xwiki.commons:xwiki-commons-xml, introduced in version 14.6-rc-1, fails to properly validate data attribute names. The sanitizer uses a regex pattern DATA_ATTR to check if an attribute is allowed, but the pattern used .find() instead of .matches(), allowing arbitrary characters after a valid prefix. This enables injection of malicious HTML attributes such as data-x/onmouseover="alert('XSS')" [1][2].
An attacker can exploit this by crafting XWiki syntax links or other content that includes a data attribute with invalid characters. For example, [[Link>>https://XWiki.example.com||data-x/onmouseover="alert('XSS1')"]] will execute JavaScript when a user hovers over the link. No authentication is required as long as the attacker can create or modify content that is rendered by XWiki [3].
Successful exploitation results in Cross-Site Scripting (XSS) in the victim's browser. If the victim has programming rights, the attacker may escalate to server-side code execution, compromising confidentiality, integrity, and availability of the XWiki instance [3].
The vulnerability is patched in XWiki 14.10.4 and 15.0 RC1 by changing the regex validation from .find() to .matches() to ensure data attribute names contain only allowed characters [2]. There are no known workarounds; upgrading is required [3].
AI Insight generated on May 20, 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.
| Package | Affected versions | Patched versions |
|---|---|---|
org.xwiki.commons:xwiki-commons-xmlMaven | >= 14.6-rc-1, < 14.10.4 | 14.10.4 |
Affected products
2- Range: >= 14.6-rc-1, < 14.10.4
Patches
10b8e9c45b7e7XCOMMONS-2606: Properly validate data attributes in SecureHTMLElementSanitizer
2 files changed · +32 −2
xwiki-commons-core/xwiki-commons-xml/src/main/java/org/xwiki/xml/internal/html/SecureHTMLElementSanitizer.java+13 −2 modified@@ -65,7 +65,18 @@ public class SecureHTMLElementSanitizer implements HTMLElementSanitizer, Initial static final Pattern ATTR_WHITESPACE = Pattern.compile("[\\u0000-\\u0020\\u00A0\\u1680\\u180E\\u2000-\\u2029\\u205F\\u3000]"); - static final Pattern DATA_ATTR = Pattern.compile("^data-[\\-\\w.\\u00B7-\\uFFFF]"); + /** + * Pattern that matches valid data-attributes. + * <p> + * Following the <a href="https://html.spec.whatwg.org/multipage/dom.html + #embedding-custom-non-visible-data-with-the-data-*-attributes">HTML standard</a> + * this means that the name starts with "data-", has at least one character after the hyphen and is + * <a href="https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible">XML-compatible</a>, + * i.e., matches the <a href="https://www.w3.org/TR/xml/#NT-Name">Name production</a> without ":". + */ + static final Pattern DATA_ATTR = Pattern.compile("^data-[A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6" + + "\\u00F8-\\u02ff\\u0370-\\u037d\\u037f-\\u1fff\\u200c\\u200d\\u2070-\\u218f\\u2c00-\\u2fef\\u3001-\\ud7ff" + + "\\uf900-\\ufdcf\\ufdf0-\\ufffd\\x{10000}-\\x{EFFFF}\\-.0-9\\u00b7\\u0300-\\u036f\\u203f-\\u2040]+$"); static final Pattern ARIA_ATTR = Pattern.compile("^aria-[\\-\\w]+$"); @@ -182,7 +193,7 @@ public boolean isAttributeAllowed(String elementName, String attributeName, Stri String lowerElement = elementName.toLowerCase(); String lowerAttribute = attributeName.toLowerCase(); - if ((DATA_ATTR.matcher(lowerAttribute).find() || ARIA_ATTR.matcher(lowerAttribute).find()) + if ((DATA_ATTR.matcher(lowerAttribute).matches() || ARIA_ATTR.matcher(lowerAttribute).matches()) && !this.forbidAttributes.contains(lowerAttribute)) { result = true;
xwiki-commons-core/xwiki-commons-xml/src/test/java/org/xwiki/xml/internal/html/SecureHTMLElementSanitizerTest.java+19 −0 modified@@ -23,13 +23,16 @@ import java.util.Collections; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.xwiki.test.annotation.BeforeComponent; import org.xwiki.test.annotation.ComponentList; import org.xwiki.test.junit5.mockito.ComponentTest; import org.xwiki.test.junit5.mockito.InjectMockComponents; import org.xwiki.test.junit5.mockito.MockComponent; import org.xwiki.xml.html.HTMLConstants; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.when; @@ -133,4 +136,20 @@ void restrictedURIs() assertFalse(this.secureHTMLElementSanitizer.isAttributeAllowed(HTMLConstants.TAG_A, HTMLConstants.ATTRIBUTE_HREF, "http://example.com")); } + + @ParameterizedTest + @CsvSource({ + "data-, false", + "data-a, true", + "data-x-wiki.test_\u0192, true", + "data-x\u2713, false", + "data-x/test, false", + "data-x>test, false", + "data-x:y, false" + }) + void dataAttributes(String attribute, boolean accepted) + { + assertEquals(accepted, this.secureHTMLElementSanitizer.isAttributeAllowed(HTMLConstants.TAG_DIV, attribute, + "hello")); + } }
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-pv7v-ph6g-3gxvghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2023-31126ghsaADVISORY
- github.com/xwiki/xwiki-commons/commit/0b8e9c45b7e7457043938f35265b2aa5adc76a68ghsax_refsource_MISCWEB
- github.com/xwiki/xwiki-commons/security/advisories/GHSA-pv7v-ph6g-3gxvghsax_refsource_CONFIRMWEB
- jira.xwiki.org/browse/XCOMMONS-2606ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.