CVE-2023-35150 (published 2023-06-23T17:15Z)

XWiki Platform is a generic wiki platform offering runtime services for applications built on top of it. Starting in version 2.40m-2 and prior to versions 14.4.8, 14.10.4, and 15.0, any user with view rights on any document can execute code with programming rights, leading to remote code execution by crafting an url with a dangerous payload. The problem has been patched in XWiki 15.0, 14.10.4 and 14.4.8.

CVSS v3 base score: 8.0, HIGH (CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-94
cwe_name: Improper Control of Generation of Code ('Code Injection')
description: 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.
extended_description: 
            When a product allows a user's input to contain code syntax, it might be possible for an attacker to craft the code in such a way that it will alter the intended control flow of the product. Such an alteration could lead to arbitrary code execution.
            Injection problems encompass a wide variety of issues -- all mitigated in very different ways. For this reason, the most effective way to discuss these weaknesses is to note the distinct features which classify them as injection weaknesses. The most important issue to note is that all injection problems share one thing in common -- i.e., they allow for the injection of control plane data into the user-controlled data plane. This means that the execution of the process may be altered by sending code in through legitimate data channels, using no other mechanism. While buffer overflows, and many other flaws, involve the use of some further issue to gain execution, injection problems need only for the data to be parsed. The most classic instantiations of this category of weakness are SQL injection and format string vulnerabilities.
         
         

Fix information:
repo_url: https://github.com/xwiki/xwiki-platform
commit_details: repo_url: https://github.com/xwiki/xwiki-platform
author: Manuel Leduc
author_date: 2023-01-24 08:38:28+01:00
msg: XWIKI-20285: Improve escaping of the Invitation Application

- Introduce an internal role (DefaultXARExtensionIndex) to know if a document is provided by an extension
- Split index-api to index-api/index-default to be able to use the api in oldcore
- Fix the escaping in invitation-ui
- Provide an automatic fix during upgrade for potential escaping issues in generated code
repository: repo_url: https://github.com/xwiki/xwiki-platform
repo_name: xwiki/xwiki-platform
description: The XWiki platform
repo_language: Java
owner: xwiki
file_changes: filename: pom.xml
change_type: ModificationType.MODIFY
diff: @@ -179,6 +179,22 @@
                 </item>
               </differences>
             </revapi.differences>
+            <revapi.differences>
+              <justification>Moved to xwiki-platform-index-default</justification>
+              <criticality>allowed</criticality>
+              <differences>
+                <item>
+                  <ignore>true</ignore>
+                  <code>java.class.removed</code>
+                  <old>class org.xwiki.index.migration.R140300000XWIKI19614DataMigration</old>
+                </item>
+                <item>
+                  <ignore>true</ignore>
+                  <code>java.class.removed</code>
+                  <old>class org.xwiki.index.migration.R140300001XWIKI19571DataMigration</old>
+                </item>
+              </differences>
+            </revapi.differences>
           </analysisConfiguration>
         </configuration>
       </plugin>

programming_language: HTML
filename: DefaultXARExtensionIndex.java
change_type: ModificationType.ADD
diff: @@ -0,0 +1,56 @@
+/*
+ * See the NOTICE file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.xwiki.extension.xar.internal;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.xwiki.component.annotation.Component;
+import org.xwiki.extension.repository.InstalledExtensionRepository;
+import org.xwiki.extension.xar.internal.handler.XarExtensionHandler;
+import org.xwiki.extension.xar.internal.repository.XarInstalledExtensionRepository;
+import org.xwiki.internal.extension.XARExtensionIndex;
+import org.xwiki.model.reference.DocumentReference;
+
+/**
+ * Default implementation of {@link XARExtensionIndex}. Uses {@link XarInstalledExtensionRepository} to implement the
+ * operations.
+ *
+ * @version $Id$
+ * @since 15.0RC1
+ * @since 14.4.8
+ * @since 14.10.4
+ */
+@Component
+@Singleton
+public class DefaultXARExtensionIndex implements XARExtensionIndex
+{
+    @Inject
+    @Named(XarExtensionHandler.TYPE)
+    private InstalledExtensionRepository installedXARs;
+
+    @Override
+    public boolean isExtensionDocument(DocumentReference documentReference)
+    {
+        return !((XarInstalledExtensionRepository) this.installedXARs).getXarInstalledExtensions(documentReference)
+            .isEmpty();
+    }
+}

programming_language: Java
filename: components.txt
change_type: ModificationType.MODIFY
diff: @@ -1,4 +1,5 @@
 org.xwiki.extension.xar.internal.DefaultXarExtensionConfiguration
+org.xwiki.extension.xar.internal.DefaultXARExtensionIndex
 org.xwiki.extension.xar.internal.delete.DocumentsDeletingListener
 org.xwiki.extension.xar.internal.doc.XarDocumentRevisionProvider
 org.xwiki.extension.xar.internal.doc.InstalledExtensionDocumentCustomizationDetector

programming_language: Scala
filename: pom.xml
change_type: ModificationType.MODIFY
diff: @@ -33,6 +33,7 @@
   <description>XWiki Platform - Index - Parent POM</description>
   <modules>
     <module>xwiki-platform-index-api</module>
+    <module>xwiki-platform-index-default</module>
     <module>xwiki-platform-index-ui</module>
     <module>xwiki-platform-index-tree</module>
   </modules>

programming_language: HTML
filename: pom.xml
change_type: ModificationType.MODIFY
diff: @@ -32,7 +32,7 @@
   <description>API to queue asynchronous tasks dedicated to the analysis of wiki pages.</description>
   <packaging>jar</packaging>
   <properties>
-    <xwiki.jacoco.instructionRatio>0.85</xwiki.jacoco.instructionRatio>
+    <xwiki.jacoco.instructionRatio>0.04</xwiki.jacoco.instructionRatio>
     <!-- Name to display by the Extension Manager -->
     <xwiki.extension.name>Index API</xwiki.extension.name>
     <xwiki.extension.category>api</xwiki.extension.category>
@@ -50,15 +50,9 @@
       <version>${project.version}</version>
     </dependency>
     <dependency>
-      <groupId>org.xwiki.platform</groupId>
-      <artifactId>xwiki-platform-query-manager</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-    <!-- Required to have access to XWikiDocumentIndexingTask. -->
-    <dependency>
-      <groupId>org.xwiki.platform</groupId>
-      <artifactId>xwiki-platform-oldcore</artifactId>
-      <version>${project.version}</version>
+      <groupId>org.xwiki.commons</groupId>
+      <artifactId>xwiki-commons-stability</artifactId>
+      <version>${commons.version}</version>
     </dependency>
     <!-- Test dependencies. -->
     <dependency>
@@ -67,29 +61,5 @@
       <version>${commons.version}</version>
       <scope>test</scope>
     </dependency>
-    <dependency>
-      <groupId>javax.servlet</groupId>
-      <artifactId>javax.servlet-api</artifactId>
-      <scope>test</scope>
-    </dependency>
   </dependencies>
-  <build>
-    <plugins>
-      <plugin>
-        <!-- Apply the Checkstyle configurations defined in the top level pom.xml file -->
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-checkstyle-plugin</artifactId>
-        <executions>
-          <execution>
-            <!-- Specify the "default" execution id so that the "blocker" one is always executed -->
-            <id>default</id>
-            <configuration>
-              <failsOnError>true</failsOnError>
-              <suppressionsLocation>${basedir}/src/checkstyle/checkstyle-suppressions.xml</suppressionsLocation>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-    </plugins>
-  </build>
 </project>

programming_language: HTML
filename: TaskConsumerScriptService.java
change_type: ModificationType.MODIFY
diff: @@ -23,14 +23,12 @@
 
 import javax.inject.Inject;
 import javax.inject.Named;
-import javax.inject.Provider;
 import javax.inject.Singleton;
 
 import org.xwiki.component.annotation.Component;
 import org.xwiki.script.service.ScriptService;
 import org.xwiki.stability.Unstable;
-
-import com.xpn.xwiki.XWikiContext;
+import org.xwiki.wiki.descriptor.WikiDescriptorManager;
 
 /**
  * Provides the operations to interact with the task consumer from the scripts.
@@ -48,13 +46,13 @@ public class TaskConsumerScriptService implements ScriptService
     private TaskManager taskManager;
 
     @Inject
-    private Provider<XWikiContext> xcontextProvider;
+    private WikiDescriptorManager wikiDescriptorManager;
 
     /**
      * @return the count of queued tasks, grouped by task type
      */
     public Map<String, Long> getQueueSizePerType()
     {
-        return this.taskManager.getQueueSizePerType(this.xcontextProvider.get().getWikiId());
+        return this.taskManager.getQueueSizePerType(this.wikiDescriptorManager.getCurrentWikiId());
     }
 }

