@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.
Files changed (210) hide show
  1. package/package.json +10 -12
  2. package/skills/dss-query/REFERENCE.md +0 -125
  3. package/skills/dss-query/SKILL.md +0 -190
  4. package/skills/dss-query/evals/README.md +0 -81
  5. package/skills/dss-query/evals/evals.json +0 -89
  6. package/skills/dss-query/evals/grade.mjs +0 -481
  7. package/src/attribute.ts +0 -190
  8. package/src/combinator.ts +0 -135
  9. package/src/error.ts +0 -78
  10. package/src/id.ts +0 -46
  11. package/src/index.ts +0 -670
  12. package/src/parser.ts +0 -122
  13. package/src/pseudo/abandoned.ts +0 -9
  14. package/src/pseudo/attr.ts +0 -92
  15. package/src/pseudo/built.ts +0 -19
  16. package/src/pseudo/confused.ts +0 -29
  17. package/src/pseudo/cve.ts +0 -93
  18. package/src/pseudo/cwe.ts +0 -97
  19. package/src/pseudo/debug.ts +0 -9
  20. package/src/pseudo/deprecated.ts +0 -9
  21. package/src/pseudo/dev.ts +0 -18
  22. package/src/pseudo/diff.ts +0 -90
  23. package/src/pseudo/dist.ts +0 -148
  24. package/src/pseudo/dynamic.ts +0 -9
  25. package/src/pseudo/empty.ts +0 -15
  26. package/src/pseudo/entropic.ts +0 -9
  27. package/src/pseudo/env.ts +0 -6
  28. package/src/pseudo/eval.ts +0 -9
  29. package/src/pseudo/fs.ts +0 -9
  30. package/src/pseudo/helpers.ts +0 -106
  31. package/src/pseudo/host.ts +0 -111
  32. package/src/pseudo/hostname.ts +0 -156
  33. package/src/pseudo/license.ts +0 -134
  34. package/src/pseudo/link.ts +0 -30
  35. package/src/pseudo/malware.ts +0 -41
  36. package/src/pseudo/minified.ts +0 -9
  37. package/src/pseudo/missing.ts +0 -16
  38. package/src/pseudo/native.ts +0 -9
  39. package/src/pseudo/network.ts +0 -9
  40. package/src/pseudo/obfuscated.ts +0 -9
  41. package/src/pseudo/optional.ts +0 -18
  42. package/src/pseudo/outdated.ts +0 -345
  43. package/src/pseudo/overridden.ts +0 -20
  44. package/src/pseudo/path.ts +0 -146
  45. package/src/pseudo/peer.ts +0 -18
  46. package/src/pseudo/prerelease.ts +0 -47
  47. package/src/pseudo/private.ts +0 -19
  48. package/src/pseudo/prod.ts +0 -18
  49. package/src/pseudo/published.ts +0 -250
  50. package/src/pseudo/registry.ts +0 -29
  51. package/src/pseudo/root.ts +0 -19
  52. package/src/pseudo/scanned.ts +0 -20
  53. package/src/pseudo/score.ts +0 -186
  54. package/src/pseudo/scripts.ts +0 -61
  55. package/src/pseudo/semver.ts +0 -320
  56. package/src/pseudo/severity.ts +0 -231
  57. package/src/pseudo/shell.ts +0 -9
  58. package/src/pseudo/shrinkwrap.ts +0 -9
  59. package/src/pseudo/skill.ts +0 -59
  60. package/src/pseudo/skills.ts +0 -29
  61. package/src/pseudo/spec.ts +0 -131
  62. package/src/pseudo/squat.ts +0 -225
  63. package/src/pseudo/suspicious.ts +0 -9
  64. package/src/pseudo/tracker.ts +0 -9
  65. package/src/pseudo/trivial.ts +0 -9
  66. package/src/pseudo/type.ts +0 -26
  67. package/src/pseudo/undesirable.ts +0 -9
  68. package/src/pseudo/unknown.ts +0 -9
  69. package/src/pseudo/unmaintained.ts +0 -9
  70. package/src/pseudo/unpopular.ts +0 -9
  71. package/src/pseudo/unstable.ts +0 -9
  72. package/src/pseudo/vuln.ts +0 -273
  73. package/src/pseudo/workspace.ts +0 -22
  74. package/src/pseudo.ts +0 -440
  75. package/src/types.ts +0 -206
  76. package/tap-snapshots/test/attribute.ts.test.cjs +0 -304
  77. package/tap-snapshots/test/combinator.ts.test.cjs +0 -333
  78. package/tap-snapshots/test/id.ts.test.cjs +0 -118
  79. package/tap-snapshots/test/parser.ts.test.cjs +0 -344
  80. package/tap-snapshots/test/pseudo/abandoned.ts.test.cjs +0 -18
  81. package/tap-snapshots/test/pseudo/attr.ts.test.cjs +0 -161
  82. package/tap-snapshots/test/pseudo/built.ts.test.cjs +0 -70
  83. package/tap-snapshots/test/pseudo/confused.ts.test.cjs +0 -32
  84. package/tap-snapshots/test/pseudo/cve.ts.test.cjs +0 -49
  85. package/tap-snapshots/test/pseudo/cwe.ts.test.cjs +0 -49
  86. package/tap-snapshots/test/pseudo/debug.ts.test.cjs +0 -18
  87. package/tap-snapshots/test/pseudo/deprecated.ts.test.cjs +0 -18
  88. package/tap-snapshots/test/pseudo/dist.ts.test.cjs +0 -30
  89. package/tap-snapshots/test/pseudo/dynamic.ts.test.cjs +0 -18
  90. package/tap-snapshots/test/pseudo/empty.ts.test.cjs +0 -50
  91. package/tap-snapshots/test/pseudo/entropic.ts.test.cjs +0 -18
  92. package/tap-snapshots/test/pseudo/env.ts.test.cjs +0 -18
  93. package/tap-snapshots/test/pseudo/eval.ts.test.cjs +0 -18
  94. package/tap-snapshots/test/pseudo/fs.ts.test.cjs +0 -18
  95. package/tap-snapshots/test/pseudo/helpers.ts.test.cjs +0 -18
  96. package/tap-snapshots/test/pseudo/hostname.ts.test.cjs +0 -148
  97. package/tap-snapshots/test/pseudo/license.ts.test.cjs +0 -41
  98. package/tap-snapshots/test/pseudo/link.ts.test.cjs +0 -36
  99. package/tap-snapshots/test/pseudo/malware.ts.test.cjs +0 -22
  100. package/tap-snapshots/test/pseudo/minified.ts.test.cjs +0 -18
  101. package/tap-snapshots/test/pseudo/missing.ts.test.cjs +0 -30
  102. package/tap-snapshots/test/pseudo/native.ts.test.cjs +0 -18
  103. package/tap-snapshots/test/pseudo/network.ts.test.cjs +0 -18
  104. package/tap-snapshots/test/pseudo/obfuscated.ts.test.cjs +0 -18
  105. package/tap-snapshots/test/pseudo/outdated.ts.test.cjs +0 -132
  106. package/tap-snapshots/test/pseudo/overridden.ts.test.cjs +0 -75
  107. package/tap-snapshots/test/pseudo/prerelease.ts.test.cjs +0 -101
  108. package/tap-snapshots/test/pseudo/private.ts.test.cjs +0 -38
  109. package/tap-snapshots/test/pseudo/published.ts.test.cjs +0 -171
  110. package/tap-snapshots/test/pseudo/registry.ts.test.cjs +0 -68
  111. package/tap-snapshots/test/pseudo/root.ts.test.cjs +0 -24
  112. package/tap-snapshots/test/pseudo/scanned.ts.test.cjs +0 -18
  113. package/tap-snapshots/test/pseudo/score.ts.test.cjs +0 -239
  114. package/tap-snapshots/test/pseudo/scripts.ts.test.cjs +0 -62
  115. package/tap-snapshots/test/pseudo/semver.ts.test.cjs +0 -575
  116. package/tap-snapshots/test/pseudo/severity.ts.test.cjs +0 -122
  117. package/tap-snapshots/test/pseudo/shell.ts.test.cjs +0 -18
  118. package/tap-snapshots/test/pseudo/shrinkwrap.ts.test.cjs +0 -18
  119. package/tap-snapshots/test/pseudo/skills.ts.test.cjs +0 -19
  120. package/tap-snapshots/test/pseudo/spec.ts.test.cjs +0 -148
  121. package/tap-snapshots/test/pseudo/squat.ts.test.cjs +0 -129
  122. package/tap-snapshots/test/pseudo/suspicious.ts.test.cjs +0 -18
  123. package/tap-snapshots/test/pseudo/tracker.ts.test.cjs +0 -18
  124. package/tap-snapshots/test/pseudo/trivial.ts.test.cjs +0 -18
  125. package/tap-snapshots/test/pseudo/type.ts.test.cjs +0 -51
  126. package/tap-snapshots/test/pseudo/undesirable.ts.test.cjs +0 -18
  127. package/tap-snapshots/test/pseudo/unknown.ts.test.cjs +0 -18
  128. package/tap-snapshots/test/pseudo/unmaintained.ts.test.cjs +0 -18
  129. package/tap-snapshots/test/pseudo/unpopular.ts.test.cjs +0 -18
  130. package/tap-snapshots/test/pseudo/unstable.ts.test.cjs +0 -18
  131. package/tap-snapshots/test/pseudo/vuln.ts.test.cjs +0 -208
  132. package/tap-snapshots/test/pseudo/vulnerable.ts.test.cjs +0 -20
  133. package/tap-snapshots/test/pseudo/workspace.ts.test.cjs +0 -19
  134. package/tap-snapshots/test/pseudo.ts.test.cjs +0 -1247
  135. package/test/attribute.ts +0 -273
  136. package/test/combinator.ts +0 -175
  137. package/test/error.ts +0 -60
  138. package/test/fixtures/graph.ts +0 -999
  139. package/test/fixtures/selector.ts +0 -103
  140. package/test/fixtures/types.ts +0 -7
  141. package/test/id.ts +0 -102
  142. package/test/index.ts +0 -1148
  143. package/test/parser.ts +0 -117
  144. package/test/pseudo/abandoned.ts +0 -98
  145. package/test/pseudo/attr.ts +0 -298
  146. package/test/pseudo/built.ts +0 -214
  147. package/test/pseudo/confused.ts +0 -160
  148. package/test/pseudo/cve.ts +0 -249
  149. package/test/pseudo/cwe.ts +0 -255
  150. package/test/pseudo/debug.ts +0 -98
  151. package/test/pseudo/deprecated.ts +0 -98
  152. package/test/pseudo/dev.ts +0 -61
  153. package/test/pseudo/diff.ts +0 -417
  154. package/test/pseudo/dist.ts +0 -215
  155. package/test/pseudo/dynamic.ts +0 -98
  156. package/test/pseudo/empty.ts +0 -108
  157. package/test/pseudo/entropic.ts +0 -101
  158. package/test/pseudo/env.ts +0 -98
  159. package/test/pseudo/eval.ts +0 -98
  160. package/test/pseudo/fs.ts +0 -98
  161. package/test/pseudo/helpers.ts +0 -370
  162. package/test/pseudo/host-context.ts +0 -276
  163. package/test/pseudo/hostname.ts +0 -314
  164. package/test/pseudo/license.ts +0 -265
  165. package/test/pseudo/link.ts +0 -94
  166. package/test/pseudo/malware.ts +0 -184
  167. package/test/pseudo/minified.ts +0 -98
  168. package/test/pseudo/missing.ts +0 -111
  169. package/test/pseudo/native.ts +0 -98
  170. package/test/pseudo/network.ts +0 -98
  171. package/test/pseudo/obfuscated.ts +0 -98
  172. package/test/pseudo/optional.ts +0 -73
  173. package/test/pseudo/outdated.ts +0 -461
  174. package/test/pseudo/overridden.ts +0 -317
  175. package/test/pseudo/path.ts +0 -680
  176. package/test/pseudo/peer.ts +0 -101
  177. package/test/pseudo/prerelease.ts +0 -279
  178. package/test/pseudo/private.ts +0 -108
  179. package/test/pseudo/prod.ts +0 -61
  180. package/test/pseudo/published.ts +0 -557
  181. package/test/pseudo/registry.ts +0 -122
  182. package/test/pseudo/root.ts +0 -66
  183. package/test/pseudo/scanned.ts +0 -144
  184. package/test/pseudo/score.ts +0 -591
  185. package/test/pseudo/scripts.ts +0 -324
  186. package/test/pseudo/semver.ts +0 -822
  187. package/test/pseudo/severity.ts +0 -342
  188. package/test/pseudo/shell.ts +0 -98
  189. package/test/pseudo/shrinkwrap.ts +0 -98
  190. package/test/pseudo/skill.ts +0 -189
  191. package/test/pseudo/skills.ts +0 -90
  192. package/test/pseudo/spec.ts +0 -525
  193. package/test/pseudo/squat.ts +0 -565
  194. package/test/pseudo/suspicious.ts +0 -98
  195. package/test/pseudo/tracker.ts +0 -98
  196. package/test/pseudo/trivial.ts +0 -98
  197. package/test/pseudo/type.ts +0 -105
  198. package/test/pseudo/undesirable.ts +0 -98
  199. package/test/pseudo/unknown.ts +0 -98
  200. package/test/pseudo/unmaintained.ts +0 -98
  201. package/test/pseudo/unpopular.ts +0 -98
  202. package/test/pseudo/unstable.ts +0 -98
  203. package/test/pseudo/vuln.ts +0 -625
  204. package/test/pseudo/vulnerable.ts +0 -137
  205. package/test/pseudo/workspace.ts +0 -111
  206. package/test/pseudo.ts +0 -584
  207. package/tsconfig.json +0 -3
  208. package/tsconfig.publish.json +0 -19
  209. package/tsconfig.publish.tsbuildinfo +0 -1
  210. 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
- }