VYPR
High severityNVD Advisory· Published Jun 13, 2025· Updated Jun 13, 2025

XWiki's required right warnings for macros are incomplete

CVE-2025-49582

Description

XWiki is a generic wiki platform. When editing content that contains "dangerous" macros like malicious script macros that were authored by a user with fewer rights, XWiki warns about the execution of these macros since XWiki 15.9RC1. These required rights analyzers that trigger these warnings are incomplete, allowing an attacker to hide malicious content. For most macros, the existing analyzers don't consider non-lowercase parameters. Further, most macro parameters that can contain XWiki syntax like titles of information boxes weren't analyzed at all. Similarly, the "source" parameters of the content and context macro weren't anylzed even though they could contain arbitrary XWiki syntax. In the worst case, this could allow a malicious to add malicious script macros including Groovy or Python macros to a page that are then executed after another user with programming righs edits the page, thus allowing remote code execution. The required rights analyzers have been made more robust and extended to cover those cases in XWiki 16.4.7, 16.10.3 and 17.0.0.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.xwiki.platform:xwiki-platform-rendering-xwikiMaven
>= 15.9-rc-1, < 16.4.716.4.7
org.xwiki.platform:xwiki-platform-rendering-xwikiMaven
>= 16.5.0-rc-1, < 16.10.316.10.3
org.xwiki.platform:xwiki-platform-rendering-xwikiMaven
>= 17.0.0-rc-1, < 17.0.017.0.0
org.xwiki.platform:xwiki-platform-rendering-macro-cacheMaven
>= 15.9-rc-1, < 16.4.716.4.7
org.xwiki.platform:xwiki-platform-rendering-macro-cacheMaven
>= 16.5.0-rc-1, < 16.10.316.10.3
org.xwiki.platform:xwiki-platform-rendering-macro-cacheMaven
>= 17.0.0-rc-1, < 17.0.017.0.0
org.xwiki.platform:xwiki-platform-security-requiredrights-defaultMaven
>= 15.9-rc-1, < 16.4.716.4.7
org.xwiki.platform:xwiki-platform-security-requiredrights-defaultMaven
>= 16.5.0-rc-1, < 16.10.316.10.3
org.xwiki.platform:xwiki-platform-security-requiredrights-defaultMaven
>= 17.0.0-rc-1, < 17.0.017.0.0
org.xwiki.platform:xwiki-platform-rendering-macro-contextMaven
>= 15.9-rc-1, < 16.4.716.4.7
org.xwiki.platform:xwiki-platform-rendering-macro-contextMaven
>= 16.5.0-rc-1, < 16.10.316.10.3
org.xwiki.platform:xwiki-platform-rendering-macro-contextMaven
>= 17.0.0-rc-1, < 17.0.017.0.0

Affected products

1

Patches

4
3d451e957fe2

XWIKI-22799: Make the required rights analyzers of the Cache, HTML and RAW macro case-insensitive