programming_language: Java
filename: components.txt
change_type: ModificationType.MODIFY
diff: @@ -1,9 +1 @@
-org.xwiki.index.internal.DefaultTasksManager
-org.xwiki.index.internal.TasksStore
-org.xwiki.index.internal.TaskExecutor
-org.xwiki.index.internal.TaskApplicationReadyListener
-org.xwiki.index.internal.DefaultLinksTaskConsumer
-org.xwiki.index.internal.listener.LinksUpdateListener
-org.xwiki.index.migration.R140300000XWIKI19614DataMigration
-org.xwiki.index.migration.R140300001XWIKI19571DataMigration
 org.xwiki.index.TaskConsumerScriptService

programming_language: Markdown
filename: TaskConsumerScriptServiceTest.java
change_type: ModificationType.MODIFY
diff: @@ -21,16 +21,12 @@
 
 import java.util.Map;
 
-import javax.inject.Provider;
-
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.mockito.Mock;
 import org.xwiki.test.junit5.mockito.ComponentTest;
 import org.xwiki.test.junit5.mockito.InjectMockComponents;
 import org.xwiki.test.junit5.mockito.MockComponent;
-
-import com.xpn.xwiki.XWikiContext;
+import org.xwiki.wiki.descriptor.WikiDescriptorManager;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.mockito.Mockito.when;
@@ -51,16 +47,13 @@
     private TaskManager taskManager;
 
     @MockComponent
-    private Provider<XWikiContext> xcontextProvider;
+    private WikiDescriptorManager wikiDescriptorManager;
 
-    @Mock
-    private XWikiContext xcontext;
 
     @BeforeEach
     void setUp()
     {
-        when(this.xcontextProvider.get()).thenReturn(this.xcontext);
-        when(this.xcontext.getWikiId()).thenReturn("xwiki");
+        when(this.wikiDescriptorManager.getCurrentWikiId()).thenReturn("xwiki");
     }
 
     @Test

