Malicious npm packages detected across Red Hat Cloud Services
github.com
github.com
I know people have opinions about cooldowns, but they would have saved you from axios, tanstack, (+ @redhat-cloud-services) and many other recent npm supply chain attacks. If you have Artifactory / Nexus, you probably already have cooldowns, but it's easy to set up if you don't. Why cooldowns? Most npm (or pypi) compromises were taken down within hours, cooldowns simply mean - ignore any package with release date younger than N days (1 day can work, 3 days is ok, 7 days is a bit of an overkill but works too)
How to set them up?
- use latest pnpm, they added 1 day cooldown by default https://pnpm.io/supply-chain-security
- or if you want a one click fix, use https://depsguard.com (cli that adds cooldowns + other recommended settings to npm, pnpm, yarn, bun, uv, dependabot and, disclaimer: I’m the maintainer)
- or use https://cooldowns.dev which is more focused on, well, cooldowns, with also a script to help set it up locally
All are open source / free.
If you know how to edit your ~/.npmrc etc, you don't really need any of them, but if you have a loved one who just needs a one click fix, these can likely save them from the next attack.
Caveat - if you need to patch a new critical CVE, you need to bypass the cooldown, but each of them have a way to do so (described in detail in depsguard.com / cooldowns.dev) In the past few months, while I don't have hard numbers, it seems more risk has come from Software Supply Chain attacks (malicious versions pushed) than from new zero day CVEs (even in the age of Mythos driven vulnerability discovery)
Generally you can also configure rules in your internal package cache about what to do if a package is missing from the cache/hasn't been pulled yet. They also commonly integrate automaticaly CVE tracking and pull statistics so they give a nice "heads up" what everyone is using even if it's a local PoC
As an added bonus, they can also lower bandwidth bills like in expensive cloud environments when you can co-locate the proxy close to CI/build machines.
And what's a proper cooldown? 1 day? 3 days? 1 week? 1 month? If you have a vulnerability, now you're exposed during that cooldown period. There's no straight forward or easy answer here.
I am speaking from my own experience here with having to sit in during these discussions where security "advice" is provided to the development team without understanding what it entails or any tradeoffs. I found that keeping things relatively secure is hard work and needs to be a part of culture.
> Exact version pinning — specifying precise versions (1.0.0, ==1.0.0, =1.0.0, = 5.31.0) rather than ranges (^, ~>, >=) in package manifests. Ranges allow any version satisfying the constraint to be resolved at install time; exact pins mean only one version is ever valid.
My understanding is that pinning the dependency within the manifest isn't the mechanism that prevents the version from changing across installs -- it's the lockfile that accomplishes this.
That's where the lockfile comes in, it pins the dependencies of the dependencies.
You end up with ancient dependencies because you add friction to periodic refreshes instead of running `package-manage refresh-lockfile` (whatever the relevant command is for your package manager)
In addition to cooldowns it'd be nice if more package managers did triage between security fixes and normal releases (bug fix / performance improvement / new functionalities).
It's totally possible to say: "A security fix must only be a security fix and cannot ship any other feature".
Then, for a start, a security fix becomes easier to audit (both by security researchers and by the tools security researchers are using).
And then a cooldown can be used for the regular releases (e.g. non security-related bug fixes, perfs improvements, new functionalities, etc.) but no cooldown (or a much smaller countdown) for security fixes.
Something has to be said about a system like Debian where you can have an extremely stable server and you can configure unattended upgrades to only apply security fixes but nothing else.
Such new package releases are easier to audit by security researchers.
A supply chain attack would likely able to publish a "security" release just as easily as a normal release, so I don't think that would help much.
Is there a sensible way to add a cooldown to Docker/Podman image pulling?
BTW it will only get much worse because popular AI coding harness (e.g. OpenCode/KiloCode) will just download random npm packages in the background without you knowing. And the devs don't care.
This feels like a very very small group of people; and people who really could do with opening the file and adding the line.
So yes, everyone could open a file and edit it, also everyone could watch a youtube video on how to do X and yet choose to have someone else do it for them :)
It is. Changing oil requires a place where you have sufficient access to the vehicle to drain it; the right equipment; the right disposal solutions. Most people who have cars do not have that. And it takes significantly more time to change your own oil than to have someone else do it as part of other specialist maintenance.
> Think of QR codes, people hardly used them for many years, because you needed to download an app for it, small step. It only started to catch up when you had it built in the camera app in most providers.
Exactly. Using a QR code app required specific knowledge of the app, an internet connection, some time, knowledge of how and when to use it, and something to use it with - the barrier of which surpassed the convenience gained from the QR code.
> So yes, everyone could open a file and edit it, also everyone could watch a youtube video on how to do X and yet choose to have someone else do it for them :)
I'm struggling to find a non-contrived group of people who:
- do not know how to open and edit a file on their system
- do use npm
- would find installing pnpm or running `sudo install -d -m 0755 /etc/apt/keyrings; curl -fsSL https://depsguard.com/apt/gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/depsguard.gpg; echo "deb [signed-by=/etc/apt/keyrings/depsguard.gpg] https://depsguard.com/apt stable main" | sudo tee /etc/apt/sources.list.d/depsguard.list >/dev/null; sudo apt update; sudo apt install depsguard` simpler
Of course, cooldowns.dev is a very long winded way of telling someone to run `npm config set min-release-age=3`, which is the simplest.
The JS ecosystem is really, really complicated, so any non-trivial app is going to use multiple bundlers, node runtimes, native runtimes, etc, etc, etc.
Every one of those has a different opinion about how to spell "cooldown".
On top of that, there's the bootstrapping issue of "I want to install the N pieces of ecosystem sprawl that read the .[p]npmrc that have the cooldown directive in them. How do I do that with a cooldown?" (Where N is unknowable, because of course it is.)
I'm not letting gam gams anywhere near that shit. She can continue writing her own apps in assembly language - it's good for her brain health!
by now, you should have received the feedback about why cooldowns don't make sense and why nobody is adopting them. look, you are writing an expression of the reason why right there.
- Most companies I know have a 24 hours (at least) cooldown via their Artifactory / Nexus. They have ways to bypass it for urgent CVEs
- pnpm just adopted 24 hours cooldown as default, based on community feedback.
- checking every update of every dependency to see if is a relevant urgent security update
- checking every update of every dependency to see if it turns out to be a supply chain exploit
am i still checking every update of every dependency? there's no heuristic here. either you check them all, or you get randomly exploited - either by using known vulnerable software or from supply chain attacked software.
There's plenty of comments mentioning delay lines, and the other good stuff pnpm (and others) have implemented in response to protect package consumers.
That bit that's getting less conversation is the tools on the package maintainer side:
- MFA for publishing: https://docs.npmjs.com/requiring-2fa-for-package-publishing-...
- trusted publishers, available for about a year: https://docs.npmjs.com/trusted-publishers
And as of recently, staged publishing, essentially combining the best of both those features: https://docs.npmjs.com/staged-publishing
Now you can: - Publish from CI, without static credentials
- AND require a maintainer to approve it using MFA before it actually goes live to the registry
If you want you can still use something like GitHub Actions Environments protection to require multiple approvals, or a time delay, on the CI side.
We need to encourage the community to adopt these publishing protections or this will continue to be an issue.
> - trusted publishers, available for about a year: https://docs.npmjs.com/trusted-publishers
According to [1] "All affected packages were published via GitHub Actions OIDC from the RedHatInsights/javascript-clients repository, indicating the upstream CI/CD pipeline itself was compromised."
So the malicious package would have gotten the happy little green star, with users assured it was "Built and signed with provenance."
The bug here isn't that we need to better authenticate already-trusted upstreams for packages, it's that the upstreams cannot be trusted as the sole source for security at all. Upstreams are a bunch of hackers[1] who aren't really interested in, nor will ever be good at, solid release engineering practices.
But some people are! The solution in the Linux world (and the one that saved us from xz-utils) is that there is a second level of human beings responsible for reviewing, auditing, packaging, and customizing those hacker-generated upstreams for the benefit of their users. These people have different eyes, different consumer requirements and different quality metrics. And they catch bugs and malfesance that the upstreams aren't prepared to do.
NPM (and cargo/PyPI et. al.) continues to think it can short circuit this requirement for human labor. It can't.
[1] In NPM's particular ecosystem, a bunch of web jockeys used to extremely fast release processes, loose compatibility requirements, and extreme reliance on reuse. This really explains why we see this with node packages more than Python or Rust: older and more conservative programmers just don't have as many rakes to step on.
AKA "unpaid labor". I don't think that's a good solution, either. Certainly it's only by pure luck that no malefactors have infiltrated the ad hoc, anonymous social proof communities that Linux depends on, and I don't think other systems should emulate it.
The real solution (for Linux too) is a paid package curation service. Or really, a small handful of them competing on price, speed, reliability.
> The real solution (for Linux too) is a paid package curation service. Or really, a small handful of them competing on price, speed, reliability.
That was also what I was thinking aloud a moment ago. And there would be a business opportunity, too. Perhaps not like RHEL et al. full-blown stuff per se, but say smaller scale guarantees with different pricing; web, AI, scientific computing, and whatnot. At the pace things are progressing, I'd guess you might even get desktop etc. users on board (for nominal pricing).
Yeah... no. Sorry, that's a wild misunderstanding of the economics of the Linux ecosystem, modern libertarian thought and the employment status of people with write access to the packaging layers.
Mandating that the final binary is compiled without having any access to any test file though would have prevented the xz-utils backdoor as it was conceived though.
A proper packaging setup would first verify that all the tests are passing and happen in an isolated environment. And that isolated environment either returns which tests failed or gives the green light.
When the greenlight is given (that all tests are passing), another environment should first delete all files related to tests and then build (in a bit-for-bit reproducible way btw and we're basically here already so that's good) the final binary / package.
If you prepare your final package in an environment that has access to test files, there are simply way too many ways obfuscated binary data can be hidden in test cases / test files.
I'm not saying the NSA (sorry, Jia Tan) wouldn't have tried something else but I think we should really move to build/packaging that discards non essential data/files before compiling.
P.S: note that as a side-effect of reproducible builds... If we have reproducible builds and if we add, later on, a builder/packager that discards tests files and ends up with a final package that's not bit-for-bit identical to the package created while having access to the test files during the build, we've just detected a backdoor hidden inside test files (like the XZ utils one). As a really mindboggling food for thoughts: if we were to recompile all the Debian binary packages that are already reproducible today (95% of them), but while discarding all tests files before the build, we may catch other backdoors.
It would, but I'm not seeing that as a suggestion? That was a very clever side channel for hiding the build-time payload. It wasn't remotely the "root cause" of the exploit, which was that a malicious actor got write access to the release process of trusted software. I mean, if you can do that, you can surely find other clever ways to hide your junk.
To wit: you're not wrong, you're just stuck on minutiae. By all means make the case, but at best you're proposing a small constant factor optimization.
If I'm reading the secondarily-linked blog post correctly, this was live for 12 days before discovery.
From the outside looking in, web dev has this frantic wild west energy to it. Mutability, dynamic typing, standards changing constantly, frameworks changing constantly, continuous delivery, CDNs, live A/B campaigns, large numbers of dependencies, sensitive user data spread out across a lot of infrastructure.
I'm not saying that's an accurate view and I don't think "I told you so" is the right attitude, but I can understand the place it comes from.
Same thing with C, Perl, PHP,…
I mean it keeps happening lmao. You can track npm attacks these on a calendar. Someone made a npm parody of the classic "no way to avoid this" The Onion article.
It's great there's work to stop it all but also... it keeps happening. I find it funny in a "here we go again" way.
> … the classic "no way to avoid this" The Onion article
But isn't point of The Onion article that A) the US has >50x as many incidents as the rest of the developed world combined [1], and yet B) acts like there is "no way to avoid this". Does NPM have >50x as many incidents as the rest of established languages combined? Is NPM claiming there is "no way to avoid this" or are they putting in place things like automatic install delays?
While all the major js package managers already support install delays, none of the big local C#/dotnet/nuget apps do (Visual Studio/Rider/nuget/dotnet/VS Code). https://github.com/NuGet/Home/issues/14657
[1] https://edition.cnn.com/2018/05/21/us/school-shooting-us-ver...
https://gist.github.com/mcollina/b294a6c39ee700d24073c0e5a4e...
The package axios was compromised, and hijacked the author's credentials, so every attempt at a fix was unfixed. https://www.trendmicro.com/en_us/research/26/c/axios-npm-pac...
The xz utility was backdoored for 2 months: https://gigazine.net/gsc_news/en/20240403-timeline-of-xz-ope...
A student researcher took over Python ctx and PHPass package maintainership, pushing out malicious changes, and that took over 7 days to be detected and fixed: https://infosecwriteups.com/how-i-hacked-ctx-and-phpass-modu...
Kaspersky found multiple PyPI packages that had been exploited for more than a year: https://www.kaspersky.com/about/press-releases/kaspersky-unc...
"LoftyLife" packages were exploited for several months: https://securelist.com/lofylife-malicious-npm-packages/10701...
Now that the attack window has changed to 7 days, all new exploits like these will come with time bombs to not trigger until 8 days.
Many automated scanners use static code analysis rather than run the installation script. Not all of them are caught, but a good part of them are and you'd be saved by a delay.
- When a feature you're developing, or a transitive dependency, requires an upgraded version, you can upgrade to the latest stable version that satisfies the dependency. But as each of those then requires an additional transitive dependency to be upgraded, you have more and more components upgraded to "latest", and the attack surface widens. So there are two alternatives:
1) (preferred) Upgrade to the latest version of the next-to-latest minor version, within the oldest major version that is supported, if that is available. This is the least number of changes that provides the needed functionality.
2) Upgrade only to the oldest version that gives you the functionality you need. If this ends up being the first version of a new major or minor version, this can cause bugs (initial releases of new major/minor always has bugs), so in that case you might as well use the latest version of that major/minor version.
This all affects security by avoiding upgrading to the latest version. It affects reliability by minimizing the amount of changes between your current version and upgraded version (changes lead to bugs).
The argument against all that, and for always upgrading to the latest versions, is intended to make software development easier. You avoid all the complexity of picking versions or reading changelogs by using software that is probably (but not always) all compatible. But it makes reliability and security worse. So you need to choose: do you want security and reliability, or an easier time writing code?
`pip install --uploaded-prior-to P7D pre-commit`
https://pip.pypa.io/en/stable/cli/pip_install/#cmdoption-upl...
https://github.com/NuGet/Home/issues/14657#issuecomment-3573...
huh? what do you suggest instead?
To give you a context, I get 20-30 PRs a week across all my repos with potentially hundreds of packages (non distinct) from dependabot. I give it a cursory look and try to get a summary of changes. Do I evaluate every single package update? Nope.
There is something to be said that Microsoft should be scanning packages pre-release. They aren't, though, so for right now there is a ton of value with very little downside if people implement a one week cooldown period.
To answer your question directly, though. If everyone else moves to a one week cooldown, I would absolutely suggest a two week cooldown is a good idea. Being the "slow" moving organization is a good security trade-off so long as you don't take it to extremes and have escape hatches when you actually need to be moving quickly.
Of course, this also assumes that Microsoft's internal scanners are much better than the scanners available to the attackers, since any reasonable attacker is going to just run their obfuscated code through a scanner as part of their CI job. (And maybe even use the MS scanner as an oracle by submitting fragments to NPM to see which pieces of their exploit chain get flagged.)
Waiting until everyone else canaries is much stronger, but even that doesn't work on a targeted attack.
* The JS ecosystem has been and will most likely continue to be fast-moving, so it's quite a safe assumption that at no point will a quarantine period be wide-spread.
* This quarantine period is for (semi-)automated scanners to catch the issue. Although considering the above there will always be a non-zero amount of end-user canaries as well.
* Maybe NPM should run scanners before distributing malware?
* If the ecosystem by any chance adopts a week-long quarantine period, you'd be safer if you applied a longer quarantine period.
1. Dependency cooldowns of 1-2 days seem to be extremely effective without negatively impacting your ability to patch for CVEs.
2. Anywhere you have `npm install` or `npm test` or anything where code executes, that should happen in an environment that has no privileges. In your github actions you can do this semi-straightforwardly by using two separate jobs - one to build the artifacts and test them, another to do any sort of publishing, signing, etc. If you use AI, add a skill / guidance to enforce this pattern.
3. If you use Github Actions, install the latest version of zizmor. It will significantly improve your posture.
(2) means that you are no longer "wormable", which is a massive part of the problem that we have today. (1) gives companies more time to respond to the attacks.
There are some vendors in this space that you can and should evaluate as well.
What if it gets compromised?
More of a joke. But was funny after saying that new packages should be delayed.
ALL the agentic orchestrators like codex, claude-code, etc. seem to do this by default.
I’m running a maven proxy locally. All builds happen inside containers. I only use public repos for python, npm, and go. So these builds happen also in containers but don’t need a repository proxy.
I'd suggest both. Cooldown for 1-2 days is very cheap and you likely won't even notice it, so it's quite harmless and from what I've seen even just 24 hours is enough to let security companies pick up malware.
But yeah, isolation is a must-have.
Or as us or companies to wrap the build tools to provide the wrapping for them.
It does make sense that the right way would be to fork every dependency you use and install from your own repo reviewing and merging from upstream as needed. Would be a giant PITA though. :)
I even managed to make that part of the workflow on one team I worked with but several other teams since thought it was a crazy idea. :)
That is, at least what we do, in theory. In practice, we cross fingers and let the LLM pick dependencies, are satisfied if it just works and we either update our deps frequently or infrequently.
1. Packj (https://github.com/ossillate-inc/packj) detects malicious PyPI/NPM/Ruby/PHP/etc. dependencies using behavioral analysis. It uses static+dynamic code analysis to scan for indicators of compromise (e.g., spawning of shell, use of SSH keys, network communication, use of decode+eval, etc). It also checks for several metadata attributes to detect bad actors (e.g., typo squatting).
I have never seen a project that uses npm and has only dozens of dependencies. Normal numbers are in the 10s of thousands (including different versions of some deps).
Gentoo graybeards know.
I'm trying to do all work in dev containers (or other sandboxes), limiting the blast radius if I'm unlucky enough to be hit by an exploit. The attackers may get a Claude token, but they won't easily be able to escape the container and scan my home dir.
Cooldowns and allow-listing of installer scripts are good additions to layered security, especially for CI. However, I think the fundamental thing that needs to change is the OS permissions model. The default of trusting third-party software with everything your user has access is no longer workable.
* Persist a volume for Claude so that conversations don't get blown away with every container rebuild. An attacker may still be able to get a Claude token from me, which is something I'd like to tighten up in the future.
* Fix file permissions issues by running rootful inside the container. (The container process still runs on the host as an ordinary user. Since my threat model is "compromised dependency scanning for credentials in project dir and home dir" rather than "attacker escaping the container", I figured that was good enough to get started.)
* Work around architectural availability issues with precompiled PyPI libraries. This I punted on by choosing a different approach and eliminating the problematic dependency (by writing my hobbyist CAD 3d printing stuff using Blender extensions instead of CadQuery). I've gotten the impression that dependency compatibility with a container workflow is an ongoing challenge.
* Run a database in a docker-compose sidecar for integration testing.
For all the projects I'm containerizing I'm the solo dev with full control over the Git repo so I can make the call to add a `.devcontainer/devcontainer.json` config file. I haven't yet explored how to isolate projects I don't control.
0: https://github.com/lovell/sharp/blob/main/install/build.js
https://sharp.pixelplumbing.com/install/#prebuilt-binaries
It can usually also download a precompiled binary for the C++ shim that sits between node and libvips, but if your node / arch / etc. is not supported, it'll compile that (that's what the build.js file you linked does).
https://www.stepsecurity.io/blog/multiple-redhat-cloud-servi...
I know of fundamental issues with JavaScript and see no reason why it's still standard on all web browsers.
1) Update by default. Manually updating your package references is annoying and does lead to other security issues as you don't automatically get latest, but it makes this risk much lower.
2) Code executed on install. Statically-typed languages don't run the code until you use them, and that might not happen on the developer machine at all for first run after upgrade, it might be a lower-priv test-server.
3) Culture of many tiny modules (this is good! It's the natural way to fight NIH! Yay modularity!) means many more points-of-failure for security for this kind of attack.
Thankfully, it's on by default since v11.
min-release-age=5
Go look in that projects issue tracker and commit log for changes to relevant configuration and you will know what I mean.
Even yarn 1.22 is a safer choice.
But first, you will of course not remake every single feature, just the one you need.
And furthermore, when you code just one feature, you don't need to make any abstraction or additional function interfaces. So it's cheaper, and probably better integrated.
Another fallacy is that you'll make bugs and introduce vulnerabilities. Maybe, if you are a bad programmer, but you will also avoid a category of bugs where the vuln is introduced at the boundary of the integration between two different libraries that weren't designed to fit exactly together. (Many such cases)
On the browser at least you have the excuse that there's no other option (hoping Wasm will eventually kill JS for good, but we're not there yet).
So if you have an unpinned version of this package and you run 'npm install', you immediately downloaded the compromised version and that's that.
[0] https://github.com/RedHatInsights/javascript-clients/commit/...
Before, the source code was located somewhere, and the CI was usually located somewhere else, and slightly unrelated. At first, the CI job was to build ("privately") the artefacts, and they were manually released and deployed by maintainers and owner of software projects.
Then, it became the norm to have the CI located within the VCS file and the VCS located source code controlling the CI. For example having "script"/"description" of the CI actions located within the VCS itself.
Then, Github killed the CI/CD software market by offering "actions" almost for free and totally integrated within Github that was already widely used.
But still, for a long time people were wary to put tokens and security keys in Github and "public" CI/CD jobs and services.
And then, a few years ago, it became the gold standard also... You would look ridiculous to manually sign and deploy new releases with well guarded keys. What is expected from you is to have all your aws, github, ... secret api keys loaded in Github, and have your deployment and infrastructure provisioning automated with ("public") github accounts. All to be deployable in a second of a change being pushed.
So, obviously, the moment a hacker get control of a Github account or Github API keys, it is game over for the entire infrastructure.
Here I only referenced Github, but the bad things that it taught us became the norm and now these patterns are replicated everywhere (Gitlab, ...)
AbstractFinalFactoryShaiHuludSerialisedFactoryIf you want paranoid mode, you can verify literally every part of the maven build process.
Quote:
> The affected packages are frontend libraries that are compiled and bundled into some container images during the Red Hat product build process.
So you might be affected if you deployed any Red Hat container images built after the compromise, I guess. (I doubt anyone besides Red Hat is using the affected packages directly.)
Updated:
1. All exploitation techniques used since May 2025: https://npm-supply-chain-attack-techniques.pagey.site/
2. All attacks that happened since May 2025: https://npm-supply-chain-attacks-25-26.pagey.site/
The alternative proposal with "trusted" publishing is to grant all of GitHub's automated infrastructure the ultimate authority to publish things on your behalf automatically in a way that it is shown as being more "trusted" than the developer uploading it themselves! I'm honestly surprised that it took this long for supply-chain attackers to start targeting this obvious security hole -- I doubt any user of GitHub would argue that GitHub Actions are a parogon of security, and yet the strongly recommended deployment workflow for several language communities involved bolting it into the core of your release mechanisms! I think the OCID stuff is interesting and provides some nice properties but it doesn't prove that the GHA workload is actually trusted in the sense that "trusted publishing" means.
The arguments for adding speed bumps to auto-applying updates apply just as much to adding speed bumps to doing releases (arguably even more so -- even rapidly evolving libraries don't have hundreds of releases a week). Automating everything to the nth degree just expands the blast radius when stuff like this happens.
I do all runc releases manually and sign them with a PGP key stored in a hardware token that requires a pin for every signature attempt. Yes, it's a bit more cumbersome but it certainly would be harder to supply-chain hundreds of projects of every maintainer would need to manually publish them. For some other projects that need to publish to crates.io or PyPI I was honestly a little dismayed at how prominently they push for "trusted" publishing and how little support there is for making the "dumb" publishing flow more secure (they even visually display such releases as less trusted, and the only way to get more green checkmarks is to let GitHub take the wheel).
Also detonated the payload: https://leitwacht.eu/blog/valid-provenance-malicious-package
Though I would expect that Insights uses RPM packages to ship components and not the public NPM packages.
Setting min age is useless if everyone is doing it. The whole point of setting min age is make someone else take the bait before you.
Is it really so hard for people to make releases manually?
Edit: some people don't understand that it's a defence to https://en.wikipedia.org/wiki/%27No_Way_to_Prevent_This,%27_...
That's an INSANE default. pnpm, by contrast, allows you to essentially "opt-in" only specific packages that need this (e.g. four out of thirty, in one of our projects). Then tacks on tons of other security settings, like minimum age, no trust downgrade, etc etc.
All attackers can attack packages by updating how a package functions; but npm is particularly problematic as it runs non-sandbox scripts as the calling user. Putting not just your project at risk, but your entire machine/network.
And this stuff has been known about for YEARS, they've taken no action.
In about 99% of cases, I have the option to pick between Microsoft, a 3rd party or myself. I'm picking that first option every time I can. If M$ can't handle it, I'm hand rolling it.
Dapper remains the only constant 3rd party dependency in my projects. I don't know how much longer this will last with LLM assistance. The frontier models are very good at writing repositories over arbitrary sql schemas with low level primitives now.
As opposed to the completely untrusted package supplied arbitrary code that the logged in user executes when they actually use the package immediately after installing it?
Regarding npm CLIENTS, PNPM is fundamentally different from (and superior to) npm or yarn.
Strongest possible recommendation to use pnpm.
It's also a good idea to use a private registry (eg via jfrog), acting as a proxy / pull-through cache, and point trad SAST and maybe AI scanners at it.
But dropping the npm client in favor of pnpm is a no-brainer. Speed, disk space, security, determinism, flexibility, fine-grained control over your dependency graph...
Between average hackers and extortion groups, foreign governments and state sponsored actors and last but not least my own government, I don't think there's much room left for non-compromised supply chains these days. Treat everything that can run foreign code as potentially compromized and keep everything compartmentalized. If you keep your crypto wallets or private banking info on the same machine where you do development, you're asking to get shafted one day. Or if you keep your big corporate github keys on the same machine where you do private weekend projects. It doesn't matter what you use in particular, even if some vectors are currently more popular than others.
rm -rf pkg/snippets & rmdir pkg\\snippets /s /q & wasm-pack build --target bundler && node prepare-web.js
Looked like a strange mix of unix shell and msdos batch that would, on my box, try to rmdir "/s" and "/q". I asked Claude about this, and he replied something like "Yes that's a standard and clever hack to delete a directory that works both on linux and windows!".Poor Claude has been trained on so much awful human code that it required several prompts for it to admit that there was indeed a problem.
The industry is the process by which convenient crap like this gets standardized.
This is semi-common and in no way unique to NPM.
I agree that not running arbitrary installation scripts is the right default, but it's just an incremental improvement.
The practical difference between code that runs at installation and code that runs when the package is executed is, very typically, a small amount of time.
IMO, the hyperbole here hurts because it distracts from more effective efforts.
Not running lifecycle scripts by default is eventually going to be the default behavior. Late is worse (edit: I meant better) than not at all. https://github.com/npm/rfcs/pull/868
pnpm can still be exposed, afterall the worm simply have to wait you run tests locally.
SIXTEEN YEARS of development and they can't even resolve a tree of dependencies in the correct manner unless you nuke the lockfile and node_modules.
Dependency resolution is literally the number one task and they fail at it. How can you expect them to be good at anything else? Absolute joke.
I went through the package.json on my machine - seems like ~400 / 60000 or 0.7% have (pre|post)install. (That's not all of the scripts that run at install)
Seems to me like a backwards compatibility is a non argument since pnpm is popular enough to stand as existence proof that scripts can be, at least, opt-in
IMO - pre- and post- install scripts should just be abolished/deprecated. It should require a special dispensation from npm to even publish one. A better system for binaries (needed by esbuild) is probably needed.
Even saying "just use pnpm" isn't enough, we need to get the developer community to herd immunity and that isn't going to happen on an opt-in basis.
I would love for npm to sandbox as well. But I think the better way forward is just turn off scripts.
This makes it so an update to a popular library can compromise a huge number of packages that depend on it.
In Java for example almost all packages specify a concrete version, even if someone compromises the latest the blast radius is usually pretty small.
It's also the standard, and by far it's the contrast to not allow this. pnpm has a massive advantage of being the non-standard package manager, npm does not have that - what do you suggest that npm does?
Many package formats before NPM allowed for it, and frankly, it matters little, because if it can add code to your app it can run malicious code. The fact it executes on package install rather than when dev runs tests or the app matters little, and in general if environment is sandboxes, the package install is also ran in the same sandbox so disallowing it changes little.
so yes, every package manager can be hit, the reason is twofold
* JS is such a lowest common denominator it has that much more clueless users so just by scale every issue will be more common than in other languages
* extreme fragmentation leading to hundreds of packages needed for even small projects, which is again more chances for compromise
It's not unreasonable: you're already installing software, which presents risks. If post-install scripts were not a thing, a payload could still run because you ran the software you installed. Or because the installer added it to auto-run. Or because the installer placed it somewhere where it would be dynamically loaded all the time.
Cargo,PyPi,Nuget,PHP has had these recent too.
It's not just only NPM. It's frequently repeated here just cause of the average bias against Node.
But this problem isn't isolated to NPM.
It’s frequently repeated here because NPM is where it keeps happening over and over and over and over and over and over again.
PyPI, May 11. [1]
Crates.io, May 22 [2]
Composer, May 22 [3]
[1] https://www.tenable.com/blog/mini-shai-hulud-frequently-aske...
[2] https://socket.dev/blog/trapdoor-crypto-stealer-npm-pypi-cra...
[3] https://phoenix.security/laravel-lang-composer-supply-chain-...
So, explicitly:
- pip
- Cargo
- apt/dpkg
- dnf/yum
- Homebrew
- RubyGems
- Composer (limited)
- Maven
...all allow scripts.
We understand the reference, it's just not correct: most package managers allow scripts, npm is the most successful package manager.
npm shouldn't allow scripts, but exploits happen everywhere.
Also not all maintainers always pull in the latest upstream changes, only rebasing to new stable release or when the new features or fixes are actually needed for the distro stack.
Definitely not bulletproof but still IMHO more robust than "Lets just spray latest code from upstream without any review directly to production with a firehose!" that seems to be the norm.
¹ Annotation processors are a thing and somewhat similar to rust macros in function, but you need to set those up manually for each dependency, iirc.
Got downvoted for saying it too. Don't let it discourage you.
Indeed, AUR is bad as a software distribution mechanism (really it's best understood as a proving ground for baby packages before they get real maintainers and distro blessing), but it's less bad than NPM which puts the malware in the trusted/default/automated path.
(Everyone claps.)
You bring up a good point that this class of problem, or related ones can occur with other package managers. It was frustrating how long it took the Crates.io team (Rust manager) to address name squatting, in what appeared to be a "no perfect solution exists, so we won't act" line of reasoning.
Why would you target xyz pkg niche manager knowing that only 200 people will install them?
NPM does perform active offline & online vuln scanning on the packages. Everyone can do more, but they are going to be the #1 target.
How do you propose we address this issue? Instead of policing what people say, are you interested in sharing your or someone else ideas?
Oh dear. Here we go again.
I feel like that would at least catch some of these
How about simply coming up with new standards that emcure such things don't happen at all?
All of these recent incidents is just developers doing stupid things ... like using their compromised devices for making production changes, which is basically a big red flag to begin with.
In fact, the entire situation has been exacerbated by coding agents because now practically everything happens on a single device that touches hundreds of different production systems with full production credentials.
Days since last malicious packages in PyPI: 30
Days since last malicious packages in Maven: 120
I'm sure this isn't 100% accurate, and there are probably better metrics (average number of malicious packages per year, average number of developers affected per year, etc) but they aren't as easy as a quick Google News search.
https://chatgpt.com/share/6a1da751-0d88-832e-ace7-572bc786e0...
Check the linked resource which has the actual data.
It’s not even really the fault of the people who pushed for these setups, it’s a seemingly simple business decision: build it in a way that looks secure, add some black-box process, and tell the overseers that the reason there are no attacks is because it’s bulletproof, and definitely not because no one has really tried
Then, when someone finally turns their attention to you and walks in: fire whoever needs to be fired, patch that specific hole, maybe spend a bunch of money on a different system, assure the overseers that it’s handled, and move on with business as usual
It’s cheaper in the long-run, it makes stockholders happy, it relieves the bosses and their bosses, and for the most part there are “no security holes”.
Until now, of course