All posts

Which jQuery versions are vulnerable, and how to find the copy your site loads

CentralCSP Team ·

Last update:

A scanner or a pentest report says "vulnerable version of the library jQuery found", and the first reaction is usually "we upgraded that years ago". Both are often true at once. Your bundle carries a current jQuery, and a plugin, a theme, a vendor widget, or a CDN line in an old template carries a second copy that nobody remembers adding.

This post lists the jQuery versions with published CVEs, explains why old copies keep turning up, shows how to find every copy a page actually loads, and ends with the fix.

Which jQuery versions are vulnerable

Every jQuery release before 3.5.0 is affected by at least one published CVE. The advisories that matter, with the versions they cover:

CVEAffected versionsClassFixed in
CVE-2020-11022Before 3.5.0XSS when HTML from an untrusted source is passed to a DOM manipulation method, even after sanitizing3.5.0
CVE-2020-11023Before 3.5.0XSS through <option> elements passed to DOM manipulation methods3.5.0
CVE-2019-11358Before 3.4.0Prototype pollution through jQuery.extend with an untrusted source object3.4.0
CVE-2015-9251Before 3.0.0XSS when a cross-domain Ajax request is made without a dataType and the response is executed3.0.0
CVE-2012-6708Before 1.9.0XSS when a string containing < is passed to jQuery(), which treats it as HTML. location.hash is the classic vector1.9.0

The short version: 3.5.0 or later has no published CVE at the time of writing (September 2026). 3.4.x has the two 2020 XSS advisories. Anything in the 1.x or 2.x lines has all of them that apply, and those lines no longer receive releases.

The severity on these is mostly Medium by CVSS, and scanners often list an outdated jQuery as an informational finding. The exploitability depends on your code: the 2020 advisories need untrusted HTML to reach .html(), .append(), or a similar method. A page that never does that is at low risk; a page that renders search results or user profiles through jQuery is not.

Why an old jQuery is still on your site

jQuery is still on about two thirds of all websites according to W3Techs, and the copy in your build is rarely the one the scanner found. jQuery arrives on a page through more routes than any other library:

  • A CMS core. WordPress ships its own jQuery and updated it slowly for years; older installs still serve a 1.x copy to every page.
  • Plugins and themes. Many bundle a private copy so they work regardless of what the site provides. Two or three copies on one page is common.
  • Admin and legacy areas. An internal dashboard or a checkout step built in another era keeps its own script tags.
  • Vendor widgets and tags. A chat widget, a survey tool, or a tag manager container loads jQuery for its own use, at a version its vendor chose.
  • A pinned CDN URL. A <script src="https://code.jquery.com/jquery-1.12.4.min.js"> line in a template works today exactly as it did when it was written.
  • jQuery UI and jQuery Migrate. Both depend on jQuery and often keep an old core alive because upgrading it would break them.

The web-scale study that first measured this, published at NDSS in 2017, found that a large share of vulnerable library inclusions came from third-party code and dynamically injected scripts rather than from the site's own developers. Nothing about that has changed.

How to check which jQuery version a page loads

For one page, open the browser console and ask:

jQuery.fn.jquery
// "3.7.1"

That answers for the copy that owns the jQuery global. It does not answer for the others. If a plugin loaded its own copy under noConflict, or a vendor widget bundled jQuery inside its own file, the console shows you one version and the page runs two.

To see them all, open the DevTools Network panel, filter on JS, and read the script list. A filename or CDN path such as jquery-1.12.4.min.js names the version. A bundled copy inside vendor.js or widget.js does not, and that is where a file scanner helps: download the scripts and run Retire.js over them. It recognizes jQuery from the file contents, not the filename.

npx retire --path ./downloaded-scripts

This is a one-page, one-day answer. The same site has other pages, other templates, and tags that change without a deployment.

What the scanner finding means

The strings vary by tool: "Vulnerable version of the library jQuery found", "Out-of-date version (jQuery)", "Outdated JavaScript libraries", "Older and vulnerable jQuery version detected". They all mean the same thing: the scanner saw a jQuery file, read a version from its contents or its URL, and matched that version against a CVE list.

