CVE-2022-31160 is a cross-site scripting (XSS) vulnerability in jQuery UI that lets attacker-controlled markup execute JavaScript when a checkboxradio widget is refreshed, and it is fixed in jQuery UI 1.13.2. It carries a CVSS 3.1 base score of 6.1 (medium), which understates the risk in apps that render user-supplied labels. If you still ship an older jQuery UI, this one is worth clearing before your next release.
The bug is narrow in scope but common in the wild because jQuery UI shipped inside so many legacy dashboards, admin panels, and CMS themes. It is the kind of finding that an SCA scan surfaces transitively long after anyone remembers adding the dependency.
What CVE-2022-31160 actually is
jQuery UI is a widget and interaction library built on top of jQuery. The checkboxradio widget wraps a native <input type="checkbox"> or <input type="radio"> and styles it, using the surrounding <label> text as the visible caption.
The vulnerability lives in how the widget reads that label text. When you call .checkboxradio("refresh") on a widget whose label contains HTML entities, jQuery UI decodes those entities and re-inserts them as live HTML instead of treating them as plain text. Encoded content that was safe on first render becomes an executable DOM node after a refresh.
The advisory gives a clean illustration. A label that starts as this:
<label>
<input id="test-input">
<img src=x onerror="alert(1)">
</label>
is rewritten after a refresh into something equivalent to:
<label>
<input id="test-input">
<img src=x onerror="alert(1)">
</label>
The < and > entities that neutralized the <img> tag get decoded, the browser now parses a real image element, the src=x fails to load, and the onerror handler fires. That is a textbook DOM-based XSS.
Affected versions and severity
The flaw affects jQuery UI versions before 1.13.2. The maintainers patched it in 1.13.2, released in July 2022. Distributions that repackage jQuery UI (for example, jquery-ui-rails in the Ruby ecosystem) issued their own advisories tracking the same CVE.
The CVSS vector reflects an attack that needs some setup: the attacker must control the label content and something must trigger a refresh on that widget. That is why it lands at 6.1 rather than a critical score. In practice, plenty of applications feed dynamic data into widget labels and re-initialize widgets on state changes, so treating it as "only medium" and deferring it indefinitely is a mistake.
Why it slips through
Three things make CVE-2022-31160 easy to miss.
First, jQuery UI is often a transitive or vendored dependency. It gets pulled in by a theme, a legacy component, or copied into a /vendor folder years ago and forgotten. Your package.json may not mention it at all.
Second, the exploit requires the refresh call, so the vulnerable code path is not exercised on every page. A quick manual test of the happy path shows nothing.
Third, developers assume HTML entity encoding is a durable defense. It usually is. This bug is notable precisely because a normally reliable mitigation gets undone by the library itself.
How to detect it in your codebase
Start by finding every copy of jQuery UI you ship, not just the one your package manager declares:
# npm / yarn projects
npm ls jquery-ui
grep -rl "jQuery UI - v1.1" ./ --include="*.js"
# look for vendored or minified copies
find . -iname "jquery-ui*.js"
Check the version banner at the top of any file you find. Anything below 1.13.2 is in scope. Software composition analysis catches the declared dependency automatically, and an SCA tool such as Safeguard can flag the transitive path back to whatever component introduced it, which is the part that is tedious to trace by hand.
If you want to confirm exploitability rather than just presence, look for call sites:
grep -rn "checkboxradio" ./src
grep -rn "\.checkboxradio\(\"refresh\"\)" ./src
A hit here plus a label that renders untrusted input means you have a real, not theoretical, exposure.
Remediation
The clean fix is to upgrade jQuery UI to 1.13.2 or later. That is the only change that removes the root cause.
npm install jquery-ui@^1.13.2
If you cannot upgrade immediately, for example because a legacy theme is pinned to an older jQuery UI, the maintainers documented a workaround: wrap all non-input content of the label in a <span> so the widget does not attempt to re-parse it during refresh.
<label>
<input id="test-input">
<span><img src=x onerror="alert(1)"></span>
</label>
This is a stopgap. It depends on every label being authored correctly, which is fragile, so schedule the upgrade regardless. As a defense-in-depth layer, a strict Content-Security-Policy that blocks inline event handlers and inline script raises the cost of DOM XSS generally, though it should never be your only control.
Fitting it into vulnerability management
CVE-2022-31160 is a good case study in prioritization. On paper it is a medium finding on a client-side library, the kind of thing that gets pushed to the bottom of a long queue. But the real risk depends entirely on context: does the affected widget render untrusted input, and does your app call refresh? Those two questions turn a generic 6.1 into either "ignore for now" or "patch this week."
That context-driven triage is more useful than sorting purely by CVSS. If you want a structured way to think about severity, reachability, and fix order, the Safeguard Academy covers how to reason about findings like this one without drowning in noise.
FAQ
Is CVE-2022-31160 a critical vulnerability?
No. It carries a CVSS 3.1 base score of 6.1, which is medium severity. The score reflects that exploitation requires attacker-controlled label content and a refresh call. That said, apps that render user data in widget labels should treat it as a real XSS risk and patch promptly.
Which jQuery UI versions are affected by CVE-2022-31160?
All jQuery UI versions before 1.13.2 are affected. The fix shipped in jQuery UI 1.13.2 in July 2022. Repackaged distributions such as jquery-ui-rails published matching advisories for the same CVE.
Can I fix CVE-2022-31160 without upgrading?
There is a documented workaround: wrap the non-input contents of the label in a <span> so the checkboxradio widget does not re-decode entities on refresh. Treat this as temporary. Upgrading to jQuery UI 1.13.2 or later is the only change that removes the underlying flaw.
How do I find jQuery UI if it is a transitive dependency?
Run your package manager's dependency tree (npm ls jquery-ui) and also grep the filesystem for vendored or minified copies, since jQuery UI is frequently bundled into themes and legacy components. Software composition analysis automates both the discovery and the transitive path back to the introducing component.