@vltpkg/query 1.5.0 → 1.6.1

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