@cdxgen/cdxgen 13.5.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 +17 -10
- package/bin/audit.js +7 -4
- package/bin/cdxgen.js +260 -37
- package/bin/convert.js +146 -22
- package/bin/evinse.js +28 -3
- package/bin/repl.js +47 -3
- package/bin/sign.js +211 -34
- package/bin/tracebom.js +18 -6
- package/bin/validate.js +24 -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/license-aliases.json +24 -2
- package/data/pqc-migration.json +239 -0
- package/data/remediations.json +176 -0
- package/data/rules/ai-bom.yaml +1 -1
- package/data/rules/brotli-decoder.yaml +107 -0
- package/data/rules/cbom-security.yaml +4 -3
- package/data/rules/package-integrity.yaml +28 -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 +25 -3
- package/lib/cli/bomAssembly.js +172 -4
- package/lib/cli/cliOptions.js +121 -17
- package/lib/cli/index.js +720 -902
- package/lib/cli/jsBom.js +156 -64
- package/lib/cli/jvmBom.js +602 -86
- package/lib/cli/managedBom.js +161 -19
- package/lib/cli/nativeBom.js +52 -5
- package/lib/core/boundedFile.js +344 -0
- package/lib/core/brotliProbe.js +182 -0
- package/lib/core/env.js +34 -0
- package/lib/core/fs.js +138 -3
- package/lib/core/globPatterns.js +218 -0
- package/lib/core/httpClient.js +98 -1
- package/lib/core/lockPaths.js +38 -0
- package/lib/core/paths.js +24 -1
- package/lib/core/processTree.js +100 -8
- package/lib/core/projectConfig.js +332 -25
- package/lib/core/toolRegistry.js +184 -0
- package/lib/ecosystems/cppEvidence.js +263 -106
- package/lib/ecosystems/cppIncludeResolver.js +16 -5
- package/lib/ecosystems/denoutils.js +2 -2
- package/lib/ecosystems/ecosystems.js +25 -10
- package/lib/ecosystems/gemHostProbe.js +301 -0
- package/lib/ecosystems/golem.js +3 -8
- package/lib/ecosystems/gradleutils.js +1909 -229
- 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-bazel.js +107 -6
- package/lib/ecosystems/parsers-js.js +505 -22
- package/lib/ecosystems/parsers-misc.js +209 -20
- package/lib/ecosystems/parsers-python.js +178 -30
- package/lib/ecosystems/parsers-rust.js +117 -15
- package/lib/ecosystems/pythonutils.js +128 -2
- package/lib/ecosystems/remote/huggingface.js +22 -0
- package/lib/ecosystems/rubyutils.js +538 -31
- package/lib/ecosystems/rusi.js +2 -4
- package/lib/ecosystems/scalacliutils.js +2 -0
- package/lib/ecosystems/scalasem.js +798 -152
- package/lib/ecosystems/vendoredCode.js +237 -3
- package/lib/ecosystems/venvProbe.js +360 -0
- package/lib/evinser/evinser.js +536 -71
- package/lib/evinser/scalasem.js +5 -1
- package/lib/helpers/agenticToolutils.js +525 -94
- package/lib/helpers/bomSigner.js +43 -9
- package/lib/helpers/jssSigner.js +1276 -0
- package/lib/helpers/vsixutils.js +1574 -315
- 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/atomUtils.js +77 -0
- package/lib/inventory/bomUtils.js +8 -1
- package/lib/inventory/brotliDecoder.js +142 -0
- package/lib/inventory/buildToolRateLimit.js +1 -1
- package/lib/inventory/cbomutils.js +56 -7
- 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 +141 -13
- package/lib/inventory/dosai.js +375 -6
- package/lib/inventory/dosaiParsers.js +3 -6
- package/lib/inventory/dotnetRuntime.js +367 -0
- package/lib/inventory/ecosystemFormulationParsers.js +979 -0
- package/lib/inventory/envcontext.js +436 -48
- package/lib/inventory/evidenceUtils.js +13 -1
- package/lib/inventory/fetchBatch.js +1 -1
- package/lib/inventory/formulationParsers.js +124 -14
- package/lib/inventory/gtfobins.js +13 -0
- package/lib/inventory/hbom.js +45 -6
- package/lib/inventory/hbomLoader.js +166 -0
- package/lib/inventory/hostRuntimeProbe.js +686 -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/mcpConfigParser.js +5 -0
- package/lib/inventory/nodeBuildInfo.js +218 -0
- package/lib/inventory/nodeHostProbe.js +202 -0
- package/lib/inventory/occurrenceKey.js +90 -0
- package/lib/inventory/osPackageResolver.js +182 -0
- package/lib/inventory/phpBuildInfo.js +10 -59
- package/lib/inventory/phpHostProbe.js +32 -488
- package/lib/inventory/pqcMigration.js +426 -0
- package/lib/inventory/rubyBuildInfo.js +972 -0
- package/lib/inventory/rubyHostProbe.js +218 -0
- package/lib/inventory/rustFormulationParser.js +21 -10
- package/lib/inventory/scalaCoords.js +4 -3
- package/lib/inventory/scalaDedupe.js +440 -0
- package/lib/inventory/source.js +114 -8
- package/lib/managers/binary.js +337 -3
- package/lib/managers/docker.js +79 -0
- package/lib/parsers/huggingfaceManifest.js +9 -1
- package/lib/parsers/huggingfaceUtils.js +18 -0
- package/lib/parsers/kiroPermissions.js +166 -0
- package/lib/parsers/largeJson.js +28 -11
- package/lib/parsers/vscodeExtensionId.js +183 -0
- package/lib/server/server.js +48 -35
- package/lib/stages/postgen/postgen.js +41 -1
- package/lib/stages/postgen/ruleEngine.js +13 -5
- package/lib/stages/postgen/specVersionCompat.js +76 -0
- package/lib/stages/pregen/envAudit.js +1 -0
- package/lib/stages/pregen/pregen.js +114 -57
- package/lib/validator/bomValidator.js +14 -2
- package/lib/validator/complianceEngine.js +30 -3
- package/lib/validator/complianceRules.js +1416 -4
- package/lib/validator/generated/validate-1.6.mjs +1 -1
- package/lib/validator/generated/validate-1.7.mjs +1 -1
- package/lib/validator/generated/validate-2.0.mjs +57554 -13851
- package/lib/validator/index.js +49 -8
- package/lib/validator/reporters/json.js +6 -0
- package/package.json +11 -10
- 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 +29 -1
- 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 +3 -18
- 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 +100 -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 +14 -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/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/paths.d.ts +9 -0
- package/types/lib/core/paths.d.ts.map +1 -1
- package/types/lib/core/processTree.d.ts +17 -4
- 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 +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.map +1 -1
- package/types/lib/ecosystems/gradleutils.d.ts +294 -20
- 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-bazel.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-misc.d.ts +19 -0
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-python.d.ts +10 -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 +19 -1
- 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 +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 +58 -16
- 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 +120 -13
- 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/atomUtils.d.ts +19 -0
- package/types/lib/inventory/atomUtils.d.ts.map +1 -1
- package/types/lib/inventory/bomUtils.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/cbomutils.d.ts +17 -0
- package/types/lib/inventory/cbomutils.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 +36 -1
- 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 +91 -0
- package/types/lib/inventory/dotnetRuntime.d.ts.map +1 -0
- 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 +74 -12
- 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/gtfobins.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/hostRuntimeProbe.d.ts +44 -0
- package/types/lib/inventory/hostRuntimeProbe.d.ts.map +1 -0
- package/types/lib/inventory/jvmLocalRepos.d.ts +5 -1
- package/types/lib/inventory/jvmLocalRepos.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/mcpConfigParser.d.ts.map +1 -1
- 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/occurrenceKey.d.ts +52 -0
- package/types/lib/inventory/occurrenceKey.d.ts.map +1 -0
- package/types/lib/inventory/osPackageResolver.d.ts +38 -0
- package/types/lib/inventory/osPackageResolver.d.ts.map +1 -1
- package/types/lib/inventory/phpBuildInfo.d.ts.map +1 -1
- package/types/lib/inventory/phpHostProbe.d.ts.map +1 -1
- package/types/lib/inventory/pqcMigration.d.ts +84 -0
- package/types/lib/inventory/pqcMigration.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/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 +25 -0
- package/types/lib/inventory/source.d.ts.map +1 -1
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts +12 -0
- package/types/lib/managers/docker.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/largeJson.d.ts +4 -3
- package/types/lib/parsers/largeJson.d.ts.map +1 -1
- 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/stages/pregen/envAudit.d.ts.map +1 -1
- package/types/lib/stages/pregen/pregen.d.ts +12 -8
- package/types/lib/stages/pregen/pregen.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
|
@@ -1,4 +1,5 @@
|
|
|
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
|
|
|
@@ -16,6 +17,7 @@ import {
|
|
|
16
17
|
safeRmSync,
|
|
17
18
|
safeWriteSync,
|
|
18
19
|
} from "../core/fs.js";
|
|
20
|
+
import { thoughtLog } from "../core/logger.js";
|
|
19
21
|
import { isProjectToolingAllowed } from "../core/projectTooling.js";
|
|
20
22
|
import { hasCppSources } from "../ecosystems/cppEvidence.js";
|
|
21
23
|
import {
|
|
@@ -41,6 +43,7 @@ import {
|
|
|
41
43
|
kosiWorkspaceComponent,
|
|
42
44
|
mergeKosiEvidence,
|
|
43
45
|
} from "../ecosystems/kosi.js";
|
|
46
|
+
import { keepResolvedNpmVersions } from "../ecosystems/npmVersionResolution.js";
|
|
44
47
|
import {
|
|
45
48
|
analyzeRusiProject,
|
|
46
49
|
collectRusiEvidence,
|
|
@@ -54,6 +57,7 @@ import {
|
|
|
54
57
|
isScalasemLanguage,
|
|
55
58
|
} from "../ecosystems/scalasem.js";
|
|
56
59
|
import {
|
|
60
|
+
atomBomFileArgs,
|
|
57
61
|
atomBuildToolArgs,
|
|
58
62
|
atomCompileCommandsArgs,
|
|
59
63
|
buildAtomCommandEnv,
|
|
@@ -69,6 +73,10 @@ import {
|
|
|
69
73
|
findCryptoAlgos,
|
|
70
74
|
} from "../inventory/cbomutils.js";
|
|
71
75
|
import { cmakeBuildDirCandidates } from "../inventory/cmakeBuildDirs.js";
|
|
76
|
+
import {
|
|
77
|
+
formatReadLossNotice,
|
|
78
|
+
loadBomForAnalysis,
|
|
79
|
+
} from "../inventory/cyclonedx20.js";
|
|
72
80
|
import { collectMvnDependencies } from "../inventory/deps.js";
|
|
73
81
|
import { mergeServices } from "../inventory/depsUtils.js";
|
|
74
82
|
import {
|
|
@@ -84,6 +92,7 @@ import {
|
|
|
84
92
|
dosaiOmittedSections,
|
|
85
93
|
dosaiReportFileOmittedSections,
|
|
86
94
|
dosaiSectionsCdxgenReads,
|
|
95
|
+
dosaiSlicesFileIsFresh,
|
|
87
96
|
isDosaiDotnetLanguage,
|
|
88
97
|
normalizeDosaiServiceMap,
|
|
89
98
|
persistDosaiSemanticsReport,
|
|
@@ -91,6 +100,11 @@ import {
|
|
|
91
100
|
readDosaiMethodsReport,
|
|
92
101
|
} from "../inventory/dosai.js";
|
|
93
102
|
import { parseOccurrenceEvidenceLocation } from "../inventory/evidenceUtils.js";
|
|
103
|
+
import {
|
|
104
|
+
dropSubsumedOccurrenceKeys,
|
|
105
|
+
occurrenceLocationKey,
|
|
106
|
+
} from "../inventory/occurrenceKey.js";
|
|
107
|
+
import { splitPurlLoose } from "../inventory/purl.js";
|
|
94
108
|
import { executeOsQuery } from "../managers/binary.js";
|
|
95
109
|
import { MAX_JSON_TEXT_BYTES, readJsonFile } from "../parsers/largeJson.js";
|
|
96
110
|
import { postProcess } from "../stages/postgen/postgen.js";
|
|
@@ -98,7 +112,8 @@ import { createOrLoad } from "./db.js";
|
|
|
98
112
|
import { findPurlLocations } from "./scalasem.js";
|
|
99
113
|
import { createSemanticsSlices } from "./swiftsem.js";
|
|
100
114
|
|
|
101
|
-
|
|
115
|
+
// keyed by type names, which can be "constructor", "toString", ...: no prototype
|
|
116
|
+
const typePurlsCache = Object.create(null);
|
|
102
117
|
|
|
103
118
|
function appendUniqueProperty(properties, name, value) {
|
|
104
119
|
if (value === undefined || value === null || value === "") {
|
|
@@ -261,6 +276,7 @@ export function buildEvinseOptions(options, args, input) {
|
|
|
261
276
|
force: false,
|
|
262
277
|
withReachables: options.deep,
|
|
263
278
|
usagesSlicesFile: options.usagesSlicesFile,
|
|
279
|
+
callsitesSlicesFile: options.callsitesSlicesFile,
|
|
264
280
|
dataFlowSlicesFile: options.dataFlowSlicesFile,
|
|
265
281
|
reachablesSlicesFile: options.reachablesSlicesFile,
|
|
266
282
|
semanticsSlicesFile: options.semanticsSlicesFile,
|
|
@@ -293,6 +309,45 @@ export function buildEvinseOptions(options, args, input) {
|
|
|
293
309
|
};
|
|
294
310
|
}
|
|
295
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
|
+
|
|
296
351
|
/**
|
|
297
352
|
* Function to create the db for the libraries referred in the sbom.
|
|
298
353
|
*
|
|
@@ -312,7 +367,7 @@ export async function prepareDB(options) {
|
|
|
312
367
|
}
|
|
313
368
|
return;
|
|
314
369
|
}
|
|
315
|
-
const bomJson =
|
|
370
|
+
const { bomJson } = loadEvinseInputBom(options);
|
|
316
371
|
if (bomJson.specVersion < 1.5) {
|
|
317
372
|
console.log(
|
|
318
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.",
|
|
@@ -734,6 +789,7 @@ export async function createSlice(
|
|
|
734
789
|
args.push("--slice-depth");
|
|
735
790
|
args.push(readEnvironmentVariable("ATOM_SLICE_DEPTH"));
|
|
736
791
|
}
|
|
792
|
+
args.push(...atomBomFileArgs(sliceType, options));
|
|
737
793
|
args.push(...atomCompileCommandsArgs(filePath, language, options));
|
|
738
794
|
args.push(...buildToolArgs);
|
|
739
795
|
args.push(resolve(filePath));
|
|
@@ -839,15 +895,18 @@ export function purlToLanguage(purl, filePath) {
|
|
|
839
895
|
* Walks the supplied components reading `internal:ImportedModules` (or
|
|
840
896
|
* `internal:Namespaces` for php/ruby, plus the `internal:ImportedSymbols` the
|
|
841
897
|
* C/C++ collector records) and `evidence.occurrences` properties to construct
|
|
842
|
-
* 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.
|
|
843
901
|
*
|
|
844
902
|
* @param {Object[]} components CycloneDX components from the input SBOM
|
|
845
903
|
* @param {string} language Application language used to select the import property name
|
|
846
|
-
* @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
|
|
847
905
|
*/
|
|
848
906
|
export function initFromSbom(components, language) {
|
|
849
907
|
const purlLocationMap = {};
|
|
850
908
|
const purlImportsMap = {};
|
|
909
|
+
const purlSymbolMap = {};
|
|
851
910
|
for (const comp of components) {
|
|
852
911
|
if (!comp?.evidence) {
|
|
853
912
|
continue;
|
|
@@ -880,17 +939,30 @@ export function initFromSbom(components, language) {
|
|
|
880
939
|
if (comp.evidence.occurrences) {
|
|
881
940
|
// Keep the `location#line` shape so a re-run over a cdxgen BOM does not
|
|
882
941
|
// degrade seeded occurrences to file-only entries and duplicate the
|
|
883
|
-
// 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.
|
|
884
946
|
purlLocationMap[comp.purl] = new Set(
|
|
885
|
-
comp.evidence.occurrences
|
|
886
|
-
v
|
|
887
|
-
|
|
947
|
+
comp.evidence.occurrences
|
|
948
|
+
.map((v) => occurrenceLocationKey(v.location, v.line, v.offset))
|
|
949
|
+
.filter(Boolean),
|
|
888
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
|
+
}
|
|
889
960
|
}
|
|
890
961
|
}
|
|
891
962
|
return {
|
|
892
963
|
purlLocationMap,
|
|
893
964
|
purlImportsMap,
|
|
965
|
+
purlSymbolMap,
|
|
894
966
|
};
|
|
895
967
|
}
|
|
896
968
|
|
|
@@ -1244,7 +1316,7 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1244
1316
|
let retMap = {};
|
|
1245
1317
|
let userDefinedTypesMap = {};
|
|
1246
1318
|
const bomFile = options.input;
|
|
1247
|
-
const bomJson =
|
|
1319
|
+
const { bomJson } = loadEvinseInputBom(options);
|
|
1248
1320
|
const components = bomJson.components || [];
|
|
1249
1321
|
let cryptoComponents = [];
|
|
1250
1322
|
let aiComponents = [];
|
|
@@ -1252,7 +1324,22 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1252
1324
|
let openapiSpecFile;
|
|
1253
1325
|
// Load any existing purl-location information from the sbom.
|
|
1254
1326
|
// For eg: cdxgen populates this information for javascript projects
|
|
1255
|
-
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
|
+
: {};
|
|
1256
1343
|
|
|
1257
1344
|
if (isGolemGoLanguage(language)) {
|
|
1258
1345
|
const golemReport = analyzeGolemProject(dirPath, options);
|
|
@@ -1271,6 +1358,7 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1271
1358
|
cryptoGeneratePurls: golemEvidence.cryptoGeneratePurls,
|
|
1272
1359
|
servicesMap,
|
|
1273
1360
|
userDefinedTypesMap,
|
|
1361
|
+
purlSymbolMap,
|
|
1274
1362
|
};
|
|
1275
1363
|
}
|
|
1276
1364
|
return {
|
|
@@ -1278,6 +1366,7 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1278
1366
|
servicesMap,
|
|
1279
1367
|
dataFlowFrames,
|
|
1280
1368
|
userDefinedTypesMap,
|
|
1369
|
+
purlSymbolMap,
|
|
1281
1370
|
};
|
|
1282
1371
|
}
|
|
1283
1372
|
|
|
@@ -1297,6 +1386,7 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1297
1386
|
cryptoGeneratePurls: rusiEvidence.cryptoGeneratePurls,
|
|
1298
1387
|
servicesMap,
|
|
1299
1388
|
userDefinedTypesMap,
|
|
1389
|
+
purlSymbolMap,
|
|
1300
1390
|
};
|
|
1301
1391
|
}
|
|
1302
1392
|
return {
|
|
@@ -1304,6 +1394,7 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1304
1394
|
servicesMap,
|
|
1305
1395
|
dataFlowFrames,
|
|
1306
1396
|
userDefinedTypesMap,
|
|
1397
|
+
purlSymbolMap,
|
|
1307
1398
|
};
|
|
1308
1399
|
}
|
|
1309
1400
|
|
|
@@ -1375,6 +1466,7 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1375
1466
|
servicesMap,
|
|
1376
1467
|
userDefinedTypesMap,
|
|
1377
1468
|
workspaceComponent,
|
|
1469
|
+
purlSymbolMap,
|
|
1378
1470
|
};
|
|
1379
1471
|
}
|
|
1380
1472
|
return {
|
|
@@ -1382,6 +1474,7 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1382
1474
|
servicesMap,
|
|
1383
1475
|
dataFlowFrames,
|
|
1384
1476
|
userDefinedTypesMap,
|
|
1477
|
+
purlSymbolMap,
|
|
1385
1478
|
};
|
|
1386
1479
|
}
|
|
1387
1480
|
|
|
@@ -1443,6 +1536,17 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1443
1536
|
`The dosai deps slices file "${options.depsSlicesFile}" omits sections (${omittedSections.join(", ")}); running dosai for the full usages report.`,
|
|
1444
1537
|
);
|
|
1445
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
|
+
}
|
|
1446
1550
|
} else {
|
|
1447
1551
|
if (DEBUG_MODE) {
|
|
1448
1552
|
console.log(
|
|
@@ -1540,6 +1644,7 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1540
1644
|
cryptoComponents,
|
|
1541
1645
|
aiComponents,
|
|
1542
1646
|
cryptoGeneratePurls,
|
|
1647
|
+
purlSymbolMap,
|
|
1543
1648
|
};
|
|
1544
1649
|
}
|
|
1545
1650
|
// Do reachables first so that usages slicing can reuse the atom file
|
|
@@ -1573,6 +1678,7 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1573
1678
|
cryptoGeneratePurls = retMap.cryptoGeneratePurls;
|
|
1574
1679
|
}
|
|
1575
1680
|
// Reuse existing usages slices
|
|
1681
|
+
let usagesAtomFile;
|
|
1576
1682
|
if (options.usagesSlicesFile && usableSlicesFile(options.usagesSlicesFile)) {
|
|
1577
1683
|
usageSlice = filterAtomSliceData(
|
|
1578
1684
|
readSlicesFile(options.usagesSlicesFile, "usages"),
|
|
@@ -1582,6 +1688,7 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1582
1688
|
} else {
|
|
1583
1689
|
// Generate our own slices
|
|
1584
1690
|
retMap = await createSlice(language, dirPath, "usages", options);
|
|
1691
|
+
usagesAtomFile = retMap?.atomFile || "";
|
|
1585
1692
|
if (retMap?.slicesFile && safeExistsSync(retMap.slicesFile)) {
|
|
1586
1693
|
usageSlice = filterAtomSliceData(
|
|
1587
1694
|
readSlicesFile(retMap.slicesFile, "usages"),
|
|
@@ -1661,6 +1768,48 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1661
1768
|
servicesMap = retMap.servicesMap;
|
|
1662
1769
|
userDefinedTypesMap = retMap.userDefinedTypesMap;
|
|
1663
1770
|
}
|
|
1771
|
+
// Parse the callsites slice (JS/TS): one record per external use site, each
|
|
1772
|
+
// record's own (file, line) is an occurrence of the module's package. The
|
|
1773
|
+
// usages slice above still provides services and endpoints.
|
|
1774
|
+
if (JS_LANGUAGES.includes(language)) {
|
|
1775
|
+
let callsitesSlice;
|
|
1776
|
+
if (
|
|
1777
|
+
options.callsitesSlicesFile &&
|
|
1778
|
+
usableSlicesFile(options.callsitesSlicesFile)
|
|
1779
|
+
) {
|
|
1780
|
+
callsitesSlice = readSlicesFile(options.callsitesSlicesFile, "callsites");
|
|
1781
|
+
} else if (
|
|
1782
|
+
usagesAtomFile !== undefined &&
|
|
1783
|
+
!safeExistsSync(usagesAtomFile)
|
|
1784
|
+
) {
|
|
1785
|
+
// The usages run above built no atom (stopped at the timeout, or atom
|
|
1786
|
+
// failed); a callsites run would rebuild the same atom and fail the same way.
|
|
1787
|
+
console.log(
|
|
1788
|
+
"Skipping the callsites slice since atom could not build the atom for the usages slice.",
|
|
1789
|
+
);
|
|
1790
|
+
} else {
|
|
1791
|
+
retMap = await createSlice(language, dirPath, "callsites", options);
|
|
1792
|
+
if (retMap?.slicesFile && safeExistsSync(retMap.slicesFile)) {
|
|
1793
|
+
callsitesSlice = readSlicesFile(retMap.slicesFile, "callsites");
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
if (callsitesSlice?.callsites?.length) {
|
|
1797
|
+
attributeJsCallsites(
|
|
1798
|
+
callsitesSlice,
|
|
1799
|
+
purlImportsMap,
|
|
1800
|
+
purlLocationMap,
|
|
1801
|
+
purlSymbolMap,
|
|
1802
|
+
);
|
|
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
|
+
);
|
|
1812
|
+
}
|
|
1664
1813
|
// A component whose calls the slices do not show (code inside the project,
|
|
1665
1814
|
// such as a vendored library) is still used where its header is included
|
|
1666
1815
|
for (const [purl, includeLocations] of cIncludeLocations) {
|
|
@@ -1734,6 +1883,8 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1734
1883
|
reachablesSlicesFile,
|
|
1735
1884
|
semanticsSlicesFile,
|
|
1736
1885
|
purlLocationMap,
|
|
1886
|
+
purlSymbolMap,
|
|
1887
|
+
clearedOccurrencePurls,
|
|
1737
1888
|
servicesMap,
|
|
1738
1889
|
dataFlowFrames,
|
|
1739
1890
|
tempDir: retMap?.tempDir,
|
|
@@ -1840,11 +1991,7 @@ export function attributeCIncludes(
|
|
|
1840
1991
|
continue;
|
|
1841
1992
|
}
|
|
1842
1993
|
const known = locations.get(purl) || new Set();
|
|
1843
|
-
known.add(
|
|
1844
|
-
slice.lineNumber
|
|
1845
|
-
? `${slice.fileName}#${slice.lineNumber}`
|
|
1846
|
-
: slice.fileName,
|
|
1847
|
-
);
|
|
1994
|
+
known.add(occurrenceLocationKey(slice.fileName, slice.lineNumber));
|
|
1848
1995
|
locations.set(purl, known);
|
|
1849
1996
|
}
|
|
1850
1997
|
return { symbols, locations };
|
|
@@ -1968,8 +2115,15 @@ export async function parseSliceUsages(
|
|
|
1968
2115
|
) {
|
|
1969
2116
|
const fileName = slice.fileName;
|
|
1970
2117
|
const typesToLookup = new Set();
|
|
1971
|
-
|
|
1972
|
-
const
|
|
2118
|
+
// keyed by type names, which can be "constructor", "toString", ...: no prototype
|
|
2119
|
+
const lKeyOverrides = Object.create(null);
|
|
2120
|
+
// A user defined type has no usages; its fields' types are evidence at the fields' lines.
|
|
2121
|
+
const usages =
|
|
2122
|
+
slice.usages ||
|
|
2123
|
+
(slice.fields || [])
|
|
2124
|
+
.filter((f) => f?.typeFullName)
|
|
2125
|
+
.map((f) => ({ targetObj: f }));
|
|
2126
|
+
const isJs = JS_LANGUAGES.includes(language);
|
|
1973
2127
|
// What should be the line number to use. slice.lineNumber would be quite coarse and could lead to reports such as
|
|
1974
2128
|
// #1670. Line numbers under targetObj and definedBy is a safe bet for dynamic languages, but occassionally leads to
|
|
1975
2129
|
// confusion when inter-procedural tracking works better than expected.
|
|
@@ -1992,13 +2146,27 @@ export async function parseSliceUsages(
|
|
|
1992
2146
|
sliceLineNumber ||
|
|
1993
2147
|
ausage?.targetObj?.lineNumber ||
|
|
1994
2148
|
ausage?.definedBy?.lineNumber;
|
|
2149
|
+
// JavaScript entries name the imported module they resolve to; attribute those by package
|
|
2150
|
+
// and keep the symbol-based matching below for entries without one.
|
|
2151
|
+
if (isJs) {
|
|
2152
|
+
attributeJsModules(ausage, fileName, purlImportsMap, purlLocationMap);
|
|
2153
|
+
}
|
|
2154
|
+
const viaSymbols = (o) => !(isJs && o?.module);
|
|
1995
2155
|
// First capture the types in the targetObj and definedBy
|
|
1996
2156
|
for (const atype of [
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2157
|
+
...(viaSymbols(ausage?.targetObj)
|
|
2158
|
+
? [
|
|
2159
|
+
[ausage?.targetObj?.isExternal, ausage?.targetObj?.typeFullName],
|
|
2160
|
+
[ausage?.targetObj?.isExternal, ausage?.targetObj?.resolvedMethod],
|
|
2161
|
+
]
|
|
2162
|
+
: []),
|
|
2163
|
+
...(viaSymbols(ausage?.definedBy)
|
|
2164
|
+
? [
|
|
2165
|
+
[ausage?.definedBy?.name?.includes("::"), ausage?.definedBy?.name],
|
|
2166
|
+
[ausage?.definedBy?.isExternal, ausage?.definedBy?.typeFullName],
|
|
2167
|
+
[ausage?.definedBy?.isExternal, ausage?.definedBy?.resolvedMethod],
|
|
2168
|
+
]
|
|
2169
|
+
: []),
|
|
2002
2170
|
...(ausage?.fields || []).map((f) => [f?.isExternal, f?.typeFullName]),
|
|
2003
2171
|
]) {
|
|
2004
2172
|
if (
|
|
@@ -2039,7 +2207,8 @@ export async function parseSliceUsages(
|
|
|
2039
2207
|
for (const acall of []
|
|
2040
2208
|
.concat(ausage?.invokedCalls || [])
|
|
2041
2209
|
.concat(ausage?.argToCalls || [])
|
|
2042
|
-
.concat(ausage?.procedures || [])
|
|
2210
|
+
.concat(ausage?.procedures || [])
|
|
2211
|
+
.filter(viaSymbols)) {
|
|
2043
2212
|
if (
|
|
2044
2213
|
acall.resolvedMethod?.startsWith("@") ||
|
|
2045
2214
|
acall?.callName?.includes("::")
|
|
@@ -2139,9 +2308,7 @@ export async function parseSliceUsages(
|
|
|
2139
2308
|
if (!purlLocationMap[apurl]) {
|
|
2140
2309
|
purlLocationMap[apurl] = new Set();
|
|
2141
2310
|
}
|
|
2142
|
-
|
|
2143
|
-
purlLocationMap[apurl].add(...lKeyOverrides[atype]);
|
|
2144
|
-
}
|
|
2311
|
+
addAllLocations(purlLocationMap[apurl], lKeyOverrides[atype]);
|
|
2145
2312
|
}
|
|
2146
2313
|
}
|
|
2147
2314
|
} else {
|
|
@@ -2149,9 +2316,7 @@ export async function parseSliceUsages(
|
|
|
2149
2316
|
if (!purlLocationMap[apurl]) {
|
|
2150
2317
|
purlLocationMap[apurl] = new Set();
|
|
2151
2318
|
}
|
|
2152
|
-
|
|
2153
|
-
purlLocationMap[apurl].add(...lKeyOverrides[atype]);
|
|
2154
|
-
}
|
|
2319
|
+
addAllLocations(purlLocationMap[apurl], lKeyOverrides[atype]);
|
|
2155
2320
|
}
|
|
2156
2321
|
}
|
|
2157
2322
|
}
|
|
@@ -2173,9 +2338,7 @@ export async function parseSliceUsages(
|
|
|
2173
2338
|
if (!purlLocationMap[ns.purl]) {
|
|
2174
2339
|
purlLocationMap[ns.purl] = new Set();
|
|
2175
2340
|
}
|
|
2176
|
-
|
|
2177
|
-
purlLocationMap[ns.purl].add(...lKeyOverrides[atype]);
|
|
2178
|
-
}
|
|
2341
|
+
addAllLocations(purlLocationMap[ns.purl], lKeyOverrides[atype]);
|
|
2179
2342
|
}
|
|
2180
2343
|
typePurlsCache[atype] = nsHits;
|
|
2181
2344
|
} else {
|
|
@@ -2231,7 +2394,7 @@ export function parseSemanticSlices(_language, components, semanticsSlice) {
|
|
|
2231
2394
|
continue;
|
|
2232
2395
|
}
|
|
2233
2396
|
for (const aline of lines) {
|
|
2234
|
-
locations.add(
|
|
2397
|
+
locations.add(occurrenceLocationKey(aswiftFile, aline));
|
|
2235
2398
|
}
|
|
2236
2399
|
}
|
|
2237
2400
|
}
|
|
@@ -2241,7 +2404,7 @@ export function parseSemanticSlices(_language, components, semanticsSlice) {
|
|
|
2241
2404
|
fileIndexes,
|
|
2242
2405
|
)) {
|
|
2243
2406
|
for (const aline of ahit.lineNumbers) {
|
|
2244
|
-
locations.add(
|
|
2407
|
+
locations.add(occurrenceLocationKey(ahit.file, aline));
|
|
2245
2408
|
}
|
|
2246
2409
|
}
|
|
2247
2410
|
}
|
|
@@ -2583,6 +2746,17 @@ export function detectServicesFromOpenAPI(
|
|
|
2583
2746
|
* @param {Array} slice Usages array for each objectSlice
|
|
2584
2747
|
* @param {Object} servicesMap Existing service map
|
|
2585
2748
|
*/
|
|
2749
|
+
/**
|
|
2750
|
+
* The text routes and endpoints are read from: JavaScript slices keep a call's source text in
|
|
2751
|
+
* `code` and the resolved callee name in `resolvedMethod`; other languages have only the latter.
|
|
2752
|
+
*
|
|
2753
|
+
* @param {Object|undefined} entry slice entry
|
|
2754
|
+
* @returns {string|undefined} text
|
|
2755
|
+
*/
|
|
2756
|
+
function sourceTextOf(entry) {
|
|
2757
|
+
return entry?.code || entry?.resolvedMethod;
|
|
2758
|
+
}
|
|
2759
|
+
|
|
2586
2760
|
export function detectServicesFromUsages(language, slice, servicesMap = {}) {
|
|
2587
2761
|
const usages = slice.usages;
|
|
2588
2762
|
if (!usages) {
|
|
@@ -2595,30 +2769,30 @@ export function detectServicesFromUsages(language, slice, servicesMap = {}) {
|
|
|
2595
2769
|
let authenticated;
|
|
2596
2770
|
if (language === "ruby" && definedBy?.name?.includes("/")) {
|
|
2597
2771
|
endpoints = extractEndpoints(language, definedBy.name);
|
|
2598
|
-
} else if (targetObj
|
|
2772
|
+
} else if (sourceTextOf(targetObj)) {
|
|
2599
2773
|
if (language !== "php") {
|
|
2600
|
-
endpoints = extractEndpoints(language, targetObj
|
|
2774
|
+
endpoints = extractEndpoints(language, sourceTextOf(targetObj));
|
|
2601
2775
|
}
|
|
2602
|
-
if (targetObj
|
|
2776
|
+
if (sourceTextOf(targetObj).toLowerCase().includes("auth")) {
|
|
2603
2777
|
authenticated = true;
|
|
2604
2778
|
}
|
|
2605
|
-
} else if (definedBy
|
|
2779
|
+
} else if (sourceTextOf(definedBy)) {
|
|
2606
2780
|
if (language !== "php") {
|
|
2607
|
-
endpoints = extractEndpoints(language, definedBy
|
|
2781
|
+
endpoints = extractEndpoints(language, sourceTextOf(definedBy));
|
|
2608
2782
|
}
|
|
2609
|
-
if (definedBy
|
|
2783
|
+
if (sourceTextOf(definedBy).toLowerCase().includes("auth")) {
|
|
2610
2784
|
authenticated = true;
|
|
2611
2785
|
}
|
|
2612
2786
|
}
|
|
2613
2787
|
if (usage.invokedCalls) {
|
|
2614
2788
|
for (const acall of usage.invokedCalls) {
|
|
2615
|
-
if (acall
|
|
2789
|
+
if (sourceTextOf(acall)) {
|
|
2616
2790
|
if (language !== "php") {
|
|
2617
2791
|
const tmpEndpoints = extractEndpoints(
|
|
2618
2792
|
language,
|
|
2619
|
-
acall
|
|
2793
|
+
sourceTextOf(acall),
|
|
2620
2794
|
);
|
|
2621
|
-
if (acall.
|
|
2795
|
+
if (sourceTextOf(acall).toLowerCase().includes("auth")) {
|
|
2622
2796
|
authenticated = true;
|
|
2623
2797
|
}
|
|
2624
2798
|
if (tmpEndpoints?.length) {
|
|
@@ -2824,12 +2998,81 @@ export function extractEndpoints(language, code) {
|
|
|
2824
2998
|
return endpoints;
|
|
2825
2999
|
}
|
|
2826
3000
|
|
|
3001
|
+
/**
|
|
3002
|
+
* Key naming the package a purl refers to, independent of its version,
|
|
3003
|
+
* qualifiers and subpath. Namespace and name are the decoded purl segments,
|
|
3004
|
+
* so an encoded scope (`%40angular/common`) keys like a plain one.
|
|
3005
|
+
*
|
|
3006
|
+
* @param {string} purl Purl string
|
|
3007
|
+
* @returns {string|undefined} `type/namespace/name`, or undefined for a non-purl
|
|
3008
|
+
*/
|
|
3009
|
+
export function versionlessPurlKey(purl) {
|
|
3010
|
+
const parts = splitPurlLoose(purl);
|
|
3011
|
+
if (!parts) {
|
|
3012
|
+
return undefined;
|
|
3013
|
+
}
|
|
3014
|
+
return `${parts.type}/${parts.namespace || ""}/${parts.name}`;
|
|
3015
|
+
}
|
|
3016
|
+
|
|
3017
|
+
/**
|
|
3018
|
+
* Index the data-flow frames recorded under versionless flow purls by
|
|
3019
|
+
* {@link versionlessPurlKey}. A flow purl without a version attaches to every
|
|
3020
|
+
* BOM component of the same package name, whatever its version (maintainer
|
|
3021
|
+
* decision, 2026-10-27); chen leaves the version off exactly when it cannot
|
|
3022
|
+
* pick one between apps that pin the package differently.
|
|
3023
|
+
*
|
|
3024
|
+
* @param {Object} dataFlowFrames Frames keyed by flow purl
|
|
3025
|
+
* @returns {Object} Frames keyed by versionless purl key
|
|
3026
|
+
*/
|
|
3027
|
+
export function indexVersionlessPurls(dataFlowFrames) {
|
|
3028
|
+
const index = {};
|
|
3029
|
+
for (const [purl, frames] of Object.entries(dataFlowFrames || {})) {
|
|
3030
|
+
if (splitPurlLoose(purl)?.version === null) {
|
|
3031
|
+
const key = versionlessPurlKey(purl);
|
|
3032
|
+
if (!key) {
|
|
3033
|
+
continue;
|
|
3034
|
+
}
|
|
3035
|
+
index[key] ??= [];
|
|
3036
|
+
index[key].push(...frames);
|
|
3037
|
+
}
|
|
3038
|
+
}
|
|
3039
|
+
return index;
|
|
3040
|
+
}
|
|
3041
|
+
|
|
3042
|
+
/**
|
|
3043
|
+
* Index the crypto generate pools recorded under versionless flow purls by
|
|
3044
|
+
* {@link versionlessPurlKey}, the crypto counterpart of
|
|
3045
|
+
* {@link indexVersionlessPurls}.
|
|
3046
|
+
*
|
|
3047
|
+
* @param {Object} cryptoGeneratePurls Algorithm bom-refs keyed by flow purl
|
|
3048
|
+
* @returns {Object} Algorithm bom-ref sets keyed by versionless purl key
|
|
3049
|
+
*/
|
|
3050
|
+
export function indexVersionlessCrypto(cryptoGeneratePurls) {
|
|
3051
|
+
const index = {};
|
|
3052
|
+
for (const [purl, algos] of Object.entries(cryptoGeneratePurls || {})) {
|
|
3053
|
+
if (splitPurlLoose(purl)?.version === null) {
|
|
3054
|
+
const key = versionlessPurlKey(purl);
|
|
3055
|
+
if (!key) {
|
|
3056
|
+
continue;
|
|
3057
|
+
}
|
|
3058
|
+
index[key] ??= new Set();
|
|
3059
|
+
for (const ref of algos || []) {
|
|
3060
|
+
index[key].add(ref);
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3063
|
+
}
|
|
3064
|
+
return index;
|
|
3065
|
+
}
|
|
3066
|
+
|
|
2827
3067
|
/**
|
|
2828
3068
|
* Method to create the SBOM with evidence file called evinse file.
|
|
2829
3069
|
*
|
|
2830
3070
|
* @param {Object} sliceArtefacts Various artefacts from the slice operation
|
|
2831
|
-
* @param {Object} options Command line options
|
|
2832
|
-
*
|
|
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
|
|
2833
3076
|
*/
|
|
2834
3077
|
export async function createEvinseFile(sliceArtefacts, options) {
|
|
2835
3078
|
const {
|
|
@@ -2839,6 +3082,8 @@ export async function createEvinseFile(sliceArtefacts, options) {
|
|
|
2839
3082
|
dataFlowSlicesFile,
|
|
2840
3083
|
reachablesSlicesFile,
|
|
2841
3084
|
purlLocationMap,
|
|
3085
|
+
purlSymbolMap = {},
|
|
3086
|
+
clearedOccurrencePurls,
|
|
2842
3087
|
servicesMap,
|
|
2843
3088
|
dataFlowFrames,
|
|
2844
3089
|
componentPropertiesMap = {},
|
|
@@ -2848,9 +3093,13 @@ export async function createEvinseFile(sliceArtefacts, options) {
|
|
|
2848
3093
|
cryptoGeneratePurls,
|
|
2849
3094
|
workspaceComponent,
|
|
2850
3095
|
} = sliceArtefacts;
|
|
2851
|
-
const bomFile = options.input;
|
|
2852
3096
|
const evinseOutFile = options.output;
|
|
2853
|
-
|
|
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);
|
|
2854
3103
|
const components = bomJson.components || [];
|
|
2855
3104
|
// Clear existing annotations
|
|
2856
3105
|
bomJson.annotations = [];
|
|
@@ -2875,27 +3124,62 @@ export async function createEvinseFile(sliceArtefacts, options) {
|
|
|
2875
3124
|
let servicesPresent = false;
|
|
2876
3125
|
const propertyEvidencePresent =
|
|
2877
3126
|
metadataProperties.length || Object.keys(componentPropertiesMap).length;
|
|
3127
|
+
// Versionless flow purls attach to every same-name component (see
|
|
3128
|
+
// indexVersionlessPurls), so look both ways: exact purl first, then the
|
|
3129
|
+
// versionless index.
|
|
3130
|
+
const versionlessFrames = indexVersionlessPurls(dataFlowFrames);
|
|
3131
|
+
const versionlessCrypto = indexVersionlessCrypto(cryptoGeneratePurls);
|
|
3132
|
+
const depCryptoPurls =
|
|
3133
|
+
Object.keys(versionlessCrypto).length && cryptoGeneratePurls
|
|
3134
|
+
? { ...cryptoGeneratePurls }
|
|
3135
|
+
: cryptoGeneratePurls;
|
|
2878
3136
|
for (const comp of [bomJson.metadata?.component, ...components]) {
|
|
2879
3137
|
if (!comp?.purl) {
|
|
2880
3138
|
continue;
|
|
2881
3139
|
}
|
|
2882
3140
|
delete comp.signature;
|
|
2883
3141
|
appendComponentProperties(comp, componentPropertiesMap[comp.purl]);
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
).
|
|
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
|
+
);
|
|
2887
3150
|
if (locationOccurrences.length) {
|
|
2888
3151
|
if (!comp.evidence) {
|
|
2889
3152
|
comp.evidence = {};
|
|
2890
3153
|
}
|
|
2891
3154
|
// This step would replace any existing occurrences
|
|
2892
3155
|
// This is fine as long as the input sbom was also generated by cdxgen
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
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
|
+
);
|
|
2896
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
|
+
}
|
|
3176
|
+
}
|
|
3177
|
+
const compKey = versionlessPurlKey(comp.purl);
|
|
3178
|
+
let dfFrames = dataFlowFrames[comp.purl];
|
|
3179
|
+
const extraFrames = compKey ? versionlessFrames[compKey] : undefined;
|
|
3180
|
+
if (extraFrames?.length) {
|
|
3181
|
+
dfFrames = [...(dfFrames || []), ...extraFrames];
|
|
2897
3182
|
}
|
|
2898
|
-
const dfFrames = dataFlowFrames[comp.purl];
|
|
2899
3183
|
if (dfFrames?.length) {
|
|
2900
3184
|
if (!comp.evidence) {
|
|
2901
3185
|
comp.evidence = {};
|
|
@@ -2909,10 +3193,20 @@ export async function createEvinseFile(sliceArtefacts, options) {
|
|
|
2909
3193
|
}
|
|
2910
3194
|
}
|
|
2911
3195
|
// Add crypto tags if this purl offers any generation algorithm
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
3196
|
+
let compCryptoPurls = cryptoGeneratePurls?.[comp.purl];
|
|
3197
|
+
const extraCrypto = compKey ? versionlessCrypto[compKey] : undefined;
|
|
3198
|
+
if (extraCrypto?.size) {
|
|
3199
|
+
compCryptoPurls = new Set([...(compCryptoPurls || []), ...extraCrypto]);
|
|
3200
|
+
// The provides section below looks pool entries up by bom-ref, so the
|
|
3201
|
+
// merged set has to be reachable that way too.
|
|
3202
|
+
if (depCryptoPurls) {
|
|
3203
|
+
depCryptoPurls[comp.purl] = compCryptoPurls;
|
|
3204
|
+
if (comp["bom-ref"]) {
|
|
3205
|
+
depCryptoPurls[comp["bom-ref"]] = compCryptoPurls;
|
|
3206
|
+
}
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
3209
|
+
if (compCryptoPurls && Array.from(compCryptoPurls).length) {
|
|
2916
3210
|
comp.tags = ["crypto", "crypto-generate"];
|
|
2917
3211
|
}
|
|
2918
3212
|
} // for
|
|
@@ -2944,14 +3238,14 @@ export async function createEvinseFile(sliceArtefacts, options) {
|
|
|
2944
3238
|
}
|
|
2945
3239
|
// Fix the dependencies section with provides information
|
|
2946
3240
|
if (
|
|
2947
|
-
|
|
2948
|
-
Object.keys(
|
|
3241
|
+
depCryptoPurls &&
|
|
3242
|
+
Object.keys(depCryptoPurls).length &&
|
|
2949
3243
|
bomJson.dependencies
|
|
2950
3244
|
) {
|
|
2951
3245
|
const newDependencies = [];
|
|
2952
3246
|
for (const depObj of bomJson.dependencies) {
|
|
2953
|
-
if (depObj.ref &&
|
|
2954
|
-
const providedAlgos = Array.from(
|
|
3247
|
+
if (depObj.ref && depCryptoPurls[depObj.ref]) {
|
|
3248
|
+
const providedAlgos = Array.from(depCryptoPurls[depObj.ref]);
|
|
2955
3249
|
if (providedAlgos.length) {
|
|
2956
3250
|
depObj.provides = providedAlgos;
|
|
2957
3251
|
}
|
|
@@ -2993,9 +3287,10 @@ export async function createEvinseFile(sliceArtefacts, options) {
|
|
|
2993
3287
|
// Redo post-processing with evinse data. `executeOsQuery` has to be injected
|
|
2994
3288
|
// because postgen no longer imports it directly (stages is below managers);
|
|
2995
3289
|
// without it the crypto formulation path receives `undefined` and throws.
|
|
3290
|
+
const { inputBomJson: _inputBomJson, ...postProcessOptions } = options;
|
|
2996
3291
|
const bomNSData = await postProcess(
|
|
2997
3292
|
{ bomJson },
|
|
2998
|
-
{ ...
|
|
3293
|
+
{ ...postProcessOptions, executeOsQuery },
|
|
2999
3294
|
);
|
|
3000
3295
|
safeWriteSync(
|
|
3001
3296
|
evinseOutFile,
|
|
@@ -3036,6 +3331,9 @@ export async function createEvinseFile(sliceArtefacts, options) {
|
|
|
3036
3331
|
if (tempDirOwned && tempDir?.startsWith(getTmpDir())) {
|
|
3037
3332
|
safeRmSync(tempDir, { recursive: true, force: true });
|
|
3038
3333
|
}
|
|
3334
|
+
if (options.returnInternalModel && bomNSData?.internalBomJson) {
|
|
3335
|
+
return bomNSData.internalBomJson;
|
|
3336
|
+
}
|
|
3039
3337
|
return bomNSData?.bomJson;
|
|
3040
3338
|
}
|
|
3041
3339
|
|
|
@@ -3196,15 +3494,15 @@ export function collectReachableFrames(_language, reachablesSlice) {
|
|
|
3196
3494
|
if (tagStr.includes("crypto")) {
|
|
3197
3495
|
isCryptoFlow = true;
|
|
3198
3496
|
}
|
|
3497
|
+
// Join with a space: findCryptoAlgos treats every non-alphanumeric as a
|
|
3498
|
+
// word separator, and a space is the one separator cleanStr keeps.
|
|
3199
3499
|
if (isCryptoFlow && fnode.code) {
|
|
3200
|
-
codeSnippets = `${codeSnippets}
|
|
3500
|
+
codeSnippets = `${codeSnippets} ${fnode.code}`;
|
|
3201
3501
|
}
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
fnode.code.includes("sign")
|
|
3207
|
-
) {
|
|
3502
|
+
// Pool membership is tag-driven: a node contributes its pkg: tags only
|
|
3503
|
+
// when the tagger marked it crypto-generate. Code substring tests read
|
|
3504
|
+
// assign/design/signup into encrypt/decrypt/sign and are gone.
|
|
3505
|
+
if (tagStr.includes("crypto-generate")) {
|
|
3208
3506
|
cpurls = tagStr.split(", ").filter((t) => t.startsWith("pkg:"));
|
|
3209
3507
|
for (const cpurl of cpurls) {
|
|
3210
3508
|
if (!cryptoGeneratePurls[cpurl]) {
|
|
@@ -3407,9 +3705,176 @@ export function getClassTypeFromSignature(language, typeFullName) {
|
|
|
3407
3705
|
return simplifyType(typeFullName);
|
|
3408
3706
|
}
|
|
3409
3707
|
|
|
3708
|
+
const JS_LANGUAGES = ["javascript", "js", "ts", "typescript"];
|
|
3709
|
+
|
|
3710
|
+
/**
|
|
3711
|
+
* Add every location of a set to a purl's locations (Set.add takes a single value).
|
|
3712
|
+
*
|
|
3713
|
+
* @param {Set<string>} target purl location set
|
|
3714
|
+
* @param {Set<string>|undefined} locations locations to add
|
|
3715
|
+
*/
|
|
3716
|
+
function addAllLocations(target, locations) {
|
|
3717
|
+
for (const l of locations || []) {
|
|
3718
|
+
target.add(l);
|
|
3719
|
+
}
|
|
3720
|
+
}
|
|
3721
|
+
|
|
3722
|
+
/**
|
|
3723
|
+
* npm package name of a JavaScript module specifier: `@scope/pkg/sub` -> `@scope/pkg`,
|
|
3724
|
+
* `lodash/fp` -> `lodash`. Node.js builtins (`node:fs`, `fs`) are not packages.
|
|
3725
|
+
*
|
|
3726
|
+
* @param {string} module module specifier
|
|
3727
|
+
* @returns {string|undefined} package name
|
|
3728
|
+
*/
|
|
3729
|
+
export function jsPackageOfModule(module) {
|
|
3730
|
+
if (!module || module.startsWith("node:") || module.startsWith(".")) {
|
|
3731
|
+
return undefined;
|
|
3732
|
+
}
|
|
3733
|
+
const parts = module.split("/");
|
|
3734
|
+
const pkg = module.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0];
|
|
3735
|
+
if (isNodeBuiltin(pkg)) {
|
|
3736
|
+
return undefined;
|
|
3737
|
+
}
|
|
3738
|
+
return pkg;
|
|
3739
|
+
}
|
|
3740
|
+
|
|
3741
|
+
function isNodeBuiltin(name) {
|
|
3742
|
+
return NODE_BUILTINS.has(name);
|
|
3743
|
+
}
|
|
3744
|
+
|
|
3745
|
+
const NODE_BUILTINS = new Set(builtinModules);
|
|
3746
|
+
|
|
3747
|
+
const jsPurlsByPackageCache = new WeakMap();
|
|
3748
|
+
|
|
3749
|
+
function jsPurlsByPackage(purlImportsMap) {
|
|
3750
|
+
let byPackage = jsPurlsByPackageCache.get(purlImportsMap);
|
|
3751
|
+
if (!byPackage) {
|
|
3752
|
+
byPackage = {};
|
|
3753
|
+
for (const apurl of Object.keys(purlImportsMap || {})) {
|
|
3754
|
+
try {
|
|
3755
|
+
const purlObj = Purl.parse(apurl);
|
|
3756
|
+
if (purlObj.type !== "npm") {
|
|
3757
|
+
continue;
|
|
3758
|
+
}
|
|
3759
|
+
const name = purlObj.namespace
|
|
3760
|
+
? `${purlObj.namespace}/${purlObj.name}`
|
|
3761
|
+
: purlObj.name;
|
|
3762
|
+
(byPackage[name] = byPackage[name] || []).push(apurl);
|
|
3763
|
+
} catch (_e) {
|
|
3764
|
+
// not a valid purl
|
|
3765
|
+
}
|
|
3766
|
+
}
|
|
3767
|
+
jsPurlsByPackageCache.set(purlImportsMap, byPackage);
|
|
3768
|
+
}
|
|
3769
|
+
return byPackage;
|
|
3770
|
+
}
|
|
3771
|
+
|
|
3772
|
+
/**
|
|
3773
|
+
* Attribute a JavaScript usage's module-qualified entries to the packages providing them.
|
|
3774
|
+
*
|
|
3775
|
+
* Each call entry with a `module` is a use of that module's package at the call's line. When
|
|
3776
|
+
* the slice's object is an import binding (defined by a require/import naming the module),
|
|
3777
|
+
* every call the object takes part in is a use of that package too.
|
|
3778
|
+
*
|
|
3779
|
+
* @param {Object} ausage usage entry of an object slice
|
|
3780
|
+
* @param {string} fileName file of the slice
|
|
3781
|
+
* @param {Object} purlImportsMap purl -> imported modules and symbols
|
|
3782
|
+
* @param {Object} purlLocationMap purl -> Set of `file#line`
|
|
3783
|
+
*/
|
|
3784
|
+
export function attributeJsModules(
|
|
3785
|
+
ausage,
|
|
3786
|
+
fileName,
|
|
3787
|
+
purlImportsMap,
|
|
3788
|
+
purlLocationMap,
|
|
3789
|
+
) {
|
|
3790
|
+
const byPackage = jsPurlsByPackage(purlImportsMap);
|
|
3791
|
+
const record = (module, line) => {
|
|
3792
|
+
const pkg = jsPackageOfModule(module);
|
|
3793
|
+
if (!pkg || !line) {
|
|
3794
|
+
return;
|
|
3795
|
+
}
|
|
3796
|
+
for (const apurl of byPackage[pkg] || []) {
|
|
3797
|
+
if (!purlLocationMap[apurl]) {
|
|
3798
|
+
purlLocationMap[apurl] = new Set();
|
|
3799
|
+
}
|
|
3800
|
+
purlLocationMap[apurl].add(occurrenceLocationKey(fileName, line));
|
|
3801
|
+
}
|
|
3802
|
+
};
|
|
3803
|
+
const calls = []
|
|
3804
|
+
.concat(ausage?.invokedCalls || [])
|
|
3805
|
+
.concat(ausage?.argToCalls || []);
|
|
3806
|
+
for (const c of [ausage?.targetObj, ausage?.definedBy, ...calls]) {
|
|
3807
|
+
if (c?.module) {
|
|
3808
|
+
record(c.module, c.lineNumber);
|
|
3809
|
+
}
|
|
3810
|
+
}
|
|
3811
|
+
// Only an import binding stands for its module: a value merely returned by a package's call
|
|
3812
|
+
// (`const s = purl.toString()`) is not a use of that package wherever it goes.
|
|
3813
|
+
const definedBy = ausage?.definedBy;
|
|
3814
|
+
const isImportBinding =
|
|
3815
|
+
definedBy?.name === "require" ||
|
|
3816
|
+
definedBy?.name === "import" ||
|
|
3817
|
+
/^(require|import)\(/.test(definedBy?.code || "");
|
|
3818
|
+
const bindingModule = isImportBinding ? definedBy?.module : undefined;
|
|
3819
|
+
if (bindingModule) {
|
|
3820
|
+
for (const c of calls) {
|
|
3821
|
+
record(bindingModule, c?.lineNumber);
|
|
3822
|
+
}
|
|
3823
|
+
}
|
|
3824
|
+
}
|
|
3825
|
+
|
|
3826
|
+
/**
|
|
3827
|
+
* Attribute a callsites slice's records to the packages providing their modules.
|
|
3828
|
+
*
|
|
3829
|
+
* Each record of `{"callsites": [...]}` (atom `callsites` command) is one external use site
|
|
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`.
|
|
3836
|
+
*
|
|
3837
|
+
* @param {Object} callsitesSlice callsites slice with a `callsites` array
|
|
3838
|
+
* @param {Object} purlImportsMap purl -> imported modules and symbols
|
|
3839
|
+
* @param {Object} purlLocationMap purl -> Set of occurrence keys
|
|
3840
|
+
* @param {Object} [purlSymbolMap] purl -> occurrence key -> symbol, filled in place
|
|
3841
|
+
*/
|
|
3842
|
+
export function attributeJsCallsites(
|
|
3843
|
+
callsitesSlice,
|
|
3844
|
+
purlImportsMap,
|
|
3845
|
+
purlLocationMap,
|
|
3846
|
+
purlSymbolMap = {},
|
|
3847
|
+
) {
|
|
3848
|
+
const byPackage = jsPurlsByPackage(purlImportsMap);
|
|
3849
|
+
for (const record of callsitesSlice?.callsites || []) {
|
|
3850
|
+
const pkg = jsPackageOfModule(record?.module);
|
|
3851
|
+
if (!pkg || !(record.line > 0)) {
|
|
3852
|
+
continue;
|
|
3853
|
+
}
|
|
3854
|
+
const key = occurrenceLocationKey(record.file, record.line, record.col);
|
|
3855
|
+
const symbol = record.path
|
|
3856
|
+
? `${record.module}.${record.path}`
|
|
3857
|
+
: record.module;
|
|
3858
|
+
for (const apurl of byPackage[pkg] || []) {
|
|
3859
|
+
if (!purlLocationMap[apurl]) {
|
|
3860
|
+
purlLocationMap[apurl] = new Set();
|
|
3861
|
+
}
|
|
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
|
+
}
|
|
3871
|
+
}
|
|
3872
|
+
}
|
|
3873
|
+
}
|
|
3874
|
+
|
|
3410
3875
|
function addToOverrides(lKeyOverrides, atype, fileName, ausageLineNumber) {
|
|
3411
3876
|
if (!lKeyOverrides[atype]) {
|
|
3412
3877
|
lKeyOverrides[atype] = new Set();
|
|
3413
3878
|
}
|
|
3414
|
-
lKeyOverrides[atype].add(
|
|
3879
|
+
lKeyOverrides[atype].add(occurrenceLocationKey(fileName, ausageLineNumber));
|
|
3415
3880
|
}
|