CVE-2023-29506: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: XWiki Commons are technical libraries common to several other top level XWiki projects. It was possible to inject some code using the URL of authenticated endpoints. This problem has been patched on XWiki 13.10.11, 14.4.7 and 14.10.
CWE description: The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
CVSS v3 base score: 6.1
Published: 2023-04-16T07:15Z
Repository: https://github.com/xwiki/xwiki-platform
Language: Java
Files changed: xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authentication/xwiki-platform-security-authentication-default/src/main/java/org/xwiki/security/authentication/internal/resource/AuthenticationResourceReferenceHandler.java, xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authentication/xwiki-platform-security-authentication-default/src/test/java/org/xwiki/security/authentication/internal/resource/AuthenticationResourceReferenceHandlerTest.java
Commit date: 2022-11-15 16:31:20 +0100
Commit message: XWIKI-20335: Wiki existence is not properly checked in authenticate

Diff: diff --git a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authentication/xwiki-platform-security-authentication-default/src/main/java/org/xwiki/security/authentication/internal/resource/AuthenticationResourceReferenceHandler.java b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authentication/xwiki-platform-security-authentication-default/src/main/java/org/xwiki/security/authentication/internal/resource/AuthenticationResourceReferenceHandler.java
index 82a4bf1096c31..72cd6ddc06121 100644
--- a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authentication/xwiki-platform-security-authentication-default/src/main/java/org/xwiki/security/authentication/internal/resource/AuthenticationResourceReferenceHandler.java
+++ b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authentication/xwiki-platform-security-authentication-default/src/main/java/org/xwiki/security/authentication/internal/resource/AuthenticationResourceReferenceHandler.java
@@ -35,10 +35,12 @@
 import org.xwiki.resource.ResourceReference;
 import org.xwiki.resource.ResourceReferenceHandlerChain;
 import org.xwiki.resource.ResourceReferenceHandlerException;
 import org.xwiki.resource.ResourceType;
 import org.xwiki.security.authentication.AuthenticationResourceReference;
+import org.xwiki.wiki.descriptor.WikiDescriptorManager;
+import org.xwiki.wiki.manager.WikiManagerException;
 
 import com.xpn.xwiki.XWikiContext;
 import com.xpn.xwiki.XWikiContextInitializer;
 import com.xpn.xwiki.web.Utils;
 
@@ -57,10 +59,13 @@ public class AuthenticationResourceReferenceHandler extends AbstractResourceRefe
     private XWikiContextInitializer xWikiContextInitializer;
 
     @Inject
     private Execution execution;
 
+    @Inject
+    private WikiDescriptorManager wikiDescriptorManager;
+
     @Override
     public List<ResourceType> getSupportedResourceReferences()
     {
         return Collections.singletonList(AuthenticationResourceReference.TYPE);
     }
