CVE-2018-25053: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: A vulnerability was found in moappi Json2html up to 1.1.x and classified as problematic. This issue affects some unknown processing of the file json2html.js. The manipulation leads to cross site scripting. The attack may be initiated remotely. Upgrading to version 1.2.0 is able to address this issue. The name of the patch is 2d3d24d971b19a8ed1fb823596300b9835d55801. It is recommended to upgrade the affected component. The associated identifier of this vulnerability is VDB-216959.
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-12-28T12:15Z
Repository: https://github.com/moappi/json2html
Language: JavaScript
Files changed: README.md, json2html.js, package.json, test/index.html, test/test.text.js
Commit date: 2018-02-09 12:32:32 -0800
Commit message: Added Text Attribute

To help combat XSS attackes the text attribute will add text only to elements rather than html

Diff: diff --git a/README.md b/README.md
index 88e2499..a88b0f7 100644
--- a/README.md
+++ b/README.md
@@ -26,11 +26,11 @@ Example
 --------------
 Transform (template)
 ```javascript
 var transform = 
  {"<>": "li", "id":"${id}", "html":[
-	{"<>": "span", "html": "${name} (${year})"}
+	{"<>": "span", "text": "${name} (${year})"}
   ]};		
 ```
 Plus JSON Data
 ```javascript
 var data = 
diff --git a/examples/default.html b/examples/default.html
deleted file mode 100644
index 78955e5..0000000
--- a/examples/default.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<html>
- <head>
-	<title> json2html examples </title>
-
-	<!-- Add JSON2HTML -->
-	<script type="text/javascript" src="../json2html.js"></script>
-	
- </head>
- <body>
-	<!-- Run the tests -->
-	<script type="text/javascript" src="example.nested.js"></script>
-	<script type="text/javascript" src="example.shorthand.js"></script>
-	<script type="text/javascript" src="example.escape.js"></script>
- </body>
-</html>
diff --git a/json2html.js b/json2html.js
index cf0666c..c20b6c7 100644
--- a/json2html.js
+++ b/json2html.js
@@ -1,6 +1,6 @@
-//Copyright (c) 2016 Crystalline Technologies
+//Copyright (c) 2018 Crystalline Technologies
 //
 //  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'),
 //  to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 //  and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 //
@@ -133,18 +133,65 @@ var json2html = {
 				//Look into the properties of this transform
 				for (var key in transform) {
 
 					switch(key) {
 						
-						//LEGACY support for tag
+						//DEPRECATED (use <> instead)
 						case 'tag':
+
+						//HTML element to render
 						case '<>':
 							//Do nothing as we have already created the element
 						break;
+						
+						//Encode as text
+						case 'text':
+							//Get the transform value associated with this key
+							var _transform = transform[key];
+							
+							//Determine what kind of object this is
+							// array => NOT SUPPORTED
+							// other => text
+							if(json2html._isArray(_transform)) {
+                                //NOT Supported
+							} else if(typeof _transform === 'function') {
+								
+								//Get the result from the function
+								var temp = _transform.call(obj, obj, index);
+								
+								//Don't allow arrays as return objects from functions
+    							if(!json2html._isArray(temp)) {
+
+    								//Determine what type of object was returned
+    								switch(typeof temp){
+                                    
+    									//Not supported for text
+    									case 'function':
+    									case 'undefined':
+    									case 'object':
+    									break; 
+    									
+    									//Append as text
+    									// string, number, boolean
+    									default:
+    										//Insure we encode as text first
+    										children.html += json2html.toText(temp);
+    									break;
+    								}
+    							}
+							} else {
+								
+								//Get the encoded text associated with this element
+								html = json2html.toText( json2html._getValue(obj,transform,key,index) );
+							}
+						break;
 
-						//LEGACY support for children
+						//DEPRECATED (use HTML instead)
 						case 'children':
+
+						//Encode as HTML
+						// accepts Array of children, functions, string, number, boolean
 						case 'html':
 
 							//Get the transform value associated with this key
 							// added as key could be children or html
 							var _transform = transform[key];
@@ -158,35 +205,39 @@ var json2html = {
 								children = json2html._append(children,json2html._apply(obj, _transform, index, options));
 							} else if(typeof _transform === 'function') {
 								
 								//Get the result from the function
 								var temp = _transform.call(obj, obj, index);
-
-								//Determine what type of object was returned
-								switch(typeof temp){
-
-									//Only returned by json2html.transform or $.json2html calls
-									case 'object':
-										//make sure this object is a valid json2html response object
-										// we ignore all other objects (since we don't know how to represent them in html)
-										if(temp.html !== undefined && temp.events !== undefined) children = json2html._append(children,temp);
-									break;
-									
-									//Not supported
-									case 'function':
-									case 'undefined':
-									break; 
-
-									//Append to html
-									// string, number, boolean
-									default:
-										children.html += temp;
-									break;
-								}
+                                
+                                //Don't allow arrays as return objects from functions
+                                if(!json2html._isArray(temp)) {
+                                    
+    								//Determine what type of object was returned
+    								switch(typeof temp){
+    
+    									//Only returned by json2html.transform or $.json2html calls
+    									case 'object':
+    										//make sure this object is a valid json2html response object
+    										// we ignore all other objects (since we don't know how to represent them in html)
+    										if(temp.html !== undefined && temp.events !== undefined) children = json2html._append(children,temp);
+    									break;
+    									
+    									//Not supported
+    									case 'function':
+    									case 'undefined':
+    									break; 
+    
+    									//Append to html
+    									// string, number, boolean
+    									default:
+    										children.html += temp;
+    									break;
+    								}
+                                }
 							} else {
 								
-								//Create the html attribute for this element
+								//Get the HTML associated with this element
 								html = json2html._getValue(obj,transform,key,index);
 							}
 						break;
 
 						default:
@@ -317,10 +368,21 @@ var json2html = {
 			out = val;
 		}
 
 		return(out);
 	},
+
+	//Encode the html to text
+	'toText':function(html) {
+		return html
+			.replace(/&/g, '&amp;')
+			.replace(/</g, '&lt;')
+			.replace(/>/g, '&gt;')
+			.replace(/\"/g, '&quot;')
+			.replace(/\'/g, '&#39;')
+			.replace(/\//g, '&#x2F;');
+	},
 	
 	//Tokenizer
 	'_tokenizer':function( tokenizers, doBuild ){
 
 		if( !(this instanceof json2html._tokenizer ) )
diff --git a/package.json b/package.json
index c0c6c0d..890d184 100644
--- a/package.json
+++ b/package.json
@@ -4,11 +4,11 @@
     "email": "info@json2html.com",
     "url": "http://json2html.com"
   },
   "name": "json2html",
   "description": "json2html - HTML Templating",
-  "version": "1.0.1",
+  "version": "1.1.0",
   "homepage": "http://json2html.com",
   "repository": {
     "url": "https://github.com/moappi/json2html.git"
   },
   "keywords": [
diff --git a/test/index.html b/test/index.html
new file mode 100644
index 0000000..e844b39
--- /dev/null
+++ b/test/index.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+ <head>
+	<title> json2html tests </title>
+
+	<!-- Add JSON2HTML -->
+	<script type="text/javascript" src="../json2html.js"></script>
+	
+ </head>
+ <body>
+	<!-- Run the tests -->
+	<script type="text/javascript" src="test.nested.js"></script>
+	<script type="text/javascript" src="test.shorthand.js"></script>
+	<script type="text/javascript" src="test.escape.js"></script>
+	<script type="text/javascript" src="test.text.js"></script>
+ </body>
+</html>
diff --git a/examples/example.escape.js b/test/test.escape.js
similarity index 100%
rename from examples/example.escape.js
rename to test/test.escape.js
diff --git a/examples/example.nested.js b/test/test.nested.js
similarity index 100%
rename from examples/example.nested.js
rename to test/test.nested.js
diff --git a/examples/example.shorthand.js b/test/test.shorthand.js
similarity index 100%
rename from examples/example.shorthand.js
rename to test/test.shorthand.js
diff --git a/test/test.text.js b/test/test.text.js
new file mode 100644
index 0000000..0f6278d
--- /dev/null
+++ b/test/test.text.js
@@ -0,0 +1,27 @@
+
+(function() {
+    
+	//Test the handling of quoted strings 
+    var test_data = [
+		{"text":"<script>alert(0);</script> no alert"},
+		{"text":"<strong>non highlighted</strong>"},
+		{"text":"& (ampersand) : ' (single quote) : \" (double quote) "}
+	];
+
+    var transform = [
+        {"<>":"div", "text":"${text}"},
+        {"<>":"div","text":[
+            {"<>":"span","text":"this shouldn't be rendered"}
+        ]},
+        {"<>":"div", "text":function(){
+            return(this.text);
+        }},
+        {"<>":"div", "text":function(){
+            return(["not rendered"]);
+        }}
+    ];
+        
+    var html = json2html.transform(test_data, transform);
+
+    document.write('<h1>Text Encoding Test</h1>'+ html);
+})();

Vulnerable code: 	{"<>": "span", "html": "${name} (${year})"}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
 <head>
	<title> json2html examples </title>

	<!-- Add JSON2HTML -->
	<script type="text/javascript" src="../json2html.js"></script>
	
 </head>
 <body>
	<!-- Run the tests -->
	<script type="text/javascript" src="example.nested.js"></script>
	<script type="text/javascript" src="example.shorthand.js"></script>
	<script type="text/javascript" src="example.escape.js"></script>
 </body>
</html>
//Copyright (c) 2016 Crystalline Technologies
						//LEGACY support for tag
						//LEGACY support for children

								//Determine what type of object was returned
								switch(typeof temp){

									//Only returned by json2html.transform or $.json2html calls
									case 'object':
										//make sure this object is a valid json2html response object
										// we ignore all other objects (since we don't know how to represent them in html)
										if(temp.html !== undefined && temp.events !== undefined) children = json2html._append(children,temp);
									break;
									
									//Not supported
									case 'function':
									case 'undefined':
									break; 

									//Append to html
									// string, number, boolean
									default:
										children.html += temp;
									break;
								}
								//Create the html attribute for this element
  "version": "1.0.1",
Fixed code: 	{"<>": "span", "text": "${name} (${year})"}
//Copyright (c) 2018 Crystalline Technologies
						//DEPRECATED (use <> instead)

						//HTML element to render
						
						//Encode as text
						case 'text':
							//Get the transform value associated with this key
							var _transform = transform[key];
							
							//Determine what kind of object this is
							// array => NOT SUPPORTED
							// other => text
							if(json2html._isArray(_transform)) {
                                //NOT Supported
							} else if(typeof _transform === 'function') {
								
								//Get the result from the function
								var temp = _transform.call(obj, obj, index);
								
								//Don't allow arrays as return objects from functions
    							if(!json2html._isArray(temp)) {

    								//Determine what type of object was returned
    								switch(typeof temp){
                                    
    									//Not supported for text
    									case 'function':
    									case 'undefined':
    									case 'object':
    									break; 
    									
    									//Append as text
    									// string, number, boolean
    									default:
    										//Insure we encode as text first
    										children.html += json2html.toText(temp);
    									break;
    								}
    							}
							} else {
								
								//Get the encoded text associated with this element
								html = json2html.toText( json2html._getValue(obj,transform,key,index) );
							}
						break;
						//DEPRECATED (use HTML instead)

						//Encode as HTML
						// accepts Array of children, functions, string, number, boolean
                                
                                //Don't allow arrays as return objects from functions
                                if(!json2html._isArray(temp)) {
                                    
    								//Determine what type of object was returned
    								switch(typeof temp){
    
    									//Only returned by json2html.transform or $.json2html calls
    									case 'object':
    										//make sure this object is a valid json2html response object
    										// we ignore all other objects (since we don't know how to represent them in html)
    										if(temp.html !== undefined && temp.events !== undefined) children = json2html._append(children,temp);
    									break;
    									
    									//Not supported
    									case 'function':
    									case 'undefined':
    									break; 
    
    									//Append to html
    									// string, number, boolean
    									default:
    										children.html += temp;
    									break;
    								}
                                }
								//Get the HTML associated with this element

	//Encode the html to text
	'toText':function(html) {
		return html
			.replace(/&/g, '&amp;')
			.replace(/</g, '&lt;')
			.replace(/>/g, '&gt;')
			.replace(/\"/g, '&quot;')
			.replace(/\'/g, '&#39;')
			.replace(/\//g, '&#x2F;');
	},
  "version": "1.1.0",
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
 <head>
	<title> json2html tests </title>

	<!-- Add JSON2HTML -->
	<script type="text/javascript" src="../json2html.js"></script>
	
 </head>
 <body>
	<!-- Run the tests -->
	<script type="text/javascript" src="test.nested.js"></script>
	<script type="text/javascript" src="test.shorthand.js"></script>
	<script type="text/javascript" src="test.escape.js"></script>
	<script type="text/javascript" src="test.text.js"></script>
 </body>
</html>

(function() {
    
	//Test the handling of quoted strings 
    var test_data = [
		{"text":"<script>alert(0);</script> no alert"},
		{"text":"<strong>non highlighted</strong>"},
		{"text":"& (ampersand) : ' (single quote) : \" (double quote) "}
	];

    var transform = [
        {"<>":"div", "text":"${text}"},
        {"<>":"div","text":[
            {"<>":"span","text":"this shouldn't be rendered"}
        ]},
        {"<>":"div", "text":function(){
            return(this.text);
        }},
        {"<>":"div", "text":function(){
            return(["not rendered"]);
        }}
    ];
        
    var html = json2html.transform(test_data, transform);

    document.write('<h1>Text Encoding Test</h1>'+ html);
})();