CVE-2022-41927: Cross-Site Request Forgery (CSRF) (CWE-352)
value: XWiki Platform is vulnerable to Cross-Site Request Forgery (CSRF) that may allow attackers to delete or rename tags without needing any confirmation. The problem has been patched in XWiki 13.10.7, 14.4.1 and 14.5RC1. Workarounds: It's possible to patch existing instances directly by editing the page Main.Tags and add this kind of check, in the code for renaming and for deleting: ``` #if (!$services.csrf.isTokenValid($request.get('form_token'))) #set ($discard = $response.sendError(401, "Wrong CSRF token")) #end ```
CWE description: The web application does not, or can not, sufficiently verify whether a well-formed, valid, consistent request was intentionally provided by the user who submitted the request.
CVSS v3 base score: 7.4
Published: 2022-11-23T19:15Z
Repository: https://github.com/xwiki/xwiki-platform
Language: XML
Files changed: xwiki-platform-core/xwiki-platform-tag/xwiki-platform-tag-ui/src/main/resources/Main/Tags.xml
Commit date: 2022-06-02 12:02:58 +0200
Commit message: XWIKI-19748: Wrong error code in tags

  * Fix the error code when manipulating tags

Diff: diff --git a/xwiki-platform-core/xwiki-platform-tag/xwiki-platform-tag-ui/src/main/resources/Main/Tags.xml b/xwiki-platform-core/xwiki-platform-tag/xwiki-platform-tag-ui/src/main/resources/Main/Tags.xml
index 3b30f590ddd7..5c6d9f8f35c4 100644
--- a/xwiki-platform-core/xwiki-platform-tag/xwiki-platform-tag-ui/src/main/resources/Main/Tags.xml
+++ b/xwiki-platform-core/xwiki-platform-tag/xwiki-platform-tag-ui/src/main/resources/Main/Tags.xml
@@ -110,23 +110,27 @@
     $services.localization.render('xe.tag.rename.renameto', [$htmlEscapedTag]) &lt;input type="text" name="renameTo" /&gt; &lt;span class="buttonwrapper"&gt;&lt;input type="submit" value="$services.localization.render('xe.tag.rename')" class="button"/&gt;&lt;/span&gt;&lt;span class="buttonwrapper"&gt;&lt;a href="$doc.getURL('view', "do=viewTag&amp;tag=${urlEscapedTag}")" class="secondary button"&gt;$services.localization.render('cancel')&lt;/a&gt;&lt;/span&gt;
    &lt;/div&gt;
   &lt;/form&gt;
 {{/html}}
 #elseif ($do == 'renameTag')
-  ##
-  ## Rename tag
-  ##
-  #set ($renameTo = "$!{request.get('renameTo')}")
-  #set ($success = false)
-  #if ($renameTo != '')
-    #set ($success = $xwiki.tag.renameTag($tag, $renameTo))
-  #end
-  #if ($success == true || $success == 'OK')
-   #set ($urlEscapedRenameTo = $escapetool.url($renameTo))
-   $response.sendRedirect($doc.getURL('view', "do=viewTag&amp;tag=${urlEscapedRenameTo}&amp;renamedTag=${urlEscapedTag}"))
+  #if (!$services.csrf.isTokenValid($request.get('form_token')))
+    #set ($discard = $response.sendError(401, "Wrong CSRF token"))
   #else
-    {{error}}$services.localization.render('xe.tag.rename.failure', ["//${wikiEscapedTag}//", "//${services.rendering.escape($renameTo, 'xwiki/2.1')}//"]){{/error}}
+    ##
+    ## Rename tag
+    ##
+    #set ($renameTo = "$!{request.get('renameTo')}")
+    #set ($success = false)
+    #if ($renameTo != '')
+      #set ($success = $xwiki.tag.renameTag($tag, $renameTo))
+    #end
+    #if ($success == true || $success == 'OK')
+     #set ($urlEscapedRenameTo = $escapetool.url($renameTo))
+     $response.sendRedirect($doc.getURL('view', "do=viewTag&amp;tag=${urlEscapedRenameTo}&amp;renamedTag=${urlEscapedTag}"))
+    #else
+      {{error}}$services.localization.render('xe.tag.rename.failure', ["//${wikiEscapedTag}//", "//${services.rendering.escape($renameTo, 'xwiki/2.1')}//"]){{/error}}
+    #end
   #end
 #elseif ($do == 'prepareDelete')
   ##
   ## Prepare delete tag
   ##
