Skip to content(if available)orjump to list(if available)

NPM debug and chalk packages compromised

NPM debug and chalk packages compromised

258 comments

·September 8, 2025

junon

Hi, yep I got pwned. Sorry everyone, very embarrassing.

More info:

- https://github.com/chalk/chalk/issues/656

- https://github.com/debug-js/debug/issues/1005#issuecomment-3...

Affected packages (at least the ones I know of):

- ansi-styles@6.2.2

- debug@4.4.2 (appears to have been yanked as of 8 Sep 18:09 CEST)

- chalk@5.6.1

- supports-color@10.2.1

- strip-ansi@7.1.1

- ansi-regex@6.2.1

- wrap-ansi@9.0.1

- color-convert@3.1.1

- color-name@2.0.1

- is-arrayish@0.3.3

- slice-ansi@7.1.1

- color@5.0.1

- color-string@2.1.1

- simple-swizzle@0.2.3

- supports-hyperlinks@4.1.1

- has-ansi@6.0.1

- chalk-template@1.1.1

- backslash@0.2.1

It looks and feels a bit like a targeted attack.

Will try to keep this comment updated as long as I can before the edit expires.

---

Chalk has been published over. The others remain compromised (8 Sep 17:50 CEST).

NPM has yet to get back to me. My NPM account is entirely unreachable; forgot password system does not work. I have no recourse right now but to wait.

Email came from support at npmjs dot help.

Looked legitimate at first glance. Not making excuses, just had a long week and a panicky morning and was just trying to knock something off my list of to-dos. Made the mistake of clicking the link instead of going directly to the site like I normally would (since I was mobile).

Just NPM is affected. Updates to be posted to the `/debug-js` link above.

Again, I'm so sorry.

winwang

Just want to agree with everyone who is thanking you for owning up (and so quickly). Got phished once while drunk in college (a long time ago), could have been anyone. NPM being slowish to get back to you is a bit surprising, though. Seems like that would only make attacks more lucrative.

33a

We also caught this right away at Socket,

https://socket.dev/blog/npm-author-qix-compromised-in-major-...

While it sucks that this happened, the good thing is that the ecosystem mobilized quickly. I think these sorts of incidents really show why package scanning is essential for securing open source package repositories.

joaomoreno

From sindresorhus:

You can run the following to check if you have the malware in your dependency tree:

`rg -u --max-columns=80 _0x112fa8`

Requires ripgrep:

`brew install rg`

https://github.com/chalk/chalk/issues/656#issuecomment-32668...

cgijoe

Sorry, I am unfamiliar with ripgrep. Is this simply scanning for the string `_0x112fa8`? Could we do the same thing with normal grep -r?

skrebbel

yes. ripgrep just does it faster, is all.

EasyMark

[flagged]

koolba

Try the same recursive grep on ~/.npm to see if you have it cached too. Not just the latest in the current project.

timsh

If it produces no output, does that mean that there's no code that could act in the future? I first acted out of nerves and deleted the whole node-modules and package.lock in a couple of freshly opened Astro projects, curious if I should considered my web surfing to still be potentially malicious

nosefurhairdo

The malware introduced here is a crypto address swapper. It's possible that even after deleting node_modules that some malicious code could persist in a browser cache.

If you have crypto wallets on the potentially compromised machine, or intend to transfer crypto via some web client, proceed with caution.

kantord

Correct me if I'm wrong, but it seems like it's enough to check a shorter substring and for only .js files.

This way it's fast enough to scan for your root folder (`/`) in a few seconds depending on your machine.

out="$(rg -u --max-columns=80 _0x112 --glob '*.js' / | tee /dev/tty)"; [ -n "$out" ] && echo " INFECTED" || echo " NOT INFECTED"

hackerindio

Hey, no problem, man. You do a lot for the community, and it's not all your fault. We learn from our mistakes. I was thinking of having a public fake profile to avoid this type of attack, but I'm not sure how it would work on the git tracking capabilities. Probably keeo it only internally for you&NPM ( the real one ) and have some fake ones open for public but not sure, just an obfuscated idea. Thanks for taking the responsibility and working in fixing ASAP. God bless you.

pryelluw

Thank you for your service.

Please take care and see this as things that happen and not your own personal failure.

zachleat