programming_language: Java
filename: pom.xml
change_type: ModificationType.ADD
diff: @@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ * See the NOTICE file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.xwiki.platform</groupId>
+    <artifactId>xwiki-platform-index</artifactId>
+    <version>15.1-SNAPSHOT</version>
+  </parent>
+  <artifactId>xwiki-platform-index-default</artifactId>
+  <name>XWiki Platform - Index - Default</name>
+  <description>Default implementation of the API to queue asynchronous tasks dedicated to the analysis of wiki pages.</description>
+  <packaging>jar</packaging>
+  <properties>
+    <xwiki.jacoco.instructionRatio>0.84</xwiki.jacoco.instructionRatio>
+    <!-- Name to display by the Extension Manager -->
+    <xwiki.extension.name>Default implementation of the Index API</xwiki.extension.name>
+  </properties>
+  <dependencies>
+    <dependency>
+      <groupId>org.xwiki.platform</groupId>
+      <artifactId>xwiki-platform-index-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.xwiki.commons</groupId>
+      <artifactId>xwiki-commons-component-api</artifactId>
+      <version>${commons.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.xwiki.platform</groupId>
+      <artifactId>xwiki-platform-oldcore</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <!-- Test dependencies. -->
+    <dependency>
+      <groupId>org.xwiki.commons</groupId>
+      <artifactId>xwiki-commons-tool-test-component</artifactId>
+      <version>${commons.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>javax.servlet-api</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+  <build>
+    <plugins>
+      <plugin>
+        <!-- Apply the Checkstyle configurations defined in the top level pom.xml file -->
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+        <executions>
+          <execution>
+            <!-- Specify the "default" execution id so that the "blocker" one is always executed -->
+            <id>default</id>
+            <configuration>
+              <failsOnError>true</failsOnError>
+              <suppressionsLocation>${basedir}/src/checkstyle/checkstyle-suppressions.xml</suppressionsLocation>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
\ No newline at end of file

programming_language: HTML
filename: TaskApplicationReadyListener.java
change_type: ModificationType.RENAME
diff: @@ -27,7 +27,6 @@
 import org.xwiki.bridge.event.ApplicationReadyEvent;
 import org.xwiki.component.annotation.Component;
 import org.xwiki.component.phase.Initializable;
-import org.xwiki.component.phase.InitializationException;
 import org.xwiki.index.TaskManager;
 import org.xwiki.observation.AbstractEventListener;
 import org.xwiki.observation.event.Event;
@@ -73,7 +72,7 @@ public void onEvent(Event event, Object source, Object data)
     }
 
     @Override
-    public void initialize() throws InitializationException
+    public void initialize()
     {
         // If the application is already initialized we start the threads immediately (e.g. in case of extension
         // install) and the implementation of type DefaultTasksManager.

programming_language: Java
filename: R140300000XWIKI19614DataMigration.java
change_type: ModificationType.RENAME
diff: @@ -21,21 +21,19 @@
 
 import java.util.List;
 
-import javax.inject.Inject;
 import javax.inject.Named;
-import javax.inject.Provider;
 import javax.inject.Singleton;
 
 import org.xwiki.component.annotation.Component;
-import org.xwiki.index.TaskManager;
+import org.xwiki.internal.migration.AbstractDocumentsMigration;
 import org.xwiki.query.Query;
 import org.xwiki.query.QueryException;
-import org.xwiki.query.QueryManager;
 
+import com.xpn.xwiki.XWiki;
 import com.xpn.xwiki.XWikiContext;
+import com.xpn.xwiki.doc.XWikiDocument;
 import com.xpn.xwiki.store.migration.DataMigrationException;
 import com.xpn.xwiki.store.migration.XWikiDBVersion;
-import com.xpn.xwiki.store.migration.hibernate.HibernateDataMigration;
 
 import static org.xwiki.index.internal.DefaultLinksTaskConsumer.LINKS_TASK_TYPE;
 
@@ -46,27 +44,17 @@
  * @since 14.2RC1
  * @deprecated link storage and indexing moved to Solr (implemented in xwiki-platform-search-solr-api)
  */
-// TODO: Implement DataMigration once XWIKI-19399 is fixed.
 @Component
 @Singleton
 @Named(R140300000XWIKI19614DataMigration.HINT)
 @Deprecated(since = "14.8RC1")
-public class R140300000XWIKI19614DataMigration implements HibernateDataMigration
+public class R140300000XWIKI19614DataMigration extends AbstractDocumentsMigration
 {
     /**
      * The hint for this component.
      */
     public static final String HINT = "R140300000XWIKI19614";
 
-    @Inject
-    private QueryManager queryManager;
-
-    @Inject
-    private TaskManager taskManager;
-
-    @Inject
-    private Provider<XWikiContext> contextProvider;
-
     @Override
     public String getName()
     {
@@ -86,43 +74,47 @@ public XWikiDBVersion getVersion()
     }
 
     @Override
-    public void migrate() throws DataMigrationException
+    protected String getTaskType()
+    {
+        return LINKS_TASK_TYPE;
+    }
+
+    @Override
+    protected List<String> selectDocuments() throws DataMigrationException
     {
-        XWikiContext context = this.contextProvider.get();
-        // No need to migrate if the wiki does not support backlinks.
+        List<String> documents;
+        XWikiContext context = getXWikiContext();
+        XWiki wiki = getXWikiContext().getWiki();
         if (context.getWiki().hasBacklinks(context)) {
-            String wikiId = context.getWikiId();
             try {
-                List<Long> ids =
-                    this.queryManager.createQuery("SELECT doc.id FROM XWikiDocument doc", Query.HQL).setWiki(wikiId)
-                        .execute();
-                for (Long id : ids) {
-                    this.taskManager.addTask(wikiId, id, LINKS_TASK_TYPE);
-                }
+                documents = wiki.getStore().getQueryManager()
+                    .createQuery("SELECT doc.fullName FROM XWikiDocument doc", Query.HQL)
+                    .setWiki(context.getWikiId())
+                    .execute();
             } catch (QueryException e) {
                 throw new DataMigrationException(
-                    String.format("Failed retrieve the list of all the documents for wiki [%s].", wikiId), e);
+                    String.format("Failed retrieve the list of all the documents for wiki [%s].", wiki.getName()), e);
             }
+        } else {
+            documents = List.of();
         }
+        return documents;
     }
 
     @Override
-    public boolean shouldExecute(XWikiDBVersion startupVersion)
+    protected void logBeforeQueuingTask(XWikiDocument document)
     {
-        return true;
+        // No logs here as it would be too verbose (all documents of the wiki are queued).
     }
 
     @Override
-    public String getPreHibernateLiquibaseChangeLog()
+    protected void logBeforeQueuingTasks(List<XWikiDocument> documents)
     {
-        // TODO: Remove once XWIKI-19399 is fixed.
-        return null;
-    }
-
-    @Override
-    public String getLiquibaseChangeLog()
-    {
-        // TODO: Remove once XWIKI-19399 is fixed.
-        return null;
+        XWikiContext context = getXWikiContext();
+        if (context.getWiki().hasBacklinks(context)) {
+            super.logBeforeQueuingTasks(documents);
+        } else {
+            this.logger.info("Skipped because backlinks are not supported on [{}]", context.getWikiId());
+        }
     }
 }

programming_language: Java
filename: components.txt
change_type: ModificationType.ADD
diff: @@ -0,0 +1,8 @@
+org.xwiki.index.internal.DefaultTasksManager
+org.xwiki.index.internal.TasksStore
+org.xwiki.index.internal.TaskExecutor
+org.xwiki.index.internal.TaskApplicationReadyListener
+org.xwiki.index.internal.DefaultLinksTaskConsumer
+org.xwiki.index.internal.listener.LinksUpdateListener
+org.xwiki.index.migration.R140300000XWIKI19614DataMigration
+org.xwiki.index.migration.R140300001XWIKI19571DataMigration

programming_language: Scala
filename: R140300000XWIKI19614DataMigrationTest.java
change_type: ModificationType.RENAME
diff: @@ -21,27 +21,37 @@
 
 import java.util.List;
 
-import javax.inject.Provider;
-
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
 import org.mockito.Mock;
+import org.xwiki.context.Execution;
+import org.xwiki.context.ExecutionContext;
 import org.xwiki.index.TaskManager;
+import org.xwiki.model.reference.DocumentReference;
+import org.xwiki.model.reference.DocumentReferenceResolver;
 import org.xwiki.query.Query;
 import org.xwiki.query.QueryException;
 import org.xwiki.query.QueryManager;
+import org.xwiki.test.LogLevel;
+import org.xwiki.test.junit5.LogCaptureExtension;
 import org.xwiki.test.junit5.mockito.ComponentTest;
 import org.xwiki.test.junit5.mockito.InjectMockComponents;
 import org.xwiki.test.junit5.mockito.MockComponent;
 
 import com.xpn.xwiki.XWiki;
 import com.xpn.xwiki.XWikiContext;
+import com.xpn.xwiki.doc.XWikiDocument;
+import com.xpn.xwiki.store.XWikiStoreInterface;
 import com.xpn.xwiki.store.migration.DataMigrationException;
 import com.xpn.xwiki.store.migration.hibernate.HibernateDataMigration;
 
+import ch.qos.logback.classic.Level;
+
 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.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoInteractions;
 import static org.mockito.Mockito.when;
@@ -58,14 +68,20 @@
     @InjectMockComponents(role = HibernateDataMigration.class)
     private R140300000XWIKI19614DataMigration migration;
 
-    @MockComponent
+    @Mock
     private QueryManager queryManager;
 
     @MockComponent
     private TaskManager taskManager;
 
     @MockComponent
-    private Provider<XWikiContext> contextProvider;
+    private Execution execution;
+
+    @MockComponent
+    private DocumentReferenceResolver<String> resolver;
+
+    @RegisterExtension
+    LogCaptureExtension logCapture = new LogCaptureExtension(LogLevel.INFO);
 
     @Mock
     private XWikiContext context;
@@ -79,10 +95,18 @@
     @BeforeEach
     void setUp() throws Exception
     {
-        when(this.contextProvider.get()).thenReturn(this.context);
+        ExecutionContext executionContext = mock(ExecutionContext.class);
+        XWikiStoreInterface xWikiStoreInterface = mock(XWikiStoreInterface.class);
+
+        when(this.execution.getContext()).thenReturn(executionContext);
+        when(executionContext.getProperty("xwikicontext")).thenReturn(this.context);
         when(this.context.getWiki()).thenReturn(this.wiki);
+        when(this.wiki.getName()).thenReturn("wiki1");
         when(this.context.getWikiId()).thenReturn("wiki1");
-        when(this.queryManager.createQuery("SELECT doc.id FROM XWikiDocument doc",
+        when(this.wiki.getStore()).thenReturn(xWikiStoreInterface);
+        when(xWikiStoreInterface.getQueryManager()).thenReturn(this.queryManager);
+
+        when(this.queryManager.createQuery("SELECT doc.fullName FROM XWikiDocument doc",
             Query.HQL)).thenReturn(this.query);
         when(this.query.setWiki(any())).thenReturn(this.query);
     }
@@ -90,15 +114,29 @@ void setUp() throws Exception
     @Test
     void migrate() throws Exception
     {
+        DocumentReference doc42 = new DocumentReference("xwiki", "XWiki", "Doc42");
+        DocumentReference doc43 = new DocumentReference("xwiki", "XWiki", "Doc43");
+        XWikiDocument xWikiDocument42 = mock(XWikiDocument.class);
+        XWikiDocument xWikiDocument43 = mock(XWikiDocument.class);
+
         when(this.wiki.hasBacklinks(this.context)).thenReturn(true);
 
-        when(this.query.execute()).thenReturn(List.of(42L, 43L));
+        when(this.query.execute()).thenReturn(List.of("xwiki.XWiki.Doc42", "xwiki.XWiki.Doc43"));
+        when(this.resolver.resolve("xwiki.XWiki.Doc42")).thenReturn(doc42);
+        when(this.resolver.resolve("xwiki.XWiki.Doc43")).thenReturn(doc43);
+        when(this.wiki.getDocument(doc42, this.context)).thenReturn(xWikiDocument42);
+        when(this.wiki.getDocument(doc43, this.context)).thenReturn(xWikiDocument43);
+        when(xWikiDocument42.getId()).thenReturn(42L);
+        when(xWikiDocument43.getId()).thenReturn(43L);
 
         this.migration.migrate();
 
         verify(this.query).setWiki("wiki1");
-        verify(this.taskManager).addTask("wiki1", 42L,  "links");
+        verify(this.taskManager).addTask("wiki1", 42L, "links");
         verify(this.taskManager).addTask("wiki1", 43L, "links");
+
+        assertEquals("[2] documents queued to task [links]", this.logCapture.getMessage(0));
+        assertEquals(Level.INFO, this.logCapture.getLogEvent(0).getLevel());
     }
 
     @Test
@@ -111,8 +149,11 @@ void migrateQueryException() throws Exception
         DataMigrationException queryException =
             assertThrows(DataMigrationException.class, () -> this.migration.migrate());
 
-        assertEquals("Failed retrieve the list of all the documents for wiki [wiki1].", queryException.getMessage());
-        assertEquals(QueryException.class, queryException.getCause().getClass());
+        assertEquals("Data migration R140300000XWIKI19614 failed", queryException.getMessage());
+        assertEquals("Failed retrieve the list of all the documents for wiki [wiki1].",
+            queryException.getCause().getMessage());
+        assertEquals(DataMigrationException.class, queryException.getCause().getClass());
+        assertEquals(QueryException.class, queryException.getCause().getCause().getClass());
 
         verify(this.query).setWiki("wiki1");
         verifyNoInteractions(this.taskManager);
@@ -125,5 +166,7 @@ void migrateNotHasBacklinks() throws Exception
         this.migration.migrate();
         verifyNoInteractions(this.queryManager);
         verifyNoInteractions(this.taskManager);
+        assertEquals("Skipped because backlinks are not supported on [wiki1]", this.logCapture.getMessage(0));
+        assertEquals(Level.INFO, this.logCapture.getLogEvent(0).getLevel());
     }
 }

programming_language: Java
filename: InvitationCommon.xml
change_type: ModificationType.MODIFY
diff: @@ -57,7 +57,9 @@
   {{info}}testLoadInvitationConfig{{/info}}
   #testLoadInvitationConfig()
 #elseif($doc.documentReference.name == 'InvitationCommon')
-  {{info}}$services.localization.render('xe.invitation.internalDocument', ["${doc.getSpace()}.WebHome"]){{/info}}
+  #set ($linkTarget = "${doc.getSpace()}.WebHome")
+  #set ($linkTarget = $services.rendering.escape($linkTarget, 'xwiki/2.1'))
+  {{info}}$services.localization.render('xe.invitation.internalDocument', [$linkTarget]){{/info}}
 #end
 ##
 #*
@@ -186,8 +188,10 @@
         $config.put($element.getName(), $defaultConfigObj.getProperty($element.getName()).getValue())
       #end
       ##
-      #set($configDocContent = '{{velo' + 'city}}{{info}}$services.localization.render(''xe.invitation.internalDocument'', ["'
-                               + "$!config.get('mainPage')" + '"]){{/info}}{{/velo' + 'city}}')
+      #set($configDocContent = 
+        '{{velocity}}{{info}}$services.localization.render(''xe.invitation.internalDocument'','
+          + '[$services.rendering.escape("' + "$!config.get('mainPage')" 
+          + '", ''xwiki/2.1'')]){{/info}}{{/velocity}}')
       $configDoc.setContent($configDocContent)
       $configDoc.setParent($configClassNameInternal)
       #set($configObj = $configDoc.newObject($configClassNameInternal))
