Skip to content

How to configure and use package managers securely

Recently there has been greater prominence given to supply-chain attacks where malicious packages run a payload at install time to exfiltrate credentials from CI pipelines or individual developer machines. While the most prominent examples have centred on the JavaScript and TypeScript ecosystem, the issue is not unique to those languages.

This how-to guide shows you how to securely configure your language-specific package managers to guard against supply chain attacks. Generally speaking we try to use the following tactics:

  • Disable the ability to run "post-install" or similar scripts. While a compromised package may still have a malicious payload which activates at runtime, we tend to run applications locally inside containers which have few credentials available. Developers tend to install packages locally to enable IDE code intelligence features and a developer's machine may have far more credentials available for a payload to exfiltrate.
  • Enforce a minimum release age. The recent spate of supply-chain vulnerabilities have been focussed on "get as much as possible as quickly as possible" and so they have been rapidly discovered. As such by imposing a minimum age of releases considered for install we give ourselves a window where compromised packages can be discovered and removed before we install them. (Our renovatebot instance does this. It's default configuration is to require a minimum age of 3 days for releases from npm.)

JavaScript and TypeScript

We recommend using yarn as a package manager because its defaults include both disabling scripts and a minimum release age of 3 days but only if you are running a recent version of yarn. This section will cover other popular package managers in addition to yarn since we use a variety.

In summary, if you have no other requirements on which package manager you use:

  • Use yarn.
  • Use corepack to make sure you are using the most recent version of yarn (see below).
  • Explicitly set enableScripts and npmMinimalAgeGate globally (see below).
  • Explicitly set enableScripts and npmMinimalAgeGate in your package (see below).
  • Explicitly set the version of yarn used by your package (see below).
  • Use yarn dlx instead of npx to run ad hoc scripts from a command.

yarn

The yarn tool has the enableScripts and npmMinimalAgeGate settings. The default values for these are to disable script running and to gate packages to those which were released at least 3 days ago.

However, you need to make sure you are running the most recent version of yarn as these defaults have changed.

To ensure you are running the most recent yarn:

$ npm install --global corepack
$ corepack prepare yarn@stable --activate
$ yarn --version  # should be >= 4.0.0
4.14.1

To set secure defaults for the yarn command:

$ yarn config set --home enableScripts false
➤ YN0000: Successfully set enableScripts to false
$ yarn config set --home npmMinimalAgeGate 3d
➤ YN0000: Successfully set npmMinimalAgeGate to 4320

To check that yarn is correctly configured:

$ yarn config get enableScripts
false
$ yarn config get npmMinimalAgeGate
4320

To make sure that your JavaScript or TypeScript package has secure defaults, firstly ensure that package.json has a packageManager field. E.g.:

{
  "packageManager": "yarn@4.14.1"
}

Important

The packageManager field specifies the exact version of yarn to use and renovatebot will keep this fresh for you.

Then, within the directory containing package.json, run:

$ yarn config set enableScripts false
➤ YN0000: Successfully set enableScripts to false
$ yarn config set npmMinimalAgeGate 3d
➤ YN0000: Successfully set npmMinimalAgeGate to 4320

This should create a .yarnrc.yml file with the following contents:

enableScripts: false

npmMinimalAgeGate: 3d

npm

Warning

Because of its secure defaults, we recommend that you use yarn for package management unless you need features only available in npm.

The npm tool has the ignore-scripts and min-release-age settings. The default values for these are to allow running scripts from packages and to have no minimum package age gate.

Firstly, ensure that you are running version 11 or greater of npm:

$ npm --version
11.12.1

Set the ignore-scripts and min-release-age settings globally by adding the following lines to ~/.npmrc:

ignore-scripts=true
min-release-age=3

Note

npm config set --global does not, as you might expect, set values in $HOME/.npmrc, it tries to set them system-wide which may required elevated privileges.

Check that these settings have taken effect:

$ npm config get ignore-scripts
true
$ npm config get before
{ ... some date three days ago ... }

To configure your package similarly, add the ignore-scripts and min-release-age to a .npmrc file in the same directory as your package.json file.

pnpm

Warning

Because of its secure defaults, we recommend that you use yarn for package management unless you need features only available in pnpm.

The pnpm tool has the ignoreScripts and minimumReleaseAge] settings. The default values for these are to allow running scripts and, post version 11 of pnpm to have a minimum age gate of 1 day.

Warning

Even if you set ignoreScripts to true, pnpm will still execute arbitraty code from the .pnpmfile.{mjs,cjs} files. Because of this we do not recommend using pnpm unless you require pnpm-specific features.

To set the enableScripts and minimumReleaseAge settings globally:

pnpm config set --location global ignore-scripts true
pnpm config set --location global minimum-release-age 4320

Check that these settings have taken effect:

$ pnpm config get ignore-scripts
true
$ pnpm config get minimum-release-age
4320

To set the enableScripts and minimumReleaseAge settings within your project, change to the directory containing package.json and run:

pnpm config set --location project ignore-scripts true
pnpm config set --location project minimum-release-age 4320

If you require pnpm to install your package, also set packageManager in package.json accordingly.

Other languages

If you feel other languages' package managers are missing in this document and if you know how to configure them securely, please consider updating this page.

Summary

In this guide you learned how to securely configure various package managers to avoid certain categories of supply chain attacks.