Yo, someone at npm needs to unpublish simple-swizzle@0.2.3 IMMEDIATELY. It’s still actively compromised.

junon

It's been almost two hours without a single email back from npm. I am sitting here struggling to figure out what to do to fix any of this. The packages that have Sindre as a co-publisher have been published over but even he isn't able to yank the malicious versions AFAIU.

If there's any ideas on what I should be doing, I'm all ears.

EDIT: I've heard back, they said they're aware and are on it, but no further details.

zachrip

Thanks for sounding the alarm. I've sent an abuse email to porkbun to hopefully get the domain taken down.

junon

Thank you, I appreciate it! I did so as well and even called their support line to have them escalate it. Hopefully they'll treat this as an urgent thing; I'd imagine I'm far from the only one getting these.

zachrip

It's down, so there's some good news. Probably worth submitting to IC3 as well.

kidk

Could happen to any of us. Thanks for reacting so quickly!!

DDerTyp

One of the most insidious parts of this malware's payload, which isn't getting enough attention, is how it chooses the replacement wallet address. It doesn't just pick one at random from its list.

It actually calculates the Levenshtein distance between the legitimate address and every address in its own list. It then selects the attacker's address that is visually most similar to the original one.

This is a brilliant piece of social engineering baked right into the code. It's designed to specifically defeat the common security habit of only checking the first and last few characters of an address before confirming a transaction.

We did a full deobfuscation of the payload and analyzed this specific function. Wrote up the details here for anyone interested: https://jdstaerk.substack.com/p/we-just-found-malicious-code...

Stay safe!

josefbud

I'm a little confused on one of the excerpts from your article.

> Our package-lock.json specified the stable version 1.3.2 or newer, so it installed the latest version 1.3.3

As far as I've always understood, the lockfile always specifies one single, locked version for each dependency, and even provides the URL to the tarball of that version. You can define "x version or newer" in the package.json file, but if it updates to a new patch version it's updating the lockfile with it. The npm docs suggest this is the case as well: https://arc.net/l/quote/cdigautx

And with that, packages usually shouldn't be getting updated in your CI pipeline.

Am I mistaken on how npm(/yarn/pnpm) lockfiles work?

bflesch

Can you attribute this technique to a specific group?

suzzer99

A few years ago, I remember reading about some NFT contract attack that did something similar. So I'm sure it's out there now.

oasisbob

> This is a brilliant piece of social engineering baked right into the code. It's designed to specifically defeat the common security habit ...

I don't agree that the exuberance over the brilliance of this attack is warranted if you give this a moment's thought. The web has been fighting lookalike attacks for decades. This is just a more dynamic version of the same.

To be honest, this whole post has the ring of AI writing, not careful analysis.

NoahZuniga

> To be honest, this whole post has the ring of AI writing, not careful analysis.

No it doesn't?

withinboredom

> To be honest, this whole post has the ring of AI writing, not careful analysis.

It has been what, hours? since the discovery? Are you expecting them to spend time analysing it instead of announcing it?

Also, nearly everyone has AI editing content these days. It doesn’t mean it wasn’t written by a human.

cddotdotslash

NPM deserves some blame here, IMO. Countless third party intel feeds and security startups can apparently detect this malicious activity, yet NPM, the single source of truth for these packages, with access to literally every data event and security signal, can't seem to stop falling victim to this type of attack? It's practically willful ignorance at this point.

PokestarFan

NPM is owned by GitHub and therefore Microsoft, who is too busy putting in Copilot into apps that have 0 reason to have any form of generative AI in them

a022311

After all these incidents, I still can't understand why package registries don't require cryptographic signatures on every package. It introduces a bit more friction (developers downloading CI artifacts and manually signing and uploading them), but it prevents most security incidents. Of course, this can fail if it's automated by some CI/CD system, as those are apparently easily compromised.

parliament32

Real registries do[1], npm is just amateur-hour which is why its usage is typically forbidden in enterprise contexts.

[1] https://www.debian.org/doc/manuals/securing-debian-manual/de...

9dev

In all fairness—npm belongs to GitHub, which belongs to Microsoft. Amateur-hour is both not a valid excuse anymore, and also a boring explanation. GitHub is going to great lengths to enable SLSA attestations for secure tool chains; there must be systemic issues in the JS ecosystem that make an implementation of proper attestations infeasible right now, everything else wouldn't really make sense.

