High severityNVD Advisory· Published Jun 13, 2025· Updated Jun 13, 2025
XWiki allows remote code execution through preview of XClass changes in AWM editor
CVE-2025-49586
Description
XWiki is an open-source wiki software platform. Any XWiki user with edit right on at least one App Within Minutes application (the default for all users XWiki) can obtain programming right/perform remote code execution by editing the application. This vulnerability has been fixed in XWiki 17.0.0, 16.4.7, and 16.10.3.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.xwiki.platform:xwiki-platform-oldcoreMaven | >= 7.2-milestone-2, < 16.4.7 | 16.4.7 |
org.xwiki.platform:xwiki-platform-oldcoreMaven | >= 16.5.0-rc-1, < 16.10.3 | 16.10.3 |
org.xwiki.platform:xwiki-platform-oldcoreMaven | >= 17.0.0-rc-1, < 17.0.0 | 17.0.0 |
Affected products
1- Range: >= 7.2-milestone-2, < 16.4.7
Patches
1ef978315649cXWIKI-22719: Custom displayers: use the author of the owner document
2 files changed · +93 −11
xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/objects/classes/PropertyClass.java+22 −3 modified@@ -35,6 +35,7 @@ import org.xwiki.model.reference.EntityReference; import org.xwiki.script.ScriptContextManager; import org.xwiki.security.authorization.AuthorExecutor; +import org.xwiki.stability.Unstable; import org.xwiki.template.Template; import org.xwiki.template.TemplateManager; @@ -307,8 +308,7 @@ public void displayCustom(StringBuffer buffer, String fieldName, String prefix, if (StringUtils.isNotEmpty(customDisplayer)) { if (customDisplayer.equals(CLASS_DISPLAYER_IDENTIFIER)) { final String rawContent = getCustomDisplay(); - XWikiDocument classDocument = - context.getWiki().getDocument(getObject().getDocumentReference(), context); + XWikiDocument classDocument = getObject().getOwnerDocument(); final String classSyntax = classDocument.getSyntax().toIdString(); // Using author reference since the document content is not relevant in this case. DocumentReference authorReference = classDocument.getAuthorReference(); @@ -322,7 +322,7 @@ public void displayCustom(StringBuffer buffer, String fieldName, String prefix, // Make sure we render the custom displayer with the rights of the user who wrote it (i.e. class // document author). content = renderContentInContext(rawContent, classSyntax, authorReference, - classDocument.getDocumentReference(), context); + classDocument.getDocumentReference(), classDocument.isRestricted(), context); } else if (customDisplayer.startsWith(DOCUMENT_DISPLAYER_IDENTIFIER_PREFIX)) { XWikiDocument displayerDoc = context.getWiki().getDocument( StringUtils.substringAfter(customDisplayer, DOCUMENT_DISPLAYER_IDENTIFIER_PREFIX), context); @@ -376,6 +376,25 @@ protected String renderContentInContext(final String content, final String synta .call(() -> context.getDoc().getRenderedContent(content, syntax, context), authorReference, secureDocument); } + /** + * Render content in the current document's context with the rights of the given user. + * + * @since 17.0.0 + * @since 16.10.3 + * @since 16.4.7 + */ + @Unstable + protected String renderContentInContext(final String content, final String syntax, + DocumentReference authorReference, DocumentReference secureDocument, + boolean restricted, final XWikiContext context) + throws Exception + { + return Utils.getComponent(AuthorExecutor.class) + .call(() -> context.getDoc().getRenderedContent(content, syntax, restricted, context), authorReference, + secureDocument); + } + + @Override public String getClassName() {
xwiki-platform-core/xwiki-platform-oldcore/src/test/java/com/xpn/xwiki/objects/classes/PropertyClassTest.java+71 −8 modified@@ -23,16 +23,26 @@ import java.util.Random; import java.util.concurrent.Callable; +import javax.inject.Named; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.xwiki.display.internal.DocumentDisplayer; +import org.xwiki.display.internal.DocumentDisplayerParameters; +import org.xwiki.job.event.status.JobProgressManager; import org.xwiki.model.reference.DocumentReference; import org.xwiki.model.reference.DocumentReferenceResolver; import org.xwiki.model.reference.EntityReferenceSerializer; +import org.xwiki.rendering.block.XDOM; +import org.xwiki.rendering.renderer.BlockRenderer; +import org.xwiki.rendering.renderer.printer.WikiPrinter; import org.xwiki.rendering.syntax.Syntax; +import org.xwiki.rendering.transformation.RenderingContext; import org.xwiki.security.authorization.AuthorExecutor; import org.xwiki.test.junit5.mockito.MockComponent; 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.meta.PropertyMetaClass; import com.xpn.xwiki.objects.meta.TextAreaMetaClass; @@ -42,8 +52,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doReturn; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** @@ -55,12 +69,32 @@ @OldcoreTest public class PropertyClassTest { + protected static final String CUSTOM_DISPLAY = "test"; + @InjectMockitoOldcore private MockitoOldcore oldCore; @MockComponent private AuthorExecutor authorExecutor; + @MockComponent + @Named("configured") + private DocumentDisplayer documentDisplayer; + + @MockComponent + private RenderingContext renderingContext; + + // Needed for XWikiDocument#getRenderedContent. + @MockComponent + private JobProgressManager jobProgressManager; + + @MockComponent + private RenderingCache renderingCache; + + @MockComponent + @Named("html/5.0") + private BlockRenderer htmlRenderer; + private BaseClass xclass = new BaseClass(); @BeforeEach @@ -69,14 +103,16 @@ public void before() throws Exception DocumentReference classReference = new DocumentReference("wiki", Arrays.asList("Path", "To"), "Class"); XWikiDocument classDocument = new XWikiDocument(classReference); classDocument.setSyntax(Syntax.XWIKI_2_1); - doReturn(classDocument).when(this.oldCore.getSpyXWiki()).getDocument(classReference, - this.oldCore.getXWikiContext()); - this.xclass.setOwnerDocument(classDocument); this.oldCore.getMocker().registerMockComponent(EntityReferenceSerializer.TYPE_STRING, "compactwiki"); this.oldCore.getMocker().registerMockComponent(DocumentReferenceResolver.TYPE_STRING, "currentmixed"); this.oldCore.getMocker().registerMockComponent(EntityReferenceSerializer.TYPE_STRING, "local"); + + DocumentReference contextDocumentReference = new DocumentReference("wiki", "XWiki", "Context"); + this.oldCore.getXWikiContext().setDoc(new XWikiDocument(contextDocumentReference)); + + when(this.renderingContext.getTargetSyntax()).thenReturn(Syntax.HTML_5_0); } /** Test the {@link PropertyClass#compareTo(PropertyClass)} method. */ @@ -124,6 +160,20 @@ public void displayCustomWithClassDisplayer() throws Exception displayCustomWithAuthor(authorReference, this.xclass.getDocumentReference()); } + @Test + void displayCustomWithRestrictedDocument() throws Exception + { + DocumentReference authorReference = new DocumentReference("wiki", "XWiki", "Bob"); + + this.xclass.getOwnerDocument().setRestricted(true); + this.xclass.getOwnerDocument().setAuthorReference(authorReference); + + displayCustomWithAuthor(authorReference, this.xclass.getDocumentReference()); + + verify(this.documentDisplayer).display(argThat(doc -> CUSTOM_DISPLAY.equals(doc.getContent())), + argThat(DocumentDisplayerParameters::isTransformationContextRestricted)); + } + @Test public void displayCustomWithClassDisplayerAndClassIsNew() throws Exception { @@ -147,19 +197,32 @@ public void displayCustomWithClassDisplayerAndGuestAuthor() throws Exception private void displayCustomWithAuthor(DocumentReference authorReference, DocumentReference secureDocument) throws Exception { - when(this.authorExecutor.call(any(Callable.class), eq(authorReference), eq(secureDocument))) - .thenReturn("output"); + when(this.authorExecutor.call(any(), eq(authorReference), eq(secureDocument))) + .then(invocationOnMock -> { + Callable<String> callable = invocationOnMock.getArgument(0); + return callable.call(); + }); + + String output = "output"; + XDOM mockXDOM = mock(); + when(this.documentDisplayer.display(any(), any())).thenReturn(mockXDOM); + + doAnswer(invocationOnMock -> { + WikiPrinter printer = invocationOnMock.getArgument(1); + printer.print(output); + return null; + }).when(this.htmlRenderer).render(same(mockXDOM), any()); PropertyClass propertyClass = new PropertyClass(); - propertyClass.setCustomDisplay("test"); + propertyClass.setCustomDisplay(CUSTOM_DISPLAY); propertyClass.setObject(this.xclass); StringBuffer buffer = new StringBuffer(); propertyClass.displayCustom(buffer, "date", "Path.To.Class_0_", "edit", new BaseObject(), this.oldCore.getXWikiContext()); - assertEquals("output", buffer.toString()); + assertEquals(output, buffer.toString()); } @Test
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-jp4x-w9cj-97q7ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-49586ghsaADVISORY
- github.com/xwiki/xwiki-platform/commit/ef978315649cf83eae396021bb33603a1a5f7e42ghsax_refsource_MISCWEB
- github.com/xwiki/xwiki-platform/security/advisories/GHSA-jp4x-w9cj-97q7ghsax_refsource_CONFIRMWEB
- jira.xwiki.org/browse/XWIKI-22719ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.