XWiki Platform remote code execution from account via custom skins support
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.
| Package | Affected versions | Patched versions |
|---|---|---|
org.xwiki.platform:xwiki-platform-oldcoreMaven | >= 6.4-milestone-1, < 14.10.19 | 14.10.19 |
org.xwiki.platform:xwiki-platform-oldcoreMaven | >= 15.0-rc-1, < 15.5.4 | 15.5.4 |
org.xwiki.platform:xwiki-platform-oldcoreMaven | >= 15.6-rc-1, < 15.10-rc-1 | 15.10-rc-1 |
Affected products
1- Range: >= 6.4-milestone-1, < 14.10.19
Patches
3626d2a5dbf95XWIKI-21478: Improve rights check of template macros
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()); + } }
da177c3c972eXWIKI-21478: Improve rights check of template macros
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()); + } }
3d4dbb41f52dXWIKI-21478: Improve rights check of template macros
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- github.com/advisories/GHSA-cv55-v6rw-7r5vghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-31987ghsaADVISORY
- github.com/xwiki/xwiki-platform/commit/3d4dbb41f52d1a6e39835cfb1695ca6668605a39ghsax_refsource_MISCWEB
- github.com/xwiki/xwiki-platform/commit/626d2a5dbf95b4e719ae13bf1a0a9c76e4edd5a2ghsax_refsource_MISCWEB
- github.com/xwiki/xwiki-platform/commit/da177c3c972e797d92c1a31e278f946012c41b56ghsax_refsource_MISCWEB
- github.com/xwiki/xwiki-platform/security/advisories/GHSA-cv55-v6rw-7r5vghsax_refsource_CONFIRMWEB
- jira.xwiki.org/browse/XWIKI-21478ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.