Skip to content

The chalk and debug npm compromise

One look-alike “update your 2FA” email handed an attacker a maintainer's npm account. Nineteen tiny packages under almost every JavaScript project shipped a payload that stayed quiet on servers and rewrote wallet addresses in users' browsers.

  • 19 packages
  • Weekly downloads 2B+
  • Malicious versions
  • CVE-2025-59144 (debug)

In the drawing, the attacker's line turns amber where the maintainer's login is passed to the real npm, and ends in a user's browser.

From a look-alike email to a swapped wallet addressSix stacked planes descend toward the lower right: a look-alike email from npmjs.help, a copy of the npm login page, the maintainer's npm account, the npm registry with 19 new versions, a front-end bundle, and a user's browser. A pale line drops through the email and the fake login page. Once the maintainer's login reaches the attacker and is used against the real npm, it turns amber and glows through the account, the registry and the bundle, ending in the browser, where a wallet address is swapped.support@npmjs.help · the lurecopy of npmjs.com · “update 2FA”qix's npm accountnpm registry · 19 new versionsfront-end bundlea user's browserlogin passed to the real npmwallet address swappedfor the attacker's

What happened

On 8 September 2025 an attacker used a phished npm account to publish one new version each of 19 packages, among them debug, chalk, ansi-styles and strip-ansi, in 8 minutes 21 seconds from 13:12:10 UTC. The added code did nothing in Node.js. In a web page it hooked fetch, XMLHttpRequest and wallet APIs and swapped cryptocurrency addresses for the attacker's.514

The maintainer, Josh Junon (qix), had followed a “Two-Factor Authentication Update Required” email from support@npmjs.help, a domain registered three days earlier. Aikido's feed flagged the publishes four minutes in; clean versions of the chalk family were out within about two hours, and npm told Junon at about 19:58 UTC that every impacted version had been taken down. The people at risk were the users of websites whose front ends were built in that window, not the developers who installed the packages.1

Am I affected?

Only if a lockfile, cache or build resolved one of the 19 exact versions below. They were published on 8 September 2025 from 13:12 UTC; the chalk family was replaced from 14:47 UTC and the rest stayed downloadable until npm removed them, by about 19:58 UTC (longer behind private mirrors). Installing one on a laptop, a server or in CI ran nothing harmful there. The danger is a browser bundle built with it and served to your users. Not affected: the packages' GitHub repositories, and builds pinned by a committed lockfile with npm ci or an equivalent.6

Covers all 19 packages published from the qix account; each had exactly one malicious version. Check the version your lockfile resolved, not the range in package.json. Try

All 19 packages, their malicious and clean versions
In publish order. Weekly downloads are Aikido's figures as reported on the day.
PackageMaliciousPublished (UTC, 8 Sep)Last good beforeFirst clean afterWeekly downloads
ansi-styles6.2.213:12:106.2.16.2.3 09-08 14:52371.41M
debug4.4.213:12:394.4.14.4.3 09-13 17:25357.6M
chalk5.6.113:13:055.6.05.6.2 09-08 14:47299.99M
supports-color10.2.113:13:2810.2.010.2.2 09-08 14:49287.1M
strip-ansi7.1.113:13:507.1.07.1.2 09-08 15:05261.17M
ansi-regex6.2.113:14:156.2.06.2.2 09-08 14:48243.64M
wrap-ansi9.0.113:14:369.0.09.0.2 09-08 15:10197.99M
color-convert3.1.113:14:533.1.03.1.2 09-13 16:37193.5M
color-name2.0.113:15:122.0.02.0.2 09-13 17:23191.71M
is-arrayish0.3.313:15:290.3.20.3.4 09-13 16:4473.8M
slice-ansi7.1.113:15:477.1.07.1.2 09-08 15:1159.8M
error-ex1.3.313:16:051.3.21.3.4 09-15 14:5847.17M
color5.0.113:16:245.0.05.0.2 09-13 17:19not in Aikido's table
color-string2.1.113:18:072.1.02.1.2 09-13 16:2727.48M
simple-swizzle0.2.313:19:050.2.20.2.4 09-13 16:1926.26M
supports-hyperlinks4.1.113:19:284.1.04.1.2 09-08 15:1419.2M
has-ansi6.0.113:19:466.0.06.0.2 09-08 15:0812.1M
chalk-template1.1.113:20:091.1.01.1.2 09-08 15:133.9M
backslash0.2.113:20:310.2.00.2.2 09-13 15:030.26M

