Security: published npm package has floating transitive dependencies #43435
Squixx
started this conversation in
Suggest an Idea
Replies: 1 comment
-
|
Possible direction for a solution #43436 |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Summary
When users install Renovate via
npx renovateornpm install -g renovate, npm resolvestransitive dependency versions at install time using the semver ranges declared by Renovate's
direct and indirect dependencies. This means the installed dependency tree is not
deterministic — npm may download package versions that were never tested, audited, or even
known at the time Renovate was published.
Renovate uses
pnpminternally with apnpm-lock.yamlthat pins the full tree. That lockfileis not shipped in the npm tarball and is not respected by npm. Consumers get floating
resolution instead.
Affected install methods
npx renovatenpm install -g renovatenpm install renovate(as a direct dependency)npm install renovateat build time without a lockfileConcrete example
renovatedepends onprotobufjs@8.2.0. However,@opentelemetry/otlp-transformer(atransitive dependency) declares
"protobufjs": "8.0.1"as a direct dependency with aseparate older version.
protobufjs@8.0.1in turn depends on@protobufjs/fetchand@protobufjs/inquireusing caret ranges (^1.1.0). These are resolved at install time towhatever version npm finds in the registry — including packages published after Renovate
itself was released.
This has already triggered blocks in security-hardened environments (e.g. Safe-chain
minimum package age policy):
Those versions are known-good and tested in our lockfile. But because npm re-resolves them
at install time, they appear as "new" unknown packages from the registry.
Real-world supply chain attacks on transitive npm packages
This class of issue is not theoretical. Multiple supply chain attacks have exploited the gap
between a package's declared direct dependencies and what actually gets installed transiently:
event-stream(2018) — a transitive dependency of thousands of packages was hijackedafter the original author transferred ownership. A malicious version was published targeting
a specific Bitcoin wallet. Downstream consumers received the malicious version because they
had floating transitive resolution.
ua-parser-js(2021) — the package was briefly taken over and malicious versions(
0.7.29,0.8.0,1.0.0) were published. Any project withua-parser-jsas atransitive dependency (including Facebook's
create-react-app) received the malware onnext install because versions were floating.
colors/faker(2022) — the maintainer intentionally published breaking/maliciousversions. Projects with
^ranges on these packages automatically pulled in the newversions.
axios(2023) — a protestware/malicious version was briefly published. Projectsfloating on
^1.xresolved the new version before it was removed.node-ipc(2022) — a widely-used transitive dependency was deliberately sabotaged withgeopolitically-targeted destructive code. Any project floating on the affected semver range
received it.
In each case, pinned lockfiles at the consuming project would have prevented automatic
uptake. But for CLI tools like Renovate that are installed directly from npm without a
project-level lockfile, the only protection is publishing a lockfile with the package itself.
Impact
Renovate release and a user's install
a compromised install could have severe blast radius
Expected behavior
Users installing Renovate via npm/npx should receive exactly the dependency versions that
were resolved in
pnpm-lock.yamlat publish time — the same versions CI tested.References
Beta Was this translation helpful? Give feedback.
All reactions