BuildNot yet confirmed elsewhere1 publisher2 min readPublished
Malicious commits hit Zod, nub and standard-schema repos, but no malicious version reached npm: Zod's release setup held and npm's scanner caught the nub version
Malicious commits landed in three GitHub repos, including Zod, yet npm's scanner held the only bad release submitted, @nubjs/nub@0.9.4. That outcome says little about whether a consuming team's own lockfile and install settings would have held.
The Engineer · Build desk
Map of five entries. A misplaced preinstall entry may have kept the hook from running. Zod's release flow kept the repo compromise off the registry. npm's scanner held the nub release before it was installable. A related campaign hit 314 packages, per GitLab. npm will move to opt-in scripts.
- constraint Attacker's package.json Preinstall entry sat at the root, not under scripts, which a commenter said may have kept it from running at all, claim 21
- capability Zod maintainers Trusted publishing plus a dedicated GitHub environment kept the repo compromise from becoming a registry compromise, claim 18
- decision npm publish-time scanner Held the one malicious version submitted, the nub release, before it was installable, per GitHub's advisory, claim 3
- precedent Mini Shai-Hulud campaign Tied to the earlier @tanstack/zod-adapter account takeover; GitLab's advisory says it hit 314 packages, claim 8
- decision npm (per GitHub) Will move to opt-in script execution for dependencies; the write-up says to prepare and not wait for the default to flip, claim 23
| Who | How | Kind | Claim |
|---|---|---|---|
| Attacker's package.json | Preinstall entry sat at the root, not under scripts, which a commenter said may have kept it from running at all | constraint | 21 |
| Zod maintainers | Trusted publishing plus a dedicated GitHub environment kept the repo compromise from becoming a registry compromise | capability | 18 |
| npm publish-time scanner | Held the one malicious version submitted, the nub release, before it was installable, per GitHub's advisory | decision | 3 |
| Mini Shai-Hulud campaign | Tied to the earlier @tanstack/zod-adapter account takeover; GitLab's advisory says it hit 314 packages | precedent | 8 |
| npm (per GitHub) | Will move to opt-in script execution for dependencies; the write-up says to prepare and not wait for the default to flip | decision | 23 |
What happened
- The payload was a remote loader that gets the download location for its next stage from an Ethereum smart contract.
- The malicious files sat in test-looking paths, packages/zod/meta.test.mjs and packages/mini/meta.test.mjs.
- A Hacker News commenter noted the preinstall entry sat at the root of package.json instead of under scripts, possibly stopping it from running, and the maintainer reverted the commit.
Why it matters
- exposure A hook that does run executes as the installing user, with reach to ~/.ssh, ~/.aws/credentials, ~/.npmrc tokens and every environment variable in the shell.
- decision Teams must choose between turning dependency install scripts off now and waiting for npm's announced opt-in default; the write-up says to upgrade to npm 11.16.0 or later and not wait.
- precedent The write-up groups this with the @tanstack/zod-adapter takeover, tied to a campaign GitLab says hit 314 packages, and the 2025 chalk and debug phishing as one stolen-credential pattern.
The write-up's headline says a lockfile did not save anyone [14]. On the facts it reports, the lockfile was never in play. The zod and standard-schema changes were never published [4], so no lockfile could have pinned a malicious version of either [15].
What stopped the compromise was narrower than the three layers the write-up lists [22]. Of the three repos [1], only one produced a submitted version [16], and npm's scanner held that one [3]. For Zod, the write-up credits the maintainer's release setup: OIDC trusted publishing with no long-lived npm tokens, a dedicated GitHub environment with required approval, and 2FA that cannot be bypassed from CI [19]. A commit to the repo did not become a release [18]. The write-up does not say whether nub had comparable publishing controls.
The write-up is of two minds about all this. It calls the ending luck [14], then says "Zod's maintainers did the right things and it worked." [20] Both can hold, because the stop differs by repo: the release flow for Zod [18], the scanner for nub [3]. In our view the weakest layer is the install path. It rests on a single commenter's account, and the author tells readers to check GitHub issue #6658 and the advisory for the authoritative timeline [24].
On the consuming side, the loader's first step needs the preinstall and postinstall hooks [6]. The write-up's lead control is one line in .npmrc, ignore-scripts=true, followed by an explicit rebuild of the few packages that need a build step, such as sharp, bcrypt and sqlite3 [10]. That setting does not cover a malicious bin entry, which runs when the command is invoked. The write-up says the Bitwarden incident did exactly that [11]. It also pairs the setting with a minimum release age, on the logic that fresh versions are where compromises live [13].
The payload is stored outside both the repo and the registry, and updating a contract is enough for the attacker to replace it [7]. The write-up's quick check greps node_modules and package-lock.json for the delivery domain dsakldjklsaj33112[.]com and says to rotate every credential on the machine if it matches [12]. A clean grep clears one delivery location, and the location is the part the attacker can change [17].
What to watch
- GitHub issue #6658 and the advisory timeline, which settle whether the root-level preinstall entry would have run.
- Whether the advisory explains how the @nubjs/nub@0.9.4 submission was made and what publishing controls nub had.
- Which npm release flips dependency script execution to opt-in; the write-up points to 11.16.0 or later as preparation.
Clarity's read
What the record supports and how the coverage leans. The claims behind it follow.
Reality
- Evidence50
- Adoption
- Insufficient
- Hype gap+25
- Incentives
- Insufficient
- Confidence55
Claim ledger
Ranked by verification strength, evidence, and original report placement.
- [1]
Malicious commits were pushed to colinhacks/zod on GitHub, and the same pattern hit nubjs/nub and standard-schema/standard-schema.
- [2]
The payload was a remote loader that asks an Ethereum smart contract where to download its next stage.
- [3]
No malicious Zod version reached npm; per GitHub's advisory, npm's publish-time scanning held the one malicious version that was submitted, @nubjs/nub@0.9.4, before it was installable.
- [4]
The zod and standard-schema changes never got published.
- [5]
The malicious files showed up in test-looking paths: packages/zod/meta.test.mjs and packages/mini/meta.test.mjs.
- [6]
Reported loader behavior per GitHub's security advisory: use preinstall/postinstall lifecycle hooks to bootstrap, query Ethereum RPC endpoints to read a smart contract, pull a delivery location out of the contract, download and execute remote JavaScript, and clean up after itself.
- [7]
The payload lives off-repo and off-registry, and the attacker can swap it by updating a contract; reviewing the package contents tells you nothing about what the loader fetches tomorrow.
- [8]
Earlier this year @tanstack/zod-adapter shipped malware (v1.166.12 and v1.166.15) after an npm account takeover, tied to the Mini Shai-Hulud campaign that GitLab's advisory says hit 314 packages. In 2025 a phished maintainer gave attackers chalk and debug; the write-up says the pattern is that someone's credential is the weak link.
- [9]
An install script runs as you, meaning it can reach ~/.ssh, ~/.aws/credentials, ~/.npmrc tokens, and every env var in your shell.
- [10]
The write-up recommends turning off dependency install scripts by default with ignore-scripts=true in ~/.npmrc or a project .npmrc, then rebuilding only what needs it, such as sharp, bcrypt and sqlite3.
- [11]
ignore-scripts is not a silver bullet: a package can still register a malicious bin entry, which runs when the command is invoked, and the Bitwarden incident did exactly that.
- [12]
The write-up's quick check greps node_modules and package-lock.json for the delivery domain dsakldjklsaj33112[.]com from the advisory, and says that if it returns anything, rotate every credential on that machine and then investigate.
- [13]
The write-up advises adding a cooldown, a minimum release age paired with ignore-scripts, because fresh versions are where compromises live.
- [14]
The write-up is headlined 'Zod Got Backdoored. Your Lockfile Didn't Save You, Luck Did.'
- [15]
Because no malicious zod or standard-schema version was published, no lockfile could have pinned one, so lockfiles were not the control that stopped those two compromises.
- [16]
Of the three compromised repos, one (nub) produced a version submitted to npm; the other two did not get as far as a publish attempt.
- [17]
A grep for the single delivery domain matches only that one delivery location; because the attacker can swap the payload by updating the contract, a clean result does not clear the loader.
- [18]
Zod's own release flow (trusted publishing plus a dedicated GitHub environment) is what kept the repo compromise from becoming a registry compromise.
ReportedContestedSource: dev.to write-up2 sources— create a free account to open themView cited source - [19]
Zod's publishing setup, which the write-up calls the template, is trusted publishing (OIDC, no long-lived npm tokens), a dedicated GitHub environment with required approval, and 2FA that can't be bypassed from CI.
ReportedContestedSource: dev.to write-up2 sources— create a free account to open themView cited source - [20]
Zod's maintainers did the right things and it worked.
ReportedContestedSource: dev.to write-up2 sources— create a free account to open themView cited source - [21]
Per the Hacker News discussion, the malicious commit was reverted by the maintainer, and one commenter noted the preinstall entry was placed in the wrong spot in package.json (root, not under scripts), which may have kept it from running at all.
- [22]
The write-up lists three layers of luck: the attacker was sloppy with the manifest, the maintainer had locked down publishing, and npm's scanner caught the nub version.
- [23]
GitHub has said npm will move to opt-in script execution for dependencies; the write-up says to upgrade to npm 11.16.0+ to prepare and not wait for the default to flip.
- [24]
The dates in the Hacker News thread are the commenter's account; the write-up says to check GitHub issue #6658 and the advisory for the authoritative timeline.
Sources
1 independent publisher whose own reporting we read for this story.
- dev.toZod Got Backdoored. Your Lockfile Didn't Save You, Luck Did.
1 article · October 12, 2026
Topics and entities
Follow any of these and your For You feed starts watching them — no settings page required.
Topics
- Package install-script controlsFollow
- Software Supply Chain SecurityFollow
- npm Package SecurityFollow