From 0d2985535c9cc3dfc3e1f355580570c9cce37d61 Mon Sep 17 00:00:00 2001 From: Gar Date: Thu, 24 Aug 2023 08:43:30 -0700 Subject: [PATCH] feat: add no-package-lock mode to npm audit --- docs/lib/content/commands/npm-audit.md | 7 +++++++ docs/lib/content/commands/npm-query.md | 7 +++++++ lib/commands/audit.js | 6 +++++- tap-snapshots/test/lib/docs.js.test.cjs | 3 ++- test/lib/commands/audit.js | 12 ++++++++++++ workspaces/arborist/lib/arborist/audit.js | 10 +++++++++- workspaces/arborist/test/arborist/audit.js | 9 +++++++++ 7 files changed, 51 insertions(+), 3 deletions(-) diff --git a/docs/lib/content/commands/npm-audit.md b/docs/lib/content/commands/npm-audit.md index 7a39b34d875be..085b7b2ea5613 100644 --- a/docs/lib/content/commands/npm-audit.md +++ b/docs/lib/content/commands/npm-audit.md @@ -30,6 +30,13 @@ vulnerability is found. It may be useful in CI environments to include the will cause the command to fail. This option does not filter the report output, it simply changes the command's failure threshold. +### Package lock + +By default npm requires a package-lock or shrinkwrap in order to run the +audit. You can bypass the package lock with `--no-package-lock` but be +aware the results may be different with every run, since npm will +re-build the dependency tree each time. + ### Audit Signatures To ensure the integrity of packages you download from the public npm registry, or any registry that supports signatures, you can verify the registry signatures of downloaded packages using the npm CLI. diff --git a/docs/lib/content/commands/npm-query.md b/docs/lib/content/commands/npm-query.md index e6bf53f3de614..42b2d086f3bbe 100644 --- a/docs/lib/content/commands/npm-query.md +++ b/docs/lib/content/commands/npm-query.md @@ -134,6 +134,13 @@ npm query ":type(git)" | jq 'map(.name)' | xargs -I {} npm why {} ... ``` +### Package lock only mode + +If package-lock-only is enabled, only the information in the package +lock (or shrinkwrap) is loaded. This means that information from the +package.json files of your dependencies will not be included in the +result set (e.g. description, homepage, engines). + ### Configuration diff --git a/lib/commands/audit.js b/lib/commands/audit.js index de5483109d598..2a40d541c20db 100644 --- a/lib/commands/audit.js +++ b/lib/commands/audit.js @@ -404,6 +404,7 @@ class Audit extends ArboristWorkspaceCmd { 'force', 'json', 'package-lock-only', + 'package-lock', 'omit', 'foreground-scripts', 'ignore-scripts', @@ -439,6 +440,10 @@ class Audit extends ArboristWorkspaceCmd { } async auditAdvisories (args) { + const fix = args[0] === 'fix' + if (this.npm.config.get('package-lock') === false && fix) { + throw this.usageError('fix can not be used without a package-lock') + } const reporter = this.npm.config.get('json') ? 'json' : 'detail' const Arborist = require('@npmcli/arborist') const opts = { @@ -450,7 +455,6 @@ class Audit extends ArboristWorkspaceCmd { } const arb = new Arborist(opts) - const fix = args[0] === 'fix' await arb.audit({ fix }) if (fix) { await reifyFinish(this.npm, arb) diff --git a/tap-snapshots/test/lib/docs.js.test.cjs b/tap-snapshots/test/lib/docs.js.test.cjs index b745044fd1bde..102204d2c1a3f 100644 --- a/tap-snapshots/test/lib/docs.js.test.cjs +++ b/tap-snapshots/test/lib/docs.js.test.cjs @@ -2552,7 +2552,7 @@ npm audit [fix|signatures] Options: [--audit-level ] [--dry-run] [-f|--force] -[--json] [--package-lock-only] +[--json] [--package-lock-only] [--no-package-lock] [--omit [--omit ...]] [--foreground-scripts] [--ignore-scripts] [-w|--workspace [-w|--workspace ...]] @@ -2569,6 +2569,7 @@ npm audit [fix|signatures] #### \`force\` #### \`json\` #### \`package-lock-only\` +#### \`package-lock\` #### \`omit\` #### \`foreground-scripts\` #### \`ignore-scripts\` diff --git a/test/lib/commands/audit.js b/test/lib/commands/audit.js index 4a776e89bd9e9..25ced6655e654 100644 --- a/test/lib/commands/audit.js +++ b/test/lib/commands/audit.js @@ -210,6 +210,18 @@ t.test('audit fix - bulk endpoint', async t => { ) }) +t.test('audit fix no package lock', async t => { + const { npm } = await loadMockNpm(t, { + config: { + 'package-lock': false, + }, + }) + await t.rejects( + npm.exec('audit', ['fix']), + { code: 'EUSAGE' } + ) +}) + t.test('completion', async t => { const { audit } = await loadMockNpm(t, { command: 'audit' }) t.test('fix', async t => { diff --git a/workspaces/arborist/lib/arborist/audit.js b/workspaces/arborist/lib/arborist/audit.js index eb4a3565531cc..af260bdc996fc 100644 --- a/workspaces/arborist/lib/arborist/audit.js +++ b/workspaces/arborist/lib/arborist/audit.js @@ -22,7 +22,15 @@ module.exports = cls => class Auditor extends cls { options = { ...this.options, ...options } process.emit('time', 'audit') - const tree = await this.loadVirtual() + let tree + if (options.packageLock === false) { + // build ideal tree + await this.loadActual(options) + await this.buildIdealTree() + tree = this.idealTree + } else { + tree = await this.loadVirtual() + } if (this[_workspaces] && this[_workspaces].length) { options.filterSet = this.workspaceDependencySet( tree, diff --git a/workspaces/arborist/test/arborist/audit.js b/workspaces/arborist/test/arborist/audit.js index ecfd9d70a0508..bf301eb62d11f 100644 --- a/workspaces/arborist/test/arborist/audit.js +++ b/workspaces/arborist/test/arborist/audit.js @@ -27,6 +27,15 @@ t.test('audit finds the bad deps', async t => { t.equal(report.size, 2) }) +t.test('no package lock finds no bad deps', async t => { + const path = resolve(fixtures, 'deprecated-dep') + t.teardown(auditResponse(resolve(fixtures, 'audit-nyc-mkdirp/audit.json'))) + const arb = newArb(path, { packageLock: false }) + const report = await arb.audit() + t.equal(report.topVulns.size, 0) + t.equal(report.size, 0) +}) + t.test('audit fix reifies out the bad deps', async t => { const path = fixture(t, 'deprecated-dep') t.teardown(auditResponse(resolve(fixtures, 'audit-nyc-mkdirp/audit.json')))