CVE-2023-26056 (Java, published 2023-03-02T19:15Z)
Description: value: 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.
Severity: nan (CVSS v3 base score: 5.4)
Weakness: CWE-863 \u2014 Incorrect Authorization
The product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check. This allows attackers to bypass intended access restrictions.
Repository: https://github.com/xwiki/xwiki-platform
Files changed:
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
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
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-async/xwiki-platform-rendering-async-macro/pom.xml
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
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
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/pom.xml
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
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
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
Commit message: XWIKI-19856: Macro execution as any user without programming rights through the context macro


Vulnerable code:
        this.id = new ArrayList<>(idElements.size());
        addElements(idElements);
            // Create a XDOM instance for the transformation context
            XDOM xdom;
            if (block instanceof XDOM) {
                xdom = (XDOM) block;
            } else {
      <artifactId>xwiki-platform-rendering-async-default</artifactId>
  <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>
import org.xwiki.bridge.DocumentAccessBridge;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.model.reference.DocumentReferenceResolver;
import org.xwiki.rendering.async.internal.block.BlockAsyncRendererExecutor;
import org.xwiki.rendering.block.Block.Axes;
import org.xwiki.rendering.block.MacroBlock;
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.transformation.RenderingContext;
public class AsyncMacro extends AbstractMacro<AsyncMacroParameters>
    @Inject
    private BlockAsyncRendererExecutor executor;

    @Inject
    private MacroContentParser parser;

    @Inject
    private DocumentAccessBridge documentAccessBridge;

    @Inject
    private DocumentReferenceResolver<String> resolver;

    @Inject
    private RenderingContext renderingContext;

    @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;
    }

        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());
        // 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());

                result = new MetaDataBlock(Collections.singletonList(result), this.getNonGeneratedContentMetaData());
      <artifactId>xwiki-platform-bridge</artifactId>
import org.xwiki.bridge.DocumentAccessBridge;
import org.xwiki.rendering.macro.AbstractMacro;
import org.xwiki.rendering.macro.MacroContentParser;
public class ContextMacro extends AbstractMacro<ContextMacroParameters>
    /**
     * Used to set the current document in the context (old way) and check rights.
     */
    private DocumentAccessBridge documentAccessBridge;
    /**
     * The parser used to parse macro content.
     */
    private MacroContentParser contentParser;
    /**
     * Used to transform document links into absolute references.
     */
    private DocumentReferenceResolver<String> macroDocumentReferenceResolver;

    @Inject
    private TransformationManager transformationManager;
    @Override
    public boolean supportsInlineMode()
        return true;
            this.macroDocumentReferenceResolver.resolve(parameters.getDocument(), context.getCurrentMacroBlock());
        boolean currentContextHasProgrammingRights = this.documentAccessBridge.hasProgrammingRights();
        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());

                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);
                }
                // 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()));
            } finally {
                this.documentAccessBridge.popDocumentFromContext(backupObjects);
            }
            if (e instanceof MacroExecutionException) {
                throw (MacroExecutionException) e;
            } else {
                throw new MacroExecutionException(
                    String.format("Failed to render page in the context of [%s]", referencedDocReference), e);
            }

        return result;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.xwiki.rendering.block.Block;
import org.xwiki.rendering.block.LinkBlock;
import org.xwiki.rendering.block.ParagraphBlock;
import org.xwiki.rendering.listener.reference.ResourceReference;
import org.xwiki.rendering.listener.reference.ResourceType;
import org.xwiki.test.mockito.MockitoComponentMockingRule;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
public class ContextMacroTest
    @Rule
    public MockitoComponentMockingRule<ContextMacro> mocker = new MockitoComponentMockingRule<>(ContextMacro.class);
    @Before
    public void setUp() throws Exception
        BeanManager beanManager = this.mocker.getInstance(BeanManager.class);
        when(beanManager.getBeanDescriptor(any())).thenReturn(descriptor);
    public void executeWhenNoDocumentSpecified() throws Exception
            this.mocker.getComponentUnderTest().execute(parameters, "", new MacroTransformationContext());
            Assert.assertEquals("You must specify a 'document' parameter pointing to the document to set in the "
    public void executeWithReferencedDocumentHavingProgrammingRightsButNotTheCallingDocument() throws Exception
        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);
        parameters.setDocument("wiki:space.page");
            this.mocker.getComponentUnderTest().execute(parameters, "", macroContext);
            assertEquals("Current document must have programming rights since the context document provided ["
                + "wiki:space.page] has programming rights.", expected.getMessage());
    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
        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.getTranslatedDocumentInstance(referencedDocumentReference)).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);
        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);
    }
    @Test
    public void executeWithRelativeDocumentReferenceParameter() 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("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");
        this.mocker.getComponentUnderTest().execute(parameters, "", macroContext);
import org.junit.runner.RunWith;
import org.xwiki.rendering.test.integration.RenderingTestSuite;
import static org.mockito.Mockito.*;
@RunWith(RenderingTestSuite.class)
public class IntegrationTests
    @RenderingTestSuite.Initialized

