Back to Blog

Is Go Safer Than Node.js Against Supply Chain Attacks?

Yes, Go is structurally safer than Node.js against supply chain attacks. The reason is one design choice: go get does not run install scripts. Here is the full case, with sources, and the honest counterexamples.

Is Go Safer Than Node.js Against Supply Chain Attacks?

Most of the big supply chain attacks of the last two years worked the same way. A developer ran npm install, and a dependency they never picked ran code on their machine before they wrote a single line. npm allows that by design, and it is now walking the decision back. Go never allowed it. This post looks at how much that one difference explains, where Go is genuinely safer, and where it is exposed to the same risks as Node. Every claim has a source.

TL;DR

Go vs Node.js supply chain security at a glance

AspectGo (Golang)Node.js (npm)
Install-time code executionNone. go get/go build run no package scripts.preinstall/install/postinstall run arbitrary code automatically.
When malicious code runsOnly when imported and the function is called.The moment you install, before you write a line.
Integrity guaranteeMerkle-tree checksum transparency log (sum.golang.org), tamper-evident forever.Lockfile hashes plus newer provenance; weaker historically.
Version resolutionMinimal Version Selection. Upgrades are explicit.Semver ranges (^, ~). A new bad patch can auto-resolve.
Dependency surfaceRich standard library, fewer direct deps.Tiny-module culture, sprawling transitive trees.
Vulnerability scanninggovulncheck does reachability analysis. Low noise.npm audit flags the whole tree. Alert fatigue.
Registry modelDecentralized. Modules are source URLs.Central accounts. One phish exposes a whole portfolio.
Still exposed toPhished maintainers, typosquats, proxy caching of malware.All of the above, plus install-script worms.

Go wins on default behavior and attack surface. Both lose to a determined attacker who phishes a human maintainer.

Why does Go avoid the attack that hits npm hardest?

Because Go has no install scripts. When you run npm install, every package in the transitive tree can register lifecycle hooks (preinstall, install, postinstall) that npm executes automatically, with your user's privileges, before you have run or even read a single line (npm scripts docs). GitHub has called install scripts the single largest code-execution surface in the npm ecosystem (GitHub Security, 2025).

Go deliberately has no equivalent. There are no build hooks. go get fetches and verifies source, and go build compiles it. Neither runs package-defined scripts. A poisoned Go dependency is inert. It sits there as plain source until your code imports it and actually calls the malicious function. That is a far narrower and far more detectable window than "runs on install for everyone, always."

Look at what the two commands actually do:

example.bashbash
# npm: the postinstall hook runs on every install, automatically
$ npm install left-pad
> [email protected] postinstall
> node ./scripts/setup.js   # <- arbitrary code, your privileges, right now

# go: nothing in the package gets to run during fetch or build
$ go get example.com/some/module
go: downloaded example.com/some/module v1.0.0   # source fetched, checksum verified, nothing executed

This is not a small detail. It is the reason npm is shipping v12 in July 2026 to block install scripts by default, and why pnpm already did so in v10 (npm scripts docs). The whole ecosystem is converging on the behavior Go shipped at launch.

How bad is the npm supply chain problem, by the numbers?

It is the dominant target by a wide margin. Sonatype identified 454,648 new malicious packages in 2025, pushing its cumulative blocked total past 1.23 million, a 75% jump year over year, with over 99% of open-source malware found on npm (Sonatype 11th State of the Software Supply Chain, 2026). Its Q4 2025 malware index put the figure at 99.8% of that quarter's blocked malware.

The most telling statistic is one that does not exist: there is no published count of malicious Go modules. Go is not tracked as a separate ecosystem in Sonatype's, OpenSSF's, or most vendors' malware indices, because the incidents are too few to warrant an index. That absence is the headline.

Where 2025 open-source malware lived (share of new malicious packages, by ecosystem):

EcosystemShare of 2025 open-source malware
npmOver 99%
All others (incl. Go)*Under 1%

*454,648 new malicious packages were identified in 2025. Go is not tracked as a separate ecosystem, because there are too few incidents to index. Source: Sonatype 11th State of the Software Supply Chain, 2026.

