CVE-2023-45134: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: XWiki Platform is a generic wiki platform offering runtime services for applications built on top of it. `org.xwiki.platform:xwiki-platform-web` starting in version 3.1-milestone-1 and prior to 13.4-rc-1, `org.xwiki.platform:xwiki-platform-web-templates` prior to versions 14.10.2 and 15.5-rc-1, and `org.xwiki.platform:xwiki-web-standard` starting in version 2.4-milestone-2 and prior to version 3.1-milestone-1 are vulnerable to cross-site scripting. An attacker can create a template provider on any document that is part of the wiki (could be the attacker's user profile) that contains malicious code. This code is executed when this template provider is selected during document creation which can be triggered by sending the user to a URL. For the attacker, the only requirement is to have an account as by default the own user profile is editable. This allows an attacker to execute arbitrary actions with the rights of the user opening the malicious link. Depending on the rights of the user, this may allow remote code execution and full read and write access to the whole XWiki installation. This has been patched in `org.xwiki.platform:xwiki-platform-web` 13.4-rc-1, `org.xwiki.platform:xwiki-platform-web-templates` 14.10.2 and 15.5-rc-1, and `org.xwiki.platform:xwiki-web-standard` 3.1-milestone-1 by adding the appropriate escaping. The vulnerable template file createinline.vm is part of XWiki's WAR and can be patched by manually applying the changes from the fix.
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: 9
Published: 2023-10-25T20:15Z
Repository: https://github.com/xwiki/xwiki-platform
Language: Other
Files changed: xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/main/resources/templates/createinline.vm, xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/test/java/org/xwiki/web/CreateInlinePageTest.java
Commit date: 2023-06-05 12:21:28 +0200
Commit message: XWIKI-20962: Escape template provider errors during document creation

* Add escaping and a test

Diff: diff --git a/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/main/resources/templates/createinline.vm b/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/main/resources/templates/createinline.vm
index 60ed5dd40f293..74d0b48955149 100644
--- a/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/main/resources/templates/createinline.vm
+++ b/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/main/resources/templates/createinline.vm
@@ -66,13 +66,15 @@
 ## check if the exception is about the non-allowed spaces for template
 #if("$!exception" != '' && $exception.code == 11016)
   <div class="box errormessage">
     #set($allowedSpaces = $createAllowedSpaces)
     #if ($allowedSpaces.size() == 1)
-      $services.localization.render('core.create.template.allowedspace.inline', [$templateProvider, $allowedSpaces.get(0)])
+      $escapetool.xml($services.localization.render('core.create.template.allowedspace.inline', [$templateProvider,
+        $allowedSpaces.get(0)]))
     #else
-      $services.localization.render('core.create.template.allowedspaces.inline', [$templateProvider, $allowedSpaces.toString()])
+      $escapetool.xml($services.localization.render('core.create.template.allowedspaces.inline', [$templateProvider,
+        $allowedSpaces.toString()]))
     #end
   </div>
 #end
 ## check if the exception is about the document which already exists
 #if("$!exception" != '' && $exception.code == 11002)
diff --git a/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/test/java/org/xwiki/web/CreateInlinePageTest.java b/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/test/java/org/xwiki/web/CreateInlinePageTest.java
index 565a315c46c14..16196beac963b 100644
--- a/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/test/java/org/xwiki/web/CreateInlinePageTest.java
+++ b/xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/test/java/org/xwiki/web/CreateInlinePageTest.java
@@ -18,18 +18,21 @@
  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  */
 package org.xwiki.web;
 
 import java.util.List;
+import java.util.stream.Stream;
 
 import javax.inject.Inject;
 
 import org.jsoup.Jsoup;
 import org.jsoup.nodes.Document;
 import org.jsoup.nodes.Element;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
 import org.xwiki.template.TemplateManager;
 import org.xwiki.test.page.PageTest;
 import org.xwiki.velocity.VelocityManager;
 
 import com.xpn.xwiki.XWikiException;
@@ -116,6 +119,51 @@ void testDocumentAlreadyExistsError() throws Exception
         String editURL = String.format("/xwiki/bin/edit/%s", urlToDocument);
         String expectedMessage = String.format("core.create.page.error.docalreadyexists [%s, %s, %s]",
             DOCUMENT_REFERENCE, viewURL, editURL);
         assertEquals(expectedMessage, errormessage.text());
     }
