@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.
- package/README.md +15 -8
- package/bin/audit.js +7 -4
- package/bin/cdxgen.js +256 -37
- package/bin/convert.js +146 -22
- package/bin/evinse.js +25 -3
- package/bin/repl.js +47 -3
- package/bin/sign.js +211 -34
- package/bin/tracebom.js +18 -6
- package/bin/validate.js +18 -3
- package/bin/verify.js +113 -5
- package/data/behavior-taxonomy.schema.json +1491 -0
- package/data/cyclonedx-2.0-bundled.schema.json +18606 -5528
- package/data/cyclonedx-2.0-cryptography-defs.schema.json +857 -0
- package/data/pqc-migration.json +239 -0
- package/data/remediations.json +52 -0
- package/data/rules/cbom-security.yaml +4 -3
- package/data/rules/package-integrity.yaml +28 -0
- package/lib/audit/index.js +25 -3
- package/lib/cli/bomAssembly.js +80 -1
- package/lib/cli/cliOptions.js +121 -17
- package/lib/cli/index.js +609 -898
- package/lib/cli/jsBom.js +3 -0
- package/lib/cli/jvmBom.js +365 -40
- package/lib/cli/managedBom.js +19 -3
- package/lib/cli/nativeBom.js +10 -0
- package/lib/core/env.js +26 -0
- package/lib/core/fs.js +126 -3
- package/lib/core/globPatterns.js +218 -0
- package/lib/core/processTree.js +99 -12
- package/lib/core/projectConfig.js +332 -25
- package/lib/core/toolRegistry.js +15 -1
- package/lib/ecosystems/denoutils.js +2 -2
- package/lib/ecosystems/golem.js +3 -8
- package/lib/ecosystems/gradleutils.js +1082 -6
- package/lib/ecosystems/jsEvidence.js +90 -38
- package/lib/ecosystems/kosi.js +3 -5
- package/lib/ecosystems/npmVersionResolution.js +376 -0
- package/lib/ecosystems/parsers-python.js +89 -28
- package/lib/ecosystems/parsers-rust.js +117 -15
- package/lib/ecosystems/remote/huggingface.js +22 -0
- package/lib/ecosystems/rusi.js +2 -4
- package/lib/ecosystems/scalasem.js +2 -1
- package/lib/evinser/evinser.js +196 -34
- package/lib/evinser/scalasem.js +5 -1
- package/lib/helpers/agenticToolutils.js +112 -1
- package/lib/helpers/bomSigner.js +43 -9
- package/lib/helpers/jssSigner.js +1276 -0
- package/lib/inventory/agentBlueprint.js +193 -0
- package/lib/inventory/agentToolNames.js +145 -0
- package/lib/inventory/aiCollector.js +153 -7
- package/lib/inventory/aiInventory.js +44 -8
- package/lib/inventory/aiProviders.js +324 -0
- package/lib/inventory/bomUtils.js +8 -1
- package/lib/inventory/ciParsers/githubActionsLock.js +821 -0
- package/lib/inventory/citations.js +31 -2
- package/lib/inventory/communityAiConfigParser.js +978 -80
- package/lib/inventory/cyclonedx20.js +152 -0
- package/lib/inventory/cyclonedx20Common.js +286 -0
- package/lib/inventory/cyclonedx20Derived.js +159 -0
- package/lib/inventory/cyclonedx20Parties.js +552 -0
- package/lib/inventory/cyclonedx20Read.js +768 -0
- package/lib/inventory/cyclonedx20ReadCommon.js +374 -0
- package/lib/inventory/cyclonedx20ReadComponents.js +692 -0
- package/lib/inventory/cyclonedx20ReadContext.js +380 -0
- package/lib/inventory/cyclonedx20ReadEvidence.js +466 -0
- package/lib/inventory/cyclonedx20ReadSections.js +599 -0
- package/lib/inventory/cyclonedx20Render.js +766 -0
- package/lib/inventory/cyclonedx20RenderComponents.js +716 -0
- package/lib/inventory/cyclonedx20RenderDefinitions.js +405 -0
- package/lib/inventory/cyclonedx20RenderEvidence.js +726 -0
- package/lib/inventory/cyclonedx20RenderHardware.js +203 -0
- package/lib/inventory/cyclonedx20RenderPatents.js +174 -0
- package/lib/inventory/cyclonedx20RenderRefIds.js +314 -0
- package/lib/inventory/cyclonedx20RenderSections.js +499 -0
- package/lib/inventory/cyclonedx20Tables.js +594 -0
- package/lib/inventory/deps.js +106 -13
- package/lib/inventory/dosai.js +236 -1
- package/lib/inventory/dosaiParsers.js +3 -6
- package/lib/inventory/dotnetRuntime.js +164 -14
- package/lib/inventory/ecosystemFormulationParsers.js +979 -0
- package/lib/inventory/envcontext.js +132 -38
- package/lib/inventory/evidenceUtils.js +2 -1
- package/lib/inventory/formulationParsers.js +95 -13
- package/lib/inventory/hbom.js +45 -6
- package/lib/inventory/hbomLoader.js +166 -0
- package/lib/inventory/mcpConfigParser.js +5 -0
- package/lib/inventory/occurrenceKey.js +90 -0
- package/lib/inventory/pqcMigration.js +426 -0
- package/lib/inventory/rustFormulationParser.js +21 -10
- package/lib/inventory/source.js +65 -6
- package/lib/parsers/huggingfaceManifest.js +9 -1
- package/lib/parsers/huggingfaceUtils.js +18 -0
- package/lib/parsers/kiroPermissions.js +166 -0
- package/lib/server/server.js +36 -34
- package/lib/stages/postgen/postgen.js +41 -1
- package/lib/stages/postgen/specVersionCompat.js +76 -0
- package/lib/validator/bomValidator.js +8 -1
- package/lib/validator/complianceEngine.js +30 -3
- package/lib/validator/complianceRules.js +1416 -4
- package/lib/validator/generated/validate-2.0.mjs +57554 -13851
- package/lib/validator/index.js +48 -7
- package/lib/validator/reporters/json.js +6 -0
- package/package.json +9 -9
- package/types/bin/repl.d.ts.map +1 -1
- package/types/lib/audit/index.d.ts +6 -2
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/cli/bomAssembly.d.ts +16 -0
- package/types/lib/cli/bomAssembly.d.ts.map +1 -1
- package/types/lib/cli/cliOptions.d.ts +6 -0
- package/types/lib/cli/cliOptions.d.ts.map +1 -1
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/cli/jsBom.d.ts +2 -1
- package/types/lib/cli/jsBom.d.ts.map +1 -1
- package/types/lib/cli/jvmBom.d.ts +15 -0
- package/types/lib/cli/jvmBom.d.ts.map +1 -1
- package/types/lib/cli/managedBom.d.ts.map +1 -1
- package/types/lib/cli/nativeBom.d.ts.map +1 -1
- package/types/lib/core/boundedFile.d.ts +30 -0
- package/types/lib/core/boundedFile.d.ts.map +1 -1
- package/types/lib/core/env.d.ts +10 -0
- package/types/lib/core/env.d.ts.map +1 -1
- package/types/lib/core/fs.d.ts +4 -0
- package/types/lib/core/fs.d.ts.map +1 -1
- package/types/lib/core/globPatterns.d.ts +70 -0
- package/types/lib/core/globPatterns.d.ts.map +1 -0
- package/types/lib/core/paths.d.ts +9 -0
- package/types/lib/core/paths.d.ts.map +1 -1
- package/types/lib/core/processTree.d.ts +16 -5
- package/types/lib/core/processTree.d.ts.map +1 -1
- package/types/lib/core/projectConfig.d.ts +47 -8
- package/types/lib/core/projectConfig.d.ts.map +1 -1
- package/types/lib/core/toolRegistry.d.ts.map +1 -1
- package/types/lib/ecosystems/golem.d.ts.map +1 -1
- package/types/lib/ecosystems/gradleutils.d.ts +248 -8
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
- package/types/lib/ecosystems/jsEvidence.d.ts +14 -1
- package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/kosi.d.ts.map +1 -1
- package/types/lib/ecosystems/npmVersionResolution.d.ts +119 -0
- package/types/lib/ecosystems/npmVersionResolution.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
- package/types/lib/ecosystems/pythonutils.d.ts +1 -1
- package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -1
- package/types/lib/ecosystems/rusi.d.ts.map +1 -1
- package/types/lib/ecosystems/scalasem.d.ts +58 -16
- package/types/lib/ecosystems/scalasem.d.ts.map +1 -1
- package/types/lib/evinser/evinser.d.ts +61 -9
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/evinser/scalasem.d.ts.map +1 -1
- package/types/lib/helpers/agenticToolutils.d.ts +53 -19
- package/types/lib/helpers/agenticToolutils.d.ts.map +1 -1
- package/types/lib/helpers/bomSigner.d.ts +20 -3
- package/types/lib/helpers/bomSigner.d.ts.map +1 -1
- package/types/lib/helpers/jssSigner.d.ts +288 -0
- package/types/lib/helpers/jssSigner.d.ts.map +1 -0
- package/types/lib/helpers/vsixutils.d.ts +303 -19
- package/types/lib/helpers/vsixutils.d.ts.map +1 -1
- package/types/lib/inventory/agentBlueprint.d.ts +70 -0
- package/types/lib/inventory/agentBlueprint.d.ts.map +1 -0
- package/types/lib/inventory/agentToolNames.d.ts +51 -0
- package/types/lib/inventory/agentToolNames.d.ts.map +1 -0
- package/types/lib/inventory/aiCollector.d.ts.map +1 -1
- package/types/lib/inventory/aiInventory.d.ts.map +1 -1
- package/types/lib/inventory/aiProviders.d.ts +85 -0
- package/types/lib/inventory/aiProviders.d.ts.map +1 -0
- package/types/lib/inventory/bomUtils.d.ts.map +1 -1
- package/types/lib/inventory/ciParsers/githubActionsLock.d.ts +101 -0
- package/types/lib/inventory/ciParsers/githubActionsLock.d.ts.map +1 -0
- package/types/lib/inventory/citations.d.ts +5 -0
- package/types/lib/inventory/citations.d.ts.map +1 -1
- package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/cyclonedx20.d.ts +99 -0
- package/types/lib/inventory/cyclonedx20.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20Common.d.ts +102 -0
- package/types/lib/inventory/cyclonedx20Common.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20Derived.d.ts +74 -0
- package/types/lib/inventory/cyclonedx20Derived.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20Parties.d.ts +91 -0
- package/types/lib/inventory/cyclonedx20Parties.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20Read.d.ts +51 -0
- package/types/lib/inventory/cyclonedx20Read.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20ReadCommon.d.ts +69 -0
- package/types/lib/inventory/cyclonedx20ReadCommon.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20ReadComponents.d.ts +77 -0
- package/types/lib/inventory/cyclonedx20ReadComponents.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20ReadContext.d.ts +118 -0
- package/types/lib/inventory/cyclonedx20ReadContext.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20ReadEvidence.d.ts +40 -0
- package/types/lib/inventory/cyclonedx20ReadEvidence.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20ReadSections.d.ts +45 -0
- package/types/lib/inventory/cyclonedx20ReadSections.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20Render.d.ts +53 -0
- package/types/lib/inventory/cyclonedx20Render.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderComponents.d.ts +73 -0
- package/types/lib/inventory/cyclonedx20RenderComponents.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderDefinitions.d.ts +29 -0
- package/types/lib/inventory/cyclonedx20RenderDefinitions.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderEvidence.d.ts +110 -0
- package/types/lib/inventory/cyclonedx20RenderEvidence.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderHardware.d.ts +42 -0
- package/types/lib/inventory/cyclonedx20RenderHardware.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderPatents.d.ts +26 -0
- package/types/lib/inventory/cyclonedx20RenderPatents.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderRefIds.d.ts +55 -0
- package/types/lib/inventory/cyclonedx20RenderRefIds.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderSections.d.ts +45 -0
- package/types/lib/inventory/cyclonedx20RenderSections.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20Tables.d.ts +134 -0
- package/types/lib/inventory/cyclonedx20Tables.d.ts.map +1 -0
- package/types/lib/inventory/deps.d.ts +35 -2
- package/types/lib/inventory/deps.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +19 -0
- package/types/lib/inventory/dosai.d.ts.map +1 -1
- package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
- package/types/lib/inventory/dotnetRuntime.d.ts +51 -4
- package/types/lib/inventory/dotnetRuntime.d.ts.map +1 -1
- package/types/lib/inventory/ecosystemFormulationParsers.d.ts +73 -0
- package/types/lib/inventory/ecosystemFormulationParsers.d.ts.map +1 -0
- package/types/lib/inventory/envcontext.d.ts +22 -6
- package/types/lib/inventory/envcontext.d.ts.map +1 -1
- package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
- package/types/lib/inventory/formulationParsers.d.ts.map +1 -1
- package/types/lib/inventory/hbom.d.ts +11 -0
- package/types/lib/inventory/hbom.d.ts.map +1 -1
- package/types/lib/inventory/hbomLoader.d.ts +54 -0
- package/types/lib/inventory/hbomLoader.d.ts.map +1 -1
- package/types/lib/inventory/jvmLocalRepos.d.ts +5 -1
- package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -1
- package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/occurrenceKey.d.ts +52 -0
- package/types/lib/inventory/occurrenceKey.d.ts.map +1 -0
- package/types/lib/inventory/pqcMigration.d.ts +84 -0
- package/types/lib/inventory/pqcMigration.d.ts.map +1 -0
- package/types/lib/inventory/rustFormulationParser.d.ts +7 -2
- package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
- package/types/lib/inventory/scalaCoords.d.ts.map +1 -1
- package/types/lib/inventory/scalaDedupe.d.ts +43 -0
- package/types/lib/inventory/scalaDedupe.d.ts.map +1 -0
- package/types/lib/inventory/source.d.ts +15 -0
- package/types/lib/inventory/source.d.ts.map +1 -1
- package/types/lib/parsers/huggingfaceManifest.d.ts +10 -1
- package/types/lib/parsers/huggingfaceManifest.d.ts.map +1 -1
- package/types/lib/parsers/huggingfaceUtils.d.ts +12 -0
- package/types/lib/parsers/huggingfaceUtils.d.ts.map +1 -1
- package/types/lib/parsers/kiroPermissions.d.ts +62 -0
- package/types/lib/parsers/kiroPermissions.d.ts.map +1 -0
- package/types/lib/parsers/vscodeExtensionId.d.ts +48 -0
- package/types/lib/parsers/vscodeExtensionId.d.ts.map +1 -0
- package/types/lib/server/server.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts +7 -0
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
- package/types/lib/stages/postgen/specVersionCompat.d.ts +25 -0
- package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts +5 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
- package/types/lib/validator/complianceEngine.d.ts +4 -4
- package/types/lib/validator/complianceEngine.d.ts.map +1 -1
- package/types/lib/validator/complianceRules.d.ts +14 -2
- package/types/lib/validator/complianceRules.d.ts.map +1 -1
- package/types/lib/validator/index.d.ts +10 -0
- package/types/lib/validator/index.d.ts.map +1 -1
- package/types/lib/validator/reporters/json.d.ts.map +1 -1
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared state and the field-mapping engine of the CycloneDX 2.0 reader.
|
|
3
|
+
*
|
|
4
|
+
* Every 2.0 object kind the reader converts has a frozen field spec: the
|
|
5
|
+
* allowlist of the members the 1.x model carries, with the 1.x name or the
|
|
6
|
+
* converter for each. `reverseFields` walks the object's own members in
|
|
7
|
+
* document order and reports every member the spec does not list as a loss,
|
|
8
|
+
* so a field the reader has no mapping for can never vanish silently.
|
|
9
|
+
*
|
|
10
|
+
* Loss pointers are RFC 6901 JSON pointers into the 2.0 input, with array
|
|
11
|
+
* indexes and `~`/`/` escaped.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { clone, isObjectRecord, shadowKey } from "./cyclonedx20Common.js";
|
|
15
|
+
|
|
16
|
+
// Marks a 1.x-shaped model that already went through the 2.0 reader or the
|
|
17
|
+
// 2.0 compatibility pass, so loadBomForAnalysis never reads it again as a 2.0
|
|
18
|
+
// document. Symbol.for keeps the mark recognisable across module instances.
|
|
19
|
+
const INTERNAL_MODEL_MARK = Symbol.for("cdxgen.cyclonedx20.internalModel");
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Mark `bom` as cdxgen's 1.x-shaped internal model. The mark is a
|
|
23
|
+
* non-enumerable symbol property, so it never reaches JSON output, and a
|
|
24
|
+
* copy made with JSON or structuredClone does not carry it.
|
|
25
|
+
*
|
|
26
|
+
* @param {object} bom The internal model
|
|
27
|
+
* @returns {object} The same object
|
|
28
|
+
*/
|
|
29
|
+
export function markInternalModel(bom) {
|
|
30
|
+
if (isObjectRecord(bom) && !Object.hasOwn(bom, INTERNAL_MODEL_MARK)) {
|
|
31
|
+
Object.defineProperty(bom, INTERNAL_MODEL_MARK, {
|
|
32
|
+
value: true,
|
|
33
|
+
enumerable: false,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return bom;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Whether `bom` carries the internal model mark of markInternalModel.
|
|
41
|
+
*
|
|
42
|
+
* @param {object} bom Any value
|
|
43
|
+
* @returns {boolean} True for a marked internal model
|
|
44
|
+
*/
|
|
45
|
+
export function isMarkedInternalModel(bom) {
|
|
46
|
+
return isObjectRecord(bom) && bom[INTERNAL_MODEL_MARK] === true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Nested objects the reader follows. A document nested deeper (components
|
|
50
|
+
// inside components, for example) is cut at this depth, with a loss, so the
|
|
51
|
+
// recursive converters stay well inside the call stack.
|
|
52
|
+
export const MAX_READ_DEPTH = 256;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Copy a parsed JSON value, leaving out every object nested deeper than
|
|
56
|
+
* `maxDepth` objects and recording its JSON pointer. Arrays do not count
|
|
57
|
+
* towards the depth, so every object entry of one array sits at the same
|
|
58
|
+
* depth and is cut with the rest; an array left empty by the cut is left out
|
|
59
|
+
* of its parent object too. Iterative, so no input depth can exhaust the
|
|
60
|
+
* call stack.
|
|
61
|
+
*
|
|
62
|
+
* @param {*} value Parsed JSON value
|
|
63
|
+
* @param {number} [maxDepth] Deepest object level kept
|
|
64
|
+
* @returns {{value: *, truncated: string[]}} The copy and the pointers of the
|
|
65
|
+
* objects that were cut
|
|
66
|
+
*/
|
|
67
|
+
export function cloneWithDepthLimit(value, maxDepth = MAX_READ_DEPTH) {
|
|
68
|
+
const truncated = [];
|
|
69
|
+
// A parsed "__proto__" member is an own property; plain assignment would
|
|
70
|
+
// set the prototype instead.
|
|
71
|
+
const assign = (target, key, child) => {
|
|
72
|
+
if (Array.isArray(target)) {
|
|
73
|
+
target.push(child);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
Object.defineProperty(target, key, {
|
|
77
|
+
value: child,
|
|
78
|
+
enumerable: true,
|
|
79
|
+
writable: true,
|
|
80
|
+
configurable: true,
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
const emptyCopy = (node) => (Array.isArray(node) ? [] : {});
|
|
84
|
+
const isContainer = (node) => node !== null && typeof node === "object";
|
|
85
|
+
const root = isContainer(value) ? emptyCopy(value) : value;
|
|
86
|
+
// Arrays that lost entries, with the object member holding them.
|
|
87
|
+
const cutArrays = [];
|
|
88
|
+
const stack = isContainer(value)
|
|
89
|
+
? [{ source: value, target: root, depth: 1, pointer: "" }]
|
|
90
|
+
: [];
|
|
91
|
+
while (stack.length) {
|
|
92
|
+
const { source, target, depth, pointer, parent, parentKey } = stack.pop();
|
|
93
|
+
const keys = Array.isArray(source) ? source.keys() : Object.keys(source);
|
|
94
|
+
let cut = false;
|
|
95
|
+
for (const key of keys) {
|
|
96
|
+
const child = source[key];
|
|
97
|
+
if (!isContainer(child)) {
|
|
98
|
+
assign(target, key, child);
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
const childPointer = jsonPointer(pointer, key);
|
|
102
|
+
const childDepth = Array.isArray(child) ? depth : depth + 1;
|
|
103
|
+
if (childDepth > maxDepth) {
|
|
104
|
+
truncated.push(childPointer);
|
|
105
|
+
cut = true;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const copy = emptyCopy(child);
|
|
109
|
+
assign(target, key, copy);
|
|
110
|
+
stack.push({
|
|
111
|
+
source: child,
|
|
112
|
+
target: copy,
|
|
113
|
+
depth: childDepth,
|
|
114
|
+
pointer: childPointer,
|
|
115
|
+
parent: target,
|
|
116
|
+
parentKey: key,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
if (cut && Array.isArray(target)) {
|
|
120
|
+
cutArrays.push({ target, parent, parentKey });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
for (const { target, parent, parentKey } of cutArrays) {
|
|
124
|
+
if (!target.length && parent && !Array.isArray(parent)) {
|
|
125
|
+
delete parent[parentKey];
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return { value: root, truncated };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* A fresh reader context for one 2.0 document.
|
|
133
|
+
*
|
|
134
|
+
* @param {object} doc The (private, cloned) 2.0 document
|
|
135
|
+
* @returns {object} Reader context
|
|
136
|
+
*/
|
|
137
|
+
export function createReadContext(doc) {
|
|
138
|
+
return {
|
|
139
|
+
doc,
|
|
140
|
+
losses: [],
|
|
141
|
+
shadows: {},
|
|
142
|
+
keptComponents: [],
|
|
143
|
+
keptIdentities: [],
|
|
144
|
+
// What renderCycloneDx20 attributes cdxgen's identity claims with; set
|
|
145
|
+
// from the read metadata (identityClaimContext).
|
|
146
|
+
claims: {},
|
|
147
|
+
shadowSuspended: false,
|
|
148
|
+
partyIndex: indexParties(doc),
|
|
149
|
+
licenseIndex: indexLicenses(doc),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Map refId → {entry, pointer} for every party declared anywhere in the
|
|
155
|
+
* document (definitions.parties first, then inline `parties` lists), so a
|
|
156
|
+
* partyReference resolves wherever its party is declared.
|
|
157
|
+
*/
|
|
158
|
+
function indexParties(doc) {
|
|
159
|
+
const index = new Map();
|
|
160
|
+
const add = (entry, pointer) => {
|
|
161
|
+
if (
|
|
162
|
+
isObjectRecord(entry) &&
|
|
163
|
+
typeof entry.refId === "string" &&
|
|
164
|
+
entry.ref === undefined &&
|
|
165
|
+
!index.has(entry.refId)
|
|
166
|
+
) {
|
|
167
|
+
index.set(entry.refId, { entry, pointer });
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
(Array.isArray(doc.definitions?.parties)
|
|
171
|
+
? doc.definitions.parties
|
|
172
|
+
: []
|
|
173
|
+
).forEach((entry, i) => {
|
|
174
|
+
add(entry, jsonPointer("/definitions/parties", i));
|
|
175
|
+
});
|
|
176
|
+
const visit = (node, pointer) => {
|
|
177
|
+
if (Array.isArray(node)) {
|
|
178
|
+
node.forEach((child, i) => {
|
|
179
|
+
visit(child, jsonPointer(pointer, i));
|
|
180
|
+
});
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (!isObjectRecord(node)) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
for (const [key, value] of Object.entries(node)) {
|
|
187
|
+
const childPointer = jsonPointer(pointer, key);
|
|
188
|
+
if (key === "parties" && Array.isArray(value)) {
|
|
189
|
+
value.forEach((entry, i) => {
|
|
190
|
+
add(entry, jsonPointer(childPointer, i));
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
visit(value, childPointer);
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
visit(doc, "");
|
|
197
|
+
return index;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Map refId → {entry, pointer} for definitions.licenses; a license
|
|
202
|
+
* declaration's refId sits on the expression or on the license object.
|
|
203
|
+
*/
|
|
204
|
+
function indexLicenses(doc) {
|
|
205
|
+
const index = new Map();
|
|
206
|
+
const licenses = Array.isArray(doc.definitions?.licenses)
|
|
207
|
+
? doc.definitions.licenses
|
|
208
|
+
: [];
|
|
209
|
+
licenses.forEach((entry, i) => {
|
|
210
|
+
const refId = entry?.refId ?? entry?.license?.refId;
|
|
211
|
+
if (typeof refId === "string" && !index.has(refId)) {
|
|
212
|
+
index.set(refId, {
|
|
213
|
+
entry,
|
|
214
|
+
pointer: jsonPointer("/definitions/licenses", i),
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
return index;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Append reference tokens to a JSON pointer (RFC 6901): `~` becomes `~0` and
|
|
223
|
+
* `/` becomes `~1`; array indexes are plain numbers.
|
|
224
|
+
*
|
|
225
|
+
* @param {string} base Pointer so far ("" is the whole document)
|
|
226
|
+
* @param {...(string|number)} tokens Member names or array indexes
|
|
227
|
+
* @returns {string} The extended pointer
|
|
228
|
+
*/
|
|
229
|
+
export function jsonPointer(base, ...tokens) {
|
|
230
|
+
let pointer = base;
|
|
231
|
+
for (const token of tokens) {
|
|
232
|
+
pointer += `/${String(token).replaceAll("~", "~0").replaceAll("/", "~1")}`;
|
|
233
|
+
}
|
|
234
|
+
return pointer;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** Record one loss. */
|
|
238
|
+
export function addLoss(ctx, pointer, reason) {
|
|
239
|
+
ctx.losses.push({ pointer, reason });
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Report every defined member of `source` that `carried` does not list.
|
|
244
|
+
*
|
|
245
|
+
* @param {object} ctx Reader context
|
|
246
|
+
* @param {string} kind 2.0 object kind, used in the reason
|
|
247
|
+
* @param {object} source 2.0 object
|
|
248
|
+
* @param {string} pointer JSON pointer of `source`
|
|
249
|
+
* @param {Set<string>|object} carried Member names (a Set or spec keys)
|
|
250
|
+
*/
|
|
251
|
+
export function reportUnmapped(ctx, kind, source, pointer, carried) {
|
|
252
|
+
const has = (key) =>
|
|
253
|
+
carried instanceof Set ? carried.has(key) : Object.hasOwn(carried, key);
|
|
254
|
+
for (const [key, value] of Object.entries(source)) {
|
|
255
|
+
if (value !== undefined && !has(key)) {
|
|
256
|
+
addLoss(
|
|
257
|
+
ctx,
|
|
258
|
+
jsonPointer(pointer, key),
|
|
259
|
+
`${kind}.${key} has no 1.x field`,
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Convert one 2.0 object with a field spec. A spec maps each carried 2.0
|
|
267
|
+
* member to `true` (copied under the same name), a string (copied under that
|
|
268
|
+
* 1.x name) or `{to?, spread?, convert}` where
|
|
269
|
+
* `convert(ctx, value, pointer, source)` returns the 1.x value or undefined
|
|
270
|
+
* (and reports its own losses); a `spread` rule returns an object of 1.x
|
|
271
|
+
* members to merge (one 2.0 member feeding several 1.x fields). Members
|
|
272
|
+
* the spec does not list are reported through reportUnmapped.
|
|
273
|
+
*
|
|
274
|
+
* @param {object} ctx Reader context
|
|
275
|
+
* @param {string} kind 2.0 object kind
|
|
276
|
+
* @param {object} source 2.0 object
|
|
277
|
+
* @param {string} pointer JSON pointer of `source`
|
|
278
|
+
* @param {object} spec Frozen field spec
|
|
279
|
+
* @returns {object} The 1.x object
|
|
280
|
+
*/
|
|
281
|
+
export function reverseFields(ctx, kind, source, pointer, spec) {
|
|
282
|
+
const out = {};
|
|
283
|
+
for (const [key, value] of Object.entries(source)) {
|
|
284
|
+
if (value === undefined || !Object.hasOwn(spec, key)) {
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
const rule = spec[key];
|
|
288
|
+
if (rule === true) {
|
|
289
|
+
out[key] = clone(value);
|
|
290
|
+
} else if (typeof rule === "string") {
|
|
291
|
+
out[rule] = clone(value);
|
|
292
|
+
} else {
|
|
293
|
+
const converted = rule.convert(
|
|
294
|
+
ctx,
|
|
295
|
+
value,
|
|
296
|
+
jsonPointer(pointer, key),
|
|
297
|
+
source,
|
|
298
|
+
);
|
|
299
|
+
if (converted !== undefined && rule.spread) {
|
|
300
|
+
Object.assign(out, converted);
|
|
301
|
+
} else if (converted !== undefined) {
|
|
302
|
+
out[rule.to ?? key] = converted;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
reportUnmapped(ctx, kind, source, pointer, spec);
|
|
307
|
+
return out;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Convert each entry of a 2.0 array with `convertEntry(ctx, entry, pointer)`,
|
|
312
|
+
* dropping entries that convert to undefined. A non-array is a loss.
|
|
313
|
+
*/
|
|
314
|
+
export function reverseList(ctx, value, pointer, convertEntry) {
|
|
315
|
+
if (!Array.isArray(value)) {
|
|
316
|
+
addLoss(ctx, pointer, "expected an array");
|
|
317
|
+
return undefined;
|
|
318
|
+
}
|
|
319
|
+
const out = [];
|
|
320
|
+
value.forEach((entry, index) => {
|
|
321
|
+
const converted = convertEntry(ctx, entry, jsonPointer(pointer, index));
|
|
322
|
+
if (converted !== undefined) {
|
|
323
|
+
out.push(converted);
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
return out.length ? out : undefined;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/** A spec rule converting an array entry by entry. */
|
|
330
|
+
export function listOf(convertEntry, to) {
|
|
331
|
+
return Object.freeze({
|
|
332
|
+
to,
|
|
333
|
+
convert: (ctx, value, pointer) =>
|
|
334
|
+
reverseList(ctx, value, pointer, convertEntry),
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/** A spec rule converting a nested object with its own spec. */
|
|
339
|
+
export function objectOf(kind, spec, to) {
|
|
340
|
+
return Object.freeze({
|
|
341
|
+
to,
|
|
342
|
+
convert: (ctx, value, pointer) =>
|
|
343
|
+
reverseObject(ctx, kind, value, pointer, spec),
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/** reverseFields for a value that must be an object; a loss otherwise. */
|
|
348
|
+
export function reverseObject(ctx, kind, value, pointer, spec) {
|
|
349
|
+
if (!isObjectRecord(value)) {
|
|
350
|
+
addLoss(ctx, pointer, `${kind} is not an object`);
|
|
351
|
+
return undefined;
|
|
352
|
+
}
|
|
353
|
+
const out = reverseFields(ctx, kind, value, pointer, spec);
|
|
354
|
+
return Object.keys(out).length ? out : undefined;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/** Keep `original` for the 1.x value `internal` in namespace `kind`. */
|
|
358
|
+
export function registerShadow(ctx, kind, internal, original) {
|
|
359
|
+
registerShadowKey(ctx, kind, shadowKey(internal), original);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/** Keep `original` under a precomputed shadow key. */
|
|
363
|
+
export function registerShadowKey(ctx, kind, key, original) {
|
|
364
|
+
const byKind = (ctx.shadows[kind] ||= {});
|
|
365
|
+
(byKind[key] ||= []).push(original);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Run `read()` with component shadows suspended: for section entries that
|
|
370
|
+
* are restored whole, whose nested components the renderer never converts.
|
|
371
|
+
*/
|
|
372
|
+
export function withoutComponentShadows(ctx, read) {
|
|
373
|
+
const previous = ctx.shadowSuspended;
|
|
374
|
+
ctx.shadowSuspended = true;
|
|
375
|
+
try {
|
|
376
|
+
return read();
|
|
377
|
+
} finally {
|
|
378
|
+
ctx.shadowSuspended = previous;
|
|
379
|
+
}
|
|
380
|
+
}
|