@@ -275,8 +279,10 @@
 #macro(loadInvitationMail, $config, $emailContainer, $mail)
   ## If this doesn't already exist, it's created.
   #if($emailContainer.isNew())
-    #set($emailContainerContent = '{{velo' + 'city}}{{info}}$services.localization.render(''xe.invitation.internalDocument'', ["'
-                                  + "$config.get('emailContainer')" + '"]){{/info}}{{/velo' + 'city}}')
+    #set($emailContainerContent = 
+      '{{velocity}}{{info}}$services.localization.render(''xe.invitation.internalDocument'','
+        + '[$services.rendering.escape("' + "$config.get('emailContainer')" 
+        + '", ''xwiki/2.1'')]){{/info}}{{/velocity}}')
     #set($discard = $emailContainer.setContent($emailContainerContent))
     #set($discard = $emailContainer.setHidden(true))
     #set($discard = $emailContainer.saveAsAuthor())

programming_language: PHP
filename: InvitationConfig.xml
change_type: ModificationType.MODIFY
diff: @@ -20,7 +20,7 @@
  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 -->
 
-<xwikidoc version="1.4" reference="Invitation.InvitationConfig" locale="">
+<xwikidoc version="1.5" reference="Invitation.InvitationConfig" locale="">
   <web>Invitation</web>
   <name>InvitationConfig</name>
   <language/>
@@ -34,7 +34,7 @@
   <title/>
   <comment/>
   <minorEdit>false</minorEdit>
-  <syntaxId>xwiki/2.0</syntaxId>
+  <syntaxId>xwiki/2.1</syntaxId>
   <hidden>true</hidden>
   <content>{{velocity}}{{info}}$services.localization.render('xe.invitation.internalDocument', ['Invitation.WebHome']){{/info}}{{/velocity}}</content>
   <attachment>

programming_language: Jupyter Notebook
filename: InvitationGuestActions.xml
change_type: ModificationType.MODIFY
diff: @@ -20,7 +20,7 @@
  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 -->
 
-<xwikidoc version="1.3" reference="Invitation.InvitationGuestActions" locale="">
+<xwikidoc version="1.5" reference="Invitation.InvitationGuestActions" locale="">
   <web>Invitation</web>
   <name>InvitationGuestActions</name>
   <language/>
@@ -34,9 +34,9 @@
   <title/>
   <comment/>
   <minorEdit>false</minorEdit>
-  <syntaxId>xwiki/2.0</syntaxId>
+  <syntaxId>xwiki/2.1</syntaxId>
   <hidden>true</hidden>
-  <content>{{include reference="Invitation.InvitationCommon" /}}
+  <content>{{include reference="Invitation.InvitationCommon"/}}
 
 {{velocity}}
 #*
@@ -93,7 +93,9 @@
             $mail,
             $emailContainer)
 #else
-  {{info}}$services.localization.render('xe.invitation.internalDocument', ["${doc.getSpace()}.WebHome"]){{/info}}
+  #set ($linkTarget = "${doc.getSpace()}.WebHome")
+  #set ($linkTarget = $services.rendering.escape($linkTarget, 'xwiki/2.1'))
+  {{info}}$services.localization.render('xe.invitation.internalDocument', [$linkTarget]){{/info}}
 #end
 ##
 ##---------------------------------------------------------------------

programming_language: PHP
filename: InvitationMailClass.xml
change_type: ModificationType.MODIFY
diff: @@ -20,7 +20,7 @@
  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 -->
 
-<xwikidoc version="1.3" reference="Invitation.InvitationMailClass" locale="">
+<xwikidoc version="1.5" reference="Invitation.InvitationMailClass" locale="">
   <web>Invitation</web>
   <name>InvitationMailClass</name>
   <language/>
@@ -34,9 +34,13 @@
   <title>InvitationMailClass</title>
   <comment/>
   <minorEdit>false</minorEdit>
-  <syntaxId>xwiki/2.0</syntaxId>
+  <syntaxId>xwiki/2.1</syntaxId>
   <hidden>true</hidden>
-  <content>{{velocity}}{{info}}$services.localization.render('xe.invitation.internalDocument', ["${doc.getSpace()}.WebHome"]){{/info}}{{/velocity}}</content>
+  <content>{{velocity}}
+  #set ($linkTarget = "${doc.getSpace()}.WebHome")
+  #set ($linkTarget = $services.rendering.escape($linkTarget, 'xwiki/2.1'))
+  {{info}}$services.localization.render('xe.invitation.internalDocument', [$linkTarget]){{/info}}
+{{/velocity}}</content>
   <class>
     <name>Invitation.InvitationMailClass</name>
     <customClass/>
@@ -121,10 +125,10 @@
       <number>3</number>
       <picker>1</picker>
       <prettyName>sendingUser</prettyName>
-      <size>30</size>
       <relationalStorage>0</relationalStorage>
       <separator> </separator>
       <separators/>
+      <size>30</size>
       <sort>none</sort>
       <unmodifiable>0</unmodifiable>
       <usesList>0</usesList>

programming_language: TypeScript
filename: InvitationMemberActions.xml
change_type: ModificationType.MODIFY
diff: @@ -20,7 +20,7 @@
  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 -->
 
-<xwikidoc version="1.3" reference="Invitation.InvitationMemberActions" locale="">
+<xwikidoc version="1.5" reference="Invitation.InvitationMemberActions" locale="">
   <web>Invitation</web>
   <name>InvitationMemberActions</name>
   <language/>
@@ -34,11 +34,11 @@
   <title>$services.localization.render('xe.invitation.tools.heading')</title>
   <comment/>
   <minorEdit>false</minorEdit>
-  <syntaxId>xwiki/2.0</syntaxId>
+  <syntaxId>xwiki/2.1</syntaxId>
   <hidden>true</hidden>
-  <content>{{include reference="Invitation.InvitationCommon" /}}
+  <content>{{include reference="Invitation.InvitationCommon"/}}
 
-{{include reference="Invitation.InvitationMembersCommon" /}}
+{{include reference="Invitation.InvitationMembersCommon"/}}
 
 {{velocity}}
 #*
@@ -120,7 +120,9 @@
   #else
     ##
     ## No orders, Lets just explain what this page is for.
-    {{info}}$services.localization.render('xe.invitation.internalDocument', ["${doc.getSpace()}.WebHome"]){{/info}}
+    #set ($linkTarget = "${doc.getSpace()}.WebHome")
+    #set ($linkTarget = $services.rendering.escape($linkTarget, 'xwiki/2.1'))
+    {{info}}$services.localization.render('xe.invitation.internalDocument', [$linkTarget]){{/info}}
   #end
 #end
 ##

programming_language: PHP
filename: InvitationMembersCommon.xml
change_type: ModificationType.MODIFY
diff: @@ -20,7 +20,7 @@
  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 -->
 
