@cdxgen/cdxgen 13.0.1 → 13.2.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 +30 -16
- package/bin/audit.js +6 -0
- package/bin/cdxgen.js +141 -7
- package/bin/evinse.js +3 -1
- package/data/component-tags.json +8 -1
- package/data/crypto-oid.json +88 -12
- package/data/frameworks-list.json +86 -1
- package/data/remediations.json +1167 -0
- package/data/rules/ai-bom.yaml +78 -0
- package/data/rules/asar-archives.yaml +1 -1
- package/data/rules/build-fidelity.yaml +782 -0
- package/data/rules/cargo-rust.yaml +132 -0
- package/data/rules/mcp-servers.yaml +122 -0
- package/data/rules/package-integrity.yaml +13 -13
- package/lib/audit/index.js +48 -4
- package/lib/audit/scoring.js +2 -8
- package/lib/audit/targets.js +6 -1
- package/lib/cli/bomAssembly.js +32 -1
- package/lib/cli/cliOptions.js +94 -7
- package/lib/cli/index.js +268 -49
- package/lib/cli/jsBom.js +91 -5
- package/lib/cli/jvmBom.js +1515 -1314
- package/lib/cli/managedBom.js +249 -28
- package/lib/cli/nativeBom.js +986 -10
- package/lib/core/activity.js +120 -2
- package/lib/core/buildLedger.js +799 -0
- package/lib/core/deferredExit.js +141 -0
- package/lib/core/env.js +108 -2
- package/lib/core/fs.js +60 -0
- package/lib/core/logger.js +12 -99
- package/lib/core/severity.js +16 -0
- package/lib/core/syncFileWriter.js +106 -0
- package/lib/core/userAgent.js +151 -0
- package/lib/ecosystems/cargoCache.js +364 -0
- package/lib/ecosystems/caxa.js +13 -5
- package/lib/ecosystems/cppEvidence.js +40 -2
- package/lib/ecosystems/ecosystems.js +250 -91
- package/lib/ecosystems/golem.js +11 -1
- package/lib/ecosystems/gradleutils.js +498 -17
- package/lib/ecosystems/kosi.js +1256 -0
- package/lib/ecosystems/parsers-crystal.js +179 -0
- package/lib/ecosystems/parsers-dotnet.js +69 -39
- package/lib/ecosystems/parsers-elm.js +816 -0
- package/lib/ecosystems/parsers-gleam.js +1 -1
- package/lib/ecosystems/parsers-js.js +26 -0
- package/lib/ecosystems/parsers-julia.js +268 -0
- package/lib/ecosystems/parsers-jvm.js +9 -0
- package/lib/ecosystems/parsers-lua.js +218 -0
- package/lib/ecosystems/parsers-misc.js +188 -2
- package/lib/ecosystems/parsers-nim.js +221 -0
- package/lib/ecosystems/parsers-opam.js +180 -0
- package/lib/ecosystems/parsers-perl.js +277 -0
- package/lib/ecosystems/parsers-python.js +172 -36
- package/lib/ecosystems/parsers-r.js +148 -0
- package/lib/ecosystems/parsers-rebar.js +411 -0
- package/lib/ecosystems/parsers-rust.js +944 -19
- package/lib/ecosystems/parsers-spack.js +204 -0
- package/lib/ecosystems/parsers-stack.js +264 -0
- package/lib/ecosystems/parsers-terraform.js +194 -0
- package/lib/ecosystems/pypiClassifiers.js +326 -0
- package/lib/ecosystems/pythonutils.js +49 -1
- package/lib/ecosystems/rubyutils.js +13 -0
- package/lib/ecosystems/rusi.js +126 -6
- package/lib/evinser/evinser.js +141 -13
- package/lib/helpers/agenticToolutils.js +925 -0
- package/lib/helpers/remote/dependency-track.js +81 -14
- package/lib/inventory/agentFormulationParser.js +57 -4
- package/lib/inventory/aiCollector.js +504 -10
- package/lib/inventory/aiInventory.js +12 -0
- package/lib/inventory/analyzer.js +488 -30
- package/lib/inventory/astWorker.js +6 -0
- package/lib/inventory/auditCategories.js +17 -0
- package/lib/inventory/cbomutils.js +30 -0
- package/lib/inventory/cdxrs.js +7 -2
- package/lib/inventory/communityAiConfigParser.js +215 -2
- package/lib/inventory/depsUtils.js +72 -0
- package/lib/inventory/dosai.js +508 -22
- package/lib/inventory/envcontext.js +215 -24
- package/lib/inventory/evidenceUtils.js +13 -0
- package/lib/inventory/fetchBatch.js +131 -2
- package/lib/inventory/fetchRate.js +74 -11
- package/lib/inventory/forgeEnricher.js +3 -2
- package/lib/inventory/jvmToolEnv.js +594 -0
- package/lib/inventory/mcpConfigParser.js +166 -2
- package/lib/inventory/mcpDiscovery.js +11 -0
- package/lib/inventory/mcpServerManifest.js +389 -0
- package/lib/inventory/mcpbBundle.js +419 -0
- package/lib/inventory/osinfo.js +1 -0
- package/lib/inventory/plugins.js +3 -0
- package/lib/inventory/protobom.js +54 -88
- package/lib/inventory/protobomLoader.js +4 -0
- package/lib/inventory/rustFormulationParser.js +1 -1
- package/lib/inventory/source.js +10 -4
- package/lib/inventory/stylePackageRefs.js +41 -0
- package/lib/inventory/svelteUtils.js +524 -0
- package/lib/inventory/toolRequirements.js +903 -0
- package/lib/managers/binary.js +24 -0
- package/lib/managers/docker.js +26 -5
- package/lib/parsers/huggingfaceUtils.js +15 -7
- package/lib/parsers/onnx.js +294 -0
- package/lib/parsers/safetensors.js +114 -0
- package/lib/server/server.js +1 -1
- package/lib/stages/postgen/annotationBuilder.js +97 -0
- package/lib/stages/postgen/auditBom.js +76 -79
- package/lib/stages/postgen/introspection/annotate.js +320 -0
- package/lib/stages/postgen/introspection/commandFacts.js +293 -0
- package/lib/stages/postgen/introspection/emit.js +192 -0
- package/lib/stages/postgen/introspection/formulationEvidence.js +427 -0
- package/lib/stages/postgen/introspection/markers.js +250 -0
- package/lib/stages/postgen/introspection/reflect.js +1214 -0
- package/lib/stages/postgen/introspection/report.js +1208 -0
- package/lib/stages/postgen/introspection/score.js +1067 -0
- package/lib/stages/postgen/introspection/shapeCommand.js +304 -0
- package/lib/stages/postgen/parentEdge.js +156 -0
- package/lib/stages/postgen/postgen.js +284 -3
- package/lib/stages/postgen/ruleEngine.js +17 -0
- package/lib/stages/pregen/envAudit.js +42 -2
- package/lib/stages/pregen/pregen.js +1171 -6
- package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +462 -0
- package/lib/validator/bomValidator.js +11 -2
- package/package.json +26 -26
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/audit/scoring.d.ts +2 -8
- package/types/lib/audit/scoring.d.ts.map +1 -1
- package/types/lib/audit/targets.d.ts.map +1 -1
- package/types/lib/cli/bomAssembly.d.ts.map +1 -1
- package/types/lib/cli/cliOptions.d.ts +2 -1
- package/types/lib/cli/cliOptions.d.ts.map +1 -1
- package/types/lib/cli/index.d.ts +10 -2
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/cli/jsBom.d.ts +23 -0
- package/types/lib/cli/jsBom.d.ts.map +1 -1
- package/types/lib/cli/jvmBom.d.ts.map +1 -1
- package/types/lib/cli/managedBom.d.ts +33 -0
- package/types/lib/cli/managedBom.d.ts.map +1 -1
- package/types/lib/cli/nativeBom.d.ts +88 -8
- package/types/lib/cli/nativeBom.d.ts.map +1 -1
- package/types/lib/core/activity.d.ts.map +1 -1
- package/types/lib/core/buildLedger.d.ts +272 -0
- package/types/lib/core/buildLedger.d.ts.map +1 -0
- package/types/lib/core/deferredExit.d.ts +84 -0
- package/types/lib/core/deferredExit.d.ts.map +1 -0
- package/types/lib/core/env.d.ts +43 -1
- package/types/lib/core/env.d.ts.map +1 -1
- package/types/lib/core/fs.d.ts +21 -0
- package/types/lib/core/fs.d.ts.map +1 -1
- package/types/lib/core/logger.d.ts +2 -11
- package/types/lib/core/logger.d.ts.map +1 -1
- package/types/lib/core/severity.d.ts +11 -0
- package/types/lib/core/severity.d.ts.map +1 -0
- package/types/lib/core/syncFileWriter.d.ts +33 -0
- package/types/lib/core/syncFileWriter.d.ts.map +1 -0
- package/types/lib/core/userAgent.d.ts +56 -0
- package/types/lib/core/userAgent.d.ts.map +1 -0
- package/types/lib/ecosystems/cargoCache.d.ts +75 -0
- package/types/lib/ecosystems/cargoCache.d.ts.map +1 -0
- package/types/lib/ecosystems/caxa.d.ts.map +1 -1
- package/types/lib/ecosystems/cppEvidence.d.ts +1 -1
- package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/ecosystems.d.ts +15 -1
- package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
- package/types/lib/ecosystems/gradleutils.d.ts +53 -0
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
- package/types/lib/ecosystems/kosi.d.ts +73 -0
- package/types/lib/ecosystems/kosi.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-crystal.d.ts +27 -0
- package/types/lib/ecosystems/parsers-crystal.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-dotnet.d.ts +19 -0
- package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-elm.d.ts +79 -0
- package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-julia.d.ts +57 -0
- package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-lua.d.ts +50 -0
- package/types/lib/ecosystems/parsers-lua.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-misc.d.ts +23 -0
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-nim.d.ts +43 -0
- package/types/lib/ecosystems/parsers-nim.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-opam.d.ts +31 -0
- package/types/lib/ecosystems/parsers-opam.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-perl.d.ts +36 -0
- package/types/lib/ecosystems/parsers-perl.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-r.d.ts +35 -0
- package/types/lib/ecosystems/parsers-r.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-rebar.d.ts +42 -0
- package/types/lib/ecosystems/parsers-rebar.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-rust.d.ts +199 -0
- package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-spack.d.ts +33 -0
- package/types/lib/ecosystems/parsers-spack.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-stack.d.ts +35 -0
- package/types/lib/ecosystems/parsers-stack.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-terraform.d.ts +34 -0
- package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -0
- package/types/lib/ecosystems/pypiClassifiers.d.ts +80 -0
- package/types/lib/ecosystems/pypiClassifiers.d.ts.map +1 -0
- package/types/lib/ecosystems/pythonutils.d.ts +21 -1
- package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rusi.d.ts.map +1 -1
- package/types/lib/evinser/evinser.d.ts +34 -1
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/helpers/agenticToolutils.d.ts +87 -0
- package/types/lib/helpers/agenticToolutils.d.ts.map +1 -0
- package/types/lib/helpers/remote/dependency-track.d.ts +45 -4
- package/types/lib/helpers/remote/dependency-track.d.ts.map +1 -1
- package/types/lib/inventory/agentFormulationParser.d.ts.map +1 -1
- package/types/lib/inventory/aiCollector.d.ts.map +1 -1
- package/types/lib/inventory/aiInventory.d.ts.map +1 -1
- package/types/lib/inventory/analyzer.d.ts.map +1 -1
- package/types/lib/inventory/auditCategories.d.ts +12 -0
- package/types/lib/inventory/auditCategories.d.ts.map +1 -1
- package/types/lib/inventory/cbomutils.d.ts.map +1 -1
- package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/depsUtils.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +43 -0
- package/types/lib/inventory/dosai.d.ts.map +1 -1
- package/types/lib/inventory/envcontext.d.ts.map +1 -1
- package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
- package/types/lib/inventory/fetchBatch.d.ts +43 -0
- package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
- package/types/lib/inventory/fetchRate.d.ts +9 -6
- package/types/lib/inventory/fetchRate.d.ts.map +1 -1
- package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
- package/types/lib/inventory/jvmToolEnv.d.ts +190 -0
- package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -0
- package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/mcpDiscovery.d.ts.map +1 -1
- package/types/lib/inventory/mcpServerManifest.d.ts +19 -0
- package/types/lib/inventory/mcpServerManifest.d.ts.map +1 -0
- package/types/lib/inventory/mcpbBundle.d.ts +19 -0
- package/types/lib/inventory/mcpbBundle.d.ts.map +1 -0
- package/types/lib/inventory/osinfo.d.ts.map +1 -1
- package/types/lib/inventory/plugins.d.ts.map +1 -1
- package/types/lib/inventory/protobom.d.ts +18 -13
- package/types/lib/inventory/protobom.d.ts.map +1 -1
- package/types/lib/inventory/protobomLoader.d.ts.map +1 -1
- package/types/lib/inventory/source.d.ts.map +1 -1
- package/types/lib/inventory/stylePackageRefs.d.ts +17 -0
- package/types/lib/inventory/stylePackageRefs.d.ts.map +1 -0
- package/types/lib/inventory/svelteUtils.d.ts +124 -0
- package/types/lib/inventory/svelteUtils.d.ts.map +1 -0
- package/types/lib/inventory/toolRequirements.d.ts +216 -0
- package/types/lib/inventory/toolRequirements.d.ts.map +1 -0
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/parsers/huggingfaceUtils.d.ts +8 -1
- package/types/lib/parsers/huggingfaceUtils.d.ts.map +1 -1
- package/types/lib/parsers/onnx.d.ts +28 -0
- package/types/lib/parsers/onnx.d.ts.map +1 -0
- package/types/lib/parsers/safetensors.d.ts +22 -0
- package/types/lib/parsers/safetensors.d.ts.map +1 -0
- package/types/lib/stages/postgen/annotationBuilder.d.ts +53 -0
- package/types/lib/stages/postgen/annotationBuilder.d.ts.map +1 -0
- package/types/lib/stages/postgen/auditBom.d.ts.map +1 -1
- package/types/lib/stages/postgen/introspection/annotate.d.ts +81 -0
- package/types/lib/stages/postgen/introspection/annotate.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/commandFacts.d.ts +32 -0
- package/types/lib/stages/postgen/introspection/commandFacts.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/emit.d.ts +71 -0
- package/types/lib/stages/postgen/introspection/emit.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/formulationEvidence.d.ts +154 -0
- package/types/lib/stages/postgen/introspection/formulationEvidence.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/markers.d.ts +58 -0
- package/types/lib/stages/postgen/introspection/markers.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/reflect.d.ts +77 -0
- package/types/lib/stages/postgen/introspection/reflect.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/report.d.ts +85 -0
- package/types/lib/stages/postgen/introspection/report.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/score.d.ts +149 -0
- package/types/lib/stages/postgen/introspection/score.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/shapeCommand.d.ts +79 -0
- package/types/lib/stages/postgen/introspection/shapeCommand.d.ts.map +1 -0
- package/types/lib/stages/postgen/parentEdge.d.ts +35 -0
- package/types/lib/stages/postgen/parentEdge.d.ts.map +1 -0
- package/types/lib/stages/postgen/postgen.d.ts +35 -2
- 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/pregen/envAudit.d.ts.map +1 -1
- package/types/lib/stages/pregen/pregen.d.ts +131 -0
- package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
package/lib/inventory/dosai.js
CHANGED
|
@@ -2,6 +2,7 @@ import { readFileSync } from "node:fs";
|
|
|
2
2
|
import { basename, delimiter, join, resolve } from "node:path";
|
|
3
3
|
|
|
4
4
|
import { DEBUG_MODE } from "../core/activity.js";
|
|
5
|
+
import { recordDegradation } from "../core/buildLedger.js";
|
|
5
6
|
import {
|
|
6
7
|
getTmpDir,
|
|
7
8
|
safeExistsSync,
|
|
@@ -25,6 +26,15 @@ const DOTNET_LANGUAGES = new Set([
|
|
|
25
26
|
"cs",
|
|
26
27
|
"dotnet",
|
|
27
28
|
"dotnet-framework",
|
|
29
|
+
// Versioned CLI types (`-t dotnet11`): the project-type aliases map them to
|
|
30
|
+
// csharp for dispatch, but gates such as shouldCollectDosaiCrypto inspect the
|
|
31
|
+
// raw options.projectType, so the versioned spellings must resolve here too.
|
|
32
|
+
"dotnet6",
|
|
33
|
+
"dotnet7",
|
|
34
|
+
"dotnet8",
|
|
35
|
+
"dotnet9",
|
|
36
|
+
"dotnet10",
|
|
37
|
+
"dotnet11",
|
|
28
38
|
"f#",
|
|
29
39
|
"fsharp",
|
|
30
40
|
"fs",
|
|
@@ -59,6 +69,39 @@ function frameFromDosaiNode(node) {
|
|
|
59
69
|
};
|
|
60
70
|
}
|
|
61
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Whether this slice's ApiEndpoints carry a resolved route in `Path`.
|
|
74
|
+
*
|
|
75
|
+
* Schema 4.0.0 introduced `Path` as the resolved route ([controller] substituted, constraints
|
|
76
|
+
* stripped) alongside the verbatim `Route`. In every earlier schema `Path` is the **source file
|
|
77
|
+
* path**, so preferring it unconditionally emits file paths as service endpoints. cdxgen ships a
|
|
78
|
+
* pinned dosai via @cdxgen/cdxgen-plugins-bin and users can point DOSAI_CMD at any build, so both
|
|
79
|
+
* shapes are live and the version has to be checked.
|
|
80
|
+
*
|
|
81
|
+
* @param {Object} methodsSlice Parsed dosai methods slice JSON
|
|
82
|
+
* @returns {boolean} True when `Path` holds a resolved route
|
|
83
|
+
*/
|
|
84
|
+
function hasResolvedEndpointPaths(methodsSlice) {
|
|
85
|
+
const version = methodsSlice?.Metadata?.SchemaVersion;
|
|
86
|
+
if (typeof version !== "string") {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
const major = Number.parseInt(version.split(".")[0], 10);
|
|
90
|
+
return Number.isFinite(major) && major >= 4;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Coerce a dosai string list into the array-of-strings shape serviceData.source/destination require.
|
|
95
|
+
* Returns undefined rather than an empty or malformed array so the key is omitted entirely.
|
|
96
|
+
*/
|
|
97
|
+
function toStringArray(value) {
|
|
98
|
+
if (!Array.isArray(value)) {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
const strings = value.filter((entry) => typeof entry === "string" && entry);
|
|
102
|
+
return strings.length ? strings : undefined;
|
|
103
|
+
}
|
|
104
|
+
|
|
62
105
|
function appendUniqueProperty(properties, name, value) {
|
|
63
106
|
if (value === undefined || value === null || value === "") {
|
|
64
107
|
return;
|
|
@@ -219,6 +262,14 @@ export function runDosaiCommand(command, src, outputFile, options = {}) {
|
|
|
219
262
|
shell: false,
|
|
220
263
|
});
|
|
221
264
|
if (dosaiSdkMessage(result)) {
|
|
265
|
+
recordDegradation("dotnet.sdk.missing", {
|
|
266
|
+
ecosystem: "csharp",
|
|
267
|
+
tool: "dotnet",
|
|
268
|
+
impact: "none",
|
|
269
|
+
command: `${basename(executable)} ${args.join(" ")}`,
|
|
270
|
+
detail:
|
|
271
|
+
"dosai reported that no .NET SDK is installed, so the requested dosai analysis could not run.",
|
|
272
|
+
});
|
|
222
273
|
console.log(
|
|
223
274
|
"Dotnet SDK is not installed. Please use the cdxgen dotnet container images to analyze this project with dosai.",
|
|
224
275
|
);
|
|
@@ -348,7 +399,21 @@ export function persistDosaiSemanticsReport(
|
|
|
348
399
|
return undefined;
|
|
349
400
|
}
|
|
350
401
|
const combined = buildCombinedDosaiReport(methodsSlice, dataFlowSlice);
|
|
351
|
-
|
|
402
|
+
try {
|
|
403
|
+
safeWriteSync(durablePath, JSON.stringify(combined));
|
|
404
|
+
} catch (err) {
|
|
405
|
+
// Combined methods + data-flow reports for large solutions can exceed the
|
|
406
|
+
// JS maximum string length. The SBOM evidence is already collected by this
|
|
407
|
+
// point, so skip the durable copy instead of losing the whole analysis.
|
|
408
|
+
recordDegradation("dosai.semantics.persistFailed", {
|
|
409
|
+
ecosystem: "csharp",
|
|
410
|
+
tool: "dosai",
|
|
411
|
+
impact: "minor",
|
|
412
|
+
command: `persistDosaiSemanticsReport ${durablePath}`,
|
|
413
|
+
detail: `The combined dosai report could not be persisted: ${err.message}. The SBOM evidence is unaffected.`,
|
|
414
|
+
});
|
|
415
|
+
return undefined;
|
|
416
|
+
}
|
|
352
417
|
return durablePath;
|
|
353
418
|
}
|
|
354
419
|
|
|
@@ -373,6 +438,74 @@ export function resolveComponentPurl(purl, purlAliasMap) {
|
|
|
373
438
|
return resolveDosaiComponentPurl(purl, purlAliasMap);
|
|
374
439
|
}
|
|
375
440
|
|
|
441
|
+
/**
|
|
442
|
+
* Copy one dosai PackageReachability fact onto a component as properties.
|
|
443
|
+
*
|
|
444
|
+
* The occurrence/location consumers of PackageReachability read SourceLocations, EdgeIds,
|
|
445
|
+
* and NodeIds only, so dosai's Confidence, EvidenceKinds, ReachabilityKind, and
|
|
446
|
+
* ConfidenceReasons never reached the BOM: an unbuilt tree (Low confidence, unresolved
|
|
447
|
+
* evidence) was indistinguishable from a package that really is only imported. These
|
|
448
|
+
* properties carry that distinction to BOM consumers. They use the consumer-facing
|
|
449
|
+
* `cdx:dosai:reachability:*` namespace - not `internal:`, which is cdxgen's private
|
|
450
|
+
* bookkeeping namespace stripped or ignored by downstream tooling.
|
|
451
|
+
*
|
|
452
|
+
* @param {Object} component BOM component object (mutated; properties created on demand)
|
|
453
|
+
* @param {Object} reachability dosai PackageReachability entry
|
|
454
|
+
*/
|
|
455
|
+
export function addDosaiReachabilityProperties(component, reachability) {
|
|
456
|
+
if (!component || !reachability) {
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
component.properties = component.properties || [];
|
|
460
|
+
const values = {
|
|
461
|
+
"cdx:dosai:reachability:kind": reachability.ReachabilityKind,
|
|
462
|
+
"cdx:dosai:reachability:confidence": reachability.Confidence,
|
|
463
|
+
"cdx:dosai:reachability:evidence": (reachability.EvidenceKinds || []).join(
|
|
464
|
+
", ",
|
|
465
|
+
),
|
|
466
|
+
"cdx:dosai:reachability:reasons": (
|
|
467
|
+
reachability.ConfidenceReasons || []
|
|
468
|
+
).join(" | "),
|
|
469
|
+
};
|
|
470
|
+
for (const [name, value] of Object.entries(values)) {
|
|
471
|
+
if (!value) {
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
// First fact wins so a repeated enrichment pass cannot stack duplicate properties.
|
|
475
|
+
if (component.properties.some((p) => p.name === name)) {
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
component.properties.push({ name, value });
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Attach dosai PackageReachability confidence facts to matching BOM components.
|
|
484
|
+
*
|
|
485
|
+
* @param {Object} methodsSlice Parsed dosai methods slice JSON
|
|
486
|
+
* @param {Object[]} [components] BOM components used to resolve purl aliases and mutate
|
|
487
|
+
* @returns {number} Number of distinct components enriched
|
|
488
|
+
*/
|
|
489
|
+
export function applyDosaiReachabilityEvidence(methodsSlice, components = []) {
|
|
490
|
+
const purlAliasMap = buildPurlAliasMap(components);
|
|
491
|
+
const componentsByPurl = new Map(
|
|
492
|
+
components
|
|
493
|
+
.filter((component) => component?.purl)
|
|
494
|
+
.map((component) => [component.purl, component]),
|
|
495
|
+
);
|
|
496
|
+
const enriched = new Set();
|
|
497
|
+
for (const reachability of methodsSlice?.PackageReachability || []) {
|
|
498
|
+
const purl = resolveComponentPurl(reachability.Purl, purlAliasMap);
|
|
499
|
+
const component = purl && componentsByPurl.get(purl);
|
|
500
|
+
if (!component) {
|
|
501
|
+
continue;
|
|
502
|
+
}
|
|
503
|
+
addDosaiReachabilityProperties(component, reachability);
|
|
504
|
+
enriched.add(component);
|
|
505
|
+
}
|
|
506
|
+
return enriched.size;
|
|
507
|
+
}
|
|
508
|
+
|
|
376
509
|
/**
|
|
377
510
|
* Map a dosai methods slice to per-purl occurrence evidence.
|
|
378
511
|
*
|
|
@@ -498,6 +631,288 @@ export function collectDosaiDataFlowFrames(dataFlowResult, components = []) {
|
|
|
498
631
|
return dataFlowFrames;
|
|
499
632
|
}
|
|
500
633
|
|
|
634
|
+
/**
|
|
635
|
+
* Consume dosai's AiComponents[] inventory (schema 4.0.0): model identifiers,
|
|
636
|
+
* on-disk model artifacts with hashes, MCP tools, prompts (redacted), and agents
|
|
637
|
+
* become CycloneDX machine-learning-model / data components with modelCard data.
|
|
638
|
+
*
|
|
639
|
+
* @param {Object} methodsSlice Parsed dosai methods slice JSON
|
|
640
|
+
* @param {Array} [components] Component list to mutate in place
|
|
641
|
+
* @returns {Array} The updated component list
|
|
642
|
+
*/
|
|
643
|
+
export function collectDosaiAiComponents(methodsSlice, components = []) {
|
|
644
|
+
for (const ai of methodsSlice?.AiComponents || []) {
|
|
645
|
+
if (!ai?.Id || !ai?.Name) {
|
|
646
|
+
continue;
|
|
647
|
+
}
|
|
648
|
+
if (components.some((c) => c["bom-ref"] === ai.Id)) {
|
|
649
|
+
continue;
|
|
650
|
+
}
|
|
651
|
+
if (ai.Kind === "model") {
|
|
652
|
+
const modelCard = {
|
|
653
|
+
modelParameters: {},
|
|
654
|
+
};
|
|
655
|
+
if (ai.Task) {
|
|
656
|
+
modelCard.modelParameters.task = ai.Task;
|
|
657
|
+
}
|
|
658
|
+
if (ai.ArchitectureFamily) {
|
|
659
|
+
modelCard.modelParameters.architectureFamily = ai.ArchitectureFamily;
|
|
660
|
+
}
|
|
661
|
+
if (ai.ModelArchitecture) {
|
|
662
|
+
// modelArchitecture lives under modelParameters; modelCard itself is
|
|
663
|
+
// additionalProperties:false, so placing it at the top level fails validation.
|
|
664
|
+
modelCard.modelParameters.modelArchitecture = ai.ModelArchitecture;
|
|
665
|
+
}
|
|
666
|
+
if (Array.isArray(ai.InputFormats) && ai.InputFormats.length) {
|
|
667
|
+
modelCard.modelParameters.inputs = ai.InputFormats.map((format) => ({
|
|
668
|
+
format,
|
|
669
|
+
}));
|
|
670
|
+
}
|
|
671
|
+
if (Array.isArray(ai.OutputFormats) && ai.OutputFormats.length) {
|
|
672
|
+
modelCard.modelParameters.outputs = ai.OutputFormats.map((format) => ({
|
|
673
|
+
format,
|
|
674
|
+
}));
|
|
675
|
+
}
|
|
676
|
+
const component = {
|
|
677
|
+
"bom-ref": ai.Id,
|
|
678
|
+
type: "machine-learning-model",
|
|
679
|
+
name: ai.Name,
|
|
680
|
+
version: ai.Version,
|
|
681
|
+
purl: ai.Purl,
|
|
682
|
+
modelCard,
|
|
683
|
+
properties: [
|
|
684
|
+
{ name: "cdx:ai:provider", value: ai.Provider || "unknown" },
|
|
685
|
+
{ name: "cdx:ai:deployment", value: ai.Deployment || "unknown" },
|
|
686
|
+
],
|
|
687
|
+
};
|
|
688
|
+
if (ai.Sha256) {
|
|
689
|
+
component.hashes = [{ alg: "SHA-256", content: ai.Sha256 }];
|
|
690
|
+
}
|
|
691
|
+
if (ai.FilePath) {
|
|
692
|
+
component.properties.push({
|
|
693
|
+
name: "cdx:ai:modelFile",
|
|
694
|
+
value: ai.FilePath,
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
components.push(component);
|
|
698
|
+
} else if (ai.Kind === "prompt" || ai.Kind === "dataset") {
|
|
699
|
+
components.push({
|
|
700
|
+
"bom-ref": ai.Id,
|
|
701
|
+
type: "data",
|
|
702
|
+
name: ai.Name,
|
|
703
|
+
properties: [
|
|
704
|
+
{ name: "cdx:ai:kind", value: ai.Kind },
|
|
705
|
+
...(ai.PromptText
|
|
706
|
+
? [{ name: "cdx:ai:promptText", value: ai.PromptText }]
|
|
707
|
+
: []),
|
|
708
|
+
...Object.entries(ai.Properties || {}).map(([name, value]) => ({
|
|
709
|
+
name: `cdx:ai:${name}`,
|
|
710
|
+
value: String(value),
|
|
711
|
+
})),
|
|
712
|
+
],
|
|
713
|
+
});
|
|
714
|
+
} else if (
|
|
715
|
+
ai.Kind === "tool" ||
|
|
716
|
+
ai.Kind === "agent" ||
|
|
717
|
+
ai.Kind === "guardrail" ||
|
|
718
|
+
ai.Kind === "embedding"
|
|
719
|
+
) {
|
|
720
|
+
const properties = [
|
|
721
|
+
{ name: "cdx:ai:kind", value: ai.Kind },
|
|
722
|
+
{ name: "cdx:ai:provider", value: ai.Provider || "unknown" },
|
|
723
|
+
];
|
|
724
|
+
if (ai.ToolSchema) {
|
|
725
|
+
properties.push({ name: "cdx:ai:toolSchema", value: ai.ToolSchema });
|
|
726
|
+
}
|
|
727
|
+
components.push({
|
|
728
|
+
"bom-ref": ai.Id,
|
|
729
|
+
type: "data",
|
|
730
|
+
name: ai.Name,
|
|
731
|
+
properties,
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
return components;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/**
|
|
739
|
+
* Consume dosai's first-class Services[] inventory (schema 4.0.0) directly: richer
|
|
740
|
+
* than deriving services from ApiEndpoints alone — stable bom-refs, trust zones,
|
|
741
|
+
* data classifications, providers, and per-service evidence occurrences.
|
|
742
|
+
*
|
|
743
|
+
* @param {Object} methodsSlice Parsed dosai methods slice JSON
|
|
744
|
+
* @param {Object} [servicesMap] Map of service key to service definition, mutated in place
|
|
745
|
+
* @returns {Object} The updated services map
|
|
746
|
+
*/
|
|
747
|
+
export function collectDosaiServiceComponents(methodsSlice, servicesMap = {}) {
|
|
748
|
+
for (const service of methodsSlice?.Services || []) {
|
|
749
|
+
if (!service?.Id || !service?.Name) {
|
|
750
|
+
continue;
|
|
751
|
+
}
|
|
752
|
+
const key = service.Id;
|
|
753
|
+
const definition = (servicesMap[key] ??= {
|
|
754
|
+
name: service.Name,
|
|
755
|
+
bomRef: service.Id,
|
|
756
|
+
endpoints: new Set(),
|
|
757
|
+
properties: [],
|
|
758
|
+
});
|
|
759
|
+
definition.group = definition.group || service.Group;
|
|
760
|
+
definition.version = definition.version || service.Version;
|
|
761
|
+
// The same service Id can appear in several Services[] entries (one per
|
|
762
|
+
// implementing type or call site). Scalar facts left unset by earlier
|
|
763
|
+
// entries must still be filled from later ones, in particular the
|
|
764
|
+
// outbound provider: an entry that is only inbound must not lock the
|
|
765
|
+
// definition into an anonymous egress.
|
|
766
|
+
if (
|
|
767
|
+
!definition.provider &&
|
|
768
|
+
service.Provider &&
|
|
769
|
+
service.Direction === "outbound"
|
|
770
|
+
) {
|
|
771
|
+
definition.provider = { name: service.Provider };
|
|
772
|
+
}
|
|
773
|
+
if (typeof definition.authenticated === "undefined") {
|
|
774
|
+
definition.authenticated = service.Authenticated ?? undefined;
|
|
775
|
+
}
|
|
776
|
+
if (!definition.trustZone) {
|
|
777
|
+
definition.trustZone = service.TrustZone;
|
|
778
|
+
}
|
|
779
|
+
if (!definition["x-trust-boundary"]) {
|
|
780
|
+
definition["x-trust-boundary"] =
|
|
781
|
+
service.CrossesTrustBoundary === true ? true : undefined;
|
|
782
|
+
}
|
|
783
|
+
for (const endpoint of service.Endpoints || []) {
|
|
784
|
+
const sanitized = sanitizeEndpoint(endpoint);
|
|
785
|
+
if (sanitized) {
|
|
786
|
+
definition.endpoints.add(sanitized);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
if (Array.isArray(service.Operations) && service.Operations.length) {
|
|
790
|
+
appendUniqueProperty(
|
|
791
|
+
definition.properties,
|
|
792
|
+
"cdx:dosai:operationCount",
|
|
793
|
+
service.Operations.length,
|
|
794
|
+
);
|
|
795
|
+
for (const operation of service.Operations) {
|
|
796
|
+
// Endpoints[] can miss routes that only Operations carry (e.g. gRPC and
|
|
797
|
+
// messaging operations resolved per method), so both are collected.
|
|
798
|
+
const sanitized = sanitizeEndpoint(operation.Path);
|
|
799
|
+
if (sanitized) {
|
|
800
|
+
definition.endpoints.add(sanitized);
|
|
801
|
+
}
|
|
802
|
+
// Verbs are small enum values; they keep the HTTP surface auditable
|
|
803
|
+
// per endpoint without copying request payloads.
|
|
804
|
+
appendUniqueProperty(
|
|
805
|
+
definition.properties,
|
|
806
|
+
"cdx:service:httpMethod",
|
|
807
|
+
operation.HttpMethod,
|
|
808
|
+
);
|
|
809
|
+
if (operation.RouteTemplate && operation.Path) {
|
|
810
|
+
appendUniqueProperty(
|
|
811
|
+
definition.properties,
|
|
812
|
+
"cdx:service:pathTemplate",
|
|
813
|
+
operation.RouteTemplate,
|
|
814
|
+
);
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
if (Array.isArray(service.Data) && service.Data.length) {
|
|
819
|
+
// First population of services[].data[] for .NET: flow + classification
|
|
820
|
+
// with auditable descriptions from dosai.
|
|
821
|
+
definition.data = (definition.data || []).concat(
|
|
822
|
+
service.Data
|
|
823
|
+
// `flow` and `classification` are both required by serviceData in the 1.7 schema; an entry
|
|
824
|
+
// missing either fails validation and would fail the whole BOM, so drop it instead.
|
|
825
|
+
.filter((entry) => entry?.Classification && entry?.Flow)
|
|
826
|
+
.map((entry) => ({
|
|
827
|
+
classification: entry.Classification,
|
|
828
|
+
// "unknown" is a legitimate dataFlowDirection member. Coercing it to "bi-directional"
|
|
829
|
+
// turned an absence of knowledge into a positive claim that data flows both ways.
|
|
830
|
+
flow: ["inbound", "outbound", "bi-directional", "unknown"].includes(
|
|
831
|
+
entry.Flow,
|
|
832
|
+
)
|
|
833
|
+
? entry.Flow
|
|
834
|
+
: "unknown",
|
|
835
|
+
name: entry.Name,
|
|
836
|
+
description: entry.Description,
|
|
837
|
+
// source/destination are arrays of strings (iri-reference or BOM-link) in the schema.
|
|
838
|
+
source: toStringArray(entry.Source),
|
|
839
|
+
destination: toStringArray(entry.Destination),
|
|
840
|
+
})),
|
|
841
|
+
);
|
|
842
|
+
}
|
|
843
|
+
const properties = definition.properties;
|
|
844
|
+
appendUniqueProperty(properties, "cdx:service:kind", service.ServiceKind);
|
|
845
|
+
appendUniqueProperty(
|
|
846
|
+
properties,
|
|
847
|
+
"cdx:service:direction",
|
|
848
|
+
service.Direction,
|
|
849
|
+
);
|
|
850
|
+
appendUniqueProperty(
|
|
851
|
+
properties,
|
|
852
|
+
"cdx:service:framework",
|
|
853
|
+
service.Framework,
|
|
854
|
+
);
|
|
855
|
+
appendUniqueProperty(
|
|
856
|
+
properties,
|
|
857
|
+
"cdx:dosai:confidence",
|
|
858
|
+
service.Confidence,
|
|
859
|
+
);
|
|
860
|
+
for (const tag of service.Tags || []) {
|
|
861
|
+
appendUniqueProperty(properties, "cdx:dosai:tag", tag);
|
|
862
|
+
}
|
|
863
|
+
// Auth surface as counts and scheme categories only; policies, roles, and
|
|
864
|
+
// claim values never cross into the BOM verbatim.
|
|
865
|
+
appendUniqueProperty(
|
|
866
|
+
properties,
|
|
867
|
+
"cdx:dosai:allowAnonymous",
|
|
868
|
+
service.AllowAnonymous,
|
|
869
|
+
);
|
|
870
|
+
for (const scheme of service.AuthenticationSchemes || []) {
|
|
871
|
+
appendUniqueProperty(properties, "cdx:dosai:authScheme", scheme);
|
|
872
|
+
}
|
|
873
|
+
appendUniqueProperty(
|
|
874
|
+
properties,
|
|
875
|
+
"cdx:dosai:authorizationPolicyCount",
|
|
876
|
+
service.AuthorizationPolicies?.length,
|
|
877
|
+
);
|
|
878
|
+
appendUniqueProperty(
|
|
879
|
+
properties,
|
|
880
|
+
"cdx:dosai:roleCount",
|
|
881
|
+
service.Roles?.length,
|
|
882
|
+
);
|
|
883
|
+
if (service.Location?.Path) {
|
|
884
|
+
// Accumulate call sites: a service implemented across files must keep
|
|
885
|
+
// every occurrence, not just the last one processed.
|
|
886
|
+
definition.evidence ??= { occurrences: [] };
|
|
887
|
+
const location = {
|
|
888
|
+
path: service.Location.Path,
|
|
889
|
+
line: service.Location.LineNumber || undefined,
|
|
890
|
+
column: service.Location.ColumnNumber || undefined,
|
|
891
|
+
};
|
|
892
|
+
const occurrenceKey = `${location.path}#${location.line}#${location.column}`;
|
|
893
|
+
const knownOccurrences = definition.evidence.occurrences;
|
|
894
|
+
if (
|
|
895
|
+
!knownOccurrences.some(
|
|
896
|
+
(known) =>
|
|
897
|
+
`${known?.location?.path}#${known?.location?.line}#${known?.location?.column}` ===
|
|
898
|
+
occurrenceKey,
|
|
899
|
+
)
|
|
900
|
+
) {
|
|
901
|
+
knownOccurrences.push({ location });
|
|
902
|
+
}
|
|
903
|
+
// services[].evidence is a CycloneDX 2.0 field that spec-version
|
|
904
|
+
// compatibility strips below 2.0, so the location also lands in a
|
|
905
|
+
// property where it survives at 1.6 and 1.7.
|
|
906
|
+
appendUniqueProperty(
|
|
907
|
+
definition.properties,
|
|
908
|
+
"cdx:dosai:location",
|
|
909
|
+
`${location.path}:${location.line || 0}:${location.column || 0}`,
|
|
910
|
+
);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
return servicesMap;
|
|
914
|
+
}
|
|
915
|
+
|
|
501
916
|
/**
|
|
502
917
|
* Infer service and endpoint definitions from a dosai methods slice.
|
|
503
918
|
*
|
|
@@ -513,21 +928,40 @@ export function collectDosaiServicesFromMethods(
|
|
|
513
928
|
methodsSlice,
|
|
514
929
|
servicesMap = {},
|
|
515
930
|
) {
|
|
931
|
+
const resolvedPaths = hasResolvedEndpointPaths(methodsSlice);
|
|
516
932
|
for (const endpoint of methodsSlice?.ApiEndpoints || []) {
|
|
517
|
-
|
|
518
|
-
|
|
933
|
+
// Endpoints owned by a provider service (ServiceId set and already
|
|
934
|
+
// collected) enrich the owner instead of being re-derived into a duplicate
|
|
935
|
+
// service. Their method and auth metadata still lands on the owner via the
|
|
936
|
+
// shared property recording below.
|
|
937
|
+
const ownedDefinition =
|
|
938
|
+
endpoint.ServiceId && servicesMap[endpoint.ServiceId]
|
|
939
|
+
? servicesMap[endpoint.ServiceId]
|
|
940
|
+
: undefined;
|
|
941
|
+
// Schema 4.0.0: Path is the resolved route (tokens substituted, constraints
|
|
942
|
+
// stripped); Route keeps the verbatim template. Preferring Path is the fix for
|
|
943
|
+
// cdxgen discussion #4333, where [controller] shipped as %5Bcontroller%5D.
|
|
944
|
+
const route = sanitizeEndpoint(
|
|
945
|
+
resolvedPaths ? endpoint.Path || endpoint.Route : endpoint.Route,
|
|
946
|
+
);
|
|
947
|
+
if (!ownedDefinition && !route) {
|
|
519
948
|
continue;
|
|
520
949
|
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
endpoint.AuthorizationRequired
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
950
|
+
let definition = ownedDefinition;
|
|
951
|
+
if (!definition) {
|
|
952
|
+
const serviceName = serviceNameFromEndpoint(endpoint);
|
|
953
|
+
definition = servicesMap[serviceName] ??= {
|
|
954
|
+
endpoints: new Set(),
|
|
955
|
+
authenticated: endpoint.AuthorizationRequired,
|
|
956
|
+
xTrustBoundary:
|
|
957
|
+
endpoint.AuthorizationRequired === true ? true : undefined,
|
|
958
|
+
properties: [],
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
if (route) {
|
|
962
|
+
definition.endpoints.add(route);
|
|
963
|
+
}
|
|
964
|
+
const properties = definition.properties;
|
|
531
965
|
appendUniqueProperty(
|
|
532
966
|
properties,
|
|
533
967
|
"cdx:service:httpMethod",
|
|
@@ -571,13 +1005,30 @@ export function collectDosaiServicesFromMethods(
|
|
|
571
1005
|
appendUniqueProperty(
|
|
572
1006
|
properties,
|
|
573
1007
|
"internal:SrcFile",
|
|
574
|
-
endpoint.
|
|
1008
|
+
endpoint.FilePath || endpoint.FileName,
|
|
575
1009
|
);
|
|
576
1010
|
if (endpoint.LineNumber) {
|
|
577
1011
|
appendUniqueProperty(
|
|
578
1012
|
properties,
|
|
579
1013
|
"cdx:dosai:location",
|
|
580
|
-
|
|
1014
|
+
// Schema 4.0.0 renamed the ApiEndpoint source file field to FilePath;
|
|
1015
|
+
// Path carries the resolved route there, not a location.
|
|
1016
|
+
`${endpoint.FilePath || endpoint.FileName}:${endpoint.LineNumber}:${endpoint.ColumnNumber || 0}`,
|
|
1017
|
+
);
|
|
1018
|
+
}
|
|
1019
|
+
if (endpoint.Route && endpoint.Path && endpoint.Route !== endpoint.Path) {
|
|
1020
|
+
// The verbatim template (e.g. api/[controller]/{id:int}) alongside the resolved path.
|
|
1021
|
+
appendUniqueProperty(
|
|
1022
|
+
properties,
|
|
1023
|
+
"cdx:service:pathTemplate",
|
|
1024
|
+
endpoint.Route,
|
|
1025
|
+
);
|
|
1026
|
+
}
|
|
1027
|
+
if (endpoint.Confidence) {
|
|
1028
|
+
appendUniqueProperty(
|
|
1029
|
+
properties,
|
|
1030
|
+
"cdx:dosai:confidence",
|
|
1031
|
+
endpoint.Confidence,
|
|
581
1032
|
);
|
|
582
1033
|
}
|
|
583
1034
|
}
|
|
@@ -591,11 +1042,46 @@ export function collectDosaiServicesFromMethods(
|
|
|
591
1042
|
* @returns {Object[]} Array of service objects with sorted endpoint arrays and properties
|
|
592
1043
|
*/
|
|
593
1044
|
export function normalizeDosaiServiceMap(servicesMap = {}) {
|
|
594
|
-
return Object.keys(servicesMap)
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
1045
|
+
return Object.keys(servicesMap)
|
|
1046
|
+
.map((serviceName) => {
|
|
1047
|
+
const definition = servicesMap[serviceName];
|
|
1048
|
+
const service = {
|
|
1049
|
+
name:
|
|
1050
|
+
definition.name ||
|
|
1051
|
+
serviceName ||
|
|
1052
|
+
`dosai-${basename(serviceName)}-service`,
|
|
1053
|
+
endpoints: Array.from(definition.endpoints || []).sort(),
|
|
1054
|
+
authenticated: definition.authenticated,
|
|
1055
|
+
// Accept both spellings. Every pre-4.0.0 producer — collectDosaiServicesFromMethods here, and
|
|
1056
|
+
// the OpenAPI/Java/Python paths in evinser.js — sets the camelCase `xTrustBoundary`; only the
|
|
1057
|
+
// new Services[] path sets the hyphenated CycloneDX key. Reading just the latter silently
|
|
1058
|
+
// dropped the flag for all of them, including languages dosai has nothing to do with.
|
|
1059
|
+
"x-trust-boundary":
|
|
1060
|
+
definition["x-trust-boundary"] ?? definition.xTrustBoundary,
|
|
1061
|
+
properties: definition.properties,
|
|
1062
|
+
};
|
|
1063
|
+
if (definition.bomRef) {
|
|
1064
|
+
service["bom-ref"] = definition.bomRef;
|
|
1065
|
+
}
|
|
1066
|
+
if (definition.group) {
|
|
1067
|
+
service.group = definition.group;
|
|
1068
|
+
}
|
|
1069
|
+
if (definition.version) {
|
|
1070
|
+
service.version = definition.version;
|
|
1071
|
+
}
|
|
1072
|
+
if (definition.trustZone) {
|
|
1073
|
+
service.trustZone = definition.trustZone;
|
|
1074
|
+
}
|
|
1075
|
+
if (definition.provider) {
|
|
1076
|
+
service.provider = definition.provider;
|
|
1077
|
+
}
|
|
1078
|
+
if (definition.data) {
|
|
1079
|
+
service.data = definition.data;
|
|
1080
|
+
}
|
|
1081
|
+
if (definition.evidence) {
|
|
1082
|
+
service.evidence = definition.evidence;
|
|
1083
|
+
}
|
|
1084
|
+
return service;
|
|
1085
|
+
})
|
|
1086
|
+
.filter((service) => service.name);
|
|
601
1087
|
}
|