NPM CI Lifecycle Script Security Controls
Overview
Party Bus pipelines now enforce npm ci --ignore-scripts to reduce supply chain attack risk. This document describes the impact on lifecycle scripts, required configuration changes, and the approved mechanism for selectively allowing necessary scripts.
New file requirement for npm pipelines
REMINDER
To provide better security for npm pipelines, a new file (even if empty) named .npm-allow-scripts is required in all npm repos. Please read the Transitive Lifecycle Scripts section below for more details on the contents of this file.
Pipeline Behavior Change
Moving forward, the npm ci command that runs in Party Bus pipelines will include the --ignore-scripts flag. We recommend you have the ignore-scripts=true setting in your projects .npmrc.
This disables:
- Automatic execution of project lifecycle scripts during install
- Dependency lifecycle scripts during installation (
prepare,preinstall,install, andpostinstall)
These changes are motivated by the increase in supply chain attacks targeting dependencies in the Node.js ecosystem. Many of these supply chain attacks have relied on the use of install-time lifecycle scripts (prepare, preinstall, install, and postinstall) that run during the installation of a compromised dependency when running either npm install or npm ci.
Unfortunately, utilizing the ignore-scripts setting blocks both transitive scripts that would run as a result of a dependency install and explicit user-defined scripts specified in the project's package.json.
Impact On Your Projects
Given that customer-defined lifecycle scripts (explicitly defined prepare, preinstall, install, and postinstall in the package.json) will no longer automatically run during the npm-ci job, we recommend that you move towards utilizing custom build scripts defined in your package.json.
INFO
If your project requires a project-level npm lifecycle script to run before the npm ci step, you must submit a P1 Help Desk ticket . The MDO team will review the request and, if approved, configure a pipeline override for the affected job.
For customers using a preinstall script to run npx force-resolutions, please switch to utilizing the overrides field in your package.json. This tells npm to enforce the versions specified across all transitive dependencies. The overrides field is npm's built-in replacement available as of npm v8.3.0 (released with Node.js 16). All of Party Bus's current pipeline images use npm version 10.x or later.
If you need to run an additional script after package install, we recommend the following:
- Change the script you wish to run in the
scriptsfield of yourpackage.jsonfrompostinstallto a custom name that works for your team. - Run the script during the
build-imageby specifyingRUN npm run <script-name>in your Dockerfile.
Transitive Lifecycle Scripts
Transitive lifecycle scripts that run during dependency installation are discouraged due to the risk that they present. It is becoming standard practice to avoid these scripts and disable them by default, which modern package managers such as pnpm and yarn already do.
Despite this, your project will likely have at least one dependency that utilizes a lifecycle script. While we highly recommend that you explore alternative options to fulfill the dependency requirement, we understand that it may not be possible for some customers.
REMINDER
The new standard expected behavior is that no lifecycle scripts will run by default during the npm ci command due to the added --ignore-scripts flag.
Allowing Required Dependency Lifecycle Scripts
If you need specific transitive lifecycle scripts to run, use the following exception path:
Create the
.npm-allow-scriptsexceptions file- If you use npm and your project has dependencies that run install scripts, create a file named
.npm-allow-scriptsin the root of your project. - This file lists approved script exceptions. If no exceptions are needed, you can leave the file empty.
- If you use npm and your project has dependencies that run install scripts, create a file named
Add allowed dependencies (if needed)
- If a dependency requires its lifecycle scripts to run, add it to the
.npm-allow-scriptsfile. - Add one dependency per line and only include dependencies you trust and require for your build.
- If a dependency requires its lifecycle scripts to run, add it to the
Commit required files
Ensure both of the following files are committed to your repository:
.npm-allow-scriptspackage-lock.json
WARNING
If either file is missing, your pipeline will fail.
Pipeline validation (
npm-scripts-check)- During the pipeline, a job called
npm-scripts-checkwill:- Verify the
.npm-allow-scriptsfile exists, - Analyze your dependencies for lifecycle scripts, and
- Provide recommendations if scripts are detected.
- Verify the
- During the pipeline, a job called
Script execution behavior
- After
npm cicompletes:- Only dependencies listed in
.npm-allow-scriptswill have their lifecycle scripts executed. - This is done via a controlled npm rebuild step.
- Only dependencies listed in
- After