CVE-2022-23647: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: Prism is a syntax highlighting library. Starting with version 1.14.0 and prior to version 1.27.0, Prism's command line plugin can be used by attackers to achieve a cross-site scripting attack. The command line plugin did not properly escape its output, leading to the input text being inserted into the DOM as HTML code. Server-side usage of Prism is not impacted. Websites that do not use the Command Line plugin are also not impacted. This bug has been fixed in v1.27.0. As a workaround, do not use the command line plugin on untrusted inputs, or sanitize all code blocks (remove all HTML code text) from all code blocks that use the command line plugin.
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-02-18T15:15Z
Repository: https://github.com/PrismJS/prism
Language: JavaScript
Files changed: plugins/command-line/prism-command-line.js, plugins/command-line/prism-command-line.min.js
Commit date: 2022-02-16 18:35:51 +0100
Commit message: Command Line: Escape markup in command line output (#3341)


Diff: diff --git a/plugins/command-line/prism-command-line.js b/plugins/command-line/prism-command-line.js
index 6437775c..415ac5dc 100644
--- a/plugins/command-line/prism-command-line.js
+++ b/plugins/command-line/prism-command-line.js
@@ -141,12 +141,14 @@
 		var codeLines = env.highlightedCode.split('\n');
 		var outputLines = commandLine.outputLines || [];
 		for (var i = 0, l = codeLines.length; i < l; i++) {
 			// Add spans to allow distinction of input/output text for styling
 			if (outputLines.hasOwnProperty(i)) {
+				// outputLines were removed from codeLines so missed out on escaping
+				// of markup so do it here.
 				codeLines[i] = '<span class="token output">'
-					+ outputLines[i] + '</span>';
+					+ Prism.util.encode(outputLines[i]) + '</span>';
 			} else {
 				codeLines[i] = '<span class="token command">'
 					+ codeLines[i] + '</span>';
 			}
 		}
diff --git a/plugins/command-line/prism-command-line.min.js b/plugins/command-line/prism-command-line.min.js
index ee6e6a47..fb2e402b 100644
--- a/plugins/command-line/prism-command-line.min.js
+++ b/plugins/command-line/prism-command-line.min.js
@@ -1 +1 @@
-!function(){if("undefined"!=typeof Prism&&"undefined"!=typeof document){var v=/(?:^|\s)command-line(?:\s|$)/,g="command-line-prompt",p="".startsWith?function(e,t){return e.startsWith(t)}:function(e,t){return 0===e.indexOf(t)},d="".endsWith?function(e,t){return e.endsWith(t)}:function(e,t){var n=e.length;return e.substring(n-t.length,n)===t};Prism.hooks.add("before-highlight",function(e){var t=N(e);if(!t.complete&&e.code){var n=e.element.parentElement;if(n&&/pre/i.test(n.nodeName)&&(v.test(n.className)||v.test(e.element.className))){var a=e.element.querySelector("."+g);a&&a.remove();var i=e.code.split("\n"),r=t.continuationLineIndicies=new Set,s=n.getAttribute("data-continuation-str");if(s&&1<i.length)for(var o=1;o<i.length;o++)i.hasOwnProperty(o-1)&&d(i[o-1],s)&&r.add(o);t.numberOfLines=i.length;var l=t.outputLines=[],m=n.getAttribute("data-output"),u=n.getAttribute("data-filter-output");if(null!==m)m.split(",").forEach(function(e){var t=e.split("-"),n=parseInt(t[0],10),a=2===t.length?parseInt(t[1],10):n;if(!isNaN(n)&&!isNaN(a)){n<1&&(n=1),a>i.length&&(a=i.length),a--;for(var r=--n;r<=a;r++)l[r]=i[r],i[r]=""}});else if(u)for(var c=0;c<i.length;c++)p(i[c],u)&&(l[c]=i[c].slice(u.length),i[c]="");e.code=i.join("\n")}else t.complete=!0}else t.complete=!0}),Prism.hooks.add("before-insert",function(e){var t=N(e);if(!t.complete){for(var n=e.highlightedCode.split("\n"),a=t.outputLines||[],r=0,i=n.length;r<i;r++)a.hasOwnProperty(r)?n[r]='<span class="token output">'+a[r]+"</span>":n[r]='<span class="token command">'+n[r]+"</span>";e.highlightedCode=n.join("\n")}}),Prism.hooks.add("complete",function(e){if(function(e){return"command-line"in(e.vars=e.vars||{})}(e)){var t=N(e);if(!t.complete){var n=e.element.parentElement;v.test(e.element.className)&&(e.element.className=e.element.className.replace(v," ")),v.test(n.className)||(n.className+=" command-line");var a,r="",i=t.numberOfLines||0,s=h("data-prompt","");if(""!==s)a='<span data-prompt="'+s+'"></span>';else a='<span data-user="'+h("data-user","user")+'" data-host="'+h("data-host","localhost")+'"></span>';for(var o=t.continuationLineIndicies||new Set,l='<span data-continuation-prompt="'+h("data-continuation-prompt",">")+'"></span>',m=0;m<i;m++)o.has(m)?r+=l:r+=a;var u=document.createElement("span");u.className=g,u.innerHTML=r;for(var c=t.outputLines||[],p=0,d=c.length;p<d;p++)if(c.hasOwnProperty(p)){var f=u.children[p];f.removeAttribute("data-user"),f.removeAttribute("data-host"),f.removeAttribute("data-prompt")}e.element.insertBefore(u,e.element.firstChild),t.complete=!0}}function h(e,t){return(n.getAttribute(e)||t).replace(/"/g,"&quot")}})}function N(e){var t=e.vars=e.vars||{};return t["command-line"]=t["command-line"]||{}}}();
\ No newline at end of file
+!function(){if("undefined"!=typeof Prism&&"undefined"!=typeof document){var v=/(?:^|\s)command-line(?:\s|$)/,g="command-line-prompt",p="".startsWith?function(e,t){return e.startsWith(t)}:function(e,t){return 0===e.indexOf(t)},d="".endsWith?function(e,t){return e.endsWith(t)}:function(e,t){var n=e.length;return e.substring(n-t.length,n)===t};Prism.hooks.add("before-highlight",function(e){var t=N(e);if(!t.complete&&e.code){var n=e.element.parentElement;if(n&&/pre/i.test(n.nodeName)&&(v.test(n.className)||v.test(e.element.className))){var a=e.element.querySelector("."+g);a&&a.remove();var i=e.code.split("\n"),r=t.continuationLineIndicies=new Set,s=n.getAttribute("data-continuation-str");if(s&&1<i.length)for(var o=1;o<i.length;o++)i.hasOwnProperty(o-1)&&d(i[o-1],s)&&r.add(o);t.numberOfLines=i.length;var l=t.outputLines=[],m=n.getAttribute("data-output"),u=n.getAttribute("data-filter-output");if(null!==m)m.split(",").forEach(function(e){var t=e.split("-"),n=parseInt(t[0],10),a=2===t.length?parseInt(t[1],10):n;if(!isNaN(n)&&!isNaN(a)){n<1&&(n=1),a>i.length&&(a=i.length),a--;for(var r=--n;r<=a;r++)l[r]=i[r],i[r]=""}});else if(u)for(var c=0;c<i.length;c++)p(i[c],u)&&(l[c]=i[c].slice(u.length),i[c]="");e.code=i.join("\n")}else t.complete=!0}else t.complete=!0}),Prism.hooks.add("before-insert",function(e){var t=N(e);if(!t.complete){for(var n=e.highlightedCode.split("\n"),a=t.outputLines||[],r=0,i=n.length;r<i;r++)a.hasOwnProperty(r)?n[r]='<span class="token output">'+Prism.util.encode(a[r])+"</span>":n[r]='<span class="token command">'+n[r]+"</span>";e.highlightedCode=n.join("\n")}}),Prism.hooks.add("complete",function(e){if(function(e){return"command-line"in(e.vars=e.vars||{})}(e)){var t=N(e);if(!t.complete){var n=e.element.parentElement;v.test(e.element.className)&&(e.element.className=e.element.className.replace(v," ")),v.test(n.className)||(n.className+=" command-line");var a,r="",i=t.numberOfLines||0,s=h("data-prompt","");if(""!==s)a='<span data-prompt="'+s+'"></span>';else a='<span data-user="'+h("data-user","user")+'" data-host="'+h("data-host","localhost")+'"></span>';for(var o=t.continuationLineIndicies||new Set,l='<span data-continuation-prompt="'+h("data-continuation-prompt",">")+'"></span>',m=0;m<i;m++)o.has(m)?r+=l:r+=a;var u=document.createElement("span");u.className=g,u.innerHTML=r;for(var c=t.outputLines||[],p=0,d=c.length;p<d;p++)if(c.hasOwnProperty(p)){var f=u.children[p];f.removeAttribute("data-user"),f.removeAttribute("data-host"),f.removeAttribute("data-prompt")}e.element.insertBefore(u,e.element.firstChild),t.complete=!0}}function h(e,t){return(n.getAttribute(e)||t).replace(/"/g,"&quot")}})}function N(e){var t=e.vars=e.vars||{};return t["command-line"]=t["command-line"]||{}}}();
\ No newline at end of file

Vulnerable code: 					+ outputLines[i] + '</span>';
!function(){if("undefined"!=typeof Prism&&"undefined"!=typeof document){var v=/(?:^|\s)command-line(?:\s|$)/,g="command-line-prompt",p="".startsWith?function(e,t){return e.startsWith(t)}:function(e,t){return 0===e.indexOf(t)},d="".endsWith?function(e,t){return e.endsWith(t)}:function(e,t){var n=e.length;return e.substring(n-t.length,n)===t};Prism.hooks.add("before-highlight",function(e){var t=N(e);if(!t.complete&&e.code){var n=e.element.parentElement;if(n&&/pre/i.test(n.nodeName)&&(v.test(n.className)||v.test(e.element.className))){var a=e.element.querySelector("."+g);a&&a.remove();var i=e.code.split("\n"),r=t.continuationLineIndicies=new Set,s=n.getAttribute("data-continuation-str");if(s&&1<i.length)for(var o=1;o<i.length;o++)i.hasOwnProperty(o-1)&&d(i[o-1],s)&&r.add(o);t.numberOfLines=i.length;var l=t.outputLines=[],m=n.getAttribute("data-output"),u=n.getAttribute("data-filter-output");if(null!==m)m.split(",").forEach(function(e){var t=e.split("-"),n=parseInt(t[0],10),a=2===t.length?parseInt(t[1],10):n;if(!isNaN(n)&&!isNaN(a)){n<1&&(n=1),a>i.length&&(a=i.length),a--;for(var r=--n;r<=a;r++)l[r]=i[r],i[r]=""}});else if(u)for(var c=0;c<i.length;c++)p(i[c],u)&&(l[c]=i[c].slice(u.length),i[c]="");e.code=i.join("\n")}else t.complete=!0}else t.complete=!0}),Prism.hooks.add("before-insert",function(e){var t=N(e);if(!t.complete){for(var n=e.highlightedCode.split("\n"),a=t.outputLines||[],r=0,i=n.length;r<i;r++)a.hasOwnProperty(r)?n[r]='<span class="token output">'+a[r]+"</span>":n[r]='<span class="token command">'+n[r]+"</span>";e.highlightedCode=n.join("\n")}}),Prism.hooks.add("complete",function(e){if(function(e){return"command-line"in(e.vars=e.vars||{})}(e)){var t=N(e);if(!t.complete){var n=e.element.parentElement;v.test(e.element.className)&&(e.element.className=e.element.className.replace(v," ")),v.test(n.className)||(n.className+=" command-line");var a,r="",i=t.numberOfLines||0,s=h("data-prompt","");if(""!==s)a='<span data-prompt="'+s+'"></span>';else a='<span data-user="'+h("data-user","user")+'" data-host="'+h("data-host","localhost")+'"></span>';for(var o=t.continuationLineIndicies||new Set,l='<span data-continuation-prompt="'+h("data-continuation-prompt",">")+'"></span>',m=0;m<i;m++)o.has(m)?r+=l:r+=a;var u=document.createElement("span");u.className=g,u.innerHTML=r;for(var c=t.outputLines||[],p=0,d=c.length;p<d;p++)if(c.hasOwnProperty(p)){var f=u.children[p];f.removeAttribute("data-user"),f.removeAttribute("data-host"),f.removeAttribute("data-prompt")}e.element.insertBefore(u,e.element.firstChild),t.complete=!0}}function h(e,t){return(n.getAttribute(e)||t).replace(/"/g,"&quot")}})}function N(e){var t=e.vars=e.vars||{};return t["command-line"]=t["command-line"]||{}}}();
Fixed code: 				// outputLines were removed from codeLines so missed out on escaping
				// of markup so do it here.
					+ Prism.util.encode(outputLines[i]) + '</span>';
!function(){if("undefined"!=typeof Prism&&"undefined"!=typeof document){var v=/(?:^|\s)command-line(?:\s|$)/,g="command-line-prompt",p="".startsWith?function(e,t){return e.startsWith(t)}:function(e,t){return 0===e.indexOf(t)},d="".endsWith?function(e,t){return e.endsWith(t)}:function(e,t){var n=e.length;return e.substring(n-t.length,n)===t};Prism.hooks.add("before-highlight",function(e){var t=N(e);if(!t.complete&&e.code){var n=e.element.parentElement;if(n&&/pre/i.test(n.nodeName)&&(v.test(n.className)||v.test(e.element.className))){var a=e.element.querySelector("."+g);a&&a.remove();var i=e.code.split("\n"),r=t.continuationLineIndicies=new Set,s=n.getAttribute("data-continuation-str");if(s&&1<i.length)for(var o=1;o<i.length;o++)i.hasOwnProperty(o-1)&&d(i[o-1],s)&&r.add(o);t.numberOfLines=i.length;var l=t.outputLines=[],m=n.getAttribute("data-output"),u=n.getAttribute("data-filter-output");if(null!==m)m.split(",").forEach(function(e){var t=e.split("-"),n=parseInt(t[0],10),a=2===t.length?parseInt(t[1],10):n;if(!isNaN(n)&&!isNaN(a)){n<1&&(n=1),a>i.length&&(a=i.length),a--;for(var r=--n;r<=a;r++)l[r]=i[r],i[r]=""}});else if(u)for(var c=0;c<i.length;c++)p(i[c],u)&&(l[c]=i[c].slice(u.length),i[c]="");e.code=i.join("\n")}else t.complete=!0}else t.complete=!0}),Prism.hooks.add("before-insert",function(e){var t=N(e);if(!t.complete){for(var n=e.highlightedCode.split("\n"),a=t.outputLines||[],r=0,i=n.length;r<i;r++)a.hasOwnProperty(r)?n[r]='<span class="token output">'+Prism.util.encode(a[r])+"</span>":n[r]='<span class="token command">'+n[r]+"</span>";e.highlightedCode=n.join("\n")}}),Prism.hooks.add("complete",function(e){if(function(e){return"command-line"in(e.vars=e.vars||{})}(e)){var t=N(e);if(!t.complete){var n=e.element.parentElement;v.test(e.element.className)&&(e.element.className=e.element.className.replace(v," ")),v.test(n.className)||(n.className+=" command-line");var a,r="",i=t.numberOfLines||0,s=h("data-prompt","");if(""!==s)a='<span data-prompt="'+s+'"></span>';else a='<span data-user="'+h("data-user","user")+'" data-host="'+h("data-host","localhost")+'"></span>';for(var o=t.continuationLineIndicies||new Set,l='<span data-continuation-prompt="'+h("data-continuation-prompt",">")+'"></span>',m=0;m<i;m++)o.has(m)?r+=l:r+=a;var u=document.createElement("span");u.className=g,u.innerHTML=r;for(var c=t.outputLines||[],p=0,d=c.length;p<d;p++)if(c.hasOwnProperty(p)){var f=u.children[p];f.removeAttribute("data-user"),f.removeAttribute("data-host"),f.removeAttribute("data-prompt")}e.element.insertBefore(u,e.element.firstChild),t.complete=!0}}function h(e,t){return(n.getAttribute(e)||t).replace(/"/g,"&quot")}})}function N(e){var t=e.vars=e.vars||{};return t["command-line"]=t["command-line"]||{}}}();