@vltpkg/query 1.0.0-rc.32 → 1.0.0-rc.33

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 (203) hide show
  1. package/dist/pseudo/hostname.js +3 -1
  2. package/dist/pseudo/scripts.js +1 -1
  3. package/package.json +19 -10
  4. package/skills/dss-query/REFERENCE.md +119 -0
  5. package/skills/dss-query/SKILL.md +190 -0
  6. package/skills/dss-query/evals/README.md +79 -0
  7. package/skills/dss-query/evals/evals.json +89 -0
  8. package/skills/dss-query/evals/grade.mjs +480 -0
  9. package/src/attribute.ts +186 -0
  10. package/src/combinator.ts +132 -0
  11. package/src/id.ts +46 -0
  12. package/src/index.ts +613 -0
  13. package/src/parser.ts +121 -0
  14. package/src/pseudo/abandoned.ts +9 -0
  15. package/src/pseudo/attr.ts +92 -0
  16. package/src/pseudo/built.ts +19 -0
  17. package/src/pseudo/confused.ts +29 -0
  18. package/src/pseudo/cve.ts +93 -0
  19. package/src/pseudo/cwe.ts +97 -0
  20. package/src/pseudo/debug.ts +9 -0
  21. package/src/pseudo/deprecated.ts +9 -0
  22. package/src/pseudo/dev.ts +18 -0
  23. package/src/pseudo/diff.ts +90 -0
  24. package/src/pseudo/dist.ts +144 -0
  25. package/src/pseudo/dynamic.ts +9 -0
  26. package/src/pseudo/empty.ts +15 -0
  27. package/src/pseudo/entropic.ts +9 -0
  28. package/src/pseudo/env.ts +6 -0
  29. package/src/pseudo/eval.ts +9 -0
  30. package/src/pseudo/fs.ts +9 -0
  31. package/src/pseudo/helpers.ts +106 -0
  32. package/src/pseudo/host.ts +111 -0
  33. package/src/pseudo/hostname.ts +156 -0
  34. package/src/pseudo/license.ts +134 -0
  35. package/src/pseudo/link.ts +30 -0
  36. package/src/pseudo/malware.ts +249 -0
  37. package/src/pseudo/minified.ts +9 -0
  38. package/src/pseudo/missing.ts +16 -0
  39. package/src/pseudo/native.ts +9 -0
  40. package/src/pseudo/network.ts +9 -0
  41. package/src/pseudo/obfuscated.ts +9 -0
  42. package/src/pseudo/optional.ts +18 -0
  43. package/src/pseudo/outdated.ts +305 -0
  44. package/src/pseudo/overridden.ts +20 -0
  45. package/src/pseudo/path.ts +146 -0
  46. package/src/pseudo/peer.ts +18 -0
  47. package/src/pseudo/prerelease.ts +47 -0
  48. package/src/pseudo/private.ts +19 -0
  49. package/src/pseudo/prod.ts +18 -0
  50. package/src/pseudo/published.ts +248 -0
  51. package/src/pseudo/registry.ts +29 -0
  52. package/src/pseudo/root.ts +19 -0
  53. package/src/pseudo/scanned.ts +20 -0
  54. package/src/pseudo/score.ts +186 -0
  55. package/src/pseudo/scripts.ts +54 -0
  56. package/src/pseudo/semver.ts +320 -0
  57. package/src/pseudo/severity.ts +231 -0
  58. package/src/pseudo/shell.ts +9 -0
  59. package/src/pseudo/shrinkwrap.ts +9 -0
  60. package/src/pseudo/spec.ts +131 -0
  61. package/src/pseudo/squat.ts +225 -0
  62. package/src/pseudo/suspicious.ts +9 -0
  63. package/src/pseudo/tracker.ts +9 -0
  64. package/src/pseudo/trivial.ts +9 -0
  65. package/src/pseudo/type.ts +26 -0
  66. package/src/pseudo/undesirable.ts +9 -0
  67. package/src/pseudo/unknown.ts +9 -0
  68. package/src/pseudo/unmaintained.ts +9 -0
  69. package/src/pseudo/unpopular.ts +9 -0
  70. package/src/pseudo/unstable.ts +9 -0
  71. package/src/pseudo/vulnerable.ts +26 -0
  72. package/src/pseudo/workspace.ts +22 -0
  73. package/src/pseudo.ts +413 -0
  74. package/src/types.ts +164 -0
  75. package/tap-snapshots/test/attribute.ts.test.cjs +304 -0
  76. package/tap-snapshots/test/combinator.ts.test.cjs +333 -0
  77. package/tap-snapshots/test/id.ts.test.cjs +118 -0
  78. package/tap-snapshots/test/parser.ts.test.cjs +344 -0
  79. package/tap-snapshots/test/pseudo/abandoned.ts.test.cjs +18 -0
  80. package/tap-snapshots/test/pseudo/attr.ts.test.cjs +161 -0
  81. package/tap-snapshots/test/pseudo/built.ts.test.cjs +70 -0
  82. package/tap-snapshots/test/pseudo/confused.ts.test.cjs +32 -0
  83. package/tap-snapshots/test/pseudo/cve.ts.test.cjs +49 -0
  84. package/tap-snapshots/test/pseudo/cwe.ts.test.cjs +49 -0
  85. package/tap-snapshots/test/pseudo/debug.ts.test.cjs +18 -0
  86. package/tap-snapshots/test/pseudo/deprecated.ts.test.cjs +18 -0
  87. package/tap-snapshots/test/pseudo/dist.ts.test.cjs +30 -0
  88. package/tap-snapshots/test/pseudo/dynamic.ts.test.cjs +18 -0
  89. package/tap-snapshots/test/pseudo/empty.ts.test.cjs +50 -0
  90. package/tap-snapshots/test/pseudo/entropic.ts.test.cjs +18 -0
  91. package/tap-snapshots/test/pseudo/env.ts.test.cjs +18 -0
  92. package/tap-snapshots/test/pseudo/eval.ts.test.cjs +18 -0
  93. package/tap-snapshots/test/pseudo/fs.ts.test.cjs +18 -0
  94. package/tap-snapshots/test/pseudo/helpers.ts.test.cjs +18 -0
  95. package/tap-snapshots/test/pseudo/hostname.ts.test.cjs +148 -0
  96. package/tap-snapshots/test/pseudo/license.ts.test.cjs +41 -0
  97. package/tap-snapshots/test/pseudo/link.ts.test.cjs +36 -0
  98. package/tap-snapshots/test/pseudo/malware.ts.test.cjs +191 -0
  99. package/tap-snapshots/test/pseudo/minified.ts.test.cjs +18 -0
  100. package/tap-snapshots/test/pseudo/missing.ts.test.cjs +30 -0
  101. package/tap-snapshots/test/pseudo/native.ts.test.cjs +18 -0
  102. package/tap-snapshots/test/pseudo/network.ts.test.cjs +18 -0
  103. package/tap-snapshots/test/pseudo/obfuscated.ts.test.cjs +18 -0
  104. package/tap-snapshots/test/pseudo/outdated.ts.test.cjs +130 -0
  105. package/tap-snapshots/test/pseudo/overridden.ts.test.cjs +75 -0
  106. package/tap-snapshots/test/pseudo/prerelease.ts.test.cjs +101 -0
  107. package/tap-snapshots/test/pseudo/private.ts.test.cjs +38 -0
  108. package/tap-snapshots/test/pseudo/published.ts.test.cjs +171 -0
  109. package/tap-snapshots/test/pseudo/registry.ts.test.cjs +68 -0
  110. package/tap-snapshots/test/pseudo/root.ts.test.cjs +24 -0
  111. package/tap-snapshots/test/pseudo/scanned.ts.test.cjs +18 -0
  112. package/tap-snapshots/test/pseudo/score.ts.test.cjs +239 -0
  113. package/tap-snapshots/test/pseudo/scripts.ts.test.cjs +61 -0
  114. package/tap-snapshots/test/pseudo/semver.ts.test.cjs +575 -0
  115. package/tap-snapshots/test/pseudo/severity.ts.test.cjs +95 -0
  116. package/tap-snapshots/test/pseudo/shell.ts.test.cjs +18 -0
  117. package/tap-snapshots/test/pseudo/shrinkwrap.ts.test.cjs +18 -0
  118. package/tap-snapshots/test/pseudo/spec.ts.test.cjs +148 -0
  119. package/tap-snapshots/test/pseudo/squat.ts.test.cjs +129 -0
  120. package/tap-snapshots/test/pseudo/suspicious.ts.test.cjs +18 -0
  121. package/tap-snapshots/test/pseudo/tracker.ts.test.cjs +18 -0
  122. package/tap-snapshots/test/pseudo/trivial.ts.test.cjs +18 -0
  123. package/tap-snapshots/test/pseudo/type.ts.test.cjs +51 -0
  124. package/tap-snapshots/test/pseudo/undesirable.ts.test.cjs +18 -0
  125. package/tap-snapshots/test/pseudo/unknown.ts.test.cjs +18 -0
  126. package/tap-snapshots/test/pseudo/unmaintained.ts.test.cjs +18 -0
  127. package/tap-snapshots/test/pseudo/unpopular.ts.test.cjs +18 -0
  128. package/tap-snapshots/test/pseudo/unstable.ts.test.cjs +18 -0
  129. package/tap-snapshots/test/pseudo/vulnerable.ts.test.cjs +18 -0
  130. package/tap-snapshots/test/pseudo/workspace.ts.test.cjs +19 -0
  131. package/tap-snapshots/test/pseudo.ts.test.cjs +1247 -0
  132. package/test/attribute.ts +266 -0
  133. package/test/combinator.ts +168 -0
  134. package/test/fixtures/graph.ts +999 -0
  135. package/test/fixtures/selector.ts +103 -0
  136. package/test/fixtures/types.ts +7 -0
  137. package/test/id.ts +102 -0
  138. package/test/index.ts +992 -0
  139. package/test/parser.ts +117 -0
  140. package/test/pseudo/abandoned.ts +98 -0
  141. package/test/pseudo/attr.ts +298 -0
  142. package/test/pseudo/built.ts +214 -0
  143. package/test/pseudo/confused.ts +160 -0
  144. package/test/pseudo/cve.ts +249 -0
  145. package/test/pseudo/cwe.ts +255 -0
  146. package/test/pseudo/debug.ts +98 -0
  147. package/test/pseudo/deprecated.ts +98 -0
  148. package/test/pseudo/dev.ts +61 -0
  149. package/test/pseudo/diff.ts +417 -0
  150. package/test/pseudo/dist.ts +215 -0
  151. package/test/pseudo/dynamic.ts +98 -0
  152. package/test/pseudo/empty.ts +108 -0
  153. package/test/pseudo/entropic.ts +101 -0
  154. package/test/pseudo/env.ts +98 -0
  155. package/test/pseudo/eval.ts +98 -0
  156. package/test/pseudo/fs.ts +98 -0
  157. package/test/pseudo/helpers.ts +370 -0
  158. package/test/pseudo/host-context.ts +276 -0
  159. package/test/pseudo/hostname.ts +314 -0
  160. package/test/pseudo/license.ts +265 -0
  161. package/test/pseudo/link.ts +94 -0
  162. package/test/pseudo/malware.ts +431 -0
  163. package/test/pseudo/minified.ts +98 -0
  164. package/test/pseudo/missing.ts +111 -0
  165. package/test/pseudo/native.ts +98 -0
  166. package/test/pseudo/network.ts +98 -0
  167. package/test/pseudo/obfuscated.ts +98 -0
  168. package/test/pseudo/optional.ts +73 -0
  169. package/test/pseudo/outdated.ts +331 -0
  170. package/test/pseudo/overridden.ts +317 -0
  171. package/test/pseudo/path.ts +680 -0
  172. package/test/pseudo/peer.ts +101 -0
  173. package/test/pseudo/prerelease.ts +279 -0
  174. package/test/pseudo/private.ts +108 -0
  175. package/test/pseudo/prod.ts +61 -0
  176. package/test/pseudo/published.ts +557 -0
  177. package/test/pseudo/registry.ts +122 -0
  178. package/test/pseudo/root.ts +66 -0
  179. package/test/pseudo/scanned.ts +78 -0
  180. package/test/pseudo/score.ts +591 -0
  181. package/test/pseudo/scripts.ts +294 -0
  182. package/test/pseudo/semver.ts +822 -0
  183. package/test/pseudo/severity.ts +310 -0
  184. package/test/pseudo/shell.ts +98 -0
  185. package/test/pseudo/shrinkwrap.ts +98 -0
  186. package/test/pseudo/spec.ts +525 -0
  187. package/test/pseudo/squat.ts +565 -0
  188. package/test/pseudo/suspicious.ts +98 -0
  189. package/test/pseudo/tracker.ts +98 -0
  190. package/test/pseudo/trivial.ts +98 -0
  191. package/test/pseudo/type.ts +105 -0
  192. package/test/pseudo/undesirable.ts +98 -0
  193. package/test/pseudo/unknown.ts +98 -0
  194. package/test/pseudo/unmaintained.ts +98 -0
  195. package/test/pseudo/unpopular.ts +98 -0
  196. package/test/pseudo/unstable.ts +98 -0
  197. package/test/pseudo/vulnerable.ts +130 -0
  198. package/test/pseudo/workspace.ts +111 -0
  199. package/test/pseudo.ts +558 -0
  200. package/tsconfig.json +3 -0
  201. package/tsconfig.publish.json +19 -0
  202. package/tsconfig.publish.tsbuildinfo +1 -0
  203. package/typedoc.mjs +2 -0
