xref: /third_party/node/deps/npm/lib/commands/audit.js (revision 1cb0ef41)
11cb0ef41Sopenharmony_ciconst npmAuditReport = require('npm-audit-report')
21cb0ef41Sopenharmony_ciconst fetch = require('npm-registry-fetch')
31cb0ef41Sopenharmony_ciconst localeCompare = require('@isaacs/string-locale-compare')('en')
41cb0ef41Sopenharmony_ciconst npa = require('npm-package-arg')
51cb0ef41Sopenharmony_ciconst pacote = require('pacote')
61cb0ef41Sopenharmony_ciconst pMap = require('p-map')
71cb0ef41Sopenharmony_ciconst tufClient = require('@sigstore/tuf')
81cb0ef41Sopenharmony_ci
91cb0ef41Sopenharmony_ciconst ArboristWorkspaceCmd = require('../arborist-cmd.js')
101cb0ef41Sopenharmony_ciconst auditError = require('../utils/audit-error.js')
111cb0ef41Sopenharmony_ciconst log = require('../utils/log-shim.js')
121cb0ef41Sopenharmony_ciconst reifyFinish = require('../utils/reify-finish.js')
131cb0ef41Sopenharmony_ci
141cb0ef41Sopenharmony_ciconst sortAlphabetically = (a, b) => localeCompare(a.name, b.name)
151cb0ef41Sopenharmony_ci
161cb0ef41Sopenharmony_ciclass VerifySignatures {
171cb0ef41Sopenharmony_ci  constructor (tree, filterSet, npm, opts) {
181cb0ef41Sopenharmony_ci    this.tree = tree
191cb0ef41Sopenharmony_ci    this.filterSet = filterSet
201cb0ef41Sopenharmony_ci    this.npm = npm
211cb0ef41Sopenharmony_ci    this.opts = opts
221cb0ef41Sopenharmony_ci    this.keys = new Map()
231cb0ef41Sopenharmony_ci    this.invalid = []
241cb0ef41Sopenharmony_ci    this.missing = []
251cb0ef41Sopenharmony_ci    this.checkedPackages = new Set()
261cb0ef41Sopenharmony_ci    this.auditedWithKeysCount = 0
271cb0ef41Sopenharmony_ci    this.verifiedSignatureCount = 0
281cb0ef41Sopenharmony_ci    this.verifiedAttestationCount = 0
291cb0ef41Sopenharmony_ci    this.exitCode = 0
301cb0ef41Sopenharmony_ci  }
311cb0ef41Sopenharmony_ci
321cb0ef41Sopenharmony_ci  async run () {
331cb0ef41Sopenharmony_ci    const start = process.hrtime.bigint()
341cb0ef41Sopenharmony_ci
351cb0ef41Sopenharmony_ci    // Find all deps in tree
361cb0ef41Sopenharmony_ci    const { edges, registries } = this.getEdgesOut(this.tree.inventory.values(), this.filterSet)
371cb0ef41Sopenharmony_ci    if (edges.size === 0) {
381cb0ef41Sopenharmony_ci      throw new Error('found no installed dependencies to audit')
391cb0ef41Sopenharmony_ci    }
401cb0ef41Sopenharmony_ci
411cb0ef41Sopenharmony_ci    const tuf = await tufClient.initTUF({
421cb0ef41Sopenharmony_ci      cachePath: this.opts.tufCache,
431cb0ef41Sopenharmony_ci      retry: this.opts.retry,
441cb0ef41Sopenharmony_ci      timeout: this.opts.timeout,
451cb0ef41Sopenharmony_ci    })
461cb0ef41Sopenharmony_ci    await Promise.all([...registries].map(registry => this.setKeys({ registry, tuf })))
471cb0ef41Sopenharmony_ci
481cb0ef41Sopenharmony_ci    const progress = log.newItem('verifying registry signatures', edges.size)
491cb0ef41Sopenharmony_ci    const mapper = async (edge) => {
501cb0ef41Sopenharmony_ci      progress.completeWork(1)
511cb0ef41Sopenharmony_ci      await this.getVerifiedInfo(edge)
521cb0ef41Sopenharmony_ci    }
531cb0ef41Sopenharmony_ci    await pMap(edges, mapper, { concurrency: 20, stopOnError: true })
541cb0ef41Sopenharmony_ci
551cb0ef41Sopenharmony_ci    // Didn't find any dependencies that could be verified, e.g. only local
561cb0ef41Sopenharmony_ci    // deps, missing version, not on a registry etc.
571cb0ef41Sopenharmony_ci    if (!this.auditedWithKeysCount) {
581cb0ef41Sopenharmony_ci      throw new Error('found no dependencies to audit that were installed from ' +
591cb0ef41Sopenharmony_ci                      'a supported registry')
601cb0ef41Sopenharmony_ci    }
611cb0ef41Sopenharmony_ci
621cb0ef41Sopenharmony_ci    const invalid = this.invalid.sort(sortAlphabetically)
631cb0ef41Sopenharmony_ci    const missing = this.missing.sort(sortAlphabetically)
641cb0ef41Sopenharmony_ci
651cb0ef41Sopenharmony_ci    const hasNoInvalidOrMissing = invalid.length === 0 && missing.length === 0
661cb0ef41Sopenharmony_ci
671cb0ef41Sopenharmony_ci    if (!hasNoInvalidOrMissing) {
681cb0ef41Sopenharmony_ci      process.exitCode = 1
691cb0ef41Sopenharmony_ci    }
701cb0ef41Sopenharmony_ci
711cb0ef41Sopenharmony_ci    if (this.npm.config.get('json')) {
721cb0ef41Sopenharmony_ci      this.npm.output(JSON.stringify({
731cb0ef41Sopenharmony_ci        invalid,
741cb0ef41Sopenharmony_ci        missing,
751cb0ef41Sopenharmony_ci      }, null, 2))
761cb0ef41Sopenharmony_ci      return
771cb0ef41Sopenharmony_ci    }
781cb0ef41Sopenharmony_ci    const end = process.hrtime.bigint()
791cb0ef41Sopenharmony_ci    const elapsed = end - start
801cb0ef41Sopenharmony_ci
811cb0ef41Sopenharmony_ci    const auditedPlural = this.auditedWithKeysCount > 1 ? 's' : ''
821cb0ef41Sopenharmony_ci    const timing = `audited ${this.auditedWithKeysCount} package${auditedPlural} in ` +
831cb0ef41Sopenharmony_ci      `${Math.floor(Number(elapsed) / 1e9)}s`
841cb0ef41Sopenharmony_ci    this.npm.output(timing)
851cb0ef41Sopenharmony_ci    this.npm.output('')
861cb0ef41Sopenharmony_ci
871cb0ef41Sopenharmony_ci    const verifiedBold = this.npm.chalk.bold('verified')
881cb0ef41Sopenharmony_ci    if (this.verifiedSignatureCount) {
891cb0ef41Sopenharmony_ci      if (this.verifiedSignatureCount === 1) {
901cb0ef41Sopenharmony_ci        /* eslint-disable-next-line max-len */
911cb0ef41Sopenharmony_ci        this.npm.output(`${this.verifiedSignatureCount} package has a ${verifiedBold} registry signature`)
921cb0ef41Sopenharmony_ci      } else {
931cb0ef41Sopenharmony_ci        /* eslint-disable-next-line max-len */
941cb0ef41Sopenharmony_ci        this.npm.output(`${this.verifiedSignatureCount} packages have ${verifiedBold} registry signatures`)
951cb0ef41Sopenharmony_ci      }
961cb0ef41Sopenharmony_ci      this.npm.output('')
971cb0ef41Sopenharmony_ci    }
981cb0ef41Sopenharmony_ci
991cb0ef41Sopenharmony_ci    if (this.verifiedAttestationCount) {
1001cb0ef41Sopenharmony_ci      if (this.verifiedAttestationCount === 1) {
1011cb0ef41Sopenharmony_ci        /* eslint-disable-next-line max-len */
1021cb0ef41Sopenharmony_ci        this.npm.output(`${this.verifiedAttestationCount} package has a ${verifiedBold} attestation`)
1031cb0ef41Sopenharmony_ci      } else {
1041cb0ef41Sopenharmony_ci        /* eslint-disable-next-line max-len */
1051cb0ef41Sopenharmony_ci        this.npm.output(`${this.verifiedAttestationCount} packages have ${verifiedBold} attestations`)
1061cb0ef41Sopenharmony_ci      }
1071cb0ef41Sopenharmony_ci      this.npm.output('')
1081cb0ef41Sopenharmony_ci    }
1091cb0ef41Sopenharmony_ci
1101cb0ef41Sopenharmony_ci    if (missing.length) {
1111cb0ef41Sopenharmony_ci      const missingClr = this.npm.chalk.bold(this.npm.chalk.red('missing'))
1121cb0ef41Sopenharmony_ci      if (missing.length === 1) {
1131cb0ef41Sopenharmony_ci        /* eslint-disable-next-line max-len */
1141cb0ef41Sopenharmony_ci        this.npm.output(`1 package has a ${missingClr} registry signature but the registry is providing signing keys:`)
1151cb0ef41Sopenharmony_ci      } else {
1161cb0ef41Sopenharmony_ci        /* eslint-disable-next-line max-len */
1171cb0ef41Sopenharmony_ci        this.npm.output(`${missing.length} packages have ${missingClr} registry signatures but the registry is providing signing keys:`)
1181cb0ef41Sopenharmony_ci      }
1191cb0ef41Sopenharmony_ci      this.npm.output('')
1201cb0ef41Sopenharmony_ci      missing.map(m =>
1211cb0ef41Sopenharmony_ci        this.npm.output(`${this.npm.chalk.red(`${m.name}@${m.version}`)} (${m.registry})`)
1221cb0ef41Sopenharmony_ci      )
1231cb0ef41Sopenharmony_ci    }
1241cb0ef41Sopenharmony_ci
1251cb0ef41Sopenharmony_ci    if (invalid.length) {
1261cb0ef41Sopenharmony_ci      if (missing.length) {
1271cb0ef41Sopenharmony_ci        this.npm.output('')
1281cb0ef41Sopenharmony_ci      }
1291cb0ef41Sopenharmony_ci      const invalidClr = this.npm.chalk.bold(this.npm.chalk.red('invalid'))
1301cb0ef41Sopenharmony_ci      // We can have either invalid signatures or invalid provenance
1311cb0ef41Sopenharmony_ci      const invalidSignatures = this.invalid.filter(i => i.code === 'EINTEGRITYSIGNATURE')
1321cb0ef41Sopenharmony_ci      if (invalidSignatures.length) {
1331cb0ef41Sopenharmony_ci        if (invalidSignatures.length === 1) {
1341cb0ef41Sopenharmony_ci          this.npm.output(`1 package has an ${invalidClr} registry signature:`)
1351cb0ef41Sopenharmony_ci        } else {
1361cb0ef41Sopenharmony_ci          /* eslint-disable-next-line max-len */
1371cb0ef41Sopenharmony_ci          this.npm.output(`${invalidSignatures.length} packages have ${invalidClr} registry signatures:`)
1381cb0ef41Sopenharmony_ci        }
1391cb0ef41Sopenharmony_ci        this.npm.output('')
1401cb0ef41Sopenharmony_ci        invalidSignatures.map(i =>
1411cb0ef41Sopenharmony_ci          this.npm.output(`${this.npm.chalk.red(`${i.name}@${i.version}`)} (${i.registry})`)
1421cb0ef41Sopenharmony_ci        )
1431cb0ef41Sopenharmony_ci        this.npm.output('')
1441cb0ef41Sopenharmony_ci      }
1451cb0ef41Sopenharmony_ci
1461cb0ef41Sopenharmony_ci      const invalidAttestations = this.invalid.filter(i => i.code === 'EATTESTATIONVERIFY')
1471cb0ef41Sopenharmony_ci      if (invalidAttestations.length) {
1481cb0ef41Sopenharmony_ci        if (invalidAttestations.length === 1) {
1491cb0ef41Sopenharmony_ci          this.npm.output(`1 package has an ${invalidClr} attestation:`)
1501cb0ef41Sopenharmony_ci        } else {
1511cb0ef41Sopenharmony_ci          /* eslint-disable-next-line max-len */
1521cb0ef41Sopenharmony_ci          this.npm.output(`${invalidAttestations.length} packages have ${invalidClr} attestations:`)
1531cb0ef41Sopenharmony_ci        }
1541cb0ef41Sopenharmony_ci        this.npm.output('')
1551cb0ef41Sopenharmony_ci        invalidAttestations.map(i =>
1561cb0ef41Sopenharmony_ci          this.npm.output(`${this.npm.chalk.red(`${i.name}@${i.version}`)} (${i.registry})`)
1571cb0ef41Sopenharmony_ci        )
1581cb0ef41Sopenharmony_ci        this.npm.output('')
1591cb0ef41Sopenharmony_ci      }
1601cb0ef41Sopenharmony_ci
1611cb0ef41Sopenharmony_ci      if (invalid.length === 1) {
1621cb0ef41Sopenharmony_ci        /* eslint-disable-next-line max-len */
1631cb0ef41Sopenharmony_ci        this.npm.output(`Someone might have tampered with this package since it was published on the registry!`)
1641cb0ef41Sopenharmony_ci      } else {
1651cb0ef41Sopenharmony_ci        /* eslint-disable-next-line max-len */
1661cb0ef41Sopenharmony_ci        this.npm.output(`Someone might have tampered with these packages since they were published on the registry!`)
1671cb0ef41Sopenharmony_ci      }
1681cb0ef41Sopenharmony_ci      this.npm.output('')
1691cb0ef41Sopenharmony_ci    }
1701cb0ef41Sopenharmony_ci  }
1711cb0ef41Sopenharmony_ci
1721cb0ef41Sopenharmony_ci  getEdgesOut (nodes, filterSet) {
1731cb0ef41Sopenharmony_ci    const edges = new Set()
1741cb0ef41Sopenharmony_ci    const registries = new Set()
1751cb0ef41Sopenharmony_ci    for (const node of nodes) {
1761cb0ef41Sopenharmony_ci      for (const edge of node.edgesOut.values()) {
1771cb0ef41Sopenharmony_ci        const filteredOut =
1781cb0ef41Sopenharmony_ci          edge.from
1791cb0ef41Sopenharmony_ci            && filterSet
1801cb0ef41Sopenharmony_ci            && filterSet.size > 0
1811cb0ef41Sopenharmony_ci            && !filterSet.has(edge.from.target)
1821cb0ef41Sopenharmony_ci
1831cb0ef41Sopenharmony_ci        if (!filteredOut) {
1841cb0ef41Sopenharmony_ci          const spec = this.getEdgeSpec(edge)
1851cb0ef41Sopenharmony_ci          if (spec) {
1861cb0ef41Sopenharmony_ci            // Prefetch and cache public keys from used registries
1871cb0ef41Sopenharmony_ci            registries.add(this.getSpecRegistry(spec))
1881cb0ef41Sopenharmony_ci          }
1891cb0ef41Sopenharmony_ci          edges.add(edge)
1901cb0ef41Sopenharmony_ci        }
1911cb0ef41Sopenharmony_ci      }
1921cb0ef41Sopenharmony_ci    }
1931cb0ef41Sopenharmony_ci    return { edges, registries }
1941cb0ef41Sopenharmony_ci  }
1951cb0ef41Sopenharmony_ci
1961cb0ef41Sopenharmony_ci  async setKeys ({ registry, tuf }) {
1971cb0ef41Sopenharmony_ci    const { host, pathname } = new URL(registry)
1981cb0ef41Sopenharmony_ci    // Strip any trailing slashes from pathname
1991cb0ef41Sopenharmony_ci    const regKey = `${host}${pathname.replace(/\/$/, '')}/keys.json`
2001cb0ef41Sopenharmony_ci    let keys = await tuf.getTarget(regKey)
2011cb0ef41Sopenharmony_ci      .then((target) => JSON.parse(target))
2021cb0ef41Sopenharmony_ci      .then(({ keys: ks }) => ks.map((key) => ({
2031cb0ef41Sopenharmony_ci        ...key,
2041cb0ef41Sopenharmony_ci        keyid: key.keyId,
2051cb0ef41Sopenharmony_ci        pemkey: `-----BEGIN PUBLIC KEY-----\n${key.publicKey.rawBytes}\n-----END PUBLIC KEY-----`,
2061cb0ef41Sopenharmony_ci        expires: key.publicKey.validFor.end || null,
2071cb0ef41Sopenharmony_ci      }))).catch(err => {
2081cb0ef41Sopenharmony_ci        if (err.code === 'TUF_FIND_TARGET_ERROR') {
2091cb0ef41Sopenharmony_ci          return null
2101cb0ef41Sopenharmony_ci        } else {
2111cb0ef41Sopenharmony_ci          throw err
2121cb0ef41Sopenharmony_ci        }
2131cb0ef41Sopenharmony_ci      })
2141cb0ef41Sopenharmony_ci
2151cb0ef41Sopenharmony_ci    // If keys not found in Sigstore TUF repo, fallback to registry keys API
2161cb0ef41Sopenharmony_ci    if (!keys) {
2171cb0ef41Sopenharmony_ci      keys = await fetch.json('/-/npm/v1/keys', {
2181cb0ef41Sopenharmony_ci        ...this.npm.flatOptions,
2191cb0ef41Sopenharmony_ci        registry,
2201cb0ef41Sopenharmony_ci      }).then(({ keys: ks }) => ks.map((key) => ({
2211cb0ef41Sopenharmony_ci        ...key,
2221cb0ef41Sopenharmony_ci        pemkey: `-----BEGIN PUBLIC KEY-----\n${key.key}\n-----END PUBLIC KEY-----`,
2231cb0ef41Sopenharmony_ci      }))).catch(err => {
2241cb0ef41Sopenharmony_ci        if (err.code === 'E404' || err.code === 'E400') {
2251cb0ef41Sopenharmony_ci          return null
2261cb0ef41Sopenharmony_ci        } else {
2271cb0ef41Sopenharmony_ci          throw err
2281cb0ef41Sopenharmony_ci        }
2291cb0ef41Sopenharmony_ci      })
2301cb0ef41Sopenharmony_ci    }
2311cb0ef41Sopenharmony_ci
2321cb0ef41Sopenharmony_ci    if (keys) {
2331cb0ef41Sopenharmony_ci      this.keys.set(registry, keys)
2341cb0ef41Sopenharmony_ci    }
2351cb0ef41Sopenharmony_ci  }
2361cb0ef41Sopenharmony_ci
2371cb0ef41Sopenharmony_ci  getEdgeType (edge) {
2381cb0ef41Sopenharmony_ci    return edge.optional ? 'optionalDependencies'
2391cb0ef41Sopenharmony_ci      : edge.peer ? 'peerDependencies'
2401cb0ef41Sopenharmony_ci      : edge.dev ? 'devDependencies'
2411cb0ef41Sopenharmony_ci      : 'dependencies'
2421cb0ef41Sopenharmony_ci  }
2431cb0ef41Sopenharmony_ci
2441cb0ef41Sopenharmony_ci  getEdgeSpec (edge) {
2451cb0ef41Sopenharmony_ci    let name = edge.name
2461cb0ef41Sopenharmony_ci    try {
2471cb0ef41Sopenharmony_ci      name = npa(edge.spec).subSpec.name
2481cb0ef41Sopenharmony_ci    } catch {
2491cb0ef41Sopenharmony_ci      // leave it as edge.name
2501cb0ef41Sopenharmony_ci    }
2511cb0ef41Sopenharmony_ci    try {
2521cb0ef41Sopenharmony_ci      return npa(`${name}@${edge.spec}`)
2531cb0ef41Sopenharmony_ci    } catch {
2541cb0ef41Sopenharmony_ci      // Skip packages with invalid spec
2551cb0ef41Sopenharmony_ci    }
2561cb0ef41Sopenharmony_ci  }
2571cb0ef41Sopenharmony_ci
2581cb0ef41Sopenharmony_ci  buildRegistryConfig (registry) {
2591cb0ef41Sopenharmony_ci    const keys = this.keys.get(registry) || []
2601cb0ef41Sopenharmony_ci    const parsedRegistry = new URL(registry)
2611cb0ef41Sopenharmony_ci    const regKey = `//${parsedRegistry.host}${parsedRegistry.pathname}`
2621cb0ef41Sopenharmony_ci    return {
2631cb0ef41Sopenharmony_ci      [`${regKey}:_keys`]: keys,
2641cb0ef41Sopenharmony_ci    }
2651cb0ef41Sopenharmony_ci  }
2661cb0ef41Sopenharmony_ci
2671cb0ef41Sopenharmony_ci  getSpecRegistry (spec) {
2681cb0ef41Sopenharmony_ci    return fetch.pickRegistry(spec, this.npm.flatOptions)
2691cb0ef41Sopenharmony_ci  }
2701cb0ef41Sopenharmony_ci
2711cb0ef41Sopenharmony_ci  getValidPackageInfo (edge) {
2721cb0ef41Sopenharmony_ci    const type = this.getEdgeType(edge)
2731cb0ef41Sopenharmony_ci    // Skip potentially optional packages that are not on disk, as these could
2741cb0ef41Sopenharmony_ci    // be omitted during install
2751cb0ef41Sopenharmony_ci    if (edge.error === 'MISSING' && type !== 'dependencies') {
2761cb0ef41Sopenharmony_ci      return
2771cb0ef41Sopenharmony_ci    }
2781cb0ef41Sopenharmony_ci
2791cb0ef41Sopenharmony_ci    const spec = this.getEdgeSpec(edge)
2801cb0ef41Sopenharmony_ci    // Skip invalid version requirements
2811cb0ef41Sopenharmony_ci    if (!spec) {
2821cb0ef41Sopenharmony_ci      return
2831cb0ef41Sopenharmony_ci    }
2841cb0ef41Sopenharmony_ci    const node = edge.to || edge
2851cb0ef41Sopenharmony_ci    const { version } = node.package || {}
2861cb0ef41Sopenharmony_ci
2871cb0ef41Sopenharmony_ci    if (node.isWorkspace || // Skip local workspaces packages
2881cb0ef41Sopenharmony_ci        !version || // Skip packages that don't have a installed version, e.g. optonal dependencies
2891cb0ef41Sopenharmony_ci        !spec.registry) { // Skip if not from registry, e.g. git package
2901cb0ef41Sopenharmony_ci      return
2911cb0ef41Sopenharmony_ci    }
2921cb0ef41Sopenharmony_ci
2931cb0ef41Sopenharmony_ci    for (const omitType of this.npm.config.get('omit')) {
2941cb0ef41Sopenharmony_ci      if (node[omitType]) {
2951cb0ef41Sopenharmony_ci        return
2961cb0ef41Sopenharmony_ci      }
2971cb0ef41Sopenharmony_ci    }
2981cb0ef41Sopenharmony_ci
2991cb0ef41Sopenharmony_ci    return {
3001cb0ef41Sopenharmony_ci      name: spec.name,
3011cb0ef41Sopenharmony_ci      version,
3021cb0ef41Sopenharmony_ci      type,
3031cb0ef41Sopenharmony_ci      location: node.location,
3041cb0ef41Sopenharmony_ci      registry: this.getSpecRegistry(spec),
3051cb0ef41Sopenharmony_ci    }
3061cb0ef41Sopenharmony_ci  }
3071cb0ef41Sopenharmony_ci
3081cb0ef41Sopenharmony_ci  async verifySignatures (name, version, registry) {
3091cb0ef41Sopenharmony_ci    const {
3101cb0ef41Sopenharmony_ci      _integrity: integrity,
3111cb0ef41Sopenharmony_ci      _signatures,
3121cb0ef41Sopenharmony_ci      _attestations,
3131cb0ef41Sopenharmony_ci      _resolved: resolved,
3141cb0ef41Sopenharmony_ci    } = await pacote.manifest(`${name}@${version}`, {
3151cb0ef41Sopenharmony_ci      verifySignatures: true,
3161cb0ef41Sopenharmony_ci      verifyAttestations: true,
3171cb0ef41Sopenharmony_ci      ...this.buildRegistryConfig(registry),
3181cb0ef41Sopenharmony_ci      ...this.npm.flatOptions,
3191cb0ef41Sopenharmony_ci    })
3201cb0ef41Sopenharmony_ci    const signatures = _signatures || []
3211cb0ef41Sopenharmony_ci    const result = {
3221cb0ef41Sopenharmony_ci      integrity,
3231cb0ef41Sopenharmony_ci      signatures,
3241cb0ef41Sopenharmony_ci      attestations: _attestations,
3251cb0ef41Sopenharmony_ci      resolved,
3261cb0ef41Sopenharmony_ci    }
3271cb0ef41Sopenharmony_ci    return result
3281cb0ef41Sopenharmony_ci  }
3291cb0ef41Sopenharmony_ci
3301cb0ef41Sopenharmony_ci  async getVerifiedInfo (edge) {
3311cb0ef41Sopenharmony_ci    const info = this.getValidPackageInfo(edge)
3321cb0ef41Sopenharmony_ci    if (!info) {
3331cb0ef41Sopenharmony_ci      return
3341cb0ef41Sopenharmony_ci    }
3351cb0ef41Sopenharmony_ci    const { name, version, location, registry, type } = info
3361cb0ef41Sopenharmony_ci    if (this.checkedPackages.has(location)) {
3371cb0ef41Sopenharmony_ci      // we already did or are doing this one
3381cb0ef41Sopenharmony_ci      return
3391cb0ef41Sopenharmony_ci    }
3401cb0ef41Sopenharmony_ci    this.checkedPackages.add(location)
3411cb0ef41Sopenharmony_ci
3421cb0ef41Sopenharmony_ci    // We only "audit" or verify the signature, or the presence of it, on
3431cb0ef41Sopenharmony_ci    // packages whose registry returns signing keys
3441cb0ef41Sopenharmony_ci    const keys = this.keys.get(registry) || []
3451cb0ef41Sopenharmony_ci    if (keys.length) {
3461cb0ef41Sopenharmony_ci      this.auditedWithKeysCount += 1
3471cb0ef41Sopenharmony_ci    }
3481cb0ef41Sopenharmony_ci
3491cb0ef41Sopenharmony_ci    try {
3501cb0ef41Sopenharmony_ci      const { integrity, signatures, attestations, resolved } = await this.verifySignatures(
3511cb0ef41Sopenharmony_ci        name, version, registry
3521cb0ef41Sopenharmony_ci      )
3531cb0ef41Sopenharmony_ci
3541cb0ef41Sopenharmony_ci      // Currently we only care about missing signatures on registries that provide a public key
3551cb0ef41Sopenharmony_ci      // We could make this configurable in the future with a strict/paranoid mode
3561cb0ef41Sopenharmony_ci      if (signatures.length) {
3571cb0ef41Sopenharmony_ci        this.verifiedSignatureCount += 1
3581cb0ef41Sopenharmony_ci      } else if (keys.length) {
3591cb0ef41Sopenharmony_ci        this.missing.push({
3601cb0ef41Sopenharmony_ci          integrity,
3611cb0ef41Sopenharmony_ci          location,
3621cb0ef41Sopenharmony_ci          name,
3631cb0ef41Sopenharmony_ci          registry,
3641cb0ef41Sopenharmony_ci          resolved,
3651cb0ef41Sopenharmony_ci          version,
3661cb0ef41Sopenharmony_ci        })
3671cb0ef41Sopenharmony_ci      }
3681cb0ef41Sopenharmony_ci
3691cb0ef41Sopenharmony_ci      // Track verified attestations separately to registry signatures, as all
3701cb0ef41Sopenharmony_ci      // packages on registries with signing keys are expected to have registry
3711cb0ef41Sopenharmony_ci      // signatures, but not all packages have provenance and publish attestations.
3721cb0ef41Sopenharmony_ci      if (attestations) {
3731cb0ef41Sopenharmony_ci        this.verifiedAttestationCount += 1
3741cb0ef41Sopenharmony_ci      }
3751cb0ef41Sopenharmony_ci    } catch (e) {
3761cb0ef41Sopenharmony_ci      if (e.code === 'EINTEGRITYSIGNATURE' || e.code === 'EATTESTATIONVERIFY') {
3771cb0ef41Sopenharmony_ci        this.invalid.push({
3781cb0ef41Sopenharmony_ci          code: e.code,
3791cb0ef41Sopenharmony_ci          message: e.message,
3801cb0ef41Sopenharmony_ci          integrity: e.integrity,
3811cb0ef41Sopenharmony_ci          keyid: e.keyid,
3821cb0ef41Sopenharmony_ci          location,
3831cb0ef41Sopenharmony_ci          name,
3841cb0ef41Sopenharmony_ci          registry,
3851cb0ef41Sopenharmony_ci          resolved: e.resolved,
3861cb0ef41Sopenharmony_ci          signature: e.signature,
3871cb0ef41Sopenharmony_ci          predicateType: e.predicateType,
3881cb0ef41Sopenharmony_ci          type,
3891cb0ef41Sopenharmony_ci          version,
3901cb0ef41Sopenharmony_ci        })
3911cb0ef41Sopenharmony_ci      } else {
3921cb0ef41Sopenharmony_ci        throw e
3931cb0ef41Sopenharmony_ci      }
3941cb0ef41Sopenharmony_ci    }
3951cb0ef41Sopenharmony_ci  }
3961cb0ef41Sopenharmony_ci}
3971cb0ef41Sopenharmony_ci
3981cb0ef41Sopenharmony_ciclass Audit extends ArboristWorkspaceCmd {
3991cb0ef41Sopenharmony_ci  static description = 'Run a security audit'
4001cb0ef41Sopenharmony_ci  static name = 'audit'
4011cb0ef41Sopenharmony_ci  static params = [
4021cb0ef41Sopenharmony_ci    'audit-level',
4031cb0ef41Sopenharmony_ci    'dry-run',
4041cb0ef41Sopenharmony_ci    'force',
4051cb0ef41Sopenharmony_ci    'json',
4061cb0ef41Sopenharmony_ci    'package-lock-only',
4071cb0ef41Sopenharmony_ci    'package-lock',
4081cb0ef41Sopenharmony_ci    'omit',
4091cb0ef41Sopenharmony_ci    'include',
4101cb0ef41Sopenharmony_ci    'foreground-scripts',
4111cb0ef41Sopenharmony_ci    'ignore-scripts',
4121cb0ef41Sopenharmony_ci    ...super.params,
4131cb0ef41Sopenharmony_ci  ]
4141cb0ef41Sopenharmony_ci
4151cb0ef41Sopenharmony_ci  static usage = ['[fix|signatures]']
4161cb0ef41Sopenharmony_ci
4171cb0ef41Sopenharmony_ci  static async completion (opts) {
4181cb0ef41Sopenharmony_ci    const argv = opts.conf.argv.remain
4191cb0ef41Sopenharmony_ci
4201cb0ef41Sopenharmony_ci    if (argv.length === 2) {
4211cb0ef41Sopenharmony_ci      return ['fix', 'signatures']
4221cb0ef41Sopenharmony_ci    }
4231cb0ef41Sopenharmony_ci
4241cb0ef41Sopenharmony_ci    switch (argv[2]) {
4251cb0ef41Sopenharmony_ci      case 'fix':
4261cb0ef41Sopenharmony_ci      case 'signatures':
4271cb0ef41Sopenharmony_ci        return []
4281cb0ef41Sopenharmony_ci      default:
4291cb0ef41Sopenharmony_ci        throw Object.assign(new Error(argv[2] + ' not recognized'), {
4301cb0ef41Sopenharmony_ci          code: 'EUSAGE',
4311cb0ef41Sopenharmony_ci        })
4321cb0ef41Sopenharmony_ci    }
4331cb0ef41Sopenharmony_ci  }
4341cb0ef41Sopenharmony_ci
4351cb0ef41Sopenharmony_ci  async exec (args) {
4361cb0ef41Sopenharmony_ci    if (args[0] === 'signatures') {
4371cb0ef41Sopenharmony_ci      await this.auditSignatures()
4381cb0ef41Sopenharmony_ci    } else {
4391cb0ef41Sopenharmony_ci      await this.auditAdvisories(args)
4401cb0ef41Sopenharmony_ci    }
4411cb0ef41Sopenharmony_ci  }
4421cb0ef41Sopenharmony_ci
4431cb0ef41Sopenharmony_ci  async auditAdvisories (args) {
4441cb0ef41Sopenharmony_ci    const fix = args[0] === 'fix'
4451cb0ef41Sopenharmony_ci    if (this.npm.config.get('package-lock') === false && fix) {
4461cb0ef41Sopenharmony_ci      throw this.usageError('fix can not be used without a package-lock')
4471cb0ef41Sopenharmony_ci    }
4481cb0ef41Sopenharmony_ci    const reporter = this.npm.config.get('json') ? 'json' : 'detail'
4491cb0ef41Sopenharmony_ci    const Arborist = require('@npmcli/arborist')
4501cb0ef41Sopenharmony_ci    const opts = {
4511cb0ef41Sopenharmony_ci      ...this.npm.flatOptions,
4521cb0ef41Sopenharmony_ci      audit: true,
4531cb0ef41Sopenharmony_ci      path: this.npm.prefix,
4541cb0ef41Sopenharmony_ci      reporter,
4551cb0ef41Sopenharmony_ci      workspaces: this.workspaceNames,
4561cb0ef41Sopenharmony_ci    }
4571cb0ef41Sopenharmony_ci
4581cb0ef41Sopenharmony_ci    const arb = new Arborist(opts)
4591cb0ef41Sopenharmony_ci    await arb.audit({ fix })
4601cb0ef41Sopenharmony_ci    if (fix) {
4611cb0ef41Sopenharmony_ci      await reifyFinish(this.npm, arb)
4621cb0ef41Sopenharmony_ci    } else {
4631cb0ef41Sopenharmony_ci      // will throw if there's an error, because this is an audit command
4641cb0ef41Sopenharmony_ci      auditError(this.npm, arb.auditReport)
4651cb0ef41Sopenharmony_ci      const result = npmAuditReport(arb.auditReport, {
4661cb0ef41Sopenharmony_ci        ...opts,
4671cb0ef41Sopenharmony_ci        chalk: this.npm.chalk,
4681cb0ef41Sopenharmony_ci      })
4691cb0ef41Sopenharmony_ci      process.exitCode = process.exitCode || result.exitCode
4701cb0ef41Sopenharmony_ci      this.npm.output(result.report)
4711cb0ef41Sopenharmony_ci    }
4721cb0ef41Sopenharmony_ci  }
4731cb0ef41Sopenharmony_ci
4741cb0ef41Sopenharmony_ci  async auditSignatures () {
4751cb0ef41Sopenharmony_ci    if (this.npm.global) {
4761cb0ef41Sopenharmony_ci      throw Object.assign(
4771cb0ef41Sopenharmony_ci        new Error('`npm audit signatures` does not support global packages'), {
4781cb0ef41Sopenharmony_ci          code: 'EAUDITGLOBAL',
4791cb0ef41Sopenharmony_ci        }
4801cb0ef41Sopenharmony_ci      )
4811cb0ef41Sopenharmony_ci    }
4821cb0ef41Sopenharmony_ci
4831cb0ef41Sopenharmony_ci    log.verbose('loading installed dependencies')
4841cb0ef41Sopenharmony_ci    const Arborist = require('@npmcli/arborist')
4851cb0ef41Sopenharmony_ci    const opts = {
4861cb0ef41Sopenharmony_ci      ...this.npm.flatOptions,
4871cb0ef41Sopenharmony_ci      path: this.npm.prefix,
4881cb0ef41Sopenharmony_ci      workspaces: this.workspaceNames,
4891cb0ef41Sopenharmony_ci    }
4901cb0ef41Sopenharmony_ci
4911cb0ef41Sopenharmony_ci    const arb = new Arborist(opts)
4921cb0ef41Sopenharmony_ci    const tree = await arb.loadActual()
4931cb0ef41Sopenharmony_ci    let filterSet = new Set()
4941cb0ef41Sopenharmony_ci    if (opts.workspaces && opts.workspaces.length) {
4951cb0ef41Sopenharmony_ci      filterSet =
4961cb0ef41Sopenharmony_ci        arb.workspaceDependencySet(
4971cb0ef41Sopenharmony_ci          tree,
4981cb0ef41Sopenharmony_ci          opts.workspaces,
4991cb0ef41Sopenharmony_ci          this.npm.flatOptions.includeWorkspaceRoot
5001cb0ef41Sopenharmony_ci        )
5011cb0ef41Sopenharmony_ci    } else if (!this.npm.flatOptions.workspacesEnabled) {
5021cb0ef41Sopenharmony_ci      filterSet =
5031cb0ef41Sopenharmony_ci        arb.excludeWorkspacesDependencySet(tree)
5041cb0ef41Sopenharmony_ci    }
5051cb0ef41Sopenharmony_ci
5061cb0ef41Sopenharmony_ci    const verify = new VerifySignatures(tree, filterSet, this.npm, { ...opts })
5071cb0ef41Sopenharmony_ci    await verify.run()
5081cb0ef41Sopenharmony_ci  }
5091cb0ef41Sopenharmony_ci}
5101cb0ef41Sopenharmony_ci
5111cb0ef41Sopenharmony_cimodule.exports = Audit
512