Skip to content

Commit

Permalink
feat: add :vuln pseudo class
Browse files Browse the repository at this point in the history
  • Loading branch information
wraithgar committed Feb 23, 2024
1 parent e0d574d commit 5541137
Show file tree
Hide file tree
Showing 3 changed files with 236 additions and 19 deletions.
42 changes: 42 additions & 0 deletions lib/index.js
Expand Up @@ -166,6 +166,46 @@ const fixupOutdated = astNode => {
}
}

const fixupVuln = astNode => {
const vulns = []
if (astNode.nodes.length) {
for (const selector of astNode.nodes) {
const vuln = {}
for (const node of selector.nodes) {
if (node.type !== 'attribute') {
throw Object.assign(
new Error(':vuln pseudo-class only accepts attribute matchers or "cwe" tag'),
{ code: 'EQUERYATTR' }
)
}
if (!['severity', 'cwe'].includes(node._attribute)) {
throw Object.assign(
new Error(':vuln pseudo-class only matches "severity" and "cwe" attributes'),
{ code: 'EQUERYATTR' }
)
}
if (!node.operator) {
node.operator = '='
node.value = '*'
}
if (node.operator !== '=') {
throw Object.assign(
new Error(':vuln pseudo-class attribute selector only accepts "=" operator', node),
{ code: 'EQUERYATTR' }
)
}
if (!vuln[node._attribute]) {
vuln[node._attribute] = []
}
vuln[node._attribute].push(node._value)
}
vulns.push(vuln)
}
astNode.vulns = vulns
astNode.nodes.length = 0
}
}