So if we're discussing anything here, why not what this reason is, instead of everyone praising their favourite package registry?

anonfordays

>npm is just amateur-hour

Understatement of the year. When a project/ecosystem is more interested in putting virtue signaling trolls like Ashley "Kill all men" Williams in leadership positions versus actual technologists, it shows.

Joker_vD

Mmm. But how does the package registry know which signing keys to trust from you? You can't just log in and upload a signing key because that means that anyone who stole your 2FA will log in and upload their own signing key, and then sign their payload with that.

I guess having some cool down period after some strange profile activity (e.g. you've suddenly logged from China instead of Germany) before you're allowed to add another signing key would help, but other than that?

9dev

Supporting Passkeys would improve things; not allowing releases for a grace period after adding new signing keys and sending notifications about this to all known means of contact would improve them some more. Ultimately, there will always be ways; this is as much a people problem as it is a technical one.

simpaticoder

I've come to the conclusion that avoiding the npm registry is a great benefit. The alternative is to import packages directly from the (git) repository. Apart from being a major vector for supply-chain attacks like this one, it is also true that there is little or no coupling between the source of a project and its published code. The 'npm publish' step takes pushes local contents into the registry, meaning that a malefactor can easily make changes to code before publishing.

HexDecOctBin

As a C developer, having being told for a decade that minimising dependencies and vendoring stuff straight from release is obsolete and regressive, and now seeing people have the novel realisation that it's not, is so so surreal.

Although I'll still be told that using single-header libraries and avoiding the C standard library are regressive and obsolete, so gotta wait 10 more years I guess.

aabbccsmith

npm's recent provenance feature fixes this, and it's pretty easy to setup. It will seriously help prevent things like this from ever happening again, and I'm really glad that big packages are starting to use it.

cstrahan

> The alternative is to import packages directly from the (git) repository.

That sounds great in theory. In practice, NPM is very, very buggy, and some of those bugs impact pulling deps from git repos. See my issue here: https://github.com/npm/cli/issues/8440

Here's the history behind that:

Projects with build steps were silently broken as late as 2020: https://github.com/npm/cli/issues/1865

Somehow no one thought to test this until 2020, and the entire NPM user base either didn't use the feature, or couldn't be arsed to raise the issue until 2020.

The problem gets kinda sorta fixed in late 2020: https://github.com/npm/pacote/issues/53

I say kinda sorta fixed, because somehow they only fixed (part of) the problem when installing package from git non-globally -- `npm install -g whatever` is still completely broken. Again, somehow no one thought to test this, I guess.

Now, I say "part of of the problem" was fixed because the npm docs are blatantly lie to you, which requires a workaround (which, again, only helps when not installing globally -- that's still completely broken); from https://docs.npmjs.com/cli/v8/using-npm/scripts:

    prepack
    
        - Runs BEFORE a tarball is packed (on "npm pack", "npm publish", and when installing a git dependencies).
Yeah, no. That's a lie. The prepack script (which would normally be used for triggering a build, e.g. TypeScript compilation) does not run for dependencies pulled directly from git.

Speaking of TypeScript, the TypeScript compiler developers ran into this very problem, and have adopted this workaround, which is to invoke a script from the npm prepare script, which in turn does some janky checks to guess if the execution is occuring from a source tree fetched from git, and if so, then it explicitly invokes the prepare script, which then kicks off compiler and such. This is the workaround they use today:

https://github.com/cspotcode/workaround-broken-npm-prepack-b...

... and while I'm mentioning bugs, even that has a nasty bug: https://github.com/cspotcode/workaround-broken-npm-prepack-b...

Yes, if the workaround calls `npm run prepack` and the prepack script fails for some reason (e.g. a compiler error), the exit code is not propagated, so `npm install` will silently install the respective git dependency in a broken state.

How no one looks at this and comes to the conclusion that NPM is in need of better stewardship, or ought to be entirely supplanted by a competing package manager, I dunno.

komali2

Do you do this in your CI as well? E.g. if you have a server somewhere that most would run `npm install` on builds, you just `git clone` into your node_modules or what?

gslepak

Tips to protect yourself from supply-chain attacks in the JavaScript ecosystem:

- Don't update dependencies unless necessary

- Don't use `npm` to install NPM packages, use Deno with appropriate sandboxing flags

- Sign up for https://socket.dev and/or https://www.aikido.dev

- Work inside a VM

stathibus

As an outsider to the npm ecosystem, reading this list of packages is astonishing. Why do js people import someone else's npm module for every little trivial thing?

austin-cheney

I can provide you with some missing background as I was a prior full time JavaScript/TypeScript developer for 15 years.

Most people writing JavaScript code for employment cannot really program. It is not a result of intellectual impairment, but appears to be more a training and cultural deficit in the work force. The result is extreme anxiety at the mere idea of writing original code, even when trivial in size and scope. The responses vary but often take the form of reused cliches of which some don't even directly apply.

What's weird about this is that it is mostly limited to the employed workforce. Developers who are self-taught or spend as much time writing personal code on side projects don't have this anxiety. This is weird because the resulting hobby projects tend to be substantially more durable than products funded by employment that are otherwise better tested by paid QA staff.

As a proof ask any JavaScript team at your employment to build their next project without a large framework and just observe how they respond both verbally and non-verbally.

jbreckmckye

> The responses vary but often take the form of reused cliches of which some don't even directly apply.

"It has been tested by a 1000 people before me"

"What if there is an upstream optimisation?"

"I'm just here to focus on Business Problems™"

"It reduces cognitive load"

---

Whilst I think you are exaggerating, I do recognise this phenomenon. For me, it was during the pandemic when I had to train / support a lot of bootcamp grads and new entrants to the career. They were anxious to perform in their new career and interpreted that as shipping tickets as fast as possible.

These developers were not dumb but they had... like, no drive at all to engage with problems. Most programmers should enjoy problems, not develop a kind of bad feeling behind the eyes, or a tightness in their chest. But for these folks, a problem was a threat, of a bad status update at their daily Scrum.

Dependencies are a socially condoned shortcut to that. You can use a library and look like a sensible and pragmatic engineer. When everyone around you appears to accept this as the norm, it's too easy to just go with the flow.

I think it is a change in the psychological demographic too. This will sound fanciful. But tech used to select for very independent, stubborn, disagreeable people. Now, agreeableness is king. And what is more agreeable than using dependencies?

austin-cheney

The two I hear the most are:

reinventing the wheel

some comparison to assembly

notmyjob

Not sure about “agreeableness” but I can see group think and disagreeableness to anything that falls outside of the group think. Cargo cult coding isn’t a new thing but the demographic shift you note is real. But is that not just the commodification of programming labor?

sangeeth96

> Most people writing JavaScript code for employment cannot really program.

> As a proof ask any JavaScript team at your employment to build their next project without a large framework and just observe how they respond both verbally and non-verbally.

With an assumption like that, I bet the answer is mostly the same if you ask any Java/Python dev for example — build your next microservice/API without Spring or DRF/Flask.

Even though I only clock at about 5YOE, I'm really tired of hearing these terrible takes since I've met plentiful share of non-JS backend folks for example, who have no idea about basic API design, design patterns or even how to properly use the same framework they use for every single project.

IshKebab

Not my experience at all. It's more like a) JS devs view NPM packages as a mark of pride and so they try to make as many as possible (there are people proud of maintaining hundreds of packages, which is obviously dumb), and b) people are lazy and will take a ready-made solution if it's available, and c) there are a lot of JavaScript developers.

The main reasons you don't see this in other languages is they don't have so many developers, and their packaging ecosystems are generally waaay higher friction. Rust is just as easy, but way higher skill level. Python is... not awful but it's definitely still a pain to publish packages for. C++, yeah why even bother.

If Python ever official adopts uv and we get a nice `uv publish` command then you will absolutely see the same thing there.

duped

I don't quite know how to put this thought together yet, but I've noticed that no one quite hates programming more than this class of programmers. It's like playing on a football team with people who hate football.

A key phrase that comes up is "this is a solved problem." So what? You should want to solve it yourself, too. It's the PM's job to tell us not to.

thewebguyd

Lack of a good batteries-included stdlib. You're either importing a ton of little dependencies (which then depend on other small libraries) or you end up writing a ton of really basic functionality yourself.

rudedogg

This is the answer IMO. The number of targets and noise would be a lot less if JS had a decent stdlib or if we had access to a better language in the browser.

I have no hope of this ever happening and am abandoning the web as a platform for interactive applications in my own projects. I’d rather build native applications using SDL3 or anything else.

imiric

To be fair, this is not a problem with the web itself, but with the Node ecosystem.

It's perfectly possible to build web apps without relying on npm at all, or by being very selective and conservative about the packages you choose as your direct and transitive dependencies. If not by reviewing every line of code, then certainly by vendoring them.

Yes, this is more inconvenient and labor intensive, but the alternative is far riskier and worse for users.

The problem is with web developers themselves, who are often lazy, and prioritize their own development experience over their users'.

tannhaeuser

npmjs is the stdlib, or what emerged from it.

It started as CommonJs ([1]) with Server-side JavaScript (SSJS) runtimes like Helma, v8cgi, etc. before node.js even existed but then was soon totally dominated by node.js. The history of Server-side JavaScript btw is even longer than Java on the server side, starting with Netscape's LifeScript in 1996 I believe. Apart from the module-loading spec, the CommonJs initiative also specified concrete modules such as the interfaces for node.js/express.js HTTP "middlewares" you can plug as routes and for things like auth handlers (JSGI itself was inspired by Ruby's easy REST DSL).

