What’s up with backdoored npm packages?

A story broke recently about a backdoor added to a Node Package Manager (NPM) package called event-stream. This package is downloaded about two million times a week by developers. That’s a pretty impressive amount, many projects would be happy with two million downloads a year.

The Register did a pretty good writeup, I don’t want to recap the details here, I have a different purpose and that’s really to look at how does this happen and can we stop it?

Firstly, the short answer is we can’t stop it. You can stop reading now if that’s all you came for. Go tell all your friends how smart you are for only using artisan C libraries instead of filthy NPM modules.

The long answer is, well, long.

So the thing is event-stream is an open source project. There are a lot of open source projects. More than we can count. Probably millions. The VAST majority of open source projects are not well funded or run by people getting paid to work on their project. A few are, Linux and Apache are easy examples. These are not the norm though.

We’ll use event-stream as our example for obvious reasons. If we look at the contributions graph, we see a project that isn’t swimming in help or commits. This is probably a pretty normal looking set of contributions for most open source.

So the way it works is if I want to help, I just pretty much start helping. It’s likely they’ll accept my pull requests without too much fanfare. I could probably become a committer in a few weeks if I really wanted to. People like it when someone helps them out, we like helpers, helpers are our friends. Humans evolved to generally trust each other because we’re mostly good. We don’t deal well with bad actors. It’s a good thing we don’t try to account for bad actors in our every day lives, it will drive you mad (I’m looking at you security industry).

So basically someone asked if they could help, they were allowed to help, then they showed their true intent once they got into the building. This is not the first time this has happened. It’s happened many times before, I pretty much guarantee it’s happening right now with other open source projects. We don’t have a good way to know who has nefarious intent when they start helping a project.

At this point if your first thought is to stop using open source you should probably slow down a little bit. Open source already won, you can’t avoid it, don’t pretend you can. Anyone who tells you different is an idiot or trying to sell you something (or both).

As long as open source is willing to allow people to contribute, this problem will exist. If people can’t contribute to open source, it’s not very open. There are some who will say we should make sure we review every commit or run some sort of code scanner or some other equally unlikely process. The reality is a small project just can’t do this, they don’t have the resources and probably never will.

It’s not all doom and gloom though, the real point to this story is that open source worked exactly how it is meant to work. Someone did something bad, it was noticed, and quickly fixed. There are some people who will be bitten by this package, it sucks if you’re one of them, but it’s just how things work sometimes.

It’s a bit like public health. We can’t stop all disease, some number of people will get sick, some will even die. We can’t prevent all disease but we can control things well enough that there isn’t an epidemic that wipes out huge numbers of people. Prevention is an impossible goal, containment is not.

This problem was found and fixed in a pretty reasonable amount of time, that’s pretty good. Our focus shouldn’t be on prevention, prevention is impossible. We should focus on containment. When something bad starts to happen, we make sure we can stop it quickly. Open source is really a story about containment, not a story about prevention. We like to focus on prevention because it sounds like the better option, but it’s really impossible. What happened with event-stream isn’t a tire fire, it’s how open source works. It will happen again. The circle will be unbroken.