@cdxgen/cdxgen 13.6.0 → 13.7.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 (264) hide show
  1. package/README.md +15 -8
  2. package/bin/audit.js +7 -4
  3. package/bin/cdxgen.js +256 -37
  4. package/bin/convert.js +146 -22
  5. package/bin/evinse.js +25 -3
  6. package/bin/repl.js +47 -3
  7. package/bin/sign.js +211 -34
  8. package/bin/tracebom.js +18 -6
  9. package/bin/validate.js +18 -3
  10. package/bin/verify.js +113 -5
  11. package/data/behavior-taxonomy.schema.json +1491 -0
  12. package/data/cyclonedx-2.0-bundled.schema.json +18606 -5528
  13. package/data/cyclonedx-2.0-cryptography-defs.schema.json +857 -0
  14. package/data/pqc-migration.json +239 -0
  15. package/data/remediations.json +52 -0
  16. package/data/rules/cbom-security.yaml +4 -3
  17. package/data/rules/package-integrity.yaml +28 -0
  18. package/lib/audit/index.js +25 -3
  19. package/lib/cli/bomAssembly.js +80 -1
  20. package/lib/cli/cliOptions.js +121 -17
  21. package/lib/cli/index.js +609 -898
  22. package/lib/cli/jsBom.js +3 -0
  23. package/lib/cli/jvmBom.js +365 -40
  24. package/lib/cli/managedBom.js +19 -3
  25. package/lib/cli/nativeBom.js +10 -0
  26. package/lib/core/env.js +26 -0
  27. package/lib/core/fs.js +126 -3
  28. package/lib/core/globPatterns.js +218 -0
  29. package/lib/core/processTree.js +99 -12
  30. package/lib/core/projectConfig.js +332 -25
  31. package/lib/core/toolRegistry.js +15 -1
  32. package/lib/ecosystems/denoutils.js +2 -2
  33. package/lib/ecosystems/golem.js +3 -8
  34. package/lib/ecosystems/gradleutils.js +1082 -6
  35. package/lib/ecosystems/jsEvidence.js +90 -38
  36. package/lib/ecosystems/kosi.js +3 -5
  37. package/lib/ecosystems/npmVersionResolution.js +376 -0
  38. package/lib/ecosystems/parsers-python.js +89 -28
  39. package/lib/ecosystems/parsers-rust.js +117 -15
  40. package/lib/ecosystems/remote/huggingface.js +22 -0
  41. package/lib/ecosystems/rusi.js +2 -4
  42. package/lib/ecosystems/scalasem.js +2 -1
  43. package/lib/evinser/evinser.js +196 -34
  44. package/lib/evinser/scalasem.js +5 -1
  45. package/lib/helpers/agenticToolutils.js +112 -1
  46. package/lib/helpers/bomSigner.js +43 -9
  47. package/lib/helpers/jssSigner.js +1276 -0
  48. package/lib/inventory/agentBlueprint.js +193 -0
  49. package/lib/inventory/agentToolNames.js +145 -0
  50. package/lib/inventory/aiCollector.js +153 -7
  51. package/lib/inventory/aiInventory.js +44 -8
  52. package/lib/inventory/aiProviders.js +324 -0
  53. package/lib/inventory/bomUtils.js +8 -1
  54. package/lib/inventory/ciParsers/githubActionsLock.js +821 -0
  55. package/lib/inventory/citations.js +31 -2
  56. package/lib/inventory/communityAiConfigParser.js +978 -80
  57. package/lib/inventory/cyclonedx20.js +152 -0
  58. package/lib/inventory/cyclonedx20Common.js +286 -0
  59. package/lib/inventory/cyclonedx20Derived.js +159 -0
  60. package/lib/inventory/cyclonedx20Parties.js +552 -0
  61. package/lib/inventory/cyclonedx20Read.js +768 -0
  62. package/lib/inventory/cyclonedx20ReadCommon.js +374 -0
  63. package/lib/inventory/cyclonedx20ReadComponents.js +692 -0
  64. package/lib/inventory/cyclonedx20ReadContext.js +380 -0
  65. package/lib/inventory/cyclonedx20ReadEvidence.js +466 -0
  66. package/lib/inventory/cyclonedx20ReadSections.js +599 -0
  67. package/lib/inventory/cyclonedx20Render.js +766 -0
  68. package/lib/inventory/cyclonedx20RenderComponents.js +716 -0
  69. package/lib/inventory/cyclonedx20RenderDefinitions.js +405 -0
  70. package/lib/inventory/cyclonedx20RenderEvidence.js +726 -0
  71. package/lib/inventory/cyclonedx20RenderHardware.js +203 -0
  72. package/lib/inventory/cyclonedx20RenderPatents.js +174 -0
  73. package/lib/inventory/cyclonedx20RenderRefIds.js +314 -0
  74. package/lib/inventory/cyclonedx20RenderSections.js +499 -0
  75. package/lib/inventory/cyclonedx20Tables.js +594 -0
  76. package/lib/inventory/deps.js +106 -13
  77. package/lib/inventory/dosai.js +236 -1
  78. package/lib/inventory/dosaiParsers.js +3 -6
  79. package/lib/inventory/dotnetRuntime.js +164 -14
  80. package/lib/inventory/ecosystemFormulationParsers.js +979 -0
  81. package/lib/inventory/envcontext.js +132 -38
  82. package/lib/inventory/evidenceUtils.js +2 -1
  83. package/lib/inventory/formulationParsers.js +95 -13
  84. package/lib/inventory/hbom.js +45 -6
  85. package/lib/inventory/hbomLoader.js +166 -0
  86. package/lib/inventory/mcpConfigParser.js +5 -0
  87. package/lib/inventory/occurrenceKey.js +90 -0
  88. package/lib/inventory/pqcMigration.js +426 -0
  89. package/lib/inventory/rustFormulationParser.js +21 -10
  90. package/lib/inventory/source.js +65 -6
  91. package/lib/parsers/huggingfaceManifest.js +9 -1
  92. package/lib/parsers/huggingfaceUtils.js +18 -0
  93. package/lib/parsers/kiroPermissions.js +166 -0
  94. package/lib/server/server.js +36 -34
  95. package/lib/stages/postgen/postgen.js +41 -1
  96. package/lib/stages/postgen/specVersionCompat.js +76 -0
  97. package/lib/validator/bomValidator.js +8 -1
  98. package/lib/validator/complianceEngine.js +30 -3
  99. package/lib/validator/complianceRules.js +1416 -4
  100. package/lib/validator/generated/validate-2.0.mjs +57554 -13851
  101. package/lib/validator/index.js +48 -7
  102. package/lib/validator/reporters/json.js +6 -0
  103. package/package.json +9 -9
  104. package/types/bin/repl.d.ts.map +1 -1
  105. package/types/lib/audit/index.d.ts +6 -2
  106. package/types/lib/audit/index.d.ts.map +1 -1
  107. package/types/lib/cli/bomAssembly.d.ts +16 -0
  108. package/types/lib/cli/bomAssembly.d.ts.map +1 -1
  109. package/types/lib/cli/cliOptions.d.ts +6 -0
  110. package/types/lib/cli/cliOptions.d.ts.map +1 -1
  111. package/types/lib/cli/index.d.ts.map +1 -1
  112. package/types/lib/cli/jsBom.d.ts +2 -1
  113. package/types/lib/cli/jsBom.d.ts.map +1 -1
  114. package/types/lib/cli/jvmBom.d.ts +15 -0
  115. package/types/lib/cli/jvmBom.d.ts.map +1 -1
  116. package/types/lib/cli/managedBom.d.ts.map +1 -1
  117. package/types/lib/cli/nativeBom.d.ts.map +1 -1
  118. package/types/lib/core/boundedFile.d.ts +30 -0
  119. package/types/lib/core/boundedFile.d.ts.map +1 -1
  120. package/types/lib/core/env.d.ts +10 -0
  121. package/types/lib/core/env.d.ts.map +1 -1
  122. package/types/lib/core/fs.d.ts +4 -0
  123. package/types/lib/core/fs.d.ts.map +1 -1
  124. package/types/lib/core/globPatterns.d.ts +70 -0
  125. package/types/lib/core/globPatterns.d.ts.map +1 -0
  126. package/types/lib/core/paths.d.ts +9 -0
  127. package/types/lib/core/paths.d.ts.map +1 -1
  128. package/types/lib/core/processTree.d.ts +16 -5
  129. package/types/lib/core/processTree.d.ts.map +1 -1
  130. package/types/lib/core/projectConfig.d.ts +47 -8
  131. package/types/lib/core/projectConfig.d.ts.map +1 -1
  132. package/types/lib/core/toolRegistry.d.ts.map +1 -1
  133. package/types/lib/ecosystems/golem.d.ts.map +1 -1
  134. package/types/lib/ecosystems/gradleutils.d.ts +248 -8
  135. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
  136. package/types/lib/ecosystems/jsEvidence.d.ts +14 -1
  137. package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
  138. package/types/lib/ecosystems/kosi.d.ts.map +1 -1
  139. package/types/lib/ecosystems/npmVersionResolution.d.ts +119 -0
  140. package/types/lib/ecosystems/npmVersionResolution.d.ts.map +1 -0
  141. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
  142. package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
  143. package/types/lib/ecosystems/pythonutils.d.ts +1 -1
  144. package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -1
  145. package/types/lib/ecosystems/rusi.d.ts.map +1 -1
  146. package/types/lib/ecosystems/scalasem.d.ts +58 -16
  147. package/types/lib/ecosystems/scalasem.d.ts.map +1 -1
  148. package/types/lib/evinser/evinser.d.ts +61 -9
  149. package/types/lib/evinser/evinser.d.ts.map +1 -1
  150. package/types/lib/evinser/scalasem.d.ts.map +1 -1
  151. package/types/lib/helpers/agenticToolutils.d.ts +53 -19
  152. package/types/lib/helpers/agenticToolutils.d.ts.map +1 -1
  153. package/types/lib/helpers/bomSigner.d.ts +20 -3
  154. package/types/lib/helpers/bomSigner.d.ts.map +1 -1
  155. package/types/lib/helpers/jssSigner.d.ts +288 -0
  156. package/types/lib/helpers/jssSigner.d.ts.map +1 -0
  157. package/types/lib/helpers/vsixutils.d.ts +303 -19
  158. package/types/lib/helpers/vsixutils.d.ts.map +1 -1
  159. package/types/lib/inventory/agentBlueprint.d.ts +70 -0
  160. package/types/lib/inventory/agentBlueprint.d.ts.map +1 -0
  161. package/types/lib/inventory/agentToolNames.d.ts +51 -0
  162. package/types/lib/inventory/agentToolNames.d.ts.map +1 -0
  163. package/types/lib/inventory/aiCollector.d.ts.map +1 -1
  164. package/types/lib/inventory/aiInventory.d.ts.map +1 -1
  165. package/types/lib/inventory/aiProviders.d.ts +85 -0
  166. package/types/lib/inventory/aiProviders.d.ts.map +1 -0
  167. package/types/lib/inventory/bomUtils.d.ts.map +1 -1
  168. package/types/lib/inventory/ciParsers/githubActionsLock.d.ts +101 -0
  169. package/types/lib/inventory/ciParsers/githubActionsLock.d.ts.map +1 -0
  170. package/types/lib/inventory/citations.d.ts +5 -0
  171. package/types/lib/inventory/citations.d.ts.map +1 -1
  172. package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
  173. package/types/lib/inventory/cyclonedx20.d.ts +99 -0
  174. package/types/lib/inventory/cyclonedx20.d.ts.map +1 -0
  175. package/types/lib/inventory/cyclonedx20Common.d.ts +102 -0
  176. package/types/lib/inventory/cyclonedx20Common.d.ts.map +1 -0
  177. package/types/lib/inventory/cyclonedx20Derived.d.ts +74 -0
  178. package/types/lib/inventory/cyclonedx20Derived.d.ts.map +1 -0
  179. package/types/lib/inventory/cyclonedx20Parties.d.ts +91 -0
  180. package/types/lib/inventory/cyclonedx20Parties.d.ts.map +1 -0
  181. package/types/lib/inventory/cyclonedx20Read.d.ts +51 -0
  182. package/types/lib/inventory/cyclonedx20Read.d.ts.map +1 -0
  183. package/types/lib/inventory/cyclonedx20ReadCommon.d.ts +69 -0
  184. package/types/lib/inventory/cyclonedx20ReadCommon.d.ts.map +1 -0
  185. package/types/lib/inventory/cyclonedx20ReadComponents.d.ts +77 -0
  186. package/types/lib/inventory/cyclonedx20ReadComponents.d.ts.map +1 -0
  187. package/types/lib/inventory/cyclonedx20ReadContext.d.ts +118 -0
  188. package/types/lib/inventory/cyclonedx20ReadContext.d.ts.map +1 -0
  189. package/types/lib/inventory/cyclonedx20ReadEvidence.d.ts +40 -0
  190. package/types/lib/inventory/cyclonedx20ReadEvidence.d.ts.map +1 -0
  191. package/types/lib/inventory/cyclonedx20ReadSections.d.ts +45 -0
  192. package/types/lib/inventory/cyclonedx20ReadSections.d.ts.map +1 -0
  193. package/types/lib/inventory/cyclonedx20Render.d.ts +53 -0
  194. package/types/lib/inventory/cyclonedx20Render.d.ts.map +1 -0
  195. package/types/lib/inventory/cyclonedx20RenderComponents.d.ts +73 -0
  196. package/types/lib/inventory/cyclonedx20RenderComponents.d.ts.map +1 -0
  197. package/types/lib/inventory/cyclonedx20RenderDefinitions.d.ts +29 -0
  198. package/types/lib/inventory/cyclonedx20RenderDefinitions.d.ts.map +1 -0
  199. package/types/lib/inventory/cyclonedx20RenderEvidence.d.ts +110 -0
  200. package/types/lib/inventory/cyclonedx20RenderEvidence.d.ts.map +1 -0
  201. package/types/lib/inventory/cyclonedx20RenderHardware.d.ts +42 -0
  202. package/types/lib/inventory/cyclonedx20RenderHardware.d.ts.map +1 -0
  203. package/types/lib/inventory/cyclonedx20RenderPatents.d.ts +26 -0
  204. package/types/lib/inventory/cyclonedx20RenderPatents.d.ts.map +1 -0
  205. package/types/lib/inventory/cyclonedx20RenderRefIds.d.ts +55 -0
  206. package/types/lib/inventory/cyclonedx20RenderRefIds.d.ts.map +1 -0
  207. package/types/lib/inventory/cyclonedx20RenderSections.d.ts +45 -0
  208. package/types/lib/inventory/cyclonedx20RenderSections.d.ts.map +1 -0
  209. package/types/lib/inventory/cyclonedx20Tables.d.ts +134 -0
  210. package/types/lib/inventory/cyclonedx20Tables.d.ts.map +1 -0
  211. package/types/lib/inventory/deps.d.ts +35 -2
  212. package/types/lib/inventory/deps.d.ts.map +1 -1
  213. package/types/lib/inventory/dosai.d.ts +19 -0
  214. package/types/lib/inventory/dosai.d.ts.map +1 -1
  215. package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
  216. package/types/lib/inventory/dotnetRuntime.d.ts +51 -4
  217. package/types/lib/inventory/dotnetRuntime.d.ts.map +1 -1
  218. package/types/lib/inventory/ecosystemFormulationParsers.d.ts +73 -0
  219. package/types/lib/inventory/ecosystemFormulationParsers.d.ts.map +1 -0
  220. package/types/lib/inventory/envcontext.d.ts +22 -6
  221. package/types/lib/inventory/envcontext.d.ts.map +1 -1
  222. package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
  223. package/types/lib/inventory/formulationParsers.d.ts.map +1 -1
  224. package/types/lib/inventory/hbom.d.ts +11 -0
  225. package/types/lib/inventory/hbom.d.ts.map +1 -1
  226. package/types/lib/inventory/hbomLoader.d.ts +54 -0
  227. package/types/lib/inventory/hbomLoader.d.ts.map +1 -1
  228. package/types/lib/inventory/jvmLocalRepos.d.ts +5 -1
  229. package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -1
  230. package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
  231. package/types/lib/inventory/occurrenceKey.d.ts +52 -0
  232. package/types/lib/inventory/occurrenceKey.d.ts.map +1 -0
  233. package/types/lib/inventory/pqcMigration.d.ts +84 -0
  234. package/types/lib/inventory/pqcMigration.d.ts.map +1 -0
  235. package/types/lib/inventory/rustFormulationParser.d.ts +7 -2
  236. package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
  237. package/types/lib/inventory/scalaCoords.d.ts.map +1 -1
  238. package/types/lib/inventory/scalaDedupe.d.ts +43 -0
  239. package/types/lib/inventory/scalaDedupe.d.ts.map +1 -0
  240. package/types/lib/inventory/source.d.ts +15 -0
  241. package/types/lib/inventory/source.d.ts.map +1 -1
  242. package/types/lib/parsers/huggingfaceManifest.d.ts +10 -1
  243. package/types/lib/parsers/huggingfaceManifest.d.ts.map +1 -1
  244. package/types/lib/parsers/huggingfaceUtils.d.ts +12 -0
  245. package/types/lib/parsers/huggingfaceUtils.d.ts.map +1 -1
  246. package/types/lib/parsers/kiroPermissions.d.ts +62 -0
  247. package/types/lib/parsers/kiroPermissions.d.ts.map +1 -0
  248. package/types/lib/parsers/vscodeExtensionId.d.ts +48 -0
  249. package/types/lib/parsers/vscodeExtensionId.d.ts.map +1 -0
  250. package/types/lib/server/server.d.ts.map +1 -1
  251. package/types/lib/stages/postgen/postgen.d.ts +7 -0
  252. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  253. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
  254. package/types/lib/stages/postgen/specVersionCompat.d.ts +25 -0
  255. package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
  256. package/types/lib/validator/bomValidator.d.ts +5 -1
  257. package/types/lib/validator/bomValidator.d.ts.map +1 -1
  258. package/types/lib/validator/complianceEngine.d.ts +4 -4
  259. package/types/lib/validator/complianceEngine.d.ts.map +1 -1
  260. package/types/lib/validator/complianceRules.d.ts +14 -2
  261. package/types/lib/validator/complianceRules.d.ts.map +1 -1
  262. package/types/lib/validator/index.d.ts +10 -0
  263. package/types/lib/validator/index.d.ts.map +1 -1
  264. package/types/lib/validator/reporters/json.d.ts.map +1 -1
