XWiki missing authorization when accessing the wiki level attachments list and metadata via REST API
Description
XWiki is a generic wiki platform. In versions starting from 1.8.1 to before 14.10.22, from 15.0-rc-1 to before 15.10.12, from 16.0.0-rc-1 to before 16.4.3, and from 16.5.0-rc-1 to before 16.7.0, anyone can access the metadata of any attachment in the wiki using the wiki attachment REST endpoint. There is no filtering for the results depending on current user rights, meaning an unauthenticated user could exploit this even in a private wiki. This issue has been patched in versions 14.10.22, 15.10.12, 16.4.3, and 16.7.0.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.xwiki.platform:xwiki-platform-rest-serverMaven | >= 1.8.1, < 14.10.22 | 14.10.22 |
org.xwiki.platform:xwiki-platform-rest-serverMaven | >= 15.0-rc-1, < 15.10.12 | 15.10.12 |
org.xwiki.platform:xwiki-platform-rest-serverMaven | >= 16.0.0-rc-1, < 16.4.3 | 16.4.3 |
org.xwiki.platform:xwiki-platform-rest-serverMaven | >= 16.5.0-rc-1, < 16.7.0 | 16.7.0 |
Affected products
1- Range: >= 1.8.1, < 14.10.22
Patches
3c02ce7843a39XWIKI-22424: Improve attachment filtering
1 file changed · +0 −5
xwiki-platform-core/xwiki-platform-rest/xwiki-platform-rest-server/src/main/java/org/xwiki/rest/internal/resources/attachments/AttachmentResourceImpl.java+0 −5 modified@@ -22,7 +22,6 @@ import java.io.ByteArrayInputStream; import java.io.InputStream; -import javax.inject.Inject; import javax.inject.Named; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; @@ -34,7 +33,6 @@ import org.xwiki.rest.internal.Utils; import org.xwiki.rest.internal.resources.BaseAttachmentsResource; import org.xwiki.rest.resources.attachments.AttachmentResource; -import org.xwiki.security.authorization.ContextualAuthorizationManager; import org.xwiki.security.authorization.Right; import com.xpn.xwiki.XWikiException; @@ -49,9 +47,6 @@ @Named("org.xwiki.rest.internal.resources.attachments.AttachmentResourceImpl") public class AttachmentResourceImpl extends BaseAttachmentsResource implements AttachmentResource { - @Inject - private ContextualAuthorizationManager authorization; - @Override public Response getAttachment(String wikiName, String spaceName, String pageName, String attachmentName) throws XWikiRestException
a43e933ddedaXWIKI-22424: Improve attachment filtering
5 files changed · +57 −7
xwiki-platform-core/xwiki-platform-rest/xwiki-platform-rest-server/src/main/java/org/xwiki/rest/internal/resources/attachments/AttachmentsResourceImpl.java+0 −4 modified@@ -49,7 +49,6 @@ import org.xwiki.rest.model.jaxb.Attachments; import org.xwiki.rest.resources.attachments.AttachmentResource; import org.xwiki.rest.resources.attachments.AttachmentsResource; -import org.xwiki.security.authorization.ContextualAuthorizationManager; import org.xwiki.security.authorization.Right; import com.xpn.xwiki.XWikiException; @@ -64,9 +63,6 @@ public class AttachmentsResourceImpl extends BaseAttachmentsResource implements { private static final String NAME = "name"; - @Inject - private ContextualAuthorizationManager authorization; - @Inject private Container container;
xwiki-platform-core/xwiki-platform-rest/xwiki-platform-rest-server/src/main/java/org/xwiki/rest/internal/resources/BaseAttachmentsResource.java+10 −1 modified@@ -56,6 +56,8 @@ import org.xwiki.rest.internal.Utils; import org.xwiki.rest.model.jaxb.Attachment; import org.xwiki.rest.model.jaxb.Attachments; +import org.xwiki.security.authorization.ContextualAuthorizationManager; +import org.xwiki.security.authorization.Right; import org.xwiki.user.UserReferenceResolver; import com.xpn.xwiki.XWiki; @@ -113,6 +115,9 @@ public boolean isAlreadyExisting() FILTER_TO_QUERY.put("author", "attachment.author"); } + @Inject + protected ContextualAuthorizationManager authorization; + @Inject private ModelFactory modelFactory; @@ -157,15 +162,19 @@ protected Attachments getAttachments(EntityReference scope, Map<String, String> List<Object> queryResults = getAttachmentsQuery(scope, filters).setLimit(limit).setOffset(offset).execute(); attachments.withAttachments(queryResults.stream().map(this::processAttachmentsQueryResult) + // Apply passed filters .filter(getFileTypeFilter(filters.getOrDefault(FILTER_FILE_TYPES, ""))) + // Filter out attachments the current user is not allowed to see + .filter(a -> authorization.hasAccess(Right.VIEW, a.getReference())) + // Convert XWikiAttachment to REST Attachment .map(xwikiAttachment -> toRestAttachment(xwikiAttachment, withPrettyNames)) .toList()); } catch (QueryException e) { throw new XWikiRestException(e); } finally { xcontext.setWikiId(database); } - + return attachments; }
xwiki-platform-core/xwiki-platform-rest/xwiki-platform-rest-server/src/test/java/org/xwiki/rest/internal/resources/attachments/AttachmentsResourceImplTest.java+3 −0 modified@@ -66,6 +66,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -157,6 +158,8 @@ void getAttachments() throws Exception new Object[] {"Path.To", "Page", "1.3", imageAttachment}); when(query.execute()).thenReturn(results); + when(this.authorization.hasAccess(same(Right.VIEW), any())).thenReturn(true); + DocumentReference documentReference = new DocumentReference("test", Arrays.asList("Path", "To"), "Page"); when(this.defaultSpaceReferenceResover.resolve(eq("Path.To"), any())) .thenReturn(documentReference.getLastSpaceReference());
xwiki-platform-core/xwiki-platform-rest/xwiki-platform-rest-server/src/test/java/org/xwiki/rest/internal/resources/spaces/SpaceAttachmentsResourceImplTest.java+21 −1 modified@@ -19,17 +19,22 @@ */ package org.xwiki.rest.internal.resources.spaces; +import java.util.Arrays; import java.util.Collections; import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.xwiki.model.reference.AttachmentReference; import org.xwiki.model.reference.SpaceReference; import org.xwiki.query.Query; import org.xwiki.rest.internal.resources.AbstractAttachmentsResourceTest; import org.xwiki.rest.model.jaxb.Attachment; import org.xwiki.rest.model.jaxb.Attachments; +import org.xwiki.security.authorization.ContextualAuthorizationManager; +import org.xwiki.security.authorization.Right; import org.xwiki.test.junit5.mockito.InjectMockComponents; +import org.xwiki.test.junit5.mockito.MockComponent; import com.xpn.xwiki.doc.XWikiAttachment; import com.xpn.xwiki.test.junit5.mockito.OldcoreTest; @@ -52,6 +57,9 @@ class SpaceAttachmentsResourceImplTest extends AbstractAttachmentsResourceTest @InjectMockComponents private SpaceAttachmentsResourceImpl spaceAttachmentsResource; + @MockComponent + private ContextualAuthorizationManager authorization; + @BeforeEach @Override public void setUp() throws Exception @@ -74,7 +82,17 @@ void getAttachments() throws Exception when(query.setLimit(5)).thenReturn(query); XWikiAttachment xwikiAttachment = mock(XWikiAttachment.class); - List<Object> results = Collections.singletonList(new Object[] {"Path.To", "Page", "1.3", xwikiAttachment}); + AttachmentReference xwikiAttachmentReference = mock(AttachmentReference.class, "image"); + when(xwikiAttachment.getReference()).thenReturn(xwikiAttachmentReference); + when(this.authorization.hasAccess(Right.VIEW, xwikiAttachmentReference)).thenReturn(true); + + XWikiAttachment forbiddenAttachment = mock(XWikiAttachment.class); + AttachmentReference forbiddenAttachmentReference = mock(AttachmentReference.class, "forbidden"); + when(forbiddenAttachment.getReference()).thenReturn(forbiddenAttachmentReference); + when(this.authorization.hasAccess(Right.VIEW, forbiddenAttachmentReference)).thenReturn(false); + + List<Object> results = Arrays.asList(new Object[] {"Path.To", "Page", "1.3", xwikiAttachment}, + new Object[] {"Path.To", "ForbiddenPage", "1.3", forbiddenAttachment}); when(query.execute()).thenReturn(results); SpaceReference spaceReference = new SpaceReference("test", "Path", "To"); @@ -89,6 +107,8 @@ void getAttachments() throws Exception this.spaceAttachmentsResource.getAttachments("test", "Path/spaces/To", "", "xyz", "", "", 10, 5, false); verify(query).bindValue("localSpaceReference", "Path.To"); + verify(this.authorization).hasAccess(Right.VIEW, xwikiAttachmentReference); + verify(this.authorization).hasAccess(Right.VIEW, forbiddenAttachmentReference); assertEquals(Collections.singletonList(attachment), attachments.getAttachments()); }
xwiki-platform-core/xwiki-platform-rest/xwiki-platform-rest-server/src/test/java/org/xwiki/rest/internal/resources/wikis/WikiAttachmentsResourceImplTest.java+23 −1 modified@@ -19,17 +19,22 @@ */ package org.xwiki.rest.internal.resources.wikis; +import java.util.Arrays; import java.util.Collections; import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.xwiki.model.reference.AttachmentReference; import org.xwiki.model.reference.SpaceReference; import org.xwiki.query.Query; import org.xwiki.rest.internal.resources.AbstractAttachmentsResourceTest; import org.xwiki.rest.model.jaxb.Attachment; import org.xwiki.rest.model.jaxb.Attachments; +import org.xwiki.security.authorization.ContextualAuthorizationManager; +import org.xwiki.security.authorization.Right; import org.xwiki.test.junit5.mockito.InjectMockComponents; +import org.xwiki.test.junit5.mockito.MockComponent; import com.xpn.xwiki.doc.XWikiAttachment; import com.xpn.xwiki.test.junit5.mockito.OldcoreTest; @@ -38,6 +43,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; /** @@ -51,6 +57,9 @@ class WikiAttachmentsResourceImplTest extends AbstractAttachmentsResourceTest @InjectMockComponents private WikiAttachmentsResourceImpl wikiAttachmentsResource; + @MockComponent + private ContextualAuthorizationManager authorization; + @BeforeEach @Override public void setUp() throws Exception @@ -71,7 +80,17 @@ void getAttachments() throws Exception when(query.setLimit(10)).thenReturn(query); XWikiAttachment xwikiAttachment = mock(XWikiAttachment.class); - List<Object> results = Collections.singletonList(new Object[] {"Path.To", "Page", "1.3", xwikiAttachment}); + AttachmentReference xwikiAttachmentReference = mock(AttachmentReference.class, "image"); + when(xwikiAttachment.getReference()).thenReturn(xwikiAttachmentReference); + when(this.authorization.hasAccess(Right.VIEW, xwikiAttachmentReference)).thenReturn(true); + + XWikiAttachment forbiddenAttachment = mock(XWikiAttachment.class); + AttachmentReference forbiddenAttachmentReference = mock(AttachmentReference.class, "forbidden"); + when(forbiddenAttachment.getReference()).thenReturn(forbiddenAttachmentReference); + when(this.authorization.hasAccess(Right.VIEW, forbiddenAttachmentReference)).thenReturn(false); + + List<Object> results = Arrays.asList(new Object[] {"Path.To", "Page", "1.3", xwikiAttachment}, + new Object[] {"Path.To", "ForbiddenPage", "1.3", forbiddenAttachment}); when(query.execute()).thenReturn(results); when(this.defaultSpaceReferenceResover.resolve(eq("Path.To"), any())) @@ -84,6 +103,9 @@ void getAttachments() throws Exception Attachments attachments = this.wikiAttachmentsResource.getAttachments("test", "", "", "abc", "", "", 0, 10, true); + verify(this.authorization).hasAccess(Right.VIEW, xwikiAttachmentReference); + verify(this.authorization).hasAccess(Right.VIEW, forbiddenAttachmentReference); + assertEquals(Collections.singletonList(attachment), attachments.getAttachments()); } }
37ecea84fdd0XWIKI-4135: Component override support does not work
2 files changed · +15 −7
xwiki-component/xwiki-component-default/src/main/java/org/xwiki/component/annotation/ComponentAnnotationLoader.java+2 −2 modified@@ -105,11 +105,11 @@ public void initialize(ComponentManager manager, ClassLoader classLoader) if (descriptorMap.containsKey(roleHint)) { // Is the component in the override list? ComponentDescriptor existingDescriptor = descriptorMap.get(roleHint); - if (!componentOverrideClassNames.contains(existingDescriptor.getImplementation())) { + if (!componentOverrideClassNames.contains(existingDescriptor.getImplementation().getName())) { descriptorMap.put(new RoleHint(componentRoleClass, descriptor.getRoleHint()), descriptor); - if (!componentOverrideClassNames.contains(descriptor.getImplementation())) { + if (!componentOverrideClassNames.contains(descriptor.getImplementation().getName())) { getLogger().warn( "Component [" + existingDescriptor.getImplementation().getName() + "] is being overwritten by component ["
xwiki-component/xwiki-component-default/src/test/java/org/xwiki/component/annotation/ComponentAnnotationLoaderTest.java+13 −5 modified@@ -26,8 +26,10 @@ import org.hamcrest.Factory; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; +import org.hamcrest.core.IsNot; import org.jmock.Expectations; import org.jmock.Mockery; +import org.junit.After; import org.junit.Assert; import org.junit.Test; import org.xwiki.component.descriptor.ComponentDescriptor; @@ -80,9 +82,9 @@ public void testFindComponentRoleClasses() public static class ComponentDescriptorMatcher extends TypeSafeMatcher<ComponentDescriptor> { - private String implementation; + private Class<?> implementation; - public ComponentDescriptorMatcher(String implementation) + public ComponentDescriptorMatcher(Class<?> implementation) { this.implementation = implementation; } @@ -100,11 +102,17 @@ public void describeTo(Description description) } @Factory - public static Matcher<ComponentDescriptor> aComponentDescriptorWithImplementation(String implementation) + public static Matcher<ComponentDescriptor> aComponentDescriptorWithImplementation(Class<?> implementation) { return new ComponentDescriptorMatcher(implementation); } + @After + public void tearDown() throws Exception + { + this.context.assertIsSatisfied(); + } + /** * Verify that when there are several component implementations for the same role/hint then * component implementations defined in META-INF/component-overrides.txt are used in priority. @@ -116,8 +124,8 @@ public void testOverrides() throws Exception final ComponentManager mockManager = this.context.mock(ComponentManager.class); this.context.checking(new Expectations() {{ - allowing(mockManager).registerComponent(with(any(ComponentDescriptor.class))); - oneOf(mockManager).registerComponent(with(aComponentDescriptorWithImplementation(OverrideRole.class.getName()))); + allowing(mockManager).registerComponent( + with(new IsNot<ComponentDescriptor>(aComponentDescriptorWithImplementation(SimpleRole.class)))); }}); loader.initialize(mockManager, this.getClass().getClassLoader());
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
8- github.com/advisories/GHSA-r5cr-xm48-97xpghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-46554ghsaADVISORY
- github.com/xwiki/xwiki-platform/commit/37ecea84fdd053c33733c2ae9a0778bf98eae608ghsax_refsource_MISCWEB
- github.com/xwiki/xwiki-platform/commit/a43e933ddeda17dad1772396e1757998260e9342ghsax_refsource_MISCWEB
- github.com/xwiki/xwiki-platform/commit/c02ce7843a39851865b9d7b6132e32fdd21e3856ghsax_refsource_MISCWEB
- github.com/xwiki/xwiki-platform/security/advisories/GHSA-r5cr-xm48-97xpghsax_refsource_CONFIRMWEB
- jira.xwiki.org/browse/XWIKI-22424ghsax_refsource_MISCWEB
- jira.xwiki.org/browse/XWIKI-22427ghsaWEB
News mentions
0No linked articles in our index yet.