/
usr
/
local
/
lib
/
node_modules
/
npm
/
lib
/
utils
/
/usr/local/lib/node_modules/npm/lib/utils
mkdir
upload
Name
Size
Mode
Actions
audit-error.js
1078
0644
edit
dl
rm
auth.js
3288
0644
edit
dl
rm
cmd-list.js
2953
0644
edit
dl
rm
completion.fish
1596
0644
edit
dl
rm
completion.sh
1898
0755
edit
dl
rm
did-you-mean.js
1143
0644
edit
dl
rm
display.js
16760
0644
edit
dl
rm
error-message.js
15775
0644
edit
dl
rm
explain-dep.js
3648
0644
edit
dl
rm
explain-eresolve.js
2582
0644
edit
dl
rm
format-bytes.js
624
0644
edit
dl
rm
format-search-stream.js
4913
0644
edit
dl
rm
format.js
2140
0644
edit
dl
rm
get-identity.js
797
0644
edit
dl
rm
get-workspaces.js
1748
0644
edit
dl
rm
installed-deep.js
1129
0644
edit
dl
rm
installed-shallow.js
583
0644
edit
dl
rm
is-windows.js
177
0644
edit
dl
rm
log-file.js
7860
0644
edit
dl
rm
npm-usage.js
2072
0644
edit
dl
rm
oidc.js
7368
0644
edit
dl
rm
open-url.js
2553
0644
edit
dl
rm
output-error.js
788
0644
edit
dl
rm
ping.js
265
0644
edit
dl
rm
queryable.js
9611
0644
edit
dl
rm
read-user-info.js
2095
0644
edit
dl
rm
reify-finish.js
753
0644
edit
dl
rm
reify-output.js
6475
0644
edit
dl
rm
sbom-cyclonedx.js
5656
0644
edit
dl
rm
sbom-spdx.js
5288
0644
edit
dl
rm
tar.js
3511
0644
edit
dl
rm
timers.js
2120
0644
edit
dl
rm
update-workspaces.js
997
0644
edit
dl
rm
validate-lockfile.js
1023
0644
edit
dl
rm
verify-signatures.js
12499
0644
edit
dl
rm
Edit:
/usr/local/lib/node_modules/npm/lib/utils/verify-signatures.js
(12499B)
const npmFetch = require('npm-registry-fetch') const localeCompare = require('@isaacs/string-locale-compare')('en') const npa = require('npm-package-arg') const pacote = require('pacote') const tufClient = require('@sigstore/tuf') const { log, output } = require('proc-log') const sortAlphabetically = (a, b) => localeCompare(a.name, b.name) class VerifySignatures { constructor (tree, filterSet, npm, opts) { this.tree = tree this.filterSet = filterSet this.npm = npm this.opts = opts this.keys = new Map() this.invalid = [] this.missing = [] this.checkedPackages = new Set() this.verified = [] this.auditedWithKeysCount = 0 this.verifiedSignatureCount = 0 this.verifiedAttestationCount = 0 this.exitCode = 0 } async run () { const start = process.hrtime.bigint() const { default: pMap } = await import('p-map') // Find all deps in tree const { edges, registries } = this.getEdgesOut(this.tree.inventory.values(), this.filterSet) if (edges.size === 0) { throw new Error('found no installed dependencies to audit') } const tuf = await tufClient.initTUF({ cachePath: this.opts.tufCache, retry: this.opts.retry, timeout: this.opts.timeout, }) await Promise.all([...registries].map(registry => this.setKeys({ registry, tuf }))) log.verbose('verifying registry signatures') await pMap(edges, (e) => this.getVerifiedInfo(e), { concurrency: 20, stopOnError: true }) // Didn't find any dependencies that could be verified, e.g. only local deps, missing version, not on a registry etc. if (!this.auditedWithKeysCount && !this.verifiedAttestationCount) { throw new Error('found no dependencies to audit that were installed from ' + 'a supported registry') } const invalid = this.invalid.sort(sortAlphabetically) const missing = this.missing.sort(sortAlphabetically) const hasNoInvalidOrMissing = invalid.length === 0 && missing.length === 0 if (!hasNoInvalidOrMissing) { process.exitCode = 1 } if (this.npm.config.get('json')) { const result = { invalid, missing } if (this.npm.config.get('include-attestations')) { result.verified = this.verified } output.buffer(result) return } const end = process.hrtime.bigint() const elapsed = end - start const auditedPlural = this.auditedWithKeysCount > 1 ? 's' : '' const timing = `audited ${this.auditedWithKeysCount} package${auditedPlural} in ` + `${Math.floor(Number(elapsed) / 1e9)}s` output.standard(timing) output.standard() const verifiedBold = this.npm.chalk.bold('verified') if (this.verifiedSignatureCount) { if (this.verifiedSignatureCount === 1) { output.standard(`${this.verifiedSignatureCount} package has a ${verifiedBold} registry signature`) } else { output.standard(`${this.verifiedSignatureCount} packages have ${verifiedBold} registry signatures`) } output.standard() } if (this.verifiedAttestationCount) { if (this.verifiedAttestationCount === 1) { output.standard(`${this.verifiedAttestationCount} package has a ${verifiedBold} attestation`) } else { output.standard(`${this.verifiedAttestationCount} packages have ${verifiedBold} attestations`) } if (!this.npm.config.get('include-attestations')) { output.standard('(use --json --include-attestations to view attestation details)') } output.standard() } if (missing.length) { const missingClr = this.npm.chalk.redBright('missing') if (missing.length === 1) { output.standard(`1 package has a ${missingClr} registry signature but the registry is providing signing keys:`) } else { output.standard(`${missing.length} packages have ${missingClr} registry signatures but the registry is providing signing keys:`) } output.standard() missing.map(m => output.standard(`${this.npm.chalk.red(`${m.name}@${m.version}`)} (${m.registry})`) ) } if (invalid.length) { if (missing.length) { output.standard() } const invalidClr = this.npm.chalk.redBright('invalid') // We can have either invalid signatures or invalid provenance const invalidSignatures = this.invalid.filter(i => i.code === 'EINTEGRITYSIGNATURE') if (invalidSignatures.length) { if (invalidSignatures.length === 1) { output.standard(`1 package has an ${invalidClr} registry signature:`) } else { output.standard(`${invalidSignatures.length} packages have ${invalidClr} registry signatures:`) } output.standard() invalidSignatures.map(i => output.standard(`${this.npm.chalk.red(`${i.name}@${i.version}`)} (${i.registry})`) ) output.standard() } const invalidAttestations = this.invalid.filter(i => i.code === 'EATTESTATIONVERIFY') if (invalidAttestations.length) { if (invalidAttestations.length === 1) { output.standard(`1 package has an ${invalidClr} attestation:`) } else { output.standard(`${invalidAttestations.length} packages have ${invalidClr} attestations:`) } output.standard() invalidAttestations.map(i => output.standard(`${this.npm.chalk.red(`${i.name}@${i.version}`)} (${i.registry})`) ) output.standard() } if (invalid.length === 1) { output.standard(`Someone might have tampered with this package since it was published on the registry!`) } else { output.standard(`Someone might have tampered with these packages since they were published on the registry!`) } output.standard() } } getEdgesOut (nodes, filterSet) { const edges = new Set() const registries = new Set() for (const node of nodes) { for (const edge of node.edgesOut.values()) { const filteredOut = edge.from && filterSet && filterSet.size > 0 && !filterSet.has(edge.from.target) if (!filteredOut) { const spec = this.getEdgeSpec(edge) if (spec) { // Prefetch and cache public keys from used registries registries.add(this.getSpecRegistry(spec)) } edges.add(edge) } } } return { edges, registries } } async setKeys ({ registry, tuf }) { const { host, pathname } = new URL(registry) // Strip any trailing slashes from pathname const regKey = `${host}${pathname.replace(/\/$/, '')}/keys.json` let keys = await tuf.getTarget(regKey) .then((target) => JSON.parse(target)) .then(({ keys: ks }) => ks.map((key) => ({ ...key, keyid: key.keyId, pemkey: `-----BEGIN PUBLIC KEY-----\n${key.publicKey.rawBytes}\n-----END PUBLIC KEY-----`, expires: key.publicKey.validFor.end || null, }))).catch(err => { if (err.code === 'TUF_FIND_TARGET_ERROR') { return null } else { throw err } }) // If keys not found in Sigstore TUF repo, fall back to registry keys API if (!keys) { log.warn(`Fetching verification keys using TUF failed. Fetching directly from ${registry}.`) keys = await npmFetch.json('/-/npm/v1/keys', { ...this.npm.flatOptions, registry, }).then(({ keys: ks }) => ks.map((key) => ({ ...key, pemkey: `-----BEGIN PUBLIC KEY-----\n${key.key}\n-----END PUBLIC KEY-----`, }))).catch(err => { if (err.code === 'E404' || err.code === 'E400') { return null } else { throw err } }) } if (keys) { this.keys.set(registry, keys) } } getEdgeType (edge) { return edge.optional ? 'optionalDependencies' : edge.peer ? 'peerDependencies' : edge.dev ? 'devDependencies' : 'dependencies' } getEdgeSpec (edge) { let name = edge.name try { name = npa(edge.spec).subSpec.name } catch { // leave it as edge.name } try { return npa(`${name}@${edge.spec}`) } catch { // Skip packages with invalid spec } } buildRegistryConfig (registry) { const keys = this.keys.get(registry) || [] const parsedRegistry = new URL(registry) const regKey = `//${parsedRegistry.host}${parsedRegistry.pathname}` return { [`${regKey}:_keys`]: keys, } } getSpecRegistry (spec) { return npmFetch.pickRegistry(spec, this.npm.flatOptions) } getValidPackageInfo (edge) { const type = this.getEdgeType(edge) // Skip potentially optional packages that are not on disk, as these could // be omitted during install if (edge.error === 'MISSING' && type !== 'dependencies') { return } const spec = this.getEdgeSpec(edge) // Skip invalid version requirements if (!spec) { return } const node = edge.to || edge const { version } = node.package || {} if (node.isWorkspace || // Skip local workspaces packages !version || // Skip packages that don't have an installed version, e.g. optional dependencies !spec.registry) { // Skip if not from registry, e.g. git package return } for (const omitType of this.npm.config.get('omit')) { if (node[omitType]) { return } } return { name: spec.name, version, type, location: node.location, registry: this.getSpecRegistry(spec), } } async verifySignatures (name, version, registry) { const { _integrity: integrity, _signatures, _attestations, _attestationBundles, _resolved: resolved, } = await pacote.manifest(`${name}@${version}`, { verifySignatures: true, verifyAttestations: true, ...this.buildRegistryConfig(registry), ...this.npm.flatOptions, }) const signatures = _signatures || [] const result = { integrity, signatures, attestations: _attestations, attestationBundles: _attestationBundles, resolved, } return result } async getVerifiedInfo (edge) { const info = this.getValidPackageInfo(edge) if (!info) { return } const { name, version, location, registry, type } = info if (this.checkedPackages.has(location)) { // we already did or are doing this one return } this.checkedPackages.add(location) // We only "audit" or verify the signature, or the presence of it, on packages whose registry returns signing keys const keys = this.keys.get(registry) || [] if (keys.length) { this.auditedWithKeysCount += 1 } try { const { integrity, signatures, attestations, attestationBundles, resolved } = await this.verifySignatures(name, version, registry) // Currently we only care about missing signatures on registries that provide a public key // We could make this configurable in the future with a strict/paranoid mode if (signatures.length) { this.verifiedSignatureCount += 1 } else if (keys.length) { this.missing.push({ integrity, location, name, registry, resolved, version, }) } // Track verified attestations separately to registry signatures, as all packages on registries with signing keys are expected to have registry signatures, but not all packages have provenance and publish attestations. if (attestations) { this.verifiedAttestationCount += 1 if (this.npm.config.get('include-attestations')) { this.verified.push({ name, version, location, registry, attestations, attestationBundles, }) } } } catch (e) { if (e.code === 'EINTEGRITYSIGNATURE' || e.code === 'EATTESTATIONVERIFY') { this.invalid.push({ code: e.code, message: e.message, integrity: e.integrity, keyid: e.keyid, location, name, registry, resolved: e.resolved, signature: e.signature, predicateType: e.predicateType, type, version, }) } else { throw e } } } } module.exports = VerifySignatures
Save
cmd:
run