// a few of the supported ast nodes need to be tweaked in order to properly be
// interpreted as proper arborist query selectors, namely semver ranges from
// both ids and :semver pseudo-class selectors need to be translated from what
Expand All @@ -192,6 +232,8 @@ const transformAst = selector => {
return fixupTypes(nextAstNode)
case ':outdated':
return fixupOutdated(nextAstNode)
case ':vuln':
return fixupVuln(nextAstNode)
}
})
}
Expand Down
201 changes: 182 additions & 19 deletions tap-snapshots/test/index.js.test.cjs
Expand Up @@ -13905,14 +13905,18 @@ exports[`test/index.js TAP queries :vuln([cwe=400],[severity=medium]) > :vuln([c
},
"type": "pseudo",
"value": ":vuln",
"vulns": Object {
"cwe": Array [
"400",
],
"severity": Array [
"medium",
],
},
"vulns": Array [
Object {
"cwe": Array [
"400",
],
},
Object {
"severity": Array [
"medium",
],
},
],
},
],
"parent": <*ref_2>,
Expand Down Expand Up @@ -13951,6 +13955,81 @@ exports[`test/index.js TAP queries :vuln([cwe=400],[severity=medium]) > :vuln([c
}
`

exports[`test/index.js TAP queries :vuln([cwe]) > :vuln([cwe]) 1`] = `
&ref_2 Root {
"_error": Function (message, errorOptions),
"indexes": Object {},
"lastEach": 1,
"nodes": Array [
&ref_1 Selector {
"indexes": Object {},
"lastEach": 1,
"nodes": Array [
Pseudo {
"nodes": Array [],
"parent": <*ref_1>,
"source": Object {
"end": Object {
"column": 12,
"line": 1,
},
"start": Object {
"column": 1,
"line": 1,
},
},
"sourceIndex": 0,
"spaces": Object {
"after": "",
"before": "",
},
"type": "pseudo",
"value": ":vuln",
"vulns": Array [
Object {
"cwe": Array [
"*",
],
},
],
},
],
"parent": <*ref_2>,
"source": Object {
"end": Object {
"column": 12,
"line": 1,
},
"start": Object {
"column": 1,
"line": 1,
},
},
"spaces": Object {
"after": "",
"before": "",
},
"type": "selector",
},
],
"source": Object {
"end": Object {
"column": 12,
"line": 1,
},
"start": Object {
"column": 1,
"line": 1,
},
},
"spaces": Object {
"after": "",
"before": "",
},
"type": "root",
}
`

exports[`test/index.js TAP queries :vuln([severity=high]) > :vuln([severity=high]) 1`] = `
&ref_2 Root {
"_error": Function (message, errorOptions),
Expand Down Expand Up @@ -13981,11 +14060,13 @@ exports[`test/index.js TAP queries :vuln([severity=high]) > :vuln([severity=high
},
"type": "pseudo",
"value": ":vuln",
"vulns": Object {
"severity": Array [
"high",
],
},
"vulns": Array [
Object {
"severity": Array [
"high",
],
},
],
},
],
"parent": <*ref_2>,
Expand Down Expand Up @@ -14054,12 +14135,18 @@ exports[`test/index.js TAP queries :vuln([severity=high],[severity=medium]) > :v
},
"type": "pseudo",
"value": ":vuln",
"vulns": Object {
"severity": Array [
"high",
"medium",
],
},
"vulns": Array [
Object {
"severity": Array [
"high",
],
},
Object {
"severity": Array [
"medium",
],
},
],
},
],
"parent": <*ref_2>,
Expand Down Expand Up @@ -14098,6 +14185,82 @@ exports[`test/index.js TAP queries :vuln([severity=high],[severity=medium]) > :v
}
`

exports[`test/index.js TAP queries :vuln([severity=high][severity=medium]) > :vuln([severity=high][severity=medium]) 1`] = `
&ref_2 Root {
"_error": Function (message, errorOptions),
"indexes": Object {},
"lastEach": 1,
"nodes": Array [
&ref_1 Selector {
"indexes": Object {},
"lastEach": 1,
"nodes": Array [
Pseudo {
"nodes": Array [],
"parent": <*ref_1>,
"source": Object {
"end": Object {
"column": 39,
"line": 1,
},
"start": Object {
"column": 1,
"line": 1,
},
},
"sourceIndex": 0,
"spaces": Object {
"after": "",
"before": "",
},
"type": "pseudo",
"value": ":vuln",
"vulns": Array [
Object {
"severity": Array [
"high",
"medium",
],
},
],
},
],
"parent": <*ref_2>,
"source": Object {
"end": Object {
"column": 39,
"line": 1,
},
"start": Object {
"column": 1,
"line": 1,
},
},
"spaces": Object {
"after": "",
"before": "",
},
"type": "selector",
},
],
"source": Object {
"end": Object {
"column": 39,
"line": 1,
},
"start": Object {
"column": 1,
"line": 1,
},
},
"spaces": Object {
"after": "",
"before": "",
},
"type": "root",
}
`

exports[`test/index.js TAP queries > #a > > #a 1`] = `
&ref_2 Root {
"_error": Function (message, errorOptions),
Expand Down
12 changes: 12 additions & 0 deletions test/index.js
@@ -1,4 +1,5 @@
'use strict'
/* eslint-disable max-len */

const t = require('tap')
const { parser } = require('../lib/index.js')
Expand Down Expand Up @@ -125,6 +126,14 @@ const checks = [
[':outdated'],
[':outdated(any)'],

// :vuln pseudo
[':vuln'],
[':vuln([cwe])'],
[':vuln([severity=high])'],
[':vuln([severity=high],[severity=medium])'],
[':vuln([severity=high][severity=medium])'],
[':vuln([cwe=400],[severity=medium])'],

// attribute matchers
['[name]'],
['[name=a]'],
Expand Down Expand Up @@ -188,6 +197,9 @@ const throws = [
[':attr(foo, bar)', { code: 'EQUERYATTR' }, 'missing attribute matcher on :attr pseudo-class'],
[':semver(14, [version], [version])', { code: 'ESEMVERFUNC' }, 'third :semver param is not a tag or string'],
[':semver([version], [version])', { code: 'ESEMVERVALUE' }, 'should throw when neither of the first :semver params is a static value'],
[':vuln(.prod)', { code: 'EQUERYATTR' }, ':vuln pseudo-class only accepts attribute matchers'],
[':vuln([description=asdf])', { code: 'EQUERYATTR' }, ':vuln pseudo-class only matches "severity" and "cwe" attributes'],
[':vuln([severity~=medium])', { code: 'EQUERYATTR' }, ':vuln pseudo-class severity selector only accepts "=" operator'],
]

t.test('queries', t => {
Expand Down

0 comments on commit 5541137

Please sign in to comment.