The reason for is-array, left-pad, etc. is that people wanted to write idiomatic code rather than use idiosyncratic JS typechecking code everywhere and use other's people packages as good citizens in a quid pro quo way.

[1]: https://wiki.commonjs.org/wiki/CommonJS

Edit: the people crying for an "authority" to just impose a stdlib fail to understand that the JS ecosystem is a heterogeneous environment around a standardized language with multiple implementations; this concept seems lost on TypeScripters who need big daddy MS or other monopolist to sort it all out for them

tracker1

Worth mentioning...

https://jsr.io/@std

DrewADesign

I just never got the argument against including things like the sort of text formatting tools and such that people always import libraries for. It’s not like an embedded system for mission-critical realtime applications where most functions people write for it get formal proofs — it’s freaking javascript. Sure it’s become a serious tool used for serious tasks for some reason, but come on.

pverheggen

Not just a stdlib, lack of an SDK as well. Both Deno and Bun have decided to ship with tooling included, which cuts down on dev dependency bloat.

skydhash

But why can’t we have a good library instead of those mini thingies?

mhitza

Because "look at how many open source packages I maintain!"

At a time small JS libraries were desired, and good library marketing approach, but nowadays simple sites ship megabytes of without a care.

In particular this developer is symptomatic of the problem of the NPM ecosystem and I've used him multiple times as an example of what not to do.