https://github.com/xwiki/xwiki-platformMichael HamannJan 20, 2025via ghsa
6 files changed · +158 26
  • xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-cache/src/main/java/org/xwiki/rendering/internal/macro/cache/CacheMacroRequiredRightsAnalyzer.java+5 2 modified
    @@ -23,8 +23,8 @@
     import javax.inject.Singleton;
     
     import org.xwiki.component.annotation.Component;
    -import org.xwiki.platform.security.requiredrights.MacroRequiredRightsAnalyzer;
     import org.xwiki.platform.security.requiredrights.MacroRequiredRightReporter;
    +import org.xwiki.platform.security.requiredrights.MacroRequiredRightsAnalyzer;
     import org.xwiki.rendering.block.MacroBlock;
     
     /**
    @@ -43,7 +43,10 @@ public class CacheMacroRequiredRightsAnalyzer implements MacroRequiredRightsAnal
         @Override
         public void analyze(MacroBlock macroBlock, MacroRequiredRightReporter reporter)
         {
    -        reporter.analyzeContent(macroBlock, macroBlock.getParameter("id"));
    +        macroBlock.getParameters().entrySet().stream()
    +            .filter(entry -> "id".equalsIgnoreCase(entry.getKey()))
    +            .forEach(entry -> reporter.analyzeContent(macroBlock, entry.getValue()));
    +
             reporter.analyzeContent(macroBlock, macroBlock.getContent());
         }
     }
    
  • xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-cache/src/test/java/org/xwiki/rendering/internal/macro/cache/CacheMacroRequiredRightsAnalyzerTest.java+33 6 modified
    @@ -19,15 +19,21 @@
      */
     package org.xwiki.rendering.internal.macro.cache;
     
    +import java.util.Map;
    +import java.util.function.Function;
    +import java.util.stream.Collectors;
    +import java.util.stream.Stream;
    +
     import org.junit.jupiter.api.Test;
    +import org.junit.jupiter.params.ParameterizedTest;
    +import org.junit.jupiter.params.provider.MethodSource;
     import org.xwiki.platform.security.requiredrights.MacroRequiredRightReporter;
     import org.xwiki.rendering.block.MacroBlock;
     import org.xwiki.test.junit5.mockito.ComponentTest;
     import org.xwiki.test.junit5.mockito.InjectMockComponents;
     
     import static org.mockito.Mockito.mock;
     import static org.mockito.Mockito.verify;
    -import static org.mockito.Mockito.when;
     
     /**
      * Unit tests for {@link CacheMacroRequiredRightsAnalyzer}.
    @@ -40,19 +46,40 @@ class CacheMacroRequiredRightsAnalyzerTest
         @InjectMockComponents
         private CacheMacroRequiredRightsAnalyzer analyzer;
     
    -    @Test
    -    void analyze()
    +    @ParameterizedTest
    +    @MethodSource("idValuesProvider")
    +    void analyze(String parameterName)
         {
    -        MacroBlock macroBlock = mock();
             String idValue = "idValue";
             String contentValue = "contentValue";
    -        when(macroBlock.getParameter("id")).thenReturn(idValue);
    -        when(macroBlock.getContent()).thenReturn(contentValue);
    +        MacroBlock macroBlock = new MacroBlock("cache", Map.of(parameterName, idValue), contentValue, false);
             MacroRequiredRightReporter reporter = mock();
     
             this.analyzer.analyze(macroBlock, reporter);
     
             verify(reporter).analyzeContent(macroBlock, idValue);
             verify(reporter).analyzeContent(macroBlock, contentValue);
         }
    +
    +    @Test
    +    void analyzeAllInOne()
    +    {
    +        Map<String, String> parameters = idValuesProvider().collect(Collectors.toMap(Function.identity(),
    +            Function.identity()));
    +        String content = "content";
    +        MacroBlock macroBlock = new MacroBlock("cache", parameters, content, false);
    +        MacroRequiredRightReporter reporter = mock();
    +
    +        this.analyzer.analyze(macroBlock, reporter);
    +
    +        verify(reporter).analyzeContent(macroBlock, content);
    +        for (String value : parameters.values()) {
    +            verify(reporter).analyzeContent(macroBlock, value);
    +        }
    +    }
    +
    +    static Stream<String> idValuesProvider()
    +    {
    +        return Stream.of("id", "ID", "Id", "iD");
    +    }
     }
    
  • xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-xwiki/src/main/java/org/xwiki/rendering/internal/macro/HTMLMacroRequiredRightsAnalyzer.java+11 4 modified
    @@ -24,6 +24,7 @@
     import java.util.HashMap;
     import java.util.List;
     import java.util.Map;
    +import java.util.stream.Stream;
     
     import javax.inject.Inject;
     import javax.inject.Named;
    @@ -67,11 +68,10 @@ public class HTMLMacroRequiredRightsAnalyzer implements MacroRequiredRightsAnaly
         @Override
         public void analyze(MacroBlock macroBlock, MacroRequiredRightReporter reporter)
         {
    -        boolean wiki = Boolean.TRUE.equals(this.converter.convert(Boolean.class, macroBlock.getParameter("wiki")));
    -        String cleanParameter = macroBlock.getParameter("clean");
    +        boolean wiki = getBooleanParameterValues(macroBlock, "wiki").anyMatch(Boolean.TRUE::equals);
    +
             // Cleaning is enabled by default.
    -        boolean clean =
    -            cleanParameter == null || Boolean.TRUE.equals(this.converter.convert(Boolean.class, cleanParameter));
    +        boolean clean = getBooleanParameterValues(macroBlock, "clean").noneMatch(Boolean.FALSE::equals);
     
             if (wiki) {
                 reporter.analyzeContent(macroBlock, macroBlock.getContent());
    @@ -105,4 +105,11 @@ public void analyze(MacroBlock macroBlock, MacroRequiredRightReporter reporter)
                 }
             }
         }
    +
    +    private Stream<Boolean> getBooleanParameterValues(MacroBlock macroBlock, String parameterName)
    +    {
    +        return macroBlock.getParameters().entrySet().stream()
    +            .filter(entry -> parameterName.equalsIgnoreCase(entry.getKey()))
    +            .map(entry -> this.converter.convert(Boolean.class, entry.getValue()));
    +    }
     }
    
  • xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-xwiki/src/main/java/org/xwiki/rendering/internal/macro/RawMacroRequiredRightsAnalyzer.java+16 6 modified
    @@ -50,15 +50,25 @@ public class RawMacroRequiredRightsAnalyzer implements MacroRequiredRightsAnalyz
     
         @Override
         public void analyze(MacroBlock macroBlock, MacroRequiredRightReporter reporter)
    +    {
    +        // Check if any parameter that is equal to "syntax" ignoring case is an HTML syntax.
    +        boolean isHTML = macroBlock.getParameters().entrySet().stream()
    +            .filter(entry -> "syntax".equalsIgnoreCase(entry.getKey()))
    +            .anyMatch(entry -> isHTMLSyntax(entry.getValue()));
    +
    +        if (isHTML) {
    +            reporter.report(macroBlock, List.of(MacroRequiredRight.SCRIPT), "rendering.macro.rawMacroRequiredRights");
    +        }
    +    }
    +
    +    private boolean isHTMLSyntax(String syntaxValue)
         {
             try {
    -            SyntaxType syntax = this.syntaxRegistry.resolveSyntax(macroBlock.getParameter("syntax")).getType();
    -            if (SyntaxType.HTML_FAMILY_TYPES.contains(syntax)) {
    -                reporter.report(macroBlock, List.of(MacroRequiredRight.SCRIPT),
    -                    "rendering.macro.rawMacroRequiredRights");
    -            }
    +            SyntaxType syntax = this.syntaxRegistry.resolveSyntax(syntaxValue).getType();
    +            return SyntaxType.HTML_FAMILY_TYPES.contains(syntax);
             } catch (ParseException e) {
    -            // Ignore, this should fail the macro or at least won't produce HTML output.
    +            // Values that can't be parsed also won't be considered as HTML by the macro itself.
    +            return false;
             }
         }
     }
    
  • xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-xwiki/src/test/java/org/xwiki/rendering/internal/macro/HTMLMacroRequiredRightsAnalyzerTest.java+65 0 modified
    @@ -21,10 +21,13 @@
     
     import java.util.List;
     import java.util.Map;
    +import java.util.stream.Stream;
     
     import org.junit.jupiter.api.BeforeEach;
     import org.junit.jupiter.params.ParameterizedTest;
    +import org.junit.jupiter.params.provider.Arguments;
     import org.junit.jupiter.params.provider.CsvSource;
    +import org.junit.jupiter.params.provider.MethodSource;
     import org.mockito.ArgumentCaptor;
     import org.xwiki.platform.security.requiredrights.MacroRequiredRight;
     import org.xwiki.platform.security.requiredrights.MacroRequiredRightReporter;
    @@ -130,4 +133,66 @@ void analyzeWithoutParameters(String content, boolean scriptRequired)
             }
             verifyNoMoreInteractions(reporter);
         }
    +
    +    /**
    +     * Test the behavior when a parameter occurs several times but with different case. Ensure that the "most
    +     * dangerous" value wins.
    +     */
    +    @ParameterizedTest
    +    @MethodSource("analyzeWithDuplicateParametersDataProvider")
    +    void analyzeWithDuplicateParameters(Map<String, String> arguments, boolean wiki, boolean clean)
    +    {
    +        String content = "content";
    +        MacroBlock macroBlock = new MacroBlock(HTML_MACRO_ID, arguments, content, false);
    +
    +        MacroRequiredRightReporter reporter = mock();
    +        this.htmlMacroRequiredRightsAnalyzer.analyze(macroBlock, reporter);
    +
    +        if (wiki) {
    +            verify(reporter).analyzeContent(macroBlock, content);
    +        }
    +
    +        if (!clean || wiki) {
    +            ArgumentCaptor<List<MacroRequiredRight>> argumentCaptor = ArgumentCaptor.captor();
    +            verify(reporter).report(eq(macroBlock), argumentCaptor.capture(), anyString());
    +            List<MacroRequiredRight> requiredRights = argumentCaptor.getValue();
    +            if (wiki && clean) {
    +                assertEquals(List.of(MacroRequiredRight.MAYBE_SCRIPT), requiredRights);
    +            } else {
    +                assertEquals(List.of(MacroRequiredRight.SCRIPT), requiredRights);
    +            }
    +        } else {
    +            verifyNoMoreInteractions(reporter);
    +        }
    +    }
    +
    +    private static Stream<Arguments> analyzeWithDuplicateParametersDataProvider()
    +    {
    +        return Stream.of(
    +            Arguments.of(Map.of(
    +                "wiki", Boolean.TRUE.toString(),
    +                "WIKI", Boolean.FALSE.toString(),
    +                "clean", Boolean.TRUE.toString(),
    +                "CLEAN", Boolean.FALSE.toString()
    +            ), true, false),
    +            Arguments.of(Map.of(
    +                "wiki", Boolean.FALSE.toString(),
    +                "WIKI", Boolean.TRUE.toString(),
    +                "clean", Boolean.FALSE.toString(),
    +                "CLEAN", Boolean.TRUE.toString()
    +            ), true, false),
    +            Arguments.of(Map.of(
    +                "wiKi", Boolean.TRUE.toString(),
    +                "clEan", Boolean.FALSE.toString()
    +            ), true, false),
    +            Arguments.of(Map.of(
    +                "wikI", Boolean.FALSE.toString(),
    +                "Wiki", Boolean.FALSE.toString(),
    +                "clean", Boolean.TRUE.toString()
    +            ), false, true),
    +            Arguments.of(Map.of(
    +                "cleaN", Boolean.FALSE.toString()
    +            ), false, false)
    +        );
    +    }
     }
    
  • xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-xwiki/src/test/java/org/xwiki/rendering/internal/macro/RawMacroRequiredRightsAnalyzerTest.java+28 8 modified
    @@ -20,8 +20,10 @@
     package org.xwiki.rendering.internal.macro;
     
     import java.util.List;
    +import java.util.Map;
     import java.util.stream.Stream;
     
    +import org.junit.jupiter.api.BeforeEach;
     import org.junit.jupiter.params.ParameterizedTest;
     import org.junit.jupiter.params.provider.Arguments;
     import org.junit.jupiter.params.provider.MethodSource;
    @@ -35,6 +37,7 @@
     import org.xwiki.test.junit5.mockito.InjectMockComponents;
     import org.xwiki.test.junit5.mockito.MockComponent;
     
    +import static org.mockito.ArgumentMatchers.anyString;
     import static org.mockito.Mockito.mock;
     import static org.mockito.Mockito.verify;
     import static org.mockito.Mockito.verifyNoInteractions;
    @@ -50,33 +53,50 @@ class RawMacroRequiredRightsAnalyzerTest
     {
         private static final String SYNTAX_PARAMETER = "syntax";
     
    +    private static final String PLAIN_VALUE = "plain";
    +
    +    private static final String HTML_VALUE = "html";
    +
         @MockComponent
         private SyntaxRegistry syntaxRegistry;
     
         @InjectMockComponents
         private RawMacroRequiredRightsAnalyzer analyzer;
     
    +    @BeforeEach
    +    void setUp() throws ParseException
    +    {
    +        when(this.syntaxRegistry.resolveSyntax(anyString())).then(invocationOnMock -> {
    +            String syntaxValue = invocationOnMock.getArgument(0);
    +            return switch (syntaxValue) {
    +                case PLAIN_VALUE -> Syntax.PLAIN_1_0;
    +                case HTML_VALUE -> Syntax.HTML_5_0;
    +                default -> throw new ParseException("Unknown syntax: " + syntaxValue);
    +            };
    +        });
    +    }
    +
         private static Stream<Arguments> analyzeTestCases()
         {
             return Stream.of(
    -            Arguments.of("plain", Syntax.PLAIN_1_0, null),
    -            Arguments.of("html", Syntax.HTML_5_0, MacroRequiredRight.SCRIPT)
    +            Arguments.of(Map.of(SYNTAX_PARAMETER, PLAIN_VALUE), null),
    +            Arguments.of(Map.of(SYNTAX_PARAMETER, HTML_VALUE), MacroRequiredRight.SCRIPT),
    +            Arguments.of(Map.of("sYnTaX", HTML_VALUE), MacroRequiredRight.SCRIPT),
    +            Arguments.of(Map.of("sYntax", HTML_VALUE, SYNTAX_PARAMETER, PLAIN_VALUE), MacroRequiredRight.SCRIPT),
    +            Arguments.of(Map.of("Syntax", PLAIN_VALUE, "SYNTAX", HTML_VALUE), MacroRequiredRight.SCRIPT),
    +            Arguments.of(Map.of("syntaX", PLAIN_VALUE), null)
             );
         }
     
         @ParameterizedTest
         @MethodSource("analyzeTestCases")
    -    void analyze(String syntaxValue, Syntax expectedSyntax, MacroRequiredRight expectedRight) throws ParseException
    +    void analyze(Map<String, String> parameters, MacroRequiredRight expectedRight)
         {
    -        when(this.syntaxRegistry.resolveSyntax(syntaxValue)).thenReturn(expectedSyntax);
    -
    -        MacroBlock macroBlock = mock();
    -        when(macroBlock.getParameter(SYNTAX_PARAMETER)).thenReturn(syntaxValue);
    +        MacroBlock macroBlock = new MacroBlock("raw", parameters, false);
     
             MacroRequiredRightReporter reporter = mock();
             this.analyzer.analyze(macroBlock, reporter);
     
    -        verify(this.syntaxRegistry).resolveSyntax(syntaxValue);
             if (expectedRight != null) {
                 verify(reporter).report(macroBlock, List.of(expectedRight), "rendering.macro.rawMacroRequiredRights");
             } else {
    
0a705e8e253c

XWIKI-22759: The content macro is missing a required rights analyzer

https://github.com/xwiki/xwiki-platformMichael HamannJan 20, 2025via ghsa
4 files changed · +295 0
  • xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-xwiki/src/main/java/org/xwiki/rendering/internal/macro/ContentMacroRequiredRightsAnalyzer.java+105 0 added
    @@ -0,0 +1,105 @@
    +/*
    + * 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.internal.macro;
    +
    +import java.util.List;
    +import java.util.Objects;
    +
    +import javax.inject.Inject;
    +import javax.inject.Named;
    +import javax.inject.Singleton;
    +
    +import org.xwiki.component.annotation.Component;
    +import org.xwiki.platform.security.requiredrights.MacroRequiredRight;
    +import org.xwiki.platform.security.requiredrights.MacroRequiredRightReporter;
    +import org.xwiki.platform.security.requiredrights.MacroRequiredRightsAnalyzer;
    +import org.xwiki.properties.ConverterManager;
    +import org.xwiki.rendering.block.MacroBlock;
    +import org.xwiki.rendering.macro.source.MacroContentSourceReference;
    +import org.xwiki.rendering.syntax.Syntax;
    +
    +/**
    + * Required rights analyzer for content macro.
    + *
    + * @version $Id$
    + * @since 16.4.7
    + * @since 16.10.3
    + * @since 17.0.0
    + */
    +@Component
    +@Singleton
    +@Named("content")
    +public class ContentMacroRequiredRightsAnalyzer implements MacroRequiredRightsAnalyzer
    +{
    +    @Inject
    +    private ConverterManager converterManager;
    +
    +    @Override
    +    public void analyze(MacroBlock macroBlock, MacroRequiredRightReporter reporter)
    +    {
    +        List<Syntax> contentSyntaxes = getParameterValues(macroBlock, Syntax.class, "syntax");
    +        List<MacroContentSourceReference> sources = getParameterValues(macroBlock, MacroContentSourceReference.class,
    +            "source");
    +
    +        if (!sources.isEmpty()) {
    +            // If there are several sources, we don't know which one will win - just analyze all, having more than
    +            // one source isn't a real use case.
    +            for (MacroContentSourceReference source : sources) {
    +                if (MacroContentSourceReference.TYPE_SCRIPT.equals(source.getType())) {
    +                    reporter.report(macroBlock, List.of(MacroRequiredRight.SCRIPT, MacroRequiredRight.MAYBE_PROGRAM),
    +                        "rendering.macro.content.requiredRights.scriptSource");
    +                } else if (MacroContentSourceReference.TYPE_STRING.equals(source.getType())) {
    +                    analyzeContentWithSyntaxes(macroBlock, reporter, contentSyntaxes, source.getReference());
    +                }
    +            }
    +        } else {
    +            analyzeContentWithSyntaxes(macroBlock, reporter, contentSyntaxes, macroBlock.getContent());
    +        }
    +    }
    +
    +    private static void analyzeContentWithSyntaxes(MacroBlock macroBlock, MacroRequiredRightReporter reporter,
    +        List<Syntax> contentSyntaxes, String content)
    +    {
    +        if (contentSyntaxes.isEmpty()) {
    +            reporter.analyzeContent(macroBlock, content);
    +        } else {
    +            // If there are several syntax parameters, we don't know which one will really be used, so analyze
    +            // with all to catch dangerous content in all syntaxes. In practice, there should be at most a single syntax
    +            // parameter.
    +            contentSyntaxes.forEach(syntax -> reporter.analyzeContent(macroBlock, content, syntax));
    +        }
    +    }
    +
    +    private <T> List<T> getParameterValues(MacroBlock macroBlock, Class<T> tClass, String parameterName)
    +    {
    +        return macroBlock.getParameters().entrySet().stream()
    +            .filter(entry -> parameterName.equalsIgnoreCase(entry.getKey()))
    +            .map(entry -> {
    +                try {
    +                    return this.converterManager.<T>convert(tClass, entry.getValue());
    +                } catch (Exception e) {
    +                    // Ignore invalid values.
    +                    return null;
    +                }
    +            })
    +            .filter(Objects::nonNull)
    +            .toList();
    +    }
    +}
    
  • xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-xwiki/src/main/resources/ApplicationResources.properties+4 0 modified
    @@ -51,3 +51,7 @@ rendering.macro.htmlRequiredRights.wikiContent=An HTML macro with wiki content c
       on the content, please review the content carefully.
     rendering.macro.htmlRequiredRights.dangerousContent=An HTML macro that contains content that would be removed by \
       cleaning in restricted mode requires script right to avoid restricted cleaning.
    +
    +rendering.macro.content.requiredRights.scriptSource=Referencing a script variable in the source parameter of the \
    +  content macro requires script right. Additionally, the script variable could contain arbitrary wiki syntax that \
    +  could require any right including programming right.
    \ No newline at end of file
    
  • xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-xwiki/src/main/resources/META-INF/components.txt+1 0 modified
    @@ -5,6 +5,7 @@
     500:org.xwiki.rendering.internal.util.XWikiErrorBlockGenerator
     500:org.xwiki.rendering.internal.wiki.XWikiWikiModel
     500:org.xwiki.rendering.internal.macro.XWikiHTMLRawBlockFilter
    +org.xwiki.rendering.internal.macro.ContentMacroRequiredRightsAnalyzer
     org.xwiki.rendering.internal.macro.HTMLMacroRequiredRightsAnalyzer
     org.xwiki.rendering.internal.macro.RawMacroRequiredRightsAnalyzer
     org.xwiki.rendering.internal.resolver.AttachmentResourceReferenceEntityReferenceResolver
    
  • xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-xwiki/src/test/java/org/xwiki/rendering/internal/macro/ContentMacroRequiredRightsAnalyzerTest.java+185 0 added
    @@ -0,0 +1,185 @@
    +/*
    + * 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.internal.macro;
    +
    +import java.util.List;
    +import java.util.Map;
    +import java.util.stream.Stream;
    +
    +import javax.inject.Inject;
    +import javax.inject.Named;
    +import javax.inject.Provider;
    +
    +import org.junit.jupiter.api.BeforeEach;
    +import org.junit.jupiter.api.Test;
    +import org.junit.jupiter.params.ParameterizedTest;
    +import org.junit.jupiter.params.provider.Arguments;
    +import org.junit.jupiter.params.provider.MethodSource;
    +import org.xwiki.component.manager.ComponentManager;
    +import org.xwiki.platform.security.requiredrights.MacroRequiredRight;
    +import org.xwiki.platform.security.requiredrights.MacroRequiredRightReporter;
    +import org.xwiki.properties.internal.DefaultConverterManager;
    +import org.xwiki.properties.internal.converter.ConvertUtilsConverter;
    +import org.xwiki.properties.internal.converter.EnumConverter;
    +import org.xwiki.rendering.block.MacroBlock;
    +import org.xwiki.rendering.internal.macro.source.MacroContentSourceReferenceConverter;
    +import org.xwiki.rendering.internal.syntax.DefaultSyntaxRegistry;
    +import org.xwiki.rendering.internal.syntax.SyntaxConverter;
    +import org.xwiki.rendering.syntax.Syntax;
    +import org.xwiki.rendering.syntax.SyntaxRegistry;
    +import org.xwiki.test.annotation.ComponentList;
    +import org.xwiki.test.junit5.mockito.ComponentTest;
    +import org.xwiki.test.junit5.mockito.InjectComponentManager;
    +import org.xwiki.test.junit5.mockito.InjectMockComponents;
    +import org.xwiki.test.junit5.mockito.MockComponent;
    +import org.xwiki.test.mockito.MockitoComponentManager;
    +
    +import static org.mockito.Mockito.mock;
    +import static org.mockito.Mockito.times;
    +import static org.mockito.Mockito.verify;
    +import static org.mockito.Mockito.verifyNoMoreInteractions;
    +import static org.mockito.Mockito.when;
    +
    +@ComponentTest
    +@ComponentList({ DefaultConverterManager.class, EnumConverter.class, ConvertUtilsConverter.class,
    +    SyntaxConverter.class, MacroContentSourceReferenceConverter.class, DefaultSyntaxRegistry.class })
    +// Adding constants for string literals wouldn't really help the readability of the test.
    +@SuppressWarnings("checkstyle:MultipleStringLiterals")
    +class ContentMacroRequiredRightsAnalyzerTest
    +{
    +    protected static final String XWIKI_2_0 = "xwiki/2.0";
    +
    +    protected static final String XWIKI_2_1 = "xwiki/2.1";
    +
    +    protected static final String INVALID = "invalid";
    +
    +    @InjectMockComponents
    +    private ContentMacroRequiredRightsAnalyzer analyzer;
    +
    +    @InjectComponentManager
    +    private MockitoComponentManager componentManager;
    +
    +    @Inject
    +    private SyntaxRegistry syntaxRegistry;
    +
    +    @MockComponent
    +    @Named("context")
    +    private Provider<ComponentManager> contextComponentManager;
    +
    +    @BeforeEach
    +    void beforeEach()
    +    {
    +        when(this.contextComponentManager.get()).thenReturn(this.componentManager);
    +        this.syntaxRegistry.registerSyntaxes(Syntax.XWIKI_2_0, Syntax.XWIKI_2_1);
    +    }
    +
    +    @ParameterizedTest
    +    @MethodSource("provideDifferentContentSyntaxes")
    +    void analyzeDifferentContentSyntaxes(Map<String, String> parameters, List<Syntax> syntaxes)
    +    {
    +        String content = "Content";
    +        MacroBlock macroBlock = new MacroBlock("content", parameters, content, false);
    +
    +        MacroRequiredRightReporter reporter = mock();
    +
    +        this.analyzer.analyze(macroBlock, reporter);
    +
    +        // Verify that the content is analyzed
    +        if (syntaxes.isEmpty()) {
    +            verify(reporter).analyzeContent(macroBlock, content);
    +        } else {
    +            for (Syntax syntax : syntaxes) {
    +                verify(reporter).analyzeContent(macroBlock, content, syntax);
    +            }
    +        }
    +    }
    +
    +    @Test
    +    void analyzeWithScriptSource()
    +    {
    +        String content = "Content";
    +        MacroBlock macroBlock = new MacroBlock("content", Map.of("source", "script:variable"), content, false);
    +
    +        MacroRequiredRightReporter reporter = mock();
    +
    +        this.analyzer.analyze(macroBlock, reporter);
    +
    +        verify(reporter).report(macroBlock, List.of(MacroRequiredRight.SCRIPT, MacroRequiredRight.MAYBE_PROGRAM),
    +            "rendering.macro.content.requiredRights.scriptSource");
    +    }
    +
    +    @Test
    +    void analyzeWithStringSource()
    +    {
    +        String stringContent1 = "Content1";
    +        String stringContent2 = "Content2";
    +        MacroBlock macroBlock = new MacroBlock("content",
    +            Map.of("source", "string:" + stringContent1, "Source", "string:" + stringContent2,
    +                "SyntaX", XWIKI_2_0, "synTaX", XWIKI_2_1),
    +            "Content", false);
    +
    +        MacroRequiredRightReporter reporter = mock();
    +
    +        this.analyzer.analyze(macroBlock, reporter);
    +
    +        for (Syntax syntax : List.of(Syntax.XWIKI_2_1, Syntax.XWIKI_2_0)) {
    +            for (String content : List.of(stringContent1, stringContent2)) {
    +                verify(reporter).analyzeContent(macroBlock, content, syntax);
    +            }
    +        }
    +
    +        verifyNoMoreInteractions(reporter);
    +    }
    +
    +    @Test
    +    void analyzeWithDifferentSourcesNoSyntax()
    +    {
    +        MacroBlock macroBlock = new MacroBlock("content", Map.of(
    +            "Source", "script:variable", "sourcE", "script:variable2",
    +            "souRCE", "string:1", "SOURCE", "string:2",
    +            "soUrce", "invalid:value"),
    +            false);
    +
    +        MacroRequiredRightReporter reporter = mock();
    +
    +        this.analyzer.analyze(macroBlock, reporter);
    +
    +        verify(reporter, times(2))
    +            .report(macroBlock, List.of(MacroRequiredRight.SCRIPT, MacroRequiredRight.MAYBE_PROGRAM),
    +                "rendering.macro.content.requiredRights.scriptSource");
    +
    +        verify(reporter).analyzeContent(macroBlock, "1");
    +        verify(reporter).analyzeContent(macroBlock, "2");
    +        verifyNoMoreInteractions(reporter);
    +    }
    +
    +    private static Stream<Arguments> provideDifferentContentSyntaxes()
    +    {
    +        return Stream.of(
    +            Arguments.of(Map.of("syntax", XWIKI_2_0), List.of(Syntax.XWIKI_2_0)),
    +            Arguments.of(Map.of("Syntax", XWIKI_2_1), List.of(Syntax.XWIKI_2_1)),
    +            Arguments.of(Map.of("sYnTax", INVALID, "syntaX", XWIKI_2_1), List.of(Syntax.XWIKI_2_1)),
    +            Arguments.of(Map.of("syNtax", XWIKI_2_0, "synTax", XWIKI_2_1), List.of(Syntax.XWIKI_2_0,
    +                Syntax.XWIKI_2_1)),
    +            Arguments.of(Map.of("syntAX", INVALID), List.of()),
    +            Arguments.of(Map.of(), List.of())
    +        );
    +    }
    +}
    
abdcefc0db27

XWIKI-22763: Analyze required rights for wiki type macro parameters

https://github.com/xwiki/xwiki-platformMichael HamannJan 15, 2025via ghsa
2 files changed · +120 8
  • xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-requiredrights/xwiki-platform-security-requiredrights-default/src/main/java/org/xwiki/platform/security/requiredrights/internal/analyzer/DefaultMacroBlockRequiredRightAnalyzer.java+41 8 modified
    @@ -19,7 +19,9 @@
      */
     package org.xwiki.platform.security.requiredrights.internal.analyzer;
     
    +import java.util.ArrayList;
     import java.util.List;
    +import java.util.Map;
     import java.util.Optional;
     
     import javax.inject.Inject;
    @@ -41,6 +43,7 @@
     import org.xwiki.rendering.block.MacroBlock;
     import org.xwiki.rendering.macro.Macro;
     import org.xwiki.rendering.macro.descriptor.ContentDescriptor;
    +import org.xwiki.rendering.macro.descriptor.ParameterDescriptor;
     import org.xwiki.rendering.macro.script.ScriptMacro;
     
     /**
    @@ -106,8 +109,9 @@ private List<RequiredRightAnalysisResult> analyzeWithExceptions(MacroBlock macro
     
                     if (macro instanceof ScriptMacro) {
                         result = this.scriptMacroAnalyzer.analyze(macroBlock);
    -                } else if (macro != null && this.shouldMacroContentBeParsed(macro)) {
    -                    result = analyzeMacroContent(macroBlock, macroBlock.getContent());
    +                } else if (macro != null) {
    +                    result = new ArrayList<>(maybeAnalyzeMacroContent(macroBlock, macro));
    +                    result.addAll(maybeAnalyzeParameters(macroBlock, macro));
                     } else {
                         result = List.of();
                     }
    @@ -117,6 +121,41 @@ private List<RequiredRightAnalysisResult> analyzeWithExceptions(MacroBlock macro
             return result;
         }
     
    +    private List<RequiredRightAnalysisResult> maybeAnalyzeParameters(MacroBlock macroBlock, Macro<?> macro)
    +        throws RequiredRightsException
    +    {
    +        List<RequiredRightAnalysisResult> results = new ArrayList<>();
    +
    +        Map<String, ParameterDescriptor> parameterDescriptorMap = macro.getDescriptor().getParameterDescriptorMap();
    +
    +        for (Map.Entry<String, String> parameter : macroBlock.getParameters().entrySet()) {
    +            ParameterDescriptor parameterDescriptor = parameterDescriptorMap.get(parameter.getKey().toLowerCase());
    +
    +            if (parameterDescriptor != null && parameter.getValue() != null
    +                // Analyze the content if either the display type or the parameter type is wiki syntax as both are a
    +                // strong indication that the content is parsed and executed.
    +                && (Block.LIST_BLOCK_TYPE.equals(parameterDescriptor.getParameterType())
    +                || Block.LIST_BLOCK_TYPE.equals(parameterDescriptor.getDisplayType())))
    +            {
    +                results.addAll(analyzeMacroContent(macroBlock, parameter.getValue()));
    +            }
    +        }
    +
    +        return results;
    +    }
    +
    +    private List<RequiredRightAnalysisResult> maybeAnalyzeMacroContent(MacroBlock macroBlock, Macro<?> macro)
    +        throws RequiredRightsException
    +    {
    +        ContentDescriptor contentDescriptor = macro.getDescriptor().getContentDescriptor();
    +
    +        if (contentDescriptor != null && Block.LIST_BLOCK_TYPE.equals(contentDescriptor.getType())) {
    +            return analyzeMacroContent(macroBlock, macroBlock.getContent());
    +        }
    +
    +        return List.of();
    +    }
    +
         private Optional<RequiredRightAnalyzer<MacroBlock>> getMacroBlockRequiredRightAnalyzer(MacroBlock macroBlock)
         {
             String macroId = macroBlock.getId();
    @@ -149,10 +188,4 @@ private Optional<MacroRequiredRightsAnalyzer> getMacroAnalyzer(String id)
                 return Optional.empty();
             }
         }
    -
    -    private boolean shouldMacroContentBeParsed(Macro<?> macro)
    -    {
    -        ContentDescriptor contentDescriptor = macro.getDescriptor().getContentDescriptor();
    -        return contentDescriptor != null && Block.LIST_BLOCK_TYPE.equals(contentDescriptor.getType());
    -    }
     }
    
  • xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-requiredrights/xwiki-platform-security-requiredrights-default/src/test/java/org/xwiki/platform/security/requiredrights/internal/analyzer/DefaultMacroBlockRequiredRightAnalyzerTest.java+79 0 modified
    @@ -19,6 +19,8 @@
      */
     package org.xwiki.platform.security.requiredrights.internal.analyzer;
     
    +import java.lang.reflect.Type;
    +import java.util.LinkedHashMap;
     import java.util.List;
     import java.util.Map;
     import java.util.Optional;
    @@ -43,7 +45,9 @@
     import org.xwiki.rendering.macro.MacroId;
     import org.xwiki.rendering.macro.MacroManager;
     import org.xwiki.rendering.macro.descriptor.ContentDescriptor;
    +import org.xwiki.rendering.macro.descriptor.DefaultContentDescriptor;
     import org.xwiki.rendering.macro.descriptor.MacroDescriptor;
    +import org.xwiki.rendering.macro.descriptor.ParameterDescriptor;
     import org.xwiki.rendering.macro.script.ScriptMacro;
     import org.xwiki.rendering.syntax.Syntax;
     import org.xwiki.test.annotation.ComponentList;
    @@ -55,12 +59,15 @@
     
     import static org.junit.jupiter.api.Assertions.assertEquals;
     import static org.mockito.ArgumentMatchers.any;
    +import static org.mockito.ArgumentMatchers.anyBoolean;
    +import static org.mockito.ArgumentMatchers.argThat;
     import static org.mockito.ArgumentMatchers.eq;
     import static org.mockito.ArgumentMatchers.isNull;
     import static org.mockito.ArgumentMatchers.same;
     import static org.mockito.Mockito.doAnswer;
     import static org.mockito.Mockito.doReturn;
     import static org.mockito.Mockito.mock;
    +import static org.mockito.Mockito.mockingDetails;
     import static org.mockito.Mockito.verify;
     import static org.mockito.Mockito.verifyNoInteractions;
     import static org.mockito.Mockito.when;
    @@ -212,4 +219,76 @@ void analyzeDefaultMacro()
             RequiredRightAnalysisResult result = results.get(0);
             assertEquals(List.of(RequiredRight.PROGRAM), result.getRequiredRights());
         }
    +
    +    @Test
    +    void analyzeWikiParameters() throws Exception
    +    {
    +        String macroName = "wikiParameter";
    +
    +        String wikiValue = "wikiValue";
    +        String wikiValue2 = "WikiValue";
    +        String wikiDisplay = "WikiDisplay";
    +        List<String> wikiValues = List.of(wikiValue, wikiValue2, wikiDisplay);
    +
    +        // Use a LinkedHashMap to ensure that the order of the parameters is preserved.
    +        Map<String, String> parameters = new LinkedHashMap<>();
    +        parameters.put("wiki", wikiValue);
    +        parameters.put("Wiki", wikiValue2);
    +        parameters.put("wikiDisplaY", wikiDisplay);
    +        parameters.put("string", "String");
    +        parameters.put("other", "other value");
    +        MacroBlock block = new MacroBlock(macroName, parameters, false);
    +
    +        // Create a fake syntax to create the macro id.
    +        Syntax syntax = mock();
    +        MacroId macroId = new MacroId(macroName, syntax);
    +
    +        // Mock the macro.
    +        Macro<?> macro = mock();
    +        doReturn(macro).when(this.macroManager).getMacro(argThat(macroIdArg -> macroName.equals(macroIdArg.getId())));
    +
    +        // Mock the macro descriptor.
    +        MacroDescriptor macroDescriptor = mock();
    +        when(macro.getDescriptor()).thenReturn(macroDescriptor);
    +        when(macroDescriptor.getId()).thenReturn(macroId);
    +        Map<String, ParameterDescriptor> parameterDescriptorMap = Map.of(
    +            "wiki", getParameterDescriptor("wiki", Block.LIST_BLOCK_TYPE, String.class),
    +            "wikidisplay", getParameterDescriptor("wikiDisplay", String.class, Block.LIST_BLOCK_TYPE),
    +            "string", getParameterDescriptor("string", String.class, String.class));
    +        when(macroDescriptor.getParameterDescriptorMap()).thenReturn(parameterDescriptorMap);
    +        when(macroDescriptor.getContentDescriptor()).thenReturn(new DefaultContentDescriptor(false));
    +
    +        // Stub the macro content parser and the XDOM analyzer to simply pass on the analyzed content as mock name.
    +        // That way, we can easily verify that all parameters were analyzed.
    +        when(this.macroContentParser.parse(any(), any(), any(), anyBoolean(), any(), anyBoolean()))
    +            .then(invocationOnMock -> {
    +                String content = invocationOnMock.getArgument(0);
    +                return mock(XDOM.class, content);
    +            });
    +
    +        when(this.xdomRequiredRightAnalyzer.analyze(any())).then(invocationOnMock -> {
    +            XDOM xdom = invocationOnMock.getArgument(0);
    +            return List.of(mock(RequiredRightAnalysisResult.class, getMockName(xdom)));
    +        });
    +
    +        List<RequiredRightAnalysisResult> results = this.analyzer.analyze(block);
    +
    +        List<String> analyzedValues = results.stream().map(this::getMockName).toList();
    +
    +        assertEquals(wikiValues, analyzedValues);
    +    }
    +
    +    private static ParameterDescriptor getParameterDescriptor(String id, Type parameterType, Type displayType)
    +    {
    +        ParameterDescriptor wikiParameterDescriptor = mock();
    +        when(wikiParameterDescriptor.getParameterType()).thenReturn(parameterType);
    +        when(wikiParameterDescriptor.getDisplayType()).thenReturn(displayType);
    +        when(wikiParameterDescriptor.getId()).thenReturn(id);
    +        return wikiParameterDescriptor;
    +    }
    +
    +    private String getMockName(Object mockObject)
    +    {
    +        return mockingDetails(mockObject).getMockCreationSettings().getMockName().toString();
    +    }
     }
    
cc74dc802efe

XWIKI-22758: Analyze required rights of the context macro's source parameter

https://github.com/xwiki/xwiki-platformMichael HamannJan 10, 2025via ghsa
3 files changed · +141 2
  • xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/src/main/java/org/xwiki/rendering/internal/macro/context/ContextMacroRequiredRightsAnalyzer.java+33 2 modified
    @@ -19,17 +19,21 @@
      */
     package org.xwiki.rendering.internal.macro.context;
     
    +import java.util.List;
    +
     import javax.inject.Inject;
     import javax.inject.Named;
     import javax.inject.Singleton;
     
     import org.xwiki.component.annotation.Component;
    +import org.xwiki.platform.security.requiredrights.MacroRequiredRight;
     import org.xwiki.platform.security.requiredrights.MacroRequiredRightReporter;
     import org.xwiki.platform.security.requiredrights.MacroRequiredRightsAnalyzer;
     import org.xwiki.properties.BeanManager;
     import org.xwiki.properties.PropertyException;
     import org.xwiki.rendering.block.MacroBlock;
     import org.xwiki.rendering.macro.context.ContextMacroParameters;
    +import org.xwiki.rendering.macro.source.MacroContentSourceReference;
     
     /**
      * Required rights analyzer for the context macro.
    @@ -53,8 +57,35 @@ public void analyze(MacroBlock macroBlock, MacroRequiredRightReporter reporter)
             try {
                 this.beanManager.populate(parameters, macroBlock.getParameters());
     
    -            // Analyze the content only when it isn't restricted.
    -            if (!parameters.isRestricted()) {
    +            // If the source parameter is set, the content is ignored, and we should analyze the source parameter
    +            // instead.
    +            if (parameters.getSource() != null) {
    +                String sourceType = parameters.getSource().getType();
    +
    +                switch (sourceType) {
    +                    case MacroContentSourceReference.TYPE_STRING:
    +                        if (!parameters.isRestricted()) {
    +                            reporter.analyzeContent(macroBlock, parameters.getSource().getReference());
    +                        }
    +                        break;
    +
    +                    case MacroContentSourceReference.TYPE_SCRIPT:
    +                        if (parameters.isRestricted()) {
    +                            reporter.report(macroBlock, List.of(MacroRequiredRight.SCRIPT),
    +                                "rendering.macro.context.requiredRights.restrictedScriptSource");
    +                        } else {
    +                            // We don't know the actual content, but at least script right is needed and the content
    +                            // could contain anything, so it might require programming right.
    +                            reporter.report(macroBlock,
    +                                List.of(MacroRequiredRight.SCRIPT, MacroRequiredRight.MAYBE_PROGRAM),
    +                                "rendering.macro.context.requiredRights.arbitraryScriptSource");
    +                        }
    +                        break;
    +
    +                    default:
    +                        // Do nothing.
    +                }
    +            } else if (!parameters.isRestricted()) {
                     reporter.analyzeContent(macroBlock, macroBlock.getContent());
                 }
             } catch (PropertyException e) {
    
  • xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/src/main/resources/ApplicationResources.properties+49 0 added
    @@ -0,0 +1,49 @@
    +# ---------------------------------------------------------------------------
    +# 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.
    +# ---------------------------------------------------------------------------
    +
    +###############################################################################
    +# XWiki Core localization
    +#
    +# This contains the translations of the module in the default language
    +# (generally English).
    +# 
    +# See https://dev.xwiki.org/xwiki/bin/view/Community/L10N/Conventions/ for more details about about
    +# translation key naming.
    +#
    +# Comments: it's possible to add some detail about a key to make easier to
    +#   translate it by adding a comment before it. To make sure a comment is not
    +#   assigned to the following key use at least three sharps (###) for the comment
    +#   or after it.
    +# 
    +# Deprecated keys:
    +#   * when deleting a key it should be moved to deprecated section at the end
    +#     of the file (between #@deprecatedstart and #@deprecatedend) and associated to the
    +#     first version in which it started to be deprecated
    +#   * when renaming a key, it should be moved to the same deprecated section
    +#     and a comment should be added with the following syntax:
    +#     #@deprecated new.key.name
    +#     old.key.name=Some translation
    +###############################################################################
    +
    +rendering.macro.context.requiredRights.restrictedScriptSource=Referencing a script variable in the source parameter \
    +  of the context macro requires script right.
    +rendering.macro.context.requiredRights.arbitraryScriptSource=Referencing a script variable in the source parameter \
    +  of the context macro requires script right. Additionally, the script variable could contain arbitrary wiki syntax \
    +  that could require any right including programming right.
    
  • xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-macros/xwiki-platform-rendering-macro-context/src/test/java/org/xwiki/rendering/internal/macro/context/ContextMacroRequiredRightsAnalyzerTest.java+59 0 modified
    @@ -19,12 +19,18 @@
      */
     package org.xwiki.rendering.internal.macro.context;
     
    +import java.util.List;
    +
     import org.junit.jupiter.api.Test;
    +import org.junit.jupiter.params.ParameterizedTest;
    +import org.junit.jupiter.params.provider.ValueSource;
    +import org.xwiki.platform.security.requiredrights.MacroRequiredRight;
     import org.xwiki.platform.security.requiredrights.MacroRequiredRightReporter;
     import org.xwiki.properties.BeanManager;
     import org.xwiki.properties.PropertyException;
     import org.xwiki.rendering.block.MacroBlock;
     import org.xwiki.rendering.macro.context.ContextMacroParameters;
    +import org.xwiki.rendering.macro.source.MacroContentSourceReference;
     import org.xwiki.test.junit5.mockito.ComponentTest;
     import org.xwiki.test.junit5.mockito.InjectMockComponents;
     import org.xwiki.test.junit5.mockito.MockComponent;
    @@ -35,6 +41,7 @@
     import static org.mockito.Mockito.mock;
     import static org.mockito.Mockito.verify;
     import static org.mockito.Mockito.verifyNoInteractions;
    +import static org.mockito.Mockito.verifyNoMoreInteractions;
     import static org.mockito.Mockito.when;
     
     /**
    @@ -98,4 +105,56 @@ void analyzeWhenPropertyExceptionOccurs() throws PropertyException
     
             verifyNoInteractions(reporter);
         }
    +
    +    @ParameterizedTest
    +    @ValueSource(booleans = { true, false })
    +    void analyzeWithStringSource(boolean restricted) throws PropertyException
    +    {
    +        MacroBlock macroBlock = mock(MacroBlock.class);
    +        MacroRequiredRightReporter reporter = mock(MacroRequiredRightReporter.class);
    +        String sourceContent = "reference";
    +
    +        setupMock(restricted, MacroContentSourceReference.TYPE_STRING, sourceContent);
    +
    +        this.analyzer.analyze(macroBlock, reporter);
    +
    +        if (!restricted) {
    +            verify(reporter).analyzeContent(macroBlock, sourceContent);
    +            verifyNoMoreInteractions(reporter);
    +        } else {
    +            verifyNoInteractions(reporter);
    +        }
    +    }
    +
    +    @ParameterizedTest
    +    @ValueSource(booleans = { true, false })
    +    void analyzeWithScriptSource(boolean restricted) throws PropertyException
    +    {
    +        MacroBlock macroBlock = mock(MacroBlock.class);
    +        MacroRequiredRightReporter reporter = mock(MacroRequiredRightReporter.class);
    +
    +        setupMock(restricted, MacroContentSourceReference.TYPE_SCRIPT, "script");
    +
    +        this.analyzer.analyze(macroBlock, reporter);
    +
    +        if (restricted) {
    +            verify(reporter).report(macroBlock, List.of(MacroRequiredRight.SCRIPT),
    +                "rendering.macro.context.requiredRights.restrictedScriptSource");
    +            verifyNoMoreInteractions(reporter);
    +        } else {
    +            verify(reporter).report(macroBlock, List.of(MacroRequiredRight.SCRIPT, MacroRequiredRight.MAYBE_PROGRAM),
    +                "rendering.macro.context.requiredRights.arbitraryScriptSource");
    +            verifyNoMoreInteractions(reporter);
    +        }
    +    }
    +
    +    private void setupMock(boolean restricted, String sourceType, String sourceReference) throws PropertyException
    +    {
    +        doAnswer(invocation -> {
    +            ContextMacroParameters params = invocation.getArgument(0);
    +            params.setRestricted(restricted);
    +            params.setSource(new MacroContentSourceReference(sourceType, sourceReference));
    +            return null;
    +        }).when(this.beanManager).populate(any(), any());
    +    }
     }
    

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

11

News mentions

0

No linked articles in our index yet.