@vltpkg/query 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +10 -12
- package/skills/dss-query/REFERENCE.md +0 -125
- package/skills/dss-query/SKILL.md +0 -190
- package/skills/dss-query/evals/README.md +0 -81
- package/skills/dss-query/evals/evals.json +0 -89
- package/skills/dss-query/evals/grade.mjs +0 -481
- package/src/attribute.ts +0 -190
- package/src/combinator.ts +0 -135
- package/src/error.ts +0 -78
- package/src/id.ts +0 -46
- package/src/index.ts +0 -670
- package/src/parser.ts +0 -122
- package/src/pseudo/abandoned.ts +0 -9
- package/src/pseudo/attr.ts +0 -92
- package/src/pseudo/built.ts +0 -19
- package/src/pseudo/confused.ts +0 -29
- package/src/pseudo/cve.ts +0 -93
- package/src/pseudo/cwe.ts +0 -97
- package/src/pseudo/debug.ts +0 -9
- package/src/pseudo/deprecated.ts +0 -9
- package/src/pseudo/dev.ts +0 -18
- package/src/pseudo/diff.ts +0 -90
- package/src/pseudo/dist.ts +0 -148
- package/src/pseudo/dynamic.ts +0 -9
- package/src/pseudo/empty.ts +0 -15
- package/src/pseudo/entropic.ts +0 -9
- package/src/pseudo/env.ts +0 -6
- package/src/pseudo/eval.ts +0 -9
- package/src/pseudo/fs.ts +0 -9
- package/src/pseudo/helpers.ts +0 -106
- package/src/pseudo/host.ts +0 -111
- package/src/pseudo/hostname.ts +0 -156
- package/src/pseudo/license.ts +0 -134
- package/src/pseudo/link.ts +0 -30
- package/src/pseudo/malware.ts +0 -41
- package/src/pseudo/minified.ts +0 -9
- package/src/pseudo/missing.ts +0 -16
- package/src/pseudo/native.ts +0 -9
- package/src/pseudo/network.ts +0 -9
- package/src/pseudo/obfuscated.ts +0 -9
- package/src/pseudo/optional.ts +0 -18
- package/src/pseudo/outdated.ts +0 -345
- package/src/pseudo/overridden.ts +0 -20
- package/src/pseudo/path.ts +0 -146
- package/src/pseudo/peer.ts +0 -18
- package/src/pseudo/prerelease.ts +0 -47
- package/src/pseudo/private.ts +0 -19
- package/src/pseudo/prod.ts +0 -18
- package/src/pseudo/published.ts +0 -250
- package/src/pseudo/registry.ts +0 -29
- package/src/pseudo/root.ts +0 -19
- package/src/pseudo/scanned.ts +0 -20
- package/src/pseudo/score.ts +0 -186
- package/src/pseudo/scripts.ts +0 -61
- package/src/pseudo/semver.ts +0 -320
- package/src/pseudo/severity.ts +0 -231
- package/src/pseudo/shell.ts +0 -9
- package/src/pseudo/shrinkwrap.ts +0 -9
- package/src/pseudo/skill.ts +0 -59
- package/src/pseudo/skills.ts +0 -29
- package/src/pseudo/spec.ts +0 -131
- package/src/pseudo/squat.ts +0 -225
- package/src/pseudo/suspicious.ts +0 -9
- package/src/pseudo/tracker.ts +0 -9
- package/src/pseudo/trivial.ts +0 -9
- package/src/pseudo/type.ts +0 -26
- package/src/pseudo/undesirable.ts +0 -9
- package/src/pseudo/unknown.ts +0 -9
- package/src/pseudo/unmaintained.ts +0 -9
- package/src/pseudo/unpopular.ts +0 -9
- package/src/pseudo/unstable.ts +0 -9
- package/src/pseudo/vuln.ts +0 -273
- package/src/pseudo/workspace.ts +0 -22
- package/src/pseudo.ts +0 -440
- package/src/types.ts +0 -206
- package/tap-snapshots/test/attribute.ts.test.cjs +0 -304
- package/tap-snapshots/test/combinator.ts.test.cjs +0 -333
- package/tap-snapshots/test/id.ts.test.cjs +0 -118
- package/tap-snapshots/test/parser.ts.test.cjs +0 -344
- package/tap-snapshots/test/pseudo/abandoned.ts.test.cjs +0 -18
- package/tap-snapshots/test/pseudo/attr.ts.test.cjs +0 -161
- package/tap-snapshots/test/pseudo/built.ts.test.cjs +0 -70
- package/tap-snapshots/test/pseudo/confused.ts.test.cjs +0 -32
- package/tap-snapshots/test/pseudo/cve.ts.test.cjs +0 -49
- package/tap-snapshots/test/pseudo/cwe.ts.test.cjs +0 -49
- package/tap-snapshots/test/pseudo/debug.ts.test.cjs +0 -18
- package/tap-snapshots/test/pseudo/deprecated.ts.test.cjs +0 -18
- package/tap-snapshots/test/pseudo/dist.ts.test.cjs +0 -30
- package/tap-snapshots/test/pseudo/dynamic.ts.test.cjs +0 -18
- package/tap-snapshots/test/pseudo/empty.ts.test.cjs +0 -50
- package/tap-snapshots/test/pseudo/entropic.ts.test.cjs +0 -18
- package/tap-snapshots/test/pseudo/env.ts.test.cjs +0 -18
- package/tap-snapshots/test/pseudo/eval.ts.test.cjs +0 -18
- package/tap-snapshots/test/pseudo/fs.ts.test.cjs +0 -18
- package/tap-snapshots/test/pseudo/helpers.ts.test.cjs +0 -18
- package/tap-snapshots/test/pseudo/hostname.ts.test.cjs +0 -148
- package/tap-snapshots/test/pseudo/license.ts.test.cjs +0 -41
- package/tap-snapshots/test/pseudo/link.ts.test.cjs +0 -36
- package/tap-snapshots/test/pseudo/malware.ts.test.cjs +0 -22
- package/tap-snapshots/test/pseudo/minified.ts.test.cjs +0 -18
- package/tap-snapshots/test/pseudo/missing.ts.test.cjs +0 -30
- package/tap-snapshots/test/pseudo/native.ts.test.cjs +0 -18
- package/tap-snapshots/test/pseudo/network.ts.test.cjs +0 -18
- package/tap-snapshots/test/pseudo/obfuscated.ts.test.cjs +0 -18
- package/tap-snapshots/test/pseudo/outdated.ts.test.cjs +0 -132
- package/tap-snapshots/test/pseudo/overridden.ts.test.cjs +0 -75
- package/tap-snapshots/test/pseudo/prerelease.ts.test.cjs +0 -101
- package/tap-snapshots/test/pseudo/private.ts.test.cjs +0 -38
- package/tap-snapshots/test/pseudo/published.ts.test.cjs +0 -171
- package/tap-snapshots/test/pseudo/registry.ts.test.cjs +0 -68
- package/tap-snapshots/test/pseudo/root.ts.test.cjs +0 -24
- package/tap-snapshots/test/pseudo/scanned.ts.test.cjs +0 -18
- package/tap-snapshots/test/pseudo/score.ts.test.cjs +0 -239
- package/tap-snapshots/test/pseudo/scripts.ts.test.cjs +0 -62
- package/tap-snapshots/test/pseudo/semver.ts.test.cjs +0 -575
- package/tap-snapshots/test/pseudo/severity.ts.test.cjs +0 -122
- package/tap-snapshots/test/pseudo/shell.ts.test.cjs +0 -18
- package/tap-snapshots/test/pseudo/shrinkwrap.ts.test.cjs +0 -18
- package/tap-snapshots/test/pseudo/skills.ts.test.cjs +0 -19
- package/tap-snapshots/test/pseudo/spec.ts.test.cjs +0 -148
- package/tap-snapshots/test/pseudo/squat.ts.test.cjs +0 -129
- package/tap-snapshots/test/pseudo/suspicious.ts.test.cjs +0 -18
- package/tap-snapshots/test/pseudo/tracker.ts.test.cjs +0 -18
- package/tap-snapshots/test/pseudo/trivial.ts.test.cjs +0 -18
- package/tap-snapshots/test/pseudo/type.ts.test.cjs +0 -51
- package/tap-snapshots/test/pseudo/undesirable.ts.test.cjs +0 -18
- package/tap-snapshots/test/pseudo/unknown.ts.test.cjs +0 -18
- package/tap-snapshots/test/pseudo/unmaintained.ts.test.cjs +0 -18
- package/tap-snapshots/test/pseudo/unpopular.ts.test.cjs +0 -18
- package/tap-snapshots/test/pseudo/unstable.ts.test.cjs +0 -18
- package/tap-snapshots/test/pseudo/vuln.ts.test.cjs +0 -208
- package/tap-snapshots/test/pseudo/vulnerable.ts.test.cjs +0 -20
- package/tap-snapshots/test/pseudo/workspace.ts.test.cjs +0 -19
- package/tap-snapshots/test/pseudo.ts.test.cjs +0 -1247
- package/test/attribute.ts +0 -273
- package/test/combinator.ts +0 -175
- package/test/error.ts +0 -60
- package/test/fixtures/graph.ts +0 -999
- package/test/fixtures/selector.ts +0 -103
- package/test/fixtures/types.ts +0 -7
- package/test/id.ts +0 -102
- package/test/index.ts +0 -1148
- package/test/parser.ts +0 -117
- package/test/pseudo/abandoned.ts +0 -98
- package/test/pseudo/attr.ts +0 -298
- package/test/pseudo/built.ts +0 -214
- package/test/pseudo/confused.ts +0 -160
- package/test/pseudo/cve.ts +0 -249
- package/test/pseudo/cwe.ts +0 -255
- package/test/pseudo/debug.ts +0 -98
- package/test/pseudo/deprecated.ts +0 -98
- package/test/pseudo/dev.ts +0 -61
- package/test/pseudo/diff.ts +0 -417
- package/test/pseudo/dist.ts +0 -215
- package/test/pseudo/dynamic.ts +0 -98
- package/test/pseudo/empty.ts +0 -108
- package/test/pseudo/entropic.ts +0 -101
- package/test/pseudo/env.ts +0 -98
- package/test/pseudo/eval.ts +0 -98
- package/test/pseudo/fs.ts +0 -98
- package/test/pseudo/helpers.ts +0 -370
- package/test/pseudo/host-context.ts +0 -276
- package/test/pseudo/hostname.ts +0 -314
- package/test/pseudo/license.ts +0 -265
- package/test/pseudo/link.ts +0 -94
- package/test/pseudo/malware.ts +0 -184
- package/test/pseudo/minified.ts +0 -98
- package/test/pseudo/missing.ts +0 -111
- package/test/pseudo/native.ts +0 -98
- package/test/pseudo/network.ts +0 -98
- package/test/pseudo/obfuscated.ts +0 -98
- package/test/pseudo/optional.ts +0 -73
- package/test/pseudo/outdated.ts +0 -461
- package/test/pseudo/overridden.ts +0 -317
- package/test/pseudo/path.ts +0 -680
- package/test/pseudo/peer.ts +0 -101
- package/test/pseudo/prerelease.ts +0 -279
- package/test/pseudo/private.ts +0 -108
- package/test/pseudo/prod.ts +0 -61
- package/test/pseudo/published.ts +0 -557
- package/test/pseudo/registry.ts +0 -122
- package/test/pseudo/root.ts +0 -66
- package/test/pseudo/scanned.ts +0 -144
- package/test/pseudo/score.ts +0 -591
- package/test/pseudo/scripts.ts +0 -324
- package/test/pseudo/semver.ts +0 -822
- package/test/pseudo/severity.ts +0 -342
- package/test/pseudo/shell.ts +0 -98
- package/test/pseudo/shrinkwrap.ts +0 -98
- package/test/pseudo/skill.ts +0 -189
- package/test/pseudo/skills.ts +0 -90
- package/test/pseudo/spec.ts +0 -525
- package/test/pseudo/squat.ts +0 -565
- package/test/pseudo/suspicious.ts +0 -98
- package/test/pseudo/tracker.ts +0 -98
- package/test/pseudo/trivial.ts +0 -98
- package/test/pseudo/type.ts +0 -105
- package/test/pseudo/undesirable.ts +0 -98
- package/test/pseudo/unknown.ts +0 -98
- package/test/pseudo/unmaintained.ts +0 -98
- package/test/pseudo/unpopular.ts +0 -98
- package/test/pseudo/unstable.ts +0 -98
- package/test/pseudo/vuln.ts +0 -625
- package/test/pseudo/vulnerable.ts +0 -137
- package/test/pseudo/workspace.ts +0 -111
- package/test/pseudo.ts +0 -584
- package/tsconfig.json +0 -3
- package/tsconfig.publish.json +0 -19
- package/tsconfig.publish.tsbuildinfo +0 -1
- package/typedoc.mjs +0 -2
package/src/index.ts
DELETED
|
@@ -1,670 +0,0 @@
|
|
|
1
|
-
import { joinDepIDTuple } from '@vltpkg/dep-id/browser'
|
|
2
|
-
import {
|
|
3
|
-
parse,
|
|
4
|
-
partPseudoNames,
|
|
5
|
-
isPostcssNodeWithChildren,
|
|
6
|
-
asPostcssNodeWithChildren,
|
|
7
|
-
isSelectorNode,
|
|
8
|
-
isPseudoNode,
|
|
9
|
-
isIdentifierNode,
|
|
10
|
-
isAttributeNode,
|
|
11
|
-
} from '@vltpkg/dss-parser'
|
|
12
|
-
import { attribute } from './attribute.ts'
|
|
13
|
-
import { combinator } from './combinator.ts'
|
|
14
|
-
import { id } from './id.ts'
|
|
15
|
-
import { pseudo, pseudoSelectorNames } from './pseudo.ts'
|
|
16
|
-
import { queryError, selectorText } from './error.ts'
|
|
17
|
-
import type { EdgeLike, NodeLike } from '@vltpkg/types'
|
|
18
|
-
import type { SecurityArchiveLike } from '@vltpkg/security-archive'
|
|
19
|
-
import type {
|
|
20
|
-
PostcssNode,
|
|
21
|
-
PostcssNodeWithChildren,
|
|
22
|
-
PartPseudoName,
|
|
23
|
-
} from '@vltpkg/dss-parser'
|
|
24
|
-
import type {
|
|
25
|
-
DiffFilesProvider,
|
|
26
|
-
GetAuthHeader,
|
|
27
|
-
HasSkills,
|
|
28
|
-
HostContextsMap,
|
|
29
|
-
ParsedSelectorToken,
|
|
30
|
-
ParserState,
|
|
31
|
-
ParserFn,
|
|
32
|
-
QueryPart,
|
|
33
|
-
QueryResponse,
|
|
34
|
-
QueryResponseNode,
|
|
35
|
-
QueryResponseEdge,
|
|
36
|
-
} from './types.ts'
|
|
37
|
-
import type { DepID } from '@vltpkg/dep-id'
|
|
38
|
-
|
|
39
|
-
export * from './types.ts'
|
|
40
|
-
|
|
41
|
-
export type SearchOptions = {
|
|
42
|
-
signal: AbortSignal
|
|
43
|
-
scopeIDs?: DepID[]
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const noopFn = async (state: ParserState) => state
|
|
47
|
-
const partPseudos = new Set<string>(partPseudoNames)
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* `.dev` and a bare `dev` are the two ways a css habit shows up in a
|
|
51
|
-
* query. Neither is valid DSS, but the intent is usually a pseudo-class
|
|
52
|
-
* of that name, and otherwise a package name.
|
|
53
|
-
*/
|
|
54
|
-
const unsupportedName = (state: ParserState) => {
|
|
55
|
-
const text = selectorText(state.current)
|
|
56
|
-
const name = text.replace(/^[.#]/, '')
|
|
57
|
-
return queryError('Unsupported selector', state.current, {
|
|
58
|
-
wanted:
|
|
59
|
-
pseudoSelectorNames.has(name) && !partPseudos.has(name) ?
|
|
60
|
-
`:${name}`
|
|
61
|
-
: name ? `#${name}`
|
|
62
|
-
: undefined,
|
|
63
|
-
})
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const selectors = {
|
|
67
|
-
attribute,
|
|
68
|
-
/* c8 ignore start -- dss-parser reports `.foo` as an escaped tag */
|
|
69
|
-
class: async (state: ParserState) => {
|
|
70
|
-
throw unsupportedName(state)
|
|
71
|
-
},
|
|
72
|
-
/* c8 ignore end */
|
|
73
|
-
combinator,
|
|
74
|
-
comment: async (state: ParserState) => {
|
|
75
|
-
if (state.current.value && !state.comment) {
|
|
76
|
-
const commentValue = state.current.value
|
|
77
|
-
const cleanComment = commentValue
|
|
78
|
-
.replace(/^\/\*/, '')
|
|
79
|
-
.replace(/\*\/$/, '')
|
|
80
|
-
.trim()
|
|
81
|
-
state.comment = cleanComment
|
|
82
|
-
}
|
|
83
|
-
return state
|
|
84
|
-
},
|
|
85
|
-
id,
|
|
86
|
-
nesting: noopFn,
|
|
87
|
-
pseudo,
|
|
88
|
-
root: noopFn,
|
|
89
|
-
selector: async (state: ParserState) => {
|
|
90
|
-
state.partial.nodes = new Set(state.initial.nodes)
|
|
91
|
-
state.partial.edges = new Set(state.initial.edges)
|
|
92
|
-
return state
|
|
93
|
-
},
|
|
94
|
-
string: async (state: ParserState) => {
|
|
95
|
-
throw queryError('Unsupported selector', state.current)
|
|
96
|
-
},
|
|
97
|
-
tag: async (state: ParserState) => {
|
|
98
|
-
if (state.current.value !== '{' && state.current.value !== '}') {
|
|
99
|
-
throw unsupportedName(state)
|
|
100
|
-
}
|
|
101
|
-
return state
|
|
102
|
-
},
|
|
103
|
-
universal: noopFn,
|
|
104
|
-
}
|
|
105
|
-
const selectorsMap = new Map<string, ParserFn>(
|
|
106
|
-
Object.entries(selectors),
|
|
107
|
-
)
|
|
108
|
-
|
|
109
|
-
export const walk = async (
|
|
110
|
-
state: ParserState,
|
|
111
|
-
): Promise<ParserState> => {
|
|
112
|
-
await state.cancellable()
|
|
113
|
-
|
|
114
|
-
const parserFn = selectorsMap.get(state.current.type)
|
|
115
|
-
|
|
116
|
-
if (!parserFn) {
|
|
117
|
-
if (state.loose) {
|
|
118
|
-
return state
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
throw queryError(
|
|
122
|
-
`Missing parser for query node: ${state.current.type}`,
|
|
123
|
-
state.current,
|
|
124
|
-
)
|
|
125
|
-
}
|
|
126
|
-
state = await parserFn(state)
|
|
127
|
-
|
|
128
|
-
// pseudo selectors handle their own sub selectors
|
|
129
|
-
if (
|
|
130
|
-
isPostcssNodeWithChildren(state.current) &&
|
|
131
|
-
state.current.type !== 'pseudo'
|
|
132
|
-
) {
|
|
133
|
-
const node: PostcssNodeWithChildren = asPostcssNodeWithChildren(
|
|
134
|
-
state.current,
|
|
135
|
-
)
|
|
136
|
-
|
|
137
|
-
if (node.nodes.length) {
|
|
138
|
-
for (let i = 0; i < node.nodes.length; i++) {
|
|
139
|
-
const current = node.nodes[i]
|
|
140
|
-
/* c8 ignore next -- impossible but TS doesn't know that */
|
|
141
|
-
if (!current) continue
|
|
142
|
-
|
|
143
|
-
const childState: ParserState = {
|
|
144
|
-
...state,
|
|
145
|
-
current,
|
|
146
|
-
next: node.nodes[i + 1],
|
|
147
|
-
prev: node.nodes[i - 1],
|
|
148
|
-
}
|
|
149
|
-
state = await walk(childState)
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if (isSelectorNode(node)) {
|
|
154
|
-
for (const edge of state.partial.edges) {
|
|
155
|
-
state.collect.edges.add(edge)
|
|
156
|
-
}
|
|
157
|
-
for (const node of state.partial.nodes) {
|
|
158
|
-
state.collect.nodes.add(node)
|
|
159
|
-
}
|
|
160
|
-
if (state.parts && node.parent?.type === 'root') {
|
|
161
|
-
state.parts.push({
|
|
162
|
-
...state.part,
|
|
163
|
-
nodes: [...state.partial.nodes],
|
|
164
|
-
})
|
|
165
|
-
delete state.part
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
return state
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
export type QueryOptions = {
|
|
173
|
-
edges: Set<EdgeLike>
|
|
174
|
-
nodes: Set<NodeLike>
|
|
175
|
-
importers: Set<NodeLike>
|
|
176
|
-
retries?: number
|
|
177
|
-
securityArchive: SecurityArchiveLike | undefined
|
|
178
|
-
hostContexts?: HostContextsMap
|
|
179
|
-
diffFiles?: DiffFilesProvider
|
|
180
|
-
getAuthHeader?: GetAuthHeader
|
|
181
|
-
/** for `:skills`; unset: `:skills` throws */
|
|
182
|
-
hasSkills?: HasSkills
|
|
183
|
-
/** part selectors to allow, e.g. `['skill']`; others throw */
|
|
184
|
-
partSelectors?: readonly PartPseudoName[]
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// A list of known security selectors that rely on
|
|
188
|
-
// data from the security-archive in order to work
|
|
189
|
-
const securitySelectors = new Set([
|
|
190
|
-
':abandoned',
|
|
191
|
-
':confused',
|
|
192
|
-
':cve',
|
|
193
|
-
':cwe',
|
|
194
|
-
':debug',
|
|
195
|
-
':deprecated',
|
|
196
|
-
':dynamic',
|
|
197
|
-
':entropic',
|
|
198
|
-
':env',
|
|
199
|
-
':eval',
|
|
200
|
-
':fs',
|
|
201
|
-
':license',
|
|
202
|
-
':malware',
|
|
203
|
-
':minified',
|
|
204
|
-
':native',
|
|
205
|
-
':network',
|
|
206
|
-
':obfuscated',
|
|
207
|
-
':scanned',
|
|
208
|
-
':score',
|
|
209
|
-
':sev',
|
|
210
|
-
':severity',
|
|
211
|
-
':shell',
|
|
212
|
-
':shrinkwrap',
|
|
213
|
-
':squat',
|
|
214
|
-
':suspicious',
|
|
215
|
-
':tracker',
|
|
216
|
-
':trivial',
|
|
217
|
-
':undesirable',
|
|
218
|
-
':unknown',
|
|
219
|
-
':unmaintained',
|
|
220
|
-
':unpopular',
|
|
221
|
-
':unstable',
|
|
222
|
-
':vuln',
|
|
223
|
-
':vulnerable',
|
|
224
|
-
])
|
|
225
|
-
|
|
226
|
-
const setMethodToJSON = (node: QueryResponseNode) => {
|
|
227
|
-
const { toJSON } = node
|
|
228
|
-
const insights = node.insights
|
|
229
|
-
node.toJSON = () => ({
|
|
230
|
-
...toJSON.call(node),
|
|
231
|
-
insights,
|
|
232
|
-
})
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* The Query class is used to search the graph for nodes and edges
|
|
237
|
-
* using the Dependency Selector Syntax (DSS).
|
|
238
|
-
*/
|
|
239
|
-
export class Query {
|
|
240
|
-
#cache: Map<string, QueryResponse>
|
|
241
|
-
#diffFiles: DiffFilesProvider | undefined
|
|
242
|
-
#getAuthHeader: GetAuthHeader | undefined
|
|
243
|
-
#hasSkills: HasSkills | undefined
|
|
244
|
-
#edges: Set<EdgeLike>
|
|
245
|
-
#nodes: Set<NodeLike>
|
|
246
|
-
#importers: Set<NodeLike>
|
|
247
|
-
#retries: number
|
|
248
|
-
#securityArchive: SecurityArchiveLike | undefined
|
|
249
|
-
#hostContexts: HostContextsMap | undefined
|
|
250
|
-
#partSelectors: ReadonlySet<PartPseudoName>
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* Helper method to determine if a given query string is using any of
|
|
254
|
-
* the known security selectors. This is useful so that operations can
|
|
255
|
-
* skip hydrating the security archive if it's not needed.
|
|
256
|
-
*/
|
|
257
|
-
static hasSecuritySelectors(query: string): boolean {
|
|
258
|
-
for (const selector of securitySelectors) {
|
|
259
|
-
if (query.includes(selector)) {
|
|
260
|
-
return true
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
return false
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* Sorts an array of QueryResponse objects by specificity. Objects with
|
|
268
|
-
* higher idCounter values come first, if idCounter values are equal,
|
|
269
|
-
* then objects with higher commonCounter values come first. Otherwise,
|
|
270
|
-
* the original order is preserved.
|
|
271
|
-
*/
|
|
272
|
-
static specificitySort(
|
|
273
|
-
responses: QueryResponse[],
|
|
274
|
-
): QueryResponse[] {
|
|
275
|
-
return [...responses].sort((a, b) => {
|
|
276
|
-
// First compare by idCounter (higher comes first)
|
|
277
|
-
if (a.specificity.idCounter !== b.specificity.idCounter) {
|
|
278
|
-
return b.specificity.idCounter - a.specificity.idCounter
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// If idCounter values are equal, compare by commonCounter
|
|
282
|
-
if (
|
|
283
|
-
a.specificity.commonCounter !== b.specificity.commonCounter
|
|
284
|
-
) {
|
|
285
|
-
return (
|
|
286
|
-
b.specificity.commonCounter - a.specificity.commonCounter
|
|
287
|
-
)
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// If both counters are equal, preserve original order
|
|
291
|
-
return 0
|
|
292
|
-
})
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
constructor({
|
|
296
|
-
edges,
|
|
297
|
-
nodes,
|
|
298
|
-
importers,
|
|
299
|
-
retries,
|
|
300
|
-
securityArchive,
|
|
301
|
-
hostContexts,
|
|
302
|
-
diffFiles,
|
|
303
|
-
getAuthHeader,
|
|
304
|
-
hasSkills,
|
|
305
|
-
partSelectors,
|
|
306
|
-
}: QueryOptions) {
|
|
307
|
-
this.#partSelectors = new Set(partSelectors)
|
|
308
|
-
this.#cache = new Map()
|
|
309
|
-
this.#diffFiles = diffFiles
|
|
310
|
-
this.#getAuthHeader = getAuthHeader
|
|
311
|
-
this.#hasSkills = hasSkills
|
|
312
|
-
this.#edges = edges
|
|
313
|
-
this.#nodes = nodes
|
|
314
|
-
this.#importers = importers
|
|
315
|
-
this.#retries = retries ?? 3
|
|
316
|
-
this.#securityArchive = securityArchive
|
|
317
|
-
this.#hostContexts = hostContexts
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
#getQueryResponseEdges(_edges: Set<EdgeLike>): QueryResponseEdge[] {
|
|
321
|
-
return Array.from(_edges) as QueryResponseEdge[]
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
#getQueryResponseNodes(_nodes: Set<NodeLike>): QueryResponseNode[] {
|
|
325
|
-
const nodes = Array.from(_nodes) as QueryResponseNode[]
|
|
326
|
-
for (const node of nodes) {
|
|
327
|
-
const securityArchiveEntry = this.#securityArchive?.get(node.id)
|
|
328
|
-
|
|
329
|
-
// if a security archive entry is not found then the insights object
|
|
330
|
-
// should just be empty with scanned=false
|
|
331
|
-
if (!securityArchiveEntry) {
|
|
332
|
-
node.insights = {
|
|
333
|
-
scanned: false,
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
setMethodToJSON(node)
|
|
337
|
-
continue
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// if a security archive entry is found then we can populate the insights
|
|
341
|
-
node.insights = {
|
|
342
|
-
scanned: true,
|
|
343
|
-
score: securityArchiveEntry.score,
|
|
344
|
-
abandoned: securityArchiveEntry.alerts.some(
|
|
345
|
-
i => i.type === 'missingAuthor',
|
|
346
|
-
),
|
|
347
|
-
confused: securityArchiveEntry.alerts.some(
|
|
348
|
-
i => i.type === 'manifestConfusion',
|
|
349
|
-
),
|
|
350
|
-
cve: securityArchiveEntry.alerts
|
|
351
|
-
.map(i => i.props?.cveId)
|
|
352
|
-
.filter(i => i !== undefined),
|
|
353
|
-
cwe: Array.from(
|
|
354
|
-
new Set(
|
|
355
|
-
securityArchiveEntry.alerts
|
|
356
|
-
.filter(i => i.props?.cveId)
|
|
357
|
-
.flatMap(i => i.props?.cwes?.map(j => j.id)),
|
|
358
|
-
),
|
|
359
|
-
) as `CWE-${string}`[],
|
|
360
|
-
debug: securityArchiveEntry.alerts.some(
|
|
361
|
-
i => i.type === 'debugAccess',
|
|
362
|
-
),
|
|
363
|
-
deprecated: securityArchiveEntry.alerts.some(
|
|
364
|
-
i => i.type === 'deprecated',
|
|
365
|
-
),
|
|
366
|
-
dynamic: securityArchiveEntry.alerts.some(
|
|
367
|
-
i => i.type === 'dynamicRequire',
|
|
368
|
-
),
|
|
369
|
-
entropic: securityArchiveEntry.alerts.some(
|
|
370
|
-
i => i.type === 'highEntropyStrings',
|
|
371
|
-
),
|
|
372
|
-
env: securityArchiveEntry.alerts.some(
|
|
373
|
-
i => i.type === 'envVars',
|
|
374
|
-
),
|
|
375
|
-
eval: securityArchiveEntry.alerts.some(
|
|
376
|
-
i => i.type === 'usesEval',
|
|
377
|
-
),
|
|
378
|
-
fs: securityArchiveEntry.alerts.some(
|
|
379
|
-
i => i.type === 'filesystemAccess',
|
|
380
|
-
),
|
|
381
|
-
license: {
|
|
382
|
-
unlicensed: securityArchiveEntry.alerts.some(
|
|
383
|
-
i => i.type === 'explicitlyUnlicensedItem',
|
|
384
|
-
),
|
|
385
|
-
misc: securityArchiveEntry.alerts.some(
|
|
386
|
-
i => i.type === 'miscLicenseIssues',
|
|
387
|
-
),
|
|
388
|
-
restricted: securityArchiveEntry.alerts.some(
|
|
389
|
-
i => i.type === 'nonpermissiveLicense',
|
|
390
|
-
),
|
|
391
|
-
ambiguous: securityArchiveEntry.alerts.some(
|
|
392
|
-
i => i.type === 'ambiguousClassifier',
|
|
393
|
-
),
|
|
394
|
-
copyleft: securityArchiveEntry.alerts.some(
|
|
395
|
-
i => i.type === 'copyleftLicense',
|
|
396
|
-
),
|
|
397
|
-
unknown: securityArchiveEntry.alerts.some(
|
|
398
|
-
i => i.type === 'unidentifiedLicense',
|
|
399
|
-
),
|
|
400
|
-
none: securityArchiveEntry.alerts.some(
|
|
401
|
-
i => i.type === 'noLicenseFound',
|
|
402
|
-
),
|
|
403
|
-
exception: securityArchiveEntry.alerts.some(
|
|
404
|
-
i => i.type === 'licenseException',
|
|
405
|
-
),
|
|
406
|
-
},
|
|
407
|
-
malware: securityArchiveEntry.alerts.some(
|
|
408
|
-
i => i.type === 'malware' || i.type === 'gptMalware',
|
|
409
|
-
),
|
|
410
|
-
minified: securityArchiveEntry.alerts.some(
|
|
411
|
-
i => i.type === 'minifiedFile',
|
|
412
|
-
),
|
|
413
|
-
native: securityArchiveEntry.alerts.some(
|
|
414
|
-
i => i.type === 'hasNativeCode',
|
|
415
|
-
),
|
|
416
|
-
network: securityArchiveEntry.alerts.some(
|
|
417
|
-
i => i.type === 'networkAccess',
|
|
418
|
-
),
|
|
419
|
-
obfuscated: securityArchiveEntry.alerts.some(
|
|
420
|
-
i => i.type === 'obfuscatedFile',
|
|
421
|
-
),
|
|
422
|
-
scripts: securityArchiveEntry.alerts.some(
|
|
423
|
-
i => i.type === 'installScripts',
|
|
424
|
-
),
|
|
425
|
-
severity: {
|
|
426
|
-
low: securityArchiveEntry.alerts.some(
|
|
427
|
-
i => i.type === 'mildCVE',
|
|
428
|
-
),
|
|
429
|
-
medium: securityArchiveEntry.alerts.some(
|
|
430
|
-
i => i.type === 'potentialVulnerability',
|
|
431
|
-
),
|
|
432
|
-
high: securityArchiveEntry.alerts.some(
|
|
433
|
-
i => i.type === 'cve',
|
|
434
|
-
),
|
|
435
|
-
critical: securityArchiveEntry.alerts.some(
|
|
436
|
-
i => i.type === 'criticalCVE',
|
|
437
|
-
),
|
|
438
|
-
},
|
|
439
|
-
shell: securityArchiveEntry.alerts.some(
|
|
440
|
-
i => i.type === 'shellAccess',
|
|
441
|
-
),
|
|
442
|
-
shrinkwrap: securityArchiveEntry.alerts.some(
|
|
443
|
-
i => i.type === 'shrinkwrap',
|
|
444
|
-
),
|
|
445
|
-
squat: {
|
|
446
|
-
medium: securityArchiveEntry.alerts.some(
|
|
447
|
-
i => i.type === 'gptDidYouMean',
|
|
448
|
-
),
|
|
449
|
-
critical: securityArchiveEntry.alerts.some(
|
|
450
|
-
i => i.type === 'didYouMean',
|
|
451
|
-
),
|
|
452
|
-
},
|
|
453
|
-
suspicious: securityArchiveEntry.alerts.some(
|
|
454
|
-
i => i.type === 'suspiciousStarActivity',
|
|
455
|
-
),
|
|
456
|
-
tracker: securityArchiveEntry.alerts.some(
|
|
457
|
-
i => i.type === 'telemetry',
|
|
458
|
-
),
|
|
459
|
-
trivial: securityArchiveEntry.alerts.some(
|
|
460
|
-
i => i.type === 'trivialPackage',
|
|
461
|
-
),
|
|
462
|
-
undesirable: securityArchiveEntry.alerts.some(
|
|
463
|
-
i => i.type === 'troll',
|
|
464
|
-
),
|
|
465
|
-
unknown: securityArchiveEntry.alerts.some(
|
|
466
|
-
i => i.type === 'newAuthor',
|
|
467
|
-
),
|
|
468
|
-
unmaintained: securityArchiveEntry.alerts.some(
|
|
469
|
-
i => i.type === 'unmaintained',
|
|
470
|
-
),
|
|
471
|
-
unpopular: securityArchiveEntry.alerts.some(
|
|
472
|
-
i => i.type === 'unpopularPackage',
|
|
473
|
-
),
|
|
474
|
-
unstable: securityArchiveEntry.alerts.some(
|
|
475
|
-
i => i.type === 'unstableOwnership',
|
|
476
|
-
),
|
|
477
|
-
vuln: {
|
|
478
|
-
low: securityArchiveEntry.alerts.some(
|
|
479
|
-
i => i.type === 'mildCVE' || i.type === 'gptAnomaly',
|
|
480
|
-
),
|
|
481
|
-
medium: securityArchiveEntry.alerts.some(
|
|
482
|
-
i =>
|
|
483
|
-
i.type === 'potentialVulnerability' ||
|
|
484
|
-
i.type === 'gptSecurity',
|
|
485
|
-
),
|
|
486
|
-
high: securityArchiveEntry.alerts.some(
|
|
487
|
-
i => i.type === 'cve',
|
|
488
|
-
),
|
|
489
|
-
critical: securityArchiveEntry.alerts.some(
|
|
490
|
-
i => i.type === 'criticalCVE',
|
|
491
|
-
),
|
|
492
|
-
},
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
setMethodToJSON(node)
|
|
496
|
-
}
|
|
497
|
-
return nodes
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
/**
|
|
501
|
-
* Search the graph for nodes and edges that match the given query.
|
|
502
|
-
*/
|
|
503
|
-
async search(
|
|
504
|
-
query: string,
|
|
505
|
-
{
|
|
506
|
-
signal,
|
|
507
|
-
scopeIDs = [joinDepIDTuple(['file', '.'])],
|
|
508
|
-
}: SearchOptions,
|
|
509
|
-
): Promise<QueryResponse> {
|
|
510
|
-
if (!query)
|
|
511
|
-
return {
|
|
512
|
-
edges: [],
|
|
513
|
-
nodes: [],
|
|
514
|
-
importers: [],
|
|
515
|
-
comment: '',
|
|
516
|
-
specificity: { idCounter: 0, commonCounter: 0 },
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
const cachedResult = this.#cache.get(query)
|
|
520
|
-
if (cachedResult) {
|
|
521
|
-
return cachedResult
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
const nodes = this.#nodes
|
|
525
|
-
const edges = this.#edges
|
|
526
|
-
const importers = this.#importers
|
|
527
|
-
|
|
528
|
-
// includes virtual workspace edges in the searched edges
|
|
529
|
-
for (const importer of importers) {
|
|
530
|
-
if (!importer.workspaces) continue
|
|
531
|
-
for (const edge of importer.workspaces.values()) {
|
|
532
|
-
edges.add(edge)
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
// parse the query string into AST
|
|
537
|
-
const current = parse(query, { loose: true })
|
|
538
|
-
// set loose mode for the entire parse in case there are multiple selectors
|
|
539
|
-
// so that using invalid pseudo selectors or other query language parser
|
|
540
|
-
// errors won't throw an error,
|
|
541
|
-
// e.g: `:root > *, #a, :foo` still returns results for `:root > ` and `#a`
|
|
542
|
-
// while :foo is ignored
|
|
543
|
-
const loose = asPostcssNodeWithChildren(current).nodes.length > 1
|
|
544
|
-
// builds initial state and walks over it,
|
|
545
|
-
// retrieving the collected result
|
|
546
|
-
const {
|
|
547
|
-
collect,
|
|
548
|
-
comment,
|
|
549
|
-
importers: stateResultImporters,
|
|
550
|
-
parts,
|
|
551
|
-
specificity,
|
|
552
|
-
} = await walk({
|
|
553
|
-
cancellable: async () => {
|
|
554
|
-
await new Promise(resolve => {
|
|
555
|
-
setTimeout(resolve, 0)
|
|
556
|
-
})
|
|
557
|
-
signal.throwIfAborted()
|
|
558
|
-
},
|
|
559
|
-
current,
|
|
560
|
-
initial: {
|
|
561
|
-
nodes: new Set(nodes),
|
|
562
|
-
edges: new Set(edges),
|
|
563
|
-
},
|
|
564
|
-
collect: {
|
|
565
|
-
nodes: new Set<NodeLike>(),
|
|
566
|
-
edges: new Set<EdgeLike>(),
|
|
567
|
-
},
|
|
568
|
-
comment: '',
|
|
569
|
-
diffFiles: this.#diffFiles,
|
|
570
|
-
getAuthHeader: this.#getAuthHeader,
|
|
571
|
-
hasSkills: this.#hasSkills,
|
|
572
|
-
loose,
|
|
573
|
-
importers,
|
|
574
|
-
partial: { nodes, edges },
|
|
575
|
-
...(this.#partSelectors.size ?
|
|
576
|
-
{ parts: [], partSelectors: this.#partSelectors }
|
|
577
|
-
: undefined),
|
|
578
|
-
retries: this.#retries,
|
|
579
|
-
signal,
|
|
580
|
-
securityArchive: this.#securityArchive,
|
|
581
|
-
scopeIDs,
|
|
582
|
-
walk,
|
|
583
|
-
specificity: { idCounter: 0, commonCounter: 0 },
|
|
584
|
-
hostContexts: this.#hostContexts,
|
|
585
|
-
})
|
|
586
|
-
|
|
587
|
-
const res: QueryResponse = {
|
|
588
|
-
edges: this.#getQueryResponseEdges(collect.edges),
|
|
589
|
-
nodes: this.#getQueryResponseNodes(collect.nodes),
|
|
590
|
-
importers: this.#getQueryResponseNodes(stateResultImporters),
|
|
591
|
-
comment,
|
|
592
|
-
specificity,
|
|
593
|
-
// part nodes are a subset of `nodes`, already hydrated above
|
|
594
|
-
...(parts ? { parts: parts as QueryPart[] } : undefined),
|
|
595
|
-
}
|
|
596
|
-
this.#cache.set(query, res)
|
|
597
|
-
return res
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
/**
|
|
601
|
-
* Parses a query string in order to retrieve an array of tokens.
|
|
602
|
-
*/
|
|
603
|
-
static getQueryTokens(query: string): ParsedSelectorToken[] {
|
|
604
|
-
if (!query) return []
|
|
605
|
-
|
|
606
|
-
const tokens: ParsedSelectorToken[] = []
|
|
607
|
-
|
|
608
|
-
const ast = (q: string) => {
|
|
609
|
-
try {
|
|
610
|
-
return parse(q, { loose: true })
|
|
611
|
-
} catch (_e) {
|
|
612
|
-
return ast(q.slice(0, -1))
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
const processNode = (node: PostcssNode) => {
|
|
617
|
-
for (const key of selectorsMap.keys()) {
|
|
618
|
-
if (node.type === key && node.type !== 'root') {
|
|
619
|
-
let token = `${node.spaces.before}${node.value}${node.spaces.after}`
|
|
620
|
-
|
|
621
|
-
if (isIdentifierNode(node)) {
|
|
622
|
-
token = `${node.spaces.before}#${node.value}${node.spaces.after}`
|
|
623
|
-
} else if (isSelectorNode(node)) {
|
|
624
|
-
token = `${node.spaces.before},${node.spaces.after}`
|
|
625
|
-
} else if (isAttributeNode(node)) {
|
|
626
|
-
token = String(
|
|
627
|
-
node.source?.start?.column &&
|
|
628
|
-
node.source.end?.column &&
|
|
629
|
-
`${node.spaces.before}${query.slice(node.source.start.column - 1, node.source.end.column)}${node.spaces.after}`,
|
|
630
|
-
)
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
if (
|
|
634
|
-
isPostcssNodeWithChildren(node) &&
|
|
635
|
-
isPseudoNode(node) &&
|
|
636
|
-
node.nodes.length
|
|
637
|
-
) {
|
|
638
|
-
token = String(token.split('(')[0])
|
|
639
|
-
token += '('
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
if (
|
|
643
|
-
!isSelectorNode(node) ||
|
|
644
|
-
node.parent?.nodes.indexOf(node) !== 0
|
|
645
|
-
) {
|
|
646
|
-
tokens.push({
|
|
647
|
-
...node,
|
|
648
|
-
token,
|
|
649
|
-
} as ParsedSelectorToken)
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
if (isPostcssNodeWithChildren(node)) {
|
|
654
|
-
for (const child of node.nodes) {
|
|
655
|
-
processNode(child)
|
|
656
|
-
}
|
|
657
|
-
if (isPseudoNode(node) && node.nodes.length) {
|
|
658
|
-
tokens.push({
|
|
659
|
-
...node,
|
|
660
|
-
token: ')' + node.spaces.after,
|
|
661
|
-
type: 'pseudo',
|
|
662
|
-
} as ParsedSelectorToken)
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
processNode(ast(query))
|
|
668
|
-
return tokens
|
|
669
|
-
}
|
|
670
|
-
}
|