CVE-2012-6662: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: Cross-site scripting (XSS) vulnerability in the default content option in jquery.ui.tooltip.js in the Tooltip widget in jQuery UI before 1.10.0 allows remote attackers to inject arbitrary web script or HTML via the title attribute, which is not properly handled in the autocomplete combo box demo.
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.
Published: 2014-11-24T16:59Z
Repository: https://github.com/jquery/jquery-ui
Language: JavaScript
Files changed: demos/autocomplete/combobox.html, tests/unit/tooltip/tooltip_options.js, ui/jquery.ui.tooltip.js
Commit date: 2012-11-27 11:21:33 -0500
Commit message: Tooltip: Escape the title attribute so that it's treated as text and not HTML. Fixes #8861 - Tooltip: XSS vulnerability in default content.

Diff: diff --git a/demos/autocomplete/combobox.html b/demos/autocomplete/combobox.html
index 8c6f59fc1..6229d47b2 100644
--- a/demos/autocomplete/combobox.html
+++ b/demos/autocomplete/combobox.html
@@ -59,11 +59,11 @@
 
 					if ( !valid ) {
 						// remove invalid value, as it didn't match anything
 						$( element )
 							.val( "" )
-							.attr( "title", $( "<a>" ).text( value ).html() + " didn't match any item" )
+							.attr( "title", value + " didn't match any item" )
 							.tooltip( "open" );
 						select.val( "" );
 						setTimeout(function() {
 							input.tooltip( "close" ).attr( "title", "" );
 						}, 2500 );
diff --git a/tests/unit/tooltip/tooltip_options.js b/tests/unit/tooltip/tooltip_options.js
index f9da27fb7..01ac25040 100644
--- a/tests/unit/tooltip/tooltip_options.js
+++ b/tests/unit/tooltip/tooltip_options.js
@@ -14,10 +14,24 @@ test( "content: default", function() {
 	expect( 1 );
 	var element = $( "#tooltipped1" ).tooltip().tooltip( "open" );
 	deepEqual( $( "#" + element.data( "ui-tooltip-id" ) ).text(), "anchortitle" );
 });
 
+test( "content: default; HTML escaping", function() {
+	expect( 2 );
+	var scriptText = "<script>$.ui.tooltip.hacked = true;</script>",
+		element = $( "#tooltipped1" );
+
+	$.ui.tooltip.hacked = false;
+	element.attr( "title", scriptText )
+		.tooltip()
+		.tooltip( "open" );
+	equal( $.ui.tooltip.hacked, false, "script did not execute" );
+	deepEqual( $( "#" + element.data( "ui-tooltip-id" ) ).text(), scriptText,
+		"correct tooltip text" );
+});
+
 test( "content: return string", function() {
 	expect( 1 );
 	var element = $( "#tooltipped1" ).tooltip({
 		content: function() {
 			return "customstring";
diff --git a/ui/jquery.ui.tooltip.js b/ui/jquery.ui.tooltip.js
index 2ccd61f46..ab8d5173c 100644
--- a/ui/jquery.ui.tooltip.js
+++ b/ui/jquery.ui.tooltip.js
@@ -44,11 +44,13 @@ function removeDescribedBy( elem ) {
 
 $.widget( "ui.tooltip", {
 	version: "@VERSION",
 	options: {
 		content: function() {
-			return $( this ).attr( "title" );
+			var title = $( this ).attr( "title" );
+			// Escape title, since we're going from an attribute to raw HTML
+			return $( "<a>" ).text( title ).html();
 		},
 		hide: true,
 		// Disabled elements have inconsistent behavior across browsers (#8661)
 		items: "[title]:not([disabled])",
 		position: {

Vulnerable code: 							.attr( "title", $( "<a>" ).text( value ).html() + " didn't match any item" )
			return $( this ).attr( "title" );
Fixed code: 							.attr( "title", value + " didn't match any item" )
test( "content: default; HTML escaping", function() {
	expect( 2 );
	var scriptText = "<script>$.ui.tooltip.hacked = true;</script>",
		element = $( "#tooltipped1" );

	$.ui.tooltip.hacked = false;
	element.attr( "title", scriptText )
		.tooltip()
		.tooltip( "open" );
	equal( $.ui.tooltip.hacked, false, "script did not execute" );
	deepEqual( $( "#" + element.data( "ui-tooltip-id" ) ).text(), scriptText,
		"correct tooltip text" );
});

			var title = $( this ).attr( "title" );
			// Escape title, since we're going from an attribute to raw HTML
			return $( "<a>" ).text( title ).html();