@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
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { platform } from "node:os";
|
|
2
|
-
import { extname } from "node:path";
|
|
2
|
+
import { basename, dirname, extname } from "node:path";
|
|
3
3
|
|
|
4
4
|
import { build } from "@cdxgen/cdx-purl";
|
|
5
5
|
|
|
6
6
|
import { safeExistsSync } from "../core/fs.js";
|
|
7
7
|
import { thoughtLog } from "../core/logger.js";
|
|
8
|
+
import { sanitizeBomUrl } from "../core/propertySanitizer.js";
|
|
9
|
+
import { vscodeExtensionPlatformFromPath } from "../parsers/vscodeExtensionId.js";
|
|
10
|
+
import { isAgentSkillSpecValid } from "./agentSkillSpec.js";
|
|
8
11
|
import {
|
|
9
12
|
addDosaiReachabilityProperties,
|
|
10
13
|
readDosaiMethodsReport,
|
|
@@ -20,16 +23,26 @@ import {
|
|
|
20
23
|
} from "./dosaiParsers.js";
|
|
21
24
|
import { createGtfoBinsPropertiesFromRow } from "./gtfobins.js";
|
|
22
25
|
import { createLolbasProperties } from "./lolbas.js";
|
|
26
|
+
import { isOsPackagePurlType } from "./osPackagePurl.js";
|
|
23
27
|
import {
|
|
24
28
|
createOsQueryFallbackBomRef,
|
|
29
|
+
createOsQueryPackageIdentity,
|
|
25
30
|
createOsQueryPurl,
|
|
26
31
|
deriveOsQueryDescription,
|
|
27
32
|
deriveOsQueryName,
|
|
28
33
|
deriveOsQueryPublisher,
|
|
29
34
|
deriveOsQueryVersion,
|
|
35
|
+
OS_PACKAGE_LICENSE_COLUMNS,
|
|
36
|
+
osPackagePropertyName,
|
|
30
37
|
sanitizeOsQueryIdentity,
|
|
31
38
|
shouldCreateOsQueryPurl,
|
|
32
39
|
} from "./osqueryTransform.js";
|
|
40
|
+
import {
|
|
41
|
+
adjustLicenseInformation,
|
|
42
|
+
findLicenseId,
|
|
43
|
+
isSpdxLicenseExpression,
|
|
44
|
+
spdxLicenses,
|
|
45
|
+
} from "./spdx.js";
|
|
33
46
|
|
|
34
47
|
/**
|
|
35
48
|
* Build a CycloneDX occurrence evidence object from a location string.
|
|
@@ -540,20 +553,55 @@ export function convertOSQueryResults(
|
|
|
540
553
|
enhance = false,
|
|
541
554
|
osPackageListers = undefined,
|
|
542
555
|
) {
|
|
556
|
+
if (queryCategory === "ai_agent_skills") {
|
|
557
|
+
return convertAgentSkillRows(results);
|
|
558
|
+
}
|
|
543
559
|
const pkgList = [];
|
|
560
|
+
// deb, rpm, apk, pacman, Homebrew, MacPorts, snap and flatpak rows describe a
|
|
561
|
+
// package, not a file or an app: the package manager names them, and nothing
|
|
562
|
+
// in such a row is a path inside the package.
|
|
563
|
+
const isOsPackage = isOsPackagePurlType(queryObj.purlType);
|
|
564
|
+
const isLocatedPackage =
|
|
565
|
+
LOCATED_PACKAGE_PURL_TYPES.has(queryObj.purlType) ||
|
|
566
|
+
LOCATED_PACKAGE_CATEGORIES.has(queryCategory);
|
|
544
567
|
if (results?.length) {
|
|
545
568
|
for (const res of results) {
|
|
546
|
-
|
|
569
|
+
let version = deriveOsQueryVersion(res);
|
|
547
570
|
let name = deriveOsQueryName(res, results.length === 1, queryObj.name);
|
|
548
571
|
if (queryObj.purlType === "chrome-extension") {
|
|
549
|
-
|
|
572
|
+
// osquery computes identifier from the manifest key, which an
|
|
573
|
+
// unpacked extension may lack; the profile still knows its ID.
|
|
574
|
+
name = (
|
|
575
|
+
res.identifier ||
|
|
576
|
+
res.referenced_identifier ||
|
|
577
|
+
res.extension_id ||
|
|
578
|
+
name ||
|
|
579
|
+
""
|
|
580
|
+
).toLowerCase();
|
|
581
|
+
}
|
|
582
|
+
let npmScope;
|
|
583
|
+
if (queryObj.purlType === "npm") {
|
|
584
|
+
const scoped = /^@([^/]+)\/(.+)$/.exec(`${name || ""}`);
|
|
585
|
+
if (scoped) {
|
|
586
|
+
[, npmScope, name] = scoped;
|
|
587
|
+
}
|
|
550
588
|
}
|
|
551
589
|
let group = "";
|
|
552
|
-
const subpath =
|
|
590
|
+
const subpath =
|
|
591
|
+
isOsPackage || isLocatedPackage
|
|
592
|
+
? undefined
|
|
593
|
+
: res.path || res.admindir || res.source;
|
|
553
594
|
const publisher = deriveOsQueryPublisher(res);
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
595
|
+
if (queryObj.purlType === "vscode-extension") {
|
|
596
|
+
// osquery names a VS Code extension by its ID, <publisher>.<name>,
|
|
597
|
+
// which the vscode-extension type splits into namespace and name. Its
|
|
598
|
+
// publisher column is the publisher's display name.
|
|
599
|
+
const extensionId = /^([^.]+)\.(.+)$/u.exec(`${name || ""}`);
|
|
600
|
+
if (extensionId) {
|
|
601
|
+
[, group, name] = extensionId;
|
|
602
|
+
} else if (publisher) {
|
|
603
|
+
group = publisher.toLowerCase();
|
|
604
|
+
}
|
|
557
605
|
}
|
|
558
606
|
let scope;
|
|
559
607
|
const compScope = res.priority;
|
|
@@ -568,25 +616,60 @@ export function convertOSQueryResults(
|
|
|
568
616
|
qualifiers = {
|
|
569
617
|
tag_id: res.identifying_number.replace("{", "").replace("}", ""),
|
|
570
618
|
};
|
|
619
|
+
} else if (queryCategory === "firefox_addons" && res.identifier) {
|
|
620
|
+
// The profile an add-on was found in is not part of its purl, so the
|
|
621
|
+
// add-on ID is what tells apart two add-ons that share a name.
|
|
622
|
+
qualifiers = { tag_id: firefoxAddonTagId(res.identifier) };
|
|
623
|
+
} else if (queryObj.purlType === "vscode-extension") {
|
|
624
|
+
// A platform-specific build is a different artefact from the one for
|
|
625
|
+
// another platform, and the extension scan names it the same way.
|
|
626
|
+
const targetPlatform = vscodeExtensionPlatformFromPath(res.path);
|
|
627
|
+
if (targetPlatform) {
|
|
628
|
+
qualifiers = { platform: targetPlatform };
|
|
629
|
+
}
|
|
571
630
|
}
|
|
572
631
|
if (name) {
|
|
573
632
|
name = sanitizeOsQueryIdentity(name);
|
|
574
|
-
|
|
633
|
+
// sanitizeOsQueryIdentity drops a leading @, which an npm scope keeps.
|
|
634
|
+
group = npmScope
|
|
635
|
+
? `@${sanitizeOsQueryIdentity(npmScope)}`
|
|
636
|
+
: sanitizeOsQueryIdentity(group);
|
|
575
637
|
const isCryptoAsset = queryObj.componentType === "cryptographic-asset";
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
638
|
+
let purl;
|
|
639
|
+
if (isOsPackage) {
|
|
640
|
+
// The version of such a row is part of its identity: an rpm's is its
|
|
641
|
+
// version and release, behind its epoch.
|
|
642
|
+
const identity = createOsQueryPackageIdentity(
|
|
643
|
+
queryObj.purlType,
|
|
644
|
+
res,
|
|
645
|
+
name,
|
|
646
|
+
);
|
|
647
|
+
version = identity.version;
|
|
648
|
+
purl = shouldCreateOsQueryPurl(queryObj.componentType)
|
|
649
|
+
? identity.purl
|
|
650
|
+
: undefined;
|
|
651
|
+
} else if (shouldCreateOsQueryPurl(queryObj.componentType)) {
|
|
652
|
+
purl = createOsQueryPurl(
|
|
653
|
+
queryObj.purlType,
|
|
654
|
+
group,
|
|
655
|
+
name,
|
|
656
|
+
version,
|
|
657
|
+
qualifiers,
|
|
658
|
+
subpath,
|
|
659
|
+
);
|
|
660
|
+
}
|
|
586
661
|
const props = [{ name: "cdx:osquery:category", value: queryCategory }];
|
|
587
662
|
props.push(...createLolbasProperties(queryCategory, res));
|
|
588
663
|
if (platform() === "linux") {
|
|
589
|
-
|
|
664
|
+
// The GTFOBins matcher reads a description for command names, and
|
|
665
|
+
// the free text of a package's description matches by chance: zlib's
|
|
666
|
+
// "the deflate compression method found in gzip" is no use of gzip.
|
|
667
|
+
props.push(
|
|
668
|
+
...createGtfoBinsPropertiesFromRow(
|
|
669
|
+
queryCategory,
|
|
670
|
+
isOsPackage ? { ...res, description: undefined } : res,
|
|
671
|
+
),
|
|
672
|
+
);
|
|
590
673
|
}
|
|
591
674
|
let providesList;
|
|
592
675
|
if (enhance) {
|
|
@@ -620,10 +703,12 @@ export function convertOSQueryResults(
|
|
|
620
703
|
version: version || "",
|
|
621
704
|
description,
|
|
622
705
|
publisher,
|
|
706
|
+
// Without a purl, the bom-ref is built from the row, and the path of
|
|
707
|
+
// a located package is not part of what it is.
|
|
623
708
|
"bom-ref": createOsQueryBomRef(
|
|
624
709
|
queryCategory,
|
|
625
710
|
queryObj.componentType,
|
|
626
|
-
res,
|
|
711
|
+
isLocatedPackage ? { ...res, path: undefined } : res,
|
|
627
712
|
name,
|
|
628
713
|
version,
|
|
629
714
|
purl,
|
|
@@ -644,25 +729,364 @@ export function convertOSQueryResults(
|
|
|
644
729
|
if (cryptoProperties) {
|
|
645
730
|
apkg.cryptoProperties = cryptoProperties;
|
|
646
731
|
}
|
|
732
|
+
const licenseColumns = isOsPackage
|
|
733
|
+
? OS_PACKAGE_LICENSE_COLUMNS[queryObj.purlType]
|
|
734
|
+
: undefined;
|
|
735
|
+
if (licenseColumns) {
|
|
736
|
+
const licenses = osPackageLicenses(
|
|
737
|
+
res[licenseColumns.license],
|
|
738
|
+
licenseColumns.separator,
|
|
739
|
+
);
|
|
740
|
+
if (licenses.length) {
|
|
741
|
+
apkg.licenses = licenses;
|
|
742
|
+
}
|
|
743
|
+
const website = licenseColumns.website
|
|
744
|
+
? osPackageWebsite(res[licenseColumns.website])
|
|
745
|
+
: undefined;
|
|
746
|
+
if (website) {
|
|
747
|
+
apkg.externalReferences = [{ type: "website", url: website }];
|
|
748
|
+
}
|
|
749
|
+
}
|
|
647
750
|
for (const k of Object.keys(res).filter((p) => {
|
|
648
751
|
if (["version", "description", "publisher"].includes(p)) {
|
|
649
752
|
return false;
|
|
650
753
|
}
|
|
651
754
|
return !(queryObj.purlType !== "chrome-extension" && p === "name");
|
|
652
755
|
})) {
|
|
653
|
-
|
|
756
|
+
const propertyName = isOsPackage
|
|
757
|
+
? osPackagePropertyName(queryObj.purlType, k)
|
|
758
|
+
: k;
|
|
759
|
+
// (none) is what rpm's database says for a tag a record lacks, such
|
|
760
|
+
// as the source rpm of a key record.
|
|
761
|
+
if (
|
|
762
|
+
propertyName &&
|
|
763
|
+
res[k] &&
|
|
764
|
+
res[k] !== "null" &&
|
|
765
|
+
!(isOsPackage && res[k] === "(none)")
|
|
766
|
+
) {
|
|
654
767
|
props.push({
|
|
655
|
-
name:
|
|
768
|
+
name: propertyName,
|
|
656
769
|
value: res[k],
|
|
657
770
|
});
|
|
658
771
|
}
|
|
659
772
|
}
|
|
773
|
+
if (isLocatedPackage) {
|
|
774
|
+
// osquery writes a JSON null as "null", which is the path Firefox
|
|
775
|
+
// gives its default theme.
|
|
776
|
+
const occurrence = createOccurrenceEvidence(
|
|
777
|
+
[res.path, res.directory].find(
|
|
778
|
+
(value) => value && value !== "null",
|
|
779
|
+
),
|
|
780
|
+
);
|
|
781
|
+
if (occurrence) {
|
|
782
|
+
apkg.evidence = { occurrences: [occurrence] };
|
|
783
|
+
}
|
|
784
|
+
}
|
|
660
785
|
apkg.properties = props;
|
|
661
786
|
pkgList.push(apkg);
|
|
662
787
|
}
|
|
663
788
|
}
|
|
664
789
|
}
|
|
665
|
-
return pkgList;
|
|
790
|
+
return isLocatedPackage ? mergeLocatedPackages(pkgList) : pkgList;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// npm and pypi rows name a package found at a path on the host, Chromium and
|
|
794
|
+
// VS Code extension rows an extension found in a browser profile or an editor's
|
|
795
|
+
// extensions folder, and a firefox_addons row an add-on found in a Firefox
|
|
796
|
+
// profile. Where it was found is not part of what it is, so the path is an
|
|
797
|
+
// occurrence rather than a subpath: a home directory stays out of the purl, and
|
|
798
|
+
// one package installed in two places is one component with two occurrences.
|
|
799
|
+
// Chocolatey rows carry the absolute path of the package's .nuspec, which a
|
|
800
|
+
// purl subpath cannot hold, so it is kept as an occurrence like these.
|
|
801
|
+
const LOCATED_PACKAGE_PURL_TYPES = new Set([
|
|
802
|
+
"npm",
|
|
803
|
+
"pypi",
|
|
804
|
+
"chrome-extension",
|
|
805
|
+
"vscode-extension",
|
|
806
|
+
"chocolatey",
|
|
807
|
+
]);
|
|
808
|
+
const LOCATED_PACKAGE_CATEGORIES = new Set(["firefox_addons"]);
|
|
809
|
+
|
|
810
|
+
const FIREFOX_ADDON_GUID =
|
|
811
|
+
/^\{([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\}$/iu;
|
|
812
|
+
|
|
813
|
+
// A Firefox add-on ID is an email-like string or a GUID in braces. A swid
|
|
814
|
+
// tag_id that is a GUID must be in lower case, and is written without braces.
|
|
815
|
+
function firefoxAddonTagId(identifier) {
|
|
816
|
+
const id = `${identifier}`.trim();
|
|
817
|
+
const guid = FIREFOX_ADDON_GUID.exec(id);
|
|
818
|
+
return guid ? guid[1].toLowerCase() : id;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
function mergeLocatedPackages(components) {
|
|
822
|
+
const byRef = new Map();
|
|
823
|
+
for (const component of components) {
|
|
824
|
+
const existing = byRef.get(component["bom-ref"]);
|
|
825
|
+
if (!existing) {
|
|
826
|
+
byRef.set(component["bom-ref"], component);
|
|
827
|
+
continue;
|
|
828
|
+
}
|
|
829
|
+
const occurrences = existing.evidence?.occurrences || [];
|
|
830
|
+
for (const occurrence of component.evidence?.occurrences || []) {
|
|
831
|
+
if (
|
|
832
|
+
!occurrences.some((known) => known.location === occurrence.location)
|
|
833
|
+
) {
|
|
834
|
+
occurrences.push(occurrence);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
if (occurrences.length) {
|
|
838
|
+
existing.evidence = { ...existing.evidence, occurrences };
|
|
839
|
+
}
|
|
840
|
+
for (const property of component.properties) {
|
|
841
|
+
if (
|
|
842
|
+
!existing.properties.some(
|
|
843
|
+
(known) =>
|
|
844
|
+
known.name === property.name && known.value === property.value,
|
|
845
|
+
)
|
|
846
|
+
) {
|
|
847
|
+
existing.properties.push(property);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
for (const component of byRef.values()) {
|
|
852
|
+
component.evidence?.occurrences?.sort((a, b) =>
|
|
853
|
+
a.location.localeCompare(b.location),
|
|
854
|
+
);
|
|
855
|
+
}
|
|
856
|
+
return [...byRef.values()];
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// The licence an OS package declares. An apk `L:` field is a list of SPDX ids
|
|
860
|
+
// separated by spaces on older packages and an SPDX expression on newer ones;
|
|
861
|
+
// pacman keeps one licence per entry, each an SPDX id or expression, which
|
|
862
|
+
// osquery joins with commas; a snap or a flatpak declares one expression. Only
|
|
863
|
+
// an explicit operator or a parenthesis makes a text an expression: the SPDX
|
|
864
|
+
// parser's own check takes the `or` inside `LGPL-2.0-or-later` for an operator,
|
|
865
|
+
// and would turn a plain list into one expression. A token is an id only when
|
|
866
|
+
// the SPDX list has what it maps to; any other is kept by name rather than
|
|
867
|
+
// dropped.
|
|
868
|
+
const MAX_OS_PACKAGE_LICENSES = 64;
|
|
869
|
+
const SPDX_OPERATORS = new Set(["AND", "OR", "WITH"]);
|
|
870
|
+
|
|
871
|
+
function isExplicitSpdxExpression(text) {
|
|
872
|
+
// No SPDX expression holds a comma, but the expression parser lets one
|
|
873
|
+
// through, which would take a whole pacman licence list for one expression.
|
|
874
|
+
return (
|
|
875
|
+
!text.includes(",") &&
|
|
876
|
+
(text.split(/\s+/u).some((token) => SPDX_OPERATORS.has(token)) ||
|
|
877
|
+
text.includes("(")) &&
|
|
878
|
+
isSpdxLicenseExpression(text)
|
|
879
|
+
);
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
function osPackageLicenses(text, separator) {
|
|
883
|
+
const license = `${text ?? ""}`.split(/\s+/u).filter(Boolean).join(" ");
|
|
884
|
+
if (!license) {
|
|
885
|
+
return [];
|
|
886
|
+
}
|
|
887
|
+
if (isExplicitSpdxExpression(license)) {
|
|
888
|
+
return [{ expression: license }];
|
|
889
|
+
}
|
|
890
|
+
const entries = [];
|
|
891
|
+
const items = separator ? license.split(separator) : [license];
|
|
892
|
+
for (const item of items.slice(0, MAX_OS_PACKAGE_LICENSES)) {
|
|
893
|
+
const entry = item.trim();
|
|
894
|
+
if (!entry || SPDX_OPERATORS.has(entry)) {
|
|
895
|
+
continue;
|
|
896
|
+
}
|
|
897
|
+
if (isExplicitSpdxExpression(entry)) {
|
|
898
|
+
entries.push({ expression: entry });
|
|
899
|
+
continue;
|
|
900
|
+
}
|
|
901
|
+
const id = findLicenseId(entry);
|
|
902
|
+
entries.push(
|
|
903
|
+
spdxLicenses.includes(id)
|
|
904
|
+
? { license: { id } }
|
|
905
|
+
: { license: { name: entry } },
|
|
906
|
+
);
|
|
907
|
+
}
|
|
908
|
+
// A list that holds an expression must become one expression, since a
|
|
909
|
+
// component's licenses are either licences or a single expression. Every
|
|
910
|
+
// entry applies, so they are joined with AND.
|
|
911
|
+
if (entries.length > 1 && entries.some((entry) => entry.expression)) {
|
|
912
|
+
const parts = entries.map((entry) =>
|
|
913
|
+
entry.expression ? `(${entry.expression})` : entry.license.id,
|
|
914
|
+
);
|
|
915
|
+
const expression = parts.join(" AND ");
|
|
916
|
+
if (parts.every(Boolean) && isSpdxLicenseExpression(expression)) {
|
|
917
|
+
return [{ expression }];
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
return adjustLicenseInformation(entries);
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
// The home page of a package. A URL a package carries is untrusted text: only
|
|
924
|
+
// an http(s) URL is kept, without credentials, query or fragment.
|
|
925
|
+
function osPackageWebsite(value) {
|
|
926
|
+
const cleaned = sanitizeBomUrl(`${value ?? ""}`.trim());
|
|
927
|
+
try {
|
|
928
|
+
const { protocol } = new URL(cleaned);
|
|
929
|
+
return protocol === "http:" || protocol === "https:" ? cleaned : undefined;
|
|
930
|
+
} catch {
|
|
931
|
+
return undefined;
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
/**
|
|
936
|
+
* Convert the packages of an apk installed database into components.
|
|
937
|
+
*
|
|
938
|
+
* The packages are named as the container scan names an Alpine, Wolfi or
|
|
939
|
+
* Chainguard package, and grouped with the other OS package categories under
|
|
940
|
+
* `cdx:osquery:category` = `apk_packages`. Beyond what an osquery package row
|
|
941
|
+
* gives, an apk record knows its licence and home page, which become
|
|
942
|
+
* `licenses` and a `website` external reference.
|
|
943
|
+
*
|
|
944
|
+
* @param {Object[]} packages Packages from readApkInstalledDb()
|
|
945
|
+
* @returns {Object[]} Components
|
|
946
|
+
*/
|
|
947
|
+
export function convertApkPackages(packages) {
|
|
948
|
+
const components = [];
|
|
949
|
+
for (const pkg of packages || []) {
|
|
950
|
+
const row = { name: pkg.name, version: pkg.version };
|
|
951
|
+
for (const column of ["arch", "origin", "maintainer"]) {
|
|
952
|
+
if (pkg[column]) {
|
|
953
|
+
row[column] = pkg[column];
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
const [component] = convertOSQueryResults(
|
|
957
|
+
"apk_packages",
|
|
958
|
+
{ purlType: "apk" },
|
|
959
|
+
[row],
|
|
960
|
+
);
|
|
961
|
+
if (!component) {
|
|
962
|
+
continue;
|
|
963
|
+
}
|
|
964
|
+
// The description is set here and not in the row. The GTFOBins matcher reads
|
|
965
|
+
// a row's description for command names, which a deb or rpm row has no
|
|
966
|
+
// column for, and the free text of an apk description matches by chance:
|
|
967
|
+
// "Alpine base dir structure" is no use of the alpine mail client.
|
|
968
|
+
if (pkg.description) {
|
|
969
|
+
component.description = pkg.description;
|
|
970
|
+
}
|
|
971
|
+
const licenses = osPackageLicenses(pkg.license, /\s+/u);
|
|
972
|
+
if (licenses.length) {
|
|
973
|
+
component.licenses = licenses;
|
|
974
|
+
}
|
|
975
|
+
const website = osPackageWebsite(pkg.url);
|
|
976
|
+
if (website) {
|
|
977
|
+
component.externalReferences = [{ type: "website", url: website }];
|
|
978
|
+
}
|
|
979
|
+
components.push(component);
|
|
980
|
+
}
|
|
981
|
+
return components;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
// The frontmatter and osquery columns of a skill row that become properties,
|
|
985
|
+
// named as the ai-skill project scan names them where it has a name.
|
|
986
|
+
const AGENT_SKILL_PROPERTY_NAMES = [
|
|
987
|
+
["name", "cdx:skill:name"],
|
|
988
|
+
["description", "cdx:skill:description"],
|
|
989
|
+
["license", "cdx:skill:license"],
|
|
990
|
+
["compatibility", "cdx:skill:compatibility"],
|
|
991
|
+
["agent", "cdx:skill:agent"],
|
|
992
|
+
["scope", "cdx:skill:scope"],
|
|
993
|
+
["allowed_tools", "cdx:skill:allowedTools"],
|
|
994
|
+
["resource_count", "cdx:skill:resourceCount"],
|
|
995
|
+
["script_count", "cdx:skill:scriptCount"],
|
|
996
|
+
["username", "username"],
|
|
997
|
+
];
|
|
998
|
+
const SHA256_HEX = /^[0-9a-f]{64}$/u;
|
|
999
|
+
|
|
1000
|
+
// A skill's licence is free text ("Complete terms in LICENSE.txt"), so it
|
|
1001
|
+
// becomes `licenses` only when it is an SPDX id or expression.
|
|
1002
|
+
function agentSkillLicenses(text) {
|
|
1003
|
+
const license = `${text ?? ""}`.trim();
|
|
1004
|
+
if (!license) {
|
|
1005
|
+
return [];
|
|
1006
|
+
}
|
|
1007
|
+
if (isExplicitSpdxExpression(license)) {
|
|
1008
|
+
return [{ expression: license }];
|
|
1009
|
+
}
|
|
1010
|
+
const id = findLicenseId(license);
|
|
1011
|
+
return spdxLicenses.includes(id) ? [{ license: { id } }] : [];
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
/**
|
|
1015
|
+
* Convert the rows of the `ai_agent_skills` query into components.
|
|
1016
|
+
*
|
|
1017
|
+
* A skill installed on the host is what the `ai-skill` project scan finds in a
|
|
1018
|
+
* repository, so it is a `file` component with the same `cdx:file:kind` and
|
|
1019
|
+
* `cdx:skill:*` properties, and no purl: a skill has no package identity. Its
|
|
1020
|
+
* bom-ref is the SHA-256 of its SKILL.md rather than the path, which is under a
|
|
1021
|
+
* home directory, and where it was found is an occurrence. The same skill
|
|
1022
|
+
* found under several roots, such as a plugin marketplace checkout and the
|
|
1023
|
+
* plugin cache, is one component with one occurrence for each.
|
|
1024
|
+
*
|
|
1025
|
+
* @param {Object[]} results Rows of `ai_agent_skills` joined with `hash`
|
|
1026
|
+
* @returns {Object[]} Components
|
|
1027
|
+
*/
|
|
1028
|
+
export function convertAgentSkillRows(results) {
|
|
1029
|
+
const components = [];
|
|
1030
|
+
for (const row of results || []) {
|
|
1031
|
+
const skillFile = `${row.path ?? ""}`;
|
|
1032
|
+
const directory = `${row.directory ?? ""}` || dirname(skillFile);
|
|
1033
|
+
// A SKILL.md whose frontmatter osquery cannot read has no name; the
|
|
1034
|
+
// project scan names such a skill after its directory too.
|
|
1035
|
+
const name = sanitizeOsQueryIdentity(row.name || basename(directory));
|
|
1036
|
+
if (!skillFile || !name) {
|
|
1037
|
+
continue;
|
|
1038
|
+
}
|
|
1039
|
+
const version = row.version || "";
|
|
1040
|
+
const sha256 = SHA256_HEX.test(`${row.sha256 ?? ""}`)
|
|
1041
|
+
? row.sha256
|
|
1042
|
+
: undefined;
|
|
1043
|
+
const properties = [
|
|
1044
|
+
{ name: "cdx:osquery:category", value: "ai_agent_skills" },
|
|
1045
|
+
{ name: "cdx:file:kind", value: "skill-file" },
|
|
1046
|
+
{ name: "cdx:agent:inventorySource", value: "osquery" },
|
|
1047
|
+
];
|
|
1048
|
+
for (const [column, propertyName] of AGENT_SKILL_PROPERTY_NAMES) {
|
|
1049
|
+
const value = `${row[column] ?? ""}`.trim();
|
|
1050
|
+
if (value && value !== "null") {
|
|
1051
|
+
properties.push({ name: propertyName, value });
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
if (Number(row.script_count) > 0) {
|
|
1055
|
+
properties.push({ name: "cdx:skill:hasScripts", value: "true" });
|
|
1056
|
+
}
|
|
1057
|
+
properties.push({
|
|
1058
|
+
name: "cdx:skill:specValid",
|
|
1059
|
+
value: isAgentSkillSpecValid(row) ? "true" : "false",
|
|
1060
|
+
});
|
|
1061
|
+
const component = {
|
|
1062
|
+
name,
|
|
1063
|
+
version,
|
|
1064
|
+
description: row.description || "",
|
|
1065
|
+
type: "file",
|
|
1066
|
+
"bom-ref": createOsQueryFallbackBomRef(
|
|
1067
|
+
"ai_agent_skills",
|
|
1068
|
+
"file",
|
|
1069
|
+
name,
|
|
1070
|
+
version,
|
|
1071
|
+
sha256 ? "sha256" : "scope",
|
|
1072
|
+
sha256 || `${row.agent || ""}/${row.scope || ""}`,
|
|
1073
|
+
),
|
|
1074
|
+
properties,
|
|
1075
|
+
};
|
|
1076
|
+
if (sha256) {
|
|
1077
|
+
component.hashes = [{ alg: "SHA-256", content: sha256 }];
|
|
1078
|
+
}
|
|
1079
|
+
const licenses = agentSkillLicenses(row.license);
|
|
1080
|
+
if (licenses.length) {
|
|
1081
|
+
component.licenses = licenses;
|
|
1082
|
+
}
|
|
1083
|
+
const occurrence = createOccurrenceEvidence(skillFile);
|
|
1084
|
+
if (occurrence) {
|
|
1085
|
+
component.evidence = { occurrences: [occurrence] };
|
|
1086
|
+
}
|
|
1087
|
+
components.push(component);
|
|
1088
|
+
}
|
|
1089
|
+
return mergeLocatedPackages(components);
|
|
666
1090
|
}
|
|
667
1091
|
function createOsQueryCryptoProperties(queryCategory, res, version) {
|
|
668
1092
|
switch (queryCategory) {
|
|
@@ -221,7 +221,7 @@ export function openHostCircuit(
|
|
|
221
221
|
);
|
|
222
222
|
} else if (breaker === "maven-central") {
|
|
223
223
|
console.warn(
|
|
224
|
-
`${host} ${what}, so cdxgen will not contact it for the next ${minutes} minute(s) and Maven metadata comes from local caches only. Set
|
|
224
|
+
`${host} ${what}, so cdxgen will not contact it for the next ${minutes} minute(s) and Maven metadata comes from local caches only. Set MAVEN_CENTRAL_MIRROR=true to use Google's Maven Central mirror, or name a repository manager in MAVEN_CENTRAL_URL, to keep license enrichment working.`,
|
|
225
225
|
);
|
|
226
226
|
} else {
|
|
227
227
|
console.warn(
|
|
@@ -4,7 +4,9 @@ import { basename } from "node:path";
|
|
|
4
4
|
import process from "node:process";
|
|
5
5
|
|
|
6
6
|
import { readEnvironmentVariable } from "../core/activity.js";
|
|
7
|
+
import { PROJECT_TYPE_ALIASES } from "../core/env.js";
|
|
7
8
|
import { getAllFiles } from "../core/fs.js";
|
|
9
|
+
import { thoughtLog } from "../core/logger.js";
|
|
8
10
|
import { collectOSCryptoLibs } from "./cbomutils.js";
|
|
9
11
|
import { azurePipelinesParser } from "./ciParsers/azurePipelines.js";
|
|
10
12
|
import { circleCiParser } from "./ciParsers/circleCi.js";
|
|
@@ -187,6 +189,16 @@ const ENV_BLOCKLIST = [
|
|
|
187
189
|
"webhook",
|
|
188
190
|
];
|
|
189
191
|
|
|
192
|
+
// The project types a scan asked for, as given on the command line.
|
|
193
|
+
function requestedProjectTypes(options) {
|
|
194
|
+
return [].concat(options?.projectType || []).flatMap((type) =>
|
|
195
|
+
`${type}`
|
|
196
|
+
.split(",")
|
|
197
|
+
.map((part) => part.trim().toLowerCase())
|
|
198
|
+
.filter(Boolean),
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
190
202
|
/**
|
|
191
203
|
* Build the formulation section for a CycloneDX BOM.
|
|
192
204
|
*
|
|
@@ -285,14 +297,31 @@ export function addFormulationSection(filePath, options, context = {}) {
|
|
|
285
297
|
}
|
|
286
298
|
}
|
|
287
299
|
|
|
288
|
-
//
|
|
289
|
-
|
|
300
|
+
// The toolchains and OS crypto libraries are probed on the machine cdxgen
|
|
301
|
+
// runs on. A container image or root filesystem was built elsewhere, so the
|
|
302
|
+
// scanning host's tools say nothing about it.
|
|
303
|
+
// Only an explicit container type counts: hasAnyProjectType treats
|
|
304
|
+
// `universal` as every type, and a universal source scan runs on the host.
|
|
305
|
+
const describesScanningHost = !requestedProjectTypes(options).some((type) =>
|
|
306
|
+
PROJECT_TYPE_ALIASES.oci.includes(type),
|
|
307
|
+
);
|
|
308
|
+
if (!describesScanningHost) {
|
|
309
|
+
thoughtLog(
|
|
310
|
+
"Skipping the toolchain and OS crypto library probes for formulation, since they would describe the scanning host and not the image.",
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Build environment details (Java, .NET, Python, Node, GCC, Rust, Go, Ruby,
|
|
315
|
+
// RubyGems, Bundler, PHP, Composer)
|
|
316
|
+
const infoComponents = describesScanningHost
|
|
317
|
+
? collectEnvInfo(projectPath, options)
|
|
318
|
+
: [];
|
|
290
319
|
if (infoComponents?.length) {
|
|
291
320
|
components = components.concat(infoComponents);
|
|
292
321
|
}
|
|
293
322
|
|
|
294
323
|
// OS crypto libraries (cbom mode)
|
|
295
|
-
if (options.includeCrypto) {
|
|
324
|
+
if (options.includeCrypto && describesScanningHost) {
|
|
296
325
|
const cryptoLibs = collectOSCryptoLibs(options, context?.executeOsQuery);
|
|
297
326
|
if (cryptoLibs?.length) {
|
|
298
327
|
components = components.concat(cryptoLibs);
|