Skip to content
Go back

The Axios Supply Chain Attack: What Laravel Developers Need to Know

Axios is one of the most popular packages on npm — over 100 million weekly downloads. On March 26, 2026, an attacker hijacked a maintainer account and published a version that silently installed a Remote Access Trojan on every machine that ran npm install.

The attack lasted 3.5 hours before Socket.dev detected it. With 100 million weekly downloads, even that short window was enough to hit thousands of CI/CD pipelines and developer machines worldwide.

Every Laravel project has a package.json. This isn’t just a JavaScript problem.

Table of contents

Open Table of contents

What happened

The attacker hijacked the npm account of jasonsaayman, a maintainer of the Axios package. They published axios@1.14.1 and axios@0.30.4 — both identical to the real Axios source code, with one change: package.json now listed plain-crypto-js@4.2.1 as a dependency.

plain-crypto-js didn’t exist before the attack. It was a brand-new package created solely to deliver malware. The name sounds like a legitimate crypto utility. The code used two layers of obfuscation — reversed Base64 encoding plus XOR cipher — to hide what it actually did.

When decoded, the strings revealed C2 (command and control) server URLs, shell commands, and file paths. The postinstall script ran automatically on npm install, downloaded a RAT (Remote Access Trojan), and then deleted itself from disk to avoid detection.

Andrej Karpathy noted the broader implication: package popularity is not a proxy for security. The bigger the dependency, the more attractive the target.

StepSecurity published a detailed writeup of the attack mechanics and timeline.

Why AI agents make this worse

When an AI coding agent runs npm install — Claude Code, Codex, Cursor — the blast radius includes everything the agent can access:

The agent sees a clean npm install success message and moves on to writing code. No human ever saw a prompt. The RAT doesn’t even need to persist — the agent’s session already has everything the attacker wants.

And AI has changed both sides of the equation:

Attacks are cheaper to produce. Generating a convincing malicious package — plausible name, well-written README, realistic source code — used to take effort. Now an attacker can spin up dozens in minutes. plain-crypto-js is a perfect example.

Targets are less vigilant. When an agent suggests a package, most people hit accept. The friction that used to exist — manually typing a name, scanning the npm page, checking download counts — is gone. As one commenter put it:

This is why AI-assisted dependency auditing needs to be standard in every CI pipeline. 300M weekly downloads means millions of attack surfaces. The npm ecosystem trusts too much by default.

How to check if you’re affected

Pushpak from the Laravel community shared a prompt for scanning your machine. Here’s an improved version that also checks for the malicious dependency plain-crypto-js — a package that has no legitimate use and exists solely as a RAT dropper:

There's an active supply chain attack on axios. The compromised versions
are axios@1.14.1, plain-crypto-js@4.2.1 and axios@0.30.4. DO NOT upgrade
or install any dependencies.

Scan my entire machine for lock files and check if any project has these
compromised versions resolved.

Step 1: Find all lock files
find ~ -maxdepth 6 \( -name "package-lock.json" -o -name "yarn.lock" \
  -o -name "pnpm-lock.yaml" -o -name "bun.lock" \) 2>/dev/null \
  | grep -v node_modules | grep -v .cache | grep -v .Trash | grep -v Library

Step 2: Search those lock files for the compromised versions AND the
malicious dependency
xargs grep -l "axios.*1\.14\.1\|axios.*0\.30\.4\|plain-crypto-js"

Note: The presence of plain-crypto-js anywhere in a lockfile is a direct
indicator of compromise. Flag it as AFFECTED immediately regardless of
which package pulled it in.

Step 3: Also check all package.json files for axios dependency declarations
using ^ ranges that could resolve to the compromised versions on next
install (e.g. ^1.14.0, ^1.13.x, ^1.x). List these as "at risk" projects.

Report:
- AFFECTED: projects with compromised versions OR plain-crypto-js in lock files
- AT RISK: projects with caret ranges that could resolve to 1.14.1 on next install
- CLEAN: if nothing found

Do not install, update, or modify anything. Read-only scan only.

10 protections for your Laravel project

Here’s the full defence stack I put in place for Growth Method, and what each layer catches:

1. Pin exact versions

Remove ^ and ~ from package.json. Caret ranges are how the compromised version would silently arrive — ^1.13.0 resolves to 1.14.1 on next install without anyone approving it.

{
  "devDependencies": {
    "@tailwindcss/postcss": "4.2.1",
    "laravel-vite-plugin": "2.1.0",
    "vite": "7.3.0"
  }
}

No ^, no ~. You control when updates happen and can review changelogs first.

2. Commit your lockfile

You probably already commit package-lock.json. This pins the entire dependency tree — not just the packages you list in package.json, but everything they depend on too. The lockfile is what stopped most Laravel projects from being affected, even if they had Axios with a ^ range.

3. Add .npmrc hardening

Create a .npmrc file in your project root:

# Block postinstall scripts — the exact attack vector
ignore-scripts=true

# Don't install packages published less than 7 days ago
min-release-age=7d

ignore-scripts=true is the single most impactful change. The Axios attack relied entirely on a postinstall script to execute the RAT. With scripts disabled, the malicious code never runs — even if the compromised package is in your node_modules.

min-release-age=7d adds a waiting period. Socket detected and npm removed the Axios attack within hours — a 7-day delay means you’d never have pulled it.

4. Run npm audit in CI

Add a GitHub Actions workflow that runs npm audit on every pull request:

# .github/workflows/npm-audit.yml
name: NPM Security Audit
on: [pull_request]
jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
      - run: npm audit --audit-level=moderate

