Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix!: remove old audit fallback request #7911

Merged
merged 3 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions DEPENDENCIES.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,10 @@
[
"npm"
],
[
"@npmcli/smoke-tests",
"libnpmaccess",
"libnpmexec",
"libnpmpublish"
],
[
"@npmcli/mock-registry",
"libnpmdiff",
"libnpmexec",
"libnpmfund",
"libnpmpack"
],
Expand All @@ -28,7 +23,9 @@
[
"@npmcli/map-workspaces",
"@npmcli/run-script",
"libnpmaccess",
"libnpmorg",
"libnpmpublish",
"libnpmsearch",
"libnpmteam",
"init-package-json",
Expand All @@ -50,6 +47,7 @@
],
[
"@npmcli/docs",
"@npmcli/smoke-tests",
"@npmcli/fs",
"npm-bundled",
"npm-install-checks",
Expand Down
9 changes: 5 additions & 4 deletions DEPENDENCIES.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ graph LR;
npmcli-arborist-->npmcli-installed-package-contents["@npmcli/installed-package-contents"];
npmcli-arborist-->npmcli-map-workspaces["@npmcli/map-workspaces"];
npmcli-arborist-->npmcli-metavuln-calculator["@npmcli/metavuln-calculator"];
npmcli-arborist-->npmcli-mock-registry["@npmcli/mock-registry"];
npmcli-arborist-->npmcli-name-from-folder["@npmcli/name-from-folder"];
npmcli-arborist-->npmcli-node-gyp["@npmcli/node-gyp"];
npmcli-arborist-->npmcli-package-json["@npmcli/package-json"];
Expand Down Expand Up @@ -585,6 +586,7 @@ graph LR;
npmcli-arborist-->npmcli-installed-package-contents["@npmcli/installed-package-contents"];
npmcli-arborist-->npmcli-map-workspaces["@npmcli/map-workspaces"];
npmcli-arborist-->npmcli-metavuln-calculator["@npmcli/metavuln-calculator"];
npmcli-arborist-->npmcli-mock-registry["@npmcli/mock-registry"];
npmcli-arborist-->npmcli-name-from-folder["@npmcli/name-from-folder"];
npmcli-arborist-->npmcli-node-gyp["@npmcli/node-gyp"];
npmcli-arborist-->npmcli-package-json["@npmcli/package-json"];
Expand Down Expand Up @@ -785,14 +787,13 @@ Each group depends on packages lower down the chain, nothing depends on
packages higher up the chain.

- npm
- @npmcli/smoke-tests, libnpmaccess, libnpmexec, libnpmpublish
- @npmcli/mock-registry, libnpmdiff, libnpmfund, libnpmpack
- @npmcli/mock-registry, libnpmdiff, libnpmexec, libnpmfund, libnpmpack
- @npmcli/arborist
- @npmcli/metavuln-calculator
- pacote, @npmcli/config, libnpmversion
- @npmcli/map-workspaces, @npmcli/run-script, libnpmorg, libnpmsearch, libnpmteam, init-package-json, npm-profile
- @npmcli/map-workspaces, @npmcli/run-script, libnpmaccess, libnpmorg, libnpmpublish, libnpmsearch, libnpmteam, init-package-json, npm-profile
- @npmcli/package-json, npm-registry-fetch
- @npmcli/git, make-fetch-happen
- @npmcli/installed-package-contents, npm-pick-manifest, cacache, promzard
- @npmcli/docs, @npmcli/fs, npm-bundled, npm-install-checks, npm-package-arg, normalize-package-data, unique-filename, npm-packlist, bin-links, nopt, parse-conflict-json, read-package-json-fast, @npmcli/mock-globals, read
- @npmcli/docs, @npmcli/smoke-tests, @npmcli/fs, npm-bundled, npm-install-checks, npm-package-arg, normalize-package-data, unique-filename, npm-packlist, bin-links, nopt, parse-conflict-json, read-package-json-fast, @npmcli/mock-globals, read
- @npmcli/eslint-config, @npmcli/template-oss, ignore-walk, semver, npm-normalize-package-bin, @npmcli/name-from-folder, @npmcli/promise-spawn, ini, hosted-git-info, proc-log, validate-npm-package-name, json-parse-even-better-errors, fs-minipass, ssri, unique-slug, @npmcli/node-gyp, @npmcli/redact, @npmcli/agent, minipass-fetch, @npmcli/query, cmd-shim, read-cmd-shim, write-file-atomic, abbrev, proggy, minify-registry-metadata, mute-stream, npm-audit-report, npm-user-validate
75 changes: 72 additions & 3 deletions mock-registry/lib/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
const pacote = require('pacote')
const Arborist = require('@npmcli/arborist')
const npa = require('npm-package-arg')
const Nock = require('nock')
const npa = require('npm-package-arg')
const pacote = require('pacote')
const path = require('node:path')
const stringify = require('json-stringify-safe')

const { createReadStream } = require('node:fs')
const fs = require('node:fs/promises')

const corgiDoc = 'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*'

const logReq = (req, ...keys) => {
const obj = JSON.parse(stringify(req))
const res = {}
Expand All @@ -15,6 +21,27 @@ const logReq = (req, ...keys) => {
return stringify(res, null, 2)
}

// helper to convert old audit results to new bulk results
// TODO eventually convert the fixture files themselves
const auditToBulk = audit => {
const bulk = {}
for (const advisory in audit.advisories) {
const {
id,
url,
title,
severity = 'high',
/* eslint-disable-next-line camelcase */
vulnerable_versions = '*',
module_name: name,
} = audit.advisories[advisory]
bulk[name] = bulk[name] || []
/* eslint-disable-next-line camelcase */
bulk[name].push({ id, url, title, severity, vulnerable_versions })
}
return bulk
}

class MockRegistry {
#tap
#nock
Expand Down Expand Up @@ -66,14 +93,14 @@ class MockRegistry {
// find mistakes quicker instead of waiting for the entire test to end
t.afterEach((t) => {
t.strictSame(server.pendingMocks(), [], 'no pending mocks after each')
t.strictSame(server.activeMocks(), [], 'no active mocks after each')
})
wraithgar marked this conversation as resolved.
Show resolved Hide resolved
}

t.teardown(() => {
Nock.enableNetConnect()
server.done()
Nock.emitter.off('no match', noMatch)
Nock.cleanAll()
})

return server
Expand Down Expand Up @@ -453,6 +480,48 @@ class MockRegistry {
}
}

// bulk advisory audit endpoint
audit ({ responseCode = 200, results = {}, convert = false, times = 1 } = {}) {
this.nock = this.nock
.post(this.fullPath('/-/npm/v1/security/advisories/bulk'))
.times(times)
.reply(
responseCode,
convert ? auditToBulk(results) : results
)
}

// Used in Arborist to mock the registry from fixture data on disk
// Will eat up all GET requests to the entire registry, so it probably doesn't work with the other GET routes very well.
mocks ({ dir }) {
const exists = (p) => fs.stat(p).then((s) => s).catch(() => false)
this.nock = this.nock.get(/.*/).reply(async function () {
const { headers, path: url } = this.req
const isCorgi = headers.accept.includes('application/vnd.npm.install-v1+json')
const encodedUrl = url.replace(/@/g, '').replace(/%2f/gi, '/')
const f = path.join(dir, 'registry-mocks', 'content', encodedUrl)
let file = f
let contentType = 'application/octet-stream'
if (isCorgi && await exists(`${f}.min.json`)) {
file = `${f}.min.json`
contentType = corgiDoc
} else if (await exists(`${f}.json`)) {
file = `${f}.json`
contentType = 'application/json'
} else if (await exists(`${f}/index.json`)) {
file = `${f}index.json`
contentType = 'application/json'
}
const stats = await exists(file)
if (stats) {
const body = createReadStream(file)
body.pause()
return [200, body, { 'content-type': contentType, 'content-length': stats.size }]
}
return [404, { error: 'not found' }]
}).persist()
}

/**
* this is a simpler convience method for creating mockable registry with
* tarballs for specific versions
Expand Down
1 change: 1 addition & 0 deletions package-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -16922,6 +16922,7 @@
},
"devDependencies": {
"@npmcli/eslint-config": "^5.0.1",
"@npmcli/mock-registry": "^1.0.0",
"@npmcli/template-oss": "4.23.3",
"benchmark": "^2.1.4",
"minify-registry-metadata": "^4.0.0",
Expand Down
36 changes: 32 additions & 4 deletions scripts/dependency-graph.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,31 @@ const { run, CWD, pkg, fs, EOL } = require('./util.js')
// npx -p @npmcli/stafftools gh repos --json | json -a name | sort > scripts/npm-cli-repos.txt
const repos = readFileSync(join(CWD, 'scripts', 'npm-cli-repos.txt'), 'utf-8').trim().split(os.EOL)

// Packages with known circular dependencies. This is typically something with arborist as a dependency which is also in arborist's dev dependencies. Not a problem if they're workspaces so we ignore repeats
const circular = new Set(['@npmcli/mock-registry'])
wraithgar marked this conversation as resolved.
Show resolved Hide resolved

// TODO Set.intersection/difference was added in node 22.11.0, once we're above that line we can use the builtin
// https://node.green/#ES2025-features-Set-methods-Set-prototype-intersection--
function intersection (set1, set2) {
const result = new Set()
for (const item of set1) {
if (set2.has(item)) {
result.add(item)
}
}
return result
}

function difference (set1, set2) {
const result = new Set()
for (const item of set1) {
if (!set2.has(item)) {
result.add(item)
}
}
return result
}

// these have a different package name than the repo name, and are ours.
const aliases = {
semver: 'node-semver',
Expand All @@ -29,6 +54,7 @@ const namespaced = [
'git',
'installed-package-contents',
'lint',
'mock-registry',
'map-workspaces',
'metavuln-calculator',
'move-file',
Expand Down Expand Up @@ -140,7 +166,11 @@ const walk = function (tree, onlyOurs) {
log.silly(dep, '::', [...dependedBy[dep]].join(', '))
log.silly('-'.repeat(80))

if (!dependedBy[dep].size) {
// things that depend on us that are at the same level
const both = intersection(allDeps, dependedBy[dep])
// ... minus the known circular dependencies
const neither = difference(both, circular)
if (!dependedBy[dep].size || !neither.size) {
level.push(dep)
foundDeps.add(dep)
}
Expand Down Expand Up @@ -177,9 +207,7 @@ const iterate = function (node, dependedBy, annotations, onlyOurs) {
dependedBy[node.packageName] = new Set()
}
for (const [name, edge] of node.edgesOut) {
if (
(!onlyOurs || isOurs(name)) && !node.dev
) {
if ((!onlyOurs || isOurs(name)) && !node.dev) {
if (!dependedBy[node.packageName].has(edge.name)) {
dependedBy[node.packageName].add(edge.name)
annotations.push(` ${stripName(node.packageName)}-->${escapeName(edge.name)};`)
Expand Down
48 changes: 0 additions & 48 deletions test/lib/commands/audit.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,54 +90,6 @@ t.test('normal audit', async t => {
t.matchSnapshot(joinedOutput())
})

t.test('fallback audit ', async t => {
const { npm, joinedOutput } = await loadMockNpm(t, {
prefixDir: tree,
})
const registry = new MockRegistry({
tap: t,
registry: npm.config.get('registry'),
})
const manifest = registry.manifest({
name: 'test-dep-a',
packuments: [{ version: '1.0.0' }, { version: '1.0.1' }],
})
await registry.package({ manifest })
const advisory = registry.advisory({
id: 100,
module_name: 'test-dep-a',
vulnerable_versions: '<1.0.1',
findings: [{ version: '1.0.0', paths: ['test-dep-a'] }],
})
registry.nock
.post('/-/npm/v1/security/advisories/bulk').reply(404)
.post('/-/npm/v1/security/audits/quick', body => {
const unzipped = JSON.parse(gunzip(Buffer.from(body, 'hex')))
return t.match(unzipped, {
name: 'test-dep',
version: '1.0.0',
requires: { 'test-dep-a': '*' },
dependencies: { 'test-dep-a': { version: '1.0.0' } },
})
}).reply(200, {
actions: [],
muted: [],
advisories: {
100: advisory,
},
metadata: {
vulnerabilities: { info: 0, low: 0, moderate: 0, high: 1, critical: 0 },
dependencies: 1,
devDependencies: 0,
optionalDependencies: 0,
totalDependencies: 1,
},
})
await npm.exec('audit', [])
t.ok(process.exitCode, 'would have exited uncleanly')
t.matchSnapshot(joinedOutput())
})

t.test('json audit', async t => {
const { npm, joinedOutput } = await loadMockNpm(t, {
prefixDir: tree,
Expand Down
Loading
Loading