CVE-2023-50723 (XML, published 2023-12-15T19:15Z)
Description: value: XWiki Platform is a generic wiki platform. Starting in 2.3 and prior to versions 14.10.15, 15.5.2, and 15.7-rc-1, anyone who can edit an arbitrary wiki page in an XWiki installation can gain programming right through several cases of missing escaping in the code for displaying sections in the administration interface. This impacts the confidentiality, integrity and availability of the whole XWiki installation. Normally, all users are allowed to edit their own user profile so this should be exploitable by all users of the XWiki instance. This has been fixed in XWiki 14.10.15, 15.5.2 and 15.7RC1. The patches can be manually applied to the `XWiki.ConfigurableClassMacros` and `XWiki.ConfigurableClass` pages.
Severity: nan (CVSS v3 base score: 8.8)
Weakness: CWE-94 \u2014 Improper Control of Generation of Code ('Code Injection')
The product constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment.
Repository: https://github.com/xwiki/xwiki-platform
Files changed:
xwiki-platform-core/xwiki-platform-administration/xwiki-platform-administration-ui/pom.xml
xwiki-platform-core/xwiki-platform-administration/xwiki-platform-administration-ui/src/main/resources/XWiki/ConfigurableClass.xml
xwiki-platform-core/xwiki-platform-administration/xwiki-platform-administration-ui/src/test/java/org/xwiki/administration/ConfigurableClassPageTest.java
Commit message: XWIKI-21121: Improve handling of headings in configurable section


Vulnerable code:
            == #evaluate($heading) ==
import org.xwiki.template.script.TemplateScriptService;
    TemplateScriptService.class
        new DocumentReference(WIKI_NAME, SPACE_NAME, "]]{{noscript /}}");
    private static final String MY_SECTION_SERIALIZED = "XWiki.]]{{noscript /}}";

Fixed code:
    <dependency>
      <groupId>org.xwiki.platform</groupId>
      <artifactId>xwiki-platform-web-templates</artifactId>
      <version>${project.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.xwiki.platform</groupId>
      <artifactId>xwiki-platform-security-authorization-script</artifactId>
      <version>${project.version}</version>
      <type>test-jar</type>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.xwiki.platform</groupId>
      <artifactId>xwiki-platform-user-default</artifactId>
      <version>${project.version}</version>
      <type>test-jar</type>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.xwiki.platform</groupId>
      <artifactId>xwiki-platform-user-default</artifactId>
      <version>${project.version}</version>
      <scope>test</scope>
    </dependency>
            #if ($services.security.authorization.hasAccess("script",
              $app.authors.effectiveMetadataAuthor, $app.documentReference) &amp;&amp; !$app.restricted)
              #set($evaluatedHeading = "#evaluate($heading)")
            #else
              #set($evaluatedHeading = $heading)
            #end
            == $services.rendering.escape($evaluatedHeading, 'xwiki/2.1') ==
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.xwiki.rendering.internal.macro.message.WarningMessageMacro;
import org.xwiki.security.authorization.Right;
import org.xwiki.security.script.SecurityScriptServiceComponentList;
import org.xwiki.user.UserReferenceComponentList;
import org.xwiki.user.internal.converter.DocumentUserReferenceConverter;
import org.xwiki.user.internal.document.DocumentUserReference;
import com.xpn.xwiki.internal.model.reference.DocumentReferenceConverter;
import com.xpn.xwiki.objects.BaseObject;
import static org.mockito.ArgumentMatchers.eq;
@SecurityScriptServiceComponentList
@UserReferenceComponentList
    WarningMessageMacro.class,
    DocumentUserReferenceConverter.class,
    DocumentReferenceConverter.class
        new DocumentReference(WIKI_NAME, SPACE_NAME, "]],{{noscript /}}");
    private static final String MY_SECTION_SERIALIZED = "XWiki.]],{{noscript /}}";

    @Test
    void escapeHeading() throws Exception
    {
        this.request.put("section", "other");
        when(this.query.execute()).thenReturn(List.of(MY_SECTION_SERIALIZED)).thenReturn(List.of());
        when(this.oldcore.getMockRightService()
            .hasAccessLevel(eq("edit"), any(), any(), any())).thenReturn(true);

        XWikiDocument mySectionDoc = new XWikiDocument(MY_SECTION);
        BaseObject object = mySectionDoc.newXObject(CONFIGURABLE_CLASS, this.context);
        object.setStringValue("displayInCategory", "other");
        object.setStringValue("displayInSection", "other");
        object.setStringValue("heading", "{{noscript /}}");
        object.set("scope", "WIKI+ALL_SPACES", this.context);
        this.xwiki.saveDocument(mySectionDoc, this.context);

        Document htmlPage = renderHTMLPage(CONFIGURABLE_CLASS);
        assertEquals("{{noscript /}}", htmlPage.selectFirst("h2").text());
    }

    @ParameterizedTest
    @ValueSource(booleans = {true, false})
    void checkScriptRight(boolean hasScript) throws Exception
    {
        this.request.put("section", "other");
        when(this.query.execute()).thenReturn(List.of(MY_SECTION_SERIALIZED)).thenReturn(List.of());
        when(this.oldcore.getMockRightService()
            .hasAccessLevel(eq("edit"), any(), any(), any())).thenReturn(true);

        XWikiDocument mySectionDoc = new XWikiDocument(MY_SECTION);
        BaseObject object = mySectionDoc.newXObject(CONFIGURABLE_CLASS, this.context);
        object.setStringValue("displayInCategory", "other");
        object.setStringValue("displayInSection", "other");
        String originalHeading = "$appName {{noscript /}}";
        object.setStringValue("heading", originalHeading);
        object.set("scope", "WIKI+ALL_SPACES", this.context);
        DocumentReference userReference = new DocumentReference(WIKI_NAME, SPACE_NAME, "Admin");
        mySectionDoc.getAuthors().setEffectiveMetadataAuthor(new DocumentUserReference(userReference, true));
        this.xwiki.saveDocument(mySectionDoc, this.context);
        when(this.oldcore.getMockAuthorizationManager().hasAccess(Right.SCRIPT,
            userReference, mySectionDoc.getDocumentReference())).thenReturn(hasScript);

        Document htmlPage = renderHTMLPage(CONFIGURABLE_CLASS);
        String expected;
        if (hasScript) {
            expected = String.format("%s {{noscript /}}", MY_SECTION_SERIALIZED);
        } else {
            expected = originalHeading;
        }
        assertEquals(expected, htmlPage.selectFirst("h2").text());
    }