sources disagree Junon's own list of 18 includes color but leaves out error-ex, which he later said he “missed”; Aikido's 18 include error-ex but not color. The registry and the GitHub advisories confirm 19. For the chalk family the first clean version is Sindre Sorhus's same-day republish; for Junon's own packages it is a later “cache-bust” release identical to the last good one. Sources: npm registry metadata; GitHub Advisory Database; Aikido.57

Same campaign, other accounts: proto-tinker-wc@0.1.87 (16:51 UTC the same day), and on 9 September duckdb@1.3.3, @duckdb/node-api@1.3.3, @duckdb/node-bindings@1.3.3 and @duckdb/duckdb-wasm@1.29.2, after a DuckDB maintainer fell for the same lure.8

Do this now

  1. Search lockfiles and SBOMs (package-lock.json, pnpm-lock.yaml, yarn.lock) for the exact versions above. A range in package.json doesn't tell you what was installed.
  2. Rebuild and redeploy front ends built from a fresh install between 8 Sep 13:12 and about 20:00 UTC (longer behind a private mirror). The debug advisory says to “rebuild any browser bundles from scratch”. Invalidate CDN and edge copies of the old assets too.617
  3. Purge caches: node_modules, the package manager's cache, CI build caches, private registry mirrors and, for Deno, DENO_DIR. Blocklist the versions in registry proxies.1
  4. Web3 sites: look for on-chain transactions to the attacker's address during the window (listed under Field marks).
  5. No credential rotation is needed for this payload: it took nothing from build machines. inference from the advisory's scope
Contents
  1. Field marks
  2. How it works
  3. Timeline
  4. Impact
  5. Phishing-resistant 2FA
  6. Clean-up
  7. Defenses
  8. Related
  9. Sources

The tells were in the registry, the build logs and the bundle.

In the registry

  • A burst of new patch versions of dormant packages: simple-swizzle and backslash last changed in 2016, is-arrayish and error-ex in 2018.
  • New versions with no matching commit or tag in the repository. That is what the first GitHub reporter noticed: src/index.js looked “like a cryptostealer”.1

In builds and bundles

  • Server or CI builds that suddenly threw errors because fetch wasn't defined: the payload assumed browser globals. researchers16
  • A distinctive obfuscated identifier in shipped JavaScript; Sindre Sorhus posted a search for it on the chalk issue.4

In the browser

  • A wallet asked to sign a transfer or an approve to an address that differs from the one you meant but looks alike at a glance.
  • Transfers to the attacker's Ethereum address, which Aikido and SEAL published for defenders.14

One email, two registries' worth of trust, and a user's wallet.

No code in these projects had a bug, and no build pipeline was broken into. The attacker needed one thing: the login of a person who could publish. After that, every step was a feature working as designed: semver ranges took the new patch, bundlers copied it into front ends, and browsers ran it for users.

It is also why the clean-up was unusual. Most npm malware runs when you install it. This code did nothing on the developer's machine and went through the build into the JavaScript that websites serve. Dashed boxes are the attacker's.

  • Second factor (2FA)Something besides the password that proves a login, such as a one-time code from an app or a passkey.
  • Crypto-clipperMalware that replaces a cryptocurrency address or transaction recipient with the attacker's own.
