Malicious packages in npm. Here’s what to do

Here’s all the information I’ve found.

💻☠️🏴

What happened?#

People found malicious packages in npm that work like real ones, are named similarly real ones, but collect and send your process environment to a third-party server when you install them:

This is dangerous because, on CI servers, the environment usually includes different secret tokens.

What to do if I’m a user?#

Regenerate the secret tokens if you installed any package from these as a dependency:

A screenshot of the cached page with packages

npm has also confirmed this list

babelcli - v1.0.1 - Babel CLI for Nodejs
crossenv - v6.1.1 - Run scripts that set and use environment variables across platforms
cross-env.js - v5.0.1
d3.js - v1.0.1 - d3.js for Nodejs
fabric-js - v1.7.18 - Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.
ffmepg - v0.0.1 - FFmpeg for Nodejs
gruntcli - v1.0.1 - Grunt CLI for Nodejs
http-proxy.js - v0.11.3 - Node.js proxy tools
jquery.js - v3.2.2-pre - jquery.js for Nodejs
mariadb - v2.13.0 - A node.js driver for mysql. It is written in JavaScript, does not require compiling, and is 100% MIT licensed.
mongose - v4.11.3 - Mongoose MongoDB ODM
mssql.js - v4.0.5 - Microsoft SQL Server client for Node.js.
mssql-node - v4.0.5 - Microsoft SQL Server client for Node.js.
mysqljs - v2.13.0 - A node.js driver for mysql. It is written in JavaScript, does not require compiling, and is 100% MIT licensed.
nodecaffe - v0.0.1 - caffe for Nodejs
nodefabric - v1.7.18 - Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.
node-fabric - v1.7.18 - Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.
nodeffmpeg - v0.0.1 - FFmpeg for Nodejs
nodemailer-js - v4.0.1 - Easy as cake e-mail sending from your Node.js applications
nodemailer.js - v4.0.1 - Easy as cake e-mail sending from your Node.js applications
nodemssql - v4.0.5 - Microsoft SQL Server client for Node.js.
node-opencv - v1.0.1 - OpenCV for Nodejs
node-opensl - v1.0.1 - OpenSSL for Nodejs
node-openssl - v1.0.1 - OpenSSL for Nodejs
noderequest - v2.81.0 - Simplified HTTP request client.
nodesass - v4.5.3 - Wrapper around libsass
nodesqlite - v2.8.1 - SQLite client for Node.js applications with SQL-based migrations API
node-sqlite - v2.8.1 - SQLite client for Node.js applications with SQL-based migrations API
node-tkinter - v1.0.1 - Tkinter for Nodejs
opencv.js - v1.0.1 - OpenCV for Nodejs
openssl.js - v1.0.1 - OpenSSL for Nodejs
proxy.js - v0.11.3 - Node.js proxy tools
shadowsock - v2.0.1 - A tunnel proxy that help you get through firewalls
smb - v1.5.1 - A Pure JavaScript SMB Server Implementation
sqlite.js - v2.8.1 - SQLite client for Node.js applications with SQL-based migrations API
sqliter - v2.8.1 - SQLite client for Node.js applications with SQL-based migrations API
sqlserver - v4.0.5 - Microsoft SQL Server client for Node.js.
tkinter - v1.0.1 - Tkinter for Nodejs

Here’s also a one-liner that will list these packages if any of them was installed as a dependency:

Twilio also listed one-liners for checking the whole file system at once (for bash and Powershell)
npm ls | grep -E "babelcli|crossenv|cross-env.js|d3.js|fabric-js|ffmepg|gruntcli|http-proxy.js|jquery.js|mariadb|mongose|mssql.js|mssql-node|mysqljs|nodecaffe|nodefabric|node-fabric|nodeffmpeg|nodemailer-js|nodemailer.js|nodemssql|node-opencv|node-opensl|node-openssl|noderequest|nodesass|nodesqlite|node-sqlite|node-tkinter|opencv.js|openssl.js|proxy.js|shadowsock|smb|sqlite.js|sqliter|sqlserver|tkinter"

Always check the name of packages you’re installing. You can look at the downloads number: if a package is popular but the downloads number is low, something is wrong.

