CVE-2023-26471 (published 2023-03-02T19:15Z)

XWiki Platform is a generic wiki platform. Starting in version 11.6-rc-1, comments are supposed to be executed with the right of superadmin but in restricted mode (anything dangerous is disabled), but the async macro does not take into account the restricted mode. This means that any user with comment right can use the async macro to make it execute any wiki content with the right of superadmin. This has been patched in XWiki 14.9, 14.4.6, and 13.10.10. The only known workaround consists of applying a patch and rebuilding and redeploying `org.xwiki.platform:xwiki-platform-rendering-async-macro`.

CVSS v3 base score: 8.8, HIGH (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: NVD-CWE-noinfo
cwe_name: Insufficient Information
description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/xwiki/xwiki-platform
commit_details: repo_url: https://github.com/xwiki/xwiki-platform
author: Thomas Mortagne
author_date: 2022-10-18 11:33:04+02:00
msg: XWIKI-20234: It's possible to execute anything with superadmin right through comments and async macro
repository: repo_url: https://github.com/xwiki/xwiki-platform
repo_name: xwiki/xwiki-platform
description: The XWiki platform
repo_language: Java
owner: xwiki
file_changes: filename: BlockAsyncRendererConfiguration.java
change_type: ModificationType.MODIFY
diff: @@ -61,6 +61,8 @@ public class BlockAsyncRendererConfiguration extends AsyncRendererConfiguration
 
     private boolean inline;
 
+    private boolean resricted;
+
     private BlockAsyncRendererDecorator decorator;
 
     private Set<EntityReference> references;
@@ -238,6 +240,28 @@ public void setInline(boolean inline)
         this.inline = inline;
     }
 
+    /**
+     * @return indicator of whether the transformation context is restricted or not
+     * @since 14.9
+     * @since 14.4.6
+     * @since 13.10.10
+     */
+    public boolean isResricted()
+    {
+        return this.resricted;
+    }
+
+    /**
+     * @param resricted indicator of whether the transformation context is restricted or not
+     * @since 14.9
+     * @since 14.4.6
+     * @since 13.10.10
+     */
+    public void setResricted(boolean resricted)
+    {
+        this.resricted = resricted;
+    }
+
     /**
      * @return the decorator
      */

programming_language: Java
filename: DefaultBlockAsyncRenderer.java
change_type: ModificationType.MODIFY
diff: @@ -147,7 +147,7 @@ public Block execute(boolean async, boolean cached) throws RenderingException
     private Block tranform(XDOM xdom, Block block) throws RenderingException
     {
         TransformationContext transformationContext =
-            new TransformationContext(xdom, this.configuration.getDefaultSyntax(), false);
+            new TransformationContext(xdom, this.configuration.getDefaultSyntax(), this.configuration.isResricted());
         transformationContext.setTargetSyntax(this.configuration.getTargetSyntax());
         transformationContext.setId(this.configuration.getTransformationId());
 

programming_language: Java
filename: AbstractExecutedContentMacro.java
change_type: ModificationType.MODIFY
diff: @@ -142,6 +142,9 @@ protected BlockAsyncRendererConfiguration createBlockAsyncRendererConfiguration(
         // Set the transformation id
         configuration.setTransformationId(context.getTransformationContext().getId());
 
+        // Indicate if we are in a restricted mode
+        configuration.setResricted(context.getTransformationContext().isRestricted());
+
         return configuration;
     }
 }