+
+    /**
+     * Test that when there is an exception about the template provider not allowing the chosen space, the allowed
+     * spaces are correctly escaped.
+     */
+    @ParameterizedTest
+    @MethodSource("allowedSpacesProvider")
+    void templateProviderRestrictionErrorEscaping(List<String> allowedSpaces) throws Exception
+    {
+        String provider = "\"provider</div>";
+        this.request.put("templateprovider", provider);
+        String template = "template</div>";
+
+        // Set "createException" to an XWikiException to simulate a template provider restriction error.
+        Object[] args = { template, DOCUMENT_REFERENCE, DOCUMENT_REFERENCE };
+        XWikiException exception = new XWikiException(XWikiException.MODULE_XWIKI_STORE,
+            XWikiException.ERROR_XWIKI_APP_TEMPLATE_NOT_AVAILABLE,
+            "Template {0} cannot be used in space {1} when creating page {2}", null, args);
+        this.velocityManager.getVelocityContext().put(CREATE_EXCEPTION_VELOCITY_KEY, exception);
+        // Set the allowed spaces to a list containing some HTML.
+        this.velocityManager.getVelocityContext().put("createAllowedSpaces", allowedSpaces);
+
+        // Render the template.
+        Document document = Jsoup.parse(this.templateManager.render(CREATE_INLINE_VM));
+        Element errormessage = document.getElementsByClass(ERROR_MESSAGE_CLASS).first();
+        assertNotNull(errormessage);
+
+        String expectedMessage;
+        if (allowedSpaces.size() == 1) {
+            expectedMessage = String.format("core.create.template.allowedspace.inline [%s, %s]",
+                provider, allowedSpaces.get(0));
+        } else {
+            expectedMessage = String.format("core.create.template.allowedspaces.inline [%s, %s]",
+                provider, allowedSpaces);
+        }
+        assertEquals(expectedMessage, errormessage.text());
+    }
+
+    static Stream<List<String>> allowedSpacesProvider()
+    {
+        return Stream.of(
+            List.of("allowedSpace</div>"),
+            List.of("allowedSpace1</div>", "allowedSpace2</div>")
+        );
+    }
 }

Vulnerable code:       $services.localization.render('core.create.template.allowedspace.inline', [$templateProvider, $allowedSpaces.get(0)])
      $services.localization.render('core.create.template.allowedspaces.inline', [$templateProvider, $allowedSpaces.toString()])
Fixed code:       $escapetool.xml($services.localization.render('core.create.template.allowedspace.inline', [$templateProvider,
        $allowedSpaces.get(0)]))
      $escapetool.xml($services.localization.render('core.create.template.allowedspaces.inline', [$templateProvider,
        $allowedSpaces.toString()]))
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

    /**
     * Test that when there is an exception about the template provider not allowing the chosen space, the allowed
     * spaces are correctly escaped.
     */
    @ParameterizedTest
    @MethodSource("allowedSpacesProvider")
    void templateProviderRestrictionErrorEscaping(List<String> allowedSpaces) throws Exception
    {
        String provider = "\"provider</div>";
        this.request.put("templateprovider", provider);
        String template = "template</div>";

        // Set "createException" to an XWikiException to simulate a template provider restriction error.
        Object[] args = { template, DOCUMENT_REFERENCE, DOCUMENT_REFERENCE };
        XWikiException exception = new XWikiException(XWikiException.MODULE_XWIKI_STORE,
            XWikiException.ERROR_XWIKI_APP_TEMPLATE_NOT_AVAILABLE,
            "Template {0} cannot be used in space {1} when creating page {2}", null, args);
        this.velocityManager.getVelocityContext().put(CREATE_EXCEPTION_VELOCITY_KEY, exception);
        // Set the allowed spaces to a list containing some HTML.
        this.velocityManager.getVelocityContext().put("createAllowedSpaces", allowedSpaces);

        // Render the template.
        Document document = Jsoup.parse(this.templateManager.render(CREATE_INLINE_VM));
        Element errormessage = document.getElementsByClass(ERROR_MESSAGE_CLASS).first();
        assertNotNull(errormessage);

        String expectedMessage;
        if (allowedSpaces.size() == 1) {
            expectedMessage = String.format("core.create.template.allowedspace.inline [%s, %s]",
                provider, allowedSpaces.get(0));
        } else {
            expectedMessage = String.format("core.create.template.allowedspaces.inline [%s, %s]",
                provider, allowedSpaces);
        }
        assertEquals(expectedMessage, errormessage.text());
    }

    static Stream<List<String>> allowedSpacesProvider()
    {
        return Stream.of(
            List.of("allowedSpace</div>"),
            List.of("allowedSpace1</div>", "allowedSpace2</div>")
        );
    }