Vulnerability: In Wiki.js before 2.4.107, there is a stored cross-site scripting through template injection. This vulnerability exists due to an insecure validation mechanism intended to insert v-pre tags into rendered HTML elements which contain curly-braces. By creating a crafted wiki page, a malicious Wiki.js user may stage a stored cross-site scripting attack. This allows the attacker to execute malicious JavaScript when the page is viewed by other users. This has been patched in 2.4.107.
Vulnerability ID: CVE-2020-4052
CWE: CWE-79
Severity score: 2.9
Project: https://github.com/Requarks/wiki
Published: 2020-06-16
File: server/modules/rendering/html-core/renderer.js
Commit message: Merge pull request from GHSA-9jgg-4xj2-vjjj
Commit author: Nicolas Giard

Vulnerable version (before fix):
 const uslug = require('uslug') const pageHelper = require('../../../helpers/page') const URL = require('url').URL  /* global WIKI */  module.exports = { module.exports = {       headers.push(headerSlug)     })      // --------------------------------     // Escape mustache expresions     // -------------------------------- module.exports = {       list.forEach(item => {         if (item.type === 'text') {           const rawText = $(item).text()           if (rawText.indexOf('{{') >= 0 && rawText.indexOf('}}') > 1) {             $(item).parent().attr('v-pre', true)           }         } else {

Fixed version (after fix):
 const uslug = require('uslug') const pageHelper = require('../../../helpers/page') const URL = require('url').URL  const mustacheRegExp = /(\{|&#x7b;?){2}(.+?)(\}|&#x7d;?){2}/i  /* global WIKI */  module.exports = { module.exports = {       headers.push(headerSlug)     })      // --------------------------------     // Wrap root text nodes     // --------------------------------      $('body').contents().toArray().forEach(item => {       if (item.type === 'text' && item.parent.name === 'body') {         $(item).wrap('<div></div>')       }     })      // --------------------------------     // Escape mustache expresions     // -------------------------------- module.exports = {       list.forEach(item => {         if (item.type === 'text') {           const rawText = $(item).text()           if (mustacheRegExp.test(rawText)) {             $(item).parent().attr('v-pre', true)           }         } else {

Patch:
patch: @@ -4,6 +4,8 @@ const uslug = require('uslug')
 const pageHelper = require('../../../helpers/page')
 const URL = require('url').URL
 
+const mustacheRegExp = /(\{|&#x7b;?){2}(.+?)(\}|&#x7d;?){2}/i
+
 /* global WIKI */
 
 module.exports = {
@@ -230,6 +232,16 @@ module.exports = {
       headers.push(headerSlug)
     })
 
+    // --------------------------------
+    // Wrap root text nodes
+    // --------------------------------
+
+    $('body').contents().toArray().forEach(item => {
+      if (item.type === 'text' && item.parent.name === 'body') {
+        $(item).wrap('<div></div>')
+      }
+    })
+
     // --------------------------------
     // Escape mustache expresions
     // --------------------------------
@@ -239,7 +251,7 @@ module.exports = {
       list.forEach(item => {
         if (item.type === 'text') {
           const rawText = $(item).text()
-          if (rawText.indexOf('{{') >= 0 && rawText.indexOf('}}') > 1) {
+          if (mustacheRegExp.test(rawText)) {
             $(item).parent().attr('v-pre', true)
           }
         } else {
path: server/modules/rendering/html-core/renderer.js