-<xwikidoc version="1.3" reference="Invitation.InvitationMembersCommon" locale="">
+<xwikidoc version="1.5" reference="Invitation.InvitationMembersCommon" locale="">
   <web>Invitation</web>
   <name>InvitationMembersCommon</name>
   <language/>
@@ -34,7 +34,7 @@
   <title/>
   <comment/>
   <minorEdit>false</minorEdit>
-  <syntaxId>xwiki/2.0</syntaxId>
+  <syntaxId>xwiki/2.1</syntaxId>
   <hidden>true</hidden>
   <content>{{velocity}}
 #*
@@ -53,7 +53,9 @@
  * Macros also depend on other macros but only other macros which are contained in this script.
  *###
 #if($doc.documentReference.name == 'InvitationMembersCommon')
-  {{info}}$services.localization.render('xe.invitation.internalDocument', ["${doc.getSpace()}.WebHome"]){{/info}}
+  #set ($linkTarget = "${doc.getSpace()}.WebHome")
+  #set ($linkTarget = $services.rendering.escape($linkTarget, 'xwiki/2.1'))
+  {{info}}$services.localization.render('xe.invitation.internalDocument', [$linkTarget]){{/info}}
 #end
 ##
 #*

programming_language: CoffeeScript
filename: InvitationCommonPageTest.java
change_type: ModificationType.MODIFY
diff: @@ -89,11 +89,11 @@ void setUp() throws Exception
     void testEq0() throws Exception
     {
         this.context.setDoc(this.xwiki.getDocument(
-            new DocumentReference("xwiki", "<script>console.log</script>", "InvitationCommon"), this.context));
+            new DocumentReference("xwiki", "]]  {{noscript/}}", "InvitationCommon"), this.context));
 
         DocumentReference invitationCommonReference = new DocumentReference("xwiki", "Invitation", "InvitationCommon");
         Document document = Jsoup.parse(loadPage(invitationCommonReference).getRenderedContent(this.context));
-        assertEquals("xe.invitation.internalDocument [<script>console\\.log</script>.WebHome]",
+        assertEquals("xe.invitation.internalDocument []] {{noscript/}}.WebHome]",
             document.selectFirst(".infomessage").text());
     }
 

programming_language: Scala
filename: InvitationConfigPageTest.java
change_type: ModificationType.ADD
diff: @@ -0,0 +1,78 @@
+/*
+ * See the NOTICE file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.xwiki.invitation;
+
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.junit.jupiter.api.Test;
+import org.xwiki.model.reference.DocumentReference;
+import org.xwiki.rendering.RenderingScriptServiceComponentList;
+import org.xwiki.rendering.internal.configuration.DefaultExtendedRenderingConfiguration;
+import org.xwiki.rendering.internal.configuration.RenderingConfigClassDocumentConfigurationSource;
+import org.xwiki.rendering.internal.macro.message.InfoMessageMacro;
+import org.xwiki.test.annotation.ComponentList;
+import org.xwiki.test.page.HTML50ComponentList;
+import org.xwiki.test.page.PageTest;
+import org.xwiki.test.page.TestNoScriptMacro;
+import org.xwiki.test.page.XWikiSyntax21ComponentList;
+
+import com.xpn.xwiki.doc.XWikiDocument;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Test of {@code Invitation.InvitationConfig}.
+ *
+ * @version $Id$
+ * @since 15.0RC1
+ * @since 14.4.8
+ * @since 14.10.4
+ */
+@HTML50ComponentList
+@XWikiSyntax21ComponentList
+@RenderingScriptServiceComponentList
+@ComponentList({
+    InfoMessageMacro.class,
+    TestNoScriptMacro.class,
+    // Start - Required in addition of RenderingScriptServiceComponentList
+    DefaultExtendedRenderingConfiguration.class,
+    RenderingConfigClassDocumentConfigurationSource.class,
+    // End - Required in additional of RenderingScriptServiceComponentList
+})
+class InvitationConfigPageTest extends PageTest
+{
+    private static final DocumentReference INVITATION_CONFIG_DOCUMENT_REFERENCE =
+        new DocumentReference("xwiki", "Invitation", "InvitationConfig");
+
+    @Test
+    void escapeInfoMessageInternalDocumentParameter() throws Exception
+    {
+        XWikiDocument invitationGuestActionsDocument = loadPage(INVITATION_CONFIG_DOCUMENT_REFERENCE);
+
+        // Set up the current doc in the context so that $doc is bound in scripts
+        this.context.setDoc(
+            this.xwiki.getDocument(new DocumentReference("xwiki", "]] {{noscript/}}", "Page"), this.context));
+
+        Document document = Jsoup.parse(invitationGuestActionsDocument.getRenderedContent(this.context));
+        Element infomessage = document.selectFirst(".infomessage");
+        assertEquals("xe.invitation.internalDocument [Invitation.WebHome]", infomessage.text());
+    }
+}

programming_language: Java
filename: InvitationGuestActionsPageTest.java
change_type: ModificationType.ADD
diff: @@ -0,0 +1,88 @@
+/*
+ * See the NOTICE file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.xwiki.invitation;
+
+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.xwiki.model.reference.DocumentReference;
+import org.xwiki.rendering.RenderingScriptServiceComponentList;
+import org.xwiki.rendering.internal.configuration.DefaultExtendedRenderingConfiguration;
+import org.xwiki.rendering.internal.configuration.RenderingConfigClassDocumentConfigurationSource;
+import org.xwiki.rendering.internal.macro.message.InfoMessageMacro;
+import org.xwiki.test.annotation.ComponentList;
+import org.xwiki.test.page.HTML50ComponentList;
+import org.xwiki.test.page.PageTest;
+import org.xwiki.test.page.TestNoScriptMacro;
+import org.xwiki.test.page.XWikiSyntax21ComponentList;
+
+import com.xpn.xwiki.doc.XWikiDocument;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Test of {@code Invitation.InvitationGuestActions}.
+ *
+ * @version $Id$
+ * @since 15.0RC1
+ * @since 14.4.8
+ * @since 14.10.4
+ */
+@HTML50ComponentList
+@XWikiSyntax21ComponentList
+@RenderingScriptServiceComponentList
+@ComponentList({
+    InfoMessageMacro.class,
+    TestNoScriptMacro.class,
+    // Start - Required in addition of RenderingScriptServiceComponentList
+    DefaultExtendedRenderingConfiguration.class,
+    RenderingConfigClassDocumentConfigurationSource.class,
+    // End - Required in additional of RenderingScriptServiceComponentList
+})
+class InvitationGuestActionsPageTest extends PageTest
+{
+    private static final DocumentReference INVITATION_COMMON_DOCUMENT_REFERENCE =
+        new DocumentReference("xwiki", "Invitation", "InvitationCommon");
+
+    private static final DocumentReference INVITATION_GUEST_ACTIONS_DOCUMENT_REFERENCE =
+        new DocumentReference("xwiki", "Invitation", "InvitationGuestActions");
+
+    @BeforeEach
+    void setUp() throws Exception
+    {
+        loadPage(INVITATION_COMMON_DOCUMENT_REFERENCE);
+    }
+
+    @Test
+    void escapeInfoMessageInternalDocumentParameter() throws Exception
+    {
+        XWikiDocument invitationGuestActionsDocument = loadPage(INVITATION_GUEST_ACTIONS_DOCUMENT_REFERENCE);
+
+        // Set up the current doc in the context so that $doc is bound in scripts
+        this.context.setDoc(
+            this.xwiki.getDocument(new DocumentReference("xwiki", "]] {{noscript/}}", "Page"), this.context));
+
+        Document document = Jsoup.parse(invitationGuestActionsDocument.getRenderedContent(this.context));
+        Element infomessage = document.selectFirst(".infomessage");
+        assertEquals("xe.invitation.internalDocument []] {{noscript/}}.WebHome]", infomessage.text());
+    }
+}

