Part of the trust problem is seeing code statically as source of truth, not what it actually does at runtime.
In axios, here's what the npm install actually does live in the CI runner: a backdoor with c2
@garnet_labs gives you that in your own CI workflow, in 3 lines of yml.
New supply chain attack this time for npm axios, the most popular HTTP client library with 300M weekly downloads.
Scanning my system I found a use imported from googleworkspace/cli from a few days ago when I was experimenting with gmail/gcal cli. The installed version (luckily) resolved to an unaffected 1.13.5, but the project dependency is not pinned, meaning that if I did this earlier today the code would have resolved to latest and I'd be pwned.
It's possible to personally defend against these to some extent with local settings e.g. release-age constraints, or containers or etc, but I think ultimately the defaults of package management projects (pip, npm etc) have to change so that a single infection (usually luckily fairly temporary in nature due to security scanning) does not spread through users at random and at scale via unpinned dependencies.
More comprehensive article:
https://t.co/EJAZbqAPIQ
@mattjay Runtime monitoring on the execution surface (CI runner / agent sandbox env)
see example of claude installing an npm package and getting flagged before any public advisory (recent DPRK campaign)
https://t.co/vEsH9saeiz
lines up with what we’re seeing from the kernel. ran the `actions-cool/issues-helper` through garnet instrumented runners -- the compromised one showing memory scraping (via proc/[pid]/mem), IMDS probe and exfil
clean/flagged comparison runs show the useful bit for your `pinGitHubActionDigests` point: same workflow shape, different runtime behavior when the action is pinned vs floating.
Interestingly, the intercom-client (npm) and lightning (pypi) compromises today are the same operator running the same kit across two ecosystems.
same C2 (zero.masscan[.]cloud / 104.21.19.236), same router_runtime.js, same bun loader.
runtime view from garnet during execution ties the link
→ [email protected]: https://t.co/u2Su0HCsiY
→ lightning==2.6.2: https://t.co/vRwIYN5RVW
At runtime on GitHub Actions runners:
→ setup.mjs downloads Bun → executes 11.7MB obfuscated router_runtime.js → beacons to zero.masscan[.]cloud
Then steals every cloud credential it can find (gh, az, gcloud, etc.) + scrapes GitHub Actions secrets from memory.
Same exact payload + C2 as the Lightning PyPI attack.
evidence: https://t.co/iuyXkquzVB
@motdotla yeah, stolen publish token would've done the same thing. the thing worth flagging imo: trusted publishing was built to remove the long-lived token as a target. attackers just moved the compromise one step upstream to the workflow that calls it.
ran @bitwarden/cli compromised version through garnet on a github runner — telltale runtime patterns we've seen in recent supply chain attack campaigns:
– install-time node→bun pivot (first seen in shai-hulud back in nov)
– process-memory scraping for actions secrets (seen in trivy and checkmarx kics in march)
– exfil through a newly registered vendor-themed typosquat (typical teampcp tactic)
- IMDS/cloud metadata probing
what's new this time: the package was published via github/npm trusted publishing (first known, per @adnanthekhan)
@ox_security already linked it to shai-hulud (via a hardcoded string in the package) — our runtime view corroborates that from the behavior side.
also: @CharlieEriksen had flagged shai-hulud 3.0 as what he suspected we were seeing in a dec 31 test — didn't take shape then. looks like it just did (i believe we need better versioning for these campaigns 😅)
@mattjay Lack of execution-time visibility
its hard to get a handle on it if we don't know whats actually running under the hood (read: https://t.co/9VkSIluojv)
Came across a newly published npm package eslint-vite (https://t.co/DNj4WQttLE) currently live with:
- only 3 versions
- ~300 weekly downloads
- 0 dependencies
Installed and ran it inside our internal GitHub runner.
At runtime, we observed outbound calls to:
Binance, Aptos, Google Drive, and some other interesting destinations.
The interesting question is: why is a linter making these calls?
Not saying this is malicious, could be benign. But worth keeping an eye on. This kind of behavior is easy to miss unless you look at what actually happens during execution.
Run profile: https://t.co/nWNkVfkISn
public run profile for anyone who wants to explore the raw telemetry → https://t.co/jP1LwNZlrX, https://t.co/xVWJyGopdd
if you want this level of visibility on your own workflows, drop the github action in 3 lines of yaml → https://t.co/hbmHUoq5a6
i spent last night watching the axios compromise at the kernel level.
most of the discourse is about the account takeover or the malware payloads. but the part i keep coming back to is the execution visibility gap — what actually happened when it ran on the machine.
it's the same gap we've seen across every supply chain incident we've profiled at @garnet_labs this month..
this is the fifth incident we’ve profiled this month at @garnet_labs. at least two distinct operators are now treating dev and ci environments as unobserved execution surfaces. the common thread isn't the specific malware—it’s the execution model.
we run more untrusted code than ever — dependencies, generated code, ai agents — in environments with secrets and network access but zero visibility into what that code actually does.
the execution model has outgrown the observation model.
full runtime process lineage and breakdown (blog post) → https://t.co/7fa3QqyqJn