VYPR
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.

PackageAffected versionsPatched versions
org.xwiki.platform:xwiki-platform-oldcoreMaven
>= 7.2-milestone-2, < 16.4.716.4.7
org.xwiki.platform:xwiki-platform-oldcoreMaven
>= 16.5.0-rc-1, < 16.10.316.10.3
org.xwiki.platform:xwiki-platform-oldcoreMaven
>= 17.0.0-rc-1, < 17.0.017.0.0

Affected products

1

Patches

1
ef978315649c

XWIKI-22719: Custom displayers: use the author of the owner document

https://github.com/xwiki/xwiki-platformMichael HamannJan 10, 2025via ghsa
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

News mentions

0

No linked articles in our index yet.