programming_language: Java
filename: InvitationMailClassPageTest.java
change_type: ModificationType.ADD
diff: @@ -0,0 +1,78 @@
+/*
+ * See the NOTICE file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.xwiki.invitation;
+
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.junit.jupiter.api.Test;
+import org.xwiki.model.reference.DocumentReference;
+import org.xwiki.rendering.RenderingScriptServiceComponentList;
+import org.xwiki.rendering.internal.configuration.DefaultExtendedRenderingConfiguration;
+import org.xwiki.rendering.internal.configuration.RenderingConfigClassDocumentConfigurationSource;
+import org.xwiki.rendering.internal.macro.message.InfoMessageMacro;
+import org.xwiki.test.annotation.ComponentList;
+import org.xwiki.test.page.HTML50ComponentList;
+import org.xwiki.test.page.PageTest;
+import org.xwiki.test.page.TestNoScriptMacro;
+import org.xwiki.test.page.XWikiSyntax21ComponentList;
+
+import com.xpn.xwiki.doc.XWikiDocument;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Test of {@code Invitation.InvitationMailClass}.
+ *
+ * @version $Id$
+ * @since 15.0RC1
+ * @since 14.4.8
+ * @since 14.10.4
+ */
+@HTML50ComponentList
+@XWikiSyntax21ComponentList
+@RenderingScriptServiceComponentList
+@ComponentList({
+    InfoMessageMacro.class,
+    TestNoScriptMacro.class,
+    // Start - Required in addition of RenderingScriptServiceComponentList
+    DefaultExtendedRenderingConfiguration.class,
+    RenderingConfigClassDocumentConfigurationSource.class,
+    // End - Required in additional of RenderingScriptServiceComponentList
+})
+class InvitationMailClassPageTest extends PageTest
+{
+    private static final DocumentReference INVITATION_MAIL_CLASS_DOCUMENT_REFERENCE =
+        new DocumentReference("xwiki", "Invitation", "InvitationMailClass");
+
+    @Test
+    void escapeInfoMessageInternalDocumentParameter() throws Exception
+    {
+        XWikiDocument invitationGuestActionsDocument = loadPage(INVITATION_MAIL_CLASS_DOCUMENT_REFERENCE);
+
+        // Set up the current doc in the context so that $doc is bound in scripts
+        this.context.setDoc(
+            this.xwiki.getDocument(new DocumentReference("xwiki", "]] {{noscript/}}", "Page"), this.context));
+
+        Document document = Jsoup.parse(invitationGuestActionsDocument.getRenderedContent(this.context));
+        Element infomessage = document.selectFirst(".infomessage");
+        assertEquals("xe.invitation.internalDocument []] {{noscript/}}.WebHome]", infomessage.text());
+    }
+}

programming_language: Java
filename: InvitationMemberActionsPageTest.java
change_type: ModificationType.ADD
diff: @@ -0,0 +1,91 @@
+/*
+ * See the NOTICE file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.xwiki.invitation;
+
+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.xwiki.model.reference.DocumentReference;
+import org.xwiki.rendering.RenderingScriptServiceComponentList;
+import org.xwiki.rendering.internal.configuration.DefaultExtendedRenderingConfiguration;
+import org.xwiki.rendering.internal.configuration.RenderingConfigClassDocumentConfigurationSource;
+import org.xwiki.rendering.internal.macro.message.InfoMessageMacro;
+import org.xwiki.test.annotation.ComponentList;
+import org.xwiki.test.page.HTML50ComponentList;
+import org.xwiki.test.page.PageTest;
+import org.xwiki.test.page.TestNoScriptMacro;
+import org.xwiki.test.page.XWikiSyntax21ComponentList;
+
+import com.xpn.xwiki.doc.XWikiDocument;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Test of {@code Invitation.InvitationMemberActions}.
+ *
+ * @version $Id$
+ * @since 15.0RC1
+ * @since 14.4.8
+ * @since 14.10.4
+ */
+@HTML50ComponentList
+@XWikiSyntax21ComponentList
+@RenderingScriptServiceComponentList
+@ComponentList({
+    InfoMessageMacro.class,
+    TestNoScriptMacro.class,
+    // Start - Required in addition of RenderingScriptServiceComponentList
+    DefaultExtendedRenderingConfiguration.class,
+    RenderingConfigClassDocumentConfigurationSource.class,
+    // End - Required in additional of RenderingScriptServiceComponentList
+})
+class InvitationMemberActionsPageTest extends PageTest
+{
+    private static final DocumentReference INVITATION_COMMON_DOCUMENT_REFERENCE =
+        new DocumentReference("xwiki", "Invitation", "InvitationCommon");
+
+    private static final DocumentReference INVITATION_MEMBER_ACTIONS_DOCUMENT_REFERENCE =
+        new DocumentReference("xwiki", "Invitation", "InvitationMemberActions");
+
+    @BeforeEach
+    void setUp() throws Exception
+    {
+        loadPage(INVITATION_COMMON_DOCUMENT_REFERENCE);
+    }
+
+    @Test
+    void escapeInfoMessageInternalDocumentParameter() throws Exception
+    {
+        XWikiDocument invitationGuestActionsDocument = loadPage(INVITATION_MEMBER_ACTIONS_DOCUMENT_REFERENCE);
+
+        // Set a non-guest user as otherwise the rendering stops early.
+        this.context.setUserReference(new DocumentReference("xwiki", "XWiki", "U1"));
+
+        // Set up the current doc in the context so that $doc is bound in scripts
+        this.context.setDoc(
+            this.xwiki.getDocument(new DocumentReference("xwiki", "]] {{noscript/}}", "Page"), this.context));
+
+        Document document = Jsoup.parse(invitationGuestActionsDocument.getRenderedContent(this.context));
+        Element infomessage = document.selectFirst(".infomessage");
+        assertEquals("xe.invitation.internalDocument []] {{noscript/}}.WebHome]", infomessage.text());
+    }
+}

programming_language: Java
filename: InvitationMembersCommonPageTest.java
change_type: ModificationType.ADD
diff: @@ -0,0 +1,81 @@
+/*
+ * See the NOTICE file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.xwiki.invitation;
+
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.junit.jupiter.api.Test;
+import org.xwiki.model.reference.DocumentReference;
+import org.xwiki.rendering.RenderingScriptServiceComponentList;
+import org.xwiki.rendering.internal.configuration.DefaultExtendedRenderingConfiguration;
+import org.xwiki.rendering.internal.configuration.RenderingConfigClassDocumentConfigurationSource;
+import org.xwiki.rendering.internal.macro.message.InfoMessageMacro;
+import org.xwiki.test.annotation.ComponentList;
+import org.xwiki.test.page.HTML50ComponentList;
+import org.xwiki.test.page.PageTest;
+import org.xwiki.test.page.TestNoScriptMacro;
+import org.xwiki.test.page.XWikiSyntax21ComponentList;
+
+import com.xpn.xwiki.doc.XWikiDocument;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Test of {@code Invitation.InvitationMembersCommon}.
+ *
+ * @version $Id$
+ * @since 15.0RC1
+ * @since 14.4.8
+ * @since 14.10.4
+ */
+@HTML50ComponentList
+@XWikiSyntax21ComponentList
+@RenderingScriptServiceComponentList
+@ComponentList({
+    InfoMessageMacro.class,
+    TestNoScriptMacro.class,
+    // Start - Required in addition of RenderingScriptServiceComponentList
+    DefaultExtendedRenderingConfiguration.class,
+    RenderingConfigClassDocumentConfigurationSource.class,
+    // End - Required in additional of RenderingScriptServiceComponentList
+})
+class InvitationMembersCommonPageTest extends PageTest
+{
+    private static final DocumentReference INVITATION_MEMBER_COMMON_DOCUMENT_REFERENCE =
+        new DocumentReference("xwiki", "Invitation", "InvitationMembersCommon");
+
+    @Test
+    void escapeInfoMessageInternalDocumentParameter() throws Exception
+    {
+        XWikiDocument invitationGuestActionsDocument = loadPage(INVITATION_MEMBER_COMMON_DOCUMENT_REFERENCE);
+
+        // Set a non-guest user as otherwise the rendering stops early.
+        this.context.setUserReference(new DocumentReference("xwiki", "XWiki", "U1"));
+
+        // Set up the current doc in the context so that $doc is bound in scripts
+        this.context.setDoc(
+            this.xwiki.getDocument(new DocumentReference("xwiki", "]] {{noscript/}}", "InvitationMembersCommon"),
+                this.context));
+
+        Document document = Jsoup.parse(invitationGuestActionsDocument.getRenderedContent(this.context));
+        assertEquals("xe.invitation.internalDocument []] {{noscript/}}.WebHome]",
+            document.selectFirst(".infomessage").text());
+    }
+}