progbits

For C++ there are Boost, Folly, Absl, several more large libraries with reputable orgs behind them. I'm surprised someone doesn't make a big npm lib like that.

Not hating on the author but I doubt similar compromise would happen to Facebook or Google owned package.

zahlman

Because you have to figure out what should be in it, and coordinate the distribution. It's not like there's a reference implementation of JavaScript maintained by a well-known team that you consciously install everywhere that you need it.

eviks

Because a mini thing can be written in mini time by a mini number of people

nine_k

Having a module for every little trivial thing allows you to only bring these modules inside the JS bundle you serve to your client. If there's a problem in one trivial-thing function, other unrelated trivial things can still be used, because they are not bundled in the same package.

A comprehensive library might offer a more neat DX, but you'd have to ship library code you don't use. (Yes, tree-shaking exists, but still is tricky and not widespread.)

0cf8612b2e1e

Given how fat a modern website is, I am not sure that a kitchen sink library would change much. It could actually improve things because there would be fewer redundant libraries for basic functionality.

Say there is neoleftpad and megaleftpad - both could see widespread adoption, so you are transitively dependent on both.

palmfacehn

There's also the option of including that standard lib with the runtime.

palmfacehn

Things like this are good illustrations as to why many feel that the entire JS ecosystem is broken. Even if you have a standard lib included in a language, you wouldn't expect a bigger binary because of the standard lib. The JS solution is often more duct tape on top of a bad design. In this case tree shaking, which may or may not work as intended.

skydhash

Doesn’t the bundler already do tree shaking? Optimizing via dependency listing is very wrong.

