Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ


Choose your language

InfoQ Homepage News NPM Worm Vulnerability Disclosed

NPM Worm Vulnerability Disclosed


The NPM project has formally acknowledged a long-standing security vulnerability in which it is possible for malicious packages to run arbitrary code on developer's systems, leading to the first NPM created worm. In vulnerability note VU319816, titled "npm fails to restrict the actions of malicious npm packages", Sam Saccone describes the steps needed to create a worm and to allow it to spread automatically. Although this was reported in January 2016, the issue has been widely known about and has existed since the initial release of the NPM repository manager.

The root of the problem is that NPM modules have associated scripts that can be executed by NPM when they are installed. These include plenty of opportunities to execute code when the module is downloaded from NPM:

  • preinstall - run before the package is installed
  • postinstall - run after the package is installed
  • preinstall - run before the package is uninstalled
  • uninstall - run while the package is uninstalled
  • postuninstall - run after the package is uninstalled

These scripts exist to make publication of NPM modules easier and to perform post-processing of the contents before it is used. For example, some modules might be originally written in CoffeeScript or TypeScript that require a translation step, or may be written in ES6 and require a Babel translation to run on current browsers. In addition JavaScript libraries are often minfied (to make them smaller) which is usually an automated step as well. Since JavaScript is interpreted, tools like Make are not used and so the npm scripts are used to do the heavy lifting.

NPM misuses these scripts to perform not just build time validation but client-side execution when modules are downloaded and used by a client. For example, the left-pad fiasco (covered by InfoQ yesterday) was resolved by republishing the modules; however, if such widely-used modules (such as the true module, whose purpose is to only print true) has an infected scripts block then code execution across millions of machines is a real possibility.

These scripts allow the creation of NPM worms, which go on to infect other packages and spread accordingly. NPM package developers can publish their packages (using npm, of course) to the NPM repository using standard credentials. In order to facilitate publication, it's possible to npm login into the repository and stay signed in for an arbitrary amount of time, after which any packages may be published to the repository. As such, once an npm developer's machine is compromised, the worm can scan for any additional packages and then republish them (using the developer's existing credentials) with the worm injected into the scripts block of the newly infected packages.

Furthermore, the package manager itself uses JavaScript execution meaning that just resolving a package's dependencies can result in arbitrary code execution. A proof of concept was published which used a variable for the package name, thus appearing to masquarade as any package. In true open source spirit, the proof of concept is MIT licensed:

echo '{
   "name": "'"$A"'",
   "version": "2.0.0",
   "description": "",
   "main": "index.js",
   "scripts": {
     "test": "echo \"Error: no test specified\" && exit 1"
   "author": "",
   "license": "ISC"
}' > package.json
npm publish

If a package developer downloads this package and executes the script then it will execute index.js, which in turn can execute arbitrary JavaScript on the end machine. The proof of concept shows how to run sudo rm -rf / which will cause problems for those without backups.

The problems are further complicated in that the NPM repository allows anyone to repurpose an existing package, simply by publishing a new version into their space with the name identifier. Unless developers explicitly lock down the version that they depend upon, automatic upgrades to the latest version are trivially possible and are likely to see further attacks in future.

The NPM response so far has been rather weak, disclaiming responsibility for scanning for malware while pointing out that developers using modules are themselves to blame for the infection. Yet the whole infrastructure is set up to allow anyone to own packages and replace them with arbitrary JavaScript; even if the scripts themselves are excluded (by using npm install --ignore-scripts or npm config set ignore-scripts true) then it is still possible to replace the body of a JavaScript file with require('shelljs').exec('rm -rf /') and have the same devestating effect when run. Furthermore leaving an npm session logged in means that any application, whether NodeJS based or otherwise, can easily publish scripts on behalf of that user without their knowledge.

What recent events have shown is that the meteoric growth of JavaScript and the use of NPM on the server was given the same focus on security as the JavaScript language itself. The only surprisng thing about the whole affair is that it has taken this long for the house of cards to have fallen down.

Rate this Article


Hello stranger!

You need to Register an InfoQ account or or login to post comments. But there's so much more behind being registered.

Get the most out of the InfoQ experience.

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Community comments

  • Misconceptions about Gradle

    by Hans Dockter,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    Your article states:

    NPM is not the only incompetent package manager; Gradle builds use the Groovy language for their build process, which can trigger arbitrary code execution during the calculation of dependencies.

    Gradle is trusted by some of the most advanced, and yes most security-conscious organizations in the software world, calling it “incompetent” shows either poor word choice or a lack of understanding.

    As your article points out, NPM has mechanisms for executing code downloaded from a dependency when a package is installed. Gradle has no such mechanism and uses a declarative data format (pom.xml or ivy.xml) when resolving dependencies--just like Maven, Ant/Ivy, and SBT. The fact that Gradle uses a Domain Specific Language (DSL) based on the Groovy language for its build automation scripts has nothing to do with it.

    The entire purpose of dependency management is to download 3rd party software onto your machine, which you will then run or ship. Everything is predicated on trusting the source of the software and the pipes that it travels over. We could all do better to make the circle of trust stronger. Specifically, our tools could be doing things like making it easier to produce and verify signatures, to whitelist dependencies, to audit usage of dependencies, to push out security updates, etc. Gradle already provides extensions to implement some of those policies, for example:

    As mentioned above, Gradle Inc. works with some of the best software teams in the world. In partnership with them, we will continue to provide leadership in this area.

    Hans Dockter,
    CEO, Gradle Inc.

  • Re: Misconceptions about Gradle

    by Charles Humble,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    Hi Hans,

    In hindsight the comments with regards Gradle could have been judged. With Alex’s permission I've removed that paragraph and also made some other minor amendments to the article.

    We could all do better to make the circle of trust stronger.

    I very much agree with this point and would like see some more discussion around some of the things that the industry could be doing to improve the situation. If nothing else the situation with regards to NPM will, I hope, encourage everyone who provides dependency management tooling to look at some of the options around security and so on.

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p