@@ -140,18 +144,22 @@
     &lt;span class="buttonwrapper"&gt;&lt;input type="submit" value="$services.localization.render('xe.tag.delete', [$htmlEscapedTag])" class="button"/&gt;&lt;/span&gt;&lt;span class="buttonwrapper"&gt;&lt;a href="$doc.getURL('view', "do=viewTag&amp;tag=${urlEscapedTag}")" class="secondary button"&gt;$services.localization.render('cancel')&lt;/a&gt;&lt;/span&gt;
    &lt;/div&gt;
   &lt;/form&gt;
 {{/html}}
 #elseif ($do == 'deleteTag')
-  ##
-  ## Delete tag
-  ##
-  #set ($success = $xwiki.tag.deleteTag($tag))
-  #if ($success == true || $success == 'OK')
-    $response.sendRedirect($doc.getURL('view', "deletedTag=${urlEscapedTag}"))
+  #if (!$services.csrf.isTokenValid($request.get('form_token')))
+    #set ($discard = $response.sendError(401, "Wrong CSRF token"))
   #else
-    {{error}}$services.localization.render('xe.tag.delete.failure', ["//${wikiEscapedTag}//"]){{/error}}
+    ##
+    ## Delete tag
+    ##
+    #set ($success = $xwiki.tag.deleteTag($tag))
+    #if ($success == true || $success == 'OK')
+      $response.sendRedirect($doc.getURL('view', "deletedTag=${urlEscapedTag}"))
+    #else
+      {{error}}$services.localization.render('xe.tag.delete.failure', ["//${wikiEscapedTag}//"]){{/error}}
+    #end
   #end
 #else
   ##
   ## View all tags (Tag Cloud)
   ##

Vulnerable code:   ##
  ## Rename tag
  ##
  #set ($renameTo = "$!{request.get('renameTo')}")
  #set ($success = false)
  #if ($renameTo != '')
    #set ($success = $xwiki.tag.renameTag($tag, $renameTo))
  #end
  #if ($success == true || $success == 'OK')
   #set ($urlEscapedRenameTo = $escapetool.url($renameTo))
   $response.sendRedirect($doc.getURL('view', "do=viewTag&amp;tag=${urlEscapedRenameTo}&amp;renamedTag=${urlEscapedTag}"))
    {{error}}$services.localization.render('xe.tag.rename.failure', ["//${wikiEscapedTag}//", "//${services.rendering.escape($renameTo, 'xwiki/2.1')}//"]){{/error}}
  ##
  ## Delete tag
  ##
  #set ($success = $xwiki.tag.deleteTag($tag))
  #if ($success == true || $success == 'OK')
    $response.sendRedirect($doc.getURL('view', "deletedTag=${urlEscapedTag}"))
    {{error}}$services.localization.render('xe.tag.delete.failure', ["//${wikiEscapedTag}//"]){{/error}}
Fixed code:   #if (!$services.csrf.isTokenValid($request.get('form_token')))
    #set ($discard = $response.sendError(401, "Wrong CSRF token"))
    ##
    ## Rename tag
    ##
    #set ($renameTo = "$!{request.get('renameTo')}")
    #set ($success = false)
    #if ($renameTo != '')
      #set ($success = $xwiki.tag.renameTag($tag, $renameTo))
    #end
    #if ($success == true || $success == 'OK')
     #set ($urlEscapedRenameTo = $escapetool.url($renameTo))
     $response.sendRedirect($doc.getURL('view', "do=viewTag&amp;tag=${urlEscapedRenameTo}&amp;renamedTag=${urlEscapedTag}"))
    #else
      {{error}}$services.localization.render('xe.tag.rename.failure', ["//${wikiEscapedTag}//", "//${services.rendering.escape($renameTo, 'xwiki/2.1')}//"]){{/error}}
    #end
  #if (!$services.csrf.isTokenValid($request.get('form_token')))
    #set ($discard = $response.sendError(401, "Wrong CSRF token"))
    ##
    ## Delete tag
    ##
    #set ($success = $xwiki.tag.deleteTag($tag))
    #if ($success == true || $success == 'OK')
      $response.sendRedirect($doc.getURL('view', "deletedTag=${urlEscapedTag}"))
    #else
      {{error}}$services.localization.render('xe.tag.delete.failure', ["//${wikiEscapedTag}//"]){{/error}}
    #end