What actually happened in the npm worm wave of 2025 and 2026?

A run of self-propagating worms turned a known risk into a continuous emergency, and every one of them rode install scripts. The npm registry has had high-profile incidents for years (event-stream in 2018 hid a Bitcoin-wallet stealer in a dependency downloaded around 8 million times; ua-parser-js in 2021 shipped cryptominers after an account takeover), but 2025 changed the tempo.

Here is the chain that defines the current era:

DateIncidentWhat happened
Sep 8, 2025qix phished18 packages hijacked, including chalk and debug. Around 2.6B weekly downloads of exposure.
Sep 2025Shai-HuludFirst npm worm. A postinstall hook harvested credentials and self-injected into roughly 100 more packages per victim.
Nov 24, 2025Shai-Hulud 2.0Moved to preinstall, added a home-directory wipe. Around 796 packages, found in 27% of scanned cloud environments.
Mar 31, 2026axiosDPRK actors social-engineered the maintainer. A postinstall RAT triggered C2 contact in over 12,000 projects.
Jun 2026MiasmaSidestepped npm's install-script block with the "Phantom Gyp" technique, an implicit node-gyp rebuild.

The September 2025 qix compromise started with a convincing 2FA-reset email from the spoofed domain npmjs.help. The attacker took over maintainer Josh Junon's account and published malicious versions of 18 packages, including chalk (~300 million downloads per week) and debug (~358 million), a combined exposure above 2.6 billion weekly downloads. The payload was a browser-side crypto-clipper. It was live for roughly two hours.

Days later came Shai-Hulud, the first true npm worm. On install, its bundle.js harvested npm, GitHub, AWS, and GCP credentials, ran TruffleHog to scrape secrets, and used the stolen npm tokens to inject itself into up to about 100 more packages owned by each victim. CISA issued an alert (CISA, 2025). Sonatype attributes 171,740 malicious packages to self-replicating npm campaigns over a few months.

Shai-Hulud 2.0 (November 24, 2025) moved execution to preinstall for a wider blast radius, installed the Bun runtime to evade Node-based monitoring, and added a destructive fallback that could wipe a home directory. Wiz found affected packages in roughly 27% of the cloud environments it scanned (Wiz, 2025).

Then 2026 made the limits of "just block install scripts" obvious. The axios compromise (March 31, 2026) did not start with a phished password. North Korea-nexus actors (tracked as UNC1069 / Sapphire Sleet) social-engineered the lead maintainer through a fake company, a branded Slack workspace, and a Teams call that planted a RAT on his machine. They then published malicious axios versions (100M+ weekly downloads) carrying a postinstall RAT. StepSecurity detected anomalous C2 contact in over 12,000 projects (StepSecurity, 2026). And Miasma (June 2026) sidestepped npm's incoming install-script block entirely with the "Phantom Gyp" technique: ship a binding.gyp file so npm's implicit node-gyp rebuild runs the payload with no declared script at all.

So is Go a silver bullet? Not quite

Go's defaults stop a whole class of attacks, but no language is a clean room, and it helps to know the rare cases where Go has been targeted. Documented malicious Go modules in 2025 number in the low dozens across a handful of campaigns. That is a tiny figure next to npm's hundreds of thousands, and the most notable case is worth understanding precisely because it shows what an attacker has to do to get past Go's design.

The boltdb-go/bolt backdoor, disclosed in February 2025 but planted back in November 2021, is the standout. It typosquatted the popular BoltDB module (github.com/boltdb/bolt, a dependency of thousands of packages, used at Shopify and Heroku) and carried a command-and-control backdoor. Notice how much effort it took: the attacker published the malicious v1.3.1, let the Go Module Mirror cache it, then rewrote the Git tag to point back to clean code so that manual GitHub audits looked fine while the proxy kept serving the backdoor. It worked, but it required exploiting an edge of the caching system rather than the easy install-time hook that npm hands attackers for free (Socket, 2025).

A few other campaigns showed up in 2025: a March wave of hypert and layout typosquats with loader malware aimed at financial-sector developers, plus some disk-wiper modules and an SSH credential stealer. Real, but isolated, and each one relied on a developer typing the wrong import path rather than on code running automatically.