tracker1

Tree shaking is less than reliable... for it to work well, all the dependencies need to be TS/ESModule imports/exports and even then may not shake out properly.

It helps, but not as much as judicious imports. I've been using Deno more for my personal projects which does have a pretty good @std library, though I do think they should keep methods that simply pass through to the Deno runtime, and should probably support working in Node and Bun as well.

jbreckmckye

"JS people" don't, but certain key dependencies do, and there are social / OSS-political reasons why.

Why do "Java people" depend on lowrie's itext? Remember the leftpad-esque incident he initiated in 2015?

jowea

This conversation been a thing since at least the leftpad event. It's just how the js ecosystem works it seems. The default library is too small perhaps?

raddan

Or the language is too braindead. `is-arrayish` should not even have to be a thing.

robrtsql

I agree that it doesn't need to exist, but as far as I can tell, almost no one depends on it directly. The only person using it is the author, who uses it in some other small libraries, which are then used in a larger, nontrivial library.

I just created a Next.js app, saw that `is-arrayish` was in my node_modules, and tried to figure out how it got there and why. Here's the chain of dependencies:

next > sharp > color > color-string > simple-swizzle > is-arrayish

`next` uses `sharp` for image optimization. Seems reasonable.

`sharp` uses `color` (https://www.npmjs.com/package/color) to convert and manipulate color strings. Again, that seems reasonable. This package is maintained by Qix-.

Everything else in the chain (color-string > simple-swizzle > is-arrayish) is also maintained by Qix-. It's obnoxious to me that he feels it is necessary to have 80 different packages, but it would also be a substantial amount of effort for the other parties to stop relying on Qix-'s stuff entirely.

lukebechtel

It's easier to find something frustrating in large code changes than in single line imports, even if the effective code being run is the same -- the PR review looks cleaner and safer to just import something that seems "trusted".

I'm not saying it is safer, just to the tired grug brain it can feel safer.

rglover

You typically don't. But a lot of packages that you do install depend on smaller stuff like this under the hood (not necessarily good and obviously better handled with bespoke code in the package, but is is what it is).

grishka

Then the question becomes, why do developers of larger libraries import someone else's module for every little trivial thing?

rglover

Because they don't have the slightest clue what they're doing.

socalgal2

Same reason they do in rust.

The rust docs, a static site generator, pull in over 700 packages.

Because it’s trivial and easy

hnquestion10987

I'm a little confused after reading everything. I have an Expo app and if I run `npm audit`, I get the notification about `simple-swizzle`.

The GitHub page (https://github.com/advisories/GHSA-hfm8-9jrf-7g9w) says to treat the computer as compromised. What does this mean? Do I have to do a full reset to be sure? Should I avoid running the app until the version is updated?

marifjeren

Definitely sounds like spear phishing targeting you specifically.

Kudos to you for owning up to it.

As others have said, it's the kind of thing that could happen to anyone, unfortunately.

mcjiggerlog

I also received the same phishing email and I only have packages with a few thousand downloads per week.

anticristi

This is really scary. It could have totally happened to me too. How can we design security which works even when people are tired or stressed?

Once upon a time, I used a software called passwordmaker. Essentially, it computed a password like hash(domain+username+master password). Genius idea, but it was a nightmare to use. Why? Because amazon.se and amazon.com share the same username/password database. Similarly, the "domain" for Amazon's app was "com.amazon.something".

Perhaps it's time for browser vendors to strongly bind credentials to the domain, the whole domain and nothing but the domain, so help me Codd.

samhh

Passkeys already solve for this, we just have to get past the FUD.

phkahler

>> which silently intercepts crypto and web3 activity in the browser, manipulates wallet interactions, and rewrites payment destinations so that funds and approvals are redirected to attacker-controlled accounts without any obvious signs to the user.

If you're doing financial transactions using a big pile of NPM dependencies, you should IMHO be financially liable for this kind of thing when your users get scammed.

bpavuk

using NPM at all must be treated as a liability at this point. it's not the first and definitely not the last time NPM got pwned this hard.

palmfacehn

It isn't uncommon in crypto ecosystems for the core foundation to shovel slop libraries on application developers.

tomxor

Finally validated for writing my own damn ANSI escape codes.