Skip to content

Commit 5f209b9

Browse files
committed
chore: retrieve npm keys via TUF
Signed-off-by: Brian DeHamer <bdehamer@github.com>
1 parent 8bd1e6d commit 5f209b9

File tree

6 files changed

+265
-93
lines changed

6 files changed

+265
-93
lines changed

DEPENDENCIES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,7 @@ graph LR;
560560
npm-->text-table;
561561
npm-->tiny-relative-date;
562562
npm-->treeverse;
563+
npm-->tufjs-repo-mock["@tufjs/repo-mock"];
563564
npm-->validate-npm-package-name;
564565
npm-->which;
565566
npm-->write-file-atomic;

lib/commands/audit.js

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ const fetch = require('npm-registry-fetch')
33
const localeCompare = require('@isaacs/string-locale-compare')('en')
44
const npa = require('npm-package-arg')
55
const pacote = require('pacote')
6+
const path = require('path')
67
const pMap = require('p-map')
8+
const { sigstore } = require('sigstore')
79

810
const ArboristWorkspaceCmd = require('../arborist-cmd.js')
911
const auditError = require('../utils/audit-error.js')
@@ -188,19 +190,41 @@ class VerifySignatures {
188190
}
189191

190192
async setKeys ({ registry }) {
191-
const keys = await fetch.json('/-/npm/v1/keys', {
192-
...this.npm.flatOptions,
193-
registry,
194-
}).then(({ keys: ks }) => ks.map((key) => ({
195-
...key,
196-
pemkey: `-----BEGIN PUBLIC KEY-----\n${key.key}\n-----END PUBLIC KEY-----`,
197-
}))).catch(err => {
198-
if (err.code === 'E404' || err.code === 'E400') {
199-
return null
200-
} else {
201-
throw err
202-
}
203-
})
193+
const { host, pathname } = new URL(registry)
194+
const regKey = `${host}${pathname === '/' ? '' : pathname}/keys.json`
195+
const tufCachePath = path.join(this.npm.cache, '_tuf')
196+
let keys = await sigstore.tuf.getTarget(regKey, { tufCachePath })
197+
.then((target) => JSON.parse(target))
198+
.then(({ keys: ks }) => ks.map((key) => ({
199+
...key,
200+
keyid: key.keyId,
201+
pemkey: `-----BEGIN PUBLIC KEY-----\n${key.publicKey.rawBytes}\n-----END PUBLIC KEY-----`,
202+
expires: key.publicKey.validFor.end || null,
203+
}))).catch(err => {
204+
if (err.code === 'TUF_FIND_TARGET_ERROR') {
205+
return null
206+
} else {
207+
throw err
208+
}
209+
})
210+
211+
// If keys not found in Sigstore TUF repo, fallback to registry keys API
212+
if (!keys) {
213+
keys = await fetch.json('/-/npm/v1/keys', {
214+
...this.npm.flatOptions,
215+
registry,
216+
}).then(({ keys: ks }) => ks.map((key) => ({
217+
...key,
218+
pemkey: `-----BEGIN PUBLIC KEY-----\n${key.key}\n-----END PUBLIC KEY-----`,
219+
}))).catch(err => {
220+
if (err.code === 'E404' || err.code === 'E400') {
221+
return null
222+
} else {
223+
throw err
224+
}
225+
})
226+
}
227+
204228
if (keys) {
205229
this.keys.set(registry, keys)
206230
}

package-lock.json

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
"read-package-json",
6767
"read-package-json-fast",
6868
"semver",
69+
"sigstore",
6970
"ssri",
7071
"tar",
7172
"text-table",
@@ -163,6 +164,7 @@
163164
"@npmcli/mock-registry": "^1.0.0",
164165
"@npmcli/promise-spawn": "^6.0.2",
165166
"@npmcli/template-oss": "4.12.1",
167+
"@tufjs/repo-mock": "^1.3.0",
166168
"licensee": "^10.0.0",
167169
"nock": "^13.3.0",
168170
"npm-packlist": "^7.0.4",
@@ -2555,6 +2557,19 @@
25552557
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
25562558
}
25572559
},
2560+
"node_modules/@tufjs/repo-mock": {
2561+
"version": "1.3.0",
2562+
"resolved": "https://registry.npmjs.org/@tufjs/repo-mock/-/repo-mock-1.3.0.tgz",
2563+
"integrity": "sha512-R2bQ5t9FeZ9qAZuWHg/E/8ixllTI1/fEzJSuVdDo4a9SGfm5wNbUICT7n9Pl9AQCsAkw6evRjLQ/JUwueijTvA==",
2564+
"dev": true,
2565+
"dependencies": {
2566+
"@tufjs/models": "1.0.3",
2567+
"nock": "^13.3.1"
2568+
},
2569+
"engines": {
2570+
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
2571+
}
2572+
},
25582573
"node_modules/@types/debug": {
25592574
"version": "4.1.7",
25602575
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz",
@@ -8831,9 +8846,9 @@
88318846
"dev": true
88328847
},
88338848
"node_modules/nock": {
8834-
"version": "13.3.0",
8835-
"resolved": "https://registry.npmjs.org/nock/-/nock-13.3.0.tgz",
8836-
"integrity": "sha512-HHqYQ6mBeiMc+N038w8LkMpDCRquCHWeNmN3v6645P3NhN2+qXOBqvPqo7Rt1VyCMzKhJ733wZqw5B7cQVFNPg==",
8849+
"version": "13.3.1",
8850+
"resolved": "https://registry.npmjs.org/nock/-/nock-13.3.1.tgz",
8851+
"integrity": "sha512-vHnopocZuI93p2ccivFyGuUfzjq2fxNyNurp7816mlT5V5HF4SzXu8lvLrVzBbNqzs+ODooZ6OksuSUNM7Njkw==",
88378852
"dev": true,
88388853
"dependencies": {
88398854
"debug": "^4.1.0",

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@
197197
"@npmcli/mock-registry": "^1.0.0",
198198
"@npmcli/promise-spawn": "^6.0.2",
199199
"@npmcli/template-oss": "4.12.1",
200+
"@tufjs/repo-mock": "^1.3.0",
200201
"licensee": "^10.0.0",
201202
"nock": "^13.3.0",
202203
"npm-packlist": "^7.0.4",

tap-snapshots/test/lib/commands/audit.js.test.cjs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,13 @@ audited 1 package in xxx
175175
176176
`
177177

178+
exports[`test/lib/commands/audit.js TAP audit signatures third-party registry with sub-path > must match snapshot 1`] = `
179+
audited 1 package in xxx
180+
181+
1 package has a verified registry signature
182+
183+
`
184+
178185
exports[`test/lib/commands/audit.js TAP audit signatures with both invalid and missing signatures > must match snapshot 1`] = `
179186
audited 2 packages in xxx
180187
@@ -230,6 +237,13 @@ Someone might have tampered with this package since it was published on the regi
230237
231238
`
232239

240+
exports[`test/lib/commands/audit.js TAP audit signatures with key fallback to legacy API > must match snapshot 1`] = `
241+
audited 1 package in xxx
242+
243+
1 package has a verified registry signature
244+
245+
`
246+
233247
exports[`test/lib/commands/audit.js TAP audit signatures with keys but missing signature > must match snapshot 1`] = `
234248
audited 1 package in xxx
235249

0 commit comments

Comments
 (0)