Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions tap-snapshots/test/lib/commands/config.js.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ exports[`test/lib/commands/config.js TAP config list --json > output matches sna
"init.license": "ISC",
"init.module": "{CWD}/home/.npm-init.js",
"init.version": "1.0.0",
"install-hooks-whitelist": null,
"install-links": false,
"install-strategy": "hoisted",
"key": null,
Expand Down Expand Up @@ -261,6 +262,7 @@ init.author.url = ""
init.license = "ISC"
init.module = "{CWD}/home/.npm-init.js"
init.version = "1.0.0"
install-hooks-whitelist = null
install-links = false
install-strategy = "hoisted"
json = false
Expand Down
14 changes: 14 additions & 0 deletions tap-snapshots/test/lib/docs.js.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,18 @@ number, if not already set in package.json.



#### \`install-hooks-whitelist\`

* Default: null
* Type: null or String

A comma-separated list of npm package names whose NPM install hooks are
allowed to run. Only hooks from packages listed here will be executed.
Package names should match exactly as published in the registry (including
scope if applicable, like @my-org/component).



#### \`install-links\`

* Default: false
Expand Down Expand Up @@ -2281,6 +2293,7 @@ Array [
"init.license",
"init.module",
"init.version",
"install-hooks-whitelist",
"install-links",
"install-strategy",
"json",
Expand Down Expand Up @@ -2440,6 +2453,7 @@ Array [
"include-staged",
"include-workspace-root",
"init-private",
"install-hooks-whitelist",
"install-links",
"install-strategy",
"json",
Expand Down
18 changes: 18 additions & 0 deletions workspaces/arborist/lib/arborist/rebuild.js
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,19 @@ module.exports = cls => class Builder extends cls {
}
}

// Determine whether install hooks may run.
// Install hooks are allowed only for packages listed in the
// install-hook-whitelist setting
#isWhitelistedInstallHook (node, event) {
if (this.options.installHooksWhitelist?.length && event !== 'prepare') {
// check whether the package name is included in the defined whitelist
return this.options.installHooksWhitelist.includes(node.name?.toLowerCase())
}
// either no whitelist exists, or the event is 'prepare'
// 'prepare' runs only on linked packages
return true
}

async #runScripts (event) {
const queue = this.#queues[event]

Expand Down Expand Up @@ -304,6 +317,11 @@ module.exports = cls => class Builder extends cls {
return
}

if (!this.#isWhitelistedInstallHook(node, event)) {
log.warn('hook execution', `skipping npm hook "${event}" for package "${node.name}". To enable hooks for this package, add "${node.name}" to your install-hooks-whitelist options`)
return
}

const timeEndLocation = time.start(`build:run:${event}:${location}`)
log.info('run', pkg._id, event, location, pkg.scripts[event])
const env = {
Expand Down
31 changes: 31 additions & 0 deletions workspaces/arborist/test/arborist/rebuild.js
Original file line number Diff line number Diff line change
Expand Up @@ -847,3 +847,34 @@ t.test('do not run lifecycle scripts of linked deps twice', async t => {
const arb = new Arborist({ path, ignoreScripts: true })
await arb.rebuild()
})

t.test('only run install hooks for whitelisted packages', async t => {
function makePackage (name, dependencies) {
return {
name,
version: '1.0.0',
scripts: {
preinstall: `echo "Pre-install ${name}"`,
},
dependencies,
}
}
const a = makePackage('@foo/a', {})
const b = makePackage('@foo/b', {})
const path = t.testdir({
'package.json': JSON.stringify(makePackage('my-project', {
'@foo/a': 'file:./node_modules/@foo/a',
'@foo/b': 'file:./node_modules/@foo/b',
})),
node_modules: {
'@foo': {
a: { 'package.json': JSON.stringify(a) },
b: { 'package.json': JSON.stringify(b) },
},
},
})
const arb = new Arborist({ path, installHooksWhitelist: ['@foo/b'] })
await arb.rebuild()
t.equal(arb.scriptsRun.size, 1)
t.equal(arb.scriptsRun.values().next().value.cmd, b.scripts.preinstall)
})
17 changes: 17 additions & 0 deletions workspaces/config/lib/definitions/definitions.js
Original file line number Diff line number Diff line change
Expand Up @@ -1061,6 +1061,23 @@ const definitions = {
Alias for \`--init-version\`
`,
}),
'install-hooks-whitelist': new Definition('install-hooks-whitelist', {
default: null,
type: [null, String],
description: `
A comma-separated list of npm package names whose NPM install hooks are allowed to run.
Only hooks from packages listed here will be executed. Package names should
match exactly as published in the registry (including scope if applicable, like @my-org/component).
`,
flatten (key, obj, flatOptions) {
if (obj[key]) {
flatOptions.installHooksWhitelist = obj[key]
.split(',')
.map(v => v.trim().toLowerCase())
.filter(Boolean)
}
},
}),
'install-links': new Definition('install-links', {
default: false,
type: Boolean,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,10 @@ Object {
"init.version": Array [
"full valid SemVer string",
],
"install-hooks-whitelist": Array [
null,
Function String(),
],
"install-links": Array [
"boolean value (true or false)",
],
Expand Down
8 changes: 8 additions & 0 deletions workspaces/config/test/definitions/definitions.js
Original file line number Diff line number Diff line change
Expand Up @@ -1013,6 +1013,14 @@ t.test('remap global-style', t => {
t.end()
})

t.test('make array from install-hooks-whitelist', t => {
const obj = { 'install-hooks-whitelist': 'foo, bar, @foo/bar' }
const flat = {}
mockDefs()['install-hooks-whitelist'].flatten('install-hooks-whitelist', obj, flat)
t.strictSame(flat, { installHooksWhitelist: ['foo', 'bar', '@foo/bar'] })
t.end()
})

t.test('otp changes auth-type', t => {
const obj = { 'auth-type': 'web', otp: 123456 }
const flat = {}
Expand Down