The chalk and debug attack chain, from a phishing email to a user's browserTop band, the phish: a look-alike email from support@npmjs.help reaches the maintainer qix, who enters his credentials on a copy of npmjs.com; the DuckDB maintainers describe how such a copy forwards username, password and two-factor code to the real site. The login reaches qix's npm account in the npm registry; the account's email is changed and the owner is locked out. The account publishes 19 new patch versions between 13:12:10 and 13:20:31 UTC. In fresh builds, semver ranges such as ^4.4.1 pick up the new patch and bundlers copy it into front-end JavaScript. On servers and in CI the payload stays dormant. In a user's browser it hooks fetch, XMLHttpRequest and wallet interfaces, and swaps addresses for look-alike attacker addresses. Finally npm takes the versions down, by about 19:58 UTC, and clean releases go out above them.The phishThe lure emailsupport@npmjs.help“Two-Factor Authentication Update Required”domain registered 5 Sep · sent 00:50:20 UTC“temporarily locked starting September 10”qix · Josh Junonmaintainer, 02:50 local timefollows “Update 2FA Now”enters his credentials“looked shockingly authentic”Copy of npmjs.comforwards to the real siteusername · password · 2FA codea one-time code still valid when relayeddescribed by DuckDB, phished next daylogin, livenpm registryqix's npm accountemail changed, owner locked outmany packages on it untouched:“a filter for high downloads” (Junon)19 new patch versionsansi-styles 6.2.2 · debug 4.4.2 · chalk 5.6.1 …13:12:10 → 13:20:31 UTC, 8 min 21 seach one: the previous patch plus an appended payloadno matching commit or tag in the GitHub repositoriesinstallversions taken downnpm, by ~19:58 UTC · clean releases above themFresh buildslaptops and CI that resolved during the windowA semver range^4.4.1 → 4.4.2any fresh resolve took the new patchcommitted lockfile + npm ci: no changeThe bundlerwebpack · Vite · Rollup · Next.jscopies the utility code intothe site's front-end JavaScriptserved to usersNode · CI · serversthe payload stays dormantdebug advisory: “not affected”some builds threw fetch errors,which became a detection signalServersA user's browserHooksfetch · XMLHttpRequestwindow.ethereum request / sendSolana-style wallet interfacesonly where browser globals existAddress swapa look-alike attacker addressnearest by edit (Levenshtein) distanceETH · BTC · SOL · TRX · LTC · BCHrewrites approve and transfer callsworks only if the user signs
Diagram of the chalk and debug attack chain. Focus it and use the left and right arrow keys to move between the eight steps listed after it; each step's text describes that part in full.

  1. 5 Sep · domain registered; 8 Sep · 00:50:20 UTC

    A look-alike domain sends a deadline.

    npmjs.help was registered on 5 September. Three days later, at 00:50:20 UTC by the email's own Date: header (02:50 in Junon's time zone), it sent “Two-Factor Authentication Update Required” from support@npmjs.help.3

    It used npm's branding, a plausible reason (2FA credentials more than 12 months old) and a deadline: accounts would be “temporarily locked starting September 10, 2025”. The button said “Update 2FA Now”. sources disagree Sygnia puts the email at 13:00 UTC; the published header says 00:50, and Socket's 2:50 AM local time matches it.18

    from
    support@npmjs.help
    domain age
    3 days
    subject
    Two-Factor Authentication Update Required
    deadline
    locked from 10 Sep 2025
    sent
    00:50:20 UTC (header)
  2. 8 Sep · between 00:50 and 13:12 UTC

    A copy of npm's login passes everything to the real one.

    Junon followed the link and entered his credentials: “It was a 2FA reset email that looked shockingly authentic.” When he did so exactly, and which second factor he typed, is not public.1

    The DuckDB maintainers, phished by the same lure the next day, describe the page: a “pixel-perfect copy” that “forwarded all actions to the actual npm website”. Their maintainer entered username, password and 2FA code, and the attacker's side then added a new API token and published with it. A one-time code is phishable for exactly this reason: a relay can pass it on while it is still valid.8

    page
    copy of npmjs.com
    behaviour
    forwards to the real site
    typed (DuckDB)
    username, password, 2FA code
    one-time code
    relayed while valid
  3. The account is taken over, and its owner locked out.

    The attacker changed the account's email address so Junon couldn't recover it. It was his personal account: “Repositories are not affected.”

    The choice of packages looked deliberate. In Junon's words: “This appears targeted, or at least with a filter for high downloads. Many other packages on my account are untouched.”1

    account
    qix (personal)
    email
    changed by the attacker
    owner
    locked out
    GitHub repos
    not affected
  4. 8 Sep · 13:12:10 → 13:20:31 UTC

    Nineteen publishes in eight minutes.

    Starting with ansi-styles@6.2.2 at 13:12:10, then debug@4.4.2 and chalk@5.6.1, each package got one new patch version, ending with backslash@0.2.1 at 13:20:31.5

    Each was “functionally identical to the previous patch version, but with a malware payload added”. To npm it was a valid publish from the owner's account, so it became the newest version. Nothing tied a publish to the source repository or to CI.6

    first
    ansi-styles 6.2.2 · 13:12:10
    last
    backslash 0.2.1 · 13:20:31
    versions
    19, one per package
    contents
    previous patch + payload
    repo commit / tag
    none
  5. Semver ranges pull the new patch in, and bundlers ship it.

    Any fresh install or lockfile update whose range allowed the new patch (^4.4.1, say) picked it up automatically. These are tiny utilities deep in almost every dependency tree.

    Bundlers such as webpack, Vite, Rollup and Next.js then copied the utility code into front-end JavaScript. Builds pinned by a committed lockfile and installed with npm ci (or an equivalent) did not move.6

    range
    ^4.4.1 → 4.4.2
    lockfile + npm ci
    unchanged
    bundler
    copies it into front-end JS
  6. Dormant on servers, awake in browsers.

    The theft logic ran only where browser globals exist. The debug advisory: “Local environments, server environments, command line applications, etc. are not affected.” In some Node builds and CI runs it threw errors because fetch wasn't defined, which helped people spot it.616

    In a web page it took over three things: the page's fetch and XMLHttpRequest, so it could rewrite responses; injected wallet providers such as window.ethereum; and Solana-style wallet interfaces.14

    Node, CI, servers
    dormant
    browser
    active
    network
    fetch, XMLHttpRequest
    wallets
    window.ethereum, Solana-style
  7. A look-alike address, in the page and in the wallet.

    Addresses for ETH, BTC, SOL, TRX, LTC and BCH in page data were replaced with the attacker address that looked most similar, picked by edit (Levenshtein) distance so a glance would not catch it.

    With a wallet present, outgoing transactions and token approve and transfer calls were rewritten to benefit the attacker. Aikido: it “avoids obvious swaps in the UI to reduce suspicion”. The theft still needed the user to sign the altered transaction.14

    chains
    ETH, BTC, SOL, TRX, LTC, BCH
    pick
    nearest look-alike address
    wallet calls
    approve, transfer rewritten
    needs
    the user's signature
  8. 8 Sep · 13:16 → ~19:58 UTC

    Caught in minutes, removed in hours, cleaned up for days.

    Aikido's feed alerted at 13:16; a user opened the debug issue at 13:35. Sindre Sorhus, co-maintainer of the chalk packages, published clean versions over the chalk family from 14:47. Junon confirmed at 15:15: “Yep, I've been pwned.” npm told him at about 19:58 UTC: “All impacted package versions have been taken down.”2

    Private registry mirrors and Deno caches kept serving the bad versions, so on 12–13 September Junon published “cache-bust” versions above them, identical to the last good ones. Vercel purged build caches for the 76 projects whose builds had picked them up.9

    first alert
    Aikido, 13:16 UTC
    clean chalk
    5.6.2 at 14:47 UTC
    all taken down
    ~19:58 UTC
    cache-bust
    12–15 Sep

