@ctrl/tinycolor Supply Chain Attack Post-mortem Date: September 16, 2025 Author: Scott Cooper Tags: TypeScript, Security --- TL;DR A malicious GitHub Actions workflow was added to a shared repository, which exfiltrated an npm token with broad publishing rights. The attacker used this token to publish compromised versions of 20 packages, including the widely used @ctrl/tinycolor. The author's GitHub account and the @ctrl/tinycolor repo were not directly compromised. No phishing or malicious packages were installed on the author's machine. No pull request was needed to introduce the malicious workflow because the repo admin can add workflows directly. GitHub and npm security teams quickly unpublished the malicious packages. Clean versions of the affected packages were re-released to flush caches. For broader analyses and community discussions, see: Socket's write-up StepSecurity analysis Hacker News discussion wiz.io post --- How I Found Out On September 15, 4:30 PM PT, Wes Todd contacted the author on Bluesky and added them to the OpenJS Foundation Slack. GitHub/npm security were already alerted and actively unpublishing malicious versions. Initial guidance involved looking for suspicious "Shai-Hulud" repos or branches, but none were found in the author’s repos. "Shai-Hulud was the Fremen term for the sandworm of Arrakis." — Dune Wiki --- What Actually Happened The author had previously collaborated on the angulartics2 repo, a shared project where multiple people had admin rights. That repo stored a GitHub Actions secret: an npm token with broad publishing access. A collaborator force-pushed a "Shai-Hulud" branch with a malicious GitHub Actions workflow to this repo. Because the collaborator had admin rights, the workflow ran immediately without review. The workflow stole the npm token and published malicious versions of 20 packages. Among the packages affected, @ctrl/tinycolor is highly popular, downloaded ~2 million times weekly. GitHub and npm security swiftly removed malicious versions. The author re-published clean versions to rebuild trust and flush caches. --- Impact Malicious versions briefly appeared on npm. Installing these compromised versions would have executed a harmful postinstall script. Detailed attack mechanisms and mitigation steps are documented by StepSecurity. If you've installed a compromised version, immediate actions are recommended: see StepSecurity’s advice. --- Publishing Setup & Interim Plan Currently using semantic-release with GitHub Actions for automated publishing. npm provenance is enabled on many packages (attesting how packages are built), but it did not prevent this attack since the attacker had a valid token. Plans to move to npm’s Trusted Publishing (OIDC) to eliminate static tokens, though semantic-release integration is ongoing (npm/cli#8525). For now, publishing @ctrl/tinycolor requires 2FA; all tokens have been revoked. Smaller packages will maintain semantic-release but with stricter controls: No new contributors added. Use granular npm tokens limited to publish-only rights per package. Local 2FA-based publishing is unsustainable; OIDC/Trusted Publishing adoption is monitored closely. Will continue using pnpm, which blocks