@@ -0,0 +1,134 @@
1
+ import { error } from '@vltpkg/error-cause'
2
+ import { asError } from '@vltpkg/types'
3
+ import {
4
+ asPostcssNodeWithChildren,
5
+ asStringNode,
6
+ asTagNode,
7
+ isStringNode,
8
+ isTagNode,
9
+ } from '@vltpkg/dss-parser'
10
+ import {
11
+ assertSecurityArchive,
12
+ removeDanglingEdges,
13
+ removeNode,
14
+ removeQuotes,
15
+ } from './helpers.ts'
16
+ import type { ParserState } from '../types.ts'
17
+ import type { PostcssNode } from '@vltpkg/dss-parser'
18
+
19
+ export type LicenseKinds =
20
+ | 'unlicensed'
21
+ | 'misc'
22
+ | 'restricted'
23
+ | 'ambiguous'
24
+ | 'copyleft'
25
+ | 'unknown'
26
+ | 'none'
27
+ | 'exception'
28
+ | undefined
29
+
30
+ export type LicenseAlertTypes =
31
+ | 'explicitlyUnlicensedItem'
32
+ | 'miscLicenseIssues'
33
+ | 'nonpermissiveLicense'
34
+ | 'ambiguousClassifier'
35
+ | 'copyleftLicense'
36
+ | 'unidentifiedLicense'
37
+ | 'noLicenseFound'
38
+ | 'licenseException'
39
+ | undefined
40
+
41
+ const kindsMap = new Map<LicenseKinds, LicenseAlertTypes>([
42
+ ['unlicensed', 'explicitlyUnlicensedItem'],
43
+ ['misc', 'miscLicenseIssues'],
44
+ ['restricted', 'nonpermissiveLicense'],
45
+ ['ambiguous', 'ambiguousClassifier'],
46
+ ['copyleft', 'copyleftLicense'],
47
+ ['unknown', 'unidentifiedLicense'],
48
+ ['none', 'noLicenseFound'],
49
+ ['exception', 'licenseException'],
50
+ [undefined, undefined],
51
+ ])
52
+ const kinds = new Set(kindsMap.keys())
53
+
54
+ export const isLicenseKind = (
55
+ value?: string,
56
+ ): value is LicenseKinds => kinds.has(value as LicenseKinds)
57
+
58
+ export const asLicenseKind = (value?: string): LicenseKinds => {
59
+ if (!isLicenseKind(value)) {
60
+ throw error('Expected a valid license kind', {
61
+ found: value,
62
+ validOptions: Array.from(kinds),
63
+ })
64
+ }
65
+ return value
66
+ }
67
+
68
+ export const parseInternals = (
69
+ nodes: PostcssNode[],
70
+ ): { kind: LicenseKinds } => {
71
+ let kind: LicenseKinds
72
+
73
+ if (isStringNode(asPostcssNodeWithChildren(nodes[0]).nodes[0])) {
74
+ kind = asLicenseKind(
75
+ removeQuotes(
76
+ asStringNode(asPostcssNodeWithChildren(nodes[0]).nodes[0])
77
+ .value,
78
+ ),
79
+ )
80
+ } else if (
81
+ isTagNode(asPostcssNodeWithChildren(nodes[0]).nodes[0])
82
+ ) {
83
+ kind = asLicenseKind(
84
+ asTagNode(asPostcssNodeWithChildren(nodes[0]).nodes[0]).value,
85
+ )
86
+ }
87
+
88
+ return { kind }
89
+ }
90
+
91
+ export const license = async (state: ParserState) => {
92
+ assertSecurityArchive(state, 'license')
93
+
94
+ let internals
95
+ try {
96
+ internals = parseInternals(
97
+ asPostcssNodeWithChildren(state.current).nodes,
98
+ )
99
+ } catch (err) {
100
+ if (asError(err).message === 'Expected a query node') {
101
+ // No parameters provided - pseudo state form: match ANY license defined (not 'none')
102
+ for (const node of state.partial.nodes) {
103
+ const report = state.securityArchive.get(node.id)
104
+ // Exclude if no report or if it has 'noLicenseFound' alert
105
+ const exclude =
106
+ !report?.alerts ||
107
+ report.alerts.some(alert => alert.type === 'noLicenseFound')
108
+ if (exclude) {
109
+ removeNode(state, node)
110
+ }
111
+ }
112
+ removeDanglingEdges(state)
113
+ return state
114
+ } else {
115
+ throw error('Failed to parse :license selector', { cause: err })
116
+ }
117
+ }
118
+
119
+ const { kind } = internals
120
+ const alertName = kindsMap.get(kind)
121
+ for (const node of state.partial.nodes) {
122
+ const report = state.securityArchive.get(node.id)
123
+ const exclude = !report?.alerts.some(
124
+ alert => alert.type === alertName,
125
+ )
126
+ if (exclude) {
127
+ removeNode(state, node)
128
+ }
129
+ }
130
+
131
+ removeDanglingEdges(state)
132
+
133
+ return state
134
+ }
@@ -0,0 +1,30 @@
1
+ import { splitDepID } from '@vltpkg/dep-id/browser'
2
+ import type { ParserState } from '../types.ts'
3
+ import { removeNode } from './helpers.ts'
4
+
5
+ /**
6
+ * :link Pseudo-Selector, matches only nodes that are file links.
7
+ *
8
+ * It filters out any node that is not of type 'file' or nodes of 'file'
9
+ * type that ends with 'tar.gz' since these are local tarballs.
10
+ */
11
+ export const link = async (state: ParserState) => {
12
+ for (const node of state.partial.nodes) {
13
+ const [type, path] = splitDepID(node.id)
14
+ if (type !== 'file' || path.endsWith('tar.gz') || path === '.') {
15
+ removeNode(state, node)
16
+ }
17
+ }
18
+
19
+ for (const edge of state.partial.edges) {
20
+ if (
21
+ !edge.spec.file ||
22
+ edge.spec.file.endsWith('tar.gz') ||
23
+ edge.spec.file === '.'
24
+ ) {
25
+ state.partial.edges.delete(edge)
26
+ }
27
+ }
28
+
29
+ return state
30
+ }
@@ -0,0 +1,249 @@
1
+ import { error } from '@vltpkg/error-cause'
2
+ import {
3
+ asPostcssNodeWithChildren,
4
+ asStringNode,
5
+ asTagNode,
6
+ isStringNode,
7
+ isTagNode,
8
+ } from '@vltpkg/dss-parser'
9
+ import {
10
+ assertSecurityArchive,
11
+ removeDanglingEdges,
12
+ removeNode,
13
+ removeQuotes,
14
+ } from './helpers.ts'
15
+ import type { ParserState } from '../types.ts'
16
+ import type { PostcssNode } from '@vltpkg/dss-parser'
17
+
18
+ export type MalwareKinds =
19
+ | '0'
20
+ | '1'
21
+ | '2'
22
+ | '3'
23
+ | 'critical'
24
+ | 'high'
25
+ | 'medium'
26
+ | 'low'
27
+ | undefined
28
+
29
+ export type MalwareAlertTypes =
30
+ 'malware' | 'gptMalware' | 'gptSecurity' | 'gptAnomaly' | undefined
31
+
32
+ export type MalwareComparator = '>' | '<' | '>=' | '<=' | undefined
33
+
34
+ const kindsMap = new Map<MalwareKinds, MalwareAlertTypes>([
35
+ ['critical', 'malware'],
36
+ ['high', 'gptMalware'],
37
+ ['medium', 'gptSecurity'],
38
+ ['low', 'gptAnomaly'],
39
+ ['0', 'malware'],
40
+ ['1', 'gptMalware'],
41
+ ['2', 'gptSecurity'],
42
+ ['3', 'gptAnomaly'],
43
+ ])
44
+
45
+ // Map numerical values to their respective kinds for comparison operations
46
+ const kindLevelMap = new Map<MalwareKinds, number>([
47
+ ['critical', 0],
48
+ ['high', 1],
49
+ ['medium', 2],
50
+ ['low', 3],
51
+ ['0', 0],
52
+ ['1', 1],
53
+ ['2', 2],
54
+ ['3', 3],
55
+ ])
56
+
57
+ const kinds = new Set(kindsMap.keys())
58
+
59
+ export const isMalwareKind = (
60
+ value?: string,
61
+ ): value is MalwareKinds => kinds.has(value as MalwareKinds)
62
+
63
+ export const asMalwareKind = (value?: string): MalwareKinds => {
64
+ if (!isMalwareKind(value)) {
65
+ throw error('Expected a valid malware kind', {
66
+ found: value,
67
+ validOptions: Array.from(kinds),
68
+ })
69
+ }
70
+ return value
71
+ }
72
+
73
+ export const parseInternals = (
74
+ nodes: PostcssNode[],
75
+ ): { kind: MalwareKinds; comparator: MalwareComparator } => {
76
+ // Handle case where no parameters are provided (parameterless :malware)
77
+ if (!nodes[0]) {
78
+ return { kind: undefined, comparator: undefined }
79
+ }
80
+
81
+ const selectorNode = asPostcssNodeWithChildren(nodes[0])
82
+ if (!selectorNode.nodes[0]) {
83
+ return { kind: undefined, comparator: undefined }
84
+ }
85
+
86
+ let kindValue = ''
87
+ let comparator: MalwareComparator = undefined
88
+ let kind: MalwareKinds
89
+
90
+ // Parse the parameter (kind with optional comparator)
91
+ if (isStringNode(selectorNode.nodes[0])) {
92
+ kindValue = removeQuotes(
93
+ asStringNode(selectorNode.nodes[0]).value,
94
+ )
95
+ } else if (isTagNode(selectorNode.nodes[0])) {
96
+ kindValue = asTagNode(selectorNode.nodes[0]).value
97
+ }
98
+
99
+ // Extract comparator if present
100
+ if (kindValue.startsWith('>=')) {
101
+ comparator = '>='
102
+ kindValue = kindValue.substring(2)
103
+ } else if (kindValue.startsWith('<=')) {
104
+ comparator = '<='
105
+ kindValue = kindValue.substring(2)
106
+ } else if (kindValue.startsWith('>')) {
107
+ comparator = '>'
108
+ kindValue = kindValue.substring(1)
109
+ } else if (kindValue.startsWith('<')) {
110
+ comparator = '<'
111
+ kindValue = kindValue.substring(1)
112
+ }
113
+
114
+ // Validate the kind without comparator
115
+ if (!comparator) {
116
+ kind = asMalwareKind(kindValue)
117
+ } else {
118
+ // For comparisons, just make sure it's a valid numeric value or a valid kind
119
+ if (isMalwareKind(kindValue)) {
120
+ kind = kindValue
121
+ } else {
122
+ throw error(
123
+ 'Expected a valid malware kind or number between 0-3',
124
+ {
125
+ found: kindValue,
126
+ },
127
+ )
128
+ }
129
+ }
130
+
131
+ return { kind, comparator }
132
+ }
133
+
134
+ /**
135
+ * :malware Pseudo-Selector, matches nodes with malware alerts.
136
+ *
137
+ * Usage:
138
+ * - :malware - matches malware with severity >= medium (critical, high, medium but not low)
139
+ * - :malware(critical) - matches specific malware kind
140
+ * - :malware(>1) - matches malware with severity greater than 1
141
+ * - :malware(">=medium") - matches malware with severity >= medium
142
+ */
143
+ export const malware = async (state: ParserState) => {
144
+ assertSecurityArchive(state, 'malware')
145
+
146
+ let internals
147
+ try {
148
+ internals = parseInternals(
149
+ asPostcssNodeWithChildren(state.current).nodes,
150
+ )
151
+ } catch (err) {
152
+ throw error('Failed to parse :malware selector', { cause: err })
153
+ }
154
+
155
+ const { kind, comparator } = internals
156
+ const alertName = comparator ? undefined : kindsMap.get(kind)
157
+
158
+ for (const node of state.partial.nodes) {
159
+ const report = state.securityArchive.get(node.id)
160
+ // Always exclude nodes that don't have security data or alerts
161
+ if (!report?.alerts || report.alerts.length === 0) {
162
+ removeNode(state, node)
163
+ }
164
+ }
165
+
166
+ for (const node of state.partial.nodes) {
167
+ const report = state.securityArchive.get(node.id)
168
+ let exclude = true
169
+
170
+ if (report) {
171
+ if (kind === undefined && comparator === undefined) {
172
+ // Parameterless :malware - match malware alerts with severity >= medium (exclude low/gptAnomaly)
173
+ exclude = !report.alerts.some(
174
+ alert =>
175
+ alert.type === 'malware' ||
176
+ alert.type === 'gptMalware' ||
177
+ alert.type === 'gptSecurity',
178
+ )
179
+ } else if (comparator) {
180
+ // retrieve the value to compare against
181
+ const kindLevel = kindLevelMap.get(kind)
182
+ // the kindLevel value has already been validated at this point
183
+ // and thus can never return an undefined/falsy value but ts doesn't
184
+ // know about that, so we have the extra check here
185
+ /* c8 ignore next - impossible */
186
+ if (kindLevel == null) break
187
+
188
+ // Check each alert to find any that match our comparison criteria
189
+ for (const alert of report.alerts) {
190
+ // Get the numerical value of the alert type
191
+ const alertType = alert.type
192
+
193
+ // retrieve a key to the current alert level to be compared against
194
+ const currentAlertLevelKey = [...kindsMap.entries()].find(
195
+ ([_, alertValue]) => alertValue === alertType,
196
+ )?.[0]
197
+
198
+ // perform the comparison based on the user-provided kindLevel
199
+ if (currentAlertLevelKey) {
200
+ const currentAlertLevel = kindLevelMap.get(
201
+ currentAlertLevelKey,
202
+ )
203
+ /* c8 ignore next - impossible but ts doesn't know */
204
+ if (currentAlertLevel == null) continue
205
+
206
+ switch (comparator) {
207
+ case '>':
208
+ if (currentAlertLevel > kindLevel) {
209
+ exclude = false
210
+ }
211
+ break
212
+ case '<':
213
+ if (currentAlertLevel < kindLevel) {
214
+ exclude = false
215
+ }
216
+ break
217
+ case '>=':
218
+ if (currentAlertLevel >= kindLevel) {
219
+ exclude = false
220
+ }
221
+ break
222
+ case '<=':
223
+ if (currentAlertLevel <= kindLevel) {
224
+ exclude = false
225
+ }
226
+ break
227
+ }
228
+
229
+ // If we've found a match, no need to check other alerts
230
+ if (!exclude) break
231
+ }
232
+ }
233
+ } else {
234
+ // Original exact match behavior
235
+ exclude = !report.alerts.some(
236
+ alert => alert.type === alertName,
237
+ )
238
+ }
239
+ }
240
+
241
+ if (exclude) {
242
+ removeNode(state, node)
243
+ }
244
+ }
245
+
246
+ removeDanglingEdges(state)
247
+
248
+ return state
249
+ }
@@ -0,0 +1,9 @@
1
+ import { createSecuritySelectorFilter } from './helpers.ts'
2
+
3
+ /**
4
+ * Filters out any node that does not have a **minifiedFile** report alert.
5
+ */
6
+ export const minified = createSecuritySelectorFilter(
7
+ 'minified',
8
+ 'minifiedFile',
9
+ )
@@ -0,0 +1,16 @@
1
+ import type { ParserState } from '../types.ts'
2
+
3
+ /**
4
+ * :missing Pseudo-Selector, matches only edges that are not linked to any node.
5
+ * It filters out any edges that have a 'to' property, keeping only dangling edges
6
+ * and clears all nodes from the result.
7
+ */
8
+ export const missing = async (state: ParserState) => {
9
+ for (const edge of state.partial.edges) {
10
+ if (edge.to) {
11
+ state.partial.edges.delete(edge)
12
+ }
13
+ }
14
+ state.partial.nodes.clear()
15
+ return state
16
+ }
@@ -0,0 +1,9 @@
1
+ import { createSecuritySelectorFilter } from './helpers.ts'
2
+
3
+ /**
4
+ * Filters out any node that does not have a **hasNativeCode** report alert.
5
+ */
6
+ export const nativeParser = createSecuritySelectorFilter(
7
+ 'native',
8
+ 'hasNativeCode',
9
+ )
@@ -0,0 +1,9 @@
1
+ import { createSecuritySelectorFilter } from './helpers.ts'
2
+
3
+ /**
4
+ * Filters out any node that does not have a **networkAccess** report alert.
5
+ */
6
+ export const network = createSecuritySelectorFilter(
7
+ 'network',
8
+ 'networkAccess',
9
+ )
@@ -0,0 +1,9 @@
1
+ import { createSecuritySelectorFilter } from './helpers.ts'
2
+
3
+ /**
4
+ * Filters out any node that does not have an **obfuscatedFile** report alert.
5
+ */
6
+ export const obfuscated = createSecuritySelectorFilter(
7
+ 'obfuscated',
8
+ 'obfuscatedFile',
9
+ )
@@ -0,0 +1,18 @@
1
+ import type { ParserState } from '../types.ts'
2
+ import { removeEdge, removeUnlinkedNodes } from './helpers.ts'
3
+
4
+ /**
5
+ * :optional Pseudo-Selector will only match optional dependencies.
6
+ */
7
+ export const optional = async (state: ParserState) => {
8
+ // filter edges that aren't marked as optional
9
+ for (const edge of state.partial.edges) {
10
+ if (!edge.optional) {
11
+ removeEdge(state, edge)
12
+ }
13
+ }
14
+
15
+ removeUnlinkedNodes(state)
16
+
17
+ return state
18
+ }