programming_language: Java
filename: AsyncMacroTest.java
change_type: ModificationType.ADD
diff: @@ -0,0 +1,107 @@
+/*
+ * See the NOTICE file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.xwiki.rendering.async;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.xwiki.rendering.async.internal.AsyncMacro;
+import org.xwiki.rendering.async.internal.block.BlockAsyncRendererConfiguration;
+import org.xwiki.rendering.async.internal.block.BlockAsyncRendererExecutor;
+import org.xwiki.rendering.block.MacroBlock;
+import org.xwiki.rendering.block.WordBlock;
+import org.xwiki.rendering.block.XDOM;
+import org.xwiki.rendering.listener.MetaData;
+import org.xwiki.rendering.macro.MacroContentParser;
+import org.xwiki.rendering.syntax.Syntax;
+import org.xwiki.rendering.transformation.MacroTransformationContext;
+import org.xwiki.test.TestEnvironment;
+import org.xwiki.test.annotation.ComponentList;
+import org.xwiki.test.junit5.mockito.ComponentTest;
+import org.xwiki.test.junit5.mockito.InjectComponentManager;
+import org.xwiki.test.junit5.mockito.InjectMockComponents;
+import org.xwiki.test.junit5.mockito.MockComponent;
+import org.xwiki.test.mockito.MockitoComponentManager;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.same;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Unit tests for {@link AsyncMacro}.
+ *
+ * @version $Id$
+ * @since 8.3RC1
+ */
+@ComponentTest
+@ComponentList(TestEnvironment.class)
+class AsyncMacroTest
+{
+    @InjectMockComponents
+    private AsyncMacro macro;
+
+    @InjectComponentManager
+    private MockitoComponentManager componentManager;
+
+    @MockComponent
+    private MacroContentParser parser;
+
+    private BlockAsyncRendererExecutor executor;
+
+    @BeforeEach
+    public void beforeEach() throws Exception
+    {
+        this.executor = this.componentManager.getInstance(BlockAsyncRendererExecutor.class);
+    }
+
+    @Test
+    void executeInRestrictedMode() throws Exception
+    {
+        MacroBlock macroBlock = new MacroBlock("async", Collections.<String, String>emptyMap(), false);
+        MetaData metadata = new MetaData();
+        metadata.addMetaData(MetaData.SOURCE, "source");
+        XDOM pageXDOM = new XDOM(Arrays.asList(macroBlock), metadata);
+        MacroTransformationContext macroContext = new MacroTransformationContext();
+        macroContext.setSyntax(Syntax.XWIKI_2_0);
+        macroContext.setCurrentMacroBlock(macroBlock);
+        macroContext.setXDOM(pageXDOM);
+        macroContext.getTransformationContext().setRestricted(true);
+
+        XDOM contentXDOM = new XDOM(Arrays.asList(new WordBlock("test")), metadata);
+        when(this.parser.parse(eq(""), same(macroContext), eq(false), eq(false))).thenReturn(contentXDOM);
+
+        when(this.executor.execute(any())).thenReturn(new WordBlock("result"));
+
+        this.macro.execute(new AsyncMacroParameters(), "", macroContext);
+
+        ArgumentCaptor<BlockAsyncRendererConfiguration> configurationCaptor =
+            ArgumentCaptor.forClass(BlockAsyncRendererConfiguration.class);
+        verify(this.executor).execute(configurationCaptor.capture());
+
+        BlockAsyncRendererConfiguration configuration = configurationCaptor.getValue();
+        assertTrue(configuration.isResricted());
+    }
+}

programming_language: Java
filename: IntegrationTests.java
change_type: ModificationType.RENAME
diff: @@ -17,7 +17,7 @@
  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  */
-package org.xwiki.rendering.macro.box;
+package org.xwiki.rendering.async;
 
 import java.util.List;
 
@@ -33,7 +33,6 @@
 import org.xwiki.model.reference.EntityReference;
 import org.xwiki.observation.ObservationManager;
 import org.xwiki.observation.event.Event;
-import org.xwiki.rendering.async.AsyncContext;
 import org.xwiki.rendering.async.internal.AsyncRendererJobRequest;
 import org.xwiki.rendering.async.internal.AsyncRendererJobStatus;
 import org.xwiki.rendering.test.integration.RenderingTestSuite;

programming_language: Java
filename: ContextMacroTest.java
change_type: ModificationType.MODIFY
diff: @@ -162,6 +162,17 @@ void executeWithReferencedDocumentNotViewableByTheAuthor() throws Exception
 
     @Test
     void executeOk() throws Exception
+    {
+        execute(false);
+    }
+
+    @Test
+    void executeInRestrictedMode() throws Exception
+    {
+        execute(true);
+    }
+
+    private void execute(boolean restricted) throws Exception
     {
         MacroBlock macroBlock = new MacroBlock("context", Collections.<String, String>emptyMap(), false);
         MetaData metadata = new MetaData();
@@ -171,12 +182,14 @@ void executeOk() throws Exception
         macroContext.setSyntax(Syntax.XWIKI_2_0);
         macroContext.setCurrentMacroBlock(macroBlock);
         macroContext.setXDOM(pageXDOM);
+        macroContext.getTransformationContext().setRestricted(restricted);
 
         DocumentModelBridge dmb = mock(DocumentModelBridge.class);
         when(this.dab.getTranslatedDocumentInstance(TARGET_REFERENCE)).thenReturn(dmb);
 
         XDOM contentXDOM = new XDOM(Arrays.asList(new WordBlock("test")), metadata);
-        when(this.parser.parse(eq(""), same(macroContext), eq(false), any(MetaData.class), eq(false))).thenReturn(contentXDOM);
+        when(this.parser.parse(eq(""), same(macroContext), eq(false), any(MetaData.class), eq(false)))
+            .thenReturn(contentXDOM);
 
         ContextMacroParameters parameters = new ContextMacroParameters();
         parameters.setDocument("target");
@@ -193,5 +206,6 @@ void executeOk() throws Exception
         assertEquals(AUTHOR, configuration.getSecureAuthorReference());
         assertEquals(SOURCE_REFERENCE, configuration.getSecureDocumentReference());
         assertSame(pageXDOM, configuration.getXDOM());
+        assertEquals(restricted, configuration.isResricted());
     }
 }

programming_language: Java