This catches known CVEs before they reach the codebase. It wouldn’t have caught the Axios zero-day (no CVE existed yet), but it’s a free 10-line workflow that covers the common case.

5. Install Socket for GitHub

Socket does what npm audit can’t — behavioural analysis. It detects obfuscated code, suspicious network calls, filesystem writes, maintainer account changes, and 70+ other risk signals. Socket is what detected plain-crypto-js within 6 minutes.

Socket is now integrated directly into npmjs.com — when you check any package page, you’re already seeing Socket’s analysis. The GitHub App automates the same checks on your pull requests.

It’s free for open-source. Install it at socket.dev/features/github.

6. Manual review before adding packages

Automated tools aren’t infallible. In a previous session, Socket gave a 76/100 score to a package that was 1.8MB of obfuscated _0x416039 gibberish with a single anonymous maintainer and no linked repository. Human review caught what tooling missed.

Before adding any package, check npmjs.com for:

7. Open-source only

Decline obfuscated code on principle. Tools like javascript-obfuscator produce output that is intentionally unreadable — and it’s the same technique the Axios attacker used to hide C2 server URLs and shell commands. If you can’t read it, you can’t trust it.

8. Isolate environment variables

If you run MCP servers or other Node subprocesses, only pass the credentials they need. Laravel’s StdioTransport env config replaces the process environment entirely — the subprocess can’t see your APP_KEY, database credentials, or other secrets unless you explicitly pass them.

9. Prefer remote integrations over local npm packages

Remote HTTP MCP servers eliminate the npm supply chain from the equation entirely. No package to compromise, no postinstall script to exploit, no version range to hijack. Local stdio servers are the fallback for vendors that don’t offer a hosted option.

10. Document your CVE decisions

Joel Clermont wrote about this for composer audit, and the same applies to npm. If you know about a vulnerability and choose not to patch, record why.

This is about to become more than good practice. Under the EU’s Cyber Resilience Act (CRA), internal documentation of existing CVEs will be mandatory if you choose not to remediate them. Compliance deadlines begin later this year for anyone selling software to European customers.

How Growth Method handles MCP integrations

Growth Method uses MCP (Model Context Protocol) servers to connect with third-party tools like Bento, Microsoft Clarity, GitHub, and Webflow. Each integration is an npm package running as a subprocess. This is exactly the kind of setup the Axios attack targets — so we built supply chain security into the integration architecture.

Users never run npm install. We pre-install every MCP package and ship it with the app. Users just enter their API credentials and connect. There’s no moment where npm install resolves a ^ range to a malicious version, because users never interact with npm.

Every package is vetted before it enters the registry. We only use official first-party MCP servers from the vendor themselves (Bento’s team, Microsoft’s team, Webflow’s team). Each one is open-source, readable, and published under a standard license. We check the maintainer, the source code, and the Socket.dev analysis before approving it.

Source transparency is built into the UI. Every integration’s edit panel includes links to the package’s GitHub repo, npm registry page, and Socket.dev security analysis. Users can verify for themselves what’s running.

Subprocesses are sandboxed. When Growth Method launches an MCP server, it replaces the process environment entirely. The subprocess only sees the credentials you explicitly pass — it can’t access your APP_KEY, database connection, or any other secret.

Remote servers are preferred over local packages. When a vendor offers a remote HTTP MCP server, we use that instead. No npm package on the server, no supply chain risk. Local stdio is the fallback for vendors that don’t offer a hosted option.

Even with all of this, the risk isn’t zero. A vetted package could have a compromised transitive dependency, or a future update could introduce something malicious. That’s why the protections are layered — pinned versions, ignore-scripts, min-release-age, and Socket for GitHub all apply on top of the vetting process.

How these layers work together

No single protection catches everything. The point is that an attacker needs to bypass all of them simultaneously.

LayerWhat it catches
Pinned versionsAutomatic upgrades to compromised versions
Lockfile committedPins the full transitive dependency tree
ignore-scripts=trueAny postinstall-based attack
min-release-age=7dBrand-new malicious versions
npm audit in CIKnown CVEs on pull requests
Socket for GitHubZero-day malicious packages, obfuscated code, account hijacks
Manual reviewSuspicious metadata tooling misses
Open-source onlyObfuscated packages rejected on principle
Env isolationLimits blast radius if a package is compromised
Remote-first integrationsRemoves npm from the equation entirely

The Axios attack would have been stopped by any one of the first four layers. That’s the whole point of defence in depth — you don’t need every layer to catch every attack. You need at least one layer to catch each attack.

What I asked and learned

Explain .npmrc — do I already have one?

I didn’t. Most Laravel projects don’t. Creating one with ignore-scripts=true and min-release-age=7d took 30 seconds and blocks the exact attack vector used in the Axios compromise.

What about Socket.dev?

Socket is a service you set up outside the codebase. The GitHub App is free, takes 30 seconds to install, and automatically reviews every PR that adds or changes a dependency. It’s the tool that caught plain-crypto-js within 6 minutes.

These APIs rarely change — Bento’s REST API and Clarity’s API are stable. New MCP tools usually just expose existing API endpoints that were already available.

This is another argument for pinning and not rushing to upgrade. MCP servers are thin wrappers around stable REST APIs. A week-old version works just as well as today’s. For stable API wrappers, “latest” is a risk, not a feature.

The bottom line

Dependency auditing needs to be treated like patching, not an afterthought. The Axios attack showed that a single compromised npm account can turn the most trusted package on the registry into a malware dropper — and that AI agents make the blast radius larger than ever.

The good news: the protections are straightforward. Pin your versions, commit your lockfile, add an .npmrc, and install Socket. None of it is hard. The hard part is doing it before you need to.


Back to top ↑