The numbered markers on the diagram match the steps.

It was a 2FA reset email that looked shockingly authentic. I should have paid better attention, but it slipped past me

Josh Junon, debug-js/debug#1005, Sep 2025

Nineteen publishes in eight minutes. The alert came during the fourth.

  • 8min 21s

    from the first malicious publish to the last.

  • 4min

    until Aikido's feed flagged the new versions.

  • 2h 04min

    until the maintainer confirmed in public.

  • ~6h 46min

    until npm said every impacted version was down.

The 19 malicious publishes on 8 September 2025, 13:12 to 13:21 UTCOne dot per package, placed at its npm publish time and sized by Aikido's weekly download figure. ansi-styles at 13:12:10, debug at 13:12:39 and chalk at 13:13:05 come first, followed by supports-color, strip-ansi, ansi-regex, wrap-ansi, color-convert, color-name, is-arrayish, slice-ansi, error-ex and color by 13:16:24. After a gap, color-string, simple-swizzle, supports-hyperlinks, has-ansi, chalk-template and finally backslash at 13:20:31: 8 minutes 21 seconds in all. Aikido's feed alerted at 13:16, while the burst was still going.13:1213:1313:1413:1513:1613:1713:1813:1913:2013:2113:16 Aikido's feed alertsfour minutes in; the burst is still goingansi-styles 6.2.2debug 4.4.2chalk 5.6.1supports-color 10.2.1strip-ansi 7.1.1ansi-regex 6.2.1wrap-ansi 9.0.1color-convert 3.1.1color-name 2.0.1is-arrayish 0.3.3slice-ansi 7.1.1error-ex 1.3.3color 5.0.1color-string 2.1.1simple-swizzle 0.2.3supports-hyperlinks 4.1.1has-ansi 6.0.1chalk-template 1.1.1backslash 0.2.113:12:10 → 13:20:31 UTC: 8 min 21 s, one new patch version per package
Fig. 1. The publish burst. One dot per package at its npm publish time, its area proportional to Aikido's weekly download figure (color, hollow, isn't in Aikido's table). Amber marks the two packages the incident is named after. Source: npm registry timestamps; Aikido.5

