Privilege escalation (PR)/RCE from account through Invitation subject/message
Description
XWiki Platform is a generic wiki platform offering runtime services for applications built on top of it. Any user who can view Invitation.WebHome can execute arbitrary script macros including Groovy and Python macros that allow remote code execution including unrestricted read and write access to all wiki contents. This vulnerability has been patched on XWiki 14.4.8, 15.2-rc-1, and 14.10.6. Users are advised to upgrade. Users unable to upgrade may manually apply the patch on Invitation.InvitationCommon and Invitation.InvitationConfig, but there are otherwise no known workarounds for this vulnerability.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.xwiki.platform:xwiki-platform-invitation-uiMaven | >= 2.5-m-1, < 14.4.8 | 14.4.8 |
org.xwiki.platform:xwiki-platform-invitation-uiMaven | >= 14.5, < 14.10.6 | 14.10.6 |
org.xwiki.platform:xwiki-platform-invitation-uiMaven | >= 15.0-rc-1, < 15.2-rc-1 | 15.2-rc-1 |
Affected products
1- Range: >= 2.5-m1, < 14.4.8
Patches
1ff1d8a1790c6XWIKI-20421: Improved escaping of subjectLineTemplate
4 files changed · +53 −16
xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationCommon.xml+1 −1 modified@@ -825,7 +825,7 @@ $services.localization.render('xe.invitation.emailContent.reportMessage', ["< <smtp_server_username/> </property> <property> - <subjectLineTemplate>{{velocity}}$services.localization.render('xe.invitation.emailContent.subjectLine', [$xcontext.getUser().replaceAll("^[^\.]*.", ""), $xwiki.getRequestURL().replaceAll("https?://([^/:]*).*$", "$1"), $!subjectLine]){{/velocity}}</subjectLineTemplate> + <subjectLineTemplate>{{velocity wiki='false'}}$services.localization.render('xe.invitation.emailContent.subjectLine', [$xcontext.getUser().replaceAll("^[^\.]*.", ""), $xwiki.getRequestURL().replaceAll("https?://([^/:]*).*$", "$1"), $!subjectLine]){{/velocity}}</subjectLineTemplate> </property> <property> <usersMayPersonalizeMessage>1</usersMayPersonalizeMessage>
xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationConfig.xml+1 −1 modified@@ -420,7 +420,7 @@ $services.localization.render('xe.invitation.emailContent.reportMessage', ["< <smtp_server_username/> </property> <property> - <subjectLineTemplate>{{velocity}}$services.localization.render('xe.invitation.emailContent.subjectLine', [$xcontext.getUser().replaceAll("^[^\.]*.", ""), $xwiki.getRequestURL().replaceAll("https?://([^/:]*).*$", "$1"), $!subjectLine]){{/velocity}}</subjectLineTemplate> + <subjectLineTemplate>{{velocity wiki='false'}}$services.localization.render('xe.invitation.emailContent.subjectLine', [$xcontext.getUser().replaceAll("^[^\.]*.", ""), $xwiki.getRequestURL().replaceAll("https?://([^/:]*).*$", "$1"), $!subjectLine]){{/velocity}}</subjectLineTemplate> </property> <property> <usersMayPersonalizeMessage>1</usersMayPersonalizeMessage>
xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/test/java/org/xwiki/invitation/InvitationCommonPageTest.java+31 −12 modified@@ -33,7 +33,6 @@ import org.xwiki.rendering.internal.configuration.DefaultRenderingConfigurationComponentList; import org.xwiki.rendering.internal.macro.message.ErrorMessageMacro; import org.xwiki.rendering.internal.macro.message.InfoMessageMacro; -import org.xwiki.rendering.syntax.Syntax; import org.xwiki.script.ScriptContextManager; import org.xwiki.security.authorization.AuthorizationManager; import org.xwiki.test.annotation.ComponentList; @@ -51,6 +50,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; +import static org.xwiki.rendering.syntax.Syntax.PLAIN_1_0; +import static org.xwiki.rendering.syntax.Syntax.XWIKI_2_0; +import static org.xwiki.rendering.syntax.Syntax.XWIKI_2_1; /** * Test of {@code Invitation.InvitationCommon}. @@ -73,6 +75,9 @@ }) class InvitationCommonPageTest extends PageTest { + public static final DocumentReference INVITATION_COMMON_REFERENCE = + new DocumentReference("xwiki", "Invitation", "InvitationCommon"); + private ScriptContext scriptContext; @BeforeEach @@ -91,8 +96,7 @@ void testEq0() throws Exception this.context.setDoc(this.xwiki.getDocument( new DocumentReference("xwiki", "]] {{noscript/}}", "InvitationCommon"), this.context)); - DocumentReference invitationCommonReference = new DocumentReference("xwiki", "Invitation", "InvitationCommon"); - Document document = Jsoup.parse(loadPage(invitationCommonReference).getRenderedContent(this.context)); + Document document = Jsoup.parse(loadPage(INVITATION_COMMON_REFERENCE).getRenderedContent(this.context)); assertEquals("xe.invitation.internalDocument []] {{noscript/}}.WebHome]", document.selectFirst(".infomessage").text()); } @@ -101,7 +105,6 @@ void testEq0() throws Exception void testEq1ConfigClassIsNew() throws Exception { String spaceName = "<script>console.log</script>]]{{noscript/}}"; - DocumentReference invitationCommonReference = new DocumentReference("xwiki", "Invitation", "InvitationCommon"); XWikiDocument doc = this.xwiki.getDocument(new DocumentReference("xwiki", spaceName, "InvitationCommon"), this.context); @@ -110,7 +113,7 @@ void testEq1ConfigClassIsNew() throws Exception this.request.put("test", "1"); - Document document = Jsoup.parse(loadPage(invitationCommonReference).getRenderedContent(this.context)); + Document document = Jsoup.parse(loadPage(INVITATION_COMMON_REFERENCE).getRenderedContent(this.context)); assertEquals("testLoadInvitationConfig", document.selectFirst(".infomessage").text()); Element errorMessage = document.selectFirst(".errormessage"); @@ -148,7 +151,7 @@ void testEq1ConfigClassExistsInvalidFromAddress() throws Exception this.request.put("test", "1"); - XWikiDocument invitationCommonDoc = loadPage(new DocumentReference("xwiki", "Invitation", "InvitationCommon")); + XWikiDocument invitationCommonDoc = loadPage(INVITATION_COMMON_REFERENCE); Document document = Jsoup.parse(invitationCommonDoc.getRenderedContent(this.context)); assertEquals("testLoadInvitationConfig", document.selectFirst(".infomessage").text()); @@ -187,8 +190,7 @@ void testEq1ConfigClassExistsConfigMapTooSmall() throws Exception this.request.put("test", "1"); - DocumentReference reference = new DocumentReference("xwiki", "Invitation", "InvitationCommon"); - XWikiDocument invitationCommonDoc = loadPage(reference); + XWikiDocument invitationCommonDoc = loadPage(INVITATION_COMMON_REFERENCE); Document document = Jsoup.parse(invitationCommonDoc.getRenderedContent(this.context)); assertEquals("testLoadInvitationConfig", document.selectFirst(".infomessage").text()); @@ -229,8 +231,7 @@ void testEq1ConfigClassExistsNewInvitationConfig() throws Exception this.request.put("test", "1"); - DocumentReference reference = new DocumentReference("xwiki", "Invitation", "InvitationCommon"); - XWikiDocument invitationCommonDoc = loadPage(reference); + XWikiDocument invitationCommonDoc = loadPage(INVITATION_COMMON_REFERENCE); Document document = Jsoup.parse(invitationCommonDoc.getRenderedContent(this.context)); assertEquals("testLoadInvitationConfig", document.selectFirst(".infomessage").text()); @@ -241,15 +242,15 @@ void testEq1ConfigClassExistsNewInvitationConfig() throws Exception @Test void displayMessageVelocityMacro() throws Exception { - loadPage(new DocumentReference("xwiki", "Invitation", "InvitationCommon")); + loadPage(INVITATION_COMMON_REFERENCE); DocumentReference invitationMailClassDocumentReference = new DocumentReference("xwiki", "Invitation", "InvitationMailClass"); loadPage(invitationMailClassDocumentReference); XWikiDocument page = this.xwiki.getDocument(new DocumentReference("xwiki", "Space", "Page"), this.context); BaseObject invitationMailXObject = page.newXObject(invitationMailClassDocumentReference, this.context); invitationMailXObject.set("messageBody", "<strong>message body</strong>", this.context); - page.setSyntax(Syntax.XWIKI_2_1); + page.setSyntax(XWIKI_2_1); page.setContent("{{include reference=\"Invitation.InvitationCommon\"/}}\n" + "\n" + "{{velocity}}\n" @@ -262,4 +263,22 @@ void displayMessageVelocityMacro() throws Exception Document document = renderHTMLPage(page); assertEquals("<strong>message body</strong>", document.selectFirst("#preview-messagebody-field").html()); } + + /** + * Check if the value of subjectLine correctly escape its parameters. + */ + @Test + void subjectLineTemplate() throws Exception + { + com.xpn.xwiki.api.Document invitationCommonDocument = + new com.xpn.xwiki.api.Document(loadPage(INVITATION_COMMON_REFERENCE), this.context); + String value = String.valueOf( + invitationCommonDocument.getObject("xwiki:Invitation.WebHome").getProperty("subjectLineTemplate") + .getValue()); + this.oldcore.getScriptContext().setAttribute("subjectLine", "{{noscript/}}", GLOBAL_SCOPE); + com.xpn.xwiki.api.Document testDocument = new com.xpn.xwiki.api.Document( + this.xwiki.getDocument(new DocumentReference("xwiki", "Space", "Test"), this.context), this.context); + String renderedContent = testDocument.getRenderedContent(value, XWIKI_2_0.toIdString(), PLAIN_1_0.toIdString()); + assertEquals("xe.invitation.emailContent.subjectLine [XWikiGuest, null, {{noscript/}}]", renderedContent); + } }
xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/test/java/org/xwiki/invitation/InvitationConfigPageTest.java+20 −2 modified@@ -36,7 +36,10 @@ import com.xpn.xwiki.doc.XWikiDocument; +import static javax.script.ScriptContext.GLOBAL_SCOPE; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.xwiki.rendering.syntax.Syntax.PLAIN_1_0; +import static org.xwiki.rendering.syntax.Syntax.XWIKI_2_0; /** * Test of {@code Invitation.InvitationConfig}. @@ -59,13 +62,13 @@ }) class InvitationConfigPageTest extends PageTest { - private static final DocumentReference INVITATION_CONFIG_DOCUMENT_REFERENCE = + private static final DocumentReference INVITATION_CONFIG_REFERENCE = new DocumentReference("xwiki", "Invitation", "InvitationConfig"); @Test void escapeInfoMessageInternalDocumentParameter() throws Exception { - XWikiDocument invitationGuestActionsDocument = loadPage(INVITATION_CONFIG_DOCUMENT_REFERENCE); + XWikiDocument invitationGuestActionsDocument = loadPage(INVITATION_CONFIG_REFERENCE); // Set up the current doc in the context so that $doc is bound in scripts this.context.setDoc( @@ -75,4 +78,19 @@ void escapeInfoMessageInternalDocumentParameter() throws Exception Element infomessage = document.selectFirst(".infomessage"); assertEquals("xe.invitation.internalDocument [Invitation.WebHome]", infomessage.text()); } + + @Test + void subjectLineTemplate() throws Exception + { + com.xpn.xwiki.api.Document invitationConfigDocument = + new com.xpn.xwiki.api.Document(loadPage(INVITATION_CONFIG_REFERENCE), this.context); + String value = String.valueOf( + invitationConfigDocument.getObject("xwiki:Invitation.WebHome").getProperty("subjectLineTemplate") + .getValue()); + this.oldcore.getScriptContext().setAttribute("subjectLine", "{{noscript/}}", GLOBAL_SCOPE); + com.xpn.xwiki.api.Document testDocument = new com.xpn.xwiki.api.Document( + this.xwiki.getDocument(new DocumentReference("xwiki", "Space", "Test"), this.context), this.context); + String renderedContent = testDocument.getRenderedContent(value, XWIKI_2_0.toIdString(), PLAIN_1_0.toIdString()); + assertEquals("xe.invitation.emailContent.subjectLine [XWikiGuest, null, {{noscript/}}]", renderedContent); + } }
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
5- github.com/advisories/GHSA-7954-6m9q-gpvfghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2023-37914ghsaADVISORY
- github.com/xwiki/xwiki-platform/commit/ff1d8a1790c6ee534c6a4478360a06efeb2d3591ghsax_refsource_MISCWEB
- github.com/xwiki/xwiki-platform/security/advisories/GHSA-7954-6m9q-gpvfghsax_refsource_CONFIRMWEB
- jira.xwiki.org/browse/XWIKI-20421ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.