Moderate severityNVD Advisory· Published Mar 2, 2023· Updated Mar 5, 2025
XWiki Platform allows macro execution as any user without programming rights through the context macro
CVE-2023-26056
Description
XWiki Platform is a generic wiki platform. Starting in version 3.0-milestone-1, it's possible to execute a script with the right of another user, provided the target user does not have programming right. The problem has been patched in XWiki 14.8-rc-1, 14.4.5, and 13.10.10. There are no known workarounds for this issue.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.xwiki.platform:xwiki-platform-rendering-macro-contextMaven | >= 3.0-milestone-1, < 13.10.10 | 13.10.10 |
org.xwiki.platform:xwiki-platform-rendering-macro-contextMaven | >= 14.0-rc-1, < 14.4.5 | 14.4.5 |
org.xwiki.platform:xwiki-platform-rendering-macro-contextMaven | >= 14.5, < 14.8-rc-1 | 14.8-rc-1 |
Affected products
1- Range: >= 3.0-milestone-1, < 13.10.10
Patches
34b75f212c2ddXWIKI-19856: Bad execution setup on the context macro
2 files changed · +12 −5
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/src/main/java/org/xwiki/rendering/internal/macro/context/ContextMacro.java+2 −0 modified@@ -169,6 +169,8 @@ public List<Block> execute(ContextMacroParameters parameters, String content, Ma // Reuse the very generic async rendering framework (even if we don't do async and caching) since it's taking // care of many other things BlockAsyncRendererConfiguration configuration = createBlockAsyncRendererConfiguration(null, xdom, context); + configuration.setAsyncAllowed(false); + configuration.setCacheAllowed(false); Map<String, Object> backupObjects = new HashMap<>(); try {
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/src/test/java/org/xwiki/rendering/internal/macro/context/ContextMacroTest.java+10 −5 modified@@ -22,7 +22,6 @@ import java.util.Arrays; import java.util.Collections; -import javax.inject.Inject; import javax.inject.Named; import org.junit.jupiter.api.BeforeEach; @@ -51,8 +50,10 @@ 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.assertEquals; import static org.junit.jupiter.api.Assertions.fail; @@ -80,9 +81,6 @@ class ContextMacroTest private static final DocumentReference SOURCE_REFERENCE = new DocumentReference("wiki", "space", "source"); - @Inject - protected BlockAsyncRendererExecutor executor; - @MockComponent private DocumentAccessBridge dab; @@ -100,11 +98,16 @@ class ContextMacroTest private DocumentReferenceResolver<String> macroReferenceResolver; @MockComponent - protected DocumentReferenceResolver<String> resolver; + private DocumentReferenceResolver<String> resolver; @InjectMockComponents private ContextMacro macro; + @InjectComponentManager + private MockitoComponentManager componentManager; + + private BlockAsyncRendererExecutor executor; + @BeforeEach public void beforeEach() throws Exception { @@ -117,6 +120,8 @@ public void beforeEach() throws Exception when(this.macroReferenceResolver.resolve(eq("target"), any())).thenReturn(TARGET_REFERENCE); when(this.resolver.resolve("source")).thenReturn(SOURCE_REFERENCE); + + this.executor = this.componentManager.getInstance(BlockAsyncRendererExecutor.class); } @Test
dd3f4735b419XWIKI-19856: Bad execution setup on the context macro
1 file changed · +3 −3
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/src/main/java/org/xwiki/rendering/internal/macro/context/ContextMacro.java+3 −3 modified@@ -170,14 +170,14 @@ public List<Block> execute(ContextMacroParameters parameters, String content, Ma // care of many other things BlockAsyncRendererConfiguration configuration = createBlockAsyncRendererConfiguration(null, xdom, context); - // Configure the Transformation Context XDOM depending on the mode asked. - configuration.setXDOM(getXDOM(referencedDocReference, parameters)); - Map<String, Object> backupObjects = new HashMap<>(); try { // Switch the context document this.documentAccessBridge.pushDocumentInContext(backupObjects, referencedDocReference); + // Configure the Transformation Context XDOM depending on the mode asked. + configuration.setXDOM(getXDOM(referencedDocReference, parameters)); + // Execute the content Block result = this.executor.execute(configuration);
bd34ad6710edXWIKI-19856: Macro execution as any user without programming rights through the context macro
9 files changed · +421 −300
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-api/src/main/java/org/xwiki/rendering/async/internal/block/BlockAsyncRendererConfiguration.java+31 −2 modified@@ -31,6 +31,7 @@ import org.xwiki.model.reference.EntityReference; import org.xwiki.rendering.async.internal.AsyncRendererConfiguration; import org.xwiki.rendering.block.Block; +import org.xwiki.rendering.block.XDOM; import org.xwiki.rendering.syntax.Syntax; import org.xwiki.rendering.transformation.TransformationContext; @@ -46,6 +47,8 @@ public class BlockAsyncRendererConfiguration extends AsyncRendererConfiguration private Block block; + private XDOM xdom; + private boolean asyncAllowed; private boolean cacheAllowed; @@ -70,8 +73,12 @@ public class BlockAsyncRendererConfiguration extends AsyncRendererConfiguration */ public BlockAsyncRendererConfiguration(List<?> idElements, Block block) { - this.id = new ArrayList<>(idElements.size()); - addElements(idElements); + if (idElements != null) { + this.id = new ArrayList<>(idElements.size()); + addElements(idElements); + } else { + this.id = new ArrayList<>(); + } this.block = block; // Enabled by default @@ -113,6 +120,28 @@ public Block getBlock() return this.block; } + /** + * @return the XDOM to use in the transformation context + * @since 14.8RC1 + * @since 14.4.5 + * @since 13.10.10 + */ + public XDOM getXDOM() + { + return this.xdom; + } + + /** + * @param xdom the XDOM to use in the transformation context + * @since 14.8RC1 + * @since 14.4.5 + * @since 13.10.10 + */ + public void setXDOM(XDOM xdom) + { + this.xdom = xdom; + } + /** * @return true if the execution should be asynchronous if possible */
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-api/src/main/java/org/xwiki/rendering/async/internal/block/DefaultBlockAsyncRenderer.java+3 −5 modified@@ -119,11 +119,9 @@ public Block execute(boolean async, boolean cached) throws RenderingException block = PARSERUTILS.convertToInline(block, true); } - // Create a XDOM instance for the transformation context - XDOM xdom; - if (block instanceof XDOM) { - xdom = (XDOM) block; - } else { + // Get the XDOM instance for the transformation context + XDOM xdom = this.configuration.getXDOM(); + if (xdom == null) { Block rootBlock = block.getRoot(); if (rootBlock instanceof XDOM) {
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-macro/pom.xml+12 −20 modified@@ -37,7 +37,7 @@ <dependencies> <dependency> <groupId>org.xwiki.platform</groupId> - <artifactId>xwiki-platform-rendering-async-default</artifactId> + <artifactId>xwiki-platform-rendering-async-api</artifactId> <version>${project.version}</version> </dependency> <dependency> @@ -52,6 +52,12 @@ </dependency> <!-- Testing dependencies --> + <dependency> + <groupId>org.xwiki.platform</groupId> + <artifactId>xwiki-platform-rendering-async-default</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> <dependency> <groupId>org.xwiki.rendering</groupId> <artifactId>xwiki-rendering-syntax-xwiki21</artifactId> @@ -81,24 +87,10 @@ <artifactId>javax.servlet-api</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.mortbay.jasper</groupId> + <artifactId>apache-el</artifactId> + <scope>test</scope> + </dependency> </dependencies> - <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-checkstyle-plugin</artifactId> - <executions> - <execution> - <!-- Specify the "default" execution id so that the "blocker" one is always executed --> - <id>default</id> - <configuration> - <excludes> - org/xwiki/rendering/internal/macro/display/DisplayMacro.java - </excludes> - </configuration> - </execution> - </executions> - </plugin> - </plugins> - </build> </project>
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-macro/src/main/java/org/xwiki/rendering/async/internal/AbstractExecutedContentMacro.java+147 −0 added@@ -0,0 +1,147 @@ +/* + * 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.internal; + +import java.util.List; + +import javax.inject.Inject; + +import org.xwiki.bridge.DocumentAccessBridge; +import org.xwiki.model.reference.DocumentReference; +import org.xwiki.model.reference.DocumentReferenceResolver; +import org.xwiki.rendering.async.internal.block.BlockAsyncRendererConfiguration; +import org.xwiki.rendering.async.internal.block.BlockAsyncRendererExecutor; +import org.xwiki.rendering.block.Block; +import org.xwiki.rendering.block.Block.Axes; +import org.xwiki.rendering.block.MacroBlock; +import org.xwiki.rendering.block.MetaDataBlock; +import org.xwiki.rendering.block.match.MetadataBlockMatcher; +import org.xwiki.rendering.listener.MetaData; +import org.xwiki.rendering.macro.AbstractMacro; +import org.xwiki.rendering.macro.Macro; +import org.xwiki.rendering.macro.MacroContentParser; +import org.xwiki.rendering.macro.descriptor.ContentDescriptor; +import org.xwiki.rendering.transformation.MacroTransformationContext; +import org.xwiki.rendering.transformation.RenderingContext; + +/** + * Base class to implement a macro which have content to execute. + * + * @param <P> the type of the macro parameters bean + * @version $Id$ + * @since 14.8RC1 + * @since 14.4.5 + * @since 13.10.10 + */ +public abstract class AbstractExecutedContentMacro<P> extends AbstractMacro<P> +{ + @Inject + protected BlockAsyncRendererExecutor executor; + + @Inject + protected MacroContentParser parser; + + @Inject + protected DocumentAccessBridge documentAccessBridge; + + @Inject + protected DocumentReferenceResolver<String> resolver; + + @Inject + protected RenderingContext renderingContext; + + /** + * Creates a new {@link Macro} instance. + * + * @param name the name of the macro (eg "Table Of Contents" for the TOC macro) + * @param description string describing this macro. + * @param contentDescriptor the {@link ContentDescriptor} describing the content of this macro. + * @param parametersBeanClass class of the parameters bean. + */ + protected AbstractExecutedContentMacro(String name, String description, ContentDescriptor contentDescriptor, + Class<?> parametersBeanClass) + { + super(name, description, contentDescriptor, parametersBeanClass); + } + + @Override + public boolean supportsInlineMode() + { + return true; + } + + protected String getCurrentSource(MacroTransformationContext context) + { + String currentSource = null; + + if (context != null) { + currentSource = + context.getTransformationContext() != null ? context.getTransformationContext().getId() : null; + + MacroBlock currentMacroBlock = context.getCurrentMacroBlock(); + + if (currentMacroBlock != null) { + MetaDataBlock metaDataBlock = + currentMacroBlock.getFirstBlock(new MetadataBlockMatcher(MetaData.SOURCE), Axes.ANCESTOR_OR_SELF); + + if (metaDataBlock != null) { + currentSource = (String) metaDataBlock.getMetaData().getMetaData(MetaData.SOURCE); + } + } + } + + return currentSource; + } + + protected BlockAsyncRendererConfiguration createBlockAsyncRendererConfiguration(List<?> idElements, Block content, + MacroTransformationContext context) + { + return createBlockAsyncRendererConfiguration(idElements, content, getCurrentSource(context), context); + } + + protected BlockAsyncRendererConfiguration createBlockAsyncRendererConfiguration(List<?> idElements, Block content, + String source, MacroTransformationContext context) + { + BlockAsyncRendererConfiguration configuration = new BlockAsyncRendererConfiguration(idElements, content); + + // Set author + if (source != null) { + DocumentReference sourceReference = this.resolver.resolve(source); + configuration.setSecureReference(sourceReference, this.documentAccessBridge.getCurrentAuthorReference()); + + // Invalidate the cache when the document containing the macro call is modified + configuration.useEntity(sourceReference); + } + + // Indicate if the result should be inline or not + configuration.setInline(context.isInline()); + + // Indicate the syntax of the content + configuration.setDefaultSyntax(this.parser.getCurrentSyntax(context)); + + // Indicate the target syntax + configuration.setTargetSyntax(this.renderingContext.getTargetSyntax()); + + // Set the transformation id + configuration.setTransformationId(context.getTransformationContext().getId()); + + return configuration; + } +}
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-macro/src/main/java/org/xwiki/rendering/async/internal/AsyncMacro.java+4 −79 modified@@ -28,27 +28,16 @@ import javax.inject.Named; import javax.inject.Singleton; -import org.xwiki.bridge.DocumentAccessBridge; import org.xwiki.component.annotation.Component; -import org.xwiki.model.reference.DocumentReference; -import org.xwiki.model.reference.DocumentReferenceResolver; import org.xwiki.rendering.async.AsyncContext; import org.xwiki.rendering.async.AsyncMacroParameters; import org.xwiki.rendering.async.internal.block.BlockAsyncRendererConfiguration; -import org.xwiki.rendering.async.internal.block.BlockAsyncRendererExecutor; import org.xwiki.rendering.block.Block; -import org.xwiki.rendering.block.Block.Axes; -import org.xwiki.rendering.block.MacroBlock; import org.xwiki.rendering.block.MetaDataBlock; import org.xwiki.rendering.block.XDOM; -import org.xwiki.rendering.block.match.MetadataBlockMatcher; -import org.xwiki.rendering.listener.MetaData; -import org.xwiki.rendering.macro.AbstractMacro; -import org.xwiki.rendering.macro.MacroContentParser; import org.xwiki.rendering.macro.MacroExecutionException; import org.xwiki.rendering.macro.descriptor.DefaultContentDescriptor; import org.xwiki.rendering.transformation.MacroTransformationContext; -import org.xwiki.rendering.transformation.RenderingContext; /** * Asynchronous and cached execution of wiki content. @@ -59,31 +48,16 @@ @Component @Named("async") @Singleton -public class AsyncMacro extends AbstractMacro<AsyncMacroParameters> +public class AsyncMacro extends AbstractExecutedContentMacro<AsyncMacroParameters> { /** * The description of the macro. */ private static final String DESCRIPTION = "Asynchronous and cached execution of wiki content."; - @Inject - private BlockAsyncRendererExecutor executor; - - @Inject - private MacroContentParser parser; - - @Inject - private DocumentAccessBridge documentAccessBridge; - - @Inject - private DocumentReferenceResolver<String> resolver; - @Inject private AsyncContext asyncContext; - @Inject - private RenderingContext renderingContext; - /** * Default constructor. */ @@ -95,35 +69,6 @@ public AsyncMacro() setDefaultCategories(Set.of(DEFAULT_CATEGORY_CONTENT)); } - @Override - public boolean supportsInlineMode() - { - return true; - } - - private String getCurrentSource(MacroTransformationContext context) - { - String currentSource = null; - - if (context != null) { - currentSource = - context.getTransformationContext() != null ? context.getTransformationContext().getId() : null; - - MacroBlock currentMacroBlock = context.getCurrentMacroBlock(); - - if (currentMacroBlock != null) { - MetaDataBlock metaDataBlock = - currentMacroBlock.getFirstBlock(new MetadataBlockMatcher(MetaData.SOURCE), Axes.ANCESTOR_OR_SELF); - - if (metaDataBlock != null) { - currentSource = (String) metaDataBlock.getMetaData().getMetaData(MetaData.SOURCE); - } - } - } - - return currentSource; - } - @Override public List<Block> execute(AsyncMacroParameters parameters, String content, MacroTransformationContext context) throws MacroExecutionException @@ -156,19 +101,8 @@ public List<Block> execute(AsyncMacroParameters parameters, String content, Macr } } - BlockAsyncRendererConfiguration configuration = new BlockAsyncRendererConfiguration(idElements, xdom); - - // Set author - if (source != null) { - DocumentReference sourceReference = this.resolver.resolve(source); - configuration.setSecureReference(sourceReference, this.documentAccessBridge.getCurrentAuthorReference()); - - // Invalidate the cache when the document containing the macro call is modified - configuration.useEntity(sourceReference); - } - - // Indicate if the result should be inline or not - configuration.setInline(context.isInline()); + BlockAsyncRendererConfiguration configuration = + createBlockAsyncRendererConfiguration(idElements, xdom, source, context); // Enable/disable async configuration.setAsyncAllowed(parameters.isAsync()); @@ -177,21 +111,12 @@ public List<Block> execute(AsyncMacroParameters parameters, String content, Macr // Indicate context entries configuration.setContextEntries(parameters.getContext()); - // Indicate the syntax of the content - configuration.setDefaultSyntax(this.parser.getCurrentSyntax(context)); - - // Indicate the target syntax - configuration.setTargetSyntax(this.renderingContext.getTargetSyntax()); - - // Set the transformation id - configuration.setTransformationId(context.getTransformationContext().getId()); - try { Block result = this.executor.execute(configuration); // Indicate the content is not transformed if the current execution is not async if (!parameters.isAsync() || !this.asyncContext.isEnabled()) { - result = new MetaDataBlock(Collections.singletonList(result), this.getNonGeneratedContentMetaData()); + result = new MetaDataBlock(Collections.singletonList(result), getNonGeneratedContentMetaData()); } return Collections.singletonList(result);
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/pom.xml+18 −1 modified@@ -38,7 +38,7 @@ <dependencies> <dependency> <groupId>org.xwiki.platform</groupId> - <artifactId>xwiki-platform-bridge</artifactId> + <artifactId>xwiki-platform-rendering-async-macro</artifactId> <version>${project.version}</version> </dependency> <dependency> @@ -47,12 +47,29 @@ <version>${project.version}</version> <scope>runtime</scope> </dependency> + <!-- Test Dependencies --> + <dependency> + <groupId>org.xwiki.platform</groupId> + <artifactId>xwiki-platform-rendering-async-default</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> <dependency> <groupId>org.xwiki.rendering</groupId> <artifactId>xwiki-rendering-macro-toc</artifactId> <version>${rendering.version}</version> <scope>test</scope> </dependency> + <dependency> + <groupId>javax.servlet</groupId> + <artifactId>javax.servlet-api</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mortbay.jasper</groupId> + <artifactId>apache-el</artifactId> + <scope>test</scope> + </dependency> </dependencies> </project>
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/src/main/java/org/xwiki/rendering/internal/macro/context/ContextMacro.java+83 −84 modified@@ -20,6 +20,7 @@ package org.xwiki.rendering.internal.macro.context; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -29,24 +30,26 @@ import javax.inject.Named; import javax.inject.Singleton; -import org.xwiki.bridge.DocumentAccessBridge; import org.xwiki.bridge.DocumentModelBridge; import org.xwiki.component.annotation.Component; import org.xwiki.model.reference.DocumentReference; import org.xwiki.model.reference.DocumentReferenceResolver; +import org.xwiki.rendering.async.internal.AbstractExecutedContentMacro; +import org.xwiki.rendering.async.internal.block.BlockAsyncRendererConfiguration; import org.xwiki.rendering.block.Block; import org.xwiki.rendering.block.MetaDataBlock; import org.xwiki.rendering.block.XDOM; import org.xwiki.rendering.listener.MetaData; -import org.xwiki.rendering.macro.AbstractMacro; -import org.xwiki.rendering.macro.MacroContentParser; import org.xwiki.rendering.macro.MacroExecutionException; import org.xwiki.rendering.macro.context.ContextMacroParameters; import org.xwiki.rendering.macro.context.TransformationContextMode; import org.xwiki.rendering.macro.descriptor.DefaultContentDescriptor; import org.xwiki.rendering.transformation.MacroTransformationContext; import org.xwiki.rendering.transformation.TransformationContext; import org.xwiki.rendering.transformation.TransformationManager; +import org.xwiki.security.authorization.AccessDeniedException; +import org.xwiki.security.authorization.AuthorizationManager; +import org.xwiki.security.authorization.Right; /** * Execute the macro's content in the context of another document's reference. @@ -57,7 +60,7 @@ @Component @Named("context") @Singleton -public class ContextMacro extends AbstractMacro<ContextMacroParameters> +public class ContextMacro extends AbstractExecutedContentMacro<ContextMacroParameters> { /** * The description of the macro. @@ -69,27 +72,15 @@ public class ContextMacro extends AbstractMacro<ContextMacroParameters> */ private static final String CONTENT_DESCRIPTION = "The content to execute"; - /** - * Used to set the current document in the context (old way) and check rights. - */ @Inject - private DocumentAccessBridge documentAccessBridge; + private AuthorizationManager authorizationManager; - /** - * The parser used to parse macro content. - */ @Inject - private MacroContentParser contentParser; + private TransformationManager transformationManager; - /** - * Used to transform document links into absolute references. - */ @Inject @Named("macro") - private DocumentReferenceResolver<String> macroDocumentReferenceResolver; - - @Inject - private TransformationManager transformationManager; + private DocumentReferenceResolver<String> macroReferenceResolver; /** * Create and initialize the descriptor of the macro. @@ -105,10 +96,48 @@ public ContextMacro() setDefaultCategories(Set.of(DEFAULT_CATEGORY_DEVELOPMENT)); } - @Override - public boolean supportsInlineMode() + private void checkAccess(DocumentReference currentAuthor, DocumentReference referencedDocReference) + throws MacroExecutionException { - return true; + // Current author must have view right on the target document to use it as context document + try { + this.authorizationManager.checkAccess(Right.VIEW, currentAuthor, referencedDocReference); + } catch (AccessDeniedException e) { + throw new MacroExecutionException("Author [" + currentAuthor + + "] is not allowed to access target document [" + referencedDocReference + "]", e); + } + } + + private XDOM getXDOM(DocumentReference referencedDocReference, ContextMacroParameters parameters) + throws MacroExecutionException + { + try { + if (parameters.getTransformationContext() == TransformationContextMode.DOCUMENT + || parameters.getTransformationContext() == TransformationContextMode.TRANSFORMATIONS) { + // Apply the transformations but with a Transformation Context having the XDOM of the passed + // document so that macros execute on the passed document's XDOM (e.g. the TOC macro will generate + // the toc for the passed document instead of the current document). + DocumentModelBridge referencedDoc = + this.documentAccessBridge.getTranslatedDocumentInstance(referencedDocReference); + XDOM referencedXDOM = referencedDoc.getXDOM(); + + if (parameters.getTransformationContext() == TransformationContextMode.TRANSFORMATIONS) { + // Get the XDOM from the referenced doc but with Transformations applied so that all macro are + // executed and contribute XDOM elements. + // IMPORTANT: This can be dangerous since it means executing macros, and thus also script macros + // defined in the referenced document. To be used with caution. + TransformationContext referencedTxContext = + new TransformationContext(referencedXDOM, referencedDoc.getSyntax()); + this.transformationManager.performTransformations(referencedXDOM, referencedTxContext); + } + + return referencedXDOM; + } + } catch (Exception e) { + throw new MacroExecutionException("Failed to resolve the XDOM to use in the transformation", e); + } + + return null; } @Override @@ -120,75 +149,45 @@ public List<Block> execute(ContextMacroParameters parameters, String content, Ma + "set in the context as the current document."); } + DocumentReference currentAuthor = this.documentAccessBridge.getCurrentAuthorReference(); DocumentReference referencedDocReference = - this.macroDocumentReferenceResolver.resolve(parameters.getDocument(), context.getCurrentMacroBlock()); + this.macroReferenceResolver.resolve(parameters.getDocument(), context.getCurrentMacroBlock()); - boolean currentContextHasProgrammingRights = this.documentAccessBridge.hasProgrammingRights(); + // Make sure the author is allowed to use the target document + checkAccess(currentAuthor, referencedDocReference); - List<Block> result; - try { - Map<String, Object> backupObjects = new HashMap<>(); - try { - this.documentAccessBridge.pushDocumentInContext(backupObjects, referencedDocReference); - - // The current document is now the passed document. Check for programming rights for it. If it has - // programming rights then the initial current document also needs programming right, else throw an - // error since it would be a security breach otherwise. - if (this.documentAccessBridge.hasProgrammingRights() && !currentContextHasProgrammingRights) { - throw new MacroExecutionException("Current document must have programming rights since the " - + "context document provided [" + parameters.getDocument() + "] has programming rights."); - } + MetaData metadata = new MetaData(); + metadata.addMetaData(MetaData.SOURCE, parameters.getDocument()); + metadata.addMetaData(MetaData.BASE, parameters.getDocument()); - MetaData metadata = new MetaData(); - metadata.addMetaData(MetaData.SOURCE, parameters.getDocument()); - metadata.addMetaData(MetaData.BASE, parameters.getDocument()); - - XDOM xdom = this.contentParser.parse(content, context, false, metadata, false); - - // Configure the Transformation Context depending on the mode asked. - if (parameters.getTransformationContext() == TransformationContextMode.DOCUMENT - || parameters.getTransformationContext() == TransformationContextMode.TRANSFORMATIONS) - { - // Apply the transformations but with a Transformation Context having the XDOM of the passed - // document so that macros execute on the passed document's XDOM (e.g. the TOC macro will generate - // the toc for the passed document instead of the current document). - DocumentModelBridge referencedDoc = - this.documentAccessBridge.getTranslatedDocumentInstance(referencedDocReference); - XDOM referencedXDOM = referencedDoc.getXDOM(); - - if (parameters.getTransformationContext() == TransformationContextMode.TRANSFORMATIONS) { - // Get the XDOM from the referenced doc but with Transformations applied so that all macro are - // executed and contribute XDOM elements. - // IMPORTANT: This can be dangerous since it means executing macros, and thus also script macros - // defined in the referenced document. To be used with caution. - TransformationContext referencedTxContext = - new TransformationContext(referencedXDOM, referencedDoc.getSyntax()); - this.transformationManager.performTransformations(referencedXDOM, referencedTxContext); - } - - // Now execute transformation on the context macro content but with the referenced XDOM in the - // Transformation context! - TransformationContext txContext = - new TransformationContext(referencedXDOM, referencedDoc.getSyntax()); - this.transformationManager.performTransformations(xdom, txContext); - } + XDOM xdom = this.parser.parse(content, context, false, metadata, context.isInline()); - // Keep metadata so that the result stay associated to context properties when inserted in the parent - // XDOM - result = Arrays.asList((Block) new MetaDataBlock(xdom.getChildren(), xdom.getMetaData())); + if (xdom.getChildren().isEmpty()) { + return Collections.emptyList(); + } - } finally { - this.documentAccessBridge.popDocumentFromContext(backupObjects); - } + // Reuse the very generic async rendering framework (even if we don't do async and caching) since it's taking + // care of many other things + BlockAsyncRendererConfiguration configuration = createBlockAsyncRendererConfiguration(null, xdom, context); + + // Configure the Transformation Context XDOM depending on the mode asked. + configuration.setXDOM(getXDOM(referencedDocReference, parameters)); + + Map<String, Object> backupObjects = new HashMap<>(); + try { + // Switch the context document + this.documentAccessBridge.pushDocumentInContext(backupObjects, referencedDocReference); + + // Execute the content + Block result = this.executor.execute(configuration); + + // Keep metadata so that the result stay associated to context properties when inserted in the parent XDOM + return Arrays.asList((Block) new MetaDataBlock(result.getChildren(), xdom.getMetaData())); } catch (Exception e) { - if (e instanceof MacroExecutionException) { - throw (MacroExecutionException) e; - } else { - throw new MacroExecutionException( - String.format("Failed to render page in the context of [%s]", referencedDocReference), e); - } + throw new MacroExecutionException("Failed start the execution of the macro", e); + } finally { + // Restore the context document + this.documentAccessBridge.popDocumentFromContext(backupObjects); } - - return result; } }
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/src/test/java/org/xwiki/rendering/internal/macro/context/ContextMacroTest.java+92 −103 modified@@ -22,178 +22,167 @@ import java.util.Arrays; import java.util.Collections; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; +import javax.inject.Inject; +import javax.inject.Named; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; import org.xwiki.bridge.DocumentAccessBridge; import org.xwiki.bridge.DocumentModelBridge; import org.xwiki.model.reference.DocumentReference; import org.xwiki.model.reference.DocumentReferenceResolver; import org.xwiki.properties.BeanDescriptor; import org.xwiki.properties.BeanManager; -import org.xwiki.rendering.block.Block; -import org.xwiki.rendering.block.LinkBlock; +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.ParagraphBlock; +import org.xwiki.rendering.block.WordBlock; import org.xwiki.rendering.block.XDOM; import org.xwiki.rendering.listener.MetaData; -import org.xwiki.rendering.listener.reference.ResourceReference; -import org.xwiki.rendering.listener.reference.ResourceType; import org.xwiki.rendering.macro.MacroContentParser; import org.xwiki.rendering.macro.MacroExecutionException; import org.xwiki.rendering.macro.context.ContextMacroParameters; import org.xwiki.rendering.syntax.Syntax; import org.xwiki.rendering.transformation.MacroTransformationContext; -import org.xwiki.test.mockito.MockitoComponentMockingRule; - -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; +import org.xwiki.security.authorization.AccessDeniedException; +import org.xwiki.security.authorization.AuthorizationManager; +import org.xwiki.security.authorization.Right; +import org.xwiki.test.TestEnvironment; +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 static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; /** * Unit tests for {@link ContextMacro}. * * @version $Id$ * @since 8.3RC1 */ -public class ContextMacroTest +@ComponentTest +@ComponentList(TestEnvironment.class) +class ContextMacroTest { - @Rule - public MockitoComponentMockingRule<ContextMacro> mocker = new MockitoComponentMockingRule<>(ContextMacro.class); + private static final DocumentReference AUTHOR = new DocumentReference("wiki", "XWiki", "author"); + + private static final DocumentReference TARGET_REFERENCE = new DocumentReference("wiki", "space", "target"); + + private static final DocumentReference SOURCE_REFERENCE = new DocumentReference("wiki", "space", "source"); + + @Inject + protected BlockAsyncRendererExecutor executor; + + @MockComponent + private DocumentAccessBridge dab; + + @MockComponent + private BeanManager beanManager; + + @MockComponent + private MacroContentParser parser; + + @MockComponent + private AuthorizationManager authorization; + + @MockComponent + @Named("macro") + private DocumentReferenceResolver<String> macroReferenceResolver; + + @MockComponent + protected DocumentReferenceResolver<String> resolver; - @Before - public void setUp() throws Exception + @InjectMockComponents + private ContextMacro macro; + + @BeforeEach + public void beforeEach() throws Exception { // Macro Descriptor set up - BeanManager beanManager = this.mocker.getInstance(BeanManager.class); BeanDescriptor descriptor = mock(BeanDescriptor.class); when(descriptor.getProperties()).thenReturn(Collections.emptyList()); - when(beanManager.getBeanDescriptor(any())).thenReturn(descriptor); + when(this.beanManager.getBeanDescriptor(any())).thenReturn(descriptor); + + when(this.dab.getCurrentAuthorReference()).thenReturn(AUTHOR); + + when(this.macroReferenceResolver.resolve(eq("target"), any())).thenReturn(TARGET_REFERENCE); + when(this.resolver.resolve("source")).thenReturn(SOURCE_REFERENCE); } @Test - public void executeWhenNoDocumentSpecified() throws Exception + void executeWhenNoDocumentSpecified() throws Exception { ContextMacroParameters parameters = new ContextMacroParameters(); try { - this.mocker.getComponentUnderTest().execute(parameters, "", new MacroTransformationContext()); + this.macro.execute(parameters, "", new MacroTransformationContext()); fail("Should have thrown an exception"); } catch (MacroExecutionException expected) { - Assert.assertEquals("You must specify a 'document' parameter pointing to the document to set in the " + assertEquals("You must specify a 'document' parameter pointing to the document to set in the " + "context as the current document.", expected.getMessage()); } } @Test - public void executeWithReferencedDocumentHavingProgrammingRightsButNotTheCallingDocument() throws Exception + void executeWithReferencedDocumentNotViewableByTheAuthor() throws Exception { MacroTransformationContext macroContext = new MacroTransformationContext(); MacroBlock macroBlock = new MacroBlock("context", Collections.emptyMap(), false); macroContext.setCurrentMacroBlock(macroBlock); - DocumentReferenceResolver<String> resolver = - this.mocker.getInstance(DocumentReferenceResolver.TYPE_STRING, "macro"); - when(resolver.resolve("wiki:space.page", macroBlock)).thenReturn( - new DocumentReference("wiki", "space", "page")); - - DocumentAccessBridge dab = this.mocker.getInstance(DocumentAccessBridge.class); - when(dab.hasProgrammingRights()).thenReturn(false).thenReturn(true); + doThrow(AccessDeniedException.class).when(this.authorization).checkAccess(Right.VIEW, AUTHOR, TARGET_REFERENCE); ContextMacroParameters parameters = new ContextMacroParameters(); - parameters.setDocument("wiki:space.page"); + parameters.setDocument("target"); try { - this.mocker.getComponentUnderTest().execute(parameters, "", macroContext); + this.macro.execute(parameters, "", macroContext); fail("Should have thrown an exception"); } catch (MacroExecutionException expected) { - assertEquals("Current document must have programming rights since the context document provided [" - + "wiki:space.page] has programming rights.", expected.getMessage()); + assertEquals("Author [wiki:XWiki.author] is not allowed to access target document [wiki:space.target]", + expected.getMessage()); } } @Test - public void executeWithReferencedDocumentHavingProgrammingRightsAndCallingDocumentToo() throws Exception - { - MacroBlock macroBlock = new MacroBlock("context", Collections.<String, String>emptyMap(), false); - MacroTransformationContext macroContext = new MacroTransformationContext(); - macroContext.setSyntax(Syntax.XWIKI_2_0); - macroContext.setCurrentMacroBlock(macroBlock); - - DocumentReferenceResolver<String> resolver = - this.mocker.getInstance(DocumentReferenceResolver.TYPE_STRING, "macro"); - DocumentReference referencedDocumentReference = new DocumentReference("wiki", "space", "page"); - when(resolver.resolve("wiki:space.page", macroBlock)).thenReturn(referencedDocumentReference); - - DocumentAccessBridge dab = this.mocker.getInstance(DocumentAccessBridge.class); - when(dab.hasProgrammingRights()).thenReturn(true).thenReturn(true); - DocumentModelBridge dmb = mock(DocumentModelBridge.class); - when(dab.getTranslatedDocumentInstance(referencedDocumentReference)).thenReturn(dmb); - - MacroContentParser parser = this.mocker.getInstance(MacroContentParser.class); - when(parser.parse(eq(""), same(macroContext), eq(false), any(MetaData.class), eq(false))).thenReturn( - new XDOM(Collections.emptyList())); - - ContextMacroParameters parameters = new ContextMacroParameters(); - parameters.setDocument("wiki:space.page"); - - this.mocker.getComponentUnderTest().execute(parameters, "", macroContext); - } - - @Test - public void executeOk() throws Exception + void executeOk() throws Exception { MacroBlock macroBlock = new MacroBlock("context", Collections.<String, String>emptyMap(), false); + MetaData metadata = new MetaData(); + metadata.addMetaData(MetaData.SOURCE, "source"); + XDOM xdom = new XDOM(Arrays.asList(macroBlock), metadata); MacroTransformationContext macroContext = new MacroTransformationContext(); macroContext.setSyntax(Syntax.XWIKI_2_0); macroContext.setCurrentMacroBlock(macroBlock); - DocumentReferenceResolver<String> resolver = - this.mocker.getInstance(DocumentReferenceResolver.TYPE_STRING, "macro"); - DocumentReference referencedDocumentReference = new DocumentReference("wiki", "space", "page"); - when(resolver.resolve("wiki:space.page", macroBlock)).thenReturn(referencedDocumentReference); - - DocumentAccessBridge dab = this.mocker.getInstance(DocumentAccessBridge.class); DocumentModelBridge dmb = mock(DocumentModelBridge.class); - when(dab.getTranslatedDocumentInstance(referencedDocumentReference)).thenReturn(dmb); + when(this.dab.getTranslatedDocumentInstance(TARGET_REFERENCE)).thenReturn(dmb); - MacroContentParser parser = this.mocker.getInstance(MacroContentParser.class); - XDOM xdom = new XDOM(Arrays.asList((Block) new ParagraphBlock(Arrays.asList((Block) new LinkBlock( - Collections.emptyList(), new ResourceReference("", ResourceType.DOCUMENT), false))))); - when(parser.parse(eq(""), same(macroContext), eq(false), any(MetaData.class), eq(false))).thenReturn(xdom); + when(this.parser.parse(eq(""), same(macroContext), eq(false), any(MetaData.class), eq(false))).thenReturn(xdom); ContextMacroParameters parameters = new ContextMacroParameters(); - parameters.setDocument("wiki:space.page"); - - // Note: we're not testing the returned value here since this is done in integation tests. - this.mocker.getComponentUnderTest().execute(parameters, "", macroContext); - } + parameters.setDocument("target"); - @Test - public void executeWithRelativeDocumentReferenceParameter() throws Exception - { - MacroBlock macroBlock = new MacroBlock("context", Collections.<String, String>emptyMap(), false); + when(this.executor.execute(any())).thenReturn(new WordBlock("result")); - MacroTransformationContext macroContext = new MacroTransformationContext(); - macroContext.setSyntax(Syntax.XWIKI_2_0); - macroContext.setCurrentMacroBlock(macroBlock); + this.macro.execute(parameters, "", macroContext); - DocumentReferenceResolver<String> resolver = - this.mocker.getInstance(DocumentReferenceResolver.TYPE_STRING, "macro"); - DocumentReference referencedDocumentReference = new DocumentReference("basewiki", "basespace", "page"); - when(resolver.resolve("page", macroBlock)).thenReturn(referencedDocumentReference); - - DocumentAccessBridge dab = this.mocker.getInstance(DocumentAccessBridge.class); - DocumentModelBridge dmb = mock(DocumentModelBridge.class); - when(dab.getTranslatedDocumentInstance(referencedDocumentReference)).thenReturn(dmb); - - MacroContentParser parser = this.mocker.getInstance(MacroContentParser.class); - when(parser.parse(eq(""), same(macroContext), eq(false), any(MetaData.class), eq(false))).thenReturn( - new XDOM(Collections.emptyList())); - - ContextMacroParameters parameters = new ContextMacroParameters(); - parameters.setDocument("page"); + ArgumentCaptor<BlockAsyncRendererConfiguration> configurationCaptor = + ArgumentCaptor.forClass(BlockAsyncRendererConfiguration.class); + verify(this.executor).execute(configurationCaptor.capture()); - this.mocker.getComponentUnderTest().execute(parameters, "", macroContext); + BlockAsyncRendererConfiguration configuration = configurationCaptor.getValue(); + assertEquals(AUTHOR, configuration.getSecureAuthorReference()); + assertEquals(SOURCE_REFERENCE, configuration.getSecureDocumentReference()); } }
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/src/test/java/org/xwiki/rendering/macro/context/IntegrationTests.java+31 −6 modified@@ -21,20 +21,31 @@ import java.io.StringReader; -import org.junit.runner.RunWith; import org.xwiki.bridge.DocumentAccessBridge; import org.xwiki.bridge.DocumentModelBridge; +import org.xwiki.component.manager.ComponentManager; import org.xwiki.component.util.DefaultParameterizedType; import org.xwiki.model.reference.DocumentReference; import org.xwiki.model.reference.DocumentReferenceResolver; +import org.xwiki.query.QueryManager; +import org.xwiki.refactoring.internal.ModelBridge; +import org.xwiki.refactoring.internal.ReferenceUpdater; import org.xwiki.rendering.block.MacroBlock; import org.xwiki.rendering.block.XDOM; import org.xwiki.rendering.parser.Parser; -import org.xwiki.rendering.test.integration.RenderingTestSuite; +import org.xwiki.rendering.test.integration.junit5.RenderingTests; +import org.xwiki.security.authorization.AuthorizationManager; +import org.xwiki.security.authorization.ContextualAuthorizationManager; +import org.xwiki.template.TemplateManager; +import org.xwiki.test.TestEnvironment; import org.xwiki.test.annotation.AllComponents; +import org.xwiki.test.annotation.ComponentList; import org.xwiki.test.mockito.MockitoComponentManager; -import static org.mockito.Mockito.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; /** * Run all tests found in {@code *.test} files located in the classpath. These {@code *.test} files must follow the @@ -43,15 +54,26 @@ * @version $Id$ * @since 8.3RC1 */ -@RunWith(RenderingTestSuite.class) @AllComponents -public class IntegrationTests +@ComponentList(TestEnvironment.class) +public class IntegrationTests implements RenderingTests { - @RenderingTestSuite.Initialized + @RenderingTests.Initialized public void initialize(MockitoComponentManager componentManager) throws Exception { + // Replace the environment by a test compatible one + componentManager.registerComponent(TestEnvironment.class); + // For performance reasons we mock some components to avoid having to draw all oldcore components + // Some components we don't really use and which trigger a lot of dependencies + componentManager.registerMockComponent(TemplateManager.class); + componentManager.registerMockComponent(ModelBridge.class); + componentManager.registerMockComponent(QueryManager.class); + componentManager.registerMockComponent(ReferenceUpdater.class); + componentManager.registerMockComponent(AuthorizationManager.class); + componentManager.registerMockComponent(ContextualAuthorizationManager.class); + // Macro Reference Resolver DocumentReferenceResolver<String> macroResolver = componentManager.registerMockComponent( new DefaultParameterizedType(null, DocumentReferenceResolver.class, String.class), "macro"); @@ -67,5 +89,8 @@ public void initialize(MockitoComponentManager componentManager) throws Exceptio Parser parser = componentManager.getInstance(Parser.class, "xwiki/2.1"); XDOM xdom = parser.parse(new StringReader("= heading1 =\n==heading2==")); when(dmb.getXDOM()).thenReturn(xdom); + + // Replace the context component manager + componentManager.registerComponent(ComponentManager.class, "context", componentManager); } }
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
7- github.com/advisories/GHSA-859x-p6jp-rc2wghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2023-26056ghsaADVISORY
- github.com/xwiki/xwiki-platform/commit/4b75f212c2dd2dfc5fb5726c7830c6dbc9a425c6ghsax_refsource_MISCWEB
- github.com/xwiki/xwiki-platform/commit/bd34ad6710ed72304304a3d5fec38b7cc050ef3bghsax_refsource_MISCWEB
- github.com/xwiki/xwiki-platform/commit/dd3f4735b41971b3afc3f3aedf6664b4e8be4894ghsax_refsource_MISCWEB
- github.com/xwiki/xwiki-platform/security/advisories/GHSA-859x-p6jp-rc2wghsax_refsource_CONFIRMWEB
- jira.xwiki.org/browse/XWIKI-19856ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.