@@ -69,10 +74,21 @@ public List<ResourceType> getSupportedResourceReferences()
     public void handle(ResourceReference reference, ResourceReferenceHandlerChain chain)
         throws ResourceReferenceHandlerException
     {
         AuthenticationResourceReference authenticationResourceReference = (AuthenticationResourceReference) reference;
 
+        WikiReference wikiReference = authenticationResourceReference.getWikiReference();
+        try {
+            if (!this.wikiDescriptorManager.exists(wikiReference.getName())) {
+                throw new ResourceReferenceHandlerException(
+                    String.format("The wiki [%s] does not exist.", wikiReference.getName()));
+            }
+        } catch (WikiManagerException e) {
+            throw new ResourceReferenceHandlerException(
+                String.format("Error when checking if wiki [%s] exists.", wikiReference.getName()), e);
+        }
+
         switch (authenticationResourceReference.getAction()) {
             case RETRIEVE_USERNAME:
                 this.handleAction("forgotusername", authenticationResourceReference.getWikiReference());
                 break;
 
diff --git a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authentication/xwiki-platform-security-authentication-default/src/test/java/org/xwiki/security/authentication/internal/resource/AuthenticationResourceReferenceHandlerTest.java b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authentication/xwiki-platform-security-authentication-default/src/test/java/org/xwiki/security/authentication/internal/resource/AuthenticationResourceReferenceHandlerTest.java
index dfc333bdad23a..48bfc9376d4a3 100644
--- a/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authentication/xwiki-platform-security-authentication-default/src/test/java/org/xwiki/security/authentication/internal/resource/AuthenticationResourceReferenceHandlerTest.java
+++ b/xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authentication/xwiki-platform-security-authentication-default/src/test/java/org/xwiki/security/authentication/internal/resource/AuthenticationResourceReferenceHandlerTest.java
@@ -29,24 +29,28 @@
 import org.junit.jupiter.api.Test;
 import org.xwiki.context.Execution;
 import org.xwiki.context.ExecutionContext;
 import org.xwiki.model.reference.WikiReference;
 import org.xwiki.resource.ResourceReferenceHandlerChain;
+import org.xwiki.resource.ResourceReferenceHandlerException;
 import org.xwiki.security.authentication.AuthenticationAction;
 import org.xwiki.security.authentication.AuthenticationResourceReference;
 import org.xwiki.test.junit5.mockito.ComponentTest;
 import org.xwiki.test.junit5.mockito.InjectMockComponents;
 import org.xwiki.test.junit5.mockito.MockComponent;
+import org.xwiki.wiki.descriptor.WikiDescriptorManager;
+import org.xwiki.wiki.manager.WikiManagerException;
 
 import com.xpn.xwiki.XWiki;
 import com.xpn.xwiki.XWikiContext;
 import com.xpn.xwiki.XWikiContextInitializer;
 import com.xpn.xwiki.XWikiException;
 import com.xpn.xwiki.plugin.XWikiPluginManager;
 import com.xpn.xwiki.web.XWikiResponse;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 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;
@@ -67,10 +71,13 @@
     private XWikiContextInitializer xWikiContextInitializer;
 
     @MockComponent
     private Execution execution;
 
+    @MockComponent
+    private WikiDescriptorManager wikiDescriptorManager;
+
     private XWikiResponse response;
 
     private XWiki xwiki;
 
     private XWikiContext context;
@@ -110,17 +117,23 @@ void getSupportedResourceReferences()
 
     @Test
     void handleResetPassword() throws Exception
     {
         WikiReference wikiReference = new WikiReference("foo");
+        when(this.wikiDescriptorManager.exists("foo")).thenReturn(false);
         AuthenticationResourceReference resourceReference = new AuthenticationResourceReference(
             wikiReference,
             AuthenticationAction.RESET_PASSWORD);
 
-        when(this.xwiki.evaluateTemplate("resetpassword.vm", context)).thenReturn("Reset password content");
-
         ResourceReferenceHandlerChain chain = mock(ResourceReferenceHandlerChain.class);
+        ResourceReferenceHandlerException exception =
+            assertThrows(ResourceReferenceHandlerException.class,
+                () -> this.resourceReferenceHandler.handle(resourceReference, chain));
+        assertEquals("The wiki [foo] does not exist.", exception.getMessage());
+
+        when(this.wikiDescriptorManager.exists("foo")).thenReturn(true);
+        when(this.xwiki.evaluateTemplate("resetpassword.vm", context)).thenReturn("Reset password content");
         this.resourceReferenceHandler.handle(resourceReference, chain);
 
         verify(response).setContentType("text/html; charset=UTF-8");
         verify(this.xWikiContextInitializer).initialize(any(ExecutionContext.class));
         verify(servletOutputStream).write("Reset password content".getBytes(StandardCharsets.UTF_8));
@@ -131,22 +144,44 @@ void handleResetPassword() throws Exception
 
     @Test
     void handleForgotUsername() throws Exception
     {
         WikiReference wikiReference = new WikiReference("bar");
+        when(this.wikiDescriptorManager.exists("bar")).thenReturn(false);
         AuthenticationResourceReference resourceReference = new AuthenticationResourceReference(
             wikiReference,
             AuthenticationAction.RETRIEVE_USERNAME);
 
+        ResourceReferenceHandlerChain chain = mock(ResourceReferenceHandlerChain.class);
+        ResourceReferenceHandlerException exception =
+            assertThrows(ResourceReferenceHandlerException.class,
+                () -> this.resourceReferenceHandler.handle(resourceReference, chain));
+        assertEquals("The wiki [bar] does not exist.", exception.getMessage());
+
+        when(this.wikiDescriptorManager.exists("bar")).thenReturn(true);
         when(this.xwiki.evaluateTemplate("forgotusername.vm", context)).thenReturn("Forgot user name content");
 
-        ResourceReferenceHandlerChain chain = mock(ResourceReferenceHandlerChain.class);
         this.resourceReferenceHandler.handle(resourceReference, chain);
-
         verify(response).setContentType("text/html; charset=UTF-8");
         verify(this.xWikiContextInitializer).initialize(any(ExecutionContext.class));
         verify(servletOutputStream).write("Forgot user name content".getBytes(StandardCharsets.UTF_8));
         verify(chain).handleNext(resourceReference);
         verify(context).setWikiReference(wikiReference);
         verify(context).setWikiReference(currentWiki);
     }
+
+    @Test
+    void handleForgotUsernameWikiDescriptorError() throws Exception
+    {
+        WikiReference wikiReference = new WikiReference("bar");
+        when(this.wikiDescriptorManager.exists("bar")).thenThrow(new WikiManagerException("Cannot access wiki"));
+        AuthenticationResourceReference resourceReference = new AuthenticationResourceReference(
+            wikiReference,
+            AuthenticationAction.RETRIEVE_USERNAME);
+
+        ResourceReferenceHandlerChain chain = mock(ResourceReferenceHandlerChain.class);
+        ResourceReferenceHandlerException exception =
+            assertThrows(ResourceReferenceHandlerException.class,
+                () -> this.resourceReferenceHandler.handle(resourceReference, chain));
+        assertEquals("Error when checking if wiki [bar] exists.", exception.getMessage());
+    }
 }

Vulnerable code:         when(this.xwiki.evaluateTemplate("resetpassword.vm", context)).thenReturn("Reset password content");

        ResourceReferenceHandlerChain chain = mock(ResourceReferenceHandlerChain.class);

Fixed code: import org.xwiki.wiki.descriptor.WikiDescriptorManager;
import org.xwiki.wiki.manager.WikiManagerException;
    @Inject
    private WikiDescriptorManager wikiDescriptorManager;

        WikiReference wikiReference = authenticationResourceReference.getWikiReference();
        try {
            if (!this.wikiDescriptorManager.exists(wikiReference.getName())) {
                throw new ResourceReferenceHandlerException(
                    String.format("The wiki [%s] does not exist.", wikiReference.getName()));
            }
        } catch (WikiManagerException e) {
            throw new ResourceReferenceHandlerException(
                String.format("Error when checking if wiki [%s] exists.", wikiReference.getName()), e);
        }

import org.xwiki.resource.ResourceReferenceHandlerException;
import org.xwiki.wiki.descriptor.WikiDescriptorManager;
import org.xwiki.wiki.manager.WikiManagerException;
import static org.junit.jupiter.api.Assertions.assertThrows;
    @MockComponent
    private WikiDescriptorManager wikiDescriptorManager;

        when(this.wikiDescriptorManager.exists("foo")).thenReturn(false);
        ResourceReferenceHandlerException exception =
            assertThrows(ResourceReferenceHandlerException.class,
                () -> this.resourceReferenceHandler.handle(resourceReference, chain));
        assertEquals("The wiki [foo] does not exist.", exception.getMessage());

        when(this.wikiDescriptorManager.exists("foo")).thenReturn(true);
        when(this.xwiki.evaluateTemplate("resetpassword.vm", context)).thenReturn("Reset password content");
        when(this.wikiDescriptorManager.exists("bar")).thenReturn(false);
        ResourceReferenceHandlerChain chain = mock(ResourceReferenceHandlerChain.class);
        ResourceReferenceHandlerException exception =
            assertThrows(ResourceReferenceHandlerException.class,
                () -> this.resourceReferenceHandler.handle(resourceReference, chain));
        assertEquals("The wiki [bar] does not exist.", exception.getMessage());

        when(this.wikiDescriptorManager.exists("bar")).thenReturn(true);

    @Test
    void handleForgotUsernameWikiDescriptorError() throws Exception
    {
        WikiReference wikiReference = new WikiReference("bar");
        when(this.wikiDescriptorManager.exists("bar")).thenThrow(new WikiManagerException("Cannot access wiki"));
        AuthenticationResourceReference resourceReference = new AuthenticationResourceReference(
            wikiReference,
            AuthenticationAction.RETRIEVE_USERNAME);

        ResourceReferenceHandlerChain chain = mock(ResourceReferenceHandlerChain.class);
        ResourceReferenceHandlerException exception =
            assertThrows(ResourceReferenceHandlerException.class,
                () -> this.resourceReferenceHandler.handle(resourceReference, chain));
        assertEquals("Error when checking if wiki [bar] exists.", exception.getMessage());
    }