CVE-2022-31160 (JavaScript, published 2022-07-20T20:15Z)
Description: value: jQuery UI is a curated set of user interface interactions, effects, widgets, and themes built on top of jQuery. Versions prior to 1.13.2 are potentially vulnerable to cross-site scripting. Initializing a checkboxradio widget on an input enclosed within a label makes that parent label contents considered as the input label. Calling `.checkboxradio( "refresh" )` on such a widget and the initial HTML contained encoded HTML entities will make them erroneously get decoded. This can lead to potentially executing JavaScript code. The bug has been patched in jQuery UI 1.13.2. To remediate the issue, someone who can change the initial HTML can wrap all the non-input contents of the `label` in a `span`.
Severity: nan (CVSS v3 base score: 6.1)
Weakness: CWE-79 \u2014 Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
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.
Repository: https://github.com/jquery/jquery-ui
Files changed:
tests/unit/checkboxradio/checkboxradio.html
tests/unit/checkboxradio/core.js
tests/unit/checkboxradio/methods.js
ui/widgets/checkboxradio.js
Commit message: Checkboxradio: Don't re-evaluate text labels as HTML

If you generate a Checkboxradio from a checkbox/radio with a label that
contains encoded HTML, e.g. `&lt;em&gt;test&lt;/em&gt;` this will work fine
at first. If, however a refresh is triggered on that instance (explicitly or
e.g. by turning it into a `Controlgroup`), the previously escaped HTML will
now be evaluated.

If the label was created based on some user input, this could lead to
unexpected code execution even though the initial output was escaped.

Fixes gh-2101
Closes gh-2102

Vulnerable code:
		var disabled, labels;
		var that = this;
		this.label.contents().not( this.element[ 0 ] ).each( function() {

			// The label contents could be text, html, or a mix. We concat each element to get a
			// string representation of the label, without the input as part of it.
			that.originalLabel += this.nodeType === 3 ? $( this ).text() : this.outerHTML;
		} );

Fixed code:
<label>
	<input type="checkbox" id="label-with-no-for-with-html"/>
	<strong>Hi</strong>, <em>I'm a label</em>
</label>
<label>
	<input type="checkbox" id="label-with-no-for-with-text"/>
	Hi, I'm a label
</label>
<label>
	<input type="checkbox" id="label-with-no-for-with-html-like-text"/>
	&lt;em&gt;Hi, I'm a label&lt;/em&gt;
</label>
QUnit.test( "Inheriting label from initial HTML", function( assert ) {
	var tests = [
		{
			id: "label-with-no-for-with-html",
			expectedLabel: "<strong>Hi</strong>, <em>I'm a label</em>"
		},
		{
			id: "label-with-no-for-with-text",
			expectedLabel: "Hi, I'm a label"
		},
		{
			id: "label-with-no-for-with-html-like-text",
			expectedLabel: "&lt;em&gt;Hi, I'm a label&lt;/em&gt;"
		}
	];

	assert.expect( tests.length );

	tests.forEach( function( testData ) {
		var id = testData.id;
		var expectedLabel = testData.expectedLabel;
		var inputElem = $( "#" + id );
		var labelElem = inputElem.parent();

		inputElem.checkboxradio( { icon: false } );

		var labelWithoutInput = labelElem.clone();
		labelWithoutInput.find( "input" ).remove();

		assert.strictEqual(
			labelWithoutInput.html().trim(),
			expectedLabel.trim(),
			"Label correct [" + id + "]"
		);
	} );
} );

QUnit.test( "Initial text label not turned to HTML on refresh", function( assert ) {
	var tests = [
		{
			id: "label-with-no-for-with-html",
			expectedLabel: "<strong>Hi</strong>, <em>I'm a label</em>"
		},
		{
			id: "label-with-no-for-with-text",
			expectedLabel: "Hi, I'm a label"
		},
		{
			id: "label-with-no-for-with-html-like-text",
			expectedLabel: "&lt;em&gt;Hi, I'm a label&lt;/em&gt;"
		}
	];

	assert.expect( tests.length );

	tests.forEach( function( testData ) {
		var id = testData.id;
		var expectedLabel = testData.expectedLabel;
		var inputElem = $( "#" + id );
		var labelElem = inputElem.parent();

		inputElem.checkboxradio( { icon: false } );
		inputElem.checkboxradio( "refresh" );

		var labelWithoutInput = labelElem.clone();
		labelWithoutInput.find( "input" ).remove();

		assert.strictEqual(
			labelWithoutInput.html().trim(),
			expectedLabel.trim(),
			"Label correct [" + id + "]"
		);
	} );
} );

		var disabled, labels, labelContents;
		// The label contents could be text, html, or a mix. We wrap all elements
		// and read the wrapper's `innerHTML` to get a string representation of
		// the label, without the input as part of it.
		labelContents = this.label.contents().not( this.element[ 0 ] );

		if ( labelContents.length ) {
			this.originalLabel += labelContents
				.clone()
				.wrapAll( "<div></div>" )
				.parent()
				.html();
		}