@cdxgen/cdxgen 13.4.0 → 13.6.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 -9
- package/bin/cdxgen-secure.js +15 -0
- package/bin/cdxgen.js +29 -5
- package/bin/evinse.js +8 -0
- package/bin/hbom.js +15 -7
- package/bin/repl.js +29 -0
- package/bin/validate.js +6 -0
- package/data/cbomosdb-queries.json +13 -1
- package/data/component-tags.json +8 -0
- package/data/license-aliases.json +24 -2
- package/data/queries-darwin.json +25 -1
- package/data/queries-win.json +19 -1
- package/data/queries.json +41 -0
- package/data/remediations.json +407 -1
- package/data/rules/ai-bom.yaml +1 -1
- package/data/rules/brotli-decoder.yaml +107 -0
- package/data/rules/cbom-security.yaml +235 -4
- package/data/rules/go-binaries.yaml +46 -0
- package/data/rules/golem-go.yaml +80 -0
- package/data/rules/hbom-compliance.yaml +40 -22
- package/data/rules/hbom-performance.yaml +7 -1
- package/data/rules/host-topology.yaml +1 -1
- package/data/rules/obom-runtime.yaml +31 -2
- package/data/rules/php-runtime.yaml +143 -0
- package/data/rules/vscode-extensions.yaml +75 -0
- package/data/spdx-license-list.json +121 -7
- package/data/spdx-licenses.json +69 -0
- package/data/spdx.schema.json +845 -2
- package/lib/audit/index.js +14 -0
- package/lib/cli/bomAssembly.js +120 -6
- package/lib/cli/cliOptions.js +110 -3
- package/lib/cli/index.js +182 -6
- package/lib/cli/jsBom.js +153 -64
- package/lib/cli/jvmBom.js +363 -66
- package/lib/cli/managedBom.js +853 -87
- package/lib/cli/nativeBom.js +267 -33
- package/lib/core/boundedFile.js +344 -0
- package/lib/core/brotliProbe.js +182 -0
- package/lib/core/env.js +38 -8
- package/lib/core/fs.js +40 -19
- package/lib/core/httpClient.js +172 -46
- package/lib/core/lockPaths.js +38 -0
- package/lib/core/paths.js +24 -1
- package/lib/core/processTree.js +5 -0
- package/lib/core/projectConfig.js +1 -0
- package/lib/core/projectTooling.js +97 -0
- package/lib/core/propertySanitizer.js +72 -0
- package/lib/core/toolRegistry.js +170 -0
- package/lib/ecosystems/cppEvidence.js +263 -106
- package/lib/ecosystems/cppIncludeResolver.js +16 -5
- package/lib/ecosystems/ecosystems.js +25 -10
- package/lib/ecosystems/gemHostProbe.js +301 -0
- package/lib/ecosystems/golem.js +550 -6
- package/lib/ecosystems/gradleutils.js +858 -244
- package/lib/ecosystems/parsers-bazel.js +107 -6
- package/lib/ecosystems/parsers-dotnet.js +117 -18
- package/lib/ecosystems/parsers-go.js +1090 -367
- package/lib/ecosystems/parsers-js.js +505 -22
- package/lib/ecosystems/parsers-jvm.js +259 -17
- package/lib/ecosystems/parsers-misc.js +438 -64
- package/lib/ecosystems/parsers-python.js +100 -12
- package/lib/ecosystems/pythonutils.js +128 -2
- package/lib/ecosystems/rubyutils.js +544 -34
- package/lib/ecosystems/rusi.js +10 -0
- package/lib/ecosystems/scalacliutils.js +4 -1
- package/lib/ecosystems/scalasem.js +798 -151
- package/lib/ecosystems/vendoredCode.js +237 -3
- package/lib/ecosystems/venvProbe.js +360 -0
- package/lib/evinser/evinser.js +472 -61
- package/lib/evinser/swiftsem.js +68 -5
- package/lib/helpers/agenticToolutils.js +413 -93
- package/lib/helpers/vsixutils.js +1574 -315
- package/lib/inventory/agentSkillSpec.js +17 -0
- package/lib/inventory/apkInstalledDb.js +243 -0
- package/lib/inventory/atomUtils.js +110 -0
- package/lib/inventory/auditCategories.js +3 -3
- package/lib/inventory/brotliDecoder.js +142 -0
- package/lib/inventory/buildToolRateLimit.js +1 -1
- package/lib/inventory/cacheDir.js +52 -9
- package/lib/inventory/cbomutils.js +56 -7
- package/lib/inventory/certificates.js +2371 -0
- package/lib/inventory/communityAiConfigParser.js +4 -9
- package/lib/inventory/deps.js +41 -4
- package/lib/inventory/display.js +200 -3
- package/lib/inventory/dosai.js +639 -63
- package/lib/inventory/dotnetRuntime.js +217 -0
- package/lib/inventory/dynamic.js +45 -3
- package/lib/inventory/envcontext.js +475 -18
- package/lib/inventory/evidenceUtils.js +447 -23
- package/lib/inventory/fetchBatch.js +1 -1
- package/lib/inventory/formulationParsers.js +32 -3
- package/lib/inventory/goBuildInfo.js +140 -0
- package/lib/inventory/gtfobins.js +13 -0
- package/lib/inventory/hbom.js +67 -7
- package/lib/inventory/hbomAnalysis.js +16 -0
- package/lib/inventory/hostRuntimeProbe.js +686 -0
- package/lib/inventory/hostTopology.js +21 -0
- package/lib/inventory/jvmLocalRepos.js +9 -2
- package/lib/inventory/jvmToolEnv.js +29 -0
- package/lib/inventory/licenseEnhancer.js +12 -0
- package/lib/inventory/mavenCentralMirror.js +534 -0
- package/lib/inventory/nodeBuildInfo.js +218 -0
- package/lib/inventory/nodeHostProbe.js +202 -0
- package/lib/inventory/osPackagePurl.js +244 -0
- package/lib/inventory/osPackageResolver.js +271 -87
- package/lib/inventory/osinfo.js +79 -31
- package/lib/inventory/osqueryTransform.js +114 -5
- package/lib/inventory/phpBuildInfo.js +438 -0
- package/lib/inventory/phpHostProbe.js +185 -0
- package/lib/inventory/rootfsCerts.js +426 -0
- package/lib/inventory/rubyBuildInfo.js +972 -0
- package/lib/inventory/rubyHostProbe.js +218 -0
- package/lib/inventory/scalaCoords.js +4 -3
- package/lib/inventory/scalaDedupe.js +440 -0
- package/lib/inventory/source.js +256 -5
- package/lib/inventory/spdx.js +144 -0
- package/lib/inventory/traceRunner.js +5 -1
- package/lib/inventory/tracedCrypto.js +129 -0
- package/lib/managers/binary.js +866 -186
- package/lib/managers/docker.js +245 -12
- package/lib/parsers/email.js +277 -0
- package/lib/parsers/largeJson.js +62 -13
- package/lib/parsers/vscodeExtensionId.js +183 -0
- package/lib/server/server.js +198 -2
- package/lib/stages/postgen/annotator.js +17 -1
- package/lib/stages/postgen/introspection/reflect.js +4 -1
- package/lib/stages/postgen/postgen.js +5 -5
- package/lib/stages/postgen/ruleEngine.js +71 -6
- package/lib/stages/postgen/specVersionCompat.js +30 -0
- package/lib/stages/pregen/envAudit.js +1 -0
- package/lib/stages/pregen/pregen.js +231 -62
- package/lib/validator/bomValidator.js +32 -6
- package/lib/validator/generated/runtime.mjs +13 -0
- package/lib/validator/generated/validate-1.6.mjs +27 -8
- package/lib/validator/generated/validate-1.7.mjs +32 -13
- package/lib/validator/generated/validate-2.0.mjs +31 -12
- package/lib/validator/index.js +1 -1
- package/package.json +19 -18
- package/types/bin/cdxgen-secure.d.ts +3 -0
- package/types/bin/cdxgen-secure.d.ts.map +1 -0
- package/types/bin/repl.d.ts.map +1 -1
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/cli/bomAssembly.d.ts +22 -3
- package/types/lib/cli/bomAssembly.d.ts.map +1 -1
- package/types/lib/cli/cliOptions.d.ts +43 -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 +1 -17
- package/types/lib/cli/jsBom.d.ts.map +1 -1
- package/types/lib/cli/jvmBom.d.ts +20 -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 +70 -0
- package/types/lib/core/boundedFile.d.ts.map +1 -0
- package/types/lib/core/brotliProbe.d.ts +37 -0
- package/types/lib/core/brotliProbe.d.ts.map +1 -0
- package/types/lib/core/env.d.ts +22 -2
- 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 +24 -0
- package/types/lib/core/httpClient.d.ts.map +1 -1
- package/types/lib/core/lockPaths.d.ts +19 -0
- package/types/lib/core/lockPaths.d.ts.map +1 -0
- package/types/lib/core/processTree.d.ts +2 -0
- package/types/lib/core/processTree.d.ts.map +1 -1
- package/types/lib/core/projectConfig.d.ts.map +1 -1
- package/types/lib/core/projectTooling.d.ts +41 -0
- package/types/lib/core/projectTooling.d.ts.map +1 -0
- package/types/lib/core/propertySanitizer.d.ts +26 -0
- package/types/lib/core/propertySanitizer.d.ts.map +1 -1
- package/types/lib/core/toolRegistry.d.ts +36 -0
- package/types/lib/core/toolRegistry.d.ts.map +1 -0
- package/types/lib/ecosystems/cppEvidence.d.ts +40 -13
- package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/cppIncludeResolver.d.ts +2 -1
- package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -1
- package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
- package/types/lib/ecosystems/gemHostProbe.d.ts +28 -0
- package/types/lib/ecosystems/gemHostProbe.d.ts.map +1 -0
- package/types/lib/ecosystems/golem.d.ts +81 -1
- package/types/lib/ecosystems/golem.d.ts.map +1 -1
- package/types/lib/ecosystems/gradleutils.d.ts +57 -21
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-bazel.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-dotnet.d.ts +40 -4
- package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-go.d.ts +85 -13
- package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-js.d.ts +17 -20
- package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-jvm.d.ts +28 -2
- package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-misc.d.ts +37 -58
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-python.d.ts +14 -3
- package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
- package/types/lib/ecosystems/pythonutils.d.ts +19 -1
- package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rubyutils.d.ts +144 -12
- package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rusi.d.ts.map +1 -1
- package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -1
- package/types/lib/ecosystems/scalasem.d.ts.map +1 -1
- package/types/lib/ecosystems/vendoredCode.d.ts +26 -0
- package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -1
- package/types/lib/ecosystems/venvProbe.d.ts +40 -0
- package/types/lib/ecosystems/venvProbe.d.ts.map +1 -0
- package/types/lib/evinser/evinser.d.ts +88 -8
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/evinser/swiftsem.d.ts +10 -0
- package/types/lib/evinser/swiftsem.d.ts.map +1 -1
- package/types/lib/inventory/agentSkillSpec.d.ts +13 -0
- package/types/lib/inventory/agentSkillSpec.d.ts.map +1 -0
- package/types/lib/inventory/apkInstalledDb.d.ts +42 -0
- package/types/lib/inventory/apkInstalledDb.d.ts.map +1 -0
- package/types/lib/inventory/atomUtils.d.ts +33 -0
- package/types/lib/inventory/atomUtils.d.ts.map +1 -1
- package/types/lib/inventory/brotliDecoder.d.ts +58 -0
- package/types/lib/inventory/brotliDecoder.d.ts.map +1 -0
- package/types/lib/inventory/cacheDir.d.ts +12 -4
- package/types/lib/inventory/cacheDir.d.ts.map +1 -1
- package/types/lib/inventory/cbomutils.d.ts +17 -0
- package/types/lib/inventory/cbomutils.d.ts.map +1 -1
- package/types/lib/inventory/certificates.d.ts +205 -0
- package/types/lib/inventory/certificates.d.ts.map +1 -0
- package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/deps.d.ts +2 -1
- package/types/lib/inventory/deps.d.ts.map +1 -1
- package/types/lib/inventory/display.d.ts +18 -0
- package/types/lib/inventory/display.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +162 -7
- package/types/lib/inventory/dosai.d.ts.map +1 -1
- package/types/lib/inventory/dotnetRuntime.d.ts +44 -0
- package/types/lib/inventory/dotnetRuntime.d.ts.map +1 -0
- package/types/lib/inventory/dynamic.d.ts +6 -1
- package/types/lib/inventory/dynamic.d.ts.map +1 -1
- package/types/lib/inventory/envcontext.d.ts +92 -8
- package/types/lib/inventory/envcontext.d.ts.map +1 -1
- package/types/lib/inventory/evidenceUtils.d.ts +29 -10
- package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
- package/types/lib/inventory/formulationParsers.d.ts.map +1 -1
- package/types/lib/inventory/goBuildInfo.d.ts +10 -0
- package/types/lib/inventory/goBuildInfo.d.ts.map +1 -0
- package/types/lib/inventory/gtfobins.d.ts.map +1 -1
- package/types/lib/inventory/hbom.d.ts.map +1 -1
- package/types/lib/inventory/hbomAnalysis.d.ts +10 -0
- package/types/lib/inventory/hbomAnalysis.d.ts.map +1 -1
- package/types/lib/inventory/hostRuntimeProbe.d.ts +44 -0
- package/types/lib/inventory/hostRuntimeProbe.d.ts.map +1 -0
- package/types/lib/inventory/hostTopology.d.ts.map +1 -1
- package/types/lib/inventory/jvmToolEnv.d.ts +12 -0
- package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -1
- package/types/lib/inventory/licenseEnhancer.d.ts.map +1 -1
- package/types/lib/inventory/mavenCentralMirror.d.ts +170 -0
- package/types/lib/inventory/mavenCentralMirror.d.ts.map +1 -0
- package/types/lib/inventory/nodeBuildInfo.d.ts +95 -0
- package/types/lib/inventory/nodeBuildInfo.d.ts.map +1 -0
- package/types/lib/inventory/nodeHostProbe.d.ts +15 -0
- package/types/lib/inventory/nodeHostProbe.d.ts.map +1 -0
- package/types/lib/inventory/osPackagePurl.d.ts +99 -0
- package/types/lib/inventory/osPackagePurl.d.ts.map +1 -0
- package/types/lib/inventory/osPackageResolver.d.ts +44 -2
- package/types/lib/inventory/osPackageResolver.d.ts.map +1 -1
- package/types/lib/inventory/osinfo.d.ts +31 -6
- package/types/lib/inventory/osinfo.d.ts.map +1 -1
- package/types/lib/inventory/osqueryTransform.d.ts +51 -0
- package/types/lib/inventory/osqueryTransform.d.ts.map +1 -1
- package/types/lib/inventory/phpBuildInfo.d.ts +142 -0
- package/types/lib/inventory/phpBuildInfo.d.ts.map +1 -0
- package/types/lib/inventory/phpHostProbe.d.ts +35 -0
- package/types/lib/inventory/phpHostProbe.d.ts.map +1 -0
- package/types/lib/inventory/rootfsCerts.d.ts +27 -0
- package/types/lib/inventory/rootfsCerts.d.ts.map +1 -0
- package/types/lib/inventory/rubyBuildInfo.d.ts +271 -0
- package/types/lib/inventory/rubyBuildInfo.d.ts.map +1 -0
- package/types/lib/inventory/rubyHostProbe.d.ts +47 -0
- package/types/lib/inventory/rubyHostProbe.d.ts.map +1 -0
- package/types/lib/inventory/source.d.ts +38 -0
- package/types/lib/inventory/source.d.ts.map +1 -1
- package/types/lib/inventory/spdx.d.ts +15 -0
- package/types/lib/inventory/spdx.d.ts.map +1 -1
- package/types/lib/inventory/traceRunner.d.ts +4 -1
- package/types/lib/inventory/traceRunner.d.ts.map +1 -1
- package/types/lib/inventory/tracedCrypto.d.ts +22 -0
- package/types/lib/inventory/tracedCrypto.d.ts.map +1 -1
- package/types/lib/managers/binary.d.ts +10 -0
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts +24 -0
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/parsers/email.d.ts +19 -0
- package/types/lib/parsers/email.d.ts.map +1 -0
- package/types/lib/parsers/largeJson.d.ts +17 -3
- package/types/lib/parsers/largeJson.d.ts.map +1 -1
- package/types/lib/server/server.d.ts +8 -0
- package/types/lib/server/server.d.ts.map +1 -1
- package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
- package/types/lib/stages/postgen/introspection/reflect.d.ts.map +1 -1
- package/types/lib/stages/postgen/ruleEngine.d.ts +7 -4
- package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
- package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
- package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
- package/types/lib/stages/pregen/pregen.d.ts +26 -9
- package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
package/lib/evinser/evinser.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
+
import { builtinModules } from "node:module";
|
|
2
3
|
import path, { basename, join, resolve } from "node:path";
|
|
3
4
|
import process from "node:process";
|
|
4
5
|
|
|
5
6
|
import { Purl } from "@cdxgen/cdx-purl";
|
|
6
7
|
|
|
7
8
|
import { DEBUG_MODE, readEnvironmentVariable } from "../core/activity.js";
|
|
9
|
+
import { recordDegradation } from "../core/buildLedger.js";
|
|
8
10
|
import { PROJECT_TYPE_ALIASES } from "../core/env.js";
|
|
9
11
|
import {
|
|
10
12
|
getAllFiles,
|
|
@@ -15,6 +17,7 @@ import {
|
|
|
15
17
|
safeRmSync,
|
|
16
18
|
safeWriteSync,
|
|
17
19
|
} from "../core/fs.js";
|
|
20
|
+
import { isProjectToolingAllowed } from "../core/projectTooling.js";
|
|
18
21
|
import { hasCppSources } from "../ecosystems/cppEvidence.js";
|
|
19
22
|
import {
|
|
20
23
|
createIncludeAttributor,
|
|
@@ -23,11 +26,11 @@ import {
|
|
|
23
26
|
import {
|
|
24
27
|
analyzeGolemProject,
|
|
25
28
|
collectGolemEvidence,
|
|
29
|
+
collectGolemServices,
|
|
26
30
|
isGolemGoLanguage,
|
|
27
31
|
} from "../ecosystems/golem.js";
|
|
28
32
|
import {
|
|
29
33
|
collectGradleDependencies,
|
|
30
|
-
getGradleCommand,
|
|
31
34
|
getMavenCommand,
|
|
32
35
|
} from "../ecosystems/gradleutils.js";
|
|
33
36
|
import {
|
|
@@ -52,6 +55,8 @@ import {
|
|
|
52
55
|
isScalasemLanguage,
|
|
53
56
|
} from "../ecosystems/scalasem.js";
|
|
54
57
|
import {
|
|
58
|
+
atomBomFileArgs,
|
|
59
|
+
atomBuildToolArgs,
|
|
55
60
|
atomCompileCommandsArgs,
|
|
56
61
|
buildAtomCommandEnv,
|
|
57
62
|
executeAtom,
|
|
@@ -77,7 +82,10 @@ import {
|
|
|
77
82
|
collectDosaiServicesFromMethods,
|
|
78
83
|
createDosaiDataFlowSlice,
|
|
79
84
|
createDosaiMethodsSlice,
|
|
80
|
-
|
|
85
|
+
dosaiFailureReported,
|
|
86
|
+
dosaiOmittedSections,
|
|
87
|
+
dosaiReportFileOmittedSections,
|
|
88
|
+
dosaiSectionsCdxgenReads,
|
|
81
89
|
isDosaiDotnetLanguage,
|
|
82
90
|
normalizeDosaiServiceMap,
|
|
83
91
|
persistDosaiSemanticsReport,
|
|
@@ -85,6 +93,7 @@ import {
|
|
|
85
93
|
readDosaiMethodsReport,
|
|
86
94
|
} from "../inventory/dosai.js";
|
|
87
95
|
import { parseOccurrenceEvidenceLocation } from "../inventory/evidenceUtils.js";
|
|
96
|
+
import { splitPurlLoose } from "../inventory/purl.js";
|
|
88
97
|
import { executeOsQuery } from "../managers/binary.js";
|
|
89
98
|
import { MAX_JSON_TEXT_BYTES, readJsonFile } from "../parsers/largeJson.js";
|
|
90
99
|
import { postProcess } from "../stages/postgen/postgen.js";
|
|
@@ -92,7 +101,8 @@ import { createOrLoad } from "./db.js";
|
|
|
92
101
|
import { findPurlLocations } from "./scalasem.js";
|
|
93
102
|
import { createSemanticsSlices } from "./swiftsem.js";
|
|
94
103
|
|
|
95
|
-
|
|
104
|
+
// keyed by type names, which can be "constructor", "toString", ...: no prototype
|
|
105
|
+
const typePurlsCache = Object.create(null);
|
|
96
106
|
|
|
97
107
|
function appendUniqueProperty(properties, name, value) {
|
|
98
108
|
if (value === undefined || value === null || value === "") {
|
|
@@ -255,6 +265,7 @@ export function buildEvinseOptions(options, args, input) {
|
|
|
255
265
|
force: false,
|
|
256
266
|
withReachables: options.deep,
|
|
257
267
|
usagesSlicesFile: options.usagesSlicesFile,
|
|
268
|
+
callsitesSlicesFile: options.callsitesSlicesFile,
|
|
258
269
|
dataFlowSlicesFile: options.dataFlowSlicesFile,
|
|
259
270
|
reachablesSlicesFile: options.reachablesSlicesFile,
|
|
260
271
|
semanticsSlicesFile: options.semanticsSlicesFile,
|
|
@@ -401,9 +412,25 @@ export async function catalogMavenDeps(
|
|
|
401
412
|
options = {},
|
|
402
413
|
) {
|
|
403
414
|
let jarNSMapping = readNamespaceMap(dirPath, options);
|
|
404
|
-
if (!jarNSMapping) {
|
|
415
|
+
if (!jarNSMapping && !isProjectToolingAllowed(options)) {
|
|
416
|
+
// `mvn dependency:copy-dependencies` evaluates the project's build and
|
|
417
|
+
// its `.mvn` configuration, so only the local repository is read.
|
|
418
|
+
console.log(
|
|
419
|
+
options.withDeepJarCollector
|
|
420
|
+
? "Skipping 'mvn dependency:copy-dependencies' since dependency installation is disabled. Only the jars already in the local Maven repository are used."
|
|
421
|
+
: "Skipping 'mvn dependency:copy-dependencies' since dependency installation is disabled. Pass --with-deep-jar-collector to use the jars already in the local Maven repository.",
|
|
422
|
+
);
|
|
423
|
+
if (options.withDeepJarCollector) {
|
|
424
|
+
jarNSMapping = await collectMvnDependencies(
|
|
425
|
+
undefined,
|
|
426
|
+
undefined,
|
|
427
|
+
true,
|
|
428
|
+
true,
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
} else if (!jarNSMapping) {
|
|
405
432
|
console.log("About to collect jar dependencies for the path", dirPath);
|
|
406
|
-
const mavenCmd = getMavenCommand(dirPath, dirPath);
|
|
433
|
+
const mavenCmd = getMavenCommand(dirPath, dirPath, options);
|
|
407
434
|
// collect all jars including from the cache if data-flow mode is enabled
|
|
408
435
|
jarNSMapping = await collectMvnDependencies(
|
|
409
436
|
mavenCmd,
|
|
@@ -457,10 +484,10 @@ export async function catalogGradleDeps(
|
|
|
457
484
|
console.log(
|
|
458
485
|
"About to collect jar dependencies from the gradle cache. This would take a while ...",
|
|
459
486
|
);
|
|
460
|
-
|
|
461
|
-
//
|
|
487
|
+
// Only the Gradle cache is read, so no Gradle command is resolved: that
|
|
488
|
+
// would probe the project's own gradlew.
|
|
462
489
|
jarNSMapping = await collectGradleDependencies(
|
|
463
|
-
|
|
490
|
+
undefined,
|
|
464
491
|
dirPath,
|
|
465
492
|
false,
|
|
466
493
|
true,
|
|
@@ -645,9 +672,9 @@ export async function createSlice(
|
|
|
645
672
|
resolve(slicesFile),
|
|
646
673
|
options,
|
|
647
674
|
);
|
|
648
|
-
// A stopped run
|
|
649
|
-
// project is supported (issue 4438).
|
|
650
|
-
if (!sliceResult && !
|
|
675
|
+
// A stopped run, or one the .NET host refused to start, has already said
|
|
676
|
+
// why; it says nothing about whether the project is supported (issue 4438).
|
|
677
|
+
if (!sliceResult && !dosaiFailureReported(resolve(slicesFile))) {
|
|
651
678
|
console.warn(
|
|
652
679
|
`Unable to generate ${sliceType} slice using dosai. Check if this is a supported .NET project.`,
|
|
653
680
|
);
|
|
@@ -658,6 +685,13 @@ export async function createSlice(
|
|
|
658
685
|
slicesFile,
|
|
659
686
|
};
|
|
660
687
|
}
|
|
688
|
+
const buildToolArgs = atomBuildToolArgs(language, options);
|
|
689
|
+
if (!buildToolArgs) {
|
|
690
|
+
console.log(
|
|
691
|
+
`Skipping the ${sliceType} slice for ${resolve(filePath)} since dependency installation is disabled and the installed atom cannot be stopped from running Maven. Upgrade atom to slice this project without its build.`,
|
|
692
|
+
);
|
|
693
|
+
return { tempDir: sliceOutputDir, tempDirOwned };
|
|
694
|
+
}
|
|
661
695
|
console.log(
|
|
662
696
|
`Creating ${sliceType} slice for ${resolve(filePath)}. Please wait ...`,
|
|
663
697
|
);
|
|
@@ -705,7 +739,9 @@ export async function createSlice(
|
|
|
705
739
|
args.push("--slice-depth");
|
|
706
740
|
args.push(readEnvironmentVariable("ATOM_SLICE_DEPTH"));
|
|
707
741
|
}
|
|
742
|
+
args.push(...atomBomFileArgs(sliceType, options));
|
|
708
743
|
args.push(...atomCompileCommandsArgs(filePath, language, options));
|
|
744
|
+
args.push(...buildToolArgs);
|
|
709
745
|
args.push(resolve(filePath));
|
|
710
746
|
const atomExcludeEnv = buildAtomCommandEnv(options, language);
|
|
711
747
|
// Execute atom
|
|
@@ -1143,6 +1179,49 @@ export function mergeServiceDefinitions(target, source) {
|
|
|
1143
1179
|
return target;
|
|
1144
1180
|
}
|
|
1145
1181
|
|
|
1182
|
+
/**
|
|
1183
|
+
* Collect the services of a Go project for the golem evidence path (#4498).
|
|
1184
|
+
*
|
|
1185
|
+
* Two sources are kept and merged by service name, the way the Scala path
|
|
1186
|
+
* merges a user-provided spec into its report's services. They roll services
|
|
1187
|
+
* up differently — golem per server, OpenAPI per path and method — so a
|
|
1188
|
+
* route both name appears under both entries:
|
|
1189
|
+
*
|
|
1190
|
+
* - the golem report itself, whose `services[]` section rolls the registered
|
|
1191
|
+
* routes up per server (a gin engine, a net/http listener) with methods and
|
|
1192
|
+
* frameworks, and the outbound hosts it calls into `kind: "external"`
|
|
1193
|
+
* services flagged as trust-boundary crossings;
|
|
1194
|
+
* - an OpenAPI document, either the one `--openapi-spec-file` names or the
|
|
1195
|
+
* `openapi.json` an earlier `atom-tools convert -t go` left in the repo,
|
|
1196
|
+
* which is reused when present so `golem`/`atom-tools` and `cdxgen` can be
|
|
1197
|
+
* run as separate steps.
|
|
1198
|
+
*
|
|
1199
|
+
* @param {object|null|undefined} golemReport Parsed golem analysis report,
|
|
1200
|
+
* when one was produced; a missing or `null` report adds no services.
|
|
1201
|
+
* @param {string} dirPath Project directory the report was produced from.
|
|
1202
|
+
* @param {Object} options Command line options.
|
|
1203
|
+
* @param {Object} [servicesMap={}] Service map to populate.
|
|
1204
|
+
* @returns {Object} The service map
|
|
1205
|
+
*/
|
|
1206
|
+
export function collectGoServices(
|
|
1207
|
+
golemReport,
|
|
1208
|
+
dirPath,
|
|
1209
|
+
options = {},
|
|
1210
|
+
servicesMap = {},
|
|
1211
|
+
) {
|
|
1212
|
+
servicesMap = collectGolemServices(golemReport, servicesMap);
|
|
1213
|
+
const openapiSpecFile = resolve(
|
|
1214
|
+
dirPath,
|
|
1215
|
+
options.openapiSpecFile || "openapi.json",
|
|
1216
|
+
);
|
|
1217
|
+
if (safeExistsSync(openapiSpecFile)) {
|
|
1218
|
+
const openapiServices = {};
|
|
1219
|
+
detectServicesFromOpenAPI("go", openapiSpecFile, openapiServices);
|
|
1220
|
+
mergeServiceDefinitions(servicesMap, openapiServices);
|
|
1221
|
+
}
|
|
1222
|
+
return servicesMap;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1146
1225
|
/**
|
|
1147
1226
|
* Function to analyze the project
|
|
1148
1227
|
*
|
|
@@ -1183,6 +1262,7 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1183
1262
|
|
|
1184
1263
|
if (isGolemGoLanguage(language)) {
|
|
1185
1264
|
const golemReport = analyzeGolemProject(dirPath, options);
|
|
1265
|
+
servicesMap = collectGoServices(golemReport, dirPath, options, servicesMap);
|
|
1186
1266
|
if (golemReport && Object.keys(golemReport).length) {
|
|
1187
1267
|
const golemEvidence = collectGolemEvidence(golemReport, [
|
|
1188
1268
|
bomJson.metadata?.component,
|
|
@@ -1329,24 +1409,58 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1329
1409
|
options.withDataFlow = true;
|
|
1330
1410
|
options.includeCrypto = true;
|
|
1331
1411
|
}
|
|
1412
|
+
let needsUsagesRun = true;
|
|
1332
1413
|
if (
|
|
1333
1414
|
options.usagesSlicesFile &&
|
|
1334
1415
|
usableSlicesFile(options.usagesSlicesFile)
|
|
1335
1416
|
) {
|
|
1336
1417
|
usageSlice = readDosaiMethodsReport(options.usagesSlicesFile);
|
|
1337
1418
|
usagesSlicesFile = options.usagesSlicesFile;
|
|
1419
|
+
needsUsagesRun = false;
|
|
1420
|
+
// A user-supplied usages slice is used as given. When it was written
|
|
1421
|
+
// with --sections and left out a section cdxgen reads, the evidence
|
|
1422
|
+
// lacks that input; persistDosaiSemanticsReport reports separately
|
|
1423
|
+
// that the persisted report is partial.
|
|
1424
|
+
const missingInputs = dosaiSectionsCdxgenReads(
|
|
1425
|
+
dosaiOmittedSections(usageSlice),
|
|
1426
|
+
);
|
|
1427
|
+
if (missingInputs.length) {
|
|
1428
|
+
recordDegradation("dosai.usages.trimmed-report", {
|
|
1429
|
+
ecosystem: "csharp",
|
|
1430
|
+
tool: "dosai",
|
|
1431
|
+
impact: "components",
|
|
1432
|
+
command: `usages ${usagesSlicesFile}`,
|
|
1433
|
+
detail: `The supplied dosai methods report was written without sections cdxgen reads: ${missingInputs.join(", ")}. The evidence from those sections is missing. Run dosai methods without --sections, or with every section cdxgen reads.`,
|
|
1434
|
+
});
|
|
1435
|
+
}
|
|
1338
1436
|
} else if (usableSlicesFile(options.depsSlicesFile)) {
|
|
1339
1437
|
// createCsharpBom already ran dosai methods on this tree for --deep runs
|
|
1340
1438
|
// and wrote the deps slice. For dosai, that output IS the usages slice,
|
|
1341
|
-
// so reuse it instead of invoking dosai a second time
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1439
|
+
// so reuse it instead of invoking dosai a second time - unless it was
|
|
1440
|
+
// written with --sections (a --deep run that performs no evidence
|
|
1441
|
+
// analysis trims it): evinse persists this file as the full native
|
|
1442
|
+
// report, so it must not build on a trimmed one and dosai runs again.
|
|
1443
|
+
const omittedSections = dosaiReportFileOmittedSections(
|
|
1444
|
+
options.depsSlicesFile,
|
|
1445
|
+
);
|
|
1446
|
+
if (omittedSections.length) {
|
|
1447
|
+
if (DEBUG_MODE) {
|
|
1448
|
+
console.log(
|
|
1449
|
+
`The dosai deps slices file "${options.depsSlicesFile}" omits sections (${omittedSections.join(", ")}); running dosai for the full usages report.`,
|
|
1450
|
+
);
|
|
1451
|
+
}
|
|
1452
|
+
} else {
|
|
1453
|
+
if (DEBUG_MODE) {
|
|
1454
|
+
console.log(
|
|
1455
|
+
`Reusing the dosai deps slices file "${options.depsSlicesFile}" for the usages analysis.`,
|
|
1456
|
+
);
|
|
1457
|
+
}
|
|
1458
|
+
usageSlice = readDosaiMethodsReport(options.depsSlicesFile);
|
|
1459
|
+
usagesSlicesFile = options.depsSlicesFile;
|
|
1460
|
+
needsUsagesRun = false;
|
|
1346
1461
|
}
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
} else {
|
|
1462
|
+
}
|
|
1463
|
+
if (needsUsagesRun) {
|
|
1350
1464
|
retMap = await createSlice(language, dirPath, "usages", options);
|
|
1351
1465
|
if (retMap?.slicesFile && safeExistsSync(retMap.slicesFile)) {
|
|
1352
1466
|
usageSlice = readDosaiMethodsReport(retMap.slicesFile);
|
|
@@ -1465,6 +1579,7 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1465
1579
|
cryptoGeneratePurls = retMap.cryptoGeneratePurls;
|
|
1466
1580
|
}
|
|
1467
1581
|
// Reuse existing usages slices
|
|
1582
|
+
let usagesAtomFile;
|
|
1468
1583
|
if (options.usagesSlicesFile && usableSlicesFile(options.usagesSlicesFile)) {
|
|
1469
1584
|
usageSlice = filterAtomSliceData(
|
|
1470
1585
|
readSlicesFile(options.usagesSlicesFile, "usages"),
|
|
@@ -1474,6 +1589,7 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1474
1589
|
} else {
|
|
1475
1590
|
// Generate our own slices
|
|
1476
1591
|
retMap = await createSlice(language, dirPath, "usages", options);
|
|
1592
|
+
usagesAtomFile = retMap?.atomFile || "";
|
|
1477
1593
|
if (retMap?.slicesFile && safeExistsSync(retMap.slicesFile)) {
|
|
1478
1594
|
usageSlice = filterAtomSliceData(
|
|
1479
1595
|
readSlicesFile(retMap.slicesFile, "usages"),
|
|
@@ -1553,6 +1669,35 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1553
1669
|
servicesMap = retMap.servicesMap;
|
|
1554
1670
|
userDefinedTypesMap = retMap.userDefinedTypesMap;
|
|
1555
1671
|
}
|
|
1672
|
+
// Parse the callsites slice (JS/TS): one record per external use site, each
|
|
1673
|
+
// record's own (file, line) is an occurrence of the module's package. The
|
|
1674
|
+
// usages slice above still provides services and endpoints.
|
|
1675
|
+
if (JS_LANGUAGES.includes(language)) {
|
|
1676
|
+
let callsitesSlice;
|
|
1677
|
+
if (
|
|
1678
|
+
options.callsitesSlicesFile &&
|
|
1679
|
+
usableSlicesFile(options.callsitesSlicesFile)
|
|
1680
|
+
) {
|
|
1681
|
+
callsitesSlice = readSlicesFile(options.callsitesSlicesFile, "callsites");
|
|
1682
|
+
} else if (
|
|
1683
|
+
usagesAtomFile !== undefined &&
|
|
1684
|
+
!safeExistsSync(usagesAtomFile)
|
|
1685
|
+
) {
|
|
1686
|
+
// The usages run above built no atom (stopped at the timeout, or atom
|
|
1687
|
+
// failed); a callsites run would rebuild the same atom and fail the same way.
|
|
1688
|
+
console.log(
|
|
1689
|
+
"Skipping the callsites slice since atom could not build the atom for the usages slice.",
|
|
1690
|
+
);
|
|
1691
|
+
} else {
|
|
1692
|
+
retMap = await createSlice(language, dirPath, "callsites", options);
|
|
1693
|
+
if (retMap?.slicesFile && safeExistsSync(retMap.slicesFile)) {
|
|
1694
|
+
callsitesSlice = readSlicesFile(retMap.slicesFile, "callsites");
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
if (callsitesSlice?.callsites?.length) {
|
|
1698
|
+
attributeJsCallsites(callsitesSlice, purlImportsMap, purlLocationMap);
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1556
1701
|
// A component whose calls the slices do not show (code inside the project,
|
|
1557
1702
|
// such as a vendored library) is still used where its header is included
|
|
1558
1703
|
for (const [purl, includeLocations] of cIncludeLocations) {
|
|
@@ -1860,8 +2005,15 @@ export async function parseSliceUsages(
|
|
|
1860
2005
|
) {
|
|
1861
2006
|
const fileName = slice.fileName;
|
|
1862
2007
|
const typesToLookup = new Set();
|
|
1863
|
-
|
|
1864
|
-
const
|
|
2008
|
+
// keyed by type names, which can be "constructor", "toString", ...: no prototype
|
|
2009
|
+
const lKeyOverrides = Object.create(null);
|
|
2010
|
+
// A user defined type has no usages; its fields' types are evidence at the fields' lines.
|
|
2011
|
+
const usages =
|
|
2012
|
+
slice.usages ||
|
|
2013
|
+
(slice.fields || [])
|
|
2014
|
+
.filter((f) => f?.typeFullName)
|
|
2015
|
+
.map((f) => ({ targetObj: f }));
|
|
2016
|
+
const isJs = JS_LANGUAGES.includes(language);
|
|
1865
2017
|
// What should be the line number to use. slice.lineNumber would be quite coarse and could lead to reports such as
|
|
1866
2018
|
// #1670. Line numbers under targetObj and definedBy is a safe bet for dynamic languages, but occassionally leads to
|
|
1867
2019
|
// confusion when inter-procedural tracking works better than expected.
|
|
@@ -1884,13 +2036,27 @@ export async function parseSliceUsages(
|
|
|
1884
2036
|
sliceLineNumber ||
|
|
1885
2037
|
ausage?.targetObj?.lineNumber ||
|
|
1886
2038
|
ausage?.definedBy?.lineNumber;
|
|
2039
|
+
// JavaScript entries name the imported module they resolve to; attribute those by package
|
|
2040
|
+
// and keep the symbol-based matching below for entries without one.
|
|
2041
|
+
if (isJs) {
|
|
2042
|
+
attributeJsModules(ausage, fileName, purlImportsMap, purlLocationMap);
|
|
2043
|
+
}
|
|
2044
|
+
const viaSymbols = (o) => !(isJs && o?.module);
|
|
1887
2045
|
// First capture the types in the targetObj and definedBy
|
|
1888
2046
|
for (const atype of [
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
2047
|
+
...(viaSymbols(ausage?.targetObj)
|
|
2048
|
+
? [
|
|
2049
|
+
[ausage?.targetObj?.isExternal, ausage?.targetObj?.typeFullName],
|
|
2050
|
+
[ausage?.targetObj?.isExternal, ausage?.targetObj?.resolvedMethod],
|
|
2051
|
+
]
|
|
2052
|
+
: []),
|
|
2053
|
+
...(viaSymbols(ausage?.definedBy)
|
|
2054
|
+
? [
|
|
2055
|
+
[ausage?.definedBy?.name?.includes("::"), ausage?.definedBy?.name],
|
|
2056
|
+
[ausage?.definedBy?.isExternal, ausage?.definedBy?.typeFullName],
|
|
2057
|
+
[ausage?.definedBy?.isExternal, ausage?.definedBy?.resolvedMethod],
|
|
2058
|
+
]
|
|
2059
|
+
: []),
|
|
1894
2060
|
...(ausage?.fields || []).map((f) => [f?.isExternal, f?.typeFullName]),
|
|
1895
2061
|
]) {
|
|
1896
2062
|
if (
|
|
@@ -1931,7 +2097,8 @@ export async function parseSliceUsages(
|
|
|
1931
2097
|
for (const acall of []
|
|
1932
2098
|
.concat(ausage?.invokedCalls || [])
|
|
1933
2099
|
.concat(ausage?.argToCalls || [])
|
|
1934
|
-
.concat(ausage?.procedures || [])
|
|
2100
|
+
.concat(ausage?.procedures || [])
|
|
2101
|
+
.filter(viaSymbols)) {
|
|
1935
2102
|
if (
|
|
1936
2103
|
acall.resolvedMethod?.startsWith("@") ||
|
|
1937
2104
|
acall?.callName?.includes("::")
|
|
@@ -2031,9 +2198,7 @@ export async function parseSliceUsages(
|
|
|
2031
2198
|
if (!purlLocationMap[apurl]) {
|
|
2032
2199
|
purlLocationMap[apurl] = new Set();
|
|
2033
2200
|
}
|
|
2034
|
-
|
|
2035
|
-
purlLocationMap[apurl].add(...lKeyOverrides[atype]);
|
|
2036
|
-
}
|
|
2201
|
+
addAllLocations(purlLocationMap[apurl], lKeyOverrides[atype]);
|
|
2037
2202
|
}
|
|
2038
2203
|
}
|
|
2039
2204
|
} else {
|
|
@@ -2041,9 +2206,7 @@ export async function parseSliceUsages(
|
|
|
2041
2206
|
if (!purlLocationMap[apurl]) {
|
|
2042
2207
|
purlLocationMap[apurl] = new Set();
|
|
2043
2208
|
}
|
|
2044
|
-
|
|
2045
|
-
purlLocationMap[apurl].add(...lKeyOverrides[atype]);
|
|
2046
|
-
}
|
|
2209
|
+
addAllLocations(purlLocationMap[apurl], lKeyOverrides[atype]);
|
|
2047
2210
|
}
|
|
2048
2211
|
}
|
|
2049
2212
|
}
|
|
@@ -2065,9 +2228,7 @@ export async function parseSliceUsages(
|
|
|
2065
2228
|
if (!purlLocationMap[ns.purl]) {
|
|
2066
2229
|
purlLocationMap[ns.purl] = new Set();
|
|
2067
2230
|
}
|
|
2068
|
-
|
|
2069
|
-
purlLocationMap[ns.purl].add(...lKeyOverrides[atype]);
|
|
2070
|
-
}
|
|
2231
|
+
addAllLocations(purlLocationMap[ns.purl], lKeyOverrides[atype]);
|
|
2071
2232
|
}
|
|
2072
2233
|
typePurlsCache[atype] = nsHits;
|
|
2073
2234
|
} else {
|
|
@@ -2475,6 +2636,17 @@ export function detectServicesFromOpenAPI(
|
|
|
2475
2636
|
* @param {Array} slice Usages array for each objectSlice
|
|
2476
2637
|
* @param {Object} servicesMap Existing service map
|
|
2477
2638
|
*/
|
|
2639
|
+
/**
|
|
2640
|
+
* The text routes and endpoints are read from: JavaScript slices keep a call's source text in
|
|
2641
|
+
* `code` and the resolved callee name in `resolvedMethod`; other languages have only the latter.
|
|
2642
|
+
*
|
|
2643
|
+
* @param {Object|undefined} entry slice entry
|
|
2644
|
+
* @returns {string|undefined} text
|
|
2645
|
+
*/
|
|
2646
|
+
function sourceTextOf(entry) {
|
|
2647
|
+
return entry?.code || entry?.resolvedMethod;
|
|
2648
|
+
}
|
|
2649
|
+
|
|
2478
2650
|
export function detectServicesFromUsages(language, slice, servicesMap = {}) {
|
|
2479
2651
|
const usages = slice.usages;
|
|
2480
2652
|
if (!usages) {
|
|
@@ -2487,30 +2659,30 @@ export function detectServicesFromUsages(language, slice, servicesMap = {}) {
|
|
|
2487
2659
|
let authenticated;
|
|
2488
2660
|
if (language === "ruby" && definedBy?.name?.includes("/")) {
|
|
2489
2661
|
endpoints = extractEndpoints(language, definedBy.name);
|
|
2490
|
-
} else if (targetObj
|
|
2662
|
+
} else if (sourceTextOf(targetObj)) {
|
|
2491
2663
|
if (language !== "php") {
|
|
2492
|
-
endpoints = extractEndpoints(language, targetObj
|
|
2664
|
+
endpoints = extractEndpoints(language, sourceTextOf(targetObj));
|
|
2493
2665
|
}
|
|
2494
|
-
if (targetObj
|
|
2666
|
+
if (sourceTextOf(targetObj).toLowerCase().includes("auth")) {
|
|
2495
2667
|
authenticated = true;
|
|
2496
2668
|
}
|
|
2497
|
-
} else if (definedBy
|
|
2669
|
+
} else if (sourceTextOf(definedBy)) {
|
|
2498
2670
|
if (language !== "php") {
|
|
2499
|
-
endpoints = extractEndpoints(language, definedBy
|
|
2671
|
+
endpoints = extractEndpoints(language, sourceTextOf(definedBy));
|
|
2500
2672
|
}
|
|
2501
|
-
if (definedBy
|
|
2673
|
+
if (sourceTextOf(definedBy).toLowerCase().includes("auth")) {
|
|
2502
2674
|
authenticated = true;
|
|
2503
2675
|
}
|
|
2504
2676
|
}
|
|
2505
2677
|
if (usage.invokedCalls) {
|
|
2506
2678
|
for (const acall of usage.invokedCalls) {
|
|
2507
|
-
if (acall
|
|
2679
|
+
if (sourceTextOf(acall)) {
|
|
2508
2680
|
if (language !== "php") {
|
|
2509
2681
|
const tmpEndpoints = extractEndpoints(
|
|
2510
2682
|
language,
|
|
2511
|
-
acall
|
|
2683
|
+
sourceTextOf(acall),
|
|
2512
2684
|
);
|
|
2513
|
-
if (acall.
|
|
2685
|
+
if (sourceTextOf(acall).toLowerCase().includes("auth")) {
|
|
2514
2686
|
authenticated = true;
|
|
2515
2687
|
}
|
|
2516
2688
|
if (tmpEndpoints?.length) {
|
|
@@ -2716,6 +2888,72 @@ export function extractEndpoints(language, code) {
|
|
|
2716
2888
|
return endpoints;
|
|
2717
2889
|
}
|
|
2718
2890
|
|
|
2891
|
+
/**
|
|
2892
|
+
* Key naming the package a purl refers to, independent of its version,
|
|
2893
|
+
* qualifiers and subpath. Namespace and name are the decoded purl segments,
|
|
2894
|
+
* so an encoded scope (`%40angular/common`) keys like a plain one.
|
|
2895
|
+
*
|
|
2896
|
+
* @param {string} purl Purl string
|
|
2897
|
+
* @returns {string|undefined} `type/namespace/name`, or undefined for a non-purl
|
|
2898
|
+
*/
|
|
2899
|
+
export function versionlessPurlKey(purl) {
|
|
2900
|
+
const parts = splitPurlLoose(purl);
|
|
2901
|
+
if (!parts) {
|
|
2902
|
+
return undefined;
|
|
2903
|
+
}
|
|
2904
|
+
return `${parts.type}/${parts.namespace || ""}/${parts.name}`;
|
|
2905
|
+
}
|
|
2906
|
+
|
|
2907
|
+
/**
|
|
2908
|
+
* Index the data-flow frames recorded under versionless flow purls by
|
|
2909
|
+
* {@link versionlessPurlKey}. A flow purl without a version attaches to every
|
|
2910
|
+
* BOM component of the same package name, whatever its version (maintainer
|
|
2911
|
+
* decision, 2026-10-27); chen leaves the version off exactly when it cannot
|
|
2912
|
+
* pick one between apps that pin the package differently.
|
|
2913
|
+
*
|
|
2914
|
+
* @param {Object} dataFlowFrames Frames keyed by flow purl
|
|
2915
|
+
* @returns {Object} Frames keyed by versionless purl key
|
|
2916
|
+
*/
|
|
2917
|
+
export function indexVersionlessPurls(dataFlowFrames) {
|
|
2918
|
+
const index = {};
|
|
2919
|
+
for (const [purl, frames] of Object.entries(dataFlowFrames || {})) {
|
|
2920
|
+
if (splitPurlLoose(purl)?.version === null) {
|
|
2921
|
+
const key = versionlessPurlKey(purl);
|
|
2922
|
+
if (!key) {
|
|
2923
|
+
continue;
|
|
2924
|
+
}
|
|
2925
|
+
index[key] ??= [];
|
|
2926
|
+
index[key].push(...frames);
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
return index;
|
|
2930
|
+
}
|
|
2931
|
+
|
|
2932
|
+
/**
|
|
2933
|
+
* Index the crypto generate pools recorded under versionless flow purls by
|
|
2934
|
+
* {@link versionlessPurlKey}, the crypto counterpart of
|
|
2935
|
+
* {@link indexVersionlessPurls}.
|
|
2936
|
+
*
|
|
2937
|
+
* @param {Object} cryptoGeneratePurls Algorithm bom-refs keyed by flow purl
|
|
2938
|
+
* @returns {Object} Algorithm bom-ref sets keyed by versionless purl key
|
|
2939
|
+
*/
|
|
2940
|
+
export function indexVersionlessCrypto(cryptoGeneratePurls) {
|
|
2941
|
+
const index = {};
|
|
2942
|
+
for (const [purl, algos] of Object.entries(cryptoGeneratePurls || {})) {
|
|
2943
|
+
if (splitPurlLoose(purl)?.version === null) {
|
|
2944
|
+
const key = versionlessPurlKey(purl);
|
|
2945
|
+
if (!key) {
|
|
2946
|
+
continue;
|
|
2947
|
+
}
|
|
2948
|
+
index[key] ??= new Set();
|
|
2949
|
+
for (const ref of algos || []) {
|
|
2950
|
+
index[key].add(ref);
|
|
2951
|
+
}
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
return index;
|
|
2955
|
+
}
|
|
2956
|
+
|
|
2719
2957
|
/**
|
|
2720
2958
|
* Method to create the SBOM with evidence file called evinse file.
|
|
2721
2959
|
*
|
|
@@ -2767,6 +3005,15 @@ export async function createEvinseFile(sliceArtefacts, options) {
|
|
|
2767
3005
|
let servicesPresent = false;
|
|
2768
3006
|
const propertyEvidencePresent =
|
|
2769
3007
|
metadataProperties.length || Object.keys(componentPropertiesMap).length;
|
|
3008
|
+
// Versionless flow purls attach to every same-name component (see
|
|
3009
|
+
// indexVersionlessPurls), so look both ways: exact purl first, then the
|
|
3010
|
+
// versionless index.
|
|
3011
|
+
const versionlessFrames = indexVersionlessPurls(dataFlowFrames);
|
|
3012
|
+
const versionlessCrypto = indexVersionlessCrypto(cryptoGeneratePurls);
|
|
3013
|
+
const depCryptoPurls =
|
|
3014
|
+
Object.keys(versionlessCrypto).length && cryptoGeneratePurls
|
|
3015
|
+
? { ...cryptoGeneratePurls }
|
|
3016
|
+
: cryptoGeneratePurls;
|
|
2770
3017
|
for (const comp of [bomJson.metadata?.component, ...components]) {
|
|
2771
3018
|
if (!comp?.purl) {
|
|
2772
3019
|
continue;
|
|
@@ -2787,7 +3034,12 @@ export async function createEvinseFile(sliceArtefacts, options) {
|
|
|
2787
3034
|
.map((l) => parseOccurrenceEvidenceLocation(l));
|
|
2788
3035
|
occEvidencePresent = true;
|
|
2789
3036
|
}
|
|
2790
|
-
const
|
|
3037
|
+
const compKey = versionlessPurlKey(comp.purl);
|
|
3038
|
+
let dfFrames = dataFlowFrames[comp.purl];
|
|
3039
|
+
const extraFrames = compKey ? versionlessFrames[compKey] : undefined;
|
|
3040
|
+
if (extraFrames?.length) {
|
|
3041
|
+
dfFrames = [...(dfFrames || []), ...extraFrames];
|
|
3042
|
+
}
|
|
2791
3043
|
if (dfFrames?.length) {
|
|
2792
3044
|
if (!comp.evidence) {
|
|
2793
3045
|
comp.evidence = {};
|
|
@@ -2801,10 +3053,20 @@ export async function createEvinseFile(sliceArtefacts, options) {
|
|
|
2801
3053
|
}
|
|
2802
3054
|
}
|
|
2803
3055
|
// Add crypto tags if this purl offers any generation algorithm
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
3056
|
+
let compCryptoPurls = cryptoGeneratePurls?.[comp.purl];
|
|
3057
|
+
const extraCrypto = compKey ? versionlessCrypto[compKey] : undefined;
|
|
3058
|
+
if (extraCrypto?.size) {
|
|
3059
|
+
compCryptoPurls = new Set([...(compCryptoPurls || []), ...extraCrypto]);
|
|
3060
|
+
// The provides section below looks pool entries up by bom-ref, so the
|
|
3061
|
+
// merged set has to be reachable that way too.
|
|
3062
|
+
if (depCryptoPurls) {
|
|
3063
|
+
depCryptoPurls[comp.purl] = compCryptoPurls;
|
|
3064
|
+
if (comp["bom-ref"]) {
|
|
3065
|
+
depCryptoPurls[comp["bom-ref"]] = compCryptoPurls;
|
|
3066
|
+
}
|
|
3067
|
+
}
|
|
3068
|
+
}
|
|
3069
|
+
if (compCryptoPurls && Array.from(compCryptoPurls).length) {
|
|
2808
3070
|
comp.tags = ["crypto", "crypto-generate"];
|
|
2809
3071
|
}
|
|
2810
3072
|
} // for
|
|
@@ -2836,14 +3098,14 @@ export async function createEvinseFile(sliceArtefacts, options) {
|
|
|
2836
3098
|
}
|
|
2837
3099
|
// Fix the dependencies section with provides information
|
|
2838
3100
|
if (
|
|
2839
|
-
|
|
2840
|
-
Object.keys(
|
|
3101
|
+
depCryptoPurls &&
|
|
3102
|
+
Object.keys(depCryptoPurls).length &&
|
|
2841
3103
|
bomJson.dependencies
|
|
2842
3104
|
) {
|
|
2843
3105
|
const newDependencies = [];
|
|
2844
3106
|
for (const depObj of bomJson.dependencies) {
|
|
2845
|
-
if (depObj.ref &&
|
|
2846
|
-
const providedAlgos = Array.from(
|
|
3107
|
+
if (depObj.ref && depCryptoPurls[depObj.ref]) {
|
|
3108
|
+
const providedAlgos = Array.from(depCryptoPurls[depObj.ref]);
|
|
2847
3109
|
if (providedAlgos.length) {
|
|
2848
3110
|
depObj.provides = providedAlgos;
|
|
2849
3111
|
}
|
|
@@ -3088,15 +3350,15 @@ export function collectReachableFrames(_language, reachablesSlice) {
|
|
|
3088
3350
|
if (tagStr.includes("crypto")) {
|
|
3089
3351
|
isCryptoFlow = true;
|
|
3090
3352
|
}
|
|
3353
|
+
// Join with a space: findCryptoAlgos treats every non-alphanumeric as a
|
|
3354
|
+
// word separator, and a space is the one separator cleanStr keeps.
|
|
3091
3355
|
if (isCryptoFlow && fnode.code) {
|
|
3092
|
-
codeSnippets = `${codeSnippets}
|
|
3356
|
+
codeSnippets = `${codeSnippets} ${fnode.code}`;
|
|
3093
3357
|
}
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
fnode.code.includes("sign")
|
|
3099
|
-
) {
|
|
3358
|
+
// Pool membership is tag-driven: a node contributes its pkg: tags only
|
|
3359
|
+
// when the tagger marked it crypto-generate. Code substring tests read
|
|
3360
|
+
// assign/design/signup into encrypt/decrypt/sign and are gone.
|
|
3361
|
+
if (tagStr.includes("crypto-generate")) {
|
|
3100
3362
|
cpurls = tagStr.split(", ").filter((t) => t.startsWith("pkg:"));
|
|
3101
3363
|
for (const cpurl of cpurls) {
|
|
3102
3364
|
if (!cryptoGeneratePurls[cpurl]) {
|
|
@@ -3299,6 +3561,155 @@ export function getClassTypeFromSignature(language, typeFullName) {
|
|
|
3299
3561
|
return simplifyType(typeFullName);
|
|
3300
3562
|
}
|
|
3301
3563
|
|
|
3564
|
+
const JS_LANGUAGES = ["javascript", "js", "ts", "typescript"];
|
|
3565
|
+
|
|
3566
|
+
/**
|
|
3567
|
+
* Add every location of a set to a purl's locations (Set.add takes a single value).
|
|
3568
|
+
*
|
|
3569
|
+
* @param {Set<string>} target purl location set
|
|
3570
|
+
* @param {Set<string>|undefined} locations locations to add
|
|
3571
|
+
*/
|
|
3572
|
+
function addAllLocations(target, locations) {
|
|
3573
|
+
for (const l of locations || []) {
|
|
3574
|
+
target.add(l);
|
|
3575
|
+
}
|
|
3576
|
+
}
|
|
3577
|
+
|
|
3578
|
+
/**
|
|
3579
|
+
* npm package name of a JavaScript module specifier: `@scope/pkg/sub` -> `@scope/pkg`,
|
|
3580
|
+
* `lodash/fp` -> `lodash`. Node.js builtins (`node:fs`, `fs`) are not packages.
|
|
3581
|
+
*
|
|
3582
|
+
* @param {string} module module specifier
|
|
3583
|
+
* @returns {string|undefined} package name
|
|
3584
|
+
*/
|
|
3585
|
+
export function jsPackageOfModule(module) {
|
|
3586
|
+
if (!module || module.startsWith("node:") || module.startsWith(".")) {
|
|
3587
|
+
return undefined;
|
|
3588
|
+
}
|
|
3589
|
+
const parts = module.split("/");
|
|
3590
|
+
const pkg = module.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0];
|
|
3591
|
+
if (isNodeBuiltin(pkg)) {
|
|
3592
|
+
return undefined;
|
|
3593
|
+
}
|
|
3594
|
+
return pkg;
|
|
3595
|
+
}
|
|
3596
|
+
|
|
3597
|
+
function isNodeBuiltin(name) {
|
|
3598
|
+
return NODE_BUILTINS.has(name);
|
|
3599
|
+
}
|
|
3600
|
+
|
|
3601
|
+
const NODE_BUILTINS = new Set(builtinModules);
|
|
3602
|
+
|
|
3603
|
+
const jsPurlsByPackageCache = new WeakMap();
|
|
3604
|
+
|
|
3605
|
+
function jsPurlsByPackage(purlImportsMap) {
|
|
3606
|
+
let byPackage = jsPurlsByPackageCache.get(purlImportsMap);
|
|
3607
|
+
if (!byPackage) {
|
|
3608
|
+
byPackage = {};
|
|
3609
|
+
for (const apurl of Object.keys(purlImportsMap || {})) {
|
|
3610
|
+
try {
|
|
3611
|
+
const purlObj = Purl.parse(apurl);
|
|
3612
|
+
if (purlObj.type !== "npm") {
|
|
3613
|
+
continue;
|
|
3614
|
+
}
|
|
3615
|
+
const name = purlObj.namespace
|
|
3616
|
+
? `${purlObj.namespace}/${purlObj.name}`
|
|
3617
|
+
: purlObj.name;
|
|
3618
|
+
(byPackage[name] = byPackage[name] || []).push(apurl);
|
|
3619
|
+
} catch (_e) {
|
|
3620
|
+
// not a valid purl
|
|
3621
|
+
}
|
|
3622
|
+
}
|
|
3623
|
+
jsPurlsByPackageCache.set(purlImportsMap, byPackage);
|
|
3624
|
+
}
|
|
3625
|
+
return byPackage;
|
|
3626
|
+
}
|
|
3627
|
+
|
|
3628
|
+
/**
|
|
3629
|
+
* Attribute a JavaScript usage's module-qualified entries to the packages providing them.
|
|
3630
|
+
*
|
|
3631
|
+
* Each call entry with a `module` is a use of that module's package at the call's line. When
|
|
3632
|
+
* the slice's object is an import binding (defined by a require/import naming the module),
|
|
3633
|
+
* every call the object takes part in is a use of that package too.
|
|
3634
|
+
*
|
|
3635
|
+
* @param {Object} ausage usage entry of an object slice
|
|
3636
|
+
* @param {string} fileName file of the slice
|
|
3637
|
+
* @param {Object} purlImportsMap purl -> imported modules and symbols
|
|
3638
|
+
* @param {Object} purlLocationMap purl -> Set of `file#line`
|
|
3639
|
+
*/
|
|
3640
|
+
export function attributeJsModules(
|
|
3641
|
+
ausage,
|
|
3642
|
+
fileName,
|
|
3643
|
+
purlImportsMap,
|
|
3644
|
+
purlLocationMap,
|
|
3645
|
+
) {
|
|
3646
|
+
const byPackage = jsPurlsByPackage(purlImportsMap);
|
|
3647
|
+
const record = (module, line) => {
|
|
3648
|
+
const pkg = jsPackageOfModule(module);
|
|
3649
|
+
if (!pkg || !line) {
|
|
3650
|
+
return;
|
|
3651
|
+
}
|
|
3652
|
+
for (const apurl of byPackage[pkg] || []) {
|
|
3653
|
+
if (!purlLocationMap[apurl]) {
|
|
3654
|
+
purlLocationMap[apurl] = new Set();
|
|
3655
|
+
}
|
|
3656
|
+
purlLocationMap[apurl].add(`${fileName}#${line}`);
|
|
3657
|
+
}
|
|
3658
|
+
};
|
|
3659
|
+
const calls = []
|
|
3660
|
+
.concat(ausage?.invokedCalls || [])
|
|
3661
|
+
.concat(ausage?.argToCalls || []);
|
|
3662
|
+
for (const c of [ausage?.targetObj, ausage?.definedBy, ...calls]) {
|
|
3663
|
+
if (c?.module) {
|
|
3664
|
+
record(c.module, c.lineNumber);
|
|
3665
|
+
}
|
|
3666
|
+
}
|
|
3667
|
+
// Only an import binding stands for its module: a value merely returned by a package's call
|
|
3668
|
+
// (`const s = purl.toString()`) is not a use of that package wherever it goes.
|
|
3669
|
+
const definedBy = ausage?.definedBy;
|
|
3670
|
+
const isImportBinding =
|
|
3671
|
+
definedBy?.name === "require" ||
|
|
3672
|
+
definedBy?.name === "import" ||
|
|
3673
|
+
/^(require|import)\(/.test(definedBy?.code || "");
|
|
3674
|
+
const bindingModule = isImportBinding ? definedBy?.module : undefined;
|
|
3675
|
+
if (bindingModule) {
|
|
3676
|
+
for (const c of calls) {
|
|
3677
|
+
record(bindingModule, c?.lineNumber);
|
|
3678
|
+
}
|
|
3679
|
+
}
|
|
3680
|
+
}
|
|
3681
|
+
|
|
3682
|
+
/**
|
|
3683
|
+
* Attribute a callsites slice's records to the packages providing their modules.
|
|
3684
|
+
*
|
|
3685
|
+
* Each record of `{"callsites": [...]}` (atom `callsites` command) is one external use site
|
|
3686
|
+
* carrying its own file and line; the record is an occurrence of every purl whose package
|
|
3687
|
+
* matches the record's module.
|
|
3688
|
+
*
|
|
3689
|
+
* @param {Object} callsitesSlice callsites slice with a `callsites` array
|
|
3690
|
+
* @param {Object} purlImportsMap purl -> imported modules and symbols
|
|
3691
|
+
* @param {Object} purlLocationMap purl -> Set of `file#line`
|
|
3692
|
+
*/
|
|
3693
|
+
export function attributeJsCallsites(
|
|
3694
|
+
callsitesSlice,
|
|
3695
|
+
purlImportsMap,
|
|
3696
|
+
purlLocationMap,
|
|
3697
|
+
) {
|
|
3698
|
+
const byPackage = jsPurlsByPackage(purlImportsMap);
|
|
3699
|
+
for (const record of callsitesSlice?.callsites || []) {
|
|
3700
|
+
const pkg = jsPackageOfModule(record?.module);
|
|
3701
|
+
if (!pkg || !(record.line > 0)) {
|
|
3702
|
+
continue;
|
|
3703
|
+
}
|
|
3704
|
+
for (const apurl of byPackage[pkg] || []) {
|
|
3705
|
+
if (!purlLocationMap[apurl]) {
|
|
3706
|
+
purlLocationMap[apurl] = new Set();
|
|
3707
|
+
}
|
|
3708
|
+
purlLocationMap[apurl].add(`${record.file}#${record.line}`);
|
|
3709
|
+
}
|
|
3710
|
+
}
|
|
3711
|
+
}
|
|
3712
|
+
|
|
3302
3713
|
function addToOverrides(lKeyOverrides, atype, fileName, ausageLineNumber) {
|
|
3303
3714
|
if (!lKeyOverrides[atype]) {
|
|
3304
3715
|
lKeyOverrides[atype] = new Set();
|