<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: HorseyofCoursey</title>
    <description>The latest articles on DEV Community by HorseyofCoursey (@horseyofcoursey).</description>
    <link>https://dev.to/horseyofcoursey</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3861439%2Fc6e4a898-f964-44de-a55c-ddbb48e832d8.png</url>
      <title>DEV Community: HorseyofCoursey</title>
      <link>https://dev.to/horseyofcoursey</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/horseyofcoursey"/>
    <language>en</language>
    <item>
      <title>How the Axios Supply Chain Attack Worked - And How to Detect It Earlier</title>
      <dc:creator>HorseyofCoursey</dc:creator>
      <pubDate>Sat, 04 Apr 2026 19:53:46 +0000</pubDate>
      <link>https://dev.to/horseyofcoursey/how-the-axios-supply-chain-attack-worked-and-how-to-detect-it-earlier-533f</link>
      <guid>https://dev.to/horseyofcoursey/how-the-axios-supply-chain-attack-worked-and-how-to-detect-it-earlier-533f</guid>
      <description>&lt;h2&gt;
  
  
  What happened
&lt;/h2&gt;

&lt;p&gt;On March 31, 2026, attackers compromised the npm account of the lead Axios maintainer and published two malicious versions of one of the most downloaded JavaScript libraries in existence with 100 million weekly downloads.&lt;/p&gt;

&lt;p&gt;The attack was surgical. They didn't touch a single line of Axios source code. They added one hidden dependency to package.json: &lt;code&gt;plain-crypto-js@4.2.1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That dependency ran a postinstall script that deployed a cross-platform Remote Access Trojan (RAT). Within 89 seconds of publish, real developer machines were already infected via auto-updating CI/CD pipelines.&lt;/p&gt;

&lt;p&gt;The malicious versions were removed in about 3 hours. But the window was enough to cause damage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why existing tooling missed it
&lt;/h2&gt;

&lt;p&gt;Static scanners like Dependabot and npm audit check against known CVE databases. There was no CVE to check for this attack because it was brand new. By the time a CVE existed, the damage was done.&lt;/p&gt;

&lt;p&gt;Provenance attestation would have caught it. The malicious versions were published directly via CLI, bypassing the normal GitHub Actions workflow and leaving no SLSA attestation. But adoption isn't universal yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  What would have caught it faster
&lt;/h2&gt;

&lt;p&gt;The attack had two very detectable signals:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Signal 1 — package.json changed&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;plain-crypto-js&lt;/code&gt; appeared as a new dependency in a mature, stable library that hadn't added a dependency in months. A simple diff against a known-good baseline would have surfaced this the moment the malicious version published.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Signal 2 — network call within 2 seconds of install&lt;/strong&gt;&lt;br&gt;
The malware called &lt;code&gt;sfrclak.com&lt;/code&gt; almost immediately. Any system watching DNS lookups during package installation would have caught this with hard evidence. Not a guess, an actual domain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building a four layer detector
&lt;/h2&gt;

&lt;p&gt;After the attack I built pakrat, an open source behavioral monitor that watches 187 npm packages every 5 minutes using four detection layers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 1 — Manifest diffing
&lt;/h3&gt;

&lt;p&gt;Every scan fetches the latest package metadata from the npm registry and diffs it against a stored baseline. New dependencies, new install scripts, version changes - all flagged immediately.&lt;/p&gt;

&lt;p&gt;The Axios attack would have triggered this the moment &lt;code&gt;plain-crypto-js&lt;/code&gt;appeared in package.json.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 2 — Docker sandbox with tcpdump
&lt;/h3&gt;

&lt;p&gt;Any package that triggers a manifest alert gets installed in an isolated Docker container while tcpdump monitors all DNS lookups. Known good domains like registry.npmjs.org are filtered out. Anything else surfaces as a flag.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sfrclak.com&lt;/code&gt; would have appeared here within seconds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 3 — Pattern matching
&lt;/h3&gt;

&lt;p&gt;Install output gets scanned for credential harvesting patterns: &lt;code&gt;.ssh&lt;/code&gt; directory access, &lt;code&gt;.aws&lt;/code&gt; config reads, environment variable access, base64 encoding. Secondary signal, but useful context alongside harder evidence.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 4 — eBPF kernel monitoring
&lt;/h3&gt;

&lt;p&gt;This is the interesting one and my favorite.&lt;/p&gt;

&lt;p&gt;bpftrace probes attach at the host kernel level while the container runs. Every execve, openat, and connect syscall is captured: process execution, file access, network connections, all from outside the container's execution environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The package has no visibility into this layer&lt;/strong&gt;. It can't fingerprint it, can't detect it, can't evade it. Even sophisticated malware that checks for Docker artifacts and goes dormant would still reveal itself by the act of checking, because those checks are syscalls too.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the output looks like
&lt;/h2&gt;

&lt;p&gt;Running pakrat against a simulated Axios-style attack:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsu2s3gm21dfbzzv3d8mm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsu2s3gm21dfbzzv3d8mm.png" alt="pakrat detects threat in terminal" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Discord alert hits your phone instantly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhx3kqbq5r7t9cexrul3n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhx3kqbq5r7t9cexrul3n.png" alt="Discord alert" width="517" height="191"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Public scan log
&lt;/h2&gt;

&lt;p&gt;Every scan updates &lt;code&gt;scan-log.json&lt;/code&gt; in the GitHub repo, a public, auditable history of what 187 packages looked like at every point in time. Anyone can query it without running anything themselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;The VM-based sandbox is the next layer — QEMU/KVM running a convincing developer machine with realistic dotfiles, browsing history, VS Code installed. A RAT that detects Docker and goes dormant won't detect a real VM. eBPF probes at the hypervisor level watch everything regardless.&lt;/p&gt;

&lt;p&gt;PyPI support is also on the roadmap. The litellm attack last week showed the Python ecosystem is just as exposed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/HorseyofCoursey/pakrat" rel="noopener noreferrer"&gt;https://github.com/HorseyofCoursey/pakrat&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Self-hosted setup is straightforward: Ubuntu VPS, Docker, Node.js 22, and a Discord webhook. The README walks through everything.&lt;/p&gt;

&lt;p&gt;Contributions welcome, especially package whitelist additions and suggestions for the watched package list.&lt;/p&gt;

</description>
      <category>security</category>
      <category>npm</category>
      <category>node</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
