@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
@@ -0,0 +1,599 @@
1
+ /**
2
+ * Reader converters for the CycloneDX 2.0 document sections other than the
3
+ * inventories: metadata, compositions, annotations, vulnerabilities,
4
+ * citations and formulation. Each frozen spec is the allowlist of the
5
+ * members 1.x carries; everything else is a loss. Every converted section
6
+ * entry is kept as a shadow, so a read-then-render round trip restores the
7
+ * 2.0 members the 1.x model has no field for while the entry is unchanged.
8
+ */
9
+
10
+ import { isObjectRecord, reverseEnvironmentVars } from "./cyclonedx20Common.js";
11
+ import {
12
+ ATTACHMENT_RULE,
13
+ LICENSES_RULE,
14
+ PROPERTIES_RULE,
15
+ reverseContact,
16
+ reverseEntity,
17
+ reverseExternalReference,
18
+ } from "./cyclonedx20ReadCommon.js";
19
+ import {
20
+ keepComponentShadow,
21
+ readComponentEntry,
22
+ } from "./cyclonedx20ReadComponents.js";
23
+ import {
24
+ addLoss,
25
+ jsonPointer,
26
+ listOf,
27
+ objectOf,
28
+ registerShadow,
29
+ reverseFields,
30
+ reverseList,
31
+ reverseObject,
32
+ withoutComponentShadows,
33
+ } from "./cyclonedx20ReadContext.js";
34
+ import {
35
+ AGGREGATE_20_TO_1X,
36
+ FORMULA_COMPONENT_REFS_PROPERTY,
37
+ SCORE_METHODS_1X,
38
+ } from "./cyclonedx20Tables.js";
39
+
40
+ /** Read `original` with `spec` and keep it as a shadow of the result. */
41
+ function readShadowed(ctx, kind, original, pointer, spec) {
42
+ const internal = withoutComponentShadows(ctx, () =>
43
+ reverseObject(ctx, kind, original, pointer, spec),
44
+ );
45
+ if (internal !== undefined) {
46
+ registerShadow(ctx, kind, internal, original);
47
+ }
48
+ return internal;
49
+ }
50
+
51
+ /** tools.components → the 1.x tools {components, services}. */
52
+ function reverseToolComponents(ctx, value, pointer) {
53
+ const tools = {};
54
+ reverseList(ctx, value, pointer, (_ctx, entry, entryPointer) => {
55
+ const read = readComponentEntry(ctx, entry, entryPointer);
56
+ if (read) {
57
+ keepComponentShadow(ctx, read);
58
+ (tools[read.isService ? "services" : "components"] ||= []).push(
59
+ read.internal,
60
+ );
61
+ }
62
+ return undefined;
63
+ });
64
+ return tools;
65
+ }
66
+
67
+ /** A `tools` member holding `{components}`. */
68
+ const TOOLS_RULE = objectOf("tools", {
69
+ components: Object.freeze({ spread: true, convert: reverseToolComponents }),
70
+ });
71
+
72
+ const METADATA_SPEC = Object.freeze({
73
+ timestamp: true,
74
+ lifecycles: true,
75
+ manufacturer: Object.freeze({ convert: reverseEntity }),
76
+ authors: listOf(reverseContact),
77
+ supplier: Object.freeze({ convert: reverseEntity }),
78
+ licenses: LICENSES_RULE,
79
+ distributionConstraints: true,
80
+ properties: PROPERTIES_RULE,
81
+ });
82
+
83
+ /**
84
+ * metadata → 1.x metadata without `component` (the inventory subject) and
85
+ * `tools` (read separately, so each tool component has its own shadow).
86
+ *
87
+ * @param {object} ctx Reader context
88
+ * @param {object} metadata 2.0 metadata
89
+ * @returns {object} 1.x metadata
90
+ */
91
+ export function readMetadata(ctx, metadata) {
92
+ if (!isObjectRecord(metadata)) {
93
+ return {};
94
+ }
95
+ const { tools, ...rest } = metadata;
96
+ const internal = reverseFields(
97
+ ctx,
98
+ "metadata",
99
+ rest,
100
+ "/metadata",
101
+ METADATA_SPEC,
102
+ );
103
+ registerShadow(ctx, "metadata", internal, rest);
104
+ if (tools !== undefined) {
105
+ const converted = TOOLS_RULE.convert(ctx, tools, "/metadata/tools");
106
+ if (converted && Object.keys(converted).length) {
107
+ internal.tools = converted;
108
+ }
109
+ }
110
+ return internal;
111
+ }
112
+
113
+ /** One composition target; an inventory refId names the inventory subject. */
114
+ function compositionTarget(ctx, target, pointer, inventorySubjects) {
115
+ if (!inventorySubjects.has(target)) {
116
+ return target;
117
+ }
118
+ const subjectRef = inventorySubjects.get(target);
119
+ if (subjectRef === undefined) {
120
+ addLoss(ctx, pointer, "the inventory's subject has no refId for 1.x");
121
+ }
122
+ return subjectRef;
123
+ }
124
+
125
+ const COMPOSITION_KIND_FIELDS = Object.freeze({
126
+ components: "assemblies",
127
+ dependencies: "dependencies",
128
+ vulnerabilities: "vulnerabilities",
129
+ });
130
+
131
+ /**
132
+ * One composition → 1.x composition: kind `components` → `assemblies`,
133
+ * `dependencies` and `vulnerabilities` keep their names, every target is
134
+ * read. Other kinds and aggregates 1.x does not spell are losses.
135
+ */
136
+ function readComposition(ctx, composition, pointer, inventorySubjects) {
137
+ if (!isObjectRecord(composition)) {
138
+ addLoss(ctx, pointer, "composition is not an object");
139
+ return undefined;
140
+ }
141
+ const aggregate = Object.hasOwn(AGGREGATE_20_TO_1X, composition.aggregate)
142
+ ? AGGREGATE_20_TO_1X[composition.aggregate]
143
+ : undefined;
144
+ const field =
145
+ typeof composition.kind === "string" &&
146
+ Object.hasOwn(COMPOSITION_KIND_FIELDS, composition.kind)
147
+ ? COMPOSITION_KIND_FIELDS[composition.kind]
148
+ : undefined;
149
+ if (!aggregate || !field) {
150
+ addLoss(
151
+ ctx,
152
+ pointer,
153
+ aggregate
154
+ ? `composition kind '${JSON.stringify(composition.kind)}' has no 1.x equivalent`
155
+ : `composition aggregate '${composition.aggregate}' has no 1.x equivalent`,
156
+ );
157
+ // Kept for 2.0 output; the renderer appends it to the compositions.
158
+ (ctx.carriedCompositions ||= []).push(composition);
159
+ return undefined;
160
+ }
161
+ const internal = reverseFields(ctx, "composition", composition, pointer, {
162
+ refId: "bom-ref",
163
+ aggregate: Object.freeze({ convert: () => aggregate }),
164
+ kind: Object.freeze({ convert: () => undefined }),
165
+ targets: Object.freeze({
166
+ to: field,
167
+ convert: (_ctx, targets, targetsPointer) =>
168
+ reverseList(ctx, targets, targetsPointer, (_c, target, targetPointer) =>
169
+ compositionTarget(ctx, target, targetPointer, inventorySubjects),
170
+ ),
171
+ }),
172
+ });
173
+ registerShadow(ctx, "composition", internal, composition);
174
+ return internal;
175
+ }
176
+
177
+ /**
178
+ * compositions → 1.x compositions.
179
+ *
180
+ * @param {object} ctx Reader context
181
+ * @param {Array} compositions 2.0 compositions
182
+ * @param {Map<string, string|undefined>} inventorySubjects inventory refId →
183
+ * subject refId
184
+ * @returns {Array|undefined} 1.x compositions
185
+ */
186
+ export function readCompositions(ctx, compositions, inventorySubjects) {
187
+ return reverseList(
188
+ ctx,
189
+ compositions,
190
+ "/compositions",
191
+ (_ctx, entry, pointer) =>
192
+ readComposition(ctx, entry, pointer, inventorySubjects),
193
+ );
194
+ }
195
+
196
+ /** annotator: an annotator component may be a reference to any component. */
197
+ function reverseAnnotator(ctx, value, pointer) {
198
+ if (!isObjectRecord(value)) {
199
+ addLoss(ctx, pointer, "annotator is not an object");
200
+ return undefined;
201
+ }
202
+ return reverseFields(ctx, "annotator", value, pointer, {
203
+ organization: Object.freeze({ convert: reverseEntity }),
204
+ individual: Object.freeze({ convert: reverseContact }),
205
+ component: Object.freeze({
206
+ spread: true,
207
+ convert: (_ctx, entry, entryPointer) => {
208
+ const read = readComponentEntry(ctx, entry, entryPointer);
209
+ if (!read) {
210
+ return undefined;
211
+ }
212
+ return read.isService
213
+ ? { service: read.internal }
214
+ : { component: read.internal };
215
+ },
216
+ }),
217
+ });
218
+ }
219
+
220
+ const ANNOTATION_SPEC = Object.freeze({
221
+ refId: "bom-ref",
222
+ subjects: true,
223
+ annotator: Object.freeze({ convert: reverseAnnotator }),
224
+ timestamp: true,
225
+ text: true,
226
+ });
227
+
228
+ /** annotations → 1.x annotations. */
229
+ export function readAnnotations(ctx, annotations) {
230
+ return reverseList(ctx, annotations, "/annotations", (_ctx, entry, pointer) =>
231
+ readShadowed(ctx, "annotation", entry, pointer, ANNOTATION_SPEC),
232
+ );
233
+ }
234
+
235
+ /** A CWE number from a weakness identifier value `CWE-<n>`. */
236
+ function cweNumber(value) {
237
+ if (typeof value !== "string" || !value.startsWith("CWE-")) {
238
+ return undefined;
239
+ }
240
+ const digits = value.slice(4);
241
+ if (!digits.length || [...digits].some((char) => char < "0" || char > "9")) {
242
+ return undefined;
243
+ }
244
+ return Number.parseInt(digits, 10);
245
+ }
246
+
247
+ /** One weakness (or reference) → its CWE numbers; the rest are losses. */
248
+ function weaknessCwes(ctx, entry, pointer) {
249
+ let weakness = entry;
250
+ let weaknessPointer = pointer;
251
+ if (isObjectRecord(entry) && typeof entry.ref === "string") {
252
+ const declared = (ctx.doc.definitions?.weaknesses || []).findIndex(
253
+ (candidate) => isObjectRecord(candidate) && candidate.refId === entry.ref,
254
+ );
255
+ if (declared < 0) {
256
+ addLoss(
257
+ ctx,
258
+ pointer,
259
+ `weakness reference '${entry.ref}' could not be resolved`,
260
+ );
261
+ return [];
262
+ }
263
+ weakness = ctx.doc.definitions.weaknesses[declared];
264
+ weaknessPointer = jsonPointer("/definitions/weaknesses", declared);
265
+ }
266
+ if (!isObjectRecord(weakness)) {
267
+ addLoss(ctx, pointer, "weakness is not an object");
268
+ return [];
269
+ }
270
+ const cwes = [];
271
+ for (const [key, value] of Object.entries(weakness)) {
272
+ if (key === "kind" && value === "flaw") {
273
+ continue;
274
+ }
275
+ if (key !== "identifiers" || !Array.isArray(value)) {
276
+ addLoss(
277
+ ctx,
278
+ jsonPointer(weaknessPointer, key),
279
+ `weakness.${key} has no 1.x field`,
280
+ );
281
+ continue;
282
+ }
283
+ value.forEach((identifier, index) => {
284
+ const cwe =
285
+ identifier?.scheme === "cwe" ? cweNumber(identifier.value) : undefined;
286
+ if (cwe === undefined) {
287
+ addLoss(
288
+ ctx,
289
+ jsonPointer(weaknessPointer, key, index),
290
+ "only CWE weakness identifiers have a 1.x field (cwes)",
291
+ );
292
+ } else {
293
+ cwes.push(cwe);
294
+ }
295
+ });
296
+ }
297
+ return cwes;
298
+ }
299
+
300
+ /** weaknesses → the 1.x `cwes` numbers. */
301
+ function reverseWeaknesses(ctx, value, pointer) {
302
+ const cwes = [];
303
+ (Array.isArray(value) ? value : []).forEach((entry, index) => {
304
+ cwes.push(...weaknessCwes(ctx, entry, jsonPointer(pointer, index)));
305
+ });
306
+ const unique = [...new Set(cwes)];
307
+ return unique.length ? unique : undefined;
308
+ }
309
+
310
+ const RATING_SPEC = Object.freeze({
311
+ source: objectOf("rating.source", { url: true, name: true }),
312
+ score: true,
313
+ severity: true,
314
+ method: Object.freeze({
315
+ convert: (ctx, value, pointer) => {
316
+ if (SCORE_METHODS_1X.has(value)) {
317
+ return value;
318
+ }
319
+ addLoss(ctx, pointer, "rating method is not a 1.x score method");
320
+ return undefined;
321
+ },
322
+ }),
323
+ vector: true,
324
+ justification: true,
325
+ });
326
+
327
+ const VULNERABILITY_SPEC = Object.freeze({
328
+ refId: "bom-ref",
329
+ id: true,
330
+ source: objectOf("vulnerability.source", { url: true, name: true }),
331
+ references: listOf((ctx, value, pointer) =>
332
+ reverseObject(ctx, "vulnerability.reference", value, pointer, {
333
+ id: true,
334
+ source: objectOf("reference.source", { url: true, name: true }),
335
+ }),
336
+ ),
337
+ ratings: listOf((ctx, value, pointer) =>
338
+ reverseObject(ctx, "rating", value, pointer, RATING_SPEC),
339
+ ),
340
+ weaknesses: Object.freeze({ to: "cwes", convert: reverseWeaknesses }),
341
+ description: true,
342
+ detail: true,
343
+ recommendation: true,
344
+ workaround: true,
345
+ proofOfConcept: objectOf("proofOfConcept", {
346
+ reproductionSteps: true,
347
+ environment: true,
348
+ supportingMaterial: listOf((ctx, value, pointer) =>
349
+ ATTACHMENT_RULE.convert(ctx, value, pointer),
350
+ ),
351
+ }),
352
+ advisories: listOf((ctx, value, pointer) =>
353
+ reverseObject(ctx, "advisory", value, pointer, { title: true, url: true }),
354
+ ),
355
+ created: true,
356
+ published: true,
357
+ updated: true,
358
+ rejected: true,
359
+ credits: objectOf("credits", {
360
+ organizations: listOf(reverseEntity),
361
+ individuals: listOf(reverseContact),
362
+ }),
363
+ tools: TOOLS_RULE,
364
+ analysis: objectOf("analysis", {
365
+ state: true,
366
+ justification: true,
367
+ response: true,
368
+ detail: true,
369
+ firstIssued: true,
370
+ lastUpdated: true,
371
+ }),
372
+ affects: listOf((ctx, value, pointer) =>
373
+ reverseObject(ctx, "affects", value, pointer, {
374
+ ref: true,
375
+ versions: listOf((c, version, versionPointer) =>
376
+ reverseObject(c, "affects.version", version, versionPointer, {
377
+ version: true,
378
+ range: true,
379
+ status: true,
380
+ }),
381
+ ),
382
+ }),
383
+ ),
384
+ properties: PROPERTIES_RULE,
385
+ });
386
+
387
+ /** vulnerabilities → 1.x vulnerabilities. */
388
+ export function readVulnerabilities(ctx, vulnerabilities) {
389
+ return reverseList(
390
+ ctx,
391
+ vulnerabilities,
392
+ "/vulnerabilities",
393
+ (_ctx, entry, pointer) =>
394
+ readShadowed(ctx, "vulnerability", entry, pointer, VULNERABILITY_SPEC),
395
+ );
396
+ }
397
+
398
+ /** A 2.0 citation JSONPath expression → its 1.x form. */
399
+ export function reverseCitationExpression(expression) {
400
+ if (typeof expression !== "string") {
401
+ return expression;
402
+ }
403
+ if (expression === "$.inventories") {
404
+ return "$.components";
405
+ }
406
+ for (const prefix of [
407
+ "$.inventories[0].components",
408
+ "$.definitions.components",
409
+ ]) {
410
+ if (expression.startsWith(prefix)) {
411
+ return `$.components${expression.slice(prefix.length)}`;
412
+ }
413
+ }
414
+ return expression;
415
+ }
416
+
417
+ const CITATION_SPEC = Object.freeze({
418
+ refId: "bom-ref",
419
+ pointers: true,
420
+ expressions: Object.freeze({
421
+ convert: (_ctx, value) =>
422
+ Array.isArray(value) ? value.map(reverseCitationExpression) : value,
423
+ }),
424
+ timestamp: true,
425
+ attributedTo: true,
426
+ process: true,
427
+ note: true,
428
+ });
429
+
430
+ /** citations → 1.x citations. */
431
+ export function readCitations(ctx, citations) {
432
+ return reverseList(ctx, citations, "/citations", (_ctx, entry, pointer) =>
433
+ readShadowed(ctx, "citation", entry, pointer, CITATION_SPEC),
434
+ );
435
+ }
436
+
437
+ const RESOURCE_REFERENCE_SPEC = Object.freeze({
438
+ ref: true,
439
+ externalReference: Object.freeze({ convert: reverseExternalReference }),
440
+ });
441
+ const RESOURCE_RULE = objectOf("resourceReference", RESOURCE_REFERENCE_SPEC);
442
+ const RESOURCE_REFERENCES_RULE = listOf((ctx, value, pointer) =>
443
+ RESOURCE_RULE.convert(ctx, value, pointer),
444
+ );
445
+ const ENVIRONMENT_VARS_RULE = Object.freeze({
446
+ convert: (_ctx, value) => reverseEnvironmentVars(value),
447
+ });
448
+ const INPUT_SPEC = Object.freeze({
449
+ source: RESOURCE_RULE,
450
+ target: RESOURCE_RULE,
451
+ resource: RESOURCE_RULE,
452
+ parameters: true,
453
+ environmentVars: ENVIRONMENT_VARS_RULE,
454
+ data: ATTACHMENT_RULE,
455
+ properties: PROPERTIES_RULE,
456
+ });
457
+ const OUTPUT_SPEC = Object.freeze({ type: true, ...INPUT_SPEC });
458
+ const IO_RULES = Object.freeze({
459
+ inputs: listOf((ctx, value, pointer) =>
460
+ reverseObject(ctx, "input", value, pointer, INPUT_SPEC),
461
+ ),
462
+ outputs: listOf((ctx, value, pointer) =>
463
+ reverseObject(ctx, "output", value, pointer, OUTPUT_SPEC),
464
+ ),
465
+ });
466
+ const DEPENDENCY_RULE = listOf((ctx, value, pointer) =>
467
+ reverseObject(ctx, "dependency", value, pointer, {
468
+ ref: true,
469
+ dependsOn: true,
470
+ provides: true,
471
+ }),
472
+ );
473
+ const STEP_SPEC = Object.freeze({
474
+ name: true,
475
+ description: true,
476
+ commands: listOf((ctx, value, pointer) =>
477
+ reverseObject(ctx, "command", value, pointer, {
478
+ executed: true,
479
+ properties: PROPERTIES_RULE,
480
+ }),
481
+ ),
482
+ properties: PROPERTIES_RULE,
483
+ });
484
+ const WORKSPACE_SPEC = Object.freeze({
485
+ refId: "bom-ref",
486
+ uid: true,
487
+ name: true,
488
+ aliases: true,
489
+ description: true,
490
+ resourceReferences: RESOURCE_REFERENCES_RULE,
491
+ accessMode: true,
492
+ mountPath: true,
493
+ managedDataType: true,
494
+ volumeRequest: true,
495
+ volume: true,
496
+ properties: PROPERTIES_RULE,
497
+ });
498
+ const TRIGGER_SPEC = Object.freeze({
499
+ refId: "bom-ref",
500
+ uid: true,
501
+ name: true,
502
+ description: true,
503
+ resourceReferences: RESOURCE_REFERENCES_RULE,
504
+ type: true,
505
+ event: objectOf("event", {
506
+ uid: true,
507
+ description: true,
508
+ timeReceived: true,
509
+ data: ATTACHMENT_RULE,
510
+ source: RESOURCE_RULE,
511
+ target: RESOURCE_RULE,
512
+ properties: PROPERTIES_RULE,
513
+ }),
514
+ conditions: true,
515
+ timeActivated: true,
516
+ ...IO_RULES,
517
+ properties: PROPERTIES_RULE,
518
+ });
519
+
520
+ /** workspaces: a workspace reference has no 1.x form. */
521
+ function reverseWorkspace(ctx, value, pointer) {
522
+ if (isObjectRecord(value) && typeof value.ref === "string") {
523
+ addLoss(ctx, pointer, "workspace reference has no 1.x form");
524
+ return undefined;
525
+ }
526
+ return reverseObject(ctx, "workspace", value, pointer, WORKSPACE_SPEC);
527
+ }
528
+
529
+ const TASK_SPEC = Object.freeze({
530
+ refId: "bom-ref",
531
+ uid: true,
532
+ name: true,
533
+ description: true,
534
+ resourceReferences: RESOURCE_REFERENCES_RULE,
535
+ taskTypes: true,
536
+ trigger: objectOf("trigger", TRIGGER_SPEC),
537
+ steps: listOf((ctx, value, pointer) =>
538
+ reverseObject(ctx, "step", value, pointer, STEP_SPEC),
539
+ ),
540
+ ...IO_RULES,
541
+ timeStart: true,
542
+ timeEnd: true,
543
+ workspaces: listOf(reverseWorkspace),
544
+ runtimeTopology: DEPENDENCY_RULE,
545
+ properties: PROPERTIES_RULE,
546
+ });
547
+ const WORKFLOW_SPEC = Object.freeze({
548
+ ...TASK_SPEC,
549
+ tasks: listOf((ctx, value, pointer) =>
550
+ reverseObject(ctx, "task", value, pointer, TASK_SPEC),
551
+ ),
552
+ taskDependencies: DEPENDENCY_RULE,
553
+ });
554
+ const FORMULA_SPEC = Object.freeze({
555
+ refId: "bom-ref",
556
+ workflows: listOf((ctx, value, pointer) =>
557
+ reverseObject(ctx, "workflow", value, pointer, WORKFLOW_SPEC),
558
+ ),
559
+ properties: PROPERTIES_RULE,
560
+ });
561
+
562
+ /**
563
+ * formulation → 1.x formulas without their components (the reader puts the
564
+ * formula components back from definitions afterwards).
565
+ *
566
+ * @param {object} ctx Reader context
567
+ * @param {Array} formulation 2.0 formulation
568
+ * @returns {Array|undefined} 1.x formulas
569
+ */
570
+ export function readFormulation(ctx, formulation) {
571
+ return reverseList(
572
+ ctx,
573
+ formulation,
574
+ "/formulation",
575
+ (_ctx, entry, pointer) => {
576
+ const formula = reverseObject(
577
+ ctx,
578
+ "formula",
579
+ entry,
580
+ pointer,
581
+ FORMULA_SPEC,
582
+ );
583
+ const internal = formula || {};
584
+ // The component list of a subject-less document's formula is read
585
+ // into the formula's components (see formulaMembership in
586
+ // cyclonedx20Read.js), not kept as a property.
587
+ if (Array.isArray(internal.properties)) {
588
+ internal.properties = internal.properties.filter(
589
+ (property) => property?.name !== FORMULA_COMPONENT_REFS_PROPERTY,
590
+ );
591
+ if (!internal.properties.length) {
592
+ delete internal.properties;
593
+ }
594
+ }
595
+ registerShadow(ctx, "formula", internal, entry);
596
+ return internal;
597
+ },
598
+ );
599
+ }