@cdxgen/cdxgen 13.3.0 → 13.4.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 +9 -6
- package/bin/cdxgen.js +90 -26
- package/bin/evinse.js +18 -1
- package/bin/repl.js +86 -0
- package/bin/sign.js +45 -15
- package/bin/tracebom.js +5 -1
- package/bin/validate.js +84 -18
- package/bin/verify.js +155 -86
- package/data/helpers/init.gradle +46 -21
- package/data/remediations.json +61 -0
- package/data/rules/dependency-sources.yaml +67 -0
- package/data/rules/scala-evidence.yaml +115 -0
- package/lib/audit/index.js +38 -6
- package/lib/cli/index.js +113 -54
- package/lib/cli/jsBom.js +50 -29
- package/lib/cli/jvmBom.js +665 -138
- package/lib/cli/managedBom.js +320 -200
- package/lib/cli/nativeBom.js +139 -30
- package/lib/core/activity.js +12 -0
- package/lib/core/env.js +17 -9
- package/lib/core/fs.js +3 -2
- package/lib/core/httpClient.js +159 -24
- package/lib/core/pluginRun.js +184 -0
- package/lib/core/processTree.js +100 -0
- package/lib/core/runState.js +39 -0
- package/lib/core/toml.js +72 -0
- package/lib/ecosystems/asarutils.js +4 -2
- package/lib/ecosystems/bunutils.js +5 -7
- package/lib/ecosystems/cargoCache.js +231 -8
- package/lib/ecosystems/cmakeResolver.js +10 -21
- package/lib/ecosystems/cppBuildContext.js +631 -0
- package/lib/ecosystems/cppEvidence.js +337 -22
- package/lib/ecosystems/cppIncludeResolver.js +294 -0
- package/lib/ecosystems/denoutils.js +93 -66
- package/lib/ecosystems/ecosystems.js +1967 -788
- package/lib/ecosystems/golem.js +28 -12
- package/lib/ecosystems/gradleutils.js +314 -30
- package/lib/ecosystems/jsEvidence.js +85 -24
- package/lib/ecosystems/kosi.js +21 -4
- package/lib/ecosystems/npmutils.js +64 -0
- package/lib/ecosystems/parsers-dotnet.js +567 -151
- package/lib/ecosystems/parsers-elm.js +46 -28
- package/lib/ecosystems/parsers-gleam.js +3 -4
- package/lib/ecosystems/parsers-go.js +324 -61
- package/lib/ecosystems/parsers-js.js +92 -26
- package/lib/ecosystems/parsers-julia.js +2 -3
- package/lib/ecosystems/parsers-jvm.js +204 -12
- package/lib/ecosystems/parsers-misc.js +182 -55
- package/lib/ecosystems/parsers-python.js +141 -10
- package/lib/ecosystems/parsers-rust.js +4 -4
- package/lib/ecosystems/parsers-terraform.js +1573 -19
- package/lib/ecosystems/pythonutils.js +102 -6
- package/lib/ecosystems/remote/huggingface.js +3 -0
- package/lib/ecosystems/rubyutils.js +67 -17
- package/lib/ecosystems/rusi.js +22 -9
- package/lib/ecosystems/sbtutils.js +243 -187
- package/lib/ecosystems/scalacliutils.js +342 -0
- package/lib/ecosystems/scalasem.js +2618 -0
- package/lib/ecosystems/terraformRegistry.js +288 -0
- package/lib/ecosystems/utils.js +2 -0
- package/lib/ecosystems/vendoredCode.js +156 -0
- package/lib/evinser/evinser.js +578 -69
- package/lib/helpers/agenticToolutils.js +2 -2
- package/lib/helpers/bomSigner.js +576 -128
- package/lib/inventory/aiCollector.js +4 -1
- package/lib/inventory/analyzer.js +110 -127
- package/lib/inventory/atomUtils.js +171 -48
- package/lib/inventory/auditCategories.js +7 -0
- package/lib/inventory/buildToolRateLimit.js +144 -0
- package/lib/inventory/cacheCommands.js +2 -1
- package/lib/inventory/cmakeBuildDirs.js +254 -0
- package/lib/inventory/deps.js +501 -124
- package/lib/inventory/dosai.js +248 -44
- package/lib/inventory/dosaiParsers.js +310 -15
- package/lib/inventory/dynamic.js +52 -6
- package/lib/inventory/evidenceUtils.js +154 -109
- package/lib/inventory/fetchBatch.js +574 -38
- package/lib/inventory/fetchDiskCache.js +597 -0
- package/lib/inventory/forgeEnricher.js +14 -5
- package/lib/inventory/jvmLocalRepos.js +917 -0
- package/lib/inventory/mcpConfigParser.js +2 -3
- package/lib/inventory/parentSelection.js +146 -0
- package/lib/inventory/rustFormulationParser.js +3 -4
- package/lib/inventory/scalaCoords.js +153 -0
- package/lib/inventory/source.js +8 -8
- package/lib/inventory/spdx.js +101 -0
- package/lib/inventory/traceRunner.js +88 -7
- package/lib/inventory/tracedCrypto.js +294 -0
- package/lib/managers/docker.js +33 -32
- package/lib/managers/oci.js +24 -2
- package/lib/parsers/cmakeCache.js +69 -0
- package/lib/parsers/cmakeLists.js +16 -2
- package/lib/parsers/cmakePresets.js +400 -0
- package/lib/parsers/compileCommands.js +557 -0
- package/lib/parsers/strictJson.js +322 -0
- package/lib/parsers/terraformConfig.js +1073 -0
- package/lib/parsers/terraformSource.js +652 -0
- package/lib/server/server.js +23 -0
- package/lib/stages/postgen/introspection/markers.js +1 -1
- package/lib/stages/postgen/postgen.js +15 -1
- package/lib/stages/postgen/specVersionCompat.js +32 -0
- package/lib/validator/index.js +25 -10
- package/lib/validator/reporters/json.js +1 -0
- package/package.json +21 -21
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/cli/jsBom.d.ts.map +1 -1
- package/types/lib/cli/jvmBom.d.ts +24 -1
- package/types/lib/cli/jvmBom.d.ts.map +1 -1
- package/types/lib/cli/managedBom.d.ts +7 -4
- package/types/lib/cli/managedBom.d.ts.map +1 -1
- package/types/lib/cli/nativeBom.d.ts +3 -1
- package/types/lib/cli/nativeBom.d.ts.map +1 -1
- package/types/lib/core/activity.d.ts.map +1 -1
- package/types/lib/core/env.d.ts +9 -0
- package/types/lib/core/env.d.ts.map +1 -1
- package/types/lib/core/fs.d.ts.map +1 -1
- package/types/lib/core/httpClient.d.ts +13 -8
- package/types/lib/core/httpClient.d.ts.map +1 -1
- package/types/lib/core/pluginRun.d.ts +97 -0
- package/types/lib/core/pluginRun.d.ts.map +1 -0
- package/types/lib/core/processTree.d.ts +36 -0
- package/types/lib/core/processTree.d.ts.map +1 -0
- package/types/lib/core/runState.d.ts +29 -0
- package/types/lib/core/runState.d.ts.map +1 -0
- package/types/lib/core/toml.d.ts +20 -0
- package/types/lib/core/toml.d.ts.map +1 -0
- package/types/lib/ecosystems/asarutils.d.ts.map +1 -1
- package/types/lib/ecosystems/bunutils.d.ts.map +1 -1
- package/types/lib/ecosystems/cargoCache.d.ts +12 -0
- package/types/lib/ecosystems/cargoCache.d.ts.map +1 -1
- package/types/lib/ecosystems/cmakeResolver.d.ts +5 -3
- package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -1
- package/types/lib/ecosystems/cppBuildContext.d.ts +55 -0
- package/types/lib/ecosystems/cppBuildContext.d.ts.map +1 -0
- package/types/lib/ecosystems/cppEvidence.d.ts +18 -2
- package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/cppIncludeResolver.d.ts +92 -0
- package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -0
- package/types/lib/ecosystems/denoutils.d.ts +9 -3
- package/types/lib/ecosystems/denoutils.d.ts.map +1 -1
- package/types/lib/ecosystems/ecosystems.d.ts +62 -13
- package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
- package/types/lib/ecosystems/golem.d.ts.map +1 -1
- package/types/lib/ecosystems/gradleutils.d.ts +50 -0
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -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/npmutils.d.ts +15 -0
- package/types/lib/ecosystems/npmutils.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-dotnet.d.ts +118 -4
- package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-gleam.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-go.d.ts +44 -2
- package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-js.d.ts +6 -1
- package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-jvm.d.ts +46 -1
- package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-misc.d.ts +3 -1
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-python.d.ts +53 -3
- package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-terraform.d.ts +38 -21
- package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -1
- package/types/lib/ecosystems/pythonutils.d.ts +27 -2
- package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
- package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -1
- package/types/lib/ecosystems/rubyutils.d.ts +21 -1
- package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rusi.d.ts.map +1 -1
- package/types/lib/ecosystems/sbtutils.d.ts +88 -2
- package/types/lib/ecosystems/sbtutils.d.ts.map +1 -1
- package/types/lib/ecosystems/scalacliutils.d.ts +56 -0
- package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -0
- package/types/lib/ecosystems/scalasem.d.ts +311 -0
- package/types/lib/ecosystems/scalasem.d.ts.map +1 -0
- package/types/lib/ecosystems/terraformRegistry.d.ts +9 -0
- package/types/lib/ecosystems/terraformRegistry.d.ts.map +1 -0
- package/types/lib/ecosystems/utils.d.ts +2 -2
- package/types/lib/ecosystems/utils.d.ts.map +1 -1
- package/types/lib/ecosystems/vendoredCode.d.ts +32 -0
- package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -0
- package/types/lib/evinser/evinser.d.ts +75 -8
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/helpers/bomSigner.d.ts +80 -4
- package/types/lib/helpers/bomSigner.d.ts.map +1 -1
- package/types/lib/inventory/aiCollector.d.ts.map +1 -1
- package/types/lib/inventory/analyzer.d.ts.map +1 -1
- package/types/lib/inventory/atomUtils.d.ts +82 -10
- package/types/lib/inventory/atomUtils.d.ts.map +1 -1
- package/types/lib/inventory/auditCategories.d.ts +3 -0
- package/types/lib/inventory/auditCategories.d.ts.map +1 -1
- package/types/lib/inventory/buildToolRateLimit.d.ts +38 -0
- package/types/lib/inventory/buildToolRateLimit.d.ts.map +1 -0
- package/types/lib/inventory/cacheCommands.d.ts +2 -1
- package/types/lib/inventory/cacheCommands.d.ts.map +1 -1
- package/types/lib/inventory/cmakeBuildDirs.d.ts +58 -0
- package/types/lib/inventory/cmakeBuildDirs.d.ts.map +1 -0
- package/types/lib/inventory/deps.d.ts +59 -1
- package/types/lib/inventory/deps.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +57 -8
- package/types/lib/inventory/dosai.d.ts.map +1 -1
- package/types/lib/inventory/dosaiParsers.d.ts +53 -7
- package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
- package/types/lib/inventory/dynamic.d.ts +10 -0
- package/types/lib/inventory/dynamic.d.ts.map +1 -1
- package/types/lib/inventory/evidenceUtils.d.ts +19 -4
- package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
- package/types/lib/inventory/fetchBatch.d.ts +83 -14
- package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
- package/types/lib/inventory/fetchDiskCache.d.ts +169 -0
- package/types/lib/inventory/fetchDiskCache.d.ts.map +1 -0
- package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
- package/types/lib/inventory/jvmLocalRepos.d.ts +178 -0
- package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -0
- package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/parentSelection.d.ts +34 -0
- package/types/lib/inventory/parentSelection.d.ts.map +1 -0
- package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
- package/types/lib/inventory/scalaCoords.d.ts +80 -0
- package/types/lib/inventory/scalaCoords.d.ts.map +1 -0
- package/types/lib/inventory/source.d.ts.map +1 -1
- package/types/lib/inventory/spdx.d.ts +9 -0
- package/types/lib/inventory/spdx.d.ts.map +1 -1
- package/types/lib/inventory/traceRunner.d.ts +12 -0
- package/types/lib/inventory/traceRunner.d.ts.map +1 -1
- package/types/lib/inventory/tracedCrypto.d.ts +42 -0
- package/types/lib/inventory/tracedCrypto.d.ts.map +1 -0
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/managers/oci.d.ts +3 -1
- package/types/lib/managers/oci.d.ts.map +1 -1
- package/types/lib/parsers/cmakeCache.d.ts +16 -0
- package/types/lib/parsers/cmakeCache.d.ts.map +1 -1
- package/types/lib/parsers/cmakeLists.d.ts +2 -1
- package/types/lib/parsers/cmakeLists.d.ts.map +1 -1
- package/types/lib/parsers/cmakePresets.d.ts +78 -0
- package/types/lib/parsers/cmakePresets.d.ts.map +1 -0
- package/types/lib/parsers/compileCommands.d.ts +115 -0
- package/types/lib/parsers/compileCommands.d.ts.map +1 -0
- package/types/lib/parsers/strictJson.d.ts +46 -0
- package/types/lib/parsers/strictJson.d.ts.map +1 -0
- package/types/lib/parsers/terraformConfig.d.ts +70 -0
- package/types/lib/parsers/terraformConfig.d.ts.map +1 -0
- package/types/lib/parsers/terraformSource.d.ts +66 -0
- package/types/lib/parsers/terraformSource.d.ts.map +1 -0
- package/types/lib/server/server.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
- package/types/lib/validator/index.d.ts +8 -0
- package/types/lib/validator/index.d.ts.map +1 -1
- package/types/lib/validator/reporters/json.d.ts.map +1 -1
- package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +0 -462
package/lib/inventory/dosai.js
CHANGED
|
@@ -20,7 +20,9 @@ import {
|
|
|
20
20
|
safeSpawnSync,
|
|
21
21
|
safeWriteChunksSync,
|
|
22
22
|
safeWriteSync,
|
|
23
|
+
TIMEOUT_MS,
|
|
23
24
|
} from "../core/fs.js";
|
|
25
|
+
import { formatDuration, reapProcessTree } from "../core/processTree.js";
|
|
24
26
|
import {
|
|
25
27
|
indexJsonObject,
|
|
26
28
|
MAX_JSON_TEXT_BYTES,
|
|
@@ -675,6 +677,69 @@ export function readDosaiDataFlowReport(reportFile, options = {}) {
|
|
|
675
677
|
});
|
|
676
678
|
}
|
|
677
679
|
|
|
680
|
+
/**
|
|
681
|
+
* Whether a dosai run ended because it ran out of time.
|
|
682
|
+
*
|
|
683
|
+
* dosai has no limit of its own: cdxgen's spawn timeout
|
|
684
|
+
* (CDXGEN_TIMEOUT_MS) stops the process, which reaches the caller only as an
|
|
685
|
+
* ETIMEDOUT error looking like any other failure (issue 4438).
|
|
686
|
+
*
|
|
687
|
+
* @param {Object} result spawnSync result
|
|
688
|
+
* @returns {boolean} true when the run was stopped by the spawn timeout
|
|
689
|
+
*/
|
|
690
|
+
export function dosaiRunTimedOut(result) {
|
|
691
|
+
return result?.error?.code === "ETIMEDOUT";
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* How a dosai run was stopped before it could finish, if it was.
|
|
696
|
+
*
|
|
697
|
+
* Besides the spawn timeout, a run is stopped when its console output outgrows
|
|
698
|
+
* CDXGEN_MAX_BUFFER (ENOBUFS) or a signal ends it, such as the kernel's
|
|
699
|
+
* out-of-memory killer. A stopped run can leave its slice half written.
|
|
700
|
+
*
|
|
701
|
+
* @param {Object} result spawnSync result
|
|
702
|
+
* @returns {"timeout"|"buffer"|"signal"|undefined} Why the run was stopped, or undefined when it ended by itself
|
|
703
|
+
*/
|
|
704
|
+
export function dosaiRunStopReason(result) {
|
|
705
|
+
if (dosaiRunTimedOut(result)) {
|
|
706
|
+
return "timeout";
|
|
707
|
+
}
|
|
708
|
+
if (result?.error?.code === "ENOBUFS") {
|
|
709
|
+
return "buffer";
|
|
710
|
+
}
|
|
711
|
+
if (result?.signal) {
|
|
712
|
+
return "signal";
|
|
713
|
+
}
|
|
714
|
+
return undefined;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
// The output files of the dosai runs of this process that were stopped, so a
|
|
718
|
+
// caller does not follow the stop warning with advice for another failure.
|
|
719
|
+
const stoppedDosaiOutputs = new Set();
|
|
720
|
+
|
|
721
|
+
/**
|
|
722
|
+
* Whether the dosai run that should have written a file was stopped (by the
|
|
723
|
+
* time limit, the output buffer limit, or a signal) and already reported so.
|
|
724
|
+
*
|
|
725
|
+
* @param {string} outputFile Output file passed to the dosai run
|
|
726
|
+
* @returns {boolean} true when the run was stopped
|
|
727
|
+
*/
|
|
728
|
+
export function dosaiRunWasStopped(outputFile) {
|
|
729
|
+
const outputPath = safeDosaiPath(outputFile);
|
|
730
|
+
return Boolean(outputPath && stoppedDosaiOutputs.has(outputPath));
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
function dosaiStopMessage(reason, signal) {
|
|
734
|
+
if (reason === "timeout") {
|
|
735
|
+
return `WARN: dosai did not finish within ${formatDuration(TIMEOUT_MS)} and was stopped, so its slices are missing or incomplete. Allow more time with CDXGEN_TIMEOUT_MS (milliseconds, every external command).`;
|
|
736
|
+
}
|
|
737
|
+
if (reason === "buffer") {
|
|
738
|
+
return "WARN: dosai wrote more console output than CDXGEN_MAX_BUFFER allows and was stopped, so its slices are missing or incomplete. Raise CDXGEN_MAX_BUFFER (bytes).";
|
|
739
|
+
}
|
|
740
|
+
return `WARN: dosai was stopped by ${signal || "a signal"} before it finished, so its slices are missing or incomplete. A SIGKILL usually means the system ran out of memory.`;
|
|
741
|
+
}
|
|
742
|
+
|
|
678
743
|
/**
|
|
679
744
|
* Run a dosai subcommand ("methods", "dataflows", or "crypto") against a source
|
|
680
745
|
* tree and write its JSON output to the given file.
|
|
@@ -720,6 +785,8 @@ export function runDosaiCommand(command, src, outputFile, options = {}) {
|
|
|
720
785
|
if (DEBUG_MODE) {
|
|
721
786
|
console.log("Executing", executable, [...args, ...excludeArgs].join(" "));
|
|
722
787
|
}
|
|
788
|
+
stoppedDosaiOutputs.delete(outputPath);
|
|
789
|
+
let startedAt = Date.now();
|
|
723
790
|
let result = safeSpawnSync(executable, [...args, ...excludeArgs], {
|
|
724
791
|
cwd: srcPath,
|
|
725
792
|
shell: false,
|
|
@@ -735,6 +802,7 @@ export function runDosaiCommand(command, src, outputFile, options = {}) {
|
|
|
735
802
|
warnDosaiOnce(
|
|
736
803
|
"This dosai build does not support --exclude, so it runs without the exclude patterns. Upgrade dosai or set DOSAI_CMD to a newer build to honour them.",
|
|
737
804
|
);
|
|
805
|
+
startedAt = Date.now();
|
|
738
806
|
result = safeSpawnSync(executable, args, {
|
|
739
807
|
cwd: srcPath,
|
|
740
808
|
shell: false,
|
|
@@ -756,6 +824,22 @@ export function runDosaiCommand(command, src, outputFile, options = {}) {
|
|
|
756
824
|
"Alternatively, download the dosai self-contained binary (-full suffix) from https://github.com/owasp-dep-scan/dosai/releases and set DOSAI_CMD to its location.",
|
|
757
825
|
);
|
|
758
826
|
}
|
|
827
|
+
const stopReason = dosaiRunStopReason(result);
|
|
828
|
+
if (stopReason) {
|
|
829
|
+
// Visible without debug output: otherwise a stopped dosai reads like any
|
|
830
|
+
// other failure while the scan silently loses its .NET evidence.
|
|
831
|
+
console.warn(dosaiStopMessage(stopReason, result.signal));
|
|
832
|
+
// Windows kills only the dosai process itself, and the build hosts it
|
|
833
|
+
// started would keep running.
|
|
834
|
+
reapProcessTree(result.pid, startedAt, Date.now(), "dosai");
|
|
835
|
+
// A stopped dosai can leave its output half written, and the caller would
|
|
836
|
+
// read the truncated file back as a slice.
|
|
837
|
+
if (safeExistsSync(outputPath)) {
|
|
838
|
+
safeRmSync(outputPath, { force: true });
|
|
839
|
+
}
|
|
840
|
+
stoppedDosaiOutputs.add(outputPath);
|
|
841
|
+
return false;
|
|
842
|
+
}
|
|
759
843
|
if (result?.status !== 0 || result?.error || !safeExistsSync(outputPath)) {
|
|
760
844
|
if (DEBUG_MODE) {
|
|
761
845
|
if (result?.stderr || result?.stdout) {
|
|
@@ -958,21 +1042,63 @@ export function persistDosaiSemanticsReport(
|
|
|
958
1042
|
* Build a purl alias map for a list of components.
|
|
959
1043
|
*
|
|
960
1044
|
* @param {Object[]} [components] Component objects with purl fields
|
|
961
|
-
* @
|
|
1045
|
+
* @param {Object} [options] Options passed to {@link buildDosaiPurlAliasMap} (`srcPath`)
|
|
1046
|
+
* @returns {Map<string, string>} Map of component purls, carrying the version-free identity index
|
|
962
1047
|
*/
|
|
963
|
-
export function buildPurlAliasMap(components = []) {
|
|
964
|
-
return buildDosaiPurlAliasMap(components);
|
|
1048
|
+
export function buildPurlAliasMap(components = [], options = {}) {
|
|
1049
|
+
return buildDosaiPurlAliasMap(components, options);
|
|
965
1050
|
}
|
|
966
1051
|
|
|
967
1052
|
/**
|
|
968
|
-
* Resolve a
|
|
1053
|
+
* Resolve a dosai purl to the canonical component purl, by version, and for a
|
|
1054
|
+
* package the BOM holds in several versions by the record's location.
|
|
969
1055
|
*
|
|
970
1056
|
* @param {string} purl Purl from a dosai report
|
|
971
1057
|
* @param {Map<string, string>} purlAliasMap Alias map built by buildPurlAliasMap
|
|
972
|
-
* @
|
|
1058
|
+
* @param {string} [location] Source location of the dosai record
|
|
1059
|
+
* @returns {string|undefined} Canonical component purl, the input purl when the BOM has no such package,
|
|
1060
|
+
* or undefined when empty or ambiguous
|
|
973
1061
|
*/
|
|
974
|
-
export function resolveComponentPurl(purl, purlAliasMap) {
|
|
975
|
-
return resolveDosaiComponentPurl(purl, purlAliasMap);
|
|
1062
|
+
export function resolveComponentPurl(purl, purlAliasMap, location) {
|
|
1063
|
+
return resolveDosaiComponentPurl(purl, purlAliasMap, location);
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
/**
|
|
1067
|
+
* The component purls one PackageReachability fact belongs to: each of its
|
|
1068
|
+
* locations resolved in its own project (a versionless purl from unrestored
|
|
1069
|
+
* projects spans several), else the purl alone.
|
|
1070
|
+
*
|
|
1071
|
+
* @param {Object} reachability dosai PackageReachability entry
|
|
1072
|
+
* @param {Map<string, string>} purlAliasMap Alias map built by buildPurlAliasMap
|
|
1073
|
+
* @param {Object} [graph] Call graph lookups `{ edgesById, nodesById }`
|
|
1074
|
+
* @returns {string[]} Component purls
|
|
1075
|
+
*/
|
|
1076
|
+
function reachabilityComponentPurls(reachability, purlAliasMap, graph = {}) {
|
|
1077
|
+
const locations = [
|
|
1078
|
+
...(reachability.SourceLocations || []).map((location) =>
|
|
1079
|
+
dosaiSourceLocation(location),
|
|
1080
|
+
),
|
|
1081
|
+
...(reachability.EdgeIds || []).map((edgeId) =>
|
|
1082
|
+
dosaiSourceLocation(graph.edgesById?.get(edgeId)),
|
|
1083
|
+
),
|
|
1084
|
+
...(reachability.NodeIds || []).map((nodeId) =>
|
|
1085
|
+
dosaiSourceLocationFromNode(graph.nodesById?.get(nodeId)),
|
|
1086
|
+
),
|
|
1087
|
+
].filter(Boolean);
|
|
1088
|
+
const purls = new Set(
|
|
1089
|
+
locations
|
|
1090
|
+
.map((location) =>
|
|
1091
|
+
resolveComponentPurl(reachability.Purl, purlAliasMap, location),
|
|
1092
|
+
)
|
|
1093
|
+
.filter(Boolean),
|
|
1094
|
+
);
|
|
1095
|
+
if (!purls.size) {
|
|
1096
|
+
const purl = resolveComponentPurl(reachability.Purl, purlAliasMap);
|
|
1097
|
+
if (purl) {
|
|
1098
|
+
purls.add(purl);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
return [...purls];
|
|
976
1102
|
}
|
|
977
1103
|
|
|
978
1104
|
/**
|
|
@@ -1021,24 +1147,51 @@ export function addDosaiReachabilityProperties(component, reachability) {
|
|
|
1021
1147
|
*
|
|
1022
1148
|
* @param {Object} methodsSlice Parsed dosai methods slice JSON
|
|
1023
1149
|
* @param {Object[]} [components] BOM components used to resolve purl aliases and mutate
|
|
1150
|
+
* @param {Object} [options] Options
|
|
1151
|
+
* @param {string} [options.srcPath] Directory dosai analyzed; a package the BOM holds in several
|
|
1152
|
+
* versions is matched by the project of the record's file
|
|
1024
1153
|
* @returns {number} Number of distinct components enriched
|
|
1025
1154
|
*/
|
|
1026
|
-
export function applyDosaiReachabilityEvidence(
|
|
1027
|
-
|
|
1155
|
+
export function applyDosaiReachabilityEvidence(
|
|
1156
|
+
methodsSlice,
|
|
1157
|
+
components = [],
|
|
1158
|
+
options = {},
|
|
1159
|
+
) {
|
|
1160
|
+
const purlAliasMap = buildPurlAliasMap(components, options);
|
|
1028
1161
|
const componentsByPurl = new Map(
|
|
1029
1162
|
components
|
|
1030
1163
|
.filter((component) => component?.purl)
|
|
1031
1164
|
.map((component) => [component.purl, component]),
|
|
1032
1165
|
);
|
|
1166
|
+
const graph = {
|
|
1167
|
+
edgesById: new Map(
|
|
1168
|
+
(methodsSlice?.CallGraph?.Edges || []).map((edge) => [edge.Id, edge]),
|
|
1169
|
+
),
|
|
1170
|
+
nodesById: new Map(
|
|
1171
|
+
(methodsSlice?.CallGraph?.Nodes || []).map((node) => [node.Id, node]),
|
|
1172
|
+
),
|
|
1173
|
+
};
|
|
1033
1174
|
const enriched = new Set();
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1175
|
+
// The first fact applied to a component wins, so a fact that names the
|
|
1176
|
+
// component's exact purl goes before one that reached it by name.
|
|
1177
|
+
const facts = [...(methodsSlice?.PackageReachability || [])].sort(
|
|
1178
|
+
(a, b) =>
|
|
1179
|
+
Number(!componentsByPurl.has(a.Purl)) -
|
|
1180
|
+
Number(!componentsByPurl.has(b.Purl)),
|
|
1181
|
+
);
|
|
1182
|
+
for (const reachability of facts) {
|
|
1183
|
+
for (const purl of reachabilityComponentPurls(
|
|
1184
|
+
reachability,
|
|
1185
|
+
purlAliasMap,
|
|
1186
|
+
graph,
|
|
1187
|
+
)) {
|
|
1188
|
+
const component = componentsByPurl.get(purl);
|
|
1189
|
+
if (!component) {
|
|
1190
|
+
continue;
|
|
1191
|
+
}
|
|
1192
|
+
addDosaiReachabilityProperties(component, reachability);
|
|
1193
|
+
enriched.add(component);
|
|
1039
1194
|
}
|
|
1040
|
-
addDosaiReachabilityProperties(component, reachability);
|
|
1041
|
-
enriched.add(component);
|
|
1042
1195
|
}
|
|
1043
1196
|
return enriched.size;
|
|
1044
1197
|
}
|
|
@@ -1051,10 +1204,17 @@ export function applyDosaiReachabilityEvidence(methodsSlice, components = []) {
|
|
|
1051
1204
|
*
|
|
1052
1205
|
* @param {Object} methodsSlice Parsed dosai methods slice JSON
|
|
1053
1206
|
* @param {Object[]} [components] BOM components used to resolve purl aliases
|
|
1207
|
+
* @param {Object} [options] Options
|
|
1208
|
+
* @param {string} [options.srcPath] Directory dosai analyzed; a package the BOM holds in several
|
|
1209
|
+
* versions is matched by the project of the record's file
|
|
1054
1210
|
* @returns {Object} Object with purlLocationMap, purlModulesMap, and purlMethodsMap keyed by purl
|
|
1055
1211
|
*/
|
|
1056
|
-
export function collectDosaiPurlEvidence(
|
|
1057
|
-
|
|
1212
|
+
export function collectDosaiPurlEvidence(
|
|
1213
|
+
methodsSlice,
|
|
1214
|
+
components = [],
|
|
1215
|
+
options = {},
|
|
1216
|
+
) {
|
|
1217
|
+
const purlAliasMap = buildPurlAliasMap(components, options);
|
|
1058
1218
|
const purlLocationMap = {};
|
|
1059
1219
|
const purlModulesMap = {};
|
|
1060
1220
|
const purlMethodsMap = {};
|
|
@@ -1066,7 +1226,11 @@ export function collectDosaiPurlEvidence(methodsSlice, components = []) {
|
|
|
1066
1226
|
);
|
|
1067
1227
|
|
|
1068
1228
|
for (const dependency of methodsSlice?.Dependencies || []) {
|
|
1069
|
-
const purl = resolveComponentPurl(
|
|
1229
|
+
const purl = resolveComponentPurl(
|
|
1230
|
+
dependency.Purl,
|
|
1231
|
+
purlAliasMap,
|
|
1232
|
+
dependency.Path,
|
|
1233
|
+
);
|
|
1070
1234
|
if (!purl) {
|
|
1071
1235
|
continue;
|
|
1072
1236
|
}
|
|
@@ -1079,42 +1243,61 @@ export function collectDosaiPurlEvidence(methodsSlice, components = []) {
|
|
|
1079
1243
|
}
|
|
1080
1244
|
|
|
1081
1245
|
for (const reachability of methodsSlice?.PackageReachability || []) {
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1246
|
+
// Every location resolves in its own project: a versionless purl from
|
|
1247
|
+
// unrestored projects spans projects restored at different versions.
|
|
1248
|
+
const resolveAt = (location) =>
|
|
1249
|
+
resolveComponentPurl(reachability.Purl, purlAliasMap, location);
|
|
1250
|
+
const factPurls = new Set();
|
|
1251
|
+
// Modules and methods of call-graph items that name no source file
|
|
1252
|
+
// (frames inside the package), held for the fact's own version.
|
|
1253
|
+
const unplaced = [];
|
|
1086
1254
|
let hasExplicitSourceLocations = false;
|
|
1087
1255
|
for (const sourceLocation of reachability.SourceLocations || []) {
|
|
1088
1256
|
const location = dosaiSourceLocation(sourceLocation);
|
|
1257
|
+
const purl = resolveAt(location);
|
|
1089
1258
|
addDosaiSetValue(purlLocationMap, purl, location);
|
|
1259
|
+
if (purl) {
|
|
1260
|
+
factPurls.add(purl);
|
|
1261
|
+
}
|
|
1090
1262
|
hasExplicitSourceLocations ||= Boolean(location);
|
|
1091
1263
|
}
|
|
1092
1264
|
for (const edgeId of reachability.EdgeIds || []) {
|
|
1093
1265
|
const edge = edgesById.get(edgeId);
|
|
1266
|
+
const location = dosaiSourceLocation(edge);
|
|
1267
|
+
const purl = resolveAt(location ?? edge?.Path);
|
|
1268
|
+
const method = edge?.CalledMethodName || edge?.TargetName;
|
|
1269
|
+
if (!purl) {
|
|
1270
|
+
unplaced.push([purlMethodsMap, method]);
|
|
1271
|
+
continue;
|
|
1272
|
+
}
|
|
1273
|
+
factPurls.add(purl);
|
|
1094
1274
|
if (!hasExplicitSourceLocations) {
|
|
1095
|
-
addDosaiSetValue(purlLocationMap, purl,
|
|
1275
|
+
addDosaiSetValue(purlLocationMap, purl, location);
|
|
1096
1276
|
}
|
|
1097
|
-
addDosaiSetValue(
|
|
1098
|
-
purlMethodsMap,
|
|
1099
|
-
purl,
|
|
1100
|
-
edge?.CalledMethodName || edge?.TargetName,
|
|
1101
|
-
);
|
|
1277
|
+
addDosaiSetValue(purlMethodsMap, purl, method);
|
|
1102
1278
|
}
|
|
1103
1279
|
for (const nodeId of reachability.NodeIds || []) {
|
|
1104
1280
|
const node = nodesById.get(nodeId);
|
|
1281
|
+
const location = dosaiSourceLocationFromNode(node);
|
|
1282
|
+
const purl = resolveAt(location);
|
|
1283
|
+
const module = node?.ClassName || node?.Module;
|
|
1284
|
+
const method = node?.Name || node?.Identity?.MethodName;
|
|
1285
|
+
if (!purl) {
|
|
1286
|
+
unplaced.push([purlModulesMap, module], [purlMethodsMap, method]);
|
|
1287
|
+
continue;
|
|
1288
|
+
}
|
|
1289
|
+
factPurls.add(purl);
|
|
1105
1290
|
if (!hasExplicitSourceLocations) {
|
|
1106
|
-
addDosaiSetValue(
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1291
|
+
addDosaiSetValue(purlLocationMap, purl, location);
|
|
1292
|
+
}
|
|
1293
|
+
addDosaiSetValue(purlModulesMap, purl, module);
|
|
1294
|
+
addDosaiSetValue(purlMethodsMap, purl, method);
|
|
1295
|
+
}
|
|
1296
|
+
if (factPurls.size === 1) {
|
|
1297
|
+
const [purl] = factPurls;
|
|
1298
|
+
for (const [map, value] of unplaced) {
|
|
1299
|
+
addDosaiSetValue(map, purl, value);
|
|
1111
1300
|
}
|
|
1112
|
-
addDosaiSetValue(purlModulesMap, purl, node?.ClassName || node?.Module);
|
|
1113
|
-
addDosaiSetValue(
|
|
1114
|
-
purlMethodsMap,
|
|
1115
|
-
purl,
|
|
1116
|
-
node?.Name || node?.Identity?.MethodName,
|
|
1117
|
-
);
|
|
1118
1301
|
}
|
|
1119
1302
|
}
|
|
1120
1303
|
return { purlLocationMap, purlModulesMap, purlMethodsMap };
|
|
@@ -1128,17 +1311,38 @@ export function collectDosaiPurlEvidence(methodsSlice, components = []) {
|
|
|
1128
1311
|
*
|
|
1129
1312
|
* @param {Object} dataFlowResult Parsed dosai data-flow slice JSON
|
|
1130
1313
|
* @param {Object[]} [components] BOM components used to resolve purl aliases
|
|
1314
|
+
* @param {Object} [options] Options
|
|
1315
|
+
* @param {string} [options.srcPath] Directory dosai analyzed; a package the BOM holds in several
|
|
1316
|
+
* versions is matched by the project of the record's file
|
|
1131
1317
|
* @returns {Object} Map of canonical purl to arrays of call-stack frame objects
|
|
1132
1318
|
*/
|
|
1133
|
-
export function collectDosaiDataFlowFrames(
|
|
1134
|
-
|
|
1319
|
+
export function collectDosaiDataFlowFrames(
|
|
1320
|
+
dataFlowResult,
|
|
1321
|
+
components = [],
|
|
1322
|
+
options = {},
|
|
1323
|
+
) {
|
|
1324
|
+
const purlAliasMap = buildPurlAliasMap(components, options);
|
|
1135
1325
|
const nodesById = new Map(
|
|
1136
1326
|
(dataFlowResult?.Nodes || []).map((node) => [node.Id, node]),
|
|
1137
1327
|
);
|
|
1138
1328
|
const dataFlowFrames = {};
|
|
1139
1329
|
const addFramesForPurl = (purl, frames) => {
|
|
1140
|
-
|
|
1141
|
-
|
|
1330
|
+
if (!frames.length) {
|
|
1331
|
+
return;
|
|
1332
|
+
}
|
|
1333
|
+
// The flow runs in the project of its first frame that settles one.
|
|
1334
|
+
let componentPurl;
|
|
1335
|
+
for (const frame of frames) {
|
|
1336
|
+
componentPurl = resolveComponentPurl(
|
|
1337
|
+
purl,
|
|
1338
|
+
purlAliasMap,
|
|
1339
|
+
frame.fullFilename,
|
|
1340
|
+
);
|
|
1341
|
+
if (componentPurl) {
|
|
1342
|
+
break;
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
if (!componentPurl) {
|
|
1142
1346
|
return;
|
|
1143
1347
|
}
|
|
1144
1348
|
dataFlowFrames[componentPurl] ??= [];
|