Fixed code:
import org.xwiki.rendering.block.XDOM;
    private XDOM xdom;

        if (idElements != null) {
            this.id = new ArrayList<>(idElements.size());
            addElements(idElements);
        } else {
            this.id = new ArrayList<>();
        }
    /**
     * @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;
    }

            // Get the XDOM instance for the transformation context
            XDOM xdom = this.configuration.getXDOM();
            if (xdom == null) {
      <artifactId>xwiki-platform-rendering-async-api</artifactId>
    <dependency>
      <groupId>org.xwiki.platform</groupId>
      <artifactId>xwiki-platform-rendering-async-default</artifactId>
      <version>${project.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.mortbay.jasper</groupId>
      <artifactId>apache-el</artifactId>
      <scope>test</scope>
    </dependency>
/*
 * 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;
    }
}
public class AsyncMacro extends AbstractExecutedContentMacro<AsyncMacroParameters>
        BlockAsyncRendererConfiguration configuration =
            createBlockAsyncRendererConfiguration(idElements, xdom, source, context);
                result = new MetaDataBlock(Collections.singletonList(result), getNonGeneratedContentMetaData());
      <artifactId>xwiki-platform-rendering-async-macro</artifactId>

    <dependency>
      <groupId>org.xwiki.platform</groupId>
      <artifactId>xwiki-platform-rendering-async-default</artifactId>
      <version>${project.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>
import java.util.Collections;
import org.xwiki.rendering.async.internal.AbstractExecutedContentMacro;
import org.xwiki.rendering.async.internal.block.BlockAsyncRendererConfiguration;
import org.xwiki.security.authorization.AccessDeniedException;
import org.xwiki.security.authorization.AuthorizationManager;
import org.xwiki.security.authorization.Right;
public class ContextMacro extends AbstractExecutedContentMacro<ContextMacroParameters>
    private AuthorizationManager authorizationManager;
    private TransformationManager transformationManager;
    private DocumentReferenceResolver<String> macroReferenceResolver;
    private void checkAccess(DocumentReference currentAuthor, DocumentReference referencedDocReference)
        throws MacroExecutionException
        // 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;
        DocumentReference currentAuthor = this.documentAccessBridge.getCurrentAuthorReference();
            this.macroReferenceResolver.resolve(parameters.getDocument(), context.getCurrentMacroBlock());
        // Make sure the author is allowed to use the target document
        checkAccess(currentAuthor, referencedDocReference);
        MetaData metadata = new MetaData();
        metadata.addMetaData(MetaData.SOURCE, parameters.getDocument());
        metadata.addMetaData(MetaData.BASE, parameters.getDocument());
        XDOM xdom = this.parser.parse(content, context, false, metadata, context.isInline());
        if (xdom.getChildren().isEmpty()) {
            return Collections.emptyList();
        }
        // 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()));
            throw new MacroExecutionException("Failed start the execution of the macro", e);
        } finally {
            // Restore the context document
            this.documentAccessBridge.popDocumentFromContext(backupObjects);
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.rendering.async.internal.block.BlockAsyncRendererConfiguration;
import org.xwiki.rendering.async.internal.block.BlockAsyncRendererExecutor;
import org.xwiki.rendering.block.WordBlock;
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;
@ComponentTest
@ComponentList(TestEnvironment.class)
class ContextMacroTest
    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;
    @InjectMockComponents
    private ContextMacro macro;

    @BeforeEach
    public void beforeEach() throws Exception
        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);
    void executeWhenNoDocumentSpecified() throws Exception
            this.macro.execute(parameters, "", new MacroTransformationContext());
            assertEquals("You must specify a 'document' parameter pointing to the document to set in the "
    void executeWithReferencedDocumentNotViewableByTheAuthor() throws Exception
        doThrow(AccessDeniedException.class).when(this.authorization).checkAccess(Right.VIEW, AUTHOR, TARGET_REFERENCE);
        parameters.setDocument("target");
            this.macro.execute(parameters, "", macroContext);
            assertEquals("Author [wiki:XWiki.author] is not allowed to access target document [wiki:space.target]",
                expected.getMessage());
    void executeOk() throws Exception
        MetaData metadata = new MetaData();
        metadata.addMetaData(MetaData.SOURCE, "source");
        XDOM xdom = new XDOM(Arrays.asList(macroBlock), metadata);
        when(this.dab.getTranslatedDocumentInstance(TARGET_REFERENCE)).thenReturn(dmb);
        when(this.parser.parse(eq(""), same(macroContext), eq(false), any(MetaData.class), eq(false))).thenReturn(xdom);
        parameters.setDocument("target");
        when(this.executor.execute(any())).thenReturn(new WordBlock("result"));
        this.macro.execute(parameters, "", macroContext);
        ArgumentCaptor<BlockAsyncRendererConfiguration> configurationCaptor =
            ArgumentCaptor.forClass(BlockAsyncRendererConfiguration.class);
        verify(this.executor).execute(configurationCaptor.capture());
        BlockAsyncRendererConfiguration configuration = configurationCaptor.getValue();
        assertEquals(AUTHOR, configuration.getSecureAuthorReference());
        assertEquals(SOURCE_REFERENCE, configuration.getSecureDocumentReference());
import org.xwiki.component.manager.ComponentManager;
import org.xwiki.query.QueryManager;
import org.xwiki.refactoring.internal.ModelBridge;
import org.xwiki.refactoring.internal.ReferenceUpdater;
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.ComponentList;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ComponentList(TestEnvironment.class)
public class IntegrationTests implements RenderingTests
    @RenderingTests.Initialized
        // Replace the environment by a test compatible one
        componentManager.registerComponent(TestEnvironment.class);
        
        // 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);


        // Replace the context component manager
        componentManager.registerComponent(ComponentManager.class, "context", componentManager);