Why the window was “about two hours”, and also longer

Wiz describes a “2-hour exposure window”, from the compromise to the maintainer's acknowledgement. The chalk family stopped being the newest version between 14:47 and 15:14 UTC, when clean versions replaced them. The others stayed downloadable until npm removed them, between about 16:33 (debug) and 19:58 UTC.

Response clock compared with a one-day install cooldownMeasured from 13:12 UTC, the first malicious publish: Aikido's feed alerted at 4 minutes, the first public GitHub issue came at 23 minutes, Wiz describes a 2-hour exposure window, Sindre Sorhus published a clean chalk 5.6.2 at 1 hour 36 minutes, the maintainer confirmed the compromise at 2 hours 4 minutes, debug's malicious version appeared to have been yanked at about 3 hours 20 minutes, and npm told the maintainer all impacted versions were taken down at about 6 hours 46 minutes. A one-day minimum release age, pnpm 11's default, is 24 hours: longer than every one of these.04 h8 h12 h16 h20 h24 hAikido's feed alerts4 minFirst public GitHub issue23 minClean chalk 5.6.2 published1 h 36 minWiz's “exposure window”2 hMaintainer confirms2 h 04 mindebug's version “appears” yanked~3 h 20 minnpm: “all … taken down”~6 h 46 min: the last versions removedpnpm 11 default cooldownminimumReleaseAge: 1440 min (one day)Off the chart: cache-bust releases of Junon's packages, 12–15 September
Fig. 2. The response clock against a one-day cooldown. Bars measure from 13:12 UTC. With a one-day minimumReleaseAge, the pnpm 11 default, a fresh install would have skipped every one of the 19 versions: all were removed from the public registry within about seven hours. Sources: Aikido, the debug issue, Junon's posts, Wiz, pnpm docs.12

All impacted package versions have been taken down.

npm, relayed by Josh Junon, Bluesky, 8 Sep 2025

Enormous reach, a tiny take, and a costly clean-up.

  • Weekly downloads (Aikido)over 2 billion for 18 packages
  • Cloud environments (Wiz)1 in 10 reached in about two hours
  • Vercel70 teams, 76 projects built with a bad version
  • Stolen (SEAL)about 5¢ of ETH + about $20 of a memecoin

Aikido put the 18 packages in its table at over 2 billion weekly downloads; Socket says “2-3 billion downloads per week”. The figures overlap heavily, because one install pulls several of these packages.1415

Wiz found the malicious code in 10% of cloud environments within about two hours, and at least one of the packages in 99% of them. Vercel counted 70 teams and 76 unique projects whose builds contained the compromised versions, and purged their build caches.179

The money was another matter. sources disagree SEAL's on-chain analysis traced about 5 cents of ETH and about $20 of an illiquid memecoin on the day, under the title “Oops, No Victims: The Largest Supply Chain Attack Stole 5 Cents”. Sygnia wrote “approximately $500” three days later, without stating its basis. Both are researchers' figures; neither npm nor GitHub has published one.1618

The real cost was the industry's response: Wiz called it more of a “denial-of-service attack on the industry”. In July 2026 Amazon attributed the incident, at medium confidence, to a DPRK-linked group; The Hacker News noted the published evidence is thin. vendor attribution1920

A passkey would have left the fake page nothing to pass on.

A one-time code can be relayed; a passkey is bound to the real siteTwo lanes. With a one-time code, the maintainer types a password and a one-time code into the look-alike site, which passes both to the real npmjs.com while the code is still valid, and the real site accepts the login. With a passkey, the browser only offers the credential to the site it was registered with, npmjs.com, so on the look-alike domain there is nothing usable to pass on, and the relay gets no login.One-time code (TOTP)Maintainertypes password + codepassword + codenpmjs.helplook-alike copy, relays it allsame code, still validnpmjs.comaccepts: logged inPasskey (WebAuthn / FIDO)Maintainer's browserpasskey made for npmjs.comorigin: npmjs.helpnpmjs.helpthe browser offers it nothingNothing to relaythe credential is bound to the real site's originThe binding is a standard WebAuthn property, stated as background.
Fig. 3. Why the relay works on codes and not on passkeys. The DuckDB maintainers' account shows the top lane: the look-alike page forwarded username, password and 2FA code to npm. A WebAuthn credential is bound to the origin it was created for, so a look-alike domain can't use it (a standard property, stated here as background).8

