codekingpro/portable-devtools
114k
1const npmAuditReport = require('npm-audit-report')
2const ArboristWorkspaceCmd = require('../arborist-cmd.js')
3const auditError = require('../utils/audit-error.js')
4const { log, output } = require('proc-log')
5const reifyFinish = require('../utils/reify-finish.js')
6const VerifySignatures = require('../utils/verify-signatures.js')
7
8class Audit extends ArboristWorkspaceCmd {
9 static description = 'Run a security audit'
10 static name = 'audit'
11 static params = [
12 'audit-level',
13 'dry-run',
14 'force',
15 'json',
16 'package-lock-only',
17 'package-lock',
18 'omit',
19 'include',
20 'foreground-scripts',
21 'ignore-scripts',
22 'include-attestations',
23 ...super.params,
24 ]
25
26 static usage = ['[fix|signatures]']
27
28 static async completion (opts) {
29 const argv = opts.conf.argv.remain
30
31 if (argv.length === 2) {
32 return ['fix', 'signatures']
33 }
34
35 switch (argv[2]) {
36 case 'fix':
37 case 'signatures':
38 return []
39 default:
40 throw Object.assign(new Error(`${argv[2]} not recognized`), {
41 code: 'EUSAGE',
42 })
43 }
44 }
45
46 async exec (args) {
47 if (args[0] === 'signatures') {
48 await this.auditSignatures()
49 } else {
50 await this.auditAdvisories(args)
51 }
52 }
53
54 async auditAdvisories (args) {
55 const fix = args[0] === 'fix'
56 if (this.npm.config.get('package-lock') === false && fix) {
57 throw this.usageError('fix cannot be used without a package-lock')
58 }
59 const reporter = this.npm.config.get('json') ? 'json' : 'detail'
60 const Arborist = require('@npmcli/arborist')
61 const opts = {
62 ...this.npm.flatOptions,
63 audit: true,
64 path: this.npm.prefix,
65 reporter,
66 workspaces: this.workspaceNames,
67 }
68
69 const arb = new Arborist(opts)
70 await arb.audit({ fix })
71 if (fix) {
72 await reifyFinish(this.npm, arb)
73 } else {
74 // will throw if there's an error, because this is an audit command
75 auditError(this.npm, arb.auditReport)
76 const result = npmAuditReport(arb.auditReport, {
77 ...opts,
78 chalk: this.npm.chalk,
79 })
80 process.exitCode = process.exitCode || result.exitCode
81 output.standard(result.report)
82 }
83 }
84
85 async auditSignatures () {
86 if (this.npm.global) {
87 throw Object.assign(
88 new Error('`npm audit signatures` does not support global packages'), {
89 code: 'EAUDITGLOBAL',
90 }
91 )
92 }
93
94 log.verbose('audit', 'loading installed dependencies')
95 const Arborist = require('@npmcli/arborist')
96 const opts = {
97 ...this.npm.flatOptions,
98 path: this.npm.prefix,
99 workspaces: this.workspaceNames,
100 }
101
102 const arb = new Arborist(opts)
103 const tree = await arb.loadActual()
104 let filterSet = new Set()
105 if (opts.workspaces && opts.workspaces.length) {
106 filterSet =
107 arb.workspaceDependencySet(
108 tree,
109 opts.workspaces,
110 this.npm.flatOptions.includeWorkspaceRoot
111 )
112 } else if (!this.npm.flatOptions.workspacesEnabled) {
113 filterSet =
114 arb.excludeWorkspacesDependencySet(tree)
115 }
116
117 const verify = new VerifySignatures(tree, filterSet, this.npm, { ...opts })
118 await verify.run()
119 }
120}
121
122module.exports = Audit
123 