Skip to content

[RRFC] Approved package lists #480

@johndiiorio

Description

@johndiiorio

Motivation ("The Why")

Today (10/22/21), ua-parser-js was maliciously compromised, a package with millions of downloads per week. The npm team responded quickly, but there was a 4-hour window where many people could have been affected. This includes those with with a package-lock.json if they were modifying dependencies. This RRFC aims to put an end to compromised transitive dependencies.

"The How"

I propose that npm implements a new feature called "Approved package lists" (name of course up for discussion). This would be an entirely opt-in feature for teams where security is critical. Here's how it works:

  • The npm package manager is now aware of a new special file, package-approvals.json. This would live along side package.json and package-lock.json. It would be excluded from files published to the registry on npm publish.
  • This file would have the following structure (for the initial version):
{
    version: number,
    packages: {
        [<package-name>]: string[],
    },
    extends?: string; // file path or URL
}

In each array associated with the package, there would be a string of version numbers, e.g. ['1.0.0', '1.1.9', '2.3.4']. Importantly, there would be no support for ranges of versions.

  • A number of commands would be added to the npm cli, such as npm approve <package-name>@<version>, npm unapprove <package-name>@<version>, and npm approve --all-current-packages. The npm approve --all-current-packages would have a Yes/No prompt to confirm this choice.
  • The package.json file would support a new key: approvals?: string. This optional key could either be a relative/absolute path to a file with the package-approvals.json structure or a URL to retrieve the file.

The idea behind the above proposal would be to change how npm install/npm ci works, and would apply to workspaces. On install, if npm would resolve a package (including transitive packages) but the package version does not exist in the approvals list, npm would error. Importantly, this would occur before any files are written to disk and package scripts (e.g. postinstall) are run. The error could include a list of all packages that don't satisfy the approvals. Developers could resolve this error by running npm approve <package-name>@<version> for a single one, or (if they are really sure their current dependencies are safe), run npm approve --all-current-packages. These convenience scripts would modify the local package-approvals.json file.

Developers would be able to leverage this new file by guaranteeing that they never use an un-audited dependency. Each team of developers could perform their own audit of the dependencies they wish to use, then add each one to the approvals list. This could be a time consuming task, but worth it. To alleviate some of the burden, this file could be hosted somewhere accessible by the internet, so teams or individuals could share their configuration via the approvals package.json field. Additionally, the package-approvals.json has an extends field that could resolve and recursively extend other package-approvals.json files. Obviously there are too many npm packages to have a single community-hosted package-approvals.json file for everyone, but the community could create shared ones for different use-cases.

Example

{
    version: 1,
    extends: 'https://example.com/path/to/some/package-approvals.json',
    packages: {
        lodash: ['4.17.19', '4.17.21'],
        react: ['16.0.8', '17.0.2'],
        // and many more
    },
}

I feel that something like this would greatly improve security in the npm community. Please let me know what you think, or if there is a similar RRFC like this already.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions