CVE-2024-22411: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: Avo is a framework to create admin panels for Ruby on Rails apps. In Avo 3 pre12, any HTML inside text that is passed to `error` or `succeed` in an `Avo::BaseAction` subclass will be rendered directly without sanitization in the toast/notification that appears in the UI on Action completion. A malicious user could exploit this vulnerability to trigger a cross site scripting attack on an unsuspecting user. This issue has been addressed in the 3.3.0 and 2.47.0 releases of Avo. Users are advised to upgrade.
CWE description: The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
CVSS v3 base score: 5.4
Published: 2024-01-16T22:15Z
Repository: https://github.com/avo-hq/avo
Language: JSON
Files changed: app/javascript/js/controllers/fields/key_value_controller.js, package.json, yarn.lock
Commit date: 2024-01-12 14:47:25 +0200
Commit message: security: sanitize key_value field content (#2357)


Diff: diff --git a/app/javascript/js/controllers/fields/key_value_controller.js b/app/javascript/js/controllers/fields/key_value_controller.js
index e048f22b..35619ce4 100644
--- a/app/javascript/js/controllers/fields/key_value_controller.js
+++ b/app/javascript/js/controllers/fields/key_value_controller.js
@@ -1,6 +1,7 @@
 /* eslint-disable max-len */
+import * as DOMPurify from 'dompurify'
 import { Controller } from '@hotwired/stimulus'
 import { castBoolean } from '../../helpers/cast_boolean'
 
 export default class extends Controller {
   static targets = ['input', 'controller', 'rows']
@@ -78,11 +79,11 @@ export default class extends Controller {
   updateKeyValueComponent() {
     let result = ''
     let index = 0
     this.fieldValue.forEach((row) => {
       const [key, value] = row
-      result += this.interpolatedRow(key, value, index)
+      result += this.interpolatedRow(DOMPurify.sanitize(key), DOMPurify.sanitize(value), index)
       index++
     })
     this.rowsTarget.innerHTML = result
     window.initTippy()
   }
diff --git a/package.json b/package.json
index d48f1a7e..245ad1f5 100644
--- a/package.json
+++ b/package.json
@@ -32,10 +32,11 @@
     "chart.js": "^3.9.1",
     "chartkick": "^4.2.0",
     "codemirror": "5.59.1",
     "core-js": "^3.35.0",
     "css-loader": "^6.9.0",
+    "dompurify": "^3.0.8",
     "easymde": "^2.18.0",
     "el-transition": "^0.0.7",
     "esbuild": "^0.14.54",
     "eslint-config-airbnb": "^19.0.4",
     "eslint-import-resolver-alias": "^1.1.2",
diff --git a/yarn.lock b/yarn.lock
index b53787bc..69e8d2ae 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2088,10 +2088,15 @@ doctrine@^3.0.0:
   resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
   integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
   dependencies:
     esutils "^2.0.2"
 
+dompurify@^3.0.8:
+  version "3.0.8"
+  resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.0.8.tgz#e0021ab1b09184bc8af7e35c7dd9063f43a8a437"
+  integrity sha512-b7uwreMYL2eZhrSCRC4ahLTeZcPZxSmYfmcQGXGkXiZSNW1X85v+SDM5KsWcpivIiUBH47Ji7NtyUdpLeF5JZQ==
+
 easymde@^2.18.0:
   version "2.18.0"
   resolved "https://registry.yarnpkg.com/easymde/-/easymde-2.18.0.tgz#ff1397d07329b1a7b9187d2d0c20766fa16b3b1b"
   integrity sha512-IxVVUxNWIoXLeqtBU4BLc+eS/ScYhT1Dcb6yF5Wchoj1iXAV+TIIDWx+NCaZhY7RcSHqDPKllbYq7nwGKILnoA==
   dependencies:

Vulnerable code:       result += this.interpolatedRow(key, value, index)
Fixed code: import * as DOMPurify from 'dompurify'
      result += this.interpolatedRow(DOMPurify.sanitize(key), DOMPurify.sanitize(value), index)
    "dompurify": "^3.0.8",
dompurify@^3.0.8:
  version "3.0.8"
  resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.0.8.tgz#e0021ab1b09184bc8af7e35c7dd9063f43a8a437"
  integrity sha512-b7uwreMYL2eZhrSCRC4ahLTeZcPZxSmYfmcQGXGkXiZSNW1X85v+SDM5KsWcpivIiUBH47Ji7NtyUdpLeF5JZQ==