XWiki programming rights may be inherited by inclusion
Description
XWiki Platform is a generic wiki platform offering runtime services for applications built on top of it. The content of a document included using {{include reference="targetdocument"/}} is executed with the right of the includer and not with the right of its author. This means that any user able to modify the target document can impersonate the author of the content which used the include macro. This vulnerability has been patched in XWiki 15.0 RC1 by making the default behavior safe.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.xwiki.platform:xwiki-platform-rendering-macro-includeMaven | < 15.0-rc-1 | 15.0-rc-1 |
Affected products
1- Range: >= 1.5-milestone-2, < 15.0-rc-1
Patches
50a4f9b026ba9XWIKI-5027: Improve included content transformation
2 files changed · +44 −1
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-include/src/main/resources/ApplicationResources.properties+3 −0 modified@@ -37,6 +37,9 @@ rendering.macro.include.parameter.author.description=The author to use to execut org.xwiki.rendering.macro.include.IncludeMacroParameters$Context.NEW=New org.xwiki.rendering.macro.include.IncludeMacroParameters$Context.CURRENT=Current +org.xwiki.rendering.macro.include.IncludeMacroParameters$Author.AUTO=Auto +org.xwiki.rendering.macro.include.IncludeMacroParameters$Author.CURRENT=Current +org.xwiki.rendering.macro.include.IncludeMacroParameters$Author.TARGET=Target ############################################################################### ## Deprecated
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-include/src/test/java/org/xwiki/rendering/internal/macro/include/IncludeMacroTest.java+41 −1 modified@@ -214,6 +214,42 @@ void executeWithCURRENTAuthor() throws Exception assertBlocks(expected, blocks, this.rendererFactory); } + @Test + void executeWithNoPRAuthor() throws Exception + { + // @formatter:off + String expected = "beginDocument\n" + + "beginMetaData [[source]=[wiki:Space.IncludedPage][syntax]=[XWiki 2.0]]\n" + + "beginParagraph\n" + + "onWord [word]\n" + + "endParagraph\n" + + "endMetaData [[source]=[wiki:Space.IncludedPage][syntax]=[XWiki 2.0]]\n" + + "endDocument"; + // @formatter:on + + List<Block> blocks = runIncludeMacro(Context.CURRENT, Author.AUTO, "word", false); + + assertBlocks(expected, blocks, this.rendererFactory); + } + + @Test + void executeWithTARGETAuthor() throws Exception + { + // @formatter:off + String expected = "beginDocument\n" + + "beginMetaData [[source]=[wiki:Space.IncludedPage][syntax]=[XWiki 2.0]]\n" + + "beginParagraph\n" + + "onWord [word]\n" + + "endParagraph\n" + + "endMetaData [[source]=[wiki:Space.IncludedPage][syntax]=[XWiki 2.0]]\n" + + "endDocument"; + // @formatter:on + + List<Block> blocks = runIncludeMacro(Context.CURRENT, Author.TARGET, "word", false); + + assertBlocks(expected, blocks, this.rendererFactory); + } + @Test void executeWithCurrentUserNoView() throws Exception { @@ -669,7 +705,10 @@ private MacroTransformationContext createMacroTransformationContext(String docum MacroTransformationContext context = new MacroTransformationContext(); MacroBlock includeMacro = new MacroBlock("include", Collections.singletonMap("reference", documentName), isInline); + XDOM xdom = new XDOM(List.of(includeMacro)); context.setCurrentMacroBlock(includeMacro); + context.setXDOM(xdom); + return context; } @@ -760,7 +799,8 @@ private List<Block> runIncludeMacro(final Context context, final Author author, verify(this.dab).pushDocumentInContext(any(Map.class), same(this.includedDocument)); verify(this.dab).popDocumentFromContext(any(Map.class)); } else { - if (author == Author.CURRENT || this.authorizationManager.hasAccess(Right.PROGRAM, INCLUDED_AUHOR, null)) { + if (parameters.getAuthor() == Author.CURRENT || (parameters.getAuthor() == Author.AUTO + && this.authorizationManager.hasAccess(Right.PROGRAM, INCLUDED_AUHOR, null))) { verifyNoInteractions(this.authorExecutor); } else { DocumentReference includedReference = this.includedDocument.getDocumentReference();
d1a84a3eea38XWIKI-5027: Improve included content transformation
2 files changed · +44 −1
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-include/src/main/resources/ApplicationResources.properties+3 −0 modified@@ -37,6 +37,9 @@ rendering.macro.include.parameter.author.description=The author to use to execut org.xwiki.rendering.macro.include.IncludeMacroParameters$Context.NEW=New org.xwiki.rendering.macro.include.IncludeMacroParameters$Context.CURRENT=Current +org.xwiki.rendering.macro.include.IncludeMacroParameters$Author.AUTO=Auto +org.xwiki.rendering.macro.include.IncludeMacroParameters$Author.CURRENT=Current +org.xwiki.rendering.macro.include.IncludeMacroParameters$Author.TARGET=Target ############################################################################### ## Deprecated
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-include/src/test/java/org/xwiki/rendering/internal/macro/include/IncludeMacroTest.java+41 −1 modified@@ -214,6 +214,42 @@ void executeWithCURRENTAuthor() throws Exception assertBlocks(expected, blocks, this.rendererFactory); } + @Test + void executeWithNoPRAuthor() throws Exception + { + // @formatter:off + String expected = "beginDocument\n" + + "beginMetaData [[source]=[wiki:Space.IncludedPage][syntax]=[XWiki 2.0]]\n" + + "beginParagraph\n" + + "onWord [word]\n" + + "endParagraph\n" + + "endMetaData [[source]=[wiki:Space.IncludedPage][syntax]=[XWiki 2.0]]\n" + + "endDocument"; + // @formatter:on + + List<Block> blocks = runIncludeMacro(Context.CURRENT, Author.AUTO, "word", false); + + assertBlocks(expected, blocks, this.rendererFactory); + } + + @Test + void executeWithTARGETAuthor() throws Exception + { + // @formatter:off + String expected = "beginDocument\n" + + "beginMetaData [[source]=[wiki:Space.IncludedPage][syntax]=[XWiki 2.0]]\n" + + "beginParagraph\n" + + "onWord [word]\n" + + "endParagraph\n" + + "endMetaData [[source]=[wiki:Space.IncludedPage][syntax]=[XWiki 2.0]]\n" + + "endDocument"; + // @formatter:on + + List<Block> blocks = runIncludeMacro(Context.CURRENT, Author.TARGET, "word", false); + + assertBlocks(expected, blocks, this.rendererFactory); + } + @Test void executeWithCurrentUserNoView() throws Exception { @@ -669,7 +705,10 @@ private MacroTransformationContext createMacroTransformationContext(String docum MacroTransformationContext context = new MacroTransformationContext(); MacroBlock includeMacro = new MacroBlock("include", Collections.singletonMap("reference", documentName), isInline); + XDOM xdom = new XDOM(List.of(includeMacro)); context.setCurrentMacroBlock(includeMacro); + context.setXDOM(xdom); + return context; } @@ -760,7 +799,8 @@ private List<Block> runIncludeMacro(final Context context, final Author author, verify(this.dab).pushDocumentInContext(any(Map.class), same(this.includedDocument)); verify(this.dab).popDocumentFromContext(any(Map.class)); } else { - if (author == Author.CURRENT || this.authorizationManager.hasAccess(Right.PROGRAM, INCLUDED_AUHOR, null)) { + if (parameters.getAuthor() == Author.CURRENT || (parameters.getAuthor() == Author.AUTO + && this.authorizationManager.hasAccess(Right.PROGRAM, INCLUDED_AUHOR, null))) { verifyNoInteractions(this.authorExecutor); } else { DocumentReference includedReference = this.includedDocument.getDocumentReference();
f627abe2dc39XWIKI-5027: Improve included content transformation
4 files changed · +85 −66
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-include/src/main/java/org/xwiki/rendering/internal/macro/include/IncludeMacro.java+32 −29 modified@@ -178,37 +178,40 @@ public List<Block> execute(IncludeMacroParameters parameters, String content, Ma metadata.getMetaData().addMetaData(MetaData.BASE, source); } - // Step 7: The the include macro is explicitly configured to be executed with the included document content - // author of if that author does not have programming right, execute it right away - // Get the translated version of the document to get the content author - DocumentModelBridge translatedDocumentBridge; - try { - translatedDocumentBridge = this.documentAccessBridge.getTranslatedDocumentInstance(documentBridge); - } catch (Exception e) { - throw new MacroExecutionException("Failed to retrieve the translated version of the document", e); - } - if (parameters.getAuthor() == Author.TARGET || parameters.getAuthor() == Author.AUTO && !this.authorization - .hasAccess(Right.PROGRAM, translatedDocumentBridge.getContentAuthorReference(), null)) { - // Merge the two XDOM before executing the included content so that it's as close as possible to the expect - // execution conditions - MacroBlock includeMacro = context.getCurrentMacroBlock(); - MacroMarkerBlock includeMacroMarker = new MacroMarkerBlock(includeMacro.getId(), - includeMacro.getParameters(), Collections.singletonList(metadata), includeMacro.isInline()); - includeMacro.getParent().replaceChild(includeMacroMarker, includeMacro); - + if (parametersContext == Context.CURRENT) { + // Step 7: If the include macro is explicitly configured to be executed with the included document content + // author of if that author does not have programming right, execute it right away + // Get the translated version of the document to get the content author + DocumentModelBridge translatedDocumentBridge; try { - // Execute the content with the right author - // Keep the same transformation context - this.authorExecutor.call(() -> { - this.transformationManager.performTransformations(metadata, context.getTransformationContext()); - return null; - }, translatedDocumentBridge.getContentAuthorReference(), documentBridge.getDocumentReference()); + translatedDocumentBridge = this.documentAccessBridge.getTranslatedDocumentInstance(documentBridge); } catch (Exception e) { - throw new MacroExecutionException("Failed to execute tranformations for document [" - + translatedDocumentBridge.getDocumentReference() + "]"); - } finally { - // Put back the macro in the main XDOM (it will be replaced that the current macro transformation) - includeMacroMarker.getParent().replaceChild(includeMacro, includeMacroMarker); + throw new MacroExecutionException("Failed to retrieve the translated version of the document", e); + } + if (parameters.getAuthor() == Author.TARGET || parameters.getAuthor() == Author.AUTO && !this.authorization + .hasAccess(Right.PROGRAM, translatedDocumentBridge.getContentAuthorReference(), null)) { + // Merge the two XDOM before executing the included content so that it's as close as possible to the + // expect + // execution conditions + MacroBlock includeMacro = context.getCurrentMacroBlock(); + MacroMarkerBlock includeMacroMarker = new MacroMarkerBlock(includeMacro.getId(), + includeMacro.getParameters(), Collections.singletonList(metadata), includeMacro.isInline()); + includeMacro.getParent().replaceChild(includeMacroMarker, includeMacro); + + try { + // Execute the content with the right author + // Keep the same transformation context + this.authorExecutor.call(() -> { + this.transformationManager.performTransformations(metadata, context.getTransformationContext()); + return null; + }, translatedDocumentBridge.getContentAuthorReference(), documentBridge.getDocumentReference()); + } catch (Exception e) { + throw new MacroExecutionException("Failed to execute tranformations for document [" + + translatedDocumentBridge.getDocumentReference() + "]"); + } finally { + // Put back the macro in the main XDOM (it will be replaced that the current macro transformation) + includeMacroMarker.getParent().replaceChild(includeMacro, includeMacroMarker); + } } }
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-include/src/main/java/org/xwiki/rendering/macro/include/IncludeMacroParameters.java+4 −4 modified@@ -104,7 +104,7 @@ public enum Author */ private boolean excludeFirstHeading; - private Author author; + private Author author = Author.AUTO; /** * @param reference the reference of the resource to include @@ -234,7 +234,7 @@ public void setPage(String page) } /** - * @return the author to use to execute the content + * @return the author to use to execute the content when {@link #getContext()} is {@value Context#CURRENT} * @since 15.0RC1 */ public Author getAuthor() @@ -243,10 +243,10 @@ public Author getAuthor() } /** - * @param author the author to use to execute the content + * @param author the author to use to execute the content when {@link #getContext()} is {@value Context#CURRENT} * @since 15.0RC1 */ - @PropertyDescription("The author to use to execute the content") + @PropertyDescription("The author to use to execute the content when context is \"Current\"") @PropertyAdvanced public void setAuthor(Author author) {
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-include/src/main/resources/ApplicationResources.properties+2 −0 modified@@ -32,6 +32,8 @@ rendering.macro.include.parameter.type.name=type rendering.macro.include.parameter.type.description=The type of the reference. rendering.macro.include.parameter.section.name=section rendering.macro.include.parameter.section.description=An optional id of a section to include in the specified page. +rendering.macro.include.parameter.author.name=Author. +rendering.macro.include.parameter.author.description=The author to use to execute the content when context is "Current". org.xwiki.rendering.macro.include.IncludeMacroParameters$Context.NEW=New org.xwiki.rendering.macro.include.IncludeMacroParameters$Context.CURRENT=Current
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-include/src/test/java/org/xwiki/rendering/internal/macro/include/IncludeMacroTest.java+47 −33 modified@@ -60,6 +60,7 @@ import org.xwiki.rendering.macro.Macro; import org.xwiki.rendering.macro.MacroExecutionException; import org.xwiki.rendering.macro.include.IncludeMacroParameters; +import org.xwiki.rendering.macro.include.IncludeMacroParameters.Author; import org.xwiki.rendering.macro.include.IncludeMacroParameters.Context; import org.xwiki.rendering.parser.Parser; import org.xwiki.rendering.renderer.PrintRendererFactory; @@ -176,7 +177,7 @@ public Void answer(InvocationOnMock invocation) throws Throwable } @Test - void execute() throws Exception + void executeWithPRAuthors() throws Exception { // @formatter:off String expected = "beginDocument\n" @@ -188,13 +189,15 @@ void execute() throws Exception + "endDocument"; // @formatter:on - List<Block> blocks = runIncludeMacro(Context.CURRENT, "word", false, true); + when(this.authorizationManager.hasAccess(Right.PROGRAM, INCLUDED_AUHOR, null)).thenReturn(true); + + List<Block> blocks = runIncludeMacro(Context.CURRENT, "word", false); assertBlocks(expected, blocks, this.rendererFactory); } @Test - void executeWithDifferentAuthors() throws Exception + void executeWithCURRENTAuthor() throws Exception { // @formatter:off String expected = "beginDocument\n" @@ -206,7 +209,7 @@ void executeWithDifferentAuthors() throws Exception + "endDocument"; // @formatter:on - List<Block> blocks = runIncludeMacro(Context.CURRENT, "word", false, false); + List<Block> blocks = runIncludeMacro(Context.CURRENT, Author.CURRENT, "word", false); assertBlocks(expected, blocks, this.rendererFactory); } @@ -274,7 +277,7 @@ void executeWithNewContextShowsPassingOnRestrictedFlag() throws Exception // @formatter:on // We verify that a Velocity macro set in the including page is not seen in the included page. - List<Block> blocks = runIncludeMacro(Context.NEW, "{{velocity}}$foo{{/velocity}}", true, false); + List<Block> blocks = runIncludeMacro(Context.NEW, "{{velocity}}$foo{{/velocity}}", true); assertBlocksStartsWith(expected, blocks, this.rendererFactory); } @@ -290,6 +293,8 @@ void executeWithCurrentContextShowsVelocityMacrosAreShared() throws Exception + "endDocument"; // @formatter:on + when(this.authorizationManager.hasAccess(Right.PROGRAM, INCLUDED_AUHOR, null)).thenReturn(true); + // We verify that a Velocity macro set in the including page is seen in the included page. List<Block> blocks = runIncludeMacroWithPreVelocity(Context.CURRENT, "#macro(testmacro)#end", "{{velocity}}#testmacro{{/velocity}}"); @@ -337,7 +342,7 @@ void executeWhenIncludingDocumentWithRelativeReferences() throws Exception PageReference includedPageReference = new PageReference("includedWiki", "includedSpace", "includedPage"); setupDocumentMocks("includedWiki:includedSpace.includedPage", includedDocumentReference, "includedWiki:includedSpace/includedPage", includedPageReference, - "[[page]] [[attach:test.png]] image:test.png", false); + "[[page]] [[attach:test.png]] image:test.png"); when(this.dab.getCurrentDocumentReference()).thenReturn(includedDocumentReference); IncludeMacroParameters parameters = new IncludeMacroParameters(); @@ -383,8 +388,7 @@ void adaptIdsOfIncludedHeadingsAndImages() throws Exception DocumentReference includedDocumentReference = new DocumentReference("includedWiki", "includedSpace", "includedPage"); - setupDocumentMocks("includedWiki:includedSpace.includedPage", includedDocumentReference, documentContent, - false); + setupDocumentMocks("includedWiki:includedSpace.includedPage", includedDocumentReference, documentContent); when(this.dab.getCurrentDocumentReference()).thenReturn(includedDocumentReference); IncludeMacroParameters parameters = new IncludeMacroParameters(); @@ -441,7 +445,7 @@ void executeWithRecursiveIncludeContextNew() throws Exception parameters.setContext(Context.NEW); DocumentReference includedDocumentReference = new DocumentReference("wiki", "space", "page"); - setupDocumentMocks("wiki:space.page", includedDocumentReference, "", false); + setupDocumentMocks("wiki:space.page", includedDocumentReference, ""); when(documentDisplayer.display(same(this.includedDocument), any(DocumentDisplayerParameters.class))) .thenAnswer((Answer) invocation -> { @@ -482,7 +486,7 @@ void executeInsideSourceMetaDataBlockAndWithRelativeDocumentReferencePassed() th DocumentReference sourceReference = new DocumentReference("wiki", "space", "page"); DocumentReference resolvedReference = new DocumentReference("wiki", "space", "relativePage"); - setupDocumentMocks("relativePage", resolvedReference, "content", false); + setupDocumentMocks("relativePage", resolvedReference, "content"); when(this.dab.getCurrentDocumentReference()).thenReturn(sourceReference); List<Block> blocks = this.includeMacro.execute(parameters, null, macroContext); @@ -509,10 +513,11 @@ void executeWhenSectionSpecified() throws Exception IncludeMacroParameters parameters = new IncludeMacroParameters(); parameters.setReference("document"); parameters.setSection("Hsection"); + parameters.setAuthor(Author.CURRENT); MacroTransformationContext macroContext = createMacroTransformationContext("whatever", false); DocumentReference resolvedReference = new DocumentReference("wiki", "space", "document"); - setupDocumentMocks("document", resolvedReference, "content1\n\n= section =\ncontent2", false); + setupDocumentMocks("document", resolvedReference, "content1\n\n= section =\ncontent2"); List<Block> blocks = this.includeMacro.execute(parameters, null, macroContext); @@ -528,7 +533,7 @@ void executeWhenInvalidSectionSpecified() throws Exception MacroTransformationContext macroContext = createMacroTransformationContext("whatever", false); DocumentReference resolvedReference = new DocumentReference("wiki", "space", "document"); - setupDocumentMocks("document", resolvedReference, "content", false); + setupDocumentMocks("document", resolvedReference, "content"); when(this.dab.getCurrentDocumentReference()) .thenReturn(new DocumentReference("wiki", "Space", "IncludingPage")); @@ -553,10 +558,11 @@ void executeWhenExcludeFirstHeadingTrueAndSectionIsFirstBlock() throws Exception IncludeMacroParameters parameters = new IncludeMacroParameters(); parameters.setReference("document"); parameters.setExcludeFirstHeading(true); + parameters.setAuthor(Author.CURRENT); MacroTransformationContext macroContext = createMacroTransformationContext("whatever", false); DocumentReference resolvedReference = new DocumentReference("wiki", "space", "document"); - setupDocumentMocks("document", resolvedReference, "= Heading =\ncontent", false); + setupDocumentMocks("document", resolvedReference, "= Heading =\ncontent"); when(this.dab.getCurrentDocumentReference()) .thenReturn(new DocumentReference("wiki", "Space", "IncludingPage")); @@ -585,10 +591,12 @@ void executeWhenExcludeFirstHeadingTrueAndSectionSpecifiedAndHeadingIsFirstBlock MacroTransformationContext macroContext = createMacroTransformationContext("whatever", false); DocumentReference resolvedReference = new DocumentReference("wiki", "space", "document"); - setupDocumentMocks("document", resolvedReference, "= Heading =\ncontent", false); + setupDocumentMocks("document", resolvedReference, "= Heading =\ncontent"); when(this.dab.getCurrentDocumentReference()) .thenReturn(new DocumentReference("wiki", "Space", "IncludingPage")); + when(this.authorizationManager.hasAccess(Right.PROGRAM, INCLUDED_AUHOR, null)).thenReturn(true); + List<Block> blocks = this.includeMacro.execute(parameters, null, macroContext); assertBlocks(expected, blocks, this.rendererFactory); @@ -612,10 +620,11 @@ void executeWhenExcludeFirstHeadingFalseAndSectionIsFirstBlock() throws Exceptio IncludeMacroParameters parameters = new IncludeMacroParameters(); parameters.setReference("document"); parameters.setExcludeFirstHeading(false); + parameters.setAuthor(Author.CURRENT); MacroTransformationContext macroContext = createMacroTransformationContext("whatever", false); DocumentReference resolvedReference = new DocumentReference("wiki", "space", "document"); - setupDocumentMocks("document", resolvedReference, "=content=", false); + setupDocumentMocks("document", resolvedReference, "=content="); when(this.dab.getCurrentDocumentReference()) .thenReturn(new DocumentReference("wiki", "Space", "IncludingPage")); @@ -644,11 +653,12 @@ void executeWhenExcludeFirstHeadingTrueAndSectionIsNotFirstBlock() throws Except IncludeMacroParameters parameters = new IncludeMacroParameters(); parameters.setReference("document"); parameters.setExcludeFirstHeading(true); + parameters.setAuthor(Author.CURRENT); // Getting the macro context MacroTransformationContext macroContext = createMacroTransformationContext("whatever", false); DocumentReference resolvedReference = new DocumentReference("wiki", "space", "document"); - setupDocumentMocks("document", resolvedReference, "(((= content =)))", false); + setupDocumentMocks("document", resolvedReference, "(((= content =)))"); List<Block> blocks = this.includeMacro.execute(parameters, null, macroContext); assertBlocks(expected, blocks, this.rendererFactory); @@ -664,14 +674,14 @@ private MacroTransformationContext createMacroTransformationContext(String docum } private void setupDocumentMocks(String includedReferenceString, DocumentReference includedReference, - String includedContent, boolean sameAuthors) throws Exception + String includedContent) throws Exception { - setupDocumentMocks(includedReferenceString, includedReference, null, null, includedContent, sameAuthors); + setupDocumentMocks(includedReferenceString, includedReference, null, null, includedContent); } private void setupDocumentMocks(String includedDocumentReferenceString, DocumentReference includedDocumentReference, - String includedPageReferenceString, PageReference includedPageReference, String includedContent, - boolean sameAuthors) throws Exception + String includedPageReferenceString, PageReference includedPageReference, String includedContent) + throws Exception { when(this.macroEntityReferenceResolver.resolve(eq(includedDocumentReferenceString), eq(EntityType.DOCUMENT), any(MacroBlock.class))).thenReturn(includedDocumentReference); @@ -690,12 +700,7 @@ private void setupDocumentMocks(String includedDocumentReferenceString, Document when(this.includedDocument.getSyntax()).thenReturn(Syntax.XWIKI_2_0); when(this.includedDocument.getXDOM()).thenReturn(getXDOM(includedContent)); when(this.includedDocument.getRealLanguage()).thenReturn(""); - - if (sameAuthors) { - when(this.includedDocument.getContentAuthorReference()).thenReturn(INCLUDER_AUHOR); - } else { - when(this.includedDocument.getContentAuthorReference()).thenReturn(INCLUDED_AUHOR); - } + when(this.includedDocument.getContentAuthorReference()).thenReturn(INCLUDED_AUHOR); } private XDOM getXDOM(String content) throws Exception @@ -717,19 +722,28 @@ private List<Block> runIncludeMacroWithPreVelocity(Context context, String veloc private List<Block> runIncludeMacro(final Context context, String includedContent) throws Exception { - return runIncludeMacro(context, includedContent, false, false); + return runIncludeMacro(context, includedContent, false); + } + + private List<Block> runIncludeMacro(final Context context, String includedContent, boolean restricted) + throws Exception + { + return runIncludeMacro(context, null, includedContent, restricted); } - private List<Block> runIncludeMacro(final Context context, String includedContent, boolean restricted, - boolean sameAuthor) throws Exception + private List<Block> runIncludeMacro(final Context context, final Author author, String includedContent, + boolean restricted) throws Exception { DocumentReference includedDocumentReference = new DocumentReference("wiki", "Space", "IncludedPage"); String includedDocStringRef = "wiki:space.page"; - setupDocumentMocks(includedDocStringRef, includedDocumentReference, includedContent, sameAuthor); + setupDocumentMocks(includedDocStringRef, includedDocumentReference, includedContent); IncludeMacroParameters parameters = new IncludeMacroParameters(); parameters.setReference(includedDocStringRef); parameters.setContext(context); + if (author != null) { + parameters.setAuthor(author); + } // Create a Macro transformation context with the Macro transformation object defined so that the include // macro can transform included page which is using a new context. @@ -746,11 +760,11 @@ private List<Block> runIncludeMacro(final Context context, String includedConten verify(this.dab).pushDocumentInContext(any(Map.class), same(this.includedDocument)); verify(this.dab).popDocumentFromContext(any(Map.class)); } else { - if (sameAuthor) { + if (author == Author.CURRENT || this.authorizationManager.hasAccess(Right.PROGRAM, INCLUDED_AUHOR, null)) { verifyNoInteractions(this.authorExecutor); } else { - verify(this.authorExecutor).call(any(), eq(INCLUDED_AUHOR), - eq(this.includedDocument.getDocumentReference())); + DocumentReference includedReference = this.includedDocument.getDocumentReference(); + verify(this.authorExecutor).call(any(), eq(INCLUDED_AUHOR), eq(includedReference)); } }
b48116a3ebe9XWIKI-5027: Improve included content transformation
8 files changed · +66 −34
xwiki-platform-core/xwiki-platform-display/xwiki-platform-display-macro/src/main/java/org/xwiki/rendering/internal/macro/display/DisplayMacro.java+1 −4 modified@@ -65,9 +65,6 @@ public DisplayMacro() { super("Display", DESCRIPTION, DisplayMacroParameters.class); - // The display macro must execute first since if it runs with the current context it needs to bring - // all the macros from the displayed page before the other macros are executed. - setPriority(10); setDefaultCategories(Set.of(DEFAULT_CATEGORY_CONTENT)); } @@ -97,7 +94,7 @@ public List<Block> execute(DisplayMacroParameters parameters, String content, Ma } // Step 3: Check right - if (!this.authorization.hasAccess(Right.VIEW, documentBridge.getDocumentReference())) { + if (!this.contextualAuthorization.hasAccess(Right.VIEW, documentBridge.getDocumentReference())) { throw new MacroExecutionException( String.format("Current user [%s] doesn't have view rights on document [%s]", this.documentAccessBridge.getCurrentUserReference(), documentBridge.getDocumentReference()));
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+0 −3 modified@@ -90,9 +90,6 @@ public ContextMacro() super("Context", DESCRIPTION, new DefaultContentDescriptor(CONTENT_DESCRIPTION, true, Block.LIST_BLOCK_TYPE), ContextMacroParameters.class); - // The Context macro must execute early since it can contain include macros which can bring stuff like headings - // for other macros (TOC macro, etc). Make it the same priority as the Include macro. - setPriority(10); setDefaultCategories(Set.of(DEFAULT_CATEGORY_DEVELOPMENT)); }
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-include/src/main/java/org/xwiki/rendering/internal/macro/include/AbstractIncludeMacro.java+1 −1 modified@@ -57,7 +57,7 @@ public abstract class AbstractIncludeMacro<P> extends AbstractMacro<P> protected DocumentAccessBridge documentAccessBridge; @Inject - protected ContextualAuthorizationManager authorization; + protected ContextualAuthorizationManager contextualAuthorization; /** * Used to serialize resolved document links into a string again since the Rendering API only manipulates Strings
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-include/src/main/java/org/xwiki/rendering/internal/macro/include/IncludeMacro.java+11 −10 modified@@ -22,7 +22,6 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.Stack; @@ -44,10 +43,12 @@ import org.xwiki.rendering.listener.MetaData; import org.xwiki.rendering.macro.MacroExecutionException; import org.xwiki.rendering.macro.include.IncludeMacroParameters; +import org.xwiki.rendering.macro.include.IncludeMacroParameters.Author; import org.xwiki.rendering.macro.include.IncludeMacroParameters.Context; import org.xwiki.rendering.transformation.MacroTransformationContext; import org.xwiki.rendering.transformation.TransformationManager; import org.xwiki.security.authorization.AuthorExecutor; +import org.xwiki.security.authorization.AuthorizationManager; import org.xwiki.security.authorization.Right; /** @@ -74,16 +75,16 @@ public class IncludeMacro extends AbstractIncludeMacro<IncludeMacroParameters> @Inject private AuthorExecutor authorExecutor; + @Inject + protected AuthorizationManager authorization; + /** * Default constructor. */ public IncludeMacro() { super("Include", DESCRIPTION, IncludeMacroParameters.class); - // The include macro must execute first since if it runs with the current context it needs to bring - // all the macros from the included page before the other macros are executed. - setPriority(10); setDefaultCategories(Set.of(DEFAULT_CATEGORY_CONTENT)); } @@ -113,7 +114,7 @@ public List<Block> execute(IncludeMacroParameters parameters, String content, Ma } // Step 3: Check right - if (!this.authorization.hasAccess(Right.VIEW, documentBridge.getDocumentReference())) { + if (!this.contextualAuthorization.hasAccess(Right.VIEW, documentBridge.getDocumentReference())) { throw new MacroExecutionException( String.format("Current user [%s] doesn't have view rights on document [%s]", this.documentAccessBridge.getCurrentUserReference(), documentBridge.getDocumentReference())); @@ -177,17 +178,17 @@ public List<Block> execute(IncludeMacroParameters parameters, String content, Ma metadata.getMetaData().addMetaData(MetaData.BASE, source); } - // Step 7: If the included content author is different from the current author, we have to execute it right now - // so that it used the proper rights - // Get the translated version of the document to compare with the right author + // Step 7: The the include macro is explicitly configured to be executed with the included document content + // author of if that author does not have programming right, execute it right away + // Get the translated version of the document to get the content author DocumentModelBridge translatedDocumentBridge; try { translatedDocumentBridge = this.documentAccessBridge.getTranslatedDocumentInstance(documentBridge); } catch (Exception e) { throw new MacroExecutionException("Failed to retrieve the translated version of the document", e); } - if (!Objects.equals(translatedDocumentBridge.getContentAuthorReference(), - this.documentAccessBridge.getCurrentAuthorReference())) { + if (parameters.getAuthor() == Author.TARGET || parameters.getAuthor() == Author.AUTO && !this.authorization + .hasAccess(Right.PROGRAM, translatedDocumentBridge.getContentAuthorReference(), null)) { // Merge the two XDOM before executing the included content so that it's as close as possible to the expect // execution conditions MacroBlock includeMacro = context.getCurrentMacroBlock();
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-include/src/main/java/org/xwiki/rendering/macro/include/IncludeMacroParameters.java+53 −7 modified@@ -54,6 +54,30 @@ public enum Context CURRENT } + /** + * Control which author to execute the included content with. + * + * @since 15.0RC1 + */ + public enum Author + { + /** + * Apply TARGET option unless the target author has programming right in which case it applies CURRENT (mainly + * for retro-compatibility reasons). + */ + AUTO, + + /** + * The content is executed with the right of the current author which uses the include macro. + */ + CURRENT, + + /** + * The content is executed with the right of the included content author. + */ + TARGET + } + /** * @see #getReference() */ @@ -74,12 +98,14 @@ public enum Context * @see #getSection() */ private String section; - + /** * @see #isExcludeFirstHeading() */ private boolean excludeFirstHeading; - + + private Author author; + /** * @param reference the reference of the resource to include * @since 3.4M1 @@ -122,9 +148,9 @@ public String getSection() } /** - * @param excludeFirstHeading {@code true} to remove the first heading found inside - * the document or the section, {@code false} to keep it - * @since 12.4RC1 + * @param excludeFirstHeading {@code true} to remove the first heading found inside the document or the section, + * {@code false} to keep it + * @since 12.4RC1 */ @PropertyName("Exclude First Heading") @PropertyDescription("Exclude the first heading from the included document or section.") @@ -133,10 +159,10 @@ public void setExcludeFirstHeading(boolean excludeFirstHeading) { this.excludeFirstHeading = excludeFirstHeading; } - + /** * @return whether to exclude the first heading from the included document or section, or not. - * @since 12.4RC1 + * @since 12.4RC1 */ public boolean isExcludeFirstHeading() { @@ -206,4 +232,24 @@ public void setPage(String page) this.reference = page; this.type = EntityType.PAGE; } + + /** + * @return the author to use to execute the content + * @since 15.0RC1 + */ + public Author getAuthor() + { + return this.author; + } + + /** + * @param author the author to use to execute the content + * @since 15.0RC1 + */ + @PropertyDescription("The author to use to execute the content") + @PropertyAdvanced + public void setAuthor(Author author) + { + this.author = author; + } }
xwiki-platform-core/xwiki-platform-template/xwiki-platform-template-api/src/main/java/org/xwiki/template/internal/macro/TemplateMacro.java+0 −3 modified@@ -63,9 +63,6 @@ public TemplateMacro() { super("Template", DESCRIPTION, TemplateMacroParameters.class); - // The template macro must execute first since if it runs with the current context it needs to bring - // all the macros from the template before the other macros are executed. - setPriority(10); setDefaultCategories(Set.of(DEFAULT_CATEGORY_DEVELOPMENT)); }
xwiki-platform-core/xwiki-platform-uiextension/xwiki-platform-uiextension-api/src/main/java/org/xwiki/uiextension/internal/macro/UIExtensionMacro.java+0 −3 modified@@ -66,9 +66,6 @@ public UIExtensionMacro() { super("UI Extensions", DESCRIPTION, UIExtensionsMacroParameters.class); - // The ui extensions macro must execute first since if it runs with the current context it needs to bring - // all the macros from the extension before the other macros are executed. - setPriority(10); setDefaultCategories(Set.of(DEFAULT_CATEGORY_DEVELOPMENT)); }
xwiki-platform-core/xwiki-platform-uiextension/xwiki-platform-uiextension-api/src/main/java/org/xwiki/uiextension/internal/macro/UIExtensionsMacro.java+0 −3 modified@@ -64,9 +64,6 @@ public UIExtensionsMacro() { super("UI Extensions", DESCRIPTION, UIExtensionsMacroParameters.class); - // The ui extensions macro must execute first since if it runs with the current context it needs to bring - // all the macros from the extension before the other macros are executed. - setPriority(10); setDefaultCategories(Set.of(DEFAULT_CATEGORY_DEVELOPMENT)); }
c1fb14402ce2XWIKI-5027: Improve included content transformation
2 files changed · +154 −41
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-include/src/main/java/org/xwiki/rendering/internal/macro/include/IncludeMacro.java+47 −3 modified@@ -22,6 +22,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.Stack; @@ -36,6 +37,7 @@ import org.xwiki.properties.BeanManager; import org.xwiki.properties.PropertyException; import org.xwiki.rendering.block.Block; +import org.xwiki.rendering.block.MacroBlock; import org.xwiki.rendering.block.MacroMarkerBlock; import org.xwiki.rendering.block.MetaDataBlock; import org.xwiki.rendering.block.XDOM; @@ -44,6 +46,8 @@ import org.xwiki.rendering.macro.include.IncludeMacroParameters; import org.xwiki.rendering.macro.include.IncludeMacroParameters.Context; import org.xwiki.rendering.transformation.MacroTransformationContext; +import org.xwiki.rendering.transformation.TransformationManager; +import org.xwiki.security.authorization.AuthorExecutor; import org.xwiki.security.authorization.Right; /** @@ -64,6 +68,12 @@ public class IncludeMacro extends AbstractIncludeMacro<IncludeMacroParameters> @Inject private BeanManager beans; + @Inject + private TransformationManager transformationManager; + + @Inject + private AuthorExecutor authorExecutor; + /** * Default constructor. */ @@ -88,8 +98,8 @@ public List<Block> execute(IncludeMacroParameters parameters, String content, Ma throws MacroExecutionException { // Step 1: Perform checks. - EntityReference includedReference = resolve(context.getCurrentMacroBlock(), parameters.getReference(), - parameters.getType(), "include"); + EntityReference includedReference = + resolve(context.getCurrentMacroBlock(), parameters.getReference(), parameters.getType(), "include"); checkRecursion(context.getCurrentMacroBlock(), includedReference); // Step 2: Retrieve the included document. @@ -149,7 +159,7 @@ public List<Block> execute(IncludeMacroParameters parameters, String content, Ma references.pop(); } } - + // Step 5: If the user has asked for it, remove both Section and Heading Blocks if the first included block is // a Section block with a Heading block inside. if (parameters.isExcludeFirstHeading()) { @@ -167,6 +177,40 @@ public List<Block> execute(IncludeMacroParameters parameters, String content, Ma metadata.getMetaData().addMetaData(MetaData.BASE, source); } + // Step 7: If the included content author is different from the current author, we have to execute it right now + // so that it used the proper rights + // Get the translated version of the document to compare with the right author + DocumentModelBridge translatedDocumentBridge; + try { + translatedDocumentBridge = this.documentAccessBridge.getTranslatedDocumentInstance(documentBridge); + } catch (Exception e) { + throw new MacroExecutionException("Failed to retrieve the translated version of the document", e); + } + if (!Objects.equals(translatedDocumentBridge.getContentAuthorReference(), + this.documentAccessBridge.getCurrentAuthorReference())) { + // Merge the two XDOM before executing the included content so that it's as close as possible to the expect + // execution conditions + MacroBlock includeMacro = context.getCurrentMacroBlock(); + MacroMarkerBlock includeMacroMarker = new MacroMarkerBlock(includeMacro.getId(), + includeMacro.getParameters(), Collections.singletonList(metadata), includeMacro.isInline()); + includeMacro.getParent().replaceChild(includeMacroMarker, includeMacro); + + try { + // Execute the content with the right author + // Keep the same transformation context + this.authorExecutor.call(() -> { + this.transformationManager.performTransformations(metadata, context.getTransformationContext()); + return null; + }, translatedDocumentBridge.getContentAuthorReference(), documentBridge.getDocumentReference()); + } catch (Exception e) { + throw new MacroExecutionException("Failed to execute tranformations for document [" + + translatedDocumentBridge.getDocumentReference() + "]"); + } finally { + // Put back the macro in the main XDOM (it will be replaced that the current macro transformation) + includeMacroMarker.getParent().replaceChild(includeMacro, includeMacroMarker); + } + } + return Collections.singletonList(metadata); }
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-include/src/test/java/org/xwiki/rendering/internal/macro/include/IncludeMacroTest.java+107 −38 modified@@ -25,11 +25,14 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.Callable; import javax.inject.Named; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.xwiki.bridge.DocumentAccessBridge; import org.xwiki.bridge.DocumentModelBridge; @@ -64,6 +67,7 @@ import org.xwiki.rendering.transformation.MacroTransformationContext; import org.xwiki.rendering.transformation.Transformation; import org.xwiki.rendering.wiki.WikiModel; +import org.xwiki.security.authorization.AuthorExecutor; import org.xwiki.security.authorization.AuthorizationManager; import org.xwiki.security.authorization.ContextualAuthorizationManager; import org.xwiki.security.authorization.DefaultAuthorizationManager; @@ -83,6 +87,7 @@ import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; import static org.xwiki.rendering.test.integration.junit5.BlockAssert.assertBlocks; import static org.xwiki.rendering.test.integration.junit5.BlockAssert.assertBlocksStartsWith; @@ -94,18 +99,16 @@ * @since 1.5M2 */ @ComponentTest -@AllComponents(excludes = { - CurrentMacroEntityReferenceResolver.class, - DefaultAuthorizationManager.class -}) +@AllComponents(excludes = {CurrentMacroEntityReferenceResolver.class, DefaultAuthorizationManager.class}) class IncludeMacroTest { + private final static DocumentReference INCLUDER_AUHOR = new DocumentReference("wiki", "XWiki", "includer"); + + private final static DocumentReference INCLUDED_AUHOR = new DocumentReference("wiki", "XWiki", "included"); + @InjectComponentManager private MockitoComponentManager componentManager; - @MockComponent - private DocumentModelBridge includedDocument; - @MockComponent private DocumentAccessBridge dab; @@ -120,6 +123,12 @@ class IncludeMacroTest @Named("current") private AttachmentReferenceResolver<String> currentAttachmentReferenceResolver; + @MockComponent + private AuthorExecutor authorExecutor; + + @Mock + private DocumentModelBridge includedDocument; + /** * Mocks the component that is used to resolve the 'reference' parameter. */ @@ -141,6 +150,8 @@ public void setUp() throws Exception this.includeMacro = this.componentManager.getInstance(Macro.class, "include"); this.rendererFactory = this.componentManager.getInstance(PrintRendererFactory.class, "event/1.0"); + when(this.dab.getCurrentAuthorReference()).thenReturn(INCLUDER_AUHOR); + // Put a fake XWiki context on the execution context. Execution execution = this.componentManager.getInstance(Execution.class); ExecutionContextManager ecm = this.componentManager.getInstance(ExecutionContextManager.class); @@ -153,6 +164,15 @@ public void setUp() throws Exception // Register a WikiModel mock so that we're in wiki mode (otherwise links will be considered as URLs for ex). this.componentManager.registerMockComponent(WikiModel.class); + + when(this.authorExecutor.call(any(), any(), any())).then(new Answer<Void>() + { + @Override + public Void answer(InvocationOnMock invocation) throws Throwable + { + return ((Callable<Void>) invocation.getArgument(0)).call(); + } + }); } @Test @@ -168,11 +188,50 @@ void execute() throws Exception + "endDocument"; // @formatter:on - List<Block> blocks = runIncludeMacro(Context.CURRENT, "word", false); + List<Block> blocks = runIncludeMacro(Context.CURRENT, "word", false, true); assertBlocks(expected, blocks, this.rendererFactory); } + @Test + void executeWithDifferentAuthors() throws Exception + { + // @formatter:off + String expected = "beginDocument\n" + + "beginMetaData [[source]=[wiki:Space.IncludedPage][syntax]=[XWiki 2.0]]\n" + + "beginParagraph\n" + + "onWord [word]\n" + + "endParagraph\n" + + "endMetaData [[source]=[wiki:Space.IncludedPage][syntax]=[XWiki 2.0]]\n" + + "endDocument"; + // @formatter:on + + List<Block> blocks = runIncludeMacro(Context.CURRENT, "word", false, false); + + assertBlocks(expected, blocks, this.rendererFactory); + } + + @Test + void executeWithCurrentUserNoView() throws Exception + { + String referenceString = "reference"; + DocumentReference reference = new DocumentReference("wiki", "space", "page"); + + when(this.macroEntityReferenceResolver.resolve(eq(referenceString), eq(EntityType.DOCUMENT), + any(MacroBlock.class))).thenReturn(reference); + when(this.dab.getDocumentInstance((EntityReference) reference)).thenReturn(this.includedDocument); + when(this.includedDocument.getDocumentReference()).thenReturn(reference); + when(this.contextualAuthorizationManager.hasAccess(Right.VIEW, reference)).thenReturn(false); + + IncludeMacroParameters parameters = new IncludeMacroParameters(); + parameters.setReference(referenceString); + + Throwable exception = assertThrows(MacroExecutionException.class, + () -> this.includeMacro.execute(parameters, null, createMacroTransformationContext("whatever", false))); + assertEquals("Current user [null] doesn't have view rights on document [wiki:space.page]", + exception.getMessage()); + } + @Test void executeWithNewContextShowsVelocityMacrosAreIsolated() throws Exception { @@ -215,7 +274,7 @@ void executeWithNewContextShowsPassingOnRestrictedFlag() throws Exception // @formatter:on // We verify that a Velocity macro set in the including page is not seen in the included page. - List<Block> blocks = runIncludeMacro(Context.NEW, "{{velocity}}$foo{{/velocity}}", true); + List<Block> blocks = runIncludeMacro(Context.NEW, "{{velocity}}$foo{{/velocity}}", true, false); assertBlocksStartsWith(expected, blocks, this.rendererFactory); } @@ -278,7 +337,7 @@ void executeWhenIncludingDocumentWithRelativeReferences() throws Exception PageReference includedPageReference = new PageReference("includedWiki", "includedSpace", "includedPage"); setupDocumentMocks("includedWiki:includedSpace.includedPage", includedDocumentReference, "includedWiki:includedSpace/includedPage", includedPageReference, - "[[page]] [[attach:test.png]] image:test.png"); + "[[page]] [[attach:test.png]] image:test.png", false); when(this.dab.getCurrentDocumentReference()).thenReturn(includedDocumentReference); IncludeMacroParameters parameters = new IncludeMacroParameters(); @@ -294,8 +353,7 @@ void executeWhenIncludingDocumentWithRelativeReferences() throws Exception parameters.setPage("includedWiki:includedSpace/includedPage"); - blocks = - this.includeMacro.execute(parameters, null, createMacroTransformationContext("whatever", false)); + blocks = this.includeMacro.execute(parameters, null, createMacroTransformationContext("whatever", false)); assertBlocks(expected, blocks, this.rendererFactory); } @@ -321,13 +379,12 @@ void adaptIdsOfIncludedHeadingsAndImages() throws Exception + "endDocument"; // @formatter:on - String documentContent = "= Heading =\n" - + "image:test.png"; + String documentContent = "= Heading =\n" + "image:test.png"; DocumentReference includedDocumentReference = new DocumentReference("includedWiki", "includedSpace", "includedPage"); - setupDocumentMocks("includedWiki:includedSpace.includedPage", includedDocumentReference, - documentContent); + setupDocumentMocks("includedWiki:includedSpace.includedPage", includedDocumentReference, documentContent, + false); when(this.dab.getCurrentDocumentReference()).thenReturn(includedDocumentReference); IncludeMacroParameters parameters = new IncludeMacroParameters(); @@ -384,18 +441,17 @@ void executeWithRecursiveIncludeContextNew() throws Exception parameters.setContext(Context.NEW); DocumentReference includedDocumentReference = new DocumentReference("wiki", "space", "page"); - setupDocumentMocks("wiki:space.page", includedDocumentReference, ""); + setupDocumentMocks("wiki:space.page", includedDocumentReference, "", false); - when(documentDisplayer.display(same(this.includedDocument), any(DocumentDisplayerParameters.class))).thenAnswer( - (Answer) invocation -> { + when(documentDisplayer.display(same(this.includedDocument), any(DocumentDisplayerParameters.class))) + .thenAnswer((Answer) invocation -> { // Call again the include macro when the document displayer executes to simulate a recursive call. // Verify that it raises a MacroExecutionException in this case. Throwable exception = assertThrows(MacroExecutionException.class, () -> this.includeMacro.execute(parameters, null, macroContext)); assertEquals("Found recursive inclusion of document [wiki:space.page]", exception.getMessage()); throw exception; - } - ); + }); // Verify that the exception bubbles up. Throwable exception = assertThrows(MacroExecutionException.class, @@ -426,7 +482,7 @@ void executeInsideSourceMetaDataBlockAndWithRelativeDocumentReferencePassed() th DocumentReference sourceReference = new DocumentReference("wiki", "space", "page"); DocumentReference resolvedReference = new DocumentReference("wiki", "space", "relativePage"); - setupDocumentMocks("relativePage", resolvedReference, "content"); + setupDocumentMocks("relativePage", resolvedReference, "content", false); when(this.dab.getCurrentDocumentReference()).thenReturn(sourceReference); List<Block> blocks = this.includeMacro.execute(parameters, null, macroContext); @@ -456,7 +512,7 @@ void executeWhenSectionSpecified() throws Exception MacroTransformationContext macroContext = createMacroTransformationContext("whatever", false); DocumentReference resolvedReference = new DocumentReference("wiki", "space", "document"); - setupDocumentMocks("document", resolvedReference, "content1\n\n= section =\ncontent2"); + setupDocumentMocks("document", resolvedReference, "content1\n\n= section =\ncontent2", false); List<Block> blocks = this.includeMacro.execute(parameters, null, macroContext); @@ -472,9 +528,9 @@ void executeWhenInvalidSectionSpecified() throws Exception MacroTransformationContext macroContext = createMacroTransformationContext("whatever", false); DocumentReference resolvedReference = new DocumentReference("wiki", "space", "document"); - setupDocumentMocks("document", resolvedReference, "content"); - when(this.dab.getCurrentDocumentReference()).thenReturn( - new DocumentReference("wiki", "Space", "IncludingPage")); + setupDocumentMocks("document", resolvedReference, "content", false); + when(this.dab.getCurrentDocumentReference()) + .thenReturn(new DocumentReference("wiki", "Space", "IncludingPage")); Throwable exception = assertThrows(MacroExecutionException.class, () -> this.includeMacro.execute(parameters, null, macroContext)); @@ -500,7 +556,7 @@ void executeWhenExcludeFirstHeadingTrueAndSectionIsFirstBlock() throws Exception MacroTransformationContext macroContext = createMacroTransformationContext("whatever", false); DocumentReference resolvedReference = new DocumentReference("wiki", "space", "document"); - setupDocumentMocks("document", resolvedReference, "= Heading =\ncontent"); + setupDocumentMocks("document", resolvedReference, "= Heading =\ncontent", false); when(this.dab.getCurrentDocumentReference()) .thenReturn(new DocumentReference("wiki", "Space", "IncludingPage")); @@ -529,7 +585,7 @@ void executeWhenExcludeFirstHeadingTrueAndSectionSpecifiedAndHeadingIsFirstBlock MacroTransformationContext macroContext = createMacroTransformationContext("whatever", false); DocumentReference resolvedReference = new DocumentReference("wiki", "space", "document"); - setupDocumentMocks("document", resolvedReference, "= Heading =\ncontent"); + setupDocumentMocks("document", resolvedReference, "= Heading =\ncontent", false); when(this.dab.getCurrentDocumentReference()) .thenReturn(new DocumentReference("wiki", "Space", "IncludingPage")); @@ -559,7 +615,7 @@ void executeWhenExcludeFirstHeadingFalseAndSectionIsFirstBlock() throws Exceptio MacroTransformationContext macroContext = createMacroTransformationContext("whatever", false); DocumentReference resolvedReference = new DocumentReference("wiki", "space", "document"); - setupDocumentMocks("document", resolvedReference, "=content="); + setupDocumentMocks("document", resolvedReference, "=content=", false); when(this.dab.getCurrentDocumentReference()) .thenReturn(new DocumentReference("wiki", "Space", "IncludingPage")); @@ -592,7 +648,7 @@ void executeWhenExcludeFirstHeadingTrueAndSectionIsNotFirstBlock() throws Except // Getting the macro context MacroTransformationContext macroContext = createMacroTransformationContext("whatever", false); DocumentReference resolvedReference = new DocumentReference("wiki", "space", "document"); - setupDocumentMocks("document", resolvedReference, "(((= content =)))"); + setupDocumentMocks("document", resolvedReference, "(((= content =)))", false); List<Block> blocks = this.includeMacro.execute(parameters, null, macroContext); assertBlocks(expected, blocks, this.rendererFactory); @@ -608,14 +664,14 @@ private MacroTransformationContext createMacroTransformationContext(String docum } private void setupDocumentMocks(String includedReferenceString, DocumentReference includedReference, - String includedContent) throws Exception + String includedContent, boolean sameAuthors) throws Exception { - setupDocumentMocks(includedReferenceString, includedReference, null, null, includedContent); + setupDocumentMocks(includedReferenceString, includedReference, null, null, includedContent, sameAuthors); } private void setupDocumentMocks(String includedDocumentReferenceString, DocumentReference includedDocumentReference, - String includedPageReferenceString, PageReference includedPageReference, String includedContent) - throws Exception + String includedPageReferenceString, PageReference includedPageReference, String includedContent, + boolean sameAuthors) throws Exception { when(this.macroEntityReferenceResolver.resolve(eq(includedDocumentReferenceString), eq(EntityType.DOCUMENT), any(MacroBlock.class))).thenReturn(includedDocumentReference); @@ -634,6 +690,12 @@ private void setupDocumentMocks(String includedDocumentReferenceString, Document when(this.includedDocument.getSyntax()).thenReturn(Syntax.XWIKI_2_0); when(this.includedDocument.getXDOM()).thenReturn(getXDOM(includedContent)); when(this.includedDocument.getRealLanguage()).thenReturn(""); + + if (sameAuthors) { + when(this.includedDocument.getContentAuthorReference()).thenReturn(INCLUDER_AUHOR); + } else { + when(this.includedDocument.getContentAuthorReference()).thenReturn(INCLUDED_AUHOR); + } } private XDOM getXDOM(String content) throws Exception @@ -655,15 +717,15 @@ private List<Block> runIncludeMacroWithPreVelocity(Context context, String veloc private List<Block> runIncludeMacro(final Context context, String includedContent) throws Exception { - return runIncludeMacro(context, includedContent, false); + return runIncludeMacro(context, includedContent, false, false); } - private List<Block> runIncludeMacro(final Context context, String includedContent, boolean restricted) - throws Exception + private List<Block> runIncludeMacro(final Context context, String includedContent, boolean restricted, + boolean sameAuthor) throws Exception { DocumentReference includedDocumentReference = new DocumentReference("wiki", "Space", "IncludedPage"); String includedDocStringRef = "wiki:space.page"; - setupDocumentMocks(includedDocStringRef, includedDocumentReference, includedContent); + setupDocumentMocks(includedDocStringRef, includedDocumentReference, includedContent, sameAuthor); IncludeMacroParameters parameters = new IncludeMacroParameters(); parameters.setReference(includedDocStringRef); @@ -683,6 +745,13 @@ private List<Block> runIncludeMacro(final Context context, String includedConten if (context == Context.NEW) { verify(this.dab).pushDocumentInContext(any(Map.class), same(this.includedDocument)); verify(this.dab).popDocumentFromContext(any(Map.class)); + } else { + if (sameAuthor) { + verifyNoInteractions(this.authorExecutor); + } else { + verify(this.authorExecutor).call(any(), eq(INCLUDED_AUHOR), + eq(this.includedDocument.getDocumentReference())); + } } return blocks;
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
10- github.com/advisories/GHSA-qcj3-wpgm-qpxhghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-38369ghsaADVISORY
- github.com/xwiki/xwiki-platform/commit/0a4f9b026ba9931516b4e9b3019da8da838c7ac6ghsaWEB
- github.com/xwiki/xwiki-platform/commit/b48116a3ebe9ce928c401b5d068d4db7e7239575ghsaWEB
- github.com/xwiki/xwiki-platform/commit/c1fb14402ce2ee569c5a8e3f1f8e64ae45dfbfb0ghsaWEB
- github.com/xwiki/xwiki-platform/commit/d1a84a3eea38305ff8e10ba411910c0675ac157cghsaWEB
- github.com/xwiki/xwiki-platform/commit/f627abe2dc39b07ff75fe68398cc8a1bbc743ef7ghsaWEB
- github.com/xwiki/xwiki-platform/security/advisories/GHSA-qcj3-wpgm-qpxhghsax_refsource_CONFIRMWEB
- jira.xwiki.org/browse/XWIKI-20471ghsaWEB
- jira.xwiki.org/browse/XWIKI-5027ghsaWEB
News mentions
0No linked articles in our index yet.