VYPR
Critical severityNVD Advisory· Published Apr 10, 2024· Updated Aug 20, 2024

XWiki Platform remote code execution from account via custom skins support

CVE-2024-31987

Description

XWiki Platform is a generic wiki platform. Starting in version 6.4-milestone-1 and prior to versions 4.10.19, 15.5.4, and 15.10-rc-1, any user who can edit any page like their profile can create a custom skin with a template override that is executed with programming right, thus allowing remote code execution. This has been patched in XWiki 14.10.19, 15.5.4 and 15.10RC1. No known workarounds are available except for upgrading.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.xwiki.platform:xwiki-platform-oldcoreMaven
>= 6.4-milestone-1, < 14.10.1914.10.19
org.xwiki.platform:xwiki-platform-oldcoreMaven
>= 15.0-rc-1, < 15.5.415.5.4
org.xwiki.platform:xwiki-platform-oldcoreMaven
>= 15.6-rc-1, < 15.10-rc-115.10-rc-1

Affected products

1

Patches

3
626d2a5dbf95

XWIKI-21478: Improve rights check of template macros

https://github.com/xwiki/xwiki-platformpjeanjeanNov 2, 2023via ghsa
2 files changed · +115 35
  • xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/render/DefaultVelocityManager.java+15 8 modified
    @@ -47,6 +47,8 @@
     import org.xwiki.observation.event.Event;
     import org.xwiki.script.ScriptContextManager;
     import org.xwiki.security.authorization.AuthorExecutor;
    +import org.xwiki.security.authorization.AuthorizationManager;
    +import org.xwiki.security.authorization.Right;
     import org.xwiki.skin.Skin;
     import org.xwiki.skin.SkinManager;
     import org.xwiki.template.Template;
    @@ -80,7 +82,7 @@ public class DefaultVelocityManager implements VelocityManager, Initializable
         private static final String VELOCITYENGINE_CACHEKEY_NAME = "velocity.engine.key";
     
         private static final List<Event> EVENTS =
    -        Arrays.<Event>asList(new TemplateUpdatedEvent(), new TemplateDeletedEvent());
    +        Arrays.asList(new TemplateUpdatedEvent(), new TemplateDeletedEvent());
     
         /**
          * Used to access the current {@link org.xwiki.context.ExecutionContext}.
    @@ -121,6 +123,9 @@ public class DefaultVelocityManager implements VelocityManager, Initializable
         @Inject
         private AuthorExecutor authorExecutor;
     
    +    @Inject
    +    private AuthorizationManager authorizationManager;
    +
         @Inject
         private Logger logger;
     
    @@ -286,11 +291,13 @@ public VelocityEngine getVelocityEngine() throws XWikiVelocityException
                     if (velocityEngine == null) {
                         velocityEngine = this.velocityFactory.createVelocityEngine(cacheKey, null);
     
    -                    if (template != null) {
    -                        // Local macros template
    -                        // We execute it ourself to support any kind of template, Velocity only support resource
    -                        // template by default
    -                        try {
    +                    try {
    +                        if (template != null && this.authorizationManager.hasAccess(Right.SCRIPT,
    +                            template.getContent().getAuthorReference(), template.getContent().getDocumentReference()))
    +                        {
    +                            // Local macros template, applied only if their author has at least Script rights.
    +                            // We execute it ourself to support any kind of template, Velocity only support resource
    +                            // template by default
                                 final VelocityEngine finalVelocityEngine = velocityEngine;
     
                                 this.authorExecutor.call(() -> {
    @@ -300,9 +307,9 @@ public VelocityEngine getVelocityEngine() throws XWikiVelocityException
                                     return null;
                                 }, template.getContent().getAuthorReference(),
                                     template.getContent().getDocumentReference());
    -                        } catch (Exception e) {
    -                            this.logger.error("Failed to evaluate macros templates [{}]", template.getPath(), e);
                             }
    +                    } catch (Exception e) {
    +                        this.logger.error("Failed to evaluate macros templates [{}]", template.getPath(), e);
                         }
                     }
                 }
    
  • xwiki-platform-core/xwiki-platform-oldcore/src/test/java/com/xpn/xwiki/render/DefaultVelocityManagerTest.java+100 27 modified
    @@ -19,60 +19,118 @@
      */
     package com.xpn.xwiki.render;
     
    +import java.io.Writer;
    +
     import org.apache.velocity.VelocityContext;
    -import org.junit.Before;
    -import org.junit.Rule;
    -import org.junit.Test;
    -import org.xwiki.component.manager.ComponentLookupException;
    +import org.apache.velocity.context.Context;
    +import org.junit.jupiter.api.BeforeEach;
    +import org.junit.jupiter.api.Test;
    +import org.mockito.Mock;
     import org.xwiki.localization.ContextualLocalizationManager;
     import org.xwiki.logging.internal.DefaultLoggerConfiguration;
     import org.xwiki.model.reference.DocumentReference;
    +import org.xwiki.properties.ConverterManager;
     import org.xwiki.script.internal.DefaultScriptContextManager;
    +import org.xwiki.security.authorization.AuthorizationManager;
    +import org.xwiki.security.authorization.Right;
    +import org.xwiki.skin.Skin;
    +import org.xwiki.skin.SkinManager;
    +import org.xwiki.template.Template;
    +import org.xwiki.template.TemplateContent;
    +import org.xwiki.template.TemplateManager;
     import org.xwiki.test.annotation.ComponentList;
    -import org.xwiki.test.mockito.MockitoComponentMockingRule;
    -import org.xwiki.velocity.VelocityManager;
    +import org.xwiki.test.junit5.mockito.InjectMockComponents;
    +import org.xwiki.test.junit5.mockito.MockComponent;
    +import org.xwiki.velocity.VelocityEngine;
     import org.xwiki.velocity.internal.DefaultVelocityConfiguration;
    +import org.xwiki.velocity.internal.DefaultVelocityFactory;
     import org.xwiki.velocity.internal.VelocityExecutionContextInitializer;
     
     import com.xpn.xwiki.api.Document;
     import com.xpn.xwiki.doc.XWikiDocument;
    -import com.xpn.xwiki.test.MockitoOldcoreRule;
    -
    -import static org.junit.Assert.assertNotNull;
    -import static org.junit.Assert.assertNotSame;
    -import static org.junit.Assert.assertNull;
    -import static org.junit.Assert.assertSame;
    +import com.xpn.xwiki.internal.security.authorization.DefaultAuthorExecutor;
    +import com.xpn.xwiki.test.MockitoOldcore;
    +import com.xpn.xwiki.test.junit5.mockito.InjectMockitoOldcore;
    +import com.xpn.xwiki.test.junit5.mockito.OldcoreTest;
    +
    +import static org.junit.jupiter.api.Assertions.assertNotNull;
    +import static org.junit.jupiter.api.Assertions.assertNotSame;
    +import static org.junit.jupiter.api.Assertions.assertNull;
    +import static org.junit.jupiter.api.Assertions.assertSame;
    +import static org.mockito.ArgumentMatchers.any;
    +import static org.mockito.ArgumentMatchers.anyString;
    +import static org.mockito.Mockito.never;
    +import static org.mockito.Mockito.verify;
    +import static org.mockito.Mockito.when;
     
     /**
      * Validate {@link DefaultVelocityManager}.
    - * 
    + *
      * @version $Id$
      */
     @ComponentList(value = { DefaultScriptContextManager.class, XWikiScriptContextInitializer.class,
    -DefaultVelocityConfiguration.class, DefaultLoggerConfiguration.class })
    +    DefaultVelocityConfiguration.class, DefaultLoggerConfiguration.class, DefaultVelocityFactory.class,
    +    DefaultAuthorExecutor.class })
    +@OldcoreTest
     public class DefaultVelocityManagerTest
     {
    -    public MockitoComponentMockingRule<VelocityManager> mocker =
    -        new MockitoComponentMockingRule<VelocityManager>(DefaultVelocityManager.class);
    +    private static final DocumentReference TEMPLATE_DOCUMENT = new DocumentReference("xwiki", "XWiki", "TestMacros");
     
    -    @Rule
    -    public MockitoOldcoreRule oldcore = new MockitoOldcoreRule(this.mocker);
    +    private static final DocumentReference SCRIPT_USER = new DocumentReference("xwiki", "XWiki", "Script");
     
    -    @Before
    -    public void before() throws Exception
    -    {
    -        this.mocker.registerMockComponent(ContextualLocalizationManager.class);
    +    @MockComponent
    +    private ContextualLocalizationManager localizationManager;
    +
    +    @InjectMockComponents
    +    private DefaultVelocityManager velocityManager;
    +
    +    @InjectMockitoOldcore
    +    private MockitoOldcore oldcore;
    +
    +    @MockComponent
    +    private SkinManager skinManager;
    +
    +    @Mock
    +    private Skin skin;
    +
    +    @MockComponent
    +    private TemplateManager templateManager;
    +
    +    @Mock
    +    private Template template;
    +
    +    @Mock
    +    private TemplateContent templateContent;
    +
    +    @MockComponent
    +    private ConverterManager converterManager;
     
    +    @MockComponent
    +    private VelocityEngine velocityEngine;
    +
    +    @BeforeEach
    +    void before() throws Exception
    +    {
             this.oldcore.getExecutionContext().setProperty(VelocityExecutionContextInitializer.VELOCITY_CONTEXT_ID,
                 new VelocityContext());
    +
    +        when(this.skinManager.getCurrentSkin(true)).thenReturn(this.skin);
    +        when(this.templateManager.getTemplate("macros.vm")).thenReturn(this.template);
    +        when(this.template.getId()).thenReturn("testMacros");
    +        when(this.template.getContent()).thenReturn(this.templateContent);
    +        when(this.templateContent.getDocumentReference()).thenReturn(TEMPLATE_DOCUMENT);
    +        when(this.templateContent.getContent()).thenReturn("");
    +
    +        AuthorizationManager authorizationManager = this.oldcore.getMockAuthorizationManager();
    +        when(authorizationManager.hasAccess(Right.SCRIPT, SCRIPT_USER, TEMPLATE_DOCUMENT)).thenReturn(true);
         }
     
         // Tests
     
         @Test
    -    public void getVelocityContext() throws ComponentLookupException
    +    void getVelocityContext()
         {
    -        VelocityContext context = this.mocker.getComponentUnderTest().getVelocityContext();
    +        VelocityContext context = this.velocityManager.getVelocityContext();
     
             assertNull(context.get("doc"));
             assertNull(context.get("sdoc"));
    @@ -83,7 +141,7 @@ public void getVelocityContext() throws ComponentLookupException
             this.oldcore.getXWikiContext().setDoc(new XWikiDocument(docReference));
             this.oldcore.getXWikiContext().put("sdoc", new XWikiDocument(sdocReference));
     
    -        context = this.mocker.getComponentUnderTest().getVelocityContext();
    +        context = this.velocityManager.getVelocityContext();
     
             Document doc = (Document) context.get("doc");
             assertNotNull(doc);
    @@ -92,7 +150,7 @@ public void getVelocityContext() throws ComponentLookupException
             assertNotNull(sdoc);
     
             // Instances are kept the same when the documents in the context don't change
    -        context = this.mocker.getComponentUnderTest().getVelocityContext();
    +        context = this.velocityManager.getVelocityContext();
             assertSame(doc, context.get("doc"));
             assertSame(sdoc, context.get("sdoc"));
     
    @@ -102,10 +160,25 @@ public void getVelocityContext() throws ComponentLookupException
             this.oldcore.getXWikiContext().setDoc(new XWikiDocument(docReference));
             this.oldcore.getXWikiContext().put("sdoc", new XWikiDocument(sdocReference));
     
    -        context = this.mocker.getComponentUnderTest().getVelocityContext();
    +        context = this.velocityManager.getVelocityContext();
             assertNotNull(context.get("doc"));
             assertNotSame(doc, context.get("doc"));
             assertNotNull(context.get("sdoc"));
             assertNotSame(sdoc, context.get("sdoc"));
         }
    +
    +    @Test
    +    void checkMacrosInjectionWithoutScriptRights() throws Exception
    +    {
    +        VelocityEngine engine = this.velocityManager.getVelocityEngine();
    +        verify(engine, never()).evaluate(any(Context.class), any(Writer.class), anyString(), anyString());
    +    }
    +
    +    @Test
    +    void checkMacrosInjectionWithScriptRights() throws Exception
    +    {
    +        when(this.templateContent.getAuthorReference()).thenReturn(SCRIPT_USER);
    +        VelocityEngine engine = this.velocityManager.getVelocityEngine();
    +        verify(engine).evaluate(any(Context.class), any(Writer.class), anyString(), anyString());
    +    }
     }
    
da177c3c972e

XWIKI-21478: Improve rights check of template macros

https://github.com/xwiki/xwiki-platformpjeanjeanNov 2, 2023via ghsa
2 files changed · +115 35
  • xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/render/DefaultVelocityManager.java+15 8 modified
    @@ -47,6 +47,8 @@
     import org.xwiki.observation.event.Event;
     import org.xwiki.script.ScriptContextManager;
     import org.xwiki.security.authorization.AuthorExecutor;
    +import org.xwiki.security.authorization.AuthorizationManager;
    +import org.xwiki.security.authorization.Right;
     import org.xwiki.skin.Skin;
     import org.xwiki.skin.SkinManager;
     import org.xwiki.template.Template;
    @@ -80,7 +82,7 @@ public class DefaultVelocityManager implements VelocityManager, Initializable
         private static final String VELOCITYENGINE_CACHEKEY_NAME = "velocity.engine.key";
     
         private static final List<Event> EVENTS =
    -        Arrays.<Event>asList(new TemplateUpdatedEvent(), new TemplateDeletedEvent());
    +        Arrays.asList(new TemplateUpdatedEvent(), new TemplateDeletedEvent());
     
         /**
          * Used to access the current {@link org.xwiki.context.ExecutionContext}.
    @@ -121,6 +123,9 @@ public class DefaultVelocityManager implements VelocityManager, Initializable
         @Inject
         private AuthorExecutor authorExecutor;
     
    +    @Inject
    +    private AuthorizationManager authorizationManager;
    +
         @Inject
         private Logger logger;
     
    @@ -286,11 +291,13 @@ public VelocityEngine getVelocityEngine() throws XWikiVelocityException
                     if (velocityEngine == null) {
                         velocityEngine = this.velocityFactory.createVelocityEngine(cacheKey, null);
     
    -                    if (template != null) {
    -                        // Local macros template
    -                        // We execute it ourself to support any kind of template, Velocity only support resource
    -                        // template by default
    -                        try {
    +                    try {
    +                        if (template != null && this.authorizationManager.hasAccess(Right.SCRIPT,
    +                            template.getContent().getAuthorReference(), template.getContent().getDocumentReference()))
    +                        {
    +                            // Local macros template, applied only if their author has at least Script rights.
    +                            // We execute it ourself to support any kind of template, Velocity only support resource
    +                            // template by default
                                 final VelocityEngine finalVelocityEngine = velocityEngine;
     
                                 this.authorExecutor.call(() -> {
    @@ -300,9 +307,9 @@ public VelocityEngine getVelocityEngine() throws XWikiVelocityException
                                     return null;
                                 }, template.getContent().getAuthorReference(),
                                     template.getContent().getDocumentReference());
    -                        } catch (Exception e) {
    -                            this.logger.error("Failed to evaluate macros templates [{}]", template.getPath(), e);
                             }
    +                    } catch (Exception e) {
    +                        this.logger.error("Failed to evaluate macros templates [{}]", template.getPath(), e);
                         }
                     }
                 }
    
  • xwiki-platform-core/xwiki-platform-oldcore/src/test/java/com/xpn/xwiki/render/DefaultVelocityManagerTest.java+100 27 modified
    @@ -19,60 +19,118 @@
      */
     package com.xpn.xwiki.render;
     
    +import java.io.Writer;
    +
     import org.apache.velocity.VelocityContext;
    -import org.junit.Before;
    -import org.junit.Rule;
    -import org.junit.Test;
    -import org.xwiki.component.manager.ComponentLookupException;
    +import org.apache.velocity.context.Context;
    +import org.junit.jupiter.api.BeforeEach;
    +import org.junit.jupiter.api.Test;
    +import org.mockito.Mock;
     import org.xwiki.localization.ContextualLocalizationManager;
     import org.xwiki.logging.internal.DefaultLoggerConfiguration;
     import org.xwiki.model.reference.DocumentReference;
    +import org.xwiki.properties.ConverterManager;
     import org.xwiki.script.internal.DefaultScriptContextManager;
    +import org.xwiki.security.authorization.AuthorizationManager;
    +import org.xwiki.security.authorization.Right;
    +import org.xwiki.skin.Skin;
    +import org.xwiki.skin.SkinManager;
    +import org.xwiki.template.Template;
    +import org.xwiki.template.TemplateContent;
    +import org.xwiki.template.TemplateManager;
     import org.xwiki.test.annotation.ComponentList;
    -import org.xwiki.test.mockito.MockitoComponentMockingRule;
    -import org.xwiki.velocity.VelocityManager;
    +import org.xwiki.test.junit5.mockito.InjectMockComponents;
    +import org.xwiki.test.junit5.mockito.MockComponent;
    +import org.xwiki.velocity.VelocityEngine;
     import org.xwiki.velocity.internal.DefaultVelocityConfiguration;
    +import org.xwiki.velocity.internal.DefaultVelocityFactory;
     import org.xwiki.velocity.internal.VelocityExecutionContextInitializer;
     
     import com.xpn.xwiki.api.Document;
     import com.xpn.xwiki.doc.XWikiDocument;
    -import com.xpn.xwiki.test.MockitoOldcoreRule;
    -
    -import static org.junit.Assert.assertNotNull;
    -import static org.junit.Assert.assertNotSame;
    -import static org.junit.Assert.assertNull;
    -import static org.junit.Assert.assertSame;
    +import com.xpn.xwiki.internal.security.authorization.DefaultAuthorExecutor;
    +import com.xpn.xwiki.test.MockitoOldcore;
    +import com.xpn.xwiki.test.junit5.mockito.InjectMockitoOldcore;
    +import com.xpn.xwiki.test.junit5.mockito.OldcoreTest;
    +
    +import static org.junit.jupiter.api.Assertions.assertNotNull;
    +import static org.junit.jupiter.api.Assertions.assertNotSame;
    +import static org.junit.jupiter.api.Assertions.assertNull;
    +import static org.junit.jupiter.api.Assertions.assertSame;
    +import static org.mockito.ArgumentMatchers.any;
    +import static org.mockito.ArgumentMatchers.anyString;
    +import static org.mockito.Mockito.never;
    +import static org.mockito.Mockito.verify;
    +import static org.mockito.Mockito.when;
     
     /**
      * Validate {@link DefaultVelocityManager}.
    - * 
    + *
      * @version $Id$
      */
     @ComponentList(value = { DefaultScriptContextManager.class, XWikiScriptContextInitializer.class,
    -DefaultVelocityConfiguration.class, DefaultLoggerConfiguration.class })
    +    DefaultVelocityConfiguration.class, DefaultLoggerConfiguration.class, DefaultVelocityFactory.class,
    +    DefaultAuthorExecutor.class })
    +@OldcoreTest
     public class DefaultVelocityManagerTest
     {
    -    public MockitoComponentMockingRule<VelocityManager> mocker =
    -        new MockitoComponentMockingRule<VelocityManager>(DefaultVelocityManager.class);
    +    private static final DocumentReference TEMPLATE_DOCUMENT = new DocumentReference("xwiki", "XWiki", "TestMacros");
     
    -    @Rule
    -    public MockitoOldcoreRule oldcore = new MockitoOldcoreRule(this.mocker);
    +    private static final DocumentReference SCRIPT_USER = new DocumentReference("xwiki", "XWiki", "Script");
     
    -    @Before
    -    public void before() throws Exception
    -    {
    -        this.mocker.registerMockComponent(ContextualLocalizationManager.class);
    +    @MockComponent
    +    private ContextualLocalizationManager localizationManager;
    +
    +    @InjectMockComponents
    +    private DefaultVelocityManager velocityManager;
    +
    +    @InjectMockitoOldcore
    +    private MockitoOldcore oldcore;
    +
    +    @MockComponent
    +    private SkinManager skinManager;
    +
    +    @Mock
    +    private Skin skin;
    +
    +    @MockComponent
    +    private TemplateManager templateManager;
    +
    +    @Mock
    +    private Template template;
    +
    +    @Mock
    +    private TemplateContent templateContent;
    +
    +    @MockComponent
    +    private ConverterManager converterManager;
     
    +    @MockComponent
    +    private VelocityEngine velocityEngine;
    +
    +    @BeforeEach
    +    void before() throws Exception
    +    {
             this.oldcore.getExecutionContext().setProperty(VelocityExecutionContextInitializer.VELOCITY_CONTEXT_ID,
                 new VelocityContext());
    +
    +        when(this.skinManager.getCurrentSkin(true)).thenReturn(this.skin);
    +        when(this.templateManager.getTemplate("macros.vm")).thenReturn(this.template);
    +        when(this.template.getId()).thenReturn("testMacros");
    +        when(this.template.getContent()).thenReturn(this.templateContent);
    +        when(this.templateContent.getDocumentReference()).thenReturn(TEMPLATE_DOCUMENT);
    +        when(this.templateContent.getContent()).thenReturn("");
    +
    +        AuthorizationManager authorizationManager = this.oldcore.getMockAuthorizationManager();
    +        when(authorizationManager.hasAccess(Right.SCRIPT, SCRIPT_USER, TEMPLATE_DOCUMENT)).thenReturn(true);
         }
     
         // Tests
     
         @Test
    -    public void getVelocityContext() throws ComponentLookupException
    +    void getVelocityContext()
         {
    -        VelocityContext context = this.mocker.getComponentUnderTest().getVelocityContext();
    +        VelocityContext context = this.velocityManager.getVelocityContext();
     
             assertNull(context.get("doc"));
             assertNull(context.get("sdoc"));
    @@ -83,7 +141,7 @@ public void getVelocityContext() throws ComponentLookupException
             this.oldcore.getXWikiContext().setDoc(new XWikiDocument(docReference));
             this.oldcore.getXWikiContext().put("sdoc", new XWikiDocument(sdocReference));
     
    -        context = this.mocker.getComponentUnderTest().getVelocityContext();
    +        context = this.velocityManager.getVelocityContext();
     
             Document doc = (Document) context.get("doc");
             assertNotNull(doc);
    @@ -92,7 +150,7 @@ public void getVelocityContext() throws ComponentLookupException
             assertNotNull(sdoc);
     
             // Instances are kept the same when the documents in the context don't change
    -        context = this.mocker.getComponentUnderTest().getVelocityContext();
    +        context = this.velocityManager.getVelocityContext();
             assertSame(doc, context.get("doc"));
             assertSame(sdoc, context.get("sdoc"));
     
    @@ -102,10 +160,25 @@ public void getVelocityContext() throws ComponentLookupException
             this.oldcore.getXWikiContext().setDoc(new XWikiDocument(docReference));
             this.oldcore.getXWikiContext().put("sdoc", new XWikiDocument(sdocReference));
     
    -        context = this.mocker.getComponentUnderTest().getVelocityContext();
    +        context = this.velocityManager.getVelocityContext();
             assertNotNull(context.get("doc"));
             assertNotSame(doc, context.get("doc"));
             assertNotNull(context.get("sdoc"));
             assertNotSame(sdoc, context.get("sdoc"));
         }
    +
    +    @Test
    +    void checkMacrosInjectionWithoutScriptRights() throws Exception
    +    {
    +        VelocityEngine engine = this.velocityManager.getVelocityEngine();
    +        verify(engine, never()).evaluate(any(Context.class), any(Writer.class), anyString(), anyString());
    +    }
    +
    +    @Test
    +    void checkMacrosInjectionWithScriptRights() throws Exception
    +    {
    +        when(this.templateContent.getAuthorReference()).thenReturn(SCRIPT_USER);
    +        VelocityEngine engine = this.velocityManager.getVelocityEngine();
    +        verify(engine).evaluate(any(Context.class), any(Writer.class), anyString(), anyString());
    +    }
     }
    
3d4dbb41f52d

XWIKI-21478: Improve rights check of template macros

https://github.com/xwiki/xwiki-platformpjeanjeanOct 30, 2023via ghsa
2 files changed · +92 16
  • xwiki-platform-core/xwiki-platform-oldcore/src/main/java/org/xwiki/internal/velocity/XWikiVelocityManager.java+21 9 modified
    @@ -42,6 +42,9 @@
     import org.xwiki.observation.EventListener;
     import org.xwiki.observation.ObservationManager;
     import org.xwiki.observation.event.Event;
    +import org.xwiki.security.authorization.AuthorExecutor;
    +import org.xwiki.security.authorization.AuthorizationManager;
    +import org.xwiki.security.authorization.Right;
     import org.xwiki.skin.Skin;
     import org.xwiki.skin.SkinManager;
     import org.xwiki.template.Template;
    @@ -73,7 +76,7 @@ public class XWikiVelocityManager extends DefaultVelocityManager implements Init
         private static final String VELOCITYENGINE_CACHEKEY_NAME = "velocity.engine.key";
     
         private static final List<Event> EVENTS =
    -        Arrays.<Event>asList(new TemplateUpdatedEvent(), new TemplateDeletedEvent());
    +        Arrays.asList(new TemplateUpdatedEvent(), new TemplateDeletedEvent());
     
         /**
          * Used to access the current {@link XWikiContext}.
    @@ -96,10 +99,16 @@ public class XWikiVelocityManager extends DefaultVelocityManager implements Init
         @Inject
         private Environment environment;
     
    +    @Inject
    +    private AuthorExecutor authorExecutor;
    +
    +    @Inject
    +    private AuthorizationManager authorizationManager;
    +
         @Inject
         private Logger logger;
     
    -    private Map<String, VelocityEngine> velocityEngines = new ConcurrentHashMap<>();
    +    private final Map<String, VelocityEngine> velocityEngines = new ConcurrentHashMap<>();
     
         @Override
         public void initialize() throws InitializationException
    @@ -114,7 +123,7 @@ public void onEvent(Event event, Object source, Object data)
                     if (event instanceof TemplateEvent) {
                         TemplateEvent templateEvent = (TemplateEvent) event;
     
    -                    velocityEngines.remove(templateEvent.getId());
    +                    XWikiVelocityManager.this.velocityEngines.remove(templateEvent.getId());
                     }
                 }
     
    @@ -177,8 +186,8 @@ private Template getVelocityEngineMacrosTemplate()
     
         /**
          * @return the Velocity Engine corresponding to the current execution context. More specifically returns the
    -     *         Velocity Engine for the current skin since each skin has its own Velocity Engine so that each skin can
    -     *         have global velocimacros defined
    +     *     Velocity Engine for the current skin since each skin has its own Velocity Engine so that each skin can have
    +     *     global velocimacros defined
          * @throws XWikiVelocityException in case of an error while creating a Velocity Engine
          */
         @Override
    @@ -248,10 +257,13 @@ private void injectBaseMacros(VelocityEngine velocityEngine, Template skinMacros
                 }
             }
     
    -        // Inject skin macros
    -        if (skinMacrosTemplate != null) {
    -            VelocityTemplate skinMacros = compile("", new StringReader(skinMacrosTemplate.getContent().getContent()));
    -
    +        // Inject skin macros if their author has at least Script rights.
    +        if (skinMacrosTemplate != null
    +            && this.authorizationManager.hasAccess(Right.SCRIPT, skinMacrosTemplate.getContent().getAuthorReference(),
    +            skinMacrosTemplate.getContent().getDocumentReference()))
    +        {
    +            VelocityTemplate skinMacros =
    +                compile("", new StringReader(skinMacrosTemplate.getContent().getContent()));
                 velocityEngine.addGlobalMacros(skinMacros.getMacros());
             }
         }
    
  • xwiki-platform-core/xwiki-platform-oldcore/src/test/java/org/xwiki/internal/velocity/XWikiVelocityManagerTest.java+71 7 modified
    @@ -22,15 +22,24 @@
     import org.apache.velocity.VelocityContext;
     import org.junit.jupiter.api.BeforeEach;
     import org.junit.jupiter.api.Test;
    -import org.xwiki.component.manager.ComponentLookupException;
    +import org.mockito.Mock;
     import org.xwiki.internal.script.XWikiScriptContextInitializer;
     import org.xwiki.localization.ContextualLocalizationManager;
     import org.xwiki.logging.internal.DefaultLoggerConfiguration;
     import org.xwiki.model.reference.DocumentReference;
    +import org.xwiki.properties.ConverterManager;
     import org.xwiki.script.internal.DefaultScriptContextManager;
    +import org.xwiki.security.authorization.AuthorizationManager;
    +import org.xwiki.security.authorization.Right;
    +import org.xwiki.skin.Skin;
    +import org.xwiki.skin.SkinManager;
    +import org.xwiki.template.Template;
    +import org.xwiki.template.TemplateContent;
    +import org.xwiki.template.TemplateManager;
     import org.xwiki.test.annotation.ComponentList;
     import org.xwiki.test.junit5.mockito.InjectMockComponents;
     import org.xwiki.test.junit5.mockito.MockComponent;
    +import org.xwiki.velocity.VelocityEngine;
     import org.xwiki.velocity.internal.DefaultVelocityConfiguration;
     import org.xwiki.velocity.internal.VelocityExecutionContextInitializer;
     
    @@ -44,17 +53,29 @@
     import static org.junit.jupiter.api.Assertions.assertNotSame;
     import static org.junit.jupiter.api.Assertions.assertNull;
     import static org.junit.jupiter.api.Assertions.assertSame;
    +import static org.mockito.ArgumentMatchers.anyMap;
    +import static org.mockito.Mockito.never;
    +import static org.mockito.Mockito.verify;
    +import static org.mockito.Mockito.when;
     
     /**
      * Validate {@link XWikiVelocityManager}.
    - * 
    + *
      * @version $Id$
      */
    -@ComponentList(value = {DefaultScriptContextManager.class, XWikiScriptContextInitializer.class,
    -    DefaultVelocityConfiguration.class, DefaultLoggerConfiguration.class})
    +@ComponentList({
    +    DefaultScriptContextManager.class,
    +    XWikiScriptContextInitializer.class,
    +    DefaultVelocityConfiguration.class,
    +    DefaultLoggerConfiguration.class
    +})
     @OldcoreTest
     public class XWikiVelocityManagerTest
     {
    +    private static final DocumentReference TEMPLATE_DOCUMENT = new DocumentReference("xwiki", "XWiki", "TestMacros");
    +
    +    private static final DocumentReference SCRIPT_USER = new DocumentReference("xwiki", "XWiki", "Script");
    +
         @MockComponent
         private ContextualLocalizationManager localizationManager;
     
    @@ -64,19 +85,47 @@ public class XWikiVelocityManagerTest
         @InjectMockitoOldcore
         private MockitoOldcore oldcore;
     
    +    @MockComponent
    +    private SkinManager skinManager;
    +
    +    @Mock
    +    private Skin skin;
    +
    +    @MockComponent
    +    private TemplateManager templateManager;
    +
    +    @Mock
    +    private Template template;
    +
    +    @Mock
    +    private TemplateContent templateContent;
    +
    +    @MockComponent
    +    private ConverterManager converterManager;
    +
         @BeforeEach
    -    public void beforeEach() throws ComponentLookupException
    +    void beforeEach() throws Exception
         {
             this.oldcore.getExecutionContext().setProperty(VelocityExecutionContextInitializer.VELOCITY_CONTEXT_ID,
                 new VelocityContext());
    +
    +        when(this.skinManager.getCurrentSkin(true)).thenReturn(this.skin);
    +        when(this.templateManager.getTemplate("macros.vm")).thenReturn(this.template);
    +        when(this.template.getId()).thenReturn("testMacros");
    +        when(this.template.getContent()).thenReturn(this.templateContent);
    +        when(this.templateContent.getDocumentReference()).thenReturn(TEMPLATE_DOCUMENT);
    +        when(this.templateContent.getContent()).thenReturn("");
    +
    +        AuthorizationManager authorizationManager = this.oldcore.getMockAuthorizationManager();
    +        when(authorizationManager.hasAccess(Right.SCRIPT, SCRIPT_USER, TEMPLATE_DOCUMENT)).thenReturn(true);
         }
     
         // Tests
     
         @Test
    -    public void getVelocityContext()
    +    void getVelocityContext()
         {
    -        VelocityContext context = velocityManager.getVelocityContext();
    +        VelocityContext context = this.velocityManager.getVelocityContext();
     
             assertNull(context.get("doc"));
             assertNull(context.get("sdoc"));
    @@ -112,4 +161,19 @@ public void getVelocityContext()
             assertNotNull(context.get("sdoc"));
             assertNotSame(sdoc, context.get("sdoc"));
         }
    +
    +    @Test
    +    void checkMacrosInjectionWithoutScriptRights() throws Exception
    +    {
    +        VelocityEngine engine = this.velocityManager.getVelocityEngine();
    +        verify(engine, never()).addGlobalMacros(anyMap());
    +    }
    +
    +    @Test
    +    void checkMacrosInjectionWithScriptRights() throws Exception
    +    {
    +        when(this.templateContent.getAuthorReference()).thenReturn(SCRIPT_USER);
    +        VelocityEngine engine = this.velocityManager.getVelocityEngine();
    +        verify(engine).addGlobalMacros(anyMap());
    +    }
     }
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

7

News mentions

0

No linked articles in our index yet.