On 22 September 2025, after this incident and the Shai-Hulud worm, GitHub announced it would “Deprecate time-based one-time password (TOTP) 2FA, migrating users to FIDO-based 2FA”, and remove the option to bypass 2FA for local publishing. The post itself cites Shai-Hulud, not this incident.10

Publishing only from CI with trusted publishing (OIDC), and setting a package to “Require two-factor authentication and disallow tokens”, means a phished web login can't mint a publish token. The DuckDB attacker published with a newly created API token. TanStack's compromise in May 2026 shows the limit: OIDC can still be abused from inside a compromised workflow.11

One irony from the recovery: Junon reported that the npm CLI wouldn't let him publish “without downgrading to TOTP”.1

Local environments, server environments, command line applications, etc. are not affected.

GitHub Advisory Database, GHSA-4x49-vf9v-38px, Sep 2025

The fix lived in deployed sites, not in node_modules.

Upgrading a package doesn't recall the JavaScript a site already served. A front end built from a fresh install during the window could carry the payload until it was rebuilt and redeployed, and CDN and edge caches could keep the old assets after that.17

  1. Check lockfiles and SBOMs for the exact versions; ranges alone don't tell you.
  2. Find builds made between 8 Sep 13:12 and about 20:00 UTC, longer behind private mirrors.
  3. Rebuild and redeploy those front ends from clean dependencies; purge CDN copies.
  4. Purge node_modules, package-manager and CI caches, private mirrors, and DENO_DIR.
  5. Blocklist the versions in registry proxies; pin or upgrade to clean versions.
  6. Scan shipped bundles for the payload's obfuscated markers.

Ordinary credential rotation wasn't needed for this code, unlike the Shai-Hulud worm a week later: the advisory scopes the impact to browsers. inference6

Phishing-resistant 2FA would have stopped it at the door.

What would have broken the chain, mapped to the steps above.

ControlEffect hereWhy
Passkeys / WebAuthn (FIDO) for maintainersBreaks step 2The credential is bound to npmjs.com, so a look-alike domain gets nothing to relay. GitHub has announced moving npm from TOTP to FIDO-based 2FA (Fig. 3).
Trusted publishing, tokens disallowedBreaks step 4With “Require two-factor authentication and disallow tokens”, a phished web login can't mint a publish token. caveat It moves the risk into the CI workflow, as TanStack found in 2026.
Lockfile + frozen installBreaks step 5Builds that installed from a committed lockfile (npm ci, pnpm install --frozen-lockfile) never picked up the new patches. Vercel recommends exactly this.
Minimum release ageBreaks step 5Every version was off the public registry within about seven hours. pnpm's minimumReleaseAge (minutes; default 1440 since v11) would have skipped them all; npm has min-release-age (days) and before.13
Rebuild front ends built in the windowEnds step 7Redeploy from clean dependencies and purge CDN and edge copies: the payload lived in served JavaScript, not on build machines.
Check the domain, not the brandingWeakens step 1npmjs.help isn't npmjs.com. DuckDB noted the password manager “did not auto-complete the login”, which “should have been a red flag”.
Registry monitoringShortens the windowAikido's feed alerted four minutes in. A sudden patch of a dormant, high-download package, with no matching repository tag, is a strong signal.

Sources

Primary sources are the maintainers, the registry, advisory databases, the platform operator and package-manager documentation. Secondary sources are the discoverers' and researchers' own analyses and the press.

Primary · maintainers

  • 1Josh Junon (Qix-) and users
    debug-js/debug#1005 · CVE list

    The admission, lockout, package list, npm's messages, cache-bust releases, DENO_DIR.

  • 2Josh Junon
    Bluesky posts

    The acknowledgement, debug yanked, npm's first contact and the takedown message.

  • 3Josh Junon
    The phishing email's full headers

    Sender, subject, Date: 00:50:20 +0000, the lure's wording.

  • 4Sindre Sorhus and users
    chalk/chalk#656

    Clean republishes, browser-only behaviour, the detection search.

Primary · registry, advisories, operators and docs

Secondary · discoverers, researchers, press

Written from sources checked on 2026-10-01. Where sources disagree (the email's send time, the amount stolen, the package count) each figure is attributed to its source. Which second factor the qix maintainer entered is not public; the relay is described from the DuckDB maintainers' account of the same lure. npm's words are known only through the maintainer's relays. Pull quotes are verbatim from the maintainer and the debug advisory.