What to do if I’m a library developer?#

I see two options:

  • Use scopes (@scope/package-name) for your packages. With scopes, it’s harder to install a wrong package accidentally: a user would have to misspell both the scope name and the package name. Unfortunately, it’s not: it’s enough to misspell just the scope name (e.g @babel/babel-cli@bable/babel-cli). Scopes might help a bit because they can have a simple name that’s harder to misspell, but it’s still not a universal solution.
  • Take the most common misspellings of your packages by yourself. Think of the most common misspellings and publish empty packages under these names. You can also warn users about the right name with npm deprecate.

Is this even OK?#

This isn’t surprising – npm doesn’t have any protection against this yet. In fact, that’s why there could be other malicious packages. Stay careful and check package names.

npm is working on a solution though:

You can also participate in the Yarn’s discussion about a white list for preinstall/postinstall script packages.


npm 4 is splitting the “prepublish” script into “prepublishOnly” and “prepare”

Updated 24 Jun 2017: reflected changes in plans about npm v5..v6.

On October 20, npm is releasing v4.0.0. This release, apart from the other breaking changes, also includes one affecting a lot of packages: the prepublish npm script will be split into prepublishOnly and prepare.

📅 💥

Why#

In v1.1.71, npm made the prepublish script also execute when you run npm install without arguments. Before this, npm ran the script only before you publish a package.

The reasoning behind this isn’t clear, but as far as I’ve got it’s the following. You usually run npm install without arguments when you clone a package, go into its directory and try installing its dependencies. If you’re doing this, you’re most likely a developer, and you’re going to do something with this package; therefore it’ll be useful to prepare the package for using. Since the prepublish script often includes commands that build the package for publishing (= prepare it for using), npm decided to execute it in such case to do this job.

However, this prepublish behavior became disliked:

  • It’s weird. prepublish is pre + publish, and a lot of people didn’t assume it’s also being run when installing dependencies. I even thought it’s a bug in npm when I first discovered the way it works.

  • It creates problems. Many projects I’ve seen put building and testing commands into the prepublish script. Even npm recommends doing this. It’s convenient: it prepares the package for publishing and prevents an occasional release of broken code.
    However, if you use a CI environment like Travis or Appveyor which installs a new version of the package on each build, things become worse. Your build and test tasks get executed twice, once on npm install and once on the actual npm test. And this creates you problems such as increased build time or wrong build status.

Current behavior is disliked

What’s next#

npm 4 is splitting the prepublish script into two new: prepare and prepublishOnly.

  • prepare will have the same behavior that prepublish has now. Commands in this script will run both before publishing and on npm install.

  • prepublishOnly, as seen from its name, will run only before publishing the package.

  • Also, prepublish will receive a warning about the changes.

npm realizes that prepublishOnly is an ugly name for a script, Therefore, there’re also some other changes planned for the later releases.

  • npm 5 will un-deprecate previously deprecated prepublish, make it run only before publishing, and will deprecate prepublishOnly. In this release, prepublish will get back its expected behavior.

  • npm 6 or later will remove prepublishOnly completely. After this, two scripts will remain: prepublish which will be run only before publishing, and prepare which will be executed both on before publishing and on npm install.

Update 24 Jun 2017: the plan above was true at the moment of publishing, but seems like npm doesn’t follow it now. See a twitter thread with @maybekatz for more details.

There will be changes in npm 4…6

What to do when npm 4 is out#

Decide based on what your package needs.

  • If your package’s prepublish script contains commands that should only be run before npm publish, rename the script into prepublishOnly. Examples of such commands are npm test and eslint.
  • If your package’s prepublish script contains commands that should run both before npm publish and on npm install, rename the script to prepare. Examples of such commands are commands that build your package (e. g. webpack or babel).
  • If you don’t know, rename the script to prepublishOnly – this is the behavior most people expect. (As another option, you can leave everything as-is, but there’s no point: it will just delay the decision, but not remove it.)

P.S. If you like this, follow me on Twitter. I tweet selected articles and my own thoughts: https://twitter.com/iamakulov