# The Outdated JavaScript Problem: Why Old Libraries Are the Most Common Unpatched Hole on the Web

A jQuery from 2019 or a Bootstrap from 2018 sitting on your site is a known, public, exploitable bug with a CVE number attached. Here&#39;s how to find every outdated library you&#39;re running and upgrade it.

Author: J.A. Watte
Published: May 29, 2026
Source: https://jwatte.com/blog/blog-outdated-js-libraries/

---

_Part of the [security audit tool stack](/blog/blog-new-security-audit-tools-2026/). See the pillar post for the full catalog of sibling audits and where this one fits._

Most "we got hacked" stories on small sites don't start with a clever zero-day. They start with a library that was three years out of date. The site was running jQuery 3.3, or a Bootstrap from 2018, or a copy of Moment.js that nobody has touched since launch. Each of those carries a published CVE — a public, numbered, documented bug with proof-of-concept code already on GitHub. No exploit research required. An attacker scans for the version string, matches it to the CVE, and runs the payload.

This is the most common real vulnerability on the small-business web, and it's also the most boring to fix: you upgrade the library. The hard part has always been *knowing* which of the libraries on your page are out of date, and by how much. That's the gap the [Mega Security Analyzer](/tools/mega-security-analyzer/) now closes with its dependency-and-framework version layer.

## Why "old" equals "exploitable"

A version number on a front-end library is not cosmetic. It's a precise statement of which known bugs are present. A few examples that the analyzer flags by default:

- **jQuery below 3.5.0** is vulnerable to cross-site scripting via `CVE-2020-11022` and `CVE-2020-11023`. jQuery is on a huge share of the web, so this is the single most common finding.
- **Bootstrap below 4.3.1** (and the entire 3.x line below 3.4.1) carries XSS through data attributes — `CVE-2019-8331` and the 2018 pair `CVE-2018-14041`/`CVE-2018-14042`.
- **Lodash below 4.17.21** has both a command-injection (`CVE-2021-23337`) and a prototype-pollution (`CVE-2020-8203`) bug.
- **Handlebars below 4.7.7** allows prototype pollution that can escalate to remote code execution (`CVE-2021-23369`).
- **AngularJS** — *any* 1.x version — is end-of-life as of January 2022. It will never be patched again, and it has open CVEs. Running it at all is the finding.
- **Moment.js** isn't end-of-life, but it's in permanent legacy/maintenance mode, and versions below 2.29.4 have a ReDoS (`CVE-2022-31129`) and a path-traversal (`CVE-2022-24785`) bug.

The pattern is always the same: the maintainers fixed the bug in a specific release, and everything below that release is a documented target.

## How the analyzer finds your versions

You can't fix what you can't see. The version layer detects libraries three ways, all from a single fetch of your page — no install on your server, nothing run in your browser:

1. **CDN URL fingerprints.** Most sites load libraries from a CDN with the version baked into the path: `cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/...`, `cdn.jsdelivr.net/npm/bootstrap@4.3.1/...`, `unpkg.com/vue@2.6.14/...`, `code.jquery.com/jquery-3.5.1.min.js`. The analyzer parses the name and version out of every script and stylesheet URL. It also reads the WordPress convention of `jquery.min.js?ver=3.7.1`, where the `?ver` query string carries the real library version — which is how it catches outdated jQuery on the millions of WordPress sites that ship it that way.
2. **License banners.** Minified library files keep a header comment — `/*! jQuery v3.5.1 |...`, `Bootstrap v5.3.3`, `moment.js version : 2.29.1`. When a library is self-hosted with no version in the filename, the banner still gives it away.
3. **Exposed dependency manifests.** If a `package.json` or `composer.json` is reachable at your web root (it shouldn't be — that's its own finding), the analyzer reads the declared dependency ranges and flags any that permit a known-vulnerable release. This is where build-time-only packages like `glob`, `minimatch`, `semver`, and `lodash` show up.

Each detected library is compared against a dated baseline of the current latest stable version and the **security floor** — the lowest version with no known high or critical CVE. You get one of three verdicts per library: current (green), outdated-but-no-known-critical-bug (amber), or below the security floor / end-of-life (red, with the CVE numbers attached).

## How to correct your site

The fix is almost always one of four moves:

1. **Upgrade in place.** If you load from a CDN, change the version in the URL to the current release and test. Most minor and patch upgrades are drop-in. Major upgrades (jQuery 1.x → 3.x, Bootstrap 4 → 5, Vue 2 → 3) need a migration pass — read the library's upgrade guide first.
2. **Self-host the current version with Subresource Integrity.** Download the latest file, serve it from your own `/js/`, and add an `integrity="sha384-..."` hash to the `<script>` tag. This removes the third-party CDN as a single point of failure *and* pins the exact bytes you reviewed. (See the [Remote Dependency Audit](/tools/remote-dependency-audit/) for the self-hosting walkthrough.)
3. **Remove what you no longer use.** A surprising amount of jQuery on the modern web is loaded for one plugin that could be ten lines of vanilla JavaScript. If a library is end-of-life (AngularJS 1.x, MooTools, Prototype.js, the old jQuery carousel plugins), the only durable fix is to replace it.
4. **Lock down the manifest leak.** If the scan read your `package.json` off the live site, move it out of the published directory. Build artifacts belong in the build step, not the web root.

