VYPR
High severityNVD Advisory· Published May 28, 2021· Updated Aug 3, 2024

Script injection without script or programming rights through Gadget titles

CVE-2021-32621

Description

XWiki Platform is a generic wiki platform offering runtime services for applications built on top of it. In versions prior to 12.6.7 and 12.10.3, a user without Script or Programming right is able to execute script requiring privileges by editing gadget titles in the dashboard. The issue has been patched in XWiki 12.6.7, 12.10.3 and 13.0RC1.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.xwiki.commons:xwiki-commons-coreMaven
< 12.6.712.6.7
org.xwiki.commons:xwiki-commons-coreMaven
>= 12.10.0, < 12.10.312.10.3

Affected products

1

Patches

1
bb7068bd911f

XWIKI-17794: Properly interpret velocity in gadget titles

https://github.com/xwiki/xwiki-platformSimon UrliJan 12, 2021via ghsa
2 files changed · +79 21
  • xwiki-platform-core/xwiki-platform-dashboard/xwiki-platform-dashboard-macro/src/main/java/org/xwiki/rendering/internal/macro/dashboard/DefaultGadgetSource.java+28 10 modified
    @@ -44,17 +44,16 @@
     import org.xwiki.rendering.block.WordBlock;
     import org.xwiki.rendering.block.XDOM;
     import org.xwiki.rendering.executor.ContentExecutor;
    -import org.xwiki.rendering.executor.ContentExecutorException;
     import org.xwiki.rendering.listener.reference.ResourceReference;
     import org.xwiki.rendering.listener.reference.ResourceType;
     import org.xwiki.rendering.macro.dashboard.Gadget;
     import org.xwiki.rendering.macro.dashboard.GadgetSource;
    -import org.xwiki.rendering.parser.MissingParserException;
    -import org.xwiki.rendering.parser.ParseException;
     import org.xwiki.rendering.syntax.Syntax;
     import org.xwiki.rendering.transformation.MacroTransformationContext;
     import org.xwiki.rendering.util.ParserUtils;
     import org.xwiki.security.authorization.AuthorExecutor;
    +import org.xwiki.security.authorization.AuthorizationManager;
    +import org.xwiki.security.authorization.Right;
     import org.xwiki.velocity.VelocityEngine;
     import org.xwiki.velocity.VelocityManager;
     
    @@ -120,6 +119,9 @@ public class DefaultGadgetSource implements GadgetSource
         @Inject
         private JobProgressManager progress;
     
    +    @Inject
    +    private AuthorizationManager authorizationManager;
    +
         /**
          * Prepare the parser to parse the title and content of the gadget into blocks.
          */
    @@ -194,19 +196,23 @@ private List<Gadget> prepareGadgets(List<BaseObject> objects, Syntax sourceSynta
                     String position = xObject.getStringValue("position");
                     String id = xObject.getNumber() + "";
     
    -                // render title with velocity
    -                StringWriter writer = new StringWriter();
    -                // FIXME: the engine has an issue with $ and # as last character. To test and fix if it happens
    -                velocityEngine.evaluate(velocityContext, writer, key, title);
    -                String gadgetTitle = writer.toString();
    +                String gadgetTitle;
    +
    +                XWikiDocument ownerDocument = xObject.getOwnerDocument();
    +                if (this.authorizationManager.hasAccess(Right.SCRIPT, ownerDocument.getAuthorReference(), ownerDocument.getDocumentReference())) {
    +                    gadgetTitle =
    +                        this.evaluateVelocityTitle(velocityContext, velocityEngine, key, title, ownerDocument);
    +                } else {
    +                    gadgetTitle = title;
    +                }
     
                     // parse both the title and content in the syntax of the transformation context
                     List<Block> titleBlocks =
                         renderGadgetProperty(gadgetTitle, sourceSyntax, xObject.getDocumentReference(),
    -                        xObject.getOwnerDocument(), context);
    +                        ownerDocument, context);
                     List<Block> contentBlocks =
                         renderGadgetProperty(content, sourceSyntax, xObject.getDocumentReference(),
    -                        xObject.getOwnerDocument(), context);
    +                        ownerDocument, context);
     
                     // create a gadget will all these and add the gadget to the container of gadgets
                     Gadget gadget = new Gadget(id, titleBlocks, contentBlocks, position);
    @@ -222,6 +228,18 @@ private List<Gadget> prepareGadgets(List<BaseObject> objects, Syntax sourceSynta
             return gadgets;
         }
     
    +    private String evaluateVelocityTitle(VelocityContext velocityContext, VelocityEngine velocityEngine, String key,
    +        String title, XWikiDocument ownerDocument) throws Exception
    +    {
    +        return this.authorExecutor.call(() -> {
    +            // render title with velocity
    +            StringWriter writer = new StringWriter();
    +            // FIXME: the engine has an issue with $ and # as last character. To test and fix if it happens
    +            velocityEngine.evaluate(velocityContext, writer, key, title);
    +            return writer.toString();
    +        }, ownerDocument.getAuthorReference(), ownerDocument.getDocumentReference());
    +    }
    +
         private List<Block> renderGadgetProperty(String content, Syntax sourceSyntax, EntityReference sourceReference,
             XWikiDocument ownerDocument, MacroTransformationContext context)
             throws Exception
    
  • xwiki-platform-core/xwiki-platform-dashboard/xwiki-platform-dashboard-macro/src/test/java/org/xwiki/rendering/internal/macro/dashboard/DefaultGadgetSourceTest.java+51 11 modified
    @@ -41,6 +41,8 @@
     import org.xwiki.rendering.transformation.MacroTransformationContext;
     import org.xwiki.rendering.transformation.TransformationContext;
     import org.xwiki.security.authorization.AuthorExecutor;
    +import org.xwiki.security.authorization.AuthorizationManager;
    +import org.xwiki.security.authorization.Right;
     import org.xwiki.test.junit5.mockito.ComponentTest;
     import org.xwiki.test.junit5.mockito.InjectMockComponents;
     import org.xwiki.test.junit5.mockito.MockComponent;
    @@ -57,6 +59,7 @@
     import static org.mockito.ArgumentMatchers.any;
     import static org.mockito.ArgumentMatchers.eq;
     import static org.mockito.Mockito.mock;
    +import static org.mockito.Mockito.verify;
     import static org.mockito.Mockito.when;
     
     @ComponentTest
    @@ -72,6 +75,9 @@ class DefaultGadgetSourceTest
         @MockComponent
         private AuthorExecutor authorExecutor;
     
    +    @MockComponent
    +    private AuthorizationManager authorizationManager;
    +
         @Mock
         private DocumentReference documentReference;
     
    @@ -99,6 +105,11 @@ class DefaultGadgetSourceTest
         @Mock
         private MacroTransformationContext macroTransformationContext;
     
    +    @Mock
    +    private VelocityEngine velocityEngine;
    +
    +    private ContentExecutor<MacroTransformationContext> contentExecutor;
    +
         @BeforeEach
         void setup(MockitoComponentManager componentManager) throws Exception
         {
    @@ -123,23 +134,14 @@ void setup(MockitoComponentManager componentManager) throws Exception
             when(transformationContext.getId()).thenReturn(transformationId);
     
             VelocityManager velocityManager = componentManager.getInstance(VelocityManager.class);
    -        VelocityEngine velocityEngine = mock(VelocityEngine.class);
             when(velocityManager.getVelocityEngine()).thenReturn(velocityEngine);
    -        when(velocityEngine.evaluate(any(), any(), any(), any(String.class))).then((Answer<Void>) invocation -> {
    -            Object[] args = invocation.getArguments();
    -            StringWriter stringWriter = (StringWriter) args[1];
    -            String title = (String) args[3];
    -            stringWriter.append(title);
    -            return null;
    -        });
     
    -        AuthorExecutor authorExecutor = componentManager.getInstance(AuthorExecutor.class);
             when(authorExecutor.call(any(), eq(ownerAuthorReference), eq(ownerSourceReference))).then(invocationOnMock -> {
                 Callable callable = (Callable) invocationOnMock.getArguments()[0];
                 return callable.call();
             });
     
    -        ContentExecutor<MacroTransformationContext> contentExecutor =
    +        this.contentExecutor =
                 componentManager.getInstance(ContentExecutor.TYPE_MACRO_TRANSFORMATION);
             when(contentExecutor.execute(any(), any(), any(), any())).then((Answer<XDOM>) invocationOnMock -> {
                 String content = invocationOnMock.getArgument(0);
    @@ -162,12 +164,50 @@ void getGadgets() throws Exception
             when(gadgetObject1.getLargeStringValue("content")).thenReturn("Some content");
             when(gadgetObject1.getStringValue("position")).thenReturn("0");
             when(gadgetObject1.getNumber()).thenReturn(42);
    +        when(this.authorizationManager.hasAccess(Right.SCRIPT, ownerAuthorReference, ownerSourceReference)).thenReturn(true);
    +        when(this.velocityEngine.evaluate(any(), any(), any(), eq("Gadget 1"))).then((Answer<Void>) invocation -> {
    +            Object[] args = invocation.getArguments();
    +            StringWriter stringWriter = (StringWriter) args[1];
    +            String title = "Evaluated velocity version of gadget 1";
    +            stringWriter.append(title);
    +            return null;
    +        });
     
             List<Gadget> gadgets = this.defaultGadgetSource.getGadgets(testSource, macroTransformationContext);
             assertEquals(1, gadgets.size());
             Gadget gadget = gadgets.get(0);
    -        assertEquals("Gadget 1", gadget.getTitle().get(0).toString());
    +        assertEquals("Evaluated velocity version of gadget 1", gadget.getTitle().get(0).toString());
             assertEquals("Some content", gadget.getContent().get(0).toString());
             assertEquals("42", gadget.getId());
    +        verify(this.contentExecutor)
    +            .execute(eq("Evaluated velocity version of gadget 1"), any(), any(), any());
    +        verify(this.contentExecutor)
    +            .execute(eq("Some content"), any(), any(), any());
    +    }
    +
    +    @Test
    +    void getGadgetWithoutScriptRight() throws Exception
    +    {
    +        assertEquals(new ArrayList<>(), this.defaultGadgetSource.getGadgets(testSource, macroTransformationContext));
    +
    +        BaseObject gadgetObject1 = mock(BaseObject.class);
    +        when(xWikiDocument.getXObjects(gadgetClassReference)).thenReturn(Collections.singletonList(gadgetObject1));
    +        when(gadgetObject1.getOwnerDocument()).thenReturn(ownerDocument);
    +        when(gadgetObject1.getStringValue("title")).thenReturn("Gadget 2");
    +        when(gadgetObject1.getLargeStringValue("content")).thenReturn("Some other content");
    +        when(gadgetObject1.getStringValue("position")).thenReturn("2");
    +        when(gadgetObject1.getNumber()).thenReturn(12);
    +        when(this.authorizationManager.hasAccess(Right.SCRIPT, ownerAuthorReference, ownerSourceReference)).thenReturn(false);
    +
    +        List<Gadget> gadgets = this.defaultGadgetSource.getGadgets(testSource, macroTransformationContext);
    +        assertEquals(1, gadgets.size());
    +        Gadget gadget = gadgets.get(0);
    +        assertEquals("Gadget 2", gadget.getTitle().get(0).toString());
    +        assertEquals("Some other content", gadget.getContent().get(0).toString());
    +        assertEquals("12", gadget.getId());
    +        verify(this.contentExecutor)
    +            .execute(eq("Gadget 2"), any(), any(), any());
    +        verify(this.contentExecutor)
    +            .execute(eq("Some other content"), any(), 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

6

News mentions

0

No linked articles in our index yet.