Parent comment is an indirect reference to US mass shootings:.
> "'No Way to Prevent This,' Says Only Nation Where This Regularly Happens" is the recurring headline of articles published by the American news satire organization The Onion after mass shootings in the United States.
I would say that npm likely has easier solutions here compared to Cargo.
Well before the npm attacks were a thing, we within the Rust project, have discussed a lot of using wasm sandboxing for build-time code execution (and also precompiled wasm for procedural macros, but that's its own thing.) However the way build scripts are used in the Rust ecosystem makes it quite difficult enforce sandbox while also enabling packages to build foreign code (C, C++ invoke make, cmake, etc.) The sandbox could still expose methods to e.g. "run the C compiler" to the build scripts, but once that's done they have an arbitrary access to a very non-trivial piece of code running in a privileged environment.
Whereas for Javascript rarely does a package invoke anything but other javascript code during the build time. Introduce a stringent sandbox for that code (kinda deno style perhaps?) and a large majority of the packages are suddenly safe by default.
This is a cultural problem created through a fundamental misunderstanding (and mis-application) of Unix philosophy. As far as I'm aware the Rust ecosystem doesn't have a problem appropriately sizing packages which in turn reduces the overall attack surface of dependencies.
It's kinda funny because "Unix philosophy" was never a coherent thing in the first place. Arguably Plan 9 came the closest to that in practice, but, well, you might note it didn't exactly have a strong uptake. Unix itself is a pile of hacks though, and it's both sad and amusing to watch people trying to divine some kind of methodology out of that.
This has nothing to do with package sizes. Cargo was just hit with a phishing campaign not too long ago, and does still use tokens for auth. NPM just has a wider surface area.
It's not just the popularity, it's partly the update mechanism and partly the culture. In what sane world would you always pull in all the newest things, regardless or whether you need them or not? This is a default at build time for so many setups. If you absolutely must use that package manager, at least lock down your versions, and update selectively. I don't even know if that's possible to do with the dependencies' dependencies (and so on), or are people forced to just pull in whatever, every time.
An example: Java Maven artifacts typically name the exact version of their dependencies. They rarely write "1.2.3 or any newer version in the 1.2.x series", as is the de-facto standard in NPM dependencies. Therefore, it's up to each dependency-user to validate newer versions of dependencies before publishing a new version of their own package. Lots of manual attention needed, so a slower pace of releases. This is a good thing!
Another example: all Debian packages are published to unstable, but cannot enter testing for at least 2-10 days, and also have to meet a slew of conditions, including that they can be and are built for all supported architectures, and that they don't cause themselves or anything else to become uninstallable. This allows for the most egregious bugs to be spotted before anyone not directly developing Debian starts using it.
You forgot to mention it is also tied to provable namespaces. People keep saying that NPM is just the biggest target...
Hate to break it to you but from targeting enterprises, java maven artifacts would be a MASSIVE target. It is just harder to compromise because NPM is such shit.
> An example: Java Maven artifacts typically name the exact version of their dependencies. They rarely write "1.2.3 or any newer version in the 1.2.x series"
You can definitely do this.
To be honest, you just end up with the same thing via dependabot/renovate.
Other languages seem to publish dependencies as self-contained packages whose installation does not require running arbitrary shell scripts.
This does not prevent said package from shipping with malware built in, but it does prevent arbitrary shell execution on install and therefore automated worm-like propagation.
You have separate people called "maintainers", and they're the ones who build and upload packages to the repository. Crucially, they're not the people who write the software. You know, like Linux has been doing since forever. https://wiki.debian.org/DebianMaintainer Instead of treating your package repository like a trash can at a music festival, you can treat it more like a museum, curated by experts. Unfortunately, this isn't quite the devil-may-care attitude the Node ecosystem is so accustomed to, and will be met with a lot of whining, so it never happens. See y'all in two weeks when this happens again.
That literally makes no difference at all. You’ll just vendor the malicious versions. No, a lock file with only exact versions is the safe path here. We haven’t seen a compromise to existing versions that I know of, only patch/minor updates with new malicious code.
I maintain that the flexibility in npm package versions is the main issue here.
I think some system would need to dynamically analyze the code (as it runs) and record what it does. Even then, that may not catch all malicious activity. It's sort of hard to define what malicious activity is. Any file read or network conn could, in theory, be malicious.
As a SW developer, you may be able to limit the damage from these attacks by using a MAC (like SELinux or Tomoyo) to ensure that your node app cannot read secrets that it is not intended to read, conns that it should not make, etc. and log attempts to do those things.
You could also reduce your use of external packages. Until slowly, over time you have very little external dependencies.
Hire an antivirus company to provide a safe and verified feed of packages. Use ML and automatic scanners to send packages to manual review. While Halting problem prevents us from 100% reliably detecting malware, at least we can block everything suspicious.
Parent comment is an indirect reference to US mass shootings:.
> "'No Way to Prevent This,' Says Only Nation Where This Regularly Happens" is the recurring headline of articles published by the American news satire organization The Onion after mass shootings in the United States.
Source: https://en.wikipedia.org/wiki/%27No_Way_to_Prevent_This,%27_...
See also Xe Iaso's posts about CVEs in the C ecosystem (https://xeiaso.net/shitposts/no-way-to-prevent-this/CVE-2025...)
There's nothing technically different between NPM and, say, Cargo, here that would save Cargo, is there?
I would say that npm likely has easier solutions here compared to Cargo.
Well before the npm attacks were a thing, we within the Rust project, have discussed a lot of using wasm sandboxing for build-time code execution (and also precompiled wasm for procedural macros, but that's its own thing.) However the way build scripts are used in the Rust ecosystem makes it quite difficult enforce sandbox while also enabling packages to build foreign code (C, C++ invoke make, cmake, etc.) The sandbox could still expose methods to e.g. "run the C compiler" to the build scripts, but once that's done they have an arbitrary access to a very non-trivial piece of code running in a privileged environment.
Whereas for Javascript rarely does a package invoke anything but other javascript code during the build time. Introduce a stringent sandbox for that code (kinda deno style perhaps?) and a large majority of the packages are suddenly safe by default.
This is a cultural problem created through a fundamental misunderstanding (and mis-application) of Unix philosophy. As far as I'm aware the Rust ecosystem doesn't have a problem appropriately sizing packages which in turn reduces the overall attack surface of dependencies.
It's kinda funny because "Unix philosophy" was never a coherent thing in the first place. Arguably Plan 9 came the closest to that in practice, but, well, you might note it didn't exactly have a strong uptake. Unix itself is a pile of hacks though, and it's both sad and amusing to watch people trying to divine some kind of methodology out of that.
This has nothing to do with package sizes. Cargo was just hit with a phishing campaign not too long ago, and does still use tokens for auth. NPM just has a wider surface area.
I agree, but imo the Rust ecosystem has the same problem. Not to the extent of NPM, but worse than C/C++.
No Preventative Measures (NPM)
You can host your own NPM reg, and examine every package, but your manager probably is NOT going to go for that.
Sounds like something a union should enforce as part of a drive to protect programmer professionalism.
The circumstances for this are not unique to NPM. The popularity is what makes it so susceptible to these attacks.
It's not just the popularity, it's partly the update mechanism and partly the culture. In what sane world would you always pull in all the newest things, regardless or whether you need them or not? This is a default at build time for so many setups. If you absolutely must use that package manager, at least lock down your versions, and update selectively. I don't even know if that's possible to do with the dependencies' dependencies (and so on), or are people forced to just pull in whatever, every time.
Okay then, tell me a way to prevent this.
An example: Java Maven artifacts typically name the exact version of their dependencies. They rarely write "1.2.3 or any newer version in the 1.2.x series", as is the de-facto standard in NPM dependencies. Therefore, it's up to each dependency-user to validate newer versions of dependencies before publishing a new version of their own package. Lots of manual attention needed, so a slower pace of releases. This is a good thing!
Another example: all Debian packages are published to unstable, but cannot enter testing for at least 2-10 days, and also have to meet a slew of conditions, including that they can be and are built for all supported architectures, and that they don't cause themselves or anything else to become uninstallable. This allows for the most egregious bugs to be spotted before anyone not directly developing Debian starts using it.
You forgot to mention it is also tied to provable namespaces. People keep saying that NPM is just the biggest target...
Hate to break it to you but from targeting enterprises, java maven artifacts would be a MASSIVE target. It is just harder to compromise because NPM is such shit.
2 replies →
> An example: Java Maven artifacts typically name the exact version of their dependencies. They rarely write "1.2.3 or any newer version in the 1.2.x series"
You can definitely do this.
To be honest, you just end up with the same thing via dependabot/renovate.
1 reply →
Other languages seem to publish dependencies as self-contained packages whose installation does not require running arbitrary shell scripts.
This does not prevent said package from shipping with malware built in, but it does prevent arbitrary shell execution on install and therefore automated worm-like propagation.
You have separate people called "maintainers", and they're the ones who build and upload packages to the repository. Crucially, they're not the people who write the software. You know, like Linux has been doing since forever. https://wiki.debian.org/DebianMaintainer Instead of treating your package repository like a trash can at a music festival, you can treat it more like a museum, curated by experts. Unfortunately, this isn't quite the devil-may-care attitude the Node ecosystem is so accustomed to, and will be met with a lot of whining, so it never happens. See y'all in two weeks when this happens again.
Build packages from source without any binaries (all the way down) and socially audit the source before building.
https://bootstrappable.org/ https://reproducible-builds.org/ https://github.com/crev-dev
The same way it always has been done - vendor your deps.
That literally makes no difference at all. You’ll just vendor the malicious versions. No, a lock file with only exact versions is the safe path here. We haven’t seen a compromise to existing versions that I know of, only patch/minor updates with new malicious code.
I maintain that the flexibility in npm package versions is the main issue here.
7 replies →
To be fair this does only work in ecosystems where libraries are stable and don't break every 3 months as it often happens on the JS world.
You can vendor your left-pad, but good luck doing that with a third-party SDK.
1 reply →
that's what I do whenever feasible. Which is often
I think some system would need to dynamically analyze the code (as it runs) and record what it does. Even then, that may not catch all malicious activity. It's sort of hard to define what malicious activity is. Any file read or network conn could, in theory, be malicious.
As a SW developer, you may be able to limit the damage from these attacks by using a MAC (like SELinux or Tomoyo) to ensure that your node app cannot read secrets that it is not intended to read, conns that it should not make, etc. and log attempts to do those things.
You could also reduce your use of external packages. Until slowly, over time you have very little external dependencies.
Other than general security practices, here are few NPM ecosystem specific ones: https://github.com/bodadotsh/npm-security-best-practices
Hire an antivirus company to provide a safe and verified feed of packages. Use ML and automatic scanners to send packages to manual review. While Halting problem prevents us from 100% reliably detecting malware, at least we can block everything suspicious.