XWiki Platform: Password and email exposure in xml.vm fields
Description
XWiki Platform is a generic wiki platform offering runtime services for applications built on top of it. XWiki Platform Legacy Old Core and XWiki Platform Old Core versions 1.1 through 16.4.6, 16.5.0-rc-1 through 16.10.4 and 17.0.0-rc-1 through 17.1.0, the XML export of a page in XWiki that can be triggered by any user with view rights on a page by appending ?xpage=xml to the URL includes password and email properties stored on a document that aren't named password or email. This is fixed in versions 16.4.7, 16.10.5 and 17.2.0-rc-1. To work around this issue, the file templates/xml.vm in the deployed WAR can be deleted if the XML isn't needed. There isn't any feature in XWiki itself that depends on the XML export.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.xwiki.platform:xwiki-platform-oldcoreMaven | >= 1.1, < 16.4.7 | 16.4.7 |
org.xwiki.platform:xwiki-platform-oldcoreMaven | >= 16.5.0-rc-1, < 16.10.5 | 16.10.5 |
org.xwiki.platform:xwiki-platform-oldcoreMaven | >= 17.0.0-rc-1, < 17.2.0-rc-1 | 17.2.0-rc-1 |
org.xwiki.platform:xwiki-platform-legacy-oldcoreMaven | >= 1.1, < 16.4.7 | 16.4.7 |
org.xwiki.platform:xwiki-platform-legacy-oldcoreMaven | >= 16.5.0-rc-1, < 16.10.5 | 16.10.5 |
org.xwiki.platform:xwiki-platform-legacy-oldcoreMaven | >= 17.0.0-rc-1, < 17.2.0-rc-1 | 17.2.0-rc-1 |
Affected products
1- Range: >= 1.1, < 16.4.7
Patches
1742ee3482ef6XWIKI-22810: Introduce and use proper APIs to filter sensitive fields from exports
4 files changed · +188 −5
xwiki-platform-core/xwiki-platform-filter/xwiki-platform-filter-instance/xwiki-platform-filter-instance-document/src/main/java/org/xwiki/filter/instance/input/DocumentInstanceInputProperties.java+35 −0 modified@@ -24,6 +24,7 @@ import org.xwiki.properties.annotation.PropertyDescription; import org.xwiki.properties.annotation.PropertyName; +import org.xwiki.stability.Unstable; /** * @version $Id$ @@ -81,6 +82,11 @@ public class DocumentInstanceInputProperties extends InstanceInputProperties */ private boolean withWikiDocumentContentHTML; + /** + * @see #getExcludedPropertyTypes() + */ + private Set<String> excludedPropertyTypes; + /** * @return Indicates if events should be generated for history */ @@ -270,4 +276,33 @@ public void setWithWikiDocumentContentHTML(boolean withWikiDocumentContentHTML) { this.withWikiDocumentContentHTML = withWikiDocumentContentHTML; } + + /** + * @return the class property types (like "Email" or "Password") for whose values no events shall be created + * when generating events for objects + * @since 16.4.7 + * @since 16.10.5 + * @since 17.2.0RC1 + */ + @PropertyName("Excluded property types") + @PropertyDescription("The class property types (like \"Email\" or \"Password\") for whose values no " + + "events shall be created when generating events for objects") + @Unstable + public Set<String> getExcludedPropertyTypes() + { + return this.excludedPropertyTypes != null ? this.excludedPropertyTypes : Set.of(); + } + + /** + * @param excludedPropertyTypes the class property types (like "Email" or "Password") for whose values no events + * shall be created when generating events for objects + * @since 16.4.7 + * @since 16.10.5 + * @since 17.2.0RC1 + */ + @Unstable + public void setExcludedPropertyTypes(Set<String> excludedPropertyTypes) + { + this.excludedPropertyTypes = excludedPropertyTypes; + } }
xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/api/Document.java+24 −4 modified@@ -21,13 +21,15 @@ import java.io.IOException; import java.io.InputStream; +import java.io.StringWriter; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Set; import java.util.Vector; import org.apache.commons.fileupload.FileItem; @@ -43,6 +45,10 @@ import org.xwiki.context.Execution; import org.xwiki.context.ExecutionContext; import org.xwiki.display.internal.DocumentDisplayerParameters; +import org.xwiki.filter.instance.input.DocumentInstanceInputProperties; +import org.xwiki.filter.output.DefaultWriterOutputTarget; +import org.xwiki.filter.output.OutputTarget; +import org.xwiki.filter.xar.output.XAROutputProperties; import org.xwiki.internal.document.DocumentRequiredRightsReader; import org.xwiki.model.document.DocumentAuthors; import org.xwiki.model.internal.document.SafeDocumentAuthors; @@ -1419,10 +1425,24 @@ private Object newObjectApi(BaseObject obj, XWikiContext context) public String getXMLContent() throws XWikiException { - String xml = this.doc.getXMLContent(getXWikiContext()); - return getXWikiContext().getUtil().substitute("s/<email>.*?<\\/email>/<email>********<\\/email>/goi", - getXWikiContext().getUtil().substitute("s/<password>.*?<\\/password>/<password>********<\\/password>/goi", - xml)); + StringWriter writer = new StringWriter(); + OutputTarget outputTarget = new DefaultWriterOutputTarget(writer); + + DocumentInstanceInputProperties documentProperties = new DocumentInstanceInputProperties(); + documentProperties.setWithWikiDocumentContentHTML(true); + documentProperties.setWithWikiAttachmentsContent(false); + documentProperties.setWithJRCSRevisions(false); + documentProperties.setWithRevisions(false); + documentProperties.setExcludedPropertyTypes(Set.of("Email", "Password")); + + // Output + XAROutputProperties xarProperties = new XAROutputProperties(); + xarProperties.setPreserveVersion(false); + xarProperties.setTarget(outputTarget); + + this.doc.toXML(documentProperties, xarProperties); + + return writer.toString(); } public String toXML() throws XWikiException
xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/internal/filter/input/BaseObjectEventGenerator.java+6 −1 modified@@ -27,6 +27,7 @@ import javax.inject.Provider; import javax.inject.Singleton; +import org.apache.commons.lang3.StringUtils; import org.xwiki.component.annotation.Component; import org.xwiki.component.util.DefaultParameterizedType; import org.xwiki.filter.FilterEventParameters; @@ -41,6 +42,7 @@ import com.xpn.xwiki.objects.BaseObject; import com.xpn.xwiki.objects.BaseProperty; import com.xpn.xwiki.objects.classes.BaseClass; +import com.xpn.xwiki.objects.classes.PropertyClass; /** * @version $Id$ @@ -98,7 +100,10 @@ public void write(BaseObject xobject, Object filter, BaseObjectFilter objectFilt BaseProperty<?> xproperty = it.next(); String pname = xproperty.getName(); - if (pname != null && !pname.trim().equals("")) { + if (StringUtils.isNotBlank(pname) + && (!(xclass.get(pname) instanceof PropertyClass propertyClass) + || !properties.getExcludedPropertyTypes().contains(propertyClass.getClassType()))) + { ((BasePropertyEventGenerator) this.propertyEventGenerator).write(xproperty, filter, (Map<String, Object>) properties); }
xwiki-platform-core/xwiki-platform-oldcore/src/test/java/com/xpn/xwiki/api/DocumentTest.java+123 −0 modified@@ -19,7 +19,9 @@ */ package com.xpn.xwiki.api; +import java.util.Date; import java.util.List; +import java.util.Locale; import javax.inject.Named; @@ -41,11 +43,14 @@ import org.xwiki.model.reference.DocumentReference; import org.xwiki.model.reference.ObjectReference; import org.xwiki.observation.ObservationManager; +import org.xwiki.rendering.syntax.Syntax; import org.xwiki.security.authorization.AccessDeniedException; import org.xwiki.security.authorization.AuthorizationException; import org.xwiki.security.authorization.AuthorizationManager; import org.xwiki.security.authorization.Right; import org.xwiki.sheet.SheetBinder; +import org.xwiki.skin.Skin; +import org.xwiki.skin.SkinManager; import org.xwiki.test.LogLevel; import org.xwiki.test.annotation.ComponentList; import org.xwiki.test.junit5.LogCaptureExtension; @@ -59,9 +64,11 @@ import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.doc.DocumentRevisionProvider; import com.xpn.xwiki.doc.XWikiDocument; +import com.xpn.xwiki.internal.cache.rendering.RenderingCache; import com.xpn.xwiki.objects.BaseObject; import com.xpn.xwiki.objects.BaseProperty; import com.xpn.xwiki.test.MockitoOldcore; +import com.xpn.xwiki.test.component.XWikiDocumentFilterUtilsComponentList; import com.xpn.xwiki.test.junit5.mockito.InjectMockitoOldcore; import com.xpn.xwiki.test.junit5.mockito.OldcoreTest; import com.xpn.xwiki.test.reference.ReferenceComponentList; @@ -74,6 +81,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.ArgumentMatchers.same; @@ -86,6 +94,7 @@ @OldcoreTest @ReferenceComponentList @ComponentList({ DocumentRequiredRightsReader.class, RequiredRightClassMandatoryDocumentInitializer.class }) +@XWikiDocumentFilterUtilsComponentList class DocumentTest { @InjectMockitoOldcore @@ -107,6 +116,12 @@ class DocumentTest @MockComponent private ContextualLocalizationManager contextualLocalizationManager; + @MockComponent + private SkinManager skinManager; + + @MockComponent + private RenderingCache renderingCache; + @RegisterExtension private LogCaptureExtension logCapture = new LogCaptureExtension(LogLevel.INFO); @@ -630,4 +645,112 @@ void getDocumentRevision(boolean allowAccess, MockitoComponentManager componentM } verify(revisionProvider).checkAccess(Right.VIEW, CurrentUserReference.INSTANCE, documentReference, revision); } + + @Test + void getXML() throws XWikiException + { + Skin skin = mock(); + when(this.skinManager.getCurrentSkin(anyBoolean())).thenReturn(skin); + when(skin.getOutputSyntax()).thenReturn(Syntax.HTML_5_0); + + XWikiDocument classDocument = + this.oldcore.getSpyXWiki().getDocument(new DocumentReference("Wiki", "XWiki", "TestClass"), + this.oldcore.getXWikiContext()); + String passwordField = "secret"; + classDocument.getXClass().addPasswordField(passwordField, "Secret Field", 20); + String emailField = "contact"; + classDocument.getXClass().addEmailField(emailField, "Contact", 20); + String textField = "name"; + classDocument.getXClass().addTextField(textField, "Name", 10); + this.oldcore.getSpyXWiki().saveDocument(classDocument, this.oldcore.getXWikiContext()); + + DocumentReference documentReference = new DocumentReference("Wiki", "Space", "Page"); + String content = "content"; + when(this.renderingCache.getRenderedContent(new DocumentReference(documentReference, Locale.ROOT), content, + this.oldcore.getXWikiContext())).thenReturn("Rendered content"); + XWikiDocument xdoc = new XWikiDocument(documentReference); + BaseObject object = xdoc.newXObject(classDocument.getDocumentReference(), this.oldcore.getXWikiContext()); + object.set(passwordField, "MySecret", this.oldcore.getXWikiContext()); + object.set(emailField, "hello@example.com", this.oldcore.getXWikiContext()); + object.set(textField, "John", this.oldcore.getXWikiContext()); + xdoc.setContent(content); + long timestamp = 1740501147000L; + Date date = new Date(timestamp); + xdoc.setCreationDate(date); + xdoc.setContentUpdateDate(date); + xdoc.setDate(date); + Document document = new Document(xdoc, this.oldcore.getXWikiContext()); + assertEquals( + """ + <?xml version='1.1' encoding='UTF-8'?> + <xwikidoc version="1.6" reference="Space.Page" locale=""> + <web>Space</web> + <name>Page</name> + <language/> + <defaultLanguage/> + <translation>0</translation> + <creator>XWiki.XWikiGuest</creator> + <creationDate>1740501147000</creationDate> + <author>XWiki.XWikiGuest</author> + <originalMetadataAuthor>XWiki.XWikiGuest</originalMetadataAuthor> + <contentAuthor>XWiki.XWikiGuest</contentAuthor> + <date>1740501147000</date> + <contentUpdateDate>1740501147000</contentUpdateDate> + <version>1.1</version> + <title/> + <comment/> + <minorEdit>false</minorEdit> + <syntaxId>xwiki/2.1</syntaxId> + <hidden>false</hidden> + <content>content</content> + <renderedcontent>Rendered content</renderedcontent> + <object> + <name>Space.Page</name> + <number>0</number> + <className>XWiki.TestClass</className> + <guid>%s</guid> + <class> + <name>XWiki.TestClass</name> + <customClass/> + <customMapping/> + <defaultViewSheet/> + <defaultEditSheet/> + <defaultWeb/> + <nameField/> + <validationScript/> + <contact> + <disabled>0</disabled> + <name>contact</name> + <number>2</number> + <prettyName>Contact</prettyName> + <size>20</size> + <unmodifiable>0</unmodifiable> + <validationRegExp>/^(([^@\\s]+)@((?:[-a-zA-Z0-9]+\\.)+[a-zA-Z]{2,}))?$/</validationRegExp> + <classType>com.xpn.xwiki.objects.classes.EmailClass</classType> + </contact> + <name> + <disabled>0</disabled> + <name>name</name> + <number>3</number> + <prettyName>Name</prettyName> + <size>10</size> + <unmodifiable>0</unmodifiable> + <classType>com.xpn.xwiki.objects.classes.StringClass</classType> + </name> + <secret> + <disabled>0</disabled> + <name>secret</name> + <number>1</number> + <prettyName>Secret Field</prettyName> + <size>20</size> + <unmodifiable>0</unmodifiable> + <classType>com.xpn.xwiki.objects.classes.PasswordClass</classType> + </secret> + </class> + <property> + <name>John</name> + </property> + </object> + </xwikidoc>""".formatted(object.getGuid()), document.getXMLContent()); + } }
Vulnerability mechanics
Generated by null/stub 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-57q2-6cp4-9mq3ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-54125ghsaADVISORY
- github.com/xwiki/xwiki-platform/commit/742ee3482ef6c2bd4ad03d0de9cdd81d0e8f3d59ghsax_refsource_MISCWEB
- github.com/xwiki/xwiki-platform/security/advisories/GHSA-57q2-6cp4-9mq3ghsax_refsource_CONFIRMWEB
- jira.xwiki.org/browse/XWIKI-22810ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.