programming_language: Java
filename: pom.xml
change_type: ModificationType.MODIFY
diff: @@ -349,6 +349,13 @@
       <version>${project.version}</version>
       <scope>runtime</scope>
     </dependency>
+    <!-- The default document task analysis implementation -->
+    <dependency>
+      <groupId>org.xwiki.platform</groupId>
+      <artifactId>xwiki-platform-index-default</artifactId>
+      <version>${project.version}</version>
+      <scope>runtime</scope>
+    </dependency>
 
     <!-- ********************************************************************** -->
     <!-- Plugins, hard to install as extensions (we need to get rid of all that) -->

programming_language: HTML
filename: pom.xml
change_type: ModificationType.MODIFY
diff: @@ -640,6 +640,11 @@
       <artifactId>apache-el</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.xwiki.platform</groupId>
+      <artifactId>xwiki-platform-index-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
   </dependencies>
   <build>
     <plugins>

programming_language: HTML
filename: XARExtensionIndex.java
change_type: ModificationType.ADD
diff: @@ -0,0 +1,41 @@
+/*
+ * See the NOTICE file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.xwiki.internal.extension;
+
+import org.xwiki.component.annotation.Role;
+import org.xwiki.model.reference.DocumentReference;
+
+/**
+ * Provides operation to access to information relative to XARs provided by extensions.
+ *
+ * @version $Id$
+ * @since 15.0RC1
+ * @since 14.4.8
+ * @since 14.10.4
+ */
+@Role
+public interface XARExtensionIndex
+{
+    /**
+     * @param documentReference a document reference
+     * @return {@code true} if a given document reference is provided by an extension
+     */
+    boolean isExtensionDocument(DocumentReference documentReference);
+}

programming_language: Java
filename: AbstractDocumentsMigration.java
change_type: ModificationType.ADD
diff: @@ -0,0 +1,120 @@
+/*
+ * See the NOTICE file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.xwiki.internal.migration;
+
+import java.util.List;
+import java.util.Optional;
+
+import javax.inject.Inject;
+
+import org.slf4j.Logger;
+import org.xwiki.index.TaskManager;
+import org.xwiki.model.reference.DocumentReferenceResolver;
+
+import com.xpn.xwiki.XWikiContext;
+import com.xpn.xwiki.XWikiException;
+import com.xpn.xwiki.doc.XWikiDocument;
+import com.xpn.xwiki.store.migration.DataMigrationException;
+import com.xpn.xwiki.store.migration.hibernate.AbstractHibernateDataMigration;
+
+import static java.util.stream.Collectors.toList;
+
+/**
+ * Allow to easily queue a document analysis task on a set documents to migrate. Sub-classes need to implement two
+ * methods:
+ * <ul>
+ *     <li>{@link #selectDocuments()}: return the list of document ids to queue for migration</li>
+ *     <li>{@link #getTaskType()}: the type of the task to queue documents to</li>
+ * </ul>
+ *
+ * @version $Id$
+ * @since 15.0RC1
+ * @since 14.4.8
+ * @since 14.10.4
+ */
+public abstract class AbstractDocumentsMigration extends AbstractHibernateDataMigration
+{
+    @Inject
+    protected Logger logger;
+
+    @Inject
+    protected DocumentReferenceResolver<String> documentReferenceResolver;
+
+    @Inject
+    private TaskManager taskManager;
+
+    @Override
+    protected void hibernateMigrate() throws DataMigrationException
+    {
+        List<XWikiDocument> documents = selectDocuments()
+            .stream()
+            .map(this::convert)
+            .filter(Optional::isPresent)
+            .map(Optional::get)
+            .collect(toList());
+        logBeforeQueuingTasks(documents);
+        for (XWikiDocument document : documents) {
+            logBeforeQueuingTask(document);
+            this.taskManager.addTask(this.getXWikiContext().getWikiId(), document.getId(), getTaskType());
+        }
+    }
+
+    private Optional<XWikiDocument> convert(String documentReference)
+    {
+        XWikiContext context = getXWikiContext();
+        try {
+            return Optional.of(
+                context.getWiki().getDocument(this.documentReferenceResolver.resolve(documentReference), context));
+        } catch (XWikiException e) {
+            this.logger.error("Failed to resolve [{}]", documentReference, e);
+            return Optional.empty();
+        }
+    }
+
+    /**
+     * @return the id of the task type to queue documents to
+     */
+    protected abstract String getTaskType();
+
+    /**
+     * @return the list of document ids to migrate
+     */
+    protected abstract List<String> selectDocuments() throws DataMigrationException;
+
+    /**
+     * Prints an info log with the number of queued documents and the type of the task.
+     *
+     * @param documents the full list of documents that will be queued
+     */
+    protected void logBeforeQueuingTasks(List<XWikiDocument> documents)
+    {
+        this.logger.info("[{}] documents queued to task [{}]", documents.size(), getTaskType());
+    }
+
+    /**
+     * Prints an info logs with an individual document and well as its queued task.
+     *
+     * @param document a individual document that will be queued
+     */
+    protected void logBeforeQueuingTask(XWikiDocument document)
+    {
+        this.logger.info("document [{}] queued to task [{}]", document, getTaskType());
+    }
+}

programming_language: Java
filename: InvitationInternalDocumentParameterEscapingFixer.java
change_type: ModificationType.ADD
diff: @@ -0,0 +1,76 @@
+/*
+ * See the NOTICE file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.xwiki.internal.migration;
+
+import java.util.Objects;
+import java.util.Optional;
+import java.util.regex.Pattern;
+
+import javax.inject.Singleton;
+
+import org.xwiki.component.annotation.Component;
+import org.xwiki.rendering.syntax.Syntax;
+
+import static java.util.regex.Matcher.quoteReplacement;
+
+/**
+ * Fix a content by looking for localization of {@code xe.invitation.internalDocument} and escaping its parameter. The
+ * translation fixed by this method is initially introduced by the invitation application but the fix is localed in
+ * oldcore so that the fix is applied on pages even if the invitation application has been uninstalled.
+ *
+ * @version $Id$
+ * @since 15.0RC1
+ * @since 14.4.8
+ * @since 14.10.4
+ */
+@Component(roles = InvitationInternalDocumentParameterEscapingFixer.class)
+@Singleton
+public class InvitationInternalDocumentParameterEscapingFixer
+{
+    private static final Pattern PATTERN = Pattern.compile("(\\{\\{info}}"
+        + "\\$services\\.localization\\.render\\('xe\\.invitation\\.internalDocument', \\[)(\"[^\"]+\")(]\\)\\"
+        + "{\\{/info}})");
+
+    /**
+     * @param content the context to fix
+     * @param syntax the syntax of the context to fix (xwiki/2.0 or xwiki/2.1)
+     * @return the context with the fix applied, or {@link Optional#empty()} if nothing needs to be fixed
+     */
+    public Optional<String> fix(String content, Syntax syntax)
+    {
+        String escapedContent = PATTERN.matcher(content).replaceAll(matchResult -> {
+            if (matchResult.group(2).contains("services.rendering.escape")) {
+                return matchResult.group();
+            } else {
+                // Concatenate the various groups of the regex while wrapping the localization argument with a
+                // call to the escaping script service.
+                String format = String.format("%s$services.rendering.escape(%s, '%s')%s", matchResult.group(1),
+                    matchResult.group(2), syntax.toIdString(), matchResult.group(3));
+                return quoteReplacement(format);
+            }
+        });
+
+        if (Objects.equals(escapedContent, content)) {
+            return Optional.empty();
+        } else {
+            return Optional.of(escapedContent);
+        }
+    }
+}

