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