Files
iptvnator/docs/architecture/dependency-security-overrides.md
T
4grayandClaude Opus 5 e91a7cde7a fix(deps): patch transitive runtime CVEs via pnpm overrides (#1258)
Closes 13 runtime-scope Dependabot advisories that Dependabot cannot fix itself:
every vulnerable package here is transitive, so the bot has no lever until each
parent publishes a release widening its own pin.

Overrides added (pinned-source form, matching existing convention):

- @xmldom/xmldom 0.8.11 -> 0.8.13  (5 high) via video.js -> mpd-parser
- fast-uri       3.1.0  -> 3.1.4   (4 high) via electron-conf -> ajv
- js-yaml        4.1.1  -> 4.3.0   (2)      via electron-updater
- form-data      4.0.5  -> 4.0.6   (1 high) via axios
- ajv            8.17.1 -> 8.18.0  (1)      via electron-conf

Every target stays inside its parent's declared semver range. For xmldom,
fast-uri and js-yaml the newest published version is outside that range
(0.9.x / 4.x / 5.x), so "latest" would have broken them; the new doc
records that constraint.

Deliberately excluded: axios and uuid are direct deps already covered by open
Dependabot PRs (#1251, #1252). undici is labelled runtime scope but every path
to it is build tooling (electron -> @electron/get, @angular/build,
@module-federation/dts-plugin) and it is not in the packaged app.

Reachability: xmldom arrives via video.js -> VHS -> mpd-parser, but the app
routes every .mpd to Shaka, which uses its own DASH parser, so that one is
defence in depth. The genuinely reachable one is js-yaml, which
electron-updater uses to parse latest.yml from releases.

Adds docs/architecture/dependency-security-overrides.md and a .changes note.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 02:21:11 +02:00

3.1 KiB

Dependency Security Overrides

How transitive CVEs are patched in this repo, and the constraint that makes "just bump to latest" the wrong move.

Why overrides exist

Dependabot can only bump packages we declare ourselves. When the vulnerable package is transitive — pulled in by video.js, electron-updater, electron-conf, axios — the bot has no lever: it would have to wait for the parent to publish a release that widens its own pin. Until then the alert stays open regardless of how many bot PRs land.

pnpm.overrides in the root package.json is that lever. Every entry uses the pinned-source form so an override only rewrites the exact resolution it was written for, and goes stale visibly instead of silently re-targeting a future version:

"@xmldom/xmldom@0.8.11": "0.8.13"

The semver ceiling

An override must stay inside the range its parent declares. pnpm applies overrides without re-checking the parent's range, so an out-of-range target installs cleanly and then fails at runtime or under load, not at install time.

For three of the five current security overrides, the newest published version is outside the parent's range. Taking "latest" would break them:

Override Pinned to Parent range Latest on npm
@xmldom/xmldom 0.8.13 mpd-parser ^0.8.3, plist ^0.8.8 0.9.x ❌
fast-uri 3.1.4 ajv ^3.0.1 4.x ❌
js-yaml 4.3.0 electron-updater ^4.1.0 5.x ❌
form-data 4.0.6 axios ^4.0.5 4.0.6 ✅
ajv 8.18.0 electron-conf ^8.13.0 8.20.0 ✅

Before changing any of these, check the parent's declared range first:

npm view <parent>@<version> dependencies --json

Verifying an override actually applied

Grepping pnpm-lock.yaml for the old version still finds it, but that hit is not a leftover package block — pnpm removes those once nothing resolves to them. It is the override's own selector key, echoed in the overrides: block at the top of the lockfile:

overrides:
    '@xmldom/xmldom@0.8.11': 0.8.13

So a bare grep proves only that the override is declared, never that it took effect. Resolve the real path on disk instead:

node -e "console.log(require('./node_modules/.pnpm/mpd-parser@1.3.1/node_modules/@xmldom/xmldom/package.json').version)"

What is deliberately not overridden

undici carries open alerts flagged runtime scope, but every path to it is build tooling — electron → @electron/get, @angular/build, and @module-federation/dts-plugin. It is not in the packaged app. The runtime label is a Dependabot classification artifact, not a shipped-code claim. Bumping it inside the Angular/Nx toolchain risks the build for no runtime benefit.

When triaging, confirm scope from the dependency graph rather than trusting the alert's scope field.