programming_language: Java
filename: InvitationInternalDocumentParameterEscapingTaskConsumer.java
change_type: ModificationType.ADD
diff: @@ -0,0 +1,106 @@
+/*
+ * See the NOTICE file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.xwiki.internal.migration;
+
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+
+import org.slf4j.Logger;
+import org.xwiki.component.annotation.Component;
+import org.xwiki.index.IndexException;
+import org.xwiki.index.TaskConsumer;
+import org.xwiki.model.reference.DocumentReference;
+import org.xwiki.rendering.syntax.Syntax;
+
+import com.xpn.xwiki.XWikiContext;
+import com.xpn.xwiki.XWikiException;
+import com.xpn.xwiki.doc.XWikiDocument;
+
+/**
+ * Apply {@link InvitationInternalDocumentParameterEscapingFixer} on the documents queued by
+ * {@link R150000000XWIKI20285DataMigration} if applicable, and log the skipped ones. This translation key is initially
+ * introduced by the invitation application but the fix is localed in oldcore so that the fix is applied on pages even
+ * if the invitation application has been uninstalled.
+ *
+ * @version $Id$
+ * @since 15.0RC1
+ * @since 14.4.8
+ * @since 14.10.4
+ */
+@Component
+@Singleton
+@Named(InvitationInternalDocumentParameterEscapingTaskConsumer.HINT)
+public class InvitationInternalDocumentParameterEscapingTaskConsumer implements TaskConsumer
+{
+    /**
+     * This task consumer hint.
+     */
+    public static final String HINT = "internal-document-parameter-escaping";
+
+    @Inject
+    private Logger logger;
+
+    @Inject
+    private Provider<XWikiContext> contextProvider;
+
+    @Inject
+    private InvitationInternalDocumentParameterEscapingFixer invitationInternalDocumentParameterEscapingFixer;
+
+    @Override
+    public void consume(DocumentReference documentReference, String version) throws IndexException
+    {
+        try {
+            XWikiContext context = this.contextProvider.get();
+            task(context.getWiki().getDocument(documentReference, context));
+        } catch (XWikiException e) {
+            throw new IndexException(String.format("Failed to resolve document [%s]", documentReference), e);
+        }
+    }
+
+    private void task(XWikiDocument document)
+    {
+        Syntax syntax = document.getSyntax();
+        if (List.of(Syntax.XWIKI_2_1, Syntax.XWIKI_2_0).contains(syntax)) {
+            try {
+                this.invitationInternalDocumentParameterEscapingFixer.fix(document.getContent(), document.getSyntax())
+                    .ifPresent(content -> {
+                        document.setContent(content);
+                        try {
+                            XWikiContext context = this.contextProvider.get();
+                            context.getWiki().saveDocument(document, "Automatic bad escaping fix.", true, context);
+                            this.logger.info("[{}] successfully fixed.", document);
+                        } catch (XWikiException e) {
+                            this.logger.error("Failed to save document [{}]", document, e);
+                        }
+                    });
+            } catch (Exception e) {
+                this.logger.error("Unexpected error while fixing [{}]", document, e);
+            }
+        } else {
+            this.logger.warn(
+                "[{}] skipped because escaping for syntax [{}] is not supported. It is advised to review this file.",
+                document, syntax);
+        }
+    }
+}

programming_language: Java
filename: R150000000XWIKI20285DataMigration.java
change_type: ModificationType.ADD
diff: @@ -0,0 +1,97 @@
+/*
+ * See the NOTICE file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.xwiki.internal.migration;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.xwiki.component.annotation.Component;
+import org.xwiki.internal.extension.XARExtensionIndex;
+import org.xwiki.query.QueryException;
+
+import com.xpn.xwiki.XWiki;
+import com.xpn.xwiki.store.migration.DataMigrationException;
+import com.xpn.xwiki.store.migration.XWikiDBVersion;
+
+import static org.xwiki.query.Query.XWQL;
+
+/**
+ * Search for documents with a known invalid content produced by code generation (i.e., that cannot be fixed by editing
+ * a xar file), and apply an automatic escaping fix.
+ *
+ * @version $Id$
+ * @since 15.0RC1
+ * @since 14.4.8
+ * @since 14.10.4
+ */
+@Component
+@Named("R150000000XWIKI20285")
+@Singleton
+public class R150000000XWIKI20285DataMigration extends AbstractDocumentsMigration
+{
+    @Inject
+    private XARExtensionIndex installedXARs;
+
+    @Override
+    public String getDescription()
+    {
+        return "Patch the InvitationConfig documents with improper escaping.";
+    }
+
+    @Override
+    public XWikiDBVersion getVersion()
+    {
+        return new XWikiDBVersion(150000000);
+    }
+
+    @Override
+    protected String getTaskType()
+    {
+        return InvitationInternalDocumentParameterEscapingTaskConsumer.HINT;
+    }
+
+    @Override
+    protected List<String> selectDocuments() throws DataMigrationException
+    {
+        XWiki wiki = getXWikiContext().getWiki();
+        try {
+            // We select potentially impacted documents using like wildcards. This selection might lead to false 
+            // positive that wild be filtered out by the more accurate regex in
+            // InvitationInternalDocumentParameterEscapingFixer.
+            return wiki.getStore().getQueryManager()
+                .createQuery("where doc.content "
+                    + "like '%{{info}}%services.localization.render%xe.invitation.internalDocument%{{/info}}%'", XWQL)
+                .<String>execute()
+                .stream()
+                // Exclude document that are provided by extensions, because they are fixed using the usual xar upgrade
+                // mechanism.
+                .filter(documentReference -> !this.installedXARs.isExtensionDocument(
+                    this.documentReferenceResolver.resolve(documentReference)))
+                .collect(Collectors.toList());
+        } catch (QueryException e) {
+            throw new DataMigrationException(
+                String.format("Failed retrieve the list of all the documents for wiki [%s].", wiki.getName()), e);
+        }
+    }
+}

programming_language: Java
filename: components.txt
change_type: ModificationType.MODIFY
diff: @@ -267,3 +267,6 @@ com.xpn.xwiki.web.ViewAction
 com.xpn.xwiki.web.ViewAttachRevAction
 2000:org.xwiki.internal.attachment.validation.VoidAttachmentValidator
 org.xwiki.internal.template.ActionTemplateRequirement
+org.xwiki.internal.migration.R150000000XWIKI20285DataMigration
+org.xwiki.internal.migration.InvitationInternalDocumentParameterEscapingFixer
+org.xwiki.internal.migration.InvitationInternalDocumentParameterEscapingTaskConsumer

programming_language: Scala
filename: InvitationInternalDocumentParameterEscapingFixerTest.java
change_type: ModificationType.ADD
diff: @@ -0,0 +1,76 @@
+/*
+ * See the NOTICE file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.xwiki.internal.migration;
+
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.xwiki.rendering.syntax.Syntax;
+import org.xwiki.test.junit5.mockito.ComponentTest;
+import org.xwiki.test.junit5.mockito.InjectMockComponents;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Test of {@link InvitationInternalDocumentParameterEscapingFixer}.
+ *
+ * @version $Id$
+ */
+@ComponentTest
+class InvitationInternalDocumentParameterEscapingFixerTest
+{
+    @InjectMockComponents
+    private InvitationInternalDocumentParameterEscapingFixer fixer;
+
+    public static Stream<Arguments> fixSource()
+    {
+        return Stream.of(
+            Arguments.of(
+                "nothing to change 1\n"
+                    + "{{info}}$services.localization.render('xe.invitation.internalDocument', [$noChange]){{/info}}"
+                    + "nothing to change 2\n"
+                    + "{{info}}$services.localization.render('xe.invitation.internalDocument', [\"$change\"]){{/info}}"
+                    + "nothing to change 3",
+                Optional.of(
+                    "nothing to change 1\n"
+                        + "{{info}}$services.localization.render('xe.invitation.internalDocument', [$noChange]){{/info}}nothing to change 2\n"
+                        + "{{info}}$services.localization.render('xe.invitation.internalDocument', [$services.rendering.escape(\"$change\", 'xwiki/2.1')]){{/info}}nothing to change 3"
+                )
+            ),
+            Arguments.of(
+                "nothing to change 1\n"
+                    + "{{info}}$services.localization.render('xe.invitation.internalDocument', [$noChange]){{/info}}"
+                    + "nothing to change 2\n"
+                    + "nothing to change 3",
+                Optional.empty()
+            )
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource("fixSource")
+    void fix(String value, Optional<String> expected)
+    {
+        assertEquals(expected, this.fixer.fix(value, Syntax.XWIKI_2_1));
+    }
+}

programming_language: Java
filename: pom.xml
change_type: ModificationType.MODIFY
diff: @@ -117,7 +117,7 @@
     </dependency>
     <dependency>
       <groupId>org.xwiki.platform</groupId>
-      <artifactId>xwiki-platform-index-api</artifactId>
+      <artifactId>xwiki-platform-index-default</artifactId>
       <version>${project.version}</version>
     </dependency>
     <dependency>

programming_language: HTML