mirror of https://gitee.com/openkylin/npm.git
268 lines
6.3 KiB
JavaScript
268 lines
6.3 KiB
JavaScript
const t = require('tap')
|
|
const Vuln = require('../lib/vuln.js')
|
|
const Node = require('../lib/node.js')
|
|
const Link = require('../lib/link.js')
|
|
const Edge = require('../lib/edge.js')
|
|
const { resolve } = require('path')
|
|
|
|
const semver = require('semver')
|
|
const semverOpt = { includePrerelease: true, loose: true }
|
|
class MockAdvisory {
|
|
constructor (obj) {
|
|
Object.assign(this, obj)
|
|
this.vulnerableVersions = this.versions.filter(v =>
|
|
semver.satisfies(v, this.range, semverOpt))
|
|
}
|
|
|
|
testVersion (v) {
|
|
return this.vulnerableVersions.includes(v)
|
|
}
|
|
}
|
|
|
|
t.test('basic vulnerability object tests', async t => {
|
|
const crit = new MockAdvisory({
|
|
type: 'advisory',
|
|
source: 420,
|
|
id: 'cafebad',
|
|
title: 'borgsafalamash',
|
|
name: 'name',
|
|
dependency: 'name',
|
|
severity: 'critical',
|
|
range: '1.x < 1.3',
|
|
versions: [
|
|
'0.0.1',
|
|
'0.0.2',
|
|
'0.0.3',
|
|
'0.1.0',
|
|
'1.0.0',
|
|
'1.0.1',
|
|
'1.1.0',
|
|
'1.2.0',
|
|
'1.2.1',
|
|
'2.0.0',
|
|
'2.0.1',
|
|
'2.1.0',
|
|
'2.2.0',
|
|
'2.2.0-pre.0',
|
|
'3.0.0-pre.0',
|
|
'3.0.0-pre.1',
|
|
'3.0.0',
|
|
'3.0.1',
|
|
'3.1.0',
|
|
'3.2.0',
|
|
],
|
|
})
|
|
|
|
const low = new MockAdvisory({
|
|
type: 'advisory',
|
|
source: 69,
|
|
id: 'deadbeef',
|
|
title: 'flerbygurrf',
|
|
name: 'name',
|
|
dependency: 'name',
|
|
severity: 'low',
|
|
range: '2.x < 2.3.2 || 3.x <3.0.1',
|
|
versions: [
|
|
'0.0.1',
|
|
'0.0.2',
|
|
'0.0.3',
|
|
'0.1.0',
|
|
'1.0.0',
|
|
'1.0.1',
|
|
'1.1.0',
|
|
'1.2.0',
|
|
'1.2.1',
|
|
'2.0.0',
|
|
'2.0.1',
|
|
'2.1.0',
|
|
'2.2.0',
|
|
'2.2.0-pre.0',
|
|
'3.0.0-pre.0',
|
|
'3.0.0-pre.1',
|
|
'3.0.0',
|
|
'3.0.1',
|
|
'3.1.0',
|
|
'3.2.0',
|
|
],
|
|
})
|
|
|
|
const v = new Vuln({ name: 'name', advisory: crit })
|
|
t.type(v, Vuln)
|
|
t.equal(v.testSpec('github:foo/bar'), true)
|
|
t.equal(v.testSpec('0.x'), false)
|
|
t.equal(v.testSpec('>4.x'), true)
|
|
t.equal(v.testSpec('npm:name@>4.x'), true)
|
|
t.strictSame([...v.advisories], [crit])
|
|
t.equal(v.severity, 'critical')
|
|
v.addAdvisory(low)
|
|
t.equal(v.testSpec('2.x'), true)
|
|
t.equal(v.severity, 'critical')
|
|
t.match(v.advisories, new Set([crit, low]))
|
|
v.deleteAdvisory(crit)
|
|
t.equal(v.severity, 'low')
|
|
t.match(v.advisories, new Set([low]))
|
|
v.addAdvisory(crit)
|
|
t.equal(v.severity, 'critical')
|
|
t.match(v.advisories, new Set([crit, low]))
|
|
t.matchSnapshot(JSON.stringify(v, 0, 2), 'json formatted')
|
|
|
|
const meta = new MockAdvisory({
|
|
type: 'metavuln',
|
|
source: 'deadbeef',
|
|
severity: 'low',
|
|
name: 'another',
|
|
dependency: 'name',
|
|
range: '1.x',
|
|
versions: [
|
|
'0.0.0',
|
|
'1.0.0',
|
|
'1.0.1',
|
|
'1.0.2',
|
|
'2.0.0',
|
|
'2.0.1',
|
|
'3.0.0',
|
|
],
|
|
})
|
|
|
|
const meta2 = new MockAdvisory({
|
|
type: 'metavuln',
|
|
source: 'cafebad',
|
|
severity: 'critical',
|
|
name: 'another',
|
|
dependency: 'name',
|
|
range: '>1.0.0 <=2.0.1',
|
|
versions: [
|
|
'0.0.0',
|
|
'1.0.0',
|
|
'1.0.1',
|
|
'2.0.0',
|
|
'2.0.1',
|
|
'3.0.0',
|
|
],
|
|
})
|
|
|
|
const v2 = new Vuln({ name: 'another', advisory: meta })
|
|
v2.addVia(v)
|
|
t.matchSnapshot(JSON.stringify(v), 'json after adding effect')
|
|
t.match(v2.advisories, new Set([meta]))
|
|
t.equal(v2.range, meta.range)
|
|
|
|
v2.addAdvisory(meta2)
|
|
t.equal(v2.range, [meta.range, meta2.range].join(' || '))
|
|
|
|
// when the simple range is loaded, it memoizes and sets range as well
|
|
t.equal(v2.simpleRange, '1.0.0 - 2.0.1')
|
|
t.equal(v2.range, v2.simpleRange)
|
|
|
|
const root = new Node({
|
|
path: '/path/to',
|
|
pkg: {
|
|
dependencies: { thing: '1.2.1' },
|
|
workspaces: [
|
|
'packages/foo',
|
|
],
|
|
},
|
|
})
|
|
|
|
// a workspace with one direct vuln and one indirect vuln
|
|
const ws = new Node({
|
|
pkg: { name: 'foo', version: '1.2.3', dependencies: { bar: '' } },
|
|
path: resolve(root.path, 'packages/foo'),
|
|
root,
|
|
children: [
|
|
{ pkg: { name: 'bar', version: '1.2.3', dependencies: { baz: '' } } },
|
|
{ pkg: { name: 'baz', version: '1.2.3' } },
|
|
],
|
|
})
|
|
|
|
// manually connect the workspace
|
|
new Link({
|
|
name: 'foo',
|
|
parent: root,
|
|
target: ws,
|
|
})
|
|
new Edge({
|
|
type: 'workspace',
|
|
name: 'foo',
|
|
spec: 'file:packages/foo',
|
|
from: root,
|
|
})
|
|
|
|
const directWsVuln = new Vuln({
|
|
name: 'bar',
|
|
advisory: new MockAdvisory({
|
|
name: 'bar',
|
|
versions: ['1.2.2', '1.2.3', '1.2.4'],
|
|
range: '<=1.2.3',
|
|
}),
|
|
})
|
|
const indirectWsVuln = new Vuln({
|
|
name: 'baz',
|
|
advisory: new MockAdvisory({
|
|
name: 'baz',
|
|
versions: ['1.2.2', '1.2.3', '1.2.4'],
|
|
range: '<=1.2.3',
|
|
}),
|
|
})
|
|
// workspace dep vulns are direct vulnerabilities as well
|
|
t.equal(directWsVuln.isVulnerable(ws.children.get('bar')), true)
|
|
t.equal(directWsVuln.isDirect, true)
|
|
t.equal(indirectWsVuln.isVulnerable(ws.children.get('baz')), true)
|
|
t.equal(indirectWsVuln.isDirect, false)
|
|
|
|
const node = new Node({
|
|
path: '/path/to/node_modules/thing',
|
|
name: 'thing',
|
|
pkg: {
|
|
name: 'name',
|
|
version: '1.2.1',
|
|
},
|
|
parent: root,
|
|
})
|
|
|
|
// check twice to hit memoizing code path
|
|
t.equal(v.isVulnerable(node), true)
|
|
t.equal(v.isVulnerable(node), true)
|
|
t.equal(v.isDirect, true)
|
|
t.match(v.nodes, new Set([node]))
|
|
|
|
// make sure we don't infinitely loop when setting it to true
|
|
v.addVia(v2)
|
|
v2.fixAvailable = true
|
|
v.fixAvailable = true
|
|
v2.fixAvailable = true
|
|
v.deleteVia(v2)
|
|
|
|
v2.fixAvailable = { isSemVerMajor: false }
|
|
t.strictSame(v.fixAvailable, { isSemVerMajor: false })
|
|
v2.fixAvailable = true
|
|
t.strictSame(v.fixAvailable, { isSemVerMajor: false })
|
|
v2.fixAvailable = { isSemVerMajor: true }
|
|
t.strictSame(v.fixAvailable, { isSemVerMajor: true })
|
|
v2.fixAvailable = false
|
|
t.strictSame(v.fixAvailable, false)
|
|
|
|
t.matchSnapshot(JSON.stringify(v, 0, 2), 'json formatted after loading')
|
|
t.matchSnapshot(JSON.stringify(v2, 0, 2), 'json formatted metavuln')
|
|
|
|
const ok = new Node({
|
|
path: '/path/to/node_modules/thing',
|
|
pkg: {
|
|
name: 'name',
|
|
version: '3.2.0',
|
|
},
|
|
})
|
|
t.match(v.isVulnerable(ok), false)
|
|
|
|
const noVersion = new Node({
|
|
path: '/path/to/node_modules/thing',
|
|
pkg: {},
|
|
})
|
|
t.match(v.isVulnerable(noVersion), false, 'node without version, no opinion')
|
|
|
|
v2.deleteAdvisory(meta2)
|
|
v2.deleteAdvisory(meta)
|
|
t.strictSame([...v2.via], [], 'removing advisories removes via')
|
|
t.strictSame([...v.effects], [], 'removing via removes effect')
|
|
})
|