Security upgrades on my Node.js, npm and Azurite
Supply chain attacks
A few days back Fireship dropped “A single PR just hijacked the NPM registry…” - as always, educational and alarming in equal measures.
I’d already been keeping an eye on supply chain risks after the xz/liblzma near-disaster that almost took down the internet.
And the numbers back up the paranoia: Sonatype reports that “over 99% of open source malware occurred on npm”.
So naturally I started wondering: what about my machine?
How Node.js ended up on my machine
I’m not a heavy Node.js user — it landed on my Windows machine in November 2024 mostly as collateral from installing other tooling. The last time I consciously updated it was February 2026; npm got a bump in March.
The main reason it’s there at all: I run Azurite locally to emulate Azure Blob Storage alongside Python Azure Functions tests. Azurite is used alongside Python Azure Functions and generates network traffic, so it’s a bit sensitive and needs to be kept in check.
I also had no idea how many other Node.js packages were lurking on my system. Spoiler: a few.
Trusting but checking
I trust Microsoft’s team to keep Azurite maintained. But trust-and-forget is not a security posture, only a light edge on hope.
So let’s actually check:
- my local npm, node.js, and Azurite security status, and update them if needed
- the node.js packages on my system and run security checks to detect vulnerabilities
- fix security vulnerabilities
Updating Node.js and npm
What I was running
npm -v11.14.1
node -vv24.14.0
Findings:
- OK. npm was already on the latest version
- Not OK. Node.js was not latest → v24.14.1 and v24.15.0 were both available
- OK. npm was ahead of the Node.js‑bundled npm (e.g. v11.12.1 in Node v24.15.0) — a newer patch/minor than what ships with node.js v24.14 or v24.15
Risk of node.js v24.14.0 vs. LTS v24.15.0 ?
v24.14.1 is a proper security release — 2 high CVEs, 4 medium, 2 low. That alone was enough to act.
v24.15.0 looked even better: per Perplexity (May 18, 2026), it’s “mainly a hardening release — low-level fixes in crypto, http, tls, stream, zlib, plus refreshed bundled deps including npm and root certificates.” No breaking changes reported, so it became the target.
Why was node.js outdated?
I installed Node.js from the .msi installer (see default node.exe path below). Turns out the .msi installer from nodejs.org (rightfully) has no auto-update mechanism. No reminder, no update.
where nodeC:\Program Files\nodejs\node.exe
Updating node.js
Installing additional tools (nvm-windows, Chocolatey…) to manage the node.js update is out of scope for this post — I ran node-v24.15.0-x64.msi from nodejs.org directly.
After installation:
node -vv24.15.0
What’s actually installed?
Global packages
npm list -gC:\Users\xxx\AppData\Roaming\npm +-- azurite@3.35.0 `-- npm@11.14.1
npm list --parseable -g --depth=0C:\Users\xxx\AppData\Roaming\npm C:\Users\xxx\AppData\Roaming\npm\node_modules\azurite C:\Users\xxx\AppData\Roaming\npm\node_modules\npm
I only had one global package: Azurite. Good enough for now.
Local packages
This PowerShell snippet finds all Node.js projects on the machine.
Packages under .venv (excluded from scan): 5
{
"rootPath": "C:\\projects",
"includeVenv": false,
"count": 0,
"projects": []
}
No local Node.js projects found.
Auditing Azurite
Checking Azurite for known vulnerabilities is unlikely to uncover serious threats — it’s a maintained Microsoft package and I’m not a security researcher. But version freshness and known CVEs in its dependency tree are worth a look.
Was Azurite up to date?
azurite --version3.35.0
It’s the latest released version of Azurite on GitHub releases, so it’s up to date.
Compatibility check
npm view azurite engines{ node: '>=10.0.0', vscode: '^1.39.0' }
System’s node.js is >=10.0.0, compatible with Azurite.
Any outdated packages?
npm outdated package checkNo outdated packages found. There’s only one global package and it’s latest, so obviously up to date in this case.
Signature verification
Working directory: C:\Users\xxx\AppData\Roaming\npm\node_modules\azurite
npm audit signaturesaudited 374 packages in 4s 374 packages have verified registry signatures 25 packages have verified attestations (use --json --include-attestations to view attestation details)
No missing signatures found — missing signatures can be a signal worth investigating, but none were found here.
Running npm audit
First pass: everything
npm audit --no-package-lock# npm audit report ... 56 vulnerabilities (10 moderate, 8 high, 38 critical) To address issues that do not require attention, run: npm audit fix To address all issues possible (including breaking changes), run: npm audit fix --force Some issues need review, and may require choosing a different dependency.
38 critical. That can’t be so bad, after all it’s a Microsoft-maintained emulator that thousands of people use daily, which means either the world is on fire, or — more likely — npm audit is over-counting in some ways.
There’s a catch: filtering out dev dependencies gets to what actually matters at runtime.
Second pass: runtime only
npm audit --no-package-lock --omit=dev# npm audit report ... 10 vulnerabilities (6 moderate, 4 high) To address issues that do not require attention, run: npm audit fix To address all issues (including breaking changes), run: npm audit fix --force
High severity vulnerabilities
| Package | Vulnerability Summary | Fix Available |
|---|---|---|
| lodash | Code Injection via _.template; Prototype Pollution via _.unset/_.omit |
npm audit fix |
| path-to-regexp | ReDoS via multiple route parameters | npm audit fix |
| axios | 15+ issues: SSRF, CSRF, Credential Leakage, No_Proxy Bypass, Prototype Pollution, DoS, Metadata Exfiltration | npm audit fix –force |
Moderate severity vulnerabilities
| Package | Vulnerability Summary | Fix Available |
|---|---|---|
| brace-expansion | Zero-step sequence → process hang & memory exhaustion (ReDoS) | npm audit fix |
| follow-redirects | Leaks Custom Auth Headers to Cross-Domain Redirect Targets | npm audit fix |
@azure/identity |
Elevation of Privilege (Azure Identity & MSAL) | npm audit fix –force |
| tough-cookie | Prototype Pollution | npm audit fix –force |
| xml2js | Prototype Pollution | npm audit fix –force |
Understanding the blast radius
Two examples illustrate the dependency tree impact before fixing anything.
Example 1: Simple case brace-expansion appears only once in the Azurite dependency tree. The blast radius in case of vulnerabilities and fix is relatively limited. Running npm audit fix is unlikely to cause issues.
npm ls brace-expansionazurite@3.35.0 C:\Users\xxx\AppData\Roaming\npm\node_modules\azurite
`-- rimraf@3.0.2
`-- glob@7.2.3
`-- minimatch@3.1.5
`-- brace-expansion@1.1.12
Example 2: Multiple copies case
npm ls follow-redirectsazurite@3.35.0 C:\Users\xxx\AppData\Roaming\npm\node_modules\azurite +-- @azure/ms-rest-js@1.11.2 | `-- axios@0.21.4 | `-- follow-redirects@1.15.11 deduped `-- axios@0.27.2 `-- follow-redirects@1.15.11
The axios package appears in 2 versions, both depending on the same follow-redirects package. A vulnerability in follow-redirects has a wider blast radius, and a fix would touch more consumers of that dependency. In this case, follow-redirects is a transitive dependency that can easily be updated with npm audit fix.
To fix or not to fix
GitHub issue #2500 has been tracking these vulnerabilities for a while with no ETA. The maintainers are presumably making a risk call: fix cost vs. actual exposure. Fair enough — I have to make the same call for my system.
The fix plan
Non-breaking fix: covers 2/3 of the high-severity issues and 2/5 of the moderates. Low risk, clear win.
Breaking fix (--force): would tackle the rest, but risks silently breaking Azurite. My usage is purely local — with appropriate host 127.0.0.1 and port 10000, no production data, no risky network exposure. The risk of a broken emulator outweighs the marginal security gain. I’ll revisit when the maintainers ship a proper fix.
Applying the safe fixes
In this case, a quick smoke test before and after is enough — no sandboxing either.
Azurite run test — successful
azurite --blobHost 127.0.0.1 --blobPort 10000Azurite Blob service is starting at http://127.0.0.1:10000 Azurite Blob service is successfully listening at http://127.0.0.1:10000 Azurite Queue service is starting at http://127.0.0.1:10001 Azurite Queue service is successfully listening at http://127.0.0.1:10001 Azurite Table service is starting at http://127.0.0.1:10002 Azurite Table service is successfully listening at http://127.0.0.1:10002 ... Azurite Blob service is closing... Azurite Queue service is closing... Azurite Table service is closing... Azurite Table service successfully closed Azurite Blob service successfully closed Azurite Queue service successfully closed
Versions of packages before audit fix
Working directory: C:\Users\xxx\AppData\Roaming\npm\node_modules\azurite
npm ls lodashazurite@3.35.0 C:\Users\xxx\AppData\Roaming\npm\node_modules\azurite `-- sequelize@6.37.8 `-- lodash@4.17.23 ...
| Package | Version |
|---|---|
| lodash | 4.17.23 |
| path-to-regexp | 0.1.12 |
| brace-expansion | 1.1.12 |
| follow-redirects | 1.15.11 |
Apply audit fix
Azurite being a global package, npm audit fix cannot be used directly — adding a lock file to a global package is not good practice. npm install -g azurite@latest is used instead.
npm install -g azurite@latest... removed 5 packages, and changed 370 packages in 11s
azurite --version3.35.0
Checking non-breaking fix success
Working directory: C:\Users\xxx\AppData\Roaming\npm\node_modules\azurite
npm ls lodashazurite@3.35.0 C:\Users\xxx\AppData\Roaming\npm\node_modules\azurite `-- sequelize@6.37.8 `-- lodash@4.18.1 ...
| Package | Version Before Update | Version After Update |
|---|---|---|
| lodash | 4.17.23 | 4.18.1 |
| path-to-regexp | 0.1.12 | 0.1.13 |
| brace-expansion | 1.1.12 | 1.1.14 |
| follow-redirects | 1.15.11 | 1.16.0 |
All 4 packages bumped to patched versions, and the associated vulnerabilities disappeared from the audit report. The breaking-fix issues remain, as expected.
npm audit --no-package-lock --omit=dev# npm audit report @azure/identity <4.2.1 Severity: moderate ... axios <=0.31.0 Severity: high ... tough-cookie <4.1.3 Severity: moderate ... xml2js <0.5.0 Severity: moderate ... 6 vulnerabilities (4 moderate, 2 high) To address all issues (including breaking changes), run: npm audit fix --force
Azurite run test — successful
azurite --blobHost 127.0.0.1 --blobPort 10000Azurite Blob service is starting at http://127.0.0.1:10000 Azurite Blob service is successfully listening at http://127.0.0.1:10000 Azurite Queue service is starting at http://127.0.0.1:10001 Azurite Queue service is successfully listening at http://127.0.0.1:10001 Azurite Table service is starting at http://127.0.0.1:10002 Azurite Table service is successfully listening at http://127.0.0.1:10002 ... Azurite Blob service is closing... Azurite Queue service is closing... Azurite Table service is closing... Azurite Table service successfully closed Azurite Blob service successfully closed Azurite Queue service successfully closed
Conclusion
Node.js was behind and carrying 2 high CVEs. Azurite had a longer list of issues, some of which the non-breaking fix took care of — the remainder require changes the maintainers haven’t shipped yet, and my risk profile doesn’t require immediate action.
The number that surprised me most wasn’t the CVE count for Azurite but the fact that its release label is the same latest 3.35.0 both before and after the update. The same 3.35.0 Azurite release can have different versions of dependencies underneath. That’s the kind of “false sense of security” that quietly bites you. Worth keeping in mind whenever you’re evaluating your own npm packages supply chain exposure.