@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
package/lib/evinser/evinser.js
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
safeRmSync,
|
|
18
18
|
safeWriteSync,
|
|
19
19
|
} from "../core/fs.js";
|
|
20
|
+
import { thoughtLog } from "../core/logger.js";
|
|
20
21
|
import { isProjectToolingAllowed } from "../core/projectTooling.js";
|
|
21
22
|
import { hasCppSources } from "../ecosystems/cppEvidence.js";
|
|
22
23
|
import {
|
|
@@ -42,6 +43,7 @@ import {
|
|
|
42
43
|
kosiWorkspaceComponent,
|
|
43
44
|
mergeKosiEvidence,
|
|
44
45
|
} from "../ecosystems/kosi.js";
|
|
46
|
+
import { keepResolvedNpmVersions } from "../ecosystems/npmVersionResolution.js";
|
|
45
47
|
import {
|
|
46
48
|
analyzeRusiProject,
|
|
47
49
|
collectRusiEvidence,
|
|
@@ -71,6 +73,10 @@ import {
|
|
|
71
73
|
findCryptoAlgos,
|
|
72
74
|
} from "../inventory/cbomutils.js";
|
|
73
75
|
import { cmakeBuildDirCandidates } from "../inventory/cmakeBuildDirs.js";
|
|
76
|
+
import {
|
|
77
|
+
formatReadLossNotice,
|
|
78
|
+
loadBomForAnalysis,
|
|
79
|
+
} from "../inventory/cyclonedx20.js";
|
|
74
80
|
import { collectMvnDependencies } from "../inventory/deps.js";
|
|
75
81
|
import { mergeServices } from "../inventory/depsUtils.js";
|
|
76
82
|
import {
|
|
@@ -86,6 +92,7 @@ import {
|
|
|
86
92
|
dosaiOmittedSections,
|
|
87
93
|
dosaiReportFileOmittedSections,
|
|
88
94
|
dosaiSectionsCdxgenReads,
|
|
95
|
+
dosaiSlicesFileIsFresh,
|
|
89
96
|
isDosaiDotnetLanguage,
|
|
90
97
|
normalizeDosaiServiceMap,
|
|
91
98
|
persistDosaiSemanticsReport,
|
|
@@ -93,6 +100,10 @@ import {
|
|
|
93
100
|
readDosaiMethodsReport,
|
|
94
101
|
} from "../inventory/dosai.js";
|
|
95
102
|
import { parseOccurrenceEvidenceLocation } from "../inventory/evidenceUtils.js";
|
|
103
|
+
import {
|
|
104
|
+
dropSubsumedOccurrenceKeys,
|
|
105
|
+
occurrenceLocationKey,
|
|
106
|
+
} from "../inventory/occurrenceKey.js";
|
|
96
107
|
import { splitPurlLoose } from "../inventory/purl.js";
|
|
97
108
|
import { executeOsQuery } from "../managers/binary.js";
|
|
98
109
|
import { MAX_JSON_TEXT_BYTES, readJsonFile } from "../parsers/largeJson.js";
|
|
@@ -298,6 +309,45 @@ export function buildEvinseOptions(options, args, input) {
|
|
|
298
309
|
};
|
|
299
310
|
}
|
|
300
311
|
|
|
312
|
+
/**
|
|
313
|
+
* Read the evinse input BOM as cdxgen's 1.x-shaped internal model. A
|
|
314
|
+
* CycloneDX 2.0 document is converted with loadBomForAnalysis; a 1.x document
|
|
315
|
+
* (or an internal model) is returned as is. When `options.inputBomJson` holds
|
|
316
|
+
* a model converted earlier, a copy of it is returned instead of reading
|
|
317
|
+
* `options.input` again, so prepareDB, analyzeProject and createEvinseFile
|
|
318
|
+
* each get their own object, as they did when each parsed the file.
|
|
319
|
+
*
|
|
320
|
+
* @param {Object} options Evinse options with `input` and optional
|
|
321
|
+
* `inputBomJson`
|
|
322
|
+
* @returns {{bomJson: Object, losses: Array<{pointer: string, reason: string}>}}
|
|
323
|
+
* The internal model and the losses of the 2.0 read (none when
|
|
324
|
+
* `inputBomJson` was given)
|
|
325
|
+
*/
|
|
326
|
+
export function loadEvinseInputBom(options) {
|
|
327
|
+
if (options?.inputBomJson && typeof options.inputBomJson === "object") {
|
|
328
|
+
return { bomJson: structuredClone(options.inputBomJson), losses: [] };
|
|
329
|
+
}
|
|
330
|
+
const parsedBom = JSON.parse(fs.readFileSync(options.input, "utf8"));
|
|
331
|
+
const { internalBom, losses } = loadBomForAnalysis(parsedBom);
|
|
332
|
+
return { bomJson: internalBom, losses };
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Print the one-line notice for the losses of reading a 2.0 evinse input,
|
|
337
|
+
* with the details in thoughtLog.
|
|
338
|
+
*
|
|
339
|
+
* @param {Array<{pointer: string, reason: string}>} losses Reader losses
|
|
340
|
+
*/
|
|
341
|
+
export function reportEvinseInputLosses(losses) {
|
|
342
|
+
if (!losses?.length) {
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
console.log(formatReadLossNotice(losses));
|
|
346
|
+
for (const loss of losses) {
|
|
347
|
+
thoughtLog(`evinse 2.0 loss at ${loss.pointer}: ${loss.reason}`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
301
351
|
/**
|
|
302
352
|
* Function to create the db for the libraries referred in the sbom.
|
|
303
353
|
*
|
|
@@ -317,7 +367,7 @@ export async function prepareDB(options) {
|
|
|
317
367
|
}
|
|
318
368
|
return;
|
|
319
369
|
}
|
|
320
|
-
const bomJson =
|
|
370
|
+
const { bomJson } = loadEvinseInputBom(options);
|
|
321
371
|
if (bomJson.specVersion < 1.5) {
|
|
322
372
|
console.log(
|
|
323
373
|
"Evinse requires the input SBOM in CycloneDX 1.5 format or above. You can generate one by invoking cdxgen without any --spec-version argument.",
|
|
@@ -845,15 +895,18 @@ export function purlToLanguage(purl, filePath) {
|
|
|
845
895
|
* Walks the supplied components reading `internal:ImportedModules` (or
|
|
846
896
|
* `internal:Namespaces` for php/ruby, plus the `internal:ImportedSymbols` the
|
|
847
897
|
* C/C++ collector records) and `evidence.occurrences` properties to construct
|
|
848
|
-
* lookup maps used during slice analysis.
|
|
898
|
+
* lookup maps used during slice analysis. An occurrence that carries `offset`
|
|
899
|
+
* or `symbol` seeds those too, so a re-run over a previous evinse output keys
|
|
900
|
+
* and annotates the same use sites the fresh slice adds.
|
|
849
901
|
*
|
|
850
902
|
* @param {Object[]} components CycloneDX components from the input SBOM
|
|
851
903
|
* @param {string} language Application language used to select the import property name
|
|
852
|
-
* @returns {{ purlLocationMap: Object<string, Set>, purlImportsMap: Object<string, string[]
|
|
904
|
+
* @returns {{ purlLocationMap: Object<string, Set>, purlImportsMap: Object<string, string[]>, purlSymbolMap: Object<string, Object<string, string>> }} Seeded purl location, import and symbol maps
|
|
853
905
|
*/
|
|
854
906
|
export function initFromSbom(components, language) {
|
|
855
907
|
const purlLocationMap = {};
|
|
856
908
|
const purlImportsMap = {};
|
|
909
|
+
const purlSymbolMap = {};
|
|
857
910
|
for (const comp of components) {
|
|
858
911
|
if (!comp?.evidence) {
|
|
859
912
|
continue;
|
|
@@ -886,17 +939,30 @@ export function initFromSbom(components, language) {
|
|
|
886
939
|
if (comp.evidence.occurrences) {
|
|
887
940
|
// Keep the `location#line` shape so a re-run over a cdxgen BOM does not
|
|
888
941
|
// degrade seeded occurrences to file-only entries and duplicate the
|
|
889
|
-
// richer locations the fresh slice merge adds to the same set.
|
|
942
|
+
// richer locations the fresh slice merge adds to the same set. The key
|
|
943
|
+
// function also writes the location with `/`, so a Windows BOM's
|
|
944
|
+
// backslash spelling and a slice's forward-slash spelling of one place
|
|
945
|
+
// are one occurrence.
|
|
890
946
|
purlLocationMap[comp.purl] = new Set(
|
|
891
|
-
comp.evidence.occurrences
|
|
892
|
-
v
|
|
893
|
-
|
|
947
|
+
comp.evidence.occurrences
|
|
948
|
+
.map((v) => occurrenceLocationKey(v.location, v.line, v.offset))
|
|
949
|
+
.filter(Boolean),
|
|
894
950
|
);
|
|
951
|
+
// The first symbol a key carries wins, so a line two records without a
|
|
952
|
+
// column share keeps one symbol across a re-run over this output.
|
|
953
|
+
const symbols = (purlSymbolMap[comp.purl] ??= {});
|
|
954
|
+
for (const v of comp.evidence.occurrences) {
|
|
955
|
+
const key = occurrenceLocationKey(v.location, v.line, v.offset);
|
|
956
|
+
if (v.symbol && key && symbols[key] === undefined) {
|
|
957
|
+
symbols[key] = v.symbol;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
895
960
|
}
|
|
896
961
|
}
|
|
897
962
|
return {
|
|
898
963
|
purlLocationMap,
|
|
899
964
|
purlImportsMap,
|
|
965
|
+
purlSymbolMap,
|
|
900
966
|
};
|
|
901
967
|
}
|
|
902
968
|
|
|
@@ -1250,7 +1316,7 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1250
1316
|
let retMap = {};
|
|
1251
1317
|
let userDefinedTypesMap = {};
|
|
1252
1318
|
const bomFile = options.input;
|
|
1253
|
-
const bomJson =
|
|
1319
|
+
const { bomJson } = loadEvinseInputBom(options);
|
|
1254
1320
|
const components = bomJson.components || [];
|
|
1255
1321
|
let cryptoComponents = [];
|
|
1256
1322
|
let aiComponents = [];
|
|
@@ -1258,7 +1324,22 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1258
1324
|
let openapiSpecFile;
|
|
1259
1325
|
// Load any existing purl-location information from the sbom.
|
|
1260
1326
|
// For eg: cdxgen populates this information for javascript projects
|
|
1261
|
-
let { purlLocationMap, purlImportsMap } = initFromSbom(
|
|
1327
|
+
let { purlLocationMap, purlImportsMap, purlSymbolMap } = initFromSbom(
|
|
1328
|
+
components,
|
|
1329
|
+
language,
|
|
1330
|
+
);
|
|
1331
|
+
// purls whose input occurrences all belong to another installed version
|
|
1332
|
+
let clearedOccurrencePurls = new Set();
|
|
1333
|
+
// The places the input BOM's own import evidence gives each npm version,
|
|
1334
|
+
// before any slice adds to them: they say which version a file uses.
|
|
1335
|
+
const seededJsLocationMap = JS_LANGUAGES.includes(language)
|
|
1336
|
+
? Object.fromEntries(
|
|
1337
|
+
Object.entries(purlLocationMap).map(([purl, keys]) => [
|
|
1338
|
+
purl,
|
|
1339
|
+
new Set(keys),
|
|
1340
|
+
]),
|
|
1341
|
+
)
|
|
1342
|
+
: {};
|
|
1262
1343
|
|
|
1263
1344
|
if (isGolemGoLanguage(language)) {
|
|
1264
1345
|
const golemReport = analyzeGolemProject(dirPath, options);
|
|
@@ -1277,6 +1358,7 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1277
1358
|
cryptoGeneratePurls: golemEvidence.cryptoGeneratePurls,
|
|
1278
1359
|
servicesMap,
|
|
1279
1360
|
userDefinedTypesMap,
|
|
1361
|
+
purlSymbolMap,
|
|
1280
1362
|
};
|
|
1281
1363
|
}
|
|
1282
1364
|
return {
|
|
@@ -1284,6 +1366,7 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1284
1366
|
servicesMap,
|
|
1285
1367
|
dataFlowFrames,
|
|
1286
1368
|
userDefinedTypesMap,
|
|
1369
|
+
purlSymbolMap,
|
|
1287
1370
|
};
|
|
1288
1371
|
}
|
|
1289
1372
|
|
|
@@ -1303,6 +1386,7 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1303
1386
|
cryptoGeneratePurls: rusiEvidence.cryptoGeneratePurls,
|
|
1304
1387
|
servicesMap,
|
|
1305
1388
|
userDefinedTypesMap,
|
|
1389
|
+
purlSymbolMap,
|
|
1306
1390
|
};
|
|
1307
1391
|
}
|
|
1308
1392
|
return {
|
|
@@ -1310,6 +1394,7 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1310
1394
|
servicesMap,
|
|
1311
1395
|
dataFlowFrames,
|
|
1312
1396
|
userDefinedTypesMap,
|
|
1397
|
+
purlSymbolMap,
|
|
1313
1398
|
};
|
|
1314
1399
|
}
|
|
1315
1400
|
|
|
@@ -1381,6 +1466,7 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1381
1466
|
servicesMap,
|
|
1382
1467
|
userDefinedTypesMap,
|
|
1383
1468
|
workspaceComponent,
|
|
1469
|
+
purlSymbolMap,
|
|
1384
1470
|
};
|
|
1385
1471
|
}
|
|
1386
1472
|
return {
|
|
@@ -1388,6 +1474,7 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1388
1474
|
servicesMap,
|
|
1389
1475
|
dataFlowFrames,
|
|
1390
1476
|
userDefinedTypesMap,
|
|
1477
|
+
purlSymbolMap,
|
|
1391
1478
|
};
|
|
1392
1479
|
}
|
|
1393
1480
|
|
|
@@ -1449,6 +1536,17 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1449
1536
|
`The dosai deps slices file "${options.depsSlicesFile}" omits sections (${omittedSections.join(", ")}); running dosai for the full usages report.`,
|
|
1450
1537
|
);
|
|
1451
1538
|
}
|
|
1539
|
+
} else if (
|
|
1540
|
+
!dosaiSlicesFileIsFresh(options.depsSlicesFile, dirPath, options)
|
|
1541
|
+
) {
|
|
1542
|
+
// A slice older than the sources it describes holds the occurrences
|
|
1543
|
+
// of the sources as they were when it was written, so reusing it
|
|
1544
|
+
// keeps occurrences of call sites that no longer exist (issue 4675).
|
|
1545
|
+
if (DEBUG_MODE) {
|
|
1546
|
+
console.log(
|
|
1547
|
+
`The dosai deps slices file "${options.depsSlicesFile}" is older than the sources it describes; running dosai for a fresh usages report.`,
|
|
1548
|
+
);
|
|
1549
|
+
}
|
|
1452
1550
|
} else {
|
|
1453
1551
|
if (DEBUG_MODE) {
|
|
1454
1552
|
console.log(
|
|
@@ -1546,6 +1644,7 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1546
1644
|
cryptoComponents,
|
|
1547
1645
|
aiComponents,
|
|
1548
1646
|
cryptoGeneratePurls,
|
|
1647
|
+
purlSymbolMap,
|
|
1549
1648
|
};
|
|
1550
1649
|
}
|
|
1551
1650
|
// Do reachables first so that usages slicing can reuse the atom file
|
|
@@ -1695,8 +1794,21 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1695
1794
|
}
|
|
1696
1795
|
}
|
|
1697
1796
|
if (callsitesSlice?.callsites?.length) {
|
|
1698
|
-
attributeJsCallsites(
|
|
1797
|
+
attributeJsCallsites(
|
|
1798
|
+
callsitesSlice,
|
|
1799
|
+
purlImportsMap,
|
|
1800
|
+
purlLocationMap,
|
|
1801
|
+
purlSymbolMap,
|
|
1802
|
+
);
|
|
1699
1803
|
}
|
|
1804
|
+
// A slice record names a package, not a version: keep each occurrence on
|
|
1805
|
+
// the installed version its file uses.
|
|
1806
|
+
clearedOccurrencePurls = keepResolvedNpmVersions(
|
|
1807
|
+
purlLocationMap,
|
|
1808
|
+
purlSymbolMap,
|
|
1809
|
+
seededJsLocationMap,
|
|
1810
|
+
bomJson,
|
|
1811
|
+
);
|
|
1700
1812
|
}
|
|
1701
1813
|
// A component whose calls the slices do not show (code inside the project,
|
|
1702
1814
|
// such as a vendored library) is still used where its header is included
|
|
@@ -1771,6 +1883,8 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1771
1883
|
reachablesSlicesFile,
|
|
1772
1884
|
semanticsSlicesFile,
|
|
1773
1885
|
purlLocationMap,
|
|
1886
|
+
purlSymbolMap,
|
|
1887
|
+
clearedOccurrencePurls,
|
|
1774
1888
|
servicesMap,
|
|
1775
1889
|
dataFlowFrames,
|
|
1776
1890
|
tempDir: retMap?.tempDir,
|
|
@@ -1877,11 +1991,7 @@ export function attributeCIncludes(
|
|
|
1877
1991
|
continue;
|
|
1878
1992
|
}
|
|
1879
1993
|
const known = locations.get(purl) || new Set();
|
|
1880
|
-
known.add(
|
|
1881
|
-
slice.lineNumber
|
|
1882
|
-
? `${slice.fileName}#${slice.lineNumber}`
|
|
1883
|
-
: slice.fileName,
|
|
1884
|
-
);
|
|
1994
|
+
known.add(occurrenceLocationKey(slice.fileName, slice.lineNumber));
|
|
1885
1995
|
locations.set(purl, known);
|
|
1886
1996
|
}
|
|
1887
1997
|
return { symbols, locations };
|
|
@@ -2284,7 +2394,7 @@ export function parseSemanticSlices(_language, components, semanticsSlice) {
|
|
|
2284
2394
|
continue;
|
|
2285
2395
|
}
|
|
2286
2396
|
for (const aline of lines) {
|
|
2287
|
-
locations.add(
|
|
2397
|
+
locations.add(occurrenceLocationKey(aswiftFile, aline));
|
|
2288
2398
|
}
|
|
2289
2399
|
}
|
|
2290
2400
|
}
|
|
@@ -2294,7 +2404,7 @@ export function parseSemanticSlices(_language, components, semanticsSlice) {
|
|
|
2294
2404
|
fileIndexes,
|
|
2295
2405
|
)) {
|
|
2296
2406
|
for (const aline of ahit.lineNumbers) {
|
|
2297
|
-
locations.add(
|
|
2407
|
+
locations.add(occurrenceLocationKey(ahit.file, aline));
|
|
2298
2408
|
}
|
|
2299
2409
|
}
|
|
2300
2410
|
}
|
|
@@ -2958,8 +3068,11 @@ export function indexVersionlessCrypto(cryptoGeneratePurls) {
|
|
|
2958
3068
|
* Method to create the SBOM with evidence file called evinse file.
|
|
2959
3069
|
*
|
|
2960
3070
|
* @param {Object} sliceArtefacts Various artefacts from the slice operation
|
|
2961
|
-
* @param {Object} options Command line options
|
|
2962
|
-
*
|
|
3071
|
+
* @param {Object} options Command line options. With `returnInternalModel`,
|
|
3072
|
+
* a CycloneDX 2.0 run returns the 1.x-shaped internal model instead of the
|
|
3073
|
+
* rendered document; the file written is the rendered document either way.
|
|
3074
|
+
* @returns {Promise<Object>} The BOM written to the output file, or its
|
|
3075
|
+
* internal model when `returnInternalModel` is set
|
|
2963
3076
|
*/
|
|
2964
3077
|
export async function createEvinseFile(sliceArtefacts, options) {
|
|
2965
3078
|
const {
|
|
@@ -2969,6 +3082,8 @@ export async function createEvinseFile(sliceArtefacts, options) {
|
|
|
2969
3082
|
dataFlowSlicesFile,
|
|
2970
3083
|
reachablesSlicesFile,
|
|
2971
3084
|
purlLocationMap,
|
|
3085
|
+
purlSymbolMap = {},
|
|
3086
|
+
clearedOccurrencePurls,
|
|
2972
3087
|
servicesMap,
|
|
2973
3088
|
dataFlowFrames,
|
|
2974
3089
|
componentPropertiesMap = {},
|
|
@@ -2978,9 +3093,13 @@ export async function createEvinseFile(sliceArtefacts, options) {
|
|
|
2978
3093
|
cryptoGeneratePurls,
|
|
2979
3094
|
workspaceComponent,
|
|
2980
3095
|
} = sliceArtefacts;
|
|
2981
|
-
const bomFile = options.input;
|
|
2982
3096
|
const evinseOutFile = options.output;
|
|
2983
|
-
|
|
3097
|
+
// A 2.0 input is read into the 1.x internal model; everything below works
|
|
3098
|
+
// on the 1.x shape and the final postProcess renders 2.0 again. A caller
|
|
3099
|
+
// that converted the input already (bin/evinse.js) passes the model as
|
|
3100
|
+
// `inputBomJson` and has printed the losses.
|
|
3101
|
+
const { bomJson, losses: bomLosses } = loadEvinseInputBom(options);
|
|
3102
|
+
reportEvinseInputLosses(bomLosses);
|
|
2984
3103
|
const components = bomJson.components || [];
|
|
2985
3104
|
// Clear existing annotations
|
|
2986
3105
|
bomJson.annotations = [];
|
|
@@ -3020,19 +3139,40 @@ export async function createEvinseFile(sliceArtefacts, options) {
|
|
|
3020
3139
|
}
|
|
3021
3140
|
delete comp.signature;
|
|
3022
3141
|
appendComponentProperties(comp, componentPropertiesMap[comp.purl]);
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
).
|
|
3142
|
+
// A key with an offset names a use site; the line-only key of the same
|
|
3143
|
+
// line names no place of its own then, so it is dropped rather than
|
|
3144
|
+
// counted beside the use sites (see dropSubsumedOccurrenceKeys).
|
|
3145
|
+
const locationOccurrences = dropSubsumedOccurrenceKeys(
|
|
3146
|
+
Array.from(purlLocationMap[comp.purl] || [])
|
|
3147
|
+
.filter(Boolean)
|
|
3148
|
+
.sort(),
|
|
3149
|
+
);
|
|
3026
3150
|
if (locationOccurrences.length) {
|
|
3027
3151
|
if (!comp.evidence) {
|
|
3028
3152
|
comp.evidence = {};
|
|
3029
3153
|
}
|
|
3030
3154
|
// This step would replace any existing occurrences
|
|
3031
3155
|
// This is fine as long as the input sbom was also generated by cdxgen
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3156
|
+
// A callsites occurrence carries its column as `offset` and its member
|
|
3157
|
+
// as `symbol`; an occurrence from any other source has neither.
|
|
3158
|
+
const symbols = purlSymbolMap[comp.purl] || {};
|
|
3159
|
+
comp.evidence.occurrences = locationOccurrences.map((l) =>
|
|
3160
|
+
parseOccurrenceEvidenceLocation(
|
|
3161
|
+
l,
|
|
3162
|
+
symbols[l] ? { symbol: symbols[l] } : {},
|
|
3163
|
+
),
|
|
3164
|
+
);
|
|
3035
3165
|
occEvidencePresent = true;
|
|
3166
|
+
} else if (
|
|
3167
|
+
clearedOccurrencePurls?.has(comp.purl) &&
|
|
3168
|
+
comp.evidence?.occurrences
|
|
3169
|
+
) {
|
|
3170
|
+
// The input BOM credited this version with places another installed
|
|
3171
|
+
// version of the package takes.
|
|
3172
|
+
delete comp.evidence.occurrences;
|
|
3173
|
+
if (!Object.keys(comp.evidence).length) {
|
|
3174
|
+
delete comp.evidence;
|
|
3175
|
+
}
|
|
3036
3176
|
}
|
|
3037
3177
|
const compKey = versionlessPurlKey(comp.purl);
|
|
3038
3178
|
let dfFrames = dataFlowFrames[comp.purl];
|
|
@@ -3147,9 +3287,10 @@ export async function createEvinseFile(sliceArtefacts, options) {
|
|
|
3147
3287
|
// Redo post-processing with evinse data. `executeOsQuery` has to be injected
|
|
3148
3288
|
// because postgen no longer imports it directly (stages is below managers);
|
|
3149
3289
|
// without it the crypto formulation path receives `undefined` and throws.
|
|
3290
|
+
const { inputBomJson: _inputBomJson, ...postProcessOptions } = options;
|
|
3150
3291
|
const bomNSData = await postProcess(
|
|
3151
3292
|
{ bomJson },
|
|
3152
|
-
{ ...
|
|
3293
|
+
{ ...postProcessOptions, executeOsQuery },
|
|
3153
3294
|
);
|
|
3154
3295
|
safeWriteSync(
|
|
3155
3296
|
evinseOutFile,
|
|
@@ -3190,6 +3331,9 @@ export async function createEvinseFile(sliceArtefacts, options) {
|
|
|
3190
3331
|
if (tempDirOwned && tempDir?.startsWith(getTmpDir())) {
|
|
3191
3332
|
safeRmSync(tempDir, { recursive: true, force: true });
|
|
3192
3333
|
}
|
|
3334
|
+
if (options.returnInternalModel && bomNSData?.internalBomJson) {
|
|
3335
|
+
return bomNSData.internalBomJson;
|
|
3336
|
+
}
|
|
3193
3337
|
return bomNSData?.bomJson;
|
|
3194
3338
|
}
|
|
3195
3339
|
|
|
@@ -3653,7 +3797,7 @@ export function attributeJsModules(
|
|
|
3653
3797
|
if (!purlLocationMap[apurl]) {
|
|
3654
3798
|
purlLocationMap[apurl] = new Set();
|
|
3655
3799
|
}
|
|
3656
|
-
purlLocationMap[apurl].add(
|
|
3800
|
+
purlLocationMap[apurl].add(occurrenceLocationKey(fileName, line));
|
|
3657
3801
|
}
|
|
3658
3802
|
};
|
|
3659
3803
|
const calls = []
|
|
@@ -3683,17 +3827,23 @@ export function attributeJsModules(
|
|
|
3683
3827
|
* Attribute a callsites slice's records to the packages providing their modules.
|
|
3684
3828
|
*
|
|
3685
3829
|
* Each record of `{"callsites": [...]}` (atom `callsites` command) is one external use site
|
|
3686
|
-
* carrying its own file and
|
|
3687
|
-
* matches the record's module.
|
|
3830
|
+
* carrying its own file, line and column; the record is an occurrence of every purl whose
|
|
3831
|
+
* package matches the record's module. A record with a column (0 or more; atom writes -1 for
|
|
3832
|
+
* none) is keyed `file:line:col`, so two use sites on one line are two occurrences, and its
|
|
3833
|
+
* occurrence carries the column as `offset` and `module` joined with `path` by `.` as
|
|
3834
|
+
* `symbol`. A record without a column keys the line (`file#line`) and its occurrence carries
|
|
3835
|
+
* the `symbol` alone; a record without a `path` leaves the symbol at the bare `module`.
|
|
3688
3836
|
*
|
|
3689
3837
|
* @param {Object} callsitesSlice callsites slice with a `callsites` array
|
|
3690
3838
|
* @param {Object} purlImportsMap purl -> imported modules and symbols
|
|
3691
|
-
* @param {Object} purlLocationMap purl -> Set of
|
|
3839
|
+
* @param {Object} purlLocationMap purl -> Set of occurrence keys
|
|
3840
|
+
* @param {Object} [purlSymbolMap] purl -> occurrence key -> symbol, filled in place
|
|
3692
3841
|
*/
|
|
3693
3842
|
export function attributeJsCallsites(
|
|
3694
3843
|
callsitesSlice,
|
|
3695
3844
|
purlImportsMap,
|
|
3696
3845
|
purlLocationMap,
|
|
3846
|
+
purlSymbolMap = {},
|
|
3697
3847
|
) {
|
|
3698
3848
|
const byPackage = jsPurlsByPackage(purlImportsMap);
|
|
3699
3849
|
for (const record of callsitesSlice?.callsites || []) {
|
|
@@ -3701,11 +3851,23 @@ export function attributeJsCallsites(
|
|
|
3701
3851
|
if (!pkg || !(record.line > 0)) {
|
|
3702
3852
|
continue;
|
|
3703
3853
|
}
|
|
3854
|
+
const key = occurrenceLocationKey(record.file, record.line, record.col);
|
|
3855
|
+
const symbol = record.path
|
|
3856
|
+
? `${record.module}.${record.path}`
|
|
3857
|
+
: record.module;
|
|
3704
3858
|
for (const apurl of byPackage[pkg] || []) {
|
|
3705
3859
|
if (!purlLocationMap[apurl]) {
|
|
3706
3860
|
purlLocationMap[apurl] = new Set();
|
|
3707
3861
|
}
|
|
3708
|
-
purlLocationMap[apurl].add(
|
|
3862
|
+
purlLocationMap[apurl].add(key);
|
|
3863
|
+
// The first symbol a key carries wins, so a re-run over a previous
|
|
3864
|
+
// output, which seeds the symbol the first run wrote, stays stable.
|
|
3865
|
+
if (symbol) {
|
|
3866
|
+
const symbols = (purlSymbolMap[apurl] ??= {});
|
|
3867
|
+
if (symbols[key] === undefined) {
|
|
3868
|
+
symbols[key] = symbol;
|
|
3869
|
+
}
|
|
3870
|
+
}
|
|
3709
3871
|
}
|
|
3710
3872
|
}
|
|
3711
3873
|
}
|
|
@@ -3714,5 +3876,5 @@ function addToOverrides(lKeyOverrides, atype, fileName, ausageLineNumber) {
|
|
|
3714
3876
|
if (!lKeyOverrides[atype]) {
|
|
3715
3877
|
lKeyOverrides[atype] = new Set();
|
|
3716
3878
|
}
|
|
3717
|
-
lKeyOverrides[atype].add(
|
|
3879
|
+
lKeyOverrides[atype].add(occurrenceLocationKey(fileName, ausageLineNumber));
|
|
3718
3880
|
}
|
package/lib/evinser/scalasem.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { occurrenceLocationKey } from "../inventory/occurrenceKey.js";
|
|
2
|
+
|
|
1
3
|
function buildNSCache(components) {
|
|
2
4
|
const typePurlsCache = {};
|
|
3
5
|
for (const comp of components) {
|
|
@@ -52,7 +54,9 @@ export function findPurlLocations(components, semanticsSlice) {
|
|
|
52
54
|
if (!purlLocationsSet[apurl]) {
|
|
53
55
|
purlLocationsSet[apurl] = new Set();
|
|
54
56
|
}
|
|
55
|
-
purlLocationsSet[apurl].add(
|
|
57
|
+
purlLocationsSet[apurl].add(
|
|
58
|
+
occurrenceLocationKey(values.sourceFile || key),
|
|
59
|
+
);
|
|
56
60
|
}
|
|
57
61
|
}
|
|
58
62
|
}
|
|
@@ -10,6 +10,10 @@ import { readUserFileText } from "../core/boundedFile.js";
|
|
|
10
10
|
import { safeExistsSync } from "../core/fs.js";
|
|
11
11
|
import { isMac, isWin, redactHomePath } from "../core/paths.js";
|
|
12
12
|
import { parseToml } from "../core/toml.js";
|
|
13
|
+
import {
|
|
14
|
+
kiroPermissionSummaryEntries,
|
|
15
|
+
summarizeKiroPermissions,
|
|
16
|
+
} from "../parsers/kiroPermissions.js";
|
|
13
17
|
import { findIdeProductInfo, IDE_PRODUCTS } from "./vsixutils.js";
|
|
14
18
|
|
|
15
19
|
export { redactHomePath };
|
|
@@ -109,6 +113,7 @@ export function getAgenticToolDirs(home = homedir()) {
|
|
|
109
113
|
join(home, ".kiro", "sessions", "cli"),
|
|
110
114
|
],
|
|
111
115
|
homeDir: join(home, ".kiro"),
|
|
116
|
+
mcpConfigs: [{ file: join("settings", "mcp.json"), key: "mcpServers" }],
|
|
112
117
|
supportDirs: isWin
|
|
113
118
|
? [join(appData, "kiro-cli")]
|
|
114
119
|
: isMac
|
|
@@ -361,6 +366,28 @@ function parseJsonFile(filePath) {
|
|
|
361
366
|
}
|
|
362
367
|
}
|
|
363
368
|
|
|
369
|
+
/**
|
|
370
|
+
* Read a small YAML settings file, such as a Kiro permissions document,
|
|
371
|
+
* through the bounded user-file reader. A file that exists but holds no
|
|
372
|
+
* document, such as an empty or comment-only permissions file, reads as an
|
|
373
|
+
* empty object so its presence still counts.
|
|
374
|
+
*
|
|
375
|
+
* @param {string} filePath Absolute file path
|
|
376
|
+
* @returns {Object|undefined} Parsed document, or undefined when the file is
|
|
377
|
+
* missing, unreadable, or malformed
|
|
378
|
+
*/
|
|
379
|
+
function parseYamlFile(filePath) {
|
|
380
|
+
try {
|
|
381
|
+
const raw = readConfigText(filePath);
|
|
382
|
+
if (typeof raw !== "string") {
|
|
383
|
+
return undefined;
|
|
384
|
+
}
|
|
385
|
+
return loadYaml(raw) || {};
|
|
386
|
+
} catch {
|
|
387
|
+
return undefined;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
364
391
|
/**
|
|
365
392
|
* Strip `//` line comments, block comments, and trailing commas from JSONC
|
|
366
393
|
* text.
|
|
@@ -556,6 +583,58 @@ function readToolVersion(tool, dir, supportDir) {
|
|
|
556
583
|
return undefined;
|
|
557
584
|
}
|
|
558
585
|
|
|
586
|
+
/**
|
|
587
|
+
* Collect the workspace-scope permission counts under
|
|
588
|
+
* `~/.kiro/workspace-roots/<hash>/permissions.yaml`. Only counts and booleans
|
|
589
|
+
* are returned; the hash-named directories, the file paths, and the rule
|
|
590
|
+
* match patterns stay out of the result.
|
|
591
|
+
*
|
|
592
|
+
* @param {string} dir Kiro home directory
|
|
593
|
+
* @returns {{workspacePermissionFileCount?: number, trustedWorkspaceCount?: number, workspaceWithAllowRuleCount?: number}}
|
|
594
|
+
* Aggregate workspace posture counts
|
|
595
|
+
*/
|
|
596
|
+
function countKiroWorkspacePermissions(dir) {
|
|
597
|
+
const rootsDir = join(dir, "workspace-roots");
|
|
598
|
+
if (!safeExistsSync(rootsDir)) {
|
|
599
|
+
return {};
|
|
600
|
+
}
|
|
601
|
+
let entries = [];
|
|
602
|
+
try {
|
|
603
|
+
entries = readdirSync(rootsDir, { withFileTypes: true });
|
|
604
|
+
} catch {
|
|
605
|
+
return {};
|
|
606
|
+
}
|
|
607
|
+
let fileCount = 0;
|
|
608
|
+
let trustedCount = 0;
|
|
609
|
+
let withAllowRuleCount = 0;
|
|
610
|
+
for (const entry of entries) {
|
|
611
|
+
if (!entry.isDirectory()) {
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
614
|
+
const permissionsPath = join(rootsDir, entry.name, "permissions.yaml");
|
|
615
|
+
const doc = parseYamlFile(permissionsPath);
|
|
616
|
+
if (doc === undefined) {
|
|
617
|
+
continue;
|
|
618
|
+
}
|
|
619
|
+
fileCount += 1;
|
|
620
|
+
const summary = summarizeKiroPermissions(doc);
|
|
621
|
+
if (summary.workspaceTrusted === true) {
|
|
622
|
+
trustedCount += 1;
|
|
623
|
+
}
|
|
624
|
+
if (summary.allowRuleCount > 0) {
|
|
625
|
+
withAllowRuleCount += 1;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
if (!fileCount) {
|
|
629
|
+
return {};
|
|
630
|
+
}
|
|
631
|
+
return {
|
|
632
|
+
trustedWorkspaceCount: trustedCount,
|
|
633
|
+
workspacePermissionFileCount: fileCount,
|
|
634
|
+
workspaceWithAllowRuleCount: withAllowRuleCount,
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
|
|
559
638
|
function countKiroSignals(dir, supportDir) {
|
|
560
639
|
const signals = {};
|
|
561
640
|
const agentsDir = join(dir, "agents");
|
|
@@ -578,6 +657,17 @@ function countKiroSignals(dir, supportDir) {
|
|
|
578
657
|
if (settings && typeof settings["chat.defaultAgent"] === "string") {
|
|
579
658
|
signals.defaultAgent = settings["chat.defaultAgent"];
|
|
580
659
|
}
|
|
660
|
+
// The V3 user-scope permission posture from ~/.kiro/settings/permissions.yaml.
|
|
661
|
+
const userPermissions = parseYamlFile(
|
|
662
|
+
join(dir, "settings", "permissions.yaml"),
|
|
663
|
+
);
|
|
664
|
+
if (userPermissions !== undefined) {
|
|
665
|
+
Object.assign(
|
|
666
|
+
signals,
|
|
667
|
+
kiroPermissionSummaryEntries(summarizeKiroPermissions(userPermissions)),
|
|
668
|
+
);
|
|
669
|
+
}
|
|
670
|
+
Object.assign(signals, countKiroWorkspacePermissions(dir));
|
|
581
671
|
return signals;
|
|
582
672
|
}
|
|
583
673
|
|
|
@@ -954,7 +1044,10 @@ function childBomRef(parentRef, kind, id) {
|
|
|
954
1044
|
* Parse Kiro agent definitions (`~/.kiro/agents/*.json`) into components.
|
|
955
1045
|
*
|
|
956
1046
|
* Each agent contributes its name, model, and redacted counts of MCP servers,
|
|
957
|
-
* tools, and allowed tools
|
|
1047
|
+
* tools, and allowed tools, plus the permission posture of its V3
|
|
1048
|
+
* `permissions` object and the integer `kiroUpgrade` revision. Prompts,
|
|
1049
|
+
* resource values, match patterns, and the upgrade fingerprint or source are
|
|
1050
|
+
* never copied.
|
|
958
1051
|
*
|
|
959
1052
|
* @param {string} dir Kiro home directory
|
|
960
1053
|
* @param {string} parentRef Parent tool bom-ref
|
|
@@ -1010,6 +1103,24 @@ function parseKiroAgents(dir, parentRef, home = homedir()) {
|
|
|
1010
1103
|
value: String(agent.allowedTools.length),
|
|
1011
1104
|
});
|
|
1012
1105
|
}
|
|
1106
|
+
if (agent.permissions && typeof agent.permissions === "object") {
|
|
1107
|
+
for (const [suffix, value] of Object.entries(
|
|
1108
|
+
kiroPermissionSummaryEntries(
|
|
1109
|
+
summarizeKiroPermissions(agent.permissions),
|
|
1110
|
+
),
|
|
1111
|
+
)) {
|
|
1112
|
+
properties.push({
|
|
1113
|
+
name: `cdx:agentic:${suffix}`,
|
|
1114
|
+
value: String(value),
|
|
1115
|
+
});
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
if (Number.isInteger(agent.kiroUpgrade?.revision)) {
|
|
1119
|
+
properties.push({
|
|
1120
|
+
name: "cdx:agentic:upgradeRevision",
|
|
1121
|
+
value: String(agent.kiroUpgrade.revision),
|
|
1122
|
+
});
|
|
1123
|
+
}
|
|
1013
1124
|
components.push({
|
|
1014
1125
|
"bom-ref": childBomRef(parentRef, "agent", agent.name),
|
|
1015
1126
|
type: "application",
|