event-stream / flatmap-stream Backdoor (2018)
A new maintainer of the popular event-stream npm package added a malicious sub-dependency, flatmap-stream, that exfiltrated cryptocurrency wallet seeds from Copay-derived applications.
Summary
In November 2018, the npm community discovered that event-stream — a widely-depended-on stream utility maintained by Dominic Tarr — had been silently shipping a malicious sub-dependency since September. The original maintainer had handed control of the package to a stranger who claimed they wanted to help maintain it. The new maintainer added flatmap-stream as a dependency, then injected a payload targeting a single downstream consumer: the Copay cryptocurrency wallet. The payload exfiltrated wallet seed phrases and private keys from Copay-derived apps to attacker infrastructure.
Timeline
- 2018-09-09 — A user named "right9ctrl" gains publish rights to
event-streamafter asking the original maintainer Dominic Tarr to take over maintenance. - 2018-09-09 —
event-stream@3.3.6ships with a new dependency onflatmap-stream@0.1.1, published the same day. - 2018-10-05 —
flatmap-stream@0.1.2is published with the malicious payload, encrypted and gated to activate only inside Copay-derived applications. - 2018-11-20 — A user notices an unexpected dependency in their tree and opens the GitHub issue that triggers public investigation.
- 2018-11-26 — npm removes the malicious package; Dominic Tarr publishes his statement; widespread coverage begins.
Attack Vector
The mechanism is a textbook social-engineering takeover of an open-source package, layered with three technical decisions that made the payload hard to spot.
- Maintainer handoff via email. The new maintainer asked Tarr for publish rights via cold email. Tarr granted them —
event-streamwas an unmaintained library he had not touched in years. There was no formal review or identity verification. - Dependency injection rather than direct modification. Rather than modifying
event-streamitself, the attacker added a new dependency. Auditors looking atevent-stream's diff would not see any malicious code. - Payload obfuscation and target gating. The malicious code in
flatmap-streamwas AES-encrypted and only decrypted at runtime if specific environment markers (the package name and description of the calling app) matched the Copay wallet. Outside the target environment the code was inert.
Tokens & Credentials Exposed
The payload was narrowly targeted but, when it activated, exfiltrated everything a cryptocurrency wallet needs to be drained:
- BIP39 mnemonic seed phrases — the master credential for HD wallets.
- Private keys for individual addresses.
- Wallet metadata including balances and transaction history, used by the attacker to prioritize which wallets to drain first.
event-stream had roughly 2 million weekly downloads at the time. The malicious sub-dependency was technically present in every install. Only Copay-derived apps activated the payload.
Confirmed Impact
- Copay wallet versions 5.0.2 through 5.1.0 shipped with the malicious dependency embedded in their bundle.
- Wallet credential exfiltration is documented; specific dollar-value loss totals were never publicly confirmed by Bitpay (Copay's parent) due to the difficulty of attributing on-chain theft to this specific vector.
- Trust damage to npm and the maintainer-handoff norm in open-source: this incident is the canonical reference cited every time the topic of unpaid-maintainer burnout meets supply chain risk.
Mitigation & Lessons
The structural lessons are still load-bearing eight years later:
- Maintainer handoffs are credential transfers. Granting publish rights on a widely-depended package is structurally identical to granting an attacker pre-approved access. There is no built-in identity verification.
- Sub-dependencies expand your trust boundary silently. A diff against
event-streamitself looked clean. The actual malice lived in a new package the diff merely referenced. - Lockfile pinning would have helped, but only for organizations that ran
npm cirather thannpm install. Re-runningnpm installwould re-resolveflatmap-streamto its latest version, which is exactly when the payload landed. - Provenance attestations now exist (added to npm in 2023) for builds running in compliant CI. They would not have prevented this attack — the attacker had legitimate publish rights — but they would make the package itself authenticated to a build pipeline rather than a maintainer's personal credentials.
Cremit Analysis
This incident is a foundational ghost-key case. The npm publish token used to ship the malicious version had no face attached to it once Dominic Tarr handed control over. The new maintainer was, for all npm's purposes, the legitimate owner. There was no anomaly signal — no unusual publish location, no out-of-pattern release cadence — because the legitimate state of the package was now whatever the attacker said it was.
It is also a drifted-key case in a slower sense. event-stream was minted as a small utility years before Copay existed; by 2018 it had drifted into the dependency tree of a wallet handling real money, and the trust model that worked when it was a small utility (one maintainer, no review) was now wildly inappropriate for the blast radius it had accumulated.
The NHI Severity Index score of 7.4 reflects ecosystem-wide reach (every install pulled the dependency) tempered by narrow target activation (only Copay-derived apps activated the payload). Production reachability is unambiguous: the credentials exfiltrated were master keys to live cryptocurrency wallets. Privilege level varied by the user's wallet balance.
The generalizable point — and the one connecting event-stream to ua-parser-js, the Shai-Hulud campaign, and every npm credential incident in between — is that the npm ecosystem treats publish access as a long-lived, transferable credential, and the people who hold those credentials are not always the same people month over month. Identity continuity is something the platform does not enforce; it is something each project has to enforce for itself.
References
- [1]GitHub Issue dominictarr/event-stream#116 — original disclosureprimary·2018-11-26·github.com
- [2]Dominic Tarr — statement on event-streamprimary·2018-11-26·gist.github.com
- [3]Compromised npm package: event-streamprimary·2018-11-27·blog.npmjs.org
- [4]Widely-used JavaScript library steals BTC from Copay appreporting·2018-11-26·zdnet.com