The encouraging part is how the ecosystem responds. When a malicious module is reported, the Go security team removes it from the proxy (which then returns a 403 SECURITY ERROR) and adds it to the Go vulnerability database. On boltdb-go, Google removed the module from both the proxy and GitHub, logged it in the vulnerability database, and pointed to ongoing work on capability analysis via Capslock and comparisons with deps.dev. Go's defenses raise the cost of an attack substantially. They are not a guarantee, and that is true of every ecosystem.

What makes Go structurally safer?

Beyond the absence of install scripts, four design choices compound the advantage.

The checksum database is the best package-integrity story in mainstream software. Your go.sum records SHA-256 hashes of every dependency, and sum.golang.org is a Merkle-tree transparency log (Certificate-Transparency style) that records each module version's hash the first time anyone fetches it, then keeps it forever (Go module reference). The go command verifies inclusion and consistency proofs before trusting code, so a force-pushed Git tag or a tampering proxy fails loudly. Cryptographer Filippo Valsorda, who led Go's security team at Google, has argued Go has the best package-integrity story of any language ecosystem precisely because every client in the world resolves a given module version to the same bytes, forever (Filippo Valsorda). The catch: the log proves consistency, not goodness. It only helps if someone monitors it.

Minimal Version Selection is a natural damper. Go builds use the lowest version that satisfies all requirements, and upgrades are explicit (MVS reference). npm's ^ and ~ ranges mean a freshly published malicious patch can be pulled into a build automatically. In Go, a bad new release does not propagate downstream until a human deliberately bumps the requirement. The qix and Shai-Hulud worms depended on exactly the auto-upgrade behavior MVS refuses to do.

govulncheck cuts noise with reachability analysis. Go's official scanner checks the curated vulnerability database at vuln.go.dev and warns only when your code actually calls the vulnerable symbol (govulncheck, Go blog). npm audit flags every vulnerable version anywhere in the tree regardless of whether you ever reach it, which trains developers to ignore the output.

There is no central account to phish into a whole portfolio. Go modules are identified by their source URL, so they inherit GitHub's (or GitLab's) account security rather than a single npm account that, once taken over, exposes everything a maintainer owns. That structure is part of why npm worms spread the way they do and Go incidents stay contained.

A larger standard library reinforces all of this. Common tasks (HTTP, JSON, crypto, templating) ship with Go, so a typical project pulls in fewer direct dependencies and fewer distinct maintainers into its trust boundary. Interestingly, a 2025 study found Go's per-dependency amplification (about 4.48x) is comparable to npm's (about 4.32x), so Go is not magically leaner per package. The win is cultural and absolute: fewer direct dependencies, a smaller trusted set.

What Go's defaults don't cover

Go closes the biggest doors automatically, but a few risks are industry-wide and worth knowing so you can cover them yourself. These are the gaps that remain for any ecosystem, Go included.

  • Phishing a maintainer is the hard limit for everyone. If a maintainer's GitHub account is taken over, a malicious release can be tagged, and the checksum database records it faithfully because it cannot tell good code from bad. The axios-style attack is the one scenario that crosses every ecosystem, which is exactly why the industry is moving to hardware keys and trusted publishing.
  • You still pick your dependencies by name. Go's checksum verification stops tampering with a module you chose, but it cannot stop you from choosing a typosquat in the first place. A quick check of the import path against the canonical repo before adding a dependency closes this one.
  • Proxy immutability is mostly a feature. The same caching that guarantees every build gets identical bytes is, in the rare boltdb-go case, what kept a cleaned-up backdoor reachable. It is a tradeoff worth knowing, and the Go team's removal process and vulnerability database are the backstop.
  • Defaults protect install time, not runtime. Go's edge is that nothing runs on fetch or build. Once you actually import and call a dependency, it runs with your privileges, the same as any language. Sandboxing untrusted code at runtime is a separate concern in every ecosystem.
  • Keep the protection on. The one self-inflicted mistake is setting GOSUMDB=off broadly, which turns off the checksum verification that makes Go safer. The fix is simple: leave it on, and scope GOPRIVATE only to genuine internal paths.

