CVE-2023-32685: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: Kanboard is project management software that focuses on the Kanban methodology. Due to improper handling of elements under the `contentEditable` element, maliciously crafted clipboard content can inject arbitrary HTML tags into the DOM. A low-privileged attacker with permission to attach a document on a vulnerable Kanboard instance can trick the victim into pasting malicious screenshot data and achieve cross-site scripting if CSP is improperly configured. This issue has been patched in version 1.2.29.

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: 2023-05-30T05:15Z
Repository: https://github.com/kanboard/kanboard
Language: JavaScript
Files changed: assets/js/app.min.js, assets/js/components/screenshot.js
Commit date: 2023-05-18 19:55:47 -0700
Commit message: Avoid potential clipboard based cross-site scripting

Diff: diff --git a/assets/js/app.min.js b/assets/js/app.min.js
index fe9c250bf2..7cd6254a0a 100644
--- a/assets/js/app.min.js
+++ b/assets/js/app.min.js
@@ -163,11 +163,11 @@ function buildComponentElement(){var roles=[];var container=KB.dom('div');for(va
 roles.push(item)}}
 container.add(KB.dom('select').attr('aria-label',getAriaLabelValue()).change(onChange).for('option',roles).build());if(isLoading){container.text(' ');container.add(KB.dom('i').attr('class','fa fa-spinner fa-pulse fa-fw').build())}else if(isSuccess){container.text(' ');container.add(KB.dom('i').attr('class','fa fa-check fa-fw icon-fade-out icon-success').build())}else if(isError){container.text(' ');container.add(KB.dom('i').attr('class','fa fa-check fa-fw icon-fade-out icon-error').build())}
 return container.build()}
 this.render=function(){componentElement=buildComponentElement();containerElement.appendChild(componentElement)}});KB.component('screenshot',function(containerElement){var pasteCatcher=null;var inputElement=null;function onFileLoaded(e){createImage(e.target.result)}
 function onPaste(e){if(e.clipboardData&&e.clipboardData.items){var items=e.clipboardData.items;if(items){for(var i=0;i<items.length;i++){if(items[i].type.indexOf("image")!==-1){var blob=items[i].getAsFile();var reader=new FileReader();reader.onload=onFileLoaded;reader.readAsDataURL(blob)}}}}else{setTimeout(checkInput,100)}}
-function initialize(){destroy();if(!window.Clipboard){pasteCatcher=document.createElement('div');pasteCatcher.id='screenshot-pastezone';pasteCatcher.contentEditable=!0;pasteCatcher.style.opacity=0;pasteCatcher.style.position='fixed';pasteCatcher.style.top=0;pasteCatcher.style.right=0;pasteCatcher.style.width=0;document.body.insertBefore(pasteCatcher,document.body.firstChild);pasteCatcher.focus();document.addEventListener('click',setFocus);document.getElementById('screenshot-zone').addEventListener('click',setFocus)}
+function initialize(){destroy();if(!window.Clipboard){pasteCatcher=document.createElement('template');pasteCatcher.id='screenshot-pastezone';pasteCatcher.contentEditable=!0;pasteCatcher.style.opacity=0;pasteCatcher.style.position='fixed';pasteCatcher.style.top=0;pasteCatcher.style.right=0;pasteCatcher.style.width=0;document.body.insertBefore(pasteCatcher,document.body.firstChild);pasteCatcher.focus();document.addEventListener('click',setFocus);document.getElementById('screenshot-zone').addEventListener('click',setFocus)}
 window.addEventListener('paste',onPaste,!1)}
 function destroy(){if(KB.exists('#screenshot-pastezone')){KB.find('#screenshot-pastezone').remove()}
 document.removeEventListener('click',setFocus);pasteCatcher=null}
 function setFocus(){if(pasteCatcher!==null){pasteCatcher.focus()}}
 function checkInput(){var child=pasteCatcher.childNodes[0];if(child){if(child.tagName==='IMG'){createImage(child.src)}}
diff --git a/assets/js/components/screenshot.js b/assets/js/components/screenshot.js
index a8acd64ef2..97cd086c6d 100644
--- a/assets/js/components/screenshot.js
+++ b/assets/js/components/screenshot.js
@@ -34,11 +34,11 @@ KB.component('screenshot', function (containerElement) {
     function initialize() {
         destroy();
 
         if (! window.Clipboard) {
             // Insert the content editable at the top to avoid scrolling down in the board view
-            pasteCatcher = document.createElement('div');
+            pasteCatcher = document.createElement('template');
             pasteCatcher.id = 'screenshot-pastezone';
             pasteCatcher.contentEditable = true;
             pasteCatcher.style.opacity = 0;
             pasteCatcher.style.position = 'fixed';
             pasteCatcher.style.top = 0;

Vulnerable code: function initialize(){destroy();if(!window.Clipboard){pasteCatcher=document.createElement('div');pasteCatcher.id='screenshot-pastezone';pasteCatcher.contentEditable=!0;pasteCatcher.style.opacity=0;pasteCatcher.style.position='fixed';pasteCatcher.style.top=0;pasteCatcher.style.right=0;pasteCatcher.style.width=0;document.body.insertBefore(pasteCatcher,document.body.firstChild);pasteCatcher.focus();document.addEventListener('click',setFocus);document.getElementById('screenshot-zone').addEventListener('click',setFocus)}
            pasteCatcher = document.createElement('div');
Fixed code: function initialize(){destroy();if(!window.Clipboard){pasteCatcher=document.createElement('template');pasteCatcher.id='screenshot-pastezone';pasteCatcher.contentEditable=!0;pasteCatcher.style.opacity=0;pasteCatcher.style.position='fixed';pasteCatcher.style.top=0;pasteCatcher.style.right=0;pasteCatcher.style.width=0;document.body.insertBefore(pasteCatcher,document.body.firstChild);pasteCatcher.focus();document.addEventListener('click',setFocus);document.getElementById('screenshot-zone').addEventListener('click',setFocus)}
            pasteCatcher = document.createElement('template');