All Insights

The Supply Chain Attack That Broke the 'Pin Your Dependencies' Rule

CivSafe Team·May 26, 2026·6 min read

This Thursday night at 10:32 PM UTC, someone with a stolen GitHub token spent about 15 minutes quietly rewriting history. Every version tag on four popular PHP packages — the ones that millions of Laravel developers use to handle app translations — got swapped out to point at a new, malicious commit.

By the time Aikido Security caught it on May 22 and Packagist yanked the packages the following day, hundreds of versions were poisoned. And here's the part that should make your stomach drop: pinning your composer.lock to a specific version tag did absolutely nothing.

What Happened

The four packages: laravel-lang/lang, laravel-lang/attributes, laravel-lang/http-statuses, and laravel-lang/actions. If you've ever built a multi-language Laravel app, you've probably got at least one of these in your composer.json. They handle things like displaying "Password is required" in French or German — boring, essential stuff.

An attacker got hold of a GitHub Personal Access Token (PAT) — believed to be leaked in a recent GitHub credential exposure — that had org-wide push access to the laravel-lang organization. From there, they did something clever and underhanded: instead of publishing a new malicious version (which might get flagged), they rewrote the existing git tags for roughly 700 historical versions to point at commits they controlled.

The attack vector is subtle. GitHub lets version tags point to commits from forks. The attacker exploited that. If your composer.lock said "laravel-lang/lang": "13.15.0", Composer would go fetch what "13.15.0" currently points to — and as of Thursday night, "13.15.0" pointed at malware.

All four compromised repositories share the same fake author identity, the same modified files, and the same payload behavior. One attacker. One stolen credential. Fifteen minutes. 700 versions backdoored.

What the Malware Did

The injected src/helpers.php file was added to Composer's autoload.files directive. That's the list of files PHP loads on every single request, before your app does anything else. The moment someone ran vendor/autoload.php — which is literally the first thing every Laravel app does on startup — the payload fired.

It pulled down a secondary binary from flipboxstudio[.]info and started harvesting:

  • Your .env file (where your OpenAI keys, AWS Bedrock credentials, database passwords, and app secrets all live)
  • AWS, GCP, and Azure access keys
  • GitHub personal access tokens
  • Kubernetes and Vault secrets
  • Stripe API keys
  • Slack tokens
  • SSH keys
  • Browser-saved passwords and credentials
  • Cryptocurrency wallets
  • VPN configurations
  • Password manager vaults

This wasn't spray-and-pray ransomware. This was a carefully targeted credential vacuum. Whoever wrote it knew exactly what lives on a Laravel developer's machine.

Why This Matters Beyond Laravel

If you're running AI tools in your organization — and increasingly, you are — there's a good chance your AI API keys live in the same place as everything else: .env files, credential stores, and shell profiles on developer machines.

The developers building or maintaining the Laravel apps in your stack probably also have OpenAI or Bedrock API keys stored locally, AWS credentials for AI infrastructure, and GitHub tokens that can trigger CI/CD pipelines. One compromised developer machine in a five-person shop can be the domino that takes down your entire AI setup. Not just your website. Your AI workflows too.

This is the pattern we keep seeing: a small team builds an AI-powered product or workflow, everything is humming, and then something three layers down in their dependency tree gets poisoned. By the time anyone notices, credentials have been walking out the door for days.

If You Use laravel-lang (or If Your Devs Do)

First, check. Run this in any Laravel project:

composer show laravel-lang/*

If you see any of these packages installed, check your vendor/laravel-lang/ directory for a file called helpers.php. It should not exist. If it does, treat the machine as compromised.

Also: did any system run composer install or composer update between 22:32 UTC on May 22 and midday May 23? If yes, assume it's compromised regardless of what composer show says.

The network indicator to check: any outbound connection to flipboxstudio[.]info in your logs.

If you're affected:

  1. Remove the packages: composer remove laravel-lang/lang laravel-lang/attributes laravel-lang/http-statuses laravel-lang/actions
  2. Delete the temp directory: <sys_get_temp_dir>/.laravel_locale/
  3. Run composer clear-cache and rebuild affected images
  4. Immediately rotate everything: AWS keys, GitHub PATs, Stripe API keys, Slack tokens, SSH keys, your Laravel APP_KEY, and any AI API keys you had on that machine

Don't skip step four. Especially the AI API keys. Especially if you're paying per-token.

The Thing Security Teams Need to Internalize

Every security guide says "pin your dependencies." Lock your composer.lock. Don't use floating versions. That advice is still sound. But this attack exposed a gap that's been quietly waiting to be exploited: you can pin to a tag, but if the tag itself gets rewritten, your pin means nothing.

The mitigation that actually protects against tag rewriting is to pin to a commit hash in your composer.json, not a version tag:

"laravel-lang/lang": "dev-main#a3f8bc9..."

It's ugly. Most dev teams won't do it until an attack like this burns them. But it's the only way to guarantee you're getting the exact bytes you audited. Tag immutability is a convention, not a technical guarantee — and this week someone proved that gap is exploitable at scale.

The broader lesson: open-source package ecosystems assume the accounts maintaining them haven't been compromised. When that assumption breaks — via a leaked PAT, a phishing attack on a maintainer, or a platform-level breach — every downstream org is on their own.

What We Tell Clients

The orgs that catch this fast have two things in common: they audit their deps regularly (weekly, not quarterly), and they've got alerting on unusual outbound traffic from their build servers. Neither of those is expensive. Both require someone to actually set it up.

If your team is running Laravel apps alongside AI integrations and you want to make sure you're not quietly leaking credentials, that's exactly the kind of setup we help with in a sprint.

CivSafe — Strategic Innovation. Community Impact.