How are both ecosystems converging on the same fixes?

Both are racing toward the same defenses, because the shared root cause is human. After the 2025 wave, GitHub announced mandatory 2FA with FIDO/WebAuthn, short-lived granular tokens, deprecation of legacy classic tokens, and a push to trusted publishing via OIDC for cryptographic build provenance (GitHub Security, 2025). npm v12 (July 2026) blocks install scripts by default and adds a min-release-age cooldown so a package that is live for two hours never reaches you.

These are real improvements. But axios showed their limit: no token policy stops a maintainer whose laptop is running a RAT. Only hardware FIDO2 keys plus provenance-verified, cooldown-gated installs would have. The lesson both communities are absorbing is that the soft target is the person, not the protocol. Go's edge is that even when the person is compromised, the blast radius is smaller, because nothing detonates on install.

What should you do right now?

The verdict is clear, but the action items differ by stack.

If you write Go:

  1. Never set GOSUMDB=off or GONOSUMCHECK broadly. Scope GOPRIVATE narrowly to genuine internal paths.
  2. Commit go.sum and build with -mod=readonly.
  3. Run govulncheck ./... in CI as a reachability-aware gate.
  4. Vet new dependencies for typosquats. Verify import paths against the canonical repo before you add them.
  5. Monitor critical dependencies for new releases with Socket or deps.dev.

If you write Node:

  1. Upgrade to npm 11.16.0+ and set ignore-scripts=true, with an allowlist (such as @lavamoat/allow-scripts) for the few packages that truly need scripts.
  2. Use npm ci with a frozen lockfile, pin exact versions, and set a 3 to 7 day min-release-age cooldown so two-hour compromises never reach you.
  3. Adopt trusted publishing (OIDC) and hardware FIDO2 keys. Do not rely on TOTP for publishing.
  4. Run real software composition analysis (Socket, Snyk) with behavioral detection, not just npm audit.
  5. Defensively register your internal package names publicly to block dependency confusion.

FAQ

Is Go really safer than Node.js for supply chain security?

Yes, structurally. The decisive reason is that go get and go build run no install-time scripts, which neutralizes the preinstall/postinstall vector behind nearly every major npm worm of 2025 and 2026. Over 99% of 2025 open-source malware appeared on npm (Sonatype, 2026). Go is safer, not immune.

What is the single biggest difference between Go and npm security?

Install-time code execution. npm runs arbitrary package scripts automatically on npm install, with your privileges, for every package in the tree. Go has no build hooks at all, so a malicious dependency stays inert until your code imports it and calls it. That is why npm v12 is now blocking install scripts by default, the behavior Go shipped with.

Has Go ever had a supply chain attack?

Yes. The boltdb-go backdoor typosquatted BoltDB and served a remote-access backdoor from Go's module cache for three years (Socket, 2025). A 2025 hypert/layout campaign shipped at least seven typosquats with loader malware. Incidents are real but rare, and removed from the proxy when reported.

What is the Go checksum database and why does it matter?

sum.golang.org is a Merkle-tree transparency log that records the hash of every module version the first time it is seen, permanently. The go command verifies these proofs before trusting code, so tampering or a rewritten Git tag fails loudly (Go module reference). It guarantees every client gets identical bytes for a given version, which is the strongest integrity guarantee in mainstream package management.

Does blocking npm install scripts fix the problem?

It helps a lot, but not completely. The axios attack used a postinstall RAT after social-engineering the maintainer, and the Miasma campaign sidestepped the script block via an implicit node-gyp rebuild. Blocking scripts plus hardware FIDO2 keys, a release cooldown, and provenance verification together close most of the gap. A phished maintainer still beats any single control.

Should I switch from Node to Go for security alone?

Security is a strong point in Go's favor, but most teams choose a language for the whole package: concurrency, hiring, ecosystem, and shipping speed. Go happens to be both a great backend language and the structurally safer one against supply chain attacks. If you are already weighing it, the security default is one more reason to learn it.

Sources

Primary sources cited in this post (last verified June 29, 2026):

Ready to master Go?

Join LevelUpGo and start building real projects with interactive, hands-on lessons.

Start Learning Free