jQuery Migrate is a compatibility shim that lets code written for old jQuery keep working on newer jQuery releases, and the security risk is that teams use it as an excuse to stay on outdated, vulnerable jQuery instead of finishing the upgrade. The plugin itself is not malicious, but the way projects rely on it often correlates with running a jQuery version that still carries known cross-site scripting flaws.
If you have jquery-migrate in your dependency tree, treat it as a signal worth investigating rather than a problem to remove blindly.
What jQuery Migrate actually does
When the jQuery team removed or changed APIs across major versions (1.x to 3.x), a lot of existing code broke. jQuery Migrate re-adds those deprecated or removed behaviors and logs a warning to the console every time your app touches one. In development builds it prints messages like JQMIGRATE: jQuery.fn.attr('checked') might be used instead of .prop(), which is a map of exactly what you need to fix.
The intended workflow is temporary: drop the plugin in, watch the warnings, fix each call site, then delete the plugin. In practice it becomes permanent scaffolding. The console warnings get ignored, the migration never finishes, and the shim ships to production for years.
Why the security team cares
The plugin does not add vulnerabilities on its own. The problem is what it enables. jQuery had two significant cross-site scripting vulnerabilities fixed in jQuery 3.5.0, released in April 2020:
- CVE-2020-11022: passing HTML containing
<option>elements from untrusted sources to DOM manipulation methods like.html()or.append(), even after sanitizing it, could execute untrusted code. It affects jQuery from 1.0.3 up to but not including 3.5.0. - CVE-2020-11023: a DOM-based XSS in the
htmlPrefilterfunction's handling of self-closing tags, also fixed in 3.5.0.
Teams that lean on jquery-migrate are frequently the same teams still shipping jQuery 1.12 or 2.x precisely because they never completed the move to 3.5.0 or later. The shim masks the friction that would otherwise force the upgrade.
Check what version you are really running
The console warning is the fastest signal, but you want the resolved versions from your lockfile, not the range in package.json. For an npm project:
npm ls jquery jquery-migrate
If you load jQuery from a CDN or a vendored file instead of npm, grep your source and static assets:
grep -rn "jquery-migrate" ./public ./src
grep -rn "jQuery.fn.jquery" ./
At runtime you can confirm in the browser console:
console.log(jQuery.fn.jquery); // e.g. "3.5.1"
Anything below 3.5.0 is exposed to the XSS issues above. jQuery Migrate itself had a 3.5.0 release as well; keep the plugin current if you keep it at all.
Fixing it properly
The right end state is modern jQuery with no migrate shim, or no jQuery at all. A staged approach that does not break your app:
- Upgrade jQuery to the latest 3.x (3.7.x at time of writing) and add jquery-migrate 3.x alongside it. The shim will now surface every deprecated call the new jQuery version rejects.
- Turn on verbose logging so nothing is silent:
jQuery.migrateMute = false;
jQuery.migrateTrace = true;
- Work through the console warnings, fixing each deprecated pattern at its call site. Common ones are
.attr()for boolean properties (use.prop()),jQuery.isFunction()(usetypeof x === 'function'), and passing HTML strings that should be built with.text(). - Once the warnings are gone, remove jquery-migrate and confirm the app still works.
Wherever you handle user-controlled HTML, stop passing it straight into .html(). Sanitize with a maintained library such as DOMPurify, or better, set text content with .text() so the browser treats it as data, not markup. That neutralizes the class of bug behind both jQuery CVEs regardless of version.
Keeping it from coming back
Removing the shim once does not stop the next developer from reintroducing an old jQuery through a transitive dependency. A WordPress plugin, an ancient UI widget, or a bundled admin theme can all drag in a vulnerable copy. Software composition analysis catches this: an SCA tool can flag a jQuery version below 3.5.0 anywhere in your dependency graph, including copies pulled in transitively that never show up in your top-level package.json. If you want to see how transitive detection differs across scanners, our comparison with Snyk walks through the mechanics.
Add a CI check that fails the build if a jQuery below your minimum shows up, so a stale copy cannot slip back in during a hurried release.
A note on Content Security Policy
Even a correct jQuery version benefits from defense in depth. A Content Security Policy that disallows inline event handlers and restricts script-src reduces the blast radius if an XSS bug slips through, whether it comes from jQuery or your own code. It is not a substitute for upgrading, but it buys you margin. Our academy covers CSP setup for legacy front ends in more depth.
FAQ
Is jquery-migrate itself a vulnerability?
No. The plugin does not introduce security bugs by itself. It becomes a risk indicator because projects that depend on it are often still running an outdated jQuery version that does carry known XSS flaws like CVE-2020-11022 and CVE-2020-11023.
Can I just delete jquery-migrate to be secure?
Not on its own. If you are on jQuery below 3.5.0, deleting the shim removes compatibility support but leaves the vulnerable jQuery in place, and it may break your app. Upgrade jQuery to 3.5.0 or later first, fix the deprecation warnings, then remove the shim.
Which jQuery version fixes the known XSS issues?
jQuery 3.5.0, released in April 2020, fixed both CVE-2020-11022 and CVE-2020-11023. Run at least 3.5.0, and ideally the latest 3.7.x, to stay current with later fixes.
How do I find every copy of jQuery in my project?
Run npm ls jquery for npm-managed copies and grep your static assets and vendored files for CDN or bundled versions. Software composition analysis will surface copies pulled in transitively that a lockfile scan of your direct dependencies alone can miss.