CVE-2022-21802: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: The package grapesjs before 0.19.5 are vulnerable to Cross-site Scripting (XSS) due to an improper sanitization of the class name in Selector Manager.
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: 6.1
Published: 2022-07-25T14:15Z
Repository: https://github.com/artf/grapesjs
Language: TypeScript
Files changed: src/selector_manager/view/ClassTagView.ts, src/selector_manager/view/ClassTagsView.ts
Commit date: 2022-06-27 16:32:49 +0200
Commit message: Protect Selector Manager views from XSS. Closes #4411

Diff: diff --git a/src/selector_manager/view/ClassTagView.ts b/src/selector_manager/view/ClassTagView.ts
index 670b6447..913aca90 100644
--- a/src/selector_manager/view/ClassTagView.ts
+++ b/src/selector_manager/view/ClassTagView.ts
@@ -1,21 +1,20 @@
 import { View } from '../../common';
 import State from '../model/State';
+import html from '../../utils/html';
 
 const inputProp = 'contentEditable';
 
 export default class ClassTagView extends View<State> {
   template() {
     const { pfx, model, config } = this;
     const label = model.get('label') || '';
 
-    return `
+    return html`
       <span id="${pfx}checkbox" class="${pfx}tag-status" data-tag-status></span>
       <span id="${pfx}tag-label" data-tag-name>${label}</span>
-      <span id="${pfx}close" class="${pfx}tag-close" data-tag-remove>
-        ${config.iconTagRemove}
-      </span>
+      <span id="${pfx}close" class="${pfx}tag-close" data-tag-remove> $${config.iconTagRemove} </span>
     `;
   }
 
   events() {
     return {
diff --git a/src/selector_manager/view/ClassTagsView.ts b/src/selector_manager/view/ClassTagsView.ts
index 83c32eb3..a082974a 100644
--- a/src/selector_manager/view/ClassTagsView.ts
+++ b/src/selector_manager/view/ClassTagsView.ts
@@ -9,40 +9,35 @@ import Component from '../../dom_components/model/Component';
 import Selector from '../model/Selector';
 import Selectors from '../model/Selectors';
 
 export default class ClassTagsView extends View<Selector> {
   template({ labelInfo, labelHead, iconSync, iconAdd, pfx, ppfx }: any) {
-    return `
-    <div id="${pfx}up" class="${pfx}header">
-      <div id="${pfx}label" class="${pfx}header-label">${labelHead}</div>
-      <div id="${pfx}status-c" class="${pfx}header-status">
-        <span id="${pfx}input-c" data-states-c>
-          <div class="${ppfx}field ${ppfx}select">
-            <span id="${ppfx}input-holder">
-              <select id="${pfx}states" data-states></select>
-            </span>
-            <div class="${ppfx}sel-arrow">
-              <div class="${ppfx}d-s-arrow"></div>
+    return html` <div id="${pfx}up" class="${pfx}header">
+        <div id="${pfx}label" class="${pfx}header-label">${labelHead}</div>
+        <div id="${pfx}status-c" class="${pfx}header-status">
+          <span id="${pfx}input-c" data-states-c>
+            <div class="${ppfx}field ${ppfx}select">
+              <span id="${ppfx}input-holder">
+                <select id="${pfx}states" data-states></select>
+              </span>
+              <div class="${ppfx}sel-arrow">
+                <div class="${ppfx}d-s-arrow"></div>
+              </div>
             </div>
-          </div>
-        </span>
+          </span>
+        </div>
       </div>
-    </div>
-    <div id="${pfx}tags-field" class="${ppfx}field">
-      <div id="${pfx}tags-c" data-selectors></div>
-      <input id="${pfx}new" data-input/>
-      <span id="${pfx}add-tag" class="${pfx}tags-btn ${pfx}tags-btn__add" data-add>
-        ${iconAdd}
-      </span>
-      <span class="${pfx}tags-btn ${pfx}tags-btn__sync" style="display: none" data-sync-style>
-        ${iconSync}
-      </span>
-    </div>
-    <div class="${pfx}sels-info">
-      <div class="${pfx}label-sel">${labelInfo}:</div>
-      <div class="${pfx}sels" data-selected></div>
-    </div>`;
+      <div id="${pfx}tags-field" class="${ppfx}field">
+        <div id="${pfx}tags-c" data-selectors></div>
+        <input id="${pfx}new" data-input />
+        <span id="${pfx}add-tag" class="${pfx}tags-btn ${pfx}tags-btn__add" data-add> $${iconAdd} </span>
+        <span class="${pfx}tags-btn ${pfx}tags-btn__sync" style="display: none" data-sync-style> $${iconSync} </span>
+      </div>
+      <div class="${pfx}sels-info">
+        <div class="${pfx}label-sel">${labelInfo}:</div>
+        <div class="${pfx}sels" data-selected></div>
+      </div>`;
   }
 
   events() {
     return {
       'change [data-states]': 'stateChanged',

Vulnerable code:     return `
      <span id="${pfx}close" class="${pfx}tag-close" data-tag-remove>
        ${config.iconTagRemove}
      </span>
    return `
    <div id="${pfx}up" class="${pfx}header">
      <div id="${pfx}label" class="${pfx}header-label">${labelHead}</div>
      <div id="${pfx}status-c" class="${pfx}header-status">
        <span id="${pfx}input-c" data-states-c>
          <div class="${ppfx}field ${ppfx}select">
            <span id="${ppfx}input-holder">
              <select id="${pfx}states" data-states></select>
            </span>
            <div class="${ppfx}sel-arrow">
              <div class="${ppfx}d-s-arrow"></div>
          </div>
        </span>
    </div>
    <div id="${pfx}tags-field" class="${ppfx}field">
      <div id="${pfx}tags-c" data-selectors></div>
      <input id="${pfx}new" data-input/>
      <span id="${pfx}add-tag" class="${pfx}tags-btn ${pfx}tags-btn__add" data-add>
        ${iconAdd}
      </span>
      <span class="${pfx}tags-btn ${pfx}tags-btn__sync" style="display: none" data-sync-style>
        ${iconSync}
      </span>
    </div>
    <div class="${pfx}sels-info">
      <div class="${pfx}label-sel">${labelInfo}:</div>
      <div class="${pfx}sels" data-selected></div>
    </div>`;
Fixed code: import html from '../../utils/html';
    return html`
      <span id="${pfx}close" class="${pfx}tag-close" data-tag-remove> $${config.iconTagRemove} </span>
    return html` <div id="${pfx}up" class="${pfx}header">
        <div id="${pfx}label" class="${pfx}header-label">${labelHead}</div>
        <div id="${pfx}status-c" class="${pfx}header-status">
          <span id="${pfx}input-c" data-states-c>
            <div class="${ppfx}field ${ppfx}select">
              <span id="${ppfx}input-holder">
                <select id="${pfx}states" data-states></select>
              </span>
              <div class="${ppfx}sel-arrow">
                <div class="${ppfx}d-s-arrow"></div>
              </div>
          </span>
        </div>
      <div id="${pfx}tags-field" class="${ppfx}field">
        <div id="${pfx}tags-c" data-selectors></div>
        <input id="${pfx}new" data-input />
        <span id="${pfx}add-tag" class="${pfx}tags-btn ${pfx}tags-btn__add" data-add> $${iconAdd} </span>
        <span class="${pfx}tags-btn ${pfx}tags-btn__sync" style="display: none" data-sync-style> $${iconSync} </span>
      </div>
      <div class="${pfx}sels-info">
        <div class="${pfx}label-sel">${labelInfo}:</div>
        <div class="${pfx}sels" data-selected></div>
      </div>`;