Cremit
/incidentsfield log
CatchesCampaignsExfilPatternsLLMIncidentsMethodology
↺rss↗cremit.io

incidents.cremit.io

A reference feed of real-world Non-Human Identity (NHI) credential leak incidents. Maintained by Cremit.

Browse

  • All incidents
  • npm supply chain
  • CI/CD compromise
  • Methodology

Subscribe

  • RSS feed
  • @cremit_io
  • GitHub
// status
monitor active
// build
2026-05-20
// origin
cremit · seoul, kr
// license
CC BY 4.0

© 2026 Cremit. content reuse encouraged with attribution.

home/incidents/eslint-scope-token-theft-2018
HIGH7.6·confirmed·disclosed Jul 12, 2018·4 min read

eslint-scope npm Publish Token Theft (2018)

An attacker stole an ESLint maintainer's npm credentials and published a malicious eslint-scope version that exfiltrated developer .npmrc tokens to a remote server.

Summary

On July 12, 2018, an attacker stole the npm publish credentials of an ESLint maintainer and published malicious versions of eslint-scope and eslint-config-eslint. The malicious code read the .npmrc file on the developer's machine, extracted the npm authentication token, and POSTed it to an attacker-controlled pastebin endpoint. The attack was a credential-harvesting worm: every developer who installed the package became a potential vector for further publish-token theft. ESLint detected the attack within hours and revoked the malicious versions, but not before an unknown number of authentication tokens were exfiltrated.

Timeline

  • 2018-07-12 ~02:49 UTC — Attacker logs into the compromised npm account and publishes eslint-scope@3.7.2 with malicious code.
  • 2018-07-12 ~03:24 UTC — Attacker also publishes eslint-config-eslint@5.0.2 carrying similar payload.
  • 2018-07-12 ~05:39 UTC — npm reports anomalous activity to ESLint; ESLint begins investigating.
  • 2018-07-12 ~07:00 UTC — Both malicious packages are unpublished. npm begins force-rotating credentials of any account that downloaded the affected versions.
  • 2018-07-13 — ESLint and npm publish post-mortems.

Attack Vector

The compromise was credential theft, not a maintainer handoff. The maintainer's npm credentials were obtained, almost certainly via password reuse — npm later confirmed the credentials matched a historical breach corpus. The attack chain:

  1. Attacker logs into a maintainer's npm account using stolen reused credentials.
  2. Attacker publishes new patch versions of widely-installed packages with credential-harvesting payload appended.
  3. Payload reads ~/.npmrc on the install target's machine, extracts the _authToken line.
  4. Payload POSTs the token to a pastebin endpoint controlled by the attacker.
  5. Attacker harvests pastebin contents, gains npm publish rights to whatever those tokens authorize.

The intended outcome was self-propagation. Each token harvested let the attacker publish malicious patch versions of further packages, harvesting more tokens, expanding the surface.

Tokens & Credentials Exposed

  • npm authentication tokens stored in ~/.npmrc on every machine that installed eslint-scope@3.7.2 or eslint-config-eslint@5.0.2 during the active window.
  • For developers who used the same machine for multiple npm scopes, the harvested token authorized publishing across all of them.
  • For organizations whose CI pipelines installed the malicious version into a runner that also held publish credentials, the runner's token was harvested.

The exposure window was ~5 hours, which sounds short but corresponds to billions of dependency-tree resolutions globally — every CI build kicked off in that window potentially pulled the malicious version.

Confirmed Impact

  • npm forced credential rotation for every account whose .npmrc was potentially exfiltrated. The exact number was not published.
  • No second-stage publishes were observed by the defender community before the malicious versions were unpublished. The window was short enough that the worm did not propagate visibly.
  • Long-tail risk persisted for any token harvested but not rotated. npm's forced rotation covered tokens used by accounts that downloaded the package, but tokens stored in long-lived CI environments may have been missed.

Mitigation & Lessons

The structural lessons:

  • Multi-factor authentication for publish rights is non-negotiable. npm rolled out 2FA enforcement for top-1000 packages in the months after this incident; mandatory 2FA for all public package maintainers landed years later.
  • Tokens in .npmrc are platform-scoped credentials. A token in ~/.npmrc is identity-equivalent to a logged-in npm session for that account. Treat it accordingly: avoid storing publish-scoped tokens on developer laptops at all.
  • Granular publish scopes matter. Fine-grained tokens that can publish only to a specific package or scope dramatically reduce blast radius if harvested. They were not available in 2018; they are now.
  • Provenance + 2FA + lockfile pinning is the defense-in-depth posture that would have stopped this attack at multiple layers today.

Cremit Analysis

This is the cleanest ghost-key case in the index. The credential involved had no human face — it was a long-lived static token in a config file, used routinely by automation. When the attacker presented it, npm had no way to distinguish "the real maintainer publishing a routine patch" from "an attacker holding a stolen token." Both look identical on the wire.

It is also a drifted-key case. The maintainer's npm credentials were almost certainly minted years before, used across multiple personal and work scopes, and reused as a password somewhere else along the way. By 2018 the credential reached more packages, with more downloads, than it did when first issued. Nobody had reviewed the scope.

The NHI Severity Index score of 7.6 reflects ecosystem-wide reach of eslint-scope (a common transitive dependency in JavaScript projects) tempered by the developer-environment reachability — the harvest target was developer laptops and CI runners, not production credentials directly. The blast radius is what made the attack worth attempting: a single harvested npm token could itself be used to ship the next malicious version, multiplying reach geometrically.

This incident set the precedent for the credential-harvesting npm worm pattern that would later show up in Shai-Hulud (2025–2026) at much larger scale. The technique did not change. What changed is the volume of credentials a modern developer or CI runner holds beyond just .npmrc.


References

  1. [1]
    Postmortem for Malicious Packages Published on July 12th, 2018
    primary·2018-07-13·eslint.org
  2. [2]
    npm Blog — Reported malicious module: getcookies
    primary·2018-07-13·blog.npmjs.org
  3. [3]
    Hacker breaches npm account, infects ESLint package
    reporting·2018-07-13·zdnet.com

Related incidents

2018-11-26·HIGH
event-stream / flatmap-stream Backdoor (2018)
2021-10-22·CRITICAL
ua-parser-js npm Account Compromise (2021)
2026-04-22·CRITICAL
Bitwarden CLI Supply Chain Compromise (2026)
last reviewed / 2026-05-04reviewer / benlicense / CC BY 4.0
// incident metadata
severity
HIGH7.6
status
confirmed
disclosed
2018-07-12
occurred
2018-07-12 → 2018-07-12
vector
npm supply chain
platforms
npm
tokens
npm Publish TokenEnvironment Variable
nhi severity index
score7.6 / 10blast radiusecosystem-widereachabilitydeveloper-envprivilegevariable
nhi kill chain
Ghost Key↗Drifted Key↗
metrics
exposure1 days