Three things to know when you read one:

  • The severity is usually Information or Low. Scanners rate the presence of the version, not the exploitability on your page. Treat it as a fact to act on, not as a risk score.
  • A backported patch does not change the version string. Some distributions patch a CVE and keep the old version number, so the finding can be a false positive. The reverse is also true: a version string proves nothing about the file's contents, which is why a content hash beats a filename.
  • The finding names one URL. It does not tell you how many other pages carry the same copy, or which other copies exist.

Find every copy across the whole site, continuously

The one place that sees every jQuery a page runs is the browser. Content Security Policy (CSP) hash reporting asks it to report a hash of every script it executes, as a csp-hash report, for every page real visitors load. The mechanism is covered in How to detect vulnerable JavaScript libraries on a live website; the short form is one source expression, 'report-sha256', and a reporting endpoint.

Technologies in CentralCSP turns those reports into one row per library version. A site running three jQuery copies shows three rows, each with its status (up to date, outdated, or deprecated), the advisories that affect it, and the list of script URLs that carry it. That list is the answer to "where does this come from": your own bundle, a plugin path, a vendor host, or a pinned CDN URL, each with a different owner and a different fix.

The engine identifies the library behind each reported script rather than trusting its filename, so a vendored copy inside another file is recognized as jQuery, and a file that changes at a stable URL shows up as a new hash. Everything is continuous, so a plugin update that reintroduces an old copy is visible the day it happens. The feature is included in the Scale and Enterprise plans; the pricing page has the comparison, and the supply-chain feature page has the tour.

How to fix it

Upgrade every copy you own, remove the copies you do not need, and pin what remains.

  1. Upgrade to the current 3.x release. Removed APIs are the usual breakage; jQuery Migrate restores them and logs each use so you can clean them up. Migrate restores compatibility, it does not patch the CVEs, so the core still has to move. Do not call jQuery.UNSAFE_restoreLegacyHtmlPrefilter() to silence Migrate warnings: it brings the 2020 XSS behaviour back.
  2. Remove duplicate copies. In WordPress, dequeue the plugin or theme copy and let the core one serve. Elsewhere, delete the second script tag and let the bundle provide jQuery.
  3. Replace or update vendor widgets that bundle an old copy. If the vendor will not update, the tag is the finding.
  4. Pin the file with Subresource Integrity. An integrity attribute on the CDN script tag, generated from the file you ship, stops a changed file from loading. It confirms the bytes, it does not tell you the version is safe, so it comes after the upgrade, not instead of it. See Subresource Integrity explained.
  5. Alert on regression. The Outdated or deprecated version alert rule fires when an old copy comes back through a plugin update or a new tag, so the fix stays fixed.

FAQ

Is jQuery 3.x still vulnerable?

Versions 3.0.0 to 3.4.1 are affected by the two 2020 XSS advisories, CVE-2020-11022 and CVE-2020-11023, and 3.0.0 to 3.3.x also by the prototype pollution advisory CVE-2019-11358. From 3.5.0 onward there is no published CVE at the time of writing. Upgrade to the current 3.x release and check for other copies on the page.

How do I check which jQuery version a website uses?

Run jQuery.fn.jquery in the browser console for the copy that owns the global, then check the DevTools Network panel for other jQuery files, because plugins and vendor widgets often load their own. For every page of a site, continuously, use CSP hash reporting and a runtime inventory rather than a manual check.

Is jQuery 1.12 safe to use?

No. The 1.x line is affected by CVE-2020-11022, CVE-2020-11023, CVE-2019-11358, and CVE-2015-9251, and it no longer receives releases, so no fix will come. Upgrade to the current 3.x release, using jQuery Migrate to bridge removed APIs during the move, and check the page for a second copy a plugin loaded.

Does jQuery Migrate fix the vulnerabilities?

No. jQuery Migrate restores APIs that newer jQuery versions removed, so old code keeps working after an upgrade, and it logs each use so you can modernize it. It does not patch security issues in the core. The fix is the newer jQuery core; Migrate only makes the upgrade easier.

Find your copies first

The upgrade is the easy part. The work is finding every copy, and the browser is the only place that sees them all. Add 'report-sha256' to your script directive, let real page loads report what runs, and read the jQuery rows off the Technologies page with the URL that carries each one. Start free with CentralCSP and the first copies show up with the first reports.

Sources