@@ -1,5 +1,8 @@
1
+ import { realpathSync } from "node:fs";
1
2
  import path from "node:path";
2
3
 
4
+ import { HOST_SCOPE_PROJECT_TYPES } from "./env.js";
5
+ import { boundUntrustedGlobPatterns } from "./globPatterns.js";
3
6
  import {
4
7
  isReservedMetadataPropertyName,
5
8
  METADATA_PROPERTY_CONFIG_KEYS,
@@ -17,10 +20,162 @@ export const PROJECT_CONFIG_FILENAMES = [
17
20
  ".cdxgen.yaml",
18
21
  ];
19
22
 
23
+ /**
24
+ * Options a config file may set, by their kebab-case name. A config file may
25
+ * also use the camelCase spelling of each, or one of the aliases in
26
+ * PROJECT_CONFIG_OPTION_ALIASES. Every other key is ignored: options that
27
+ * only library callers pass (such as `dosaiCommand` or `traceCmd`), yargs'
28
+ * own `extends`, and anything cdxgen does not know.
29
+ *
30
+ * Every option bin/cdxgen.js declares is either here or in
31
+ * OPERATOR_ONLY_OPTIONS, which projectConfig.poku.js checks.
32
+ */
33
+ export const PROJECT_CONFIG_OPTIONS = new Set([
34
+ "activity-report",
35
+ "api-key",
36
+ "author",
37
+ "auto-compositions",
38
+ "auto-create",
39
+ "bom-audit",
40
+ "bom-audit-categories",
41
+ "bom-audit-fail-severity",
42
+ "bom-audit-include-trusted",
43
+ "bom-audit-max-targets",
44
+ "bom-audit-min-severity",
45
+ "bom-audit-only-trusted",
46
+ "bom-audit-rules-dir",
47
+ "bom-audit-scope",
48
+ "cache",
49
+ "cache-ttl",
50
+ "callsites-slices-file",
51
+ "caxa-app-dir",
52
+ "cmake-cache",
53
+ "color",
54
+ "compile-commands",
55
+ "component-type",
56
+ "data-flow-slices-file",
57
+ "deep",
58
+ "deps-slices-file",
59
+ "dry-run",
60
+ "env-audit",
61
+ "evidence",
62
+ "evinse-output",
63
+ "exclude",
64
+ "exclude-type",
65
+ "experimental-mcp-pinning",
66
+ "export-proto",
67
+ "fail-on-error",
68
+ "filter",
69
+ "format",
70
+ "generate-key-and-sign",
71
+ "git-branch",
72
+ "include-crypto",
73
+ "include-formulation",
74
+ "include-regex",
75
+ "include-release-notes",
76
+ "include-runtime",
77
+ "install-deps",
78
+ "introspect",
79
+ "introspect-annotate",
80
+ "introspect-fail-below",
81
+ "introspect-json",
82
+ "introspect-report",
83
+ "is-latest",
84
+ "json-pretty",
85
+ "license-policy",
86
+ "license-ref",
87
+ "lifecycle",
88
+ "log-format",
89
+ "metadata-property",
90
+ "min-confidence",
91
+ "no-babel",
92
+ "no-banner",
93
+ "no-ignore",
94
+ "only",
95
+ "openapi-spec-file",
96
+ "output",
97
+ "package-extensions",
98
+ "parent-project-id",
99
+ "parent-project-name",
100
+ "parent-project-version",
101
+ "print",
102
+ "profile",
103
+ "progress",
104
+ "project-group",
105
+ "project-id",
106
+ "project-name",
107
+ "project-tag",
108
+ "project-version",
109
+ "proto-bin-file",
110
+ "quiet",
111
+ "reachables-slices-file",
112
+ "recurse",
113
+ "release-notes-current-tag",
114
+ "release-notes-previous-tag",
115
+ "required-only",
116
+ "resolve-class",
117
+ "rust",
118
+ "scalasem",
119
+ "scalasem-include-tests",
120
+ "semantics-slices-file",
121
+ "server-url",
122
+ "spec-version",
123
+ "standard",
124
+ "tea-artifact-url",
125
+ "tea-author-email",
126
+ "tea-author-name",
127
+ "tea-collection-name",
128
+ "tea-fetch",
129
+ "tea-leaf-identifier",
130
+ "tea-publish",
131
+ "tea-reason",
132
+ "tea-token",
133
+ "technique",
134
+ "tlp-classification",
135
+ "tui",
136
+ "type",
137
+ "usages-slices-file",
138
+ "validate",
139
+ "verbose",
140
+ ]);
141
+
142
+ /**
143
+ * Options only the operator may set, on the command line or through a
144
+ * CDXGEN_* environment variable. They choose an executable, start a listener,
145
+ * turn off certificate checks on the operator's own upload, opt into
146
+ * experimental tool installation, or end the run without a BOM. A config file
147
+ * that sets one is told so and ignored.
148
+ */
149
+ export const OPERATOR_ONLY_OPTIONS = new Set([
150
+ "feature-flags",
151
+ "help",
152
+ "scalasem-command",
153
+ "server",
154
+ "server-host",
155
+ "server-port",
156
+ "skip-dt-tls-check",
157
+ ]);
158
+
159
+ /**
160
+ * Alternate option names a config file may use, with the option each one
161
+ * stands for. yargs applies its aliases to config keys too, so each is
162
+ * checked as the option it names.
163
+ */
164
+ export const PROJECT_CONFIG_OPTION_ALIASES = new Map([
165
+ ["c", "resolve-class"],
166
+ ["exclude-regex", "exclude"],
167
+ ["h", "help"],
168
+ ["o", "output"],
169
+ ["p", "print"],
170
+ ["q", "quiet"],
171
+ ["r", "recurse"],
172
+ ["t", "type"],
173
+ ]);
174
+
20
175
  /**
21
176
  * Options whose values name a file or directory cdxgen writes to or reads
22
177
  * from. A config file may set these, but only to a location inside its own
23
- * directory.
178
+ * directory, after symbolic links are resolved.
24
179
  */
25
180
  const PATH_OPTIONS = new Set([
26
181
  "output",
@@ -28,14 +183,26 @@ const PATH_OPTIONS = new Set([
28
183
  "proto-bin-file",
29
184
  "deps-slices-file",
30
185
  "usages-slices-file",
186
+ "callsites-slices-file",
31
187
  "data-flow-slices-file",
32
188
  "reachables-slices-file",
33
189
  "semantics-slices-file",
34
190
  "openapi-spec-file",
35
191
  "license-policy",
36
192
  "bom-audit-rules-dir",
193
+ "introspect-report",
194
+ "introspect-json",
195
+ "cmake-cache",
196
+ "compile-commands",
197
+ "caxa-app-dir",
37
198
  ]);
38
199
 
200
+ /**
201
+ * Options whose values are glob patterns matched against every file a scan
202
+ * walks, in the order they draw on the shared expansion budget.
203
+ */
204
+ const PATTERN_OPTIONS = ["exclude", "include-regex"];
205
+
39
206
  /**
40
207
  * Options that change where cdxgen sends data or how much of the host it
41
208
  * touches. These stay usable, but never silently.
@@ -64,9 +231,53 @@ function optionSpellings(option) {
64
231
  return camel === option ? [option] : [option, camel];
65
232
  }
66
233
 
234
+ /**
235
+ * Find the option a config key names.
236
+ *
237
+ * @param {string} key config file key
238
+ * @returns {string|undefined} kebab-case option name, or undefined when the
239
+ * key is not the kebab-case spelling, the camelCase spelling or an alias of
240
+ * an option cdxgen declares
241
+ */
242
+ function configKeyOption(key) {
243
+ if (PROJECT_CONFIG_OPTION_ALIASES.has(key)) {
244
+ return PROJECT_CONFIG_OPTION_ALIASES.get(key);
245
+ }
246
+ const kebab = key.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
247
+ if (!PROJECT_CONFIG_OPTIONS.has(kebab) && !OPERATOR_ONLY_OPTIONS.has(kebab)) {
248
+ return undefined;
249
+ }
250
+ return optionSpellings(kebab).includes(key) ? kebab : undefined;
251
+ }
252
+
253
+ /**
254
+ * Resolve a path through the symbolic links of its longest existing prefix.
255
+ * The part that does not exist yet is appended unchanged.
256
+ *
257
+ * @param {string} absolutePath absolute path to resolve
258
+ * @returns {string} the path with every existing component resolved
259
+ */
260
+ function resolveExistingPrefix(absolutePath) {
261
+ const missing = [];
262
+ let current = absolutePath;
263
+ for (;;) {
264
+ try {
265
+ return path.join(realpathSync(current), ...missing.reverse());
266
+ } catch {
267
+ const parent = path.dirname(current);
268
+ if (parent === current) {
269
+ return absolutePath;
270
+ }
271
+ missing.push(path.basename(current));
272
+ current = parent;
273
+ }
274
+ }
275
+ }
276
+
67
277
  /**
68
278
  * Determine whether a config-supplied path stays inside the config's own
69
- * directory.
279
+ * directory once symbolic links are resolved, so a link committed to the
280
+ * scanned tree cannot carry a path option outside it.
70
281
  *
71
282
  * @param {string} configDir directory the config file was read from
72
283
  * @param {unknown} value config-supplied path value
@@ -76,40 +287,95 @@ function isContainedPath(configDir, value) {
76
287
  if (typeof value !== "string" || !value.length) {
77
288
  return false;
78
289
  }
79
- const relativePath = path.relative(
80
- path.resolve(configDir),
81
- path.resolve(configDir, value),
82
- );
290
+ const root = resolveExistingPrefix(path.resolve(configDir));
291
+ const target = resolveExistingPrefix(path.resolve(configDir, value));
292
+ const relativePath = path.relative(root, target);
83
293
  return (
84
294
  relativePath === "" ||
85
- (!relativePath.startsWith("..") && !path.isAbsolute(relativePath))
295
+ (relativePath !== ".." &&
296
+ !relativePath.startsWith(`..${path.sep}`) &&
297
+ !path.isAbsolute(relativePath))
86
298
  );
87
299
  }
88
300
 
301
+ /**
302
+ * Split a config-supplied project type value into the individual types.
303
+ *
304
+ * @param {unknown} value `type` value from a config file
305
+ * @returns {string[]} trimmed, non-empty project types
306
+ */
307
+ function projectTypeList(value) {
308
+ return (Array.isArray(value) ? value : [value])
309
+ .flatMap((entry) => `${entry ?? ""}`.split(","))
310
+ .map((entry) => entry.trim())
311
+ .filter(Boolean);
312
+ }
313
+
89
314
  /**
90
315
  * Apply the trust boundary between cdxgen and the directory it is scanning.
91
316
  *
92
317
  * A config file lives in the tree under analysis, so it is attacker-controlled
93
- * whenever that tree is. Path options are therefore confined to the config's
94
- * own directory - a scanned project may choose where in itself the BOM lands,
95
- * but not write outside it - and options that redirect data off the host or
96
- * widen what cdxgen executes are announced on the console. A project may
97
- * describe itself with custom metadata properties, but not in the namespaces
98
- * cdxgen reserves for the facts it establishes, so a scanned tree cannot pass
99
- * its own claims off as cdxgen's.
318
+ * whenever that tree is. It may therefore only set the options listed in
319
+ * PROJECT_CONFIG_OPTIONS: options that choose an executable, start a listener
320
+ * or weaken the operator's own checks, and every key cdxgen does not declare
321
+ * as an option, are dropped. Path options are confined to the config's own
322
+ * directory - a scanned project may choose where in itself the BOM lands, but
323
+ * not write outside it - and project types that inventory the host rather
324
+ * than the scan path are dropped. Exclude and include patterns are kept
325
+ * within the bounds of boundUntrustedGlobPatterns, so a project cannot make
326
+ * every file match against thousands of brace-expanded patterns. Options that
327
+ * redirect data off the host or widen what cdxgen does are announced on the
328
+ * console. A project may describe itself with custom metadata properties, but
329
+ * not in the namespaces cdxgen reserves for the facts it establishes, so a
330
+ * scanned tree cannot pass its own claims off as cdxgen's.
100
331
  *
101
332
  * @param {Object} config parsed config file contents
102
333
  * @param {string} configDir directory the config file was read from
103
- * @returns {{config: Object, rejected: string[], announced: string[], reservedProperties: string[]}} sanitized config and what it did
334
+ * @returns {{config: Object, rejected: string[], announced: string[], reservedProperties: string[], ignored: string[], operatorOnly: string[], hostScopeTypes: string[], refusedPatterns: {key: string, pattern: string, reason: string}[]}} sanitized config and what it did
104
335
  */
105
336
  export function sanitizeProjectConfig(config, configDir) {
106
- if (!config || typeof config !== "object") {
107
- return { announced: [], config: {}, rejected: [], reservedProperties: [] };
337
+ const result = {
338
+ announced: [],
339
+ config: {},
340
+ hostScopeTypes: [],
341
+ ignored: [],
342
+ operatorOnly: [],
343
+ refusedPatterns: [],
344
+ rejected: [],
345
+ reservedProperties: [],
346
+ };
347
+ if (!config || typeof config !== "object" || Array.isArray(config)) {
348
+ return result;
349
+ }
350
+ const sanitized = result.config;
351
+ const aliasEntries = [];
352
+ for (const [key, value] of Object.entries(config)) {
353
+ if (METADATA_PROPERTY_CONFIG_KEYS.includes(key)) {
354
+ sanitized[key] = value;
355
+ continue;
356
+ }
357
+ const option = configKeyOption(key);
358
+ if (!option) {
359
+ result.ignored.push(key);
360
+ continue;
361
+ }
362
+ if (OPERATOR_ONLY_OPTIONS.has(option)) {
363
+ result.operatorOnly.push(key);
364
+ continue;
365
+ }
366
+ if (PROJECT_CONFIG_OPTION_ALIASES.has(key)) {
367
+ aliasEntries.push([option, value]);
368
+ continue;
369
+ }
370
+ sanitized[key] = value;
371
+ }
372
+ // An alias is stored under the option it names, so the checks below see
373
+ // it. A spelled-out option in the same file takes precedence.
374
+ for (const [option, value] of aliasEntries) {
375
+ if (optionSpellings(option).every((key) => sanitized[key] === undefined)) {
376
+ sanitized[option] = value;
377
+ }
108
378
  }
109
- const sanitized = { ...config };
110
- const rejected = [];
111
- const announced = [];
112
- const reservedProperties = [];
113
379
  for (const key of METADATA_PROPERTY_CONFIG_KEYS) {
114
380
  if (sanitized[key] === undefined) {
115
381
  continue;
@@ -117,7 +383,7 @@ export function sanitizeProjectConfig(config, configDir) {
117
383
  const properties = [];
118
384
  for (const aprop of parseMetadataProperties(sanitized[key])) {
119
385
  if (isReservedMetadataPropertyName(aprop.name)) {
120
- reservedProperties.push(aprop.name);
386
+ result.reservedProperties.push(aprop.name);
121
387
  } else {
122
388
  properties.push(aprop);
123
389
  }
@@ -134,17 +400,58 @@ export function sanitizeProjectConfig(config, configDir) {
134
400
  continue;
135
401
  }
136
402
  if (!isContainedPath(configDir, sanitized[key])) {
137
- rejected.push(`${key}=${sanitized[key]}`);
403
+ result.rejected.push(`${key}=${sanitized[key]}`);
138
404
  delete sanitized[key];
139
405
  }
140
406
  }
141
407
  }
408
+ if (sanitized.type !== undefined) {
409
+ const types = projectTypeList(sanitized.type);
410
+ const projectTypes = types.filter(
411
+ (type) => !HOST_SCOPE_PROJECT_TYPES.has(type.toLowerCase()),
412
+ );
413
+ result.hostScopeTypes = types.filter(
414
+ (type) => !projectTypes.includes(type),
415
+ );
416
+ if (result.hostScopeTypes.length) {
417
+ if (projectTypes.length) {
418
+ sanitized.type = projectTypes;
419
+ } else {
420
+ delete sanitized.type;
421
+ }
422
+ }
423
+ }
424
+ // Exclude patterns, then include patterns, share one expansion budget.
425
+ let patternsUsed = 0;
426
+ for (const option of PATTERN_OPTIONS) {
427
+ for (const key of optionSpellings(option)) {
428
+ if (sanitized[key] === undefined) {
429
+ continue;
430
+ }
431
+ const { accepted, expansions, refused } = boundUntrustedGlobPatterns(
432
+ sanitized[key],
433
+ { used: patternsUsed },
434
+ );
435
+ patternsUsed += expansions;
436
+ for (const entry of refused) {
437
+ result.refusedPatterns.push({ key, ...entry });
438
+ }
439
+ if (!refused.length) {
440
+ continue;
441
+ }
442
+ if (!accepted.length) {
443
+ delete sanitized[key];
444
+ } else {
445
+ sanitized[key] = Array.isArray(sanitized[key]) ? accepted : accepted[0];
446
+ }
447
+ }
448
+ }
142
449
  for (const option of ANNOUNCED_OPTIONS) {
143
450
  for (const key of optionSpellings(option)) {
144
451
  if (sanitized[key] !== undefined) {
145
- announced.push(key);
452
+ result.announced.push(key);
146
453
  }
147
454
  }
148
455
  }
149
- return { announced, config: sanitized, rejected, reservedProperties };
456
+ return result;
150
457
  }
@@ -48,6 +48,16 @@ const WRAPPER_BASENAMES = new Set([
48
48
  "zsh",
49
49
  ]);
50
50
 
51
+ /**
52
+ * Commands cdxgen runs to learn about the machine it runs on rather than the
53
+ * project, such as `ldd --version` choosing between the glibc and musl
54
+ * plugin binaries. They did not help generate the BOM, so they are not tools
55
+ * of it.
56
+ *
57
+ * @type {Set<string>}
58
+ */
59
+ const HOST_PROBE_BASENAMES = new Set(["ldd"]);
60
+
51
61
  /**
52
62
  * Strip a trailing Windows executable suffix from a binary name.
53
63
  *
@@ -97,7 +107,11 @@ export function recordExecutedTool(name, version) {
97
107
  return;
98
108
  }
99
109
  const toolName = toolNameOf(name);
100
- if (!toolName || WRAPPER_BASENAMES.has(toolName)) {
110
+ if (
111
+ !toolName ||
112
+ WRAPPER_BASENAMES.has(toolName) ||
113
+ HOST_PROBE_BASENAMES.has(toolName)
114
+ ) {
101
115
  return;
102
116
  }
103
117
  const key = toolName.toLowerCase();
@@ -721,7 +721,7 @@ export async function parseDenoLock(lockFile, options = {}) {
721
721
  // Deterministic canonical jsr page for the original specifier, e.g.
722
722
  // `https://jsr.io/@std/assert@1.0.19`. The npm-mirror tarball URL is only
723
723
  // available after a registry lookup, so this gives every jsr component a
724
- // stable reference even without --fetch-license.
724
+ // stable reference even when FETCH_LICENSE is not set.
725
725
  const jsrHomepage = `https://jsr.io/${group ? `${group}/` : ""}${name}@${version}`;
726
726
  const pkgObj = {
727
727
  group: JSR_NPM_SCOPE,
@@ -1056,7 +1056,7 @@ export async function parseDenoLock(lockFile, options = {}) {
1056
1056
 
1057
1057
  // Offline enrichment first: mine license/description/repository from a
1058
1058
  // project's node_modules and the global deno npm cache. This needs no
1059
- // network and runs even without --fetch-license.
1059
+ // network and runs even when FETCH_LICENSE is not set.
1060
1060
  mineInstalledMetadata(
1061
1061
  pkgList,
1062
1062
  options.projectRoot || dirname(lockFile),
@@ -20,6 +20,7 @@ import {
20
20
  hasInlineCredential,
21
21
  sanitizeBomUrl,
22
22
  } from "../core/propertySanitizer.js";
23
+ import { occurrenceLocationKey } from "../inventory/occurrenceKey.js";
23
24
  import { resolvePluginBinary } from "../inventory/plugins.js";
24
25
  import { canonicalPurlFromLooseString } from "../inventory/purl.js";
25
26
 
@@ -93,20 +94,14 @@ function rangeLocation(range) {
93
94
  if (!start?.filename) {
94
95
  return undefined;
95
96
  }
96
- if (start.line && start.line > 0) {
97
- return `${start.filename}#${start.line}`;
98
- }
99
- return start.filename;
97
+ return occurrenceLocationKey(start.filename, start.line);
100
98
  }
101
99
 
102
100
  function positionLocation(position) {
103
101
  if (!position?.filename) {
104
102
  return undefined;
105
103
  }
106
- if (position.line && position.line > 0) {
107
- return `${position.filename}#${position.line}`;
108
- }
109
- return position.filename;
104
+ return occurrenceLocationKey(position.filename, position.line);
110
105
  }
111
106
 
112
107
  function purlWithoutVersion(purl) {