Skip to content
Rezha Julio
Go back

Chasing a Transitive Dependency Vulnerability

3 min read

Last week I found out my blog was shipping a vulnerable dependency. Not one I installed directly, but one hiding two levels deep in the dependency tree: fast-xml-parser.

The Vulnerability

Versions 4.3.6 through 5.3.3 of fast-xml-parser throw a RangeError when they encounter out-of-range numeric character entities like � or �. That might sound obscure, but it means any application that processes untrusted XML input can be crashed with a carefully crafted payload.

For a blog, the attack surface is the RSS feed. This blog uses @astrojs/rss to generate feeds, and @astrojs/rss depends on fast-xml-parser@^5.3.3. If someone sent malformed XML to any endpoint that parses it, the whole thing could go down.

How I Found It

I was doing a routine bun audit (okay, I was procrastinating on actually writing) and the report flagged fast-xml-parser. I checked my package.json and didn’t see it listed anywhere. Took me a minute to realize it was coming in through @astrojs/rss.

Terminal window
bun pm ls | grep fast-xml-parser

Sure enough, there it was. A transitive dependency I never explicitly chose to install.

The Annoying Part

The fix for fast-xml-parser already existed in version 5.3.4. But @astrojs/rss hadn’t released an update that bumped its dependency yet. So I couldn’t just run bun update @astrojs/rss and move on.

This is the part about transitive dependencies that bugs me. You’re trusting that your dependencies keep their dependencies up to date. When they don’t, or when they’re slow about it, you’re left in an awkward spot. Wait and stay vulnerable, or take matters into your own hands.

I took matters into my own hands.

The Fix

The solution was to use overrides in package.json to force the resolved version of fast-xml-parser to the patched release:

package.json
{
"overrides": {
"fast-xml-parser": "^5.3.4"
}
}

I also added it as a devDependency so Dependabot would pick it up and notify me about future updates:

package.json
{
"devDependencies": {
"fast-xml-parser": "^5.3.4"
}
}

After running bun install, I double-checked bun.lock to make sure the resolved version was actually 5.3.4+. It was.

Terminal window
grep -A 2 'fast-xml-parser' bun.lock

Build passed, RSS feeds still generated correctly. Done.

The uncomfortable truth about transitive deps

You carefully vet the libraries you install. But each of those libraries pulls in its own tree of dependencies that you probably never look at. With npm or bun, your actual dependency tree is way larger than what’s in package.json. A vulnerability anywhere in that tree is your problem, even if you didn’t put it there.

The overrides approach works, but it’s a hack. You’re pinning a version that your direct dependency didn’t ask for, and there’s always a small risk of breaking something. For a patch bump that fixes a security issue, it’s almost always fine. But it’s one more thing to remember to clean up once upstream catches up.

Run bun audit regularly. Learn how overrides work. And if you override something, add it as a devDependency too so Dependabot can keep an eye on it for you. Check back later and remove the override once the upstream package updates.

Maintaining a website means maintaining everything under it, including the stuff you didn’t choose to install.


Related Posts