The analyzer's AI fix prompt bundles all of this — every outdated library, its current target version, the CVE it closes, and a migration note — into a single paste-into-Claude-or-ChatGPT prompt so you get a per-library upgrade plan for your exact stack.

## Where this fits in the bigger picture

Outdated dependencies map cleanly onto the security frameworks the analyzer references. In the **OWASP Top 10 (2021)** this is category **A06: Vulnerable and Outdated Components** — its own line item precisely because it's so common. The **CWE** entry is **CWE-1104: Use of Unmaintained Third Party Components**, and each specific bug rolls up to its own weakness (XSS is CWE-79, prototype pollution is CWE-1321, ReDoS is CWE-1333). In **NIST Cybersecurity Framework 2.0** it lives under **Identify → Asset Management** and **Protect → Platform Security**; in the **CIS Critical Security Controls** it's **Control 2: Inventory and Control of Software Assets**. Knowing the framework label matters when you have to hand the finding to anyone else — an insurer, an auditor, an MSP — because that's the language they file it under.

If you're building and maintaining sites for clients, dependency currency is the kind of recurring, unglamorous maintenance that quietly justifies a retainer. I wrote about turning that exact work into a sustainable practice in *The $20 Dollar Agency* — the chapter on productized maintenance is built around findings like this one.

## The honest limits

Version detection from the wire is fingerprint-based, so a couple of caveats apply:

- A site that self-hosts libraries with **no version in the filename and no banner** may not be fingerprintable. That's fine for security-through-obscurity, but it means a clean scan isn't proof you're current — only proof nothing *advertised* an old version.
- Manifest-declared ranges (`^4.17.0`) describe what the build *allows*, not necessarily what shipped. The analyzer flags the range when its floor permits a known-vulnerable release, and says so.
- The baseline of "latest" and "security floor" is a dated snapshot, refreshed periodically. Treat the CVE numbers as the durable part and re-check the exact latest version against npm before you pin it.

## Related reading

- [Seven Security Layers In One Scan: Mega Security Analyzer](/blog/blog-tool-mega-security-analyzer/) — the parent tool and the other six layers
- [Remote Dependency Audit](/tools/remote-dependency-audit/) — the self-hosting and SRI companion
- [Server, OS, and DevOps leaks](/blog/blog-server-os-devops-leaks/) — version disclosure at the server layer
- [Universal file exposure](/blog/blog-universal-file-exposure/) — why an exposed `package.json` is its own problem
- [Modern security headers](/blog/blog-modern-security-headers/) — the header layer that pairs with this one

## Fact-check notes and sources

- jQuery XSS: CVE-2020-11022 and CVE-2020-11023, fixed in jQuery 3.5.0. https://github.com/advisories/GHSA-gxr4-xjj5-5px2
- Bootstrap XSS: CVE-2019-8331 (fixed 3.4.1 / 4.3.1) and CVE-2018-14041/14042. https://github.com/advisories
- Lodash: CVE-2021-23337 and CVE-2020-8203, fixed in 4.17.21. https://github.com/advisories/GHSA-35jh-r3h4-6jhm
- Handlebars: CVE-2021-23369, prototype pollution, fixed 4.7.7. https://github.com/advisories
- AngularJS end-of-life January 2022. https://docs.angularjs.org/misc/version-support-status
- Moment.js project status (maintenance/legacy mode). https://momentjs.com/docs/#/-project-status/
- OWASP Top 10 2021, A06 Vulnerable and Outdated Components. https://owasp.org/Top10/A06_2021-Vulnerable_and_Outdated_Components/
- CWE-1104 Use of Unmaintained Third Party Components. https://cwe.mitre.org/data/definitions/1104.html
- NIST Cybersecurity Framework 2.0. https://www.nist.gov/cyberframework
- CIS Critical Security Controls v8.1, Control 2. https://www.cisecurity.org/controls

_This post is informational, not security-consulting, legal, or compliance advice. CWE and CVE are maintained by The MITRE Corporation; OWASP is a trademark of the OWASP Foundation. All references are nominative fair use. Only scan sites you own or have explicit written authorization to test._


---

Canonical HTML: https://jwatte.com/blog/blog-outdated-js-libraries/
RSS: https://jwatte.com/feed.xml
JSON Feed: https://jwatte.com/feed.json
Hero image: https://jwatte.com/images/blog-outdated-js-libraries.webp
