Git Pull Killed My Saturday, And What I Learned About Supply Chain Attacks We're Not Ready For

A

Adil Sher

Author

Sep 27, 2026
5 min read
1 views
Git Pull Killed My Saturday, And What I Learned About Supply Chain Attacks We're Not Ready For

I remember the moment. It was 11 PM on a Friday, and I'd just merged a legitimate PR into our develop branch. Standard stuff. Code review passed, CI ran green, everything looked fine. Then Saturday morning, our security team's automated alerts started screaming. Someone's local machine had been compromised, and by the time we realized it, malicious code had been force-pushed across our repositories. No new packages installed. No weird dependencies. Just a git pull and npm run build, and suddenly our build config was weaponized. That was the day I realized how naive I'd been about supply chain security.

What happened to us is part of a coordinated campaign that's been quietly targeting npm and Vite projects. And here's the thing that keeps me up at night: it's elegant. Not in a "beautiful code" way, but in the way a really good exploit is elegant, it works because it understands exactly how we work, what we don't pay attention to, and what our tools miss.

The Attack That Hides in Plain Sight

The attack works in three coordinated stages, and each one is designed to evade the defenses we actually have in place.

First, the attacker plants obfuscated code directly into your build config file, vite.config.js, in this case. They hide it at the end of the file, padding it with hundreds of spaces so it scrolls off-screen. When you run git diff, it just looks like someone tweaked the closing brace. Two lines changed. Nothing suspicious. This is the opposite of how modern supply chain attacks usually work. We're trained to scan package.json and scrutinize node_modules. We run audit tools constantly. But a poisoned config file? That slips right through because we're looking in the wrong place.

Second, they use the blockchain as a dead drop for the command-and-control server address. This is the part that actually impressed me, even though it terrified me. The malware connects to public Ethereum RPC endpoints, walks through recent blocks looking for a specific transaction, and extracts the C2 server address from it. When law enforcement seizes the server, the attacker just writes a new address to the blockchain. It never stops working. Blocking by destination domain is impossible because the malware phones home to legitimate, public blockchain infrastructure. You can't blacklist publicnode.com without breaking half the web3 ecosystem.

Third, once the resident process is installed, it's basically a remote code execution vuln waiting to happen. It calls eval() on code received from the C2 server. Anything is possible from there.

The Part That Actually Got Me, Forged Commits

What genuinely alarmed me was how the malicious code got into the repository in the first place. The attacker didn't create a new commit. They copied a legitimate merge commit wholesale, injected the payload, and force-pushed it back under the same message, same author, same timestamps. On GitHub's web UI, it looked like one ordinary merge. Two commits with identical merge messages. No red flags.

This tells me the attacker had stolen Git hosting credentials from someone's machine. But it also tells me something worse: our commit verification process failed completely. GitHub shows a green checkmark if there's a valid signature, but this commit didn't have one. Did we notice? Probably not. I know I wouldn't have scrolled down to check.

What This Means for How We Actually Work

I've been thinking about this wrong. We treat security like it's someone else's job, the DevOps person, the security team, whoever. But this attack bypasses all of that because it targets developers directly. Fourteen minutes after a legitimate PR merged, a dozen branches were poisoned in automated fashion. If your CI/CD is running, it goes straight to production.

Here's what I'm actually doing now:

# Check for unsigned commits in your repo
git log --pretty=format:"%h %G? %s" | grep -v "G"

# Verify commit signatures on recent pushes
git verify-commit HEAD~5..HEAD

# Look for whitespace anomalies in config files
cat -A vite.config.js | tail -20

The first command will show you any commits without valid signatures. The second verifies the last few commits. The third uses cat -A to reveal hidden whitespace, tabs, trailing spaces, all of it.

But honestly? These are band-aids. The real problem is that we've normalized trusting code we don't understand because it came through the right process. A merge commit looks legitimate, so we don't look at it. A config file change is small, so we don't review it carefully.

What I'm Still Struggling With

I don't have a clean answer here. We can require GPG signatures on all commits. We can audit config files aggressively. We can monitor for force-pushes. But an attacker with stolen credentials and three minutes can do all of this faster than we can detect it.

What would actually help? Immutable audit logs that can't be overwritten, even with force-push. Real-time alerts on force-pushes to protected branches. And honestly, we need to stop treating build config files as infrastructure we don't need to review carefully.

I want to know: are you checking your recent commits for unsigned pushes? Have you thought about what happens if someone's local credentials get compromised? Because I'm pretty sure most of us haven't, and that's exactly what makes this attack work.

Source: This post was inspired by "Infected by git pull and npm run build, Malware planted in a build config through a forged merge commit" by Dev.to. Read the original article

Share this article

Written by Adil Sher

Full stack developer building high-traffic platforms, AI services, and custom web applications. Explore my portfolio, learn about my background, or get in touch.

Related Articles

I Finally Understand Why My PRDs Keep Dying in Production
Web Development Sep 26

I Finally Understand Why My PRDs Keep Dying in Production

Two years ago, I watched a payment feature I built fail spectacularly because "instant refunds" meant something completely different to our finance team than it did to me. The PM wrote "instant," I designed async eventual consistency, and we shipped a system that technically work...

Stop Selling Your Code. Start Selling Solutions.
Web Development Sep 10

Stop Selling Your Code. Start Selling Solutions.

I've been grinding as a freelancer in Islamabad for five years now, and I've made every mistake in the book. I've spent weeks perfecting a portfolio that nobody reads. I've engaged in architecture debates with CTOs that went nowhere. I've underbid jobs and overworked myself becau...