The Newsroom Jun 25th, 2026

Hardening Our Supply Chain Security

Hardening Our Supply Chain Security

Supply chain attacks are having a moment and AI is making them cheaper to pull off. Here's what we're doing about it.

Jason Varga 8 min read

Supply chain attacks are all the rage these days (arguably more popular than those πŸ₯¦ haircuts), and AI is fueling and fanning the fire at a never-before-seen pace. Automated tooling makes it cheaper than ever to find a leaked token, poison a package, and let it ride into thousands of apps before anyone notices.

So we spent a chunk of time proactively tearing apart our own release pipeline and security procedures and rebuilding them to be a lot harder to abuse.

Here's how we approached it and everything we changed. Steal whatever's useful for your own setup β€” we're all in this brave new world together.

The attack that got our attention

In May 2026, the popular laravel-lang packages were compromised in a supply chain attack. The mechanics are worth understanding because they're not exotic. They're the kind of thing that could happen to almost any honest package maintainer.

  • The attacker got hold of a leaked GitHub access token and used it to rewrite git tags, pushing poisoned versions under existing, legitimate version numbers.
  • The malicious code added an entry to the package's autoloaded files, so it ran on every request of every app that installed the infected version.
  • The payload was a credential stealer, harvesting secrets from affected applications.
  • Hundreds of versions were affected before it was caught, and the impact rippled across everyone who pulled the poisoned releases.

This sort of thing happens all the time in NPM Land, but it's much rarer in our beautiful PHP Garden with Composer. Seeing it hit a well-known Laravel package was honestly a bit eye-opening. We took it as a reminder that the underlying flaw isn't ecosystem-specific, ours just hasn't been highly targeted like the JS community has. A single leaked access token that can publish releases is all it takes.

Obviously we didn't want to be the next headline, but more importantly we didn't want to put you at risk by shipping something poisoned through a Statamic release. So we got to work.

How we approached it

We worked backwards from the root cause as a thought experiment, imagining we had a leaked token used to publish an infected payload β€” and looked back up that chain for exploit opportunities. That gave us five jobs to tackle, each one a different point where we could prevent an attack. Here's how we handled each, in order β€”Β top to bottom.

1. Reduce the tokens that exist

The simplest way to stop a leaked token from doing damage is to have fewer tokens worth leaking. So we audited and removed unused GitHub access tokens, shortened token lifetimes, tightened what tokens are even allowed to exist in the first place, and who had them.

Where automation genuinely needed credentials, we moved it onto short-lived, narrowly-scoped ones instead of standing long-lived tokens. The fewer broad, permanent credentials floating around, the smaller the blast radius if one ever gets out.

2. Make accounts harder to take over

A hijacked account is just another route to a token, so closing that path matters as much as the tokens themselves.

Strong two-factor is now required across the entire org, with weak SMS-based methods disallowed β€” phishing-resistant methods only. If someone can't get into the account, they can't mint a token from it. Box checked.

3. Limit what a token can do even if it leaks

This is the big one, and it's where releases and tags come in.

A release tag is the most sensitive thing in the whole pipeline. It's the moment code becomes something you install β€” and it's exactly what the laravel-lang attacker abused. So that's where we focused most of our energy.

Our Statamic releases are now cut only by an automated pipeline. No human β€” not even an admin β€” can create a release tag directly anymore. Even a trusted person's leaked credential shouldn't be able to publish a release on its own, and taking humans out of the path means a stray token simply can't mint a release tag. Control now lives in who can trigger the release workflow, which is fully auditable, rather than who holds a token that can push a tag.

That pipeline runs through a protected GitHub environment, restricted with a branch whitelist. A release can only ever be cut from an approved branch like 6.x or 5.x β€” never from an arbitrary or attacker-created branch.

And the tags themselves are locked down. They can't be deleted, moved, or overwritten, which defeats the retroactive tag-rewrite trick at the heart of the attack.

We also stopped allowing direct commits to release branches entirely. Changes to branches like 6.x now must go through pull requests with required checks and reviews, never a direct push. We've always worked this way (though every now and then a typo or tiny CSS tweak may have been made via a direct branch commit), but now it's enforced.

Finally, we run Zizmor, a linter for our GitHub Actions workflows, in its strictest mode on every change, so risky workflow patterns get caught in review. It enforces things like pinning third-party actions to a specific commit SHA (rather than a tag like v4, which can be silently re-pointed at malicious code), and giving each workflow the lowest privileges it needs instead of a broad default token.

4. Catch a poisoned payload if one slips through

We assume, despite all of the above, that something could still get through. So we built tripwires around the parts that would hurt most.

An autoload tripwire. The exact laravel-lang payload worked by adding a file to the package's autoload list. We added an automated check plus a required security review on those files, so that change can't happen unnoticed.

A dependency cooldown. Dependabot applies a cooldown across all our dependencies β€” GitHub Actions, npm, and Composer alike β€” that waits a set period before pulling in a new release, long enough for a freshly-poisoned version to get caught before we'd ever adopt it. It also keeps our SHA-pinned GitHub Actions up to date automatically, so pinning doesn't mean going stale.

5. Monitor everything

If everything else fails, we want to know instantly.

Every tag and release pings us in Slack and Discord, showing who triggered it. As a last line of defense, if a token ever did get stolen and used to push a release, it'd be immediately obvious β€” one came through that none of us actually triggered.

Laravel Moat

Thankfully we didn't need to do all this by hand. We used Laravel Moat to audit the security posture of our GitHub org and repos β€” things like 2FA enforcement, branch protection, and secret scanning. It surfaced the recommendations and we did the work. It gives us a running checklist of things to harden instead of trusting ourselves to spot every gap.

If you're looking to harden your own release security in a similar way, I highly recommend incorporating Moat into your process.

Bonus points: compiled assets

While we were in here, we took the opportunity to shrink our attack surface in a spot unrelated to supply-chain attacks: how we bundle compiled CSS and JS assets.

The old way: Statamic's compiled front-end assets weren't committed to the repo. Instead, a Composer plugin (pixelfear/composer-dist-plugin) downloaded a separate pre-built asset tarball from the GitHub release when you installed Statamic.

We did it that way on purpose, and still think it was a good solution overall. It kept compiled assets out of the repo so the source stayed clean and we weren't committing build output alongside the code. (That has its own security upside β€” it's easier for someone to slip malicious code into a minified file, because no human actually reads the whole thing.)

But that tarball was a second, independent thing to trust. It lived outside our new hardened source and version chain, which made it its own potential poisoning target. Not to mention every install ran a third-party plugin in its path, even though only one of us (me β€”Β Jason Varga) maintained it. Be wary of single points of failure like this.

The new way: our release pipeline now builds the compiled assets in CI and commits them straight into the tagged commit, cutting out the plugin entirely.

The assets now ride the exact same integrity chain as the source code β€” tag β†’ commit β†’ locked in composer.lock. There's no separate tarball to swap, and no extra plugin in your install path. It's nice and clean.

Looking ahead

The pipeline that ships Statamic to you is significantly stronger and more resilient than ever, but security is never finished. I'm sure there will be new and unforeseen exploits out there thanks to the power of AI (Mythos, anybody?), so we'll stay vigilant.

We've got your back.