@cdxgen/cdxgen 13.3.0 → 13.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -6
- package/bin/cdxgen.js +90 -26
- package/bin/evinse.js +18 -1
- package/bin/repl.js +86 -0
- package/bin/sign.js +45 -15
- package/bin/tracebom.js +5 -1
- package/bin/validate.js +84 -18
- package/bin/verify.js +155 -86
- package/data/helpers/init.gradle +46 -21
- package/data/remediations.json +61 -0
- package/data/rules/dependency-sources.yaml +67 -0
- package/data/rules/scala-evidence.yaml +115 -0
- package/lib/audit/index.js +38 -6
- package/lib/cli/index.js +113 -54
- package/lib/cli/jsBom.js +50 -29
- package/lib/cli/jvmBom.js +665 -138
- package/lib/cli/managedBom.js +320 -200
- package/lib/cli/nativeBom.js +139 -30
- package/lib/core/activity.js +12 -0
- package/lib/core/env.js +17 -9
- package/lib/core/fs.js +3 -2
- package/lib/core/httpClient.js +159 -24
- package/lib/core/pluginRun.js +184 -0
- package/lib/core/processTree.js +100 -0
- package/lib/core/runState.js +39 -0
- package/lib/core/toml.js +72 -0
- package/lib/ecosystems/asarutils.js +4 -2
- package/lib/ecosystems/bunutils.js +5 -7
- package/lib/ecosystems/cargoCache.js +231 -8
- package/lib/ecosystems/cmakeResolver.js +10 -21
- package/lib/ecosystems/cppBuildContext.js +631 -0
- package/lib/ecosystems/cppEvidence.js +337 -22
- package/lib/ecosystems/cppIncludeResolver.js +294 -0
- package/lib/ecosystems/denoutils.js +93 -66
- package/lib/ecosystems/ecosystems.js +1967 -788
- package/lib/ecosystems/golem.js +28 -12
- package/lib/ecosystems/gradleutils.js +314 -30
- package/lib/ecosystems/jsEvidence.js +85 -24
- package/lib/ecosystems/kosi.js +21 -4
- package/lib/ecosystems/npmutils.js +64 -0
- package/lib/ecosystems/parsers-dotnet.js +567 -151
- package/lib/ecosystems/parsers-elm.js +46 -28
- package/lib/ecosystems/parsers-gleam.js +3 -4
- package/lib/ecosystems/parsers-go.js +324 -61
- package/lib/ecosystems/parsers-js.js +92 -26
- package/lib/ecosystems/parsers-julia.js +2 -3
- package/lib/ecosystems/parsers-jvm.js +204 -12
- package/lib/ecosystems/parsers-misc.js +182 -55
- package/lib/ecosystems/parsers-python.js +141 -10
- package/lib/ecosystems/parsers-rust.js +4 -4
- package/lib/ecosystems/parsers-terraform.js +1573 -19
- package/lib/ecosystems/pythonutils.js +102 -6
- package/lib/ecosystems/remote/huggingface.js +3 -0
- package/lib/ecosystems/rubyutils.js +67 -17
- package/lib/ecosystems/rusi.js +22 -9
- package/lib/ecosystems/sbtutils.js +243 -187
- package/lib/ecosystems/scalacliutils.js +342 -0
- package/lib/ecosystems/scalasem.js +2618 -0
- package/lib/ecosystems/terraformRegistry.js +288 -0
- package/lib/ecosystems/utils.js +2 -0
- package/lib/ecosystems/vendoredCode.js +156 -0
- package/lib/evinser/evinser.js +578 -69
- package/lib/helpers/agenticToolutils.js +2 -2
- package/lib/helpers/bomSigner.js +576 -128
- package/lib/inventory/aiCollector.js +4 -1
- package/lib/inventory/analyzer.js +110 -127
- package/lib/inventory/atomUtils.js +171 -48
- package/lib/inventory/auditCategories.js +7 -0
- package/lib/inventory/buildToolRateLimit.js +144 -0
- package/lib/inventory/cacheCommands.js +2 -1
- package/lib/inventory/cmakeBuildDirs.js +254 -0
- package/lib/inventory/deps.js +501 -124
- package/lib/inventory/dosai.js +248 -44
- package/lib/inventory/dosaiParsers.js +310 -15
- package/lib/inventory/dynamic.js +52 -6
- package/lib/inventory/evidenceUtils.js +154 -109
- package/lib/inventory/fetchBatch.js +574 -38
- package/lib/inventory/fetchDiskCache.js +597 -0
- package/lib/inventory/forgeEnricher.js +14 -5
- package/lib/inventory/jvmLocalRepos.js +917 -0
- package/lib/inventory/mcpConfigParser.js +2 -3
- package/lib/inventory/parentSelection.js +146 -0
- package/lib/inventory/rustFormulationParser.js +3 -4
- package/lib/inventory/scalaCoords.js +153 -0
- package/lib/inventory/source.js +8 -8
- package/lib/inventory/spdx.js +101 -0
- package/lib/inventory/traceRunner.js +88 -7
- package/lib/inventory/tracedCrypto.js +294 -0
- package/lib/managers/docker.js +33 -32
- package/lib/managers/oci.js +24 -2
- package/lib/parsers/cmakeCache.js +69 -0
- package/lib/parsers/cmakeLists.js +16 -2
- package/lib/parsers/cmakePresets.js +400 -0
- package/lib/parsers/compileCommands.js +557 -0
- package/lib/parsers/strictJson.js +322 -0
- package/lib/parsers/terraformConfig.js +1073 -0
- package/lib/parsers/terraformSource.js +652 -0
- package/lib/server/server.js +23 -0
- package/lib/stages/postgen/introspection/markers.js +1 -1
- package/lib/stages/postgen/postgen.js +15 -1
- package/lib/stages/postgen/specVersionCompat.js +32 -0
- package/lib/validator/index.js +25 -10
- package/lib/validator/reporters/json.js +1 -0
- package/package.json +21 -21
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/cli/jsBom.d.ts.map +1 -1
- package/types/lib/cli/jvmBom.d.ts +24 -1
- package/types/lib/cli/jvmBom.d.ts.map +1 -1
- package/types/lib/cli/managedBom.d.ts +7 -4
- package/types/lib/cli/managedBom.d.ts.map +1 -1
- package/types/lib/cli/nativeBom.d.ts +3 -1
- package/types/lib/cli/nativeBom.d.ts.map +1 -1
- package/types/lib/core/activity.d.ts.map +1 -1
- package/types/lib/core/env.d.ts +9 -0
- package/types/lib/core/env.d.ts.map +1 -1
- package/types/lib/core/fs.d.ts.map +1 -1
- package/types/lib/core/httpClient.d.ts +13 -8
- package/types/lib/core/httpClient.d.ts.map +1 -1
- package/types/lib/core/pluginRun.d.ts +97 -0
- package/types/lib/core/pluginRun.d.ts.map +1 -0
- package/types/lib/core/processTree.d.ts +36 -0
- package/types/lib/core/processTree.d.ts.map +1 -0
- package/types/lib/core/runState.d.ts +29 -0
- package/types/lib/core/runState.d.ts.map +1 -0
- package/types/lib/core/toml.d.ts +20 -0
- package/types/lib/core/toml.d.ts.map +1 -0
- package/types/lib/ecosystems/asarutils.d.ts.map +1 -1
- package/types/lib/ecosystems/bunutils.d.ts.map +1 -1
- package/types/lib/ecosystems/cargoCache.d.ts +12 -0
- package/types/lib/ecosystems/cargoCache.d.ts.map +1 -1
- package/types/lib/ecosystems/cmakeResolver.d.ts +5 -3
- package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -1
- package/types/lib/ecosystems/cppBuildContext.d.ts +55 -0
- package/types/lib/ecosystems/cppBuildContext.d.ts.map +1 -0
- package/types/lib/ecosystems/cppEvidence.d.ts +18 -2
- package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/cppIncludeResolver.d.ts +92 -0
- package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -0
- package/types/lib/ecosystems/denoutils.d.ts +9 -3
- package/types/lib/ecosystems/denoutils.d.ts.map +1 -1
- package/types/lib/ecosystems/ecosystems.d.ts +62 -13
- package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
- package/types/lib/ecosystems/golem.d.ts.map +1 -1
- package/types/lib/ecosystems/gradleutils.d.ts +50 -0
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
- package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/kosi.d.ts.map +1 -1
- package/types/lib/ecosystems/npmutils.d.ts +15 -0
- package/types/lib/ecosystems/npmutils.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-dotnet.d.ts +118 -4
- package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-gleam.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-go.d.ts +44 -2
- package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-js.d.ts +6 -1
- package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-jvm.d.ts +46 -1
- package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-misc.d.ts +3 -1
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-python.d.ts +53 -3
- package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-terraform.d.ts +38 -21
- package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -1
- package/types/lib/ecosystems/pythonutils.d.ts +27 -2
- package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
- package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -1
- package/types/lib/ecosystems/rubyutils.d.ts +21 -1
- package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rusi.d.ts.map +1 -1
- package/types/lib/ecosystems/sbtutils.d.ts +88 -2
- package/types/lib/ecosystems/sbtutils.d.ts.map +1 -1
- package/types/lib/ecosystems/scalacliutils.d.ts +56 -0
- package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -0
- package/types/lib/ecosystems/scalasem.d.ts +311 -0
- package/types/lib/ecosystems/scalasem.d.ts.map +1 -0
- package/types/lib/ecosystems/terraformRegistry.d.ts +9 -0
- package/types/lib/ecosystems/terraformRegistry.d.ts.map +1 -0
- package/types/lib/ecosystems/utils.d.ts +2 -2
- package/types/lib/ecosystems/utils.d.ts.map +1 -1
- package/types/lib/ecosystems/vendoredCode.d.ts +32 -0
- package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -0
- package/types/lib/evinser/evinser.d.ts +75 -8
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/helpers/bomSigner.d.ts +80 -4
- package/types/lib/helpers/bomSigner.d.ts.map +1 -1
- package/types/lib/inventory/aiCollector.d.ts.map +1 -1
- package/types/lib/inventory/analyzer.d.ts.map +1 -1
- package/types/lib/inventory/atomUtils.d.ts +82 -10
- package/types/lib/inventory/atomUtils.d.ts.map +1 -1
- package/types/lib/inventory/auditCategories.d.ts +3 -0
- package/types/lib/inventory/auditCategories.d.ts.map +1 -1
- package/types/lib/inventory/buildToolRateLimit.d.ts +38 -0
- package/types/lib/inventory/buildToolRateLimit.d.ts.map +1 -0
- package/types/lib/inventory/cacheCommands.d.ts +2 -1
- package/types/lib/inventory/cacheCommands.d.ts.map +1 -1
- package/types/lib/inventory/cmakeBuildDirs.d.ts +58 -0
- package/types/lib/inventory/cmakeBuildDirs.d.ts.map +1 -0
- package/types/lib/inventory/deps.d.ts +59 -1
- package/types/lib/inventory/deps.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +57 -8
- package/types/lib/inventory/dosai.d.ts.map +1 -1
- package/types/lib/inventory/dosaiParsers.d.ts +53 -7
- package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
- package/types/lib/inventory/dynamic.d.ts +10 -0
- package/types/lib/inventory/dynamic.d.ts.map +1 -1
- package/types/lib/inventory/evidenceUtils.d.ts +19 -4
- package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
- package/types/lib/inventory/fetchBatch.d.ts +83 -14
- package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
- package/types/lib/inventory/fetchDiskCache.d.ts +169 -0
- package/types/lib/inventory/fetchDiskCache.d.ts.map +1 -0
- package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
- package/types/lib/inventory/jvmLocalRepos.d.ts +178 -0
- package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -0
- package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/parentSelection.d.ts +34 -0
- package/types/lib/inventory/parentSelection.d.ts.map +1 -0
- package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
- package/types/lib/inventory/scalaCoords.d.ts +80 -0
- package/types/lib/inventory/scalaCoords.d.ts.map +1 -0
- package/types/lib/inventory/source.d.ts.map +1 -1
- package/types/lib/inventory/spdx.d.ts +9 -0
- package/types/lib/inventory/spdx.d.ts.map +1 -1
- package/types/lib/inventory/traceRunner.d.ts +12 -0
- package/types/lib/inventory/traceRunner.d.ts.map +1 -1
- package/types/lib/inventory/tracedCrypto.d.ts +42 -0
- package/types/lib/inventory/tracedCrypto.d.ts.map +1 -0
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/managers/oci.d.ts +3 -1
- package/types/lib/managers/oci.d.ts.map +1 -1
- package/types/lib/parsers/cmakeCache.d.ts +16 -0
- package/types/lib/parsers/cmakeCache.d.ts.map +1 -1
- package/types/lib/parsers/cmakeLists.d.ts +2 -1
- package/types/lib/parsers/cmakeLists.d.ts.map +1 -1
- package/types/lib/parsers/cmakePresets.d.ts +78 -0
- package/types/lib/parsers/cmakePresets.d.ts.map +1 -0
- package/types/lib/parsers/compileCommands.d.ts +115 -0
- package/types/lib/parsers/compileCommands.d.ts.map +1 -0
- package/types/lib/parsers/strictJson.d.ts +46 -0
- package/types/lib/parsers/strictJson.d.ts.map +1 -0
- package/types/lib/parsers/terraformConfig.d.ts +70 -0
- package/types/lib/parsers/terraformConfig.d.ts.map +1 -0
- package/types/lib/parsers/terraformSource.d.ts +66 -0
- package/types/lib/parsers/terraformSource.d.ts.map +1 -0
- package/types/lib/server/server.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
- package/types/lib/validator/index.d.ts +8 -0
- package/types/lib/validator/index.d.ts.map +1 -1
- package/types/lib/validator/reporters/json.d.ts.map +1 -1
- package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +0 -462
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { Buffer } from "node:buffer";
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
3
|
-
import {
|
|
2
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { delimiter, dirname, join } from "node:path";
|
|
4
5
|
|
|
5
6
|
import { Purl } from "@cdxgen/cdx-purl";
|
|
6
7
|
|
|
7
|
-
import { DEBUG_MODE } from "../core/activity.js";
|
|
8
|
+
import { DEBUG_MODE, readEnvironmentVariable } from "../core/activity.js";
|
|
8
9
|
import { shouldFetchLicense, shouldFetchVCS } from "../core/env.js";
|
|
9
|
-
import { safeExistsSync } from "../core/fs.js";
|
|
10
10
|
import {
|
|
11
11
|
applyPurl,
|
|
12
12
|
canonicalPurlFromLooseString,
|
|
13
13
|
tryBuildPurl,
|
|
14
14
|
tryParsePurl,
|
|
15
15
|
} from "../inventory/purl.js";
|
|
16
|
+
import { guessLicenseId } from "../inventory/spdx.js";
|
|
16
17
|
import {
|
|
17
|
-
_clearMetadataCache,
|
|
18
18
|
getGoPkgLicense,
|
|
19
19
|
getGoPkgVCSUrl,
|
|
20
20
|
prefetchGoPkgMetadata,
|
|
@@ -30,6 +30,163 @@ function toBase64(hexString) {
|
|
|
30
30
|
return Buffer.from(hexString, "hex").toString("base64");
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* The root of the Go module cache on this machine.
|
|
35
|
+
*
|
|
36
|
+
* @returns {string|undefined} `$GOMODCACHE`, or `$GOPATH/pkg/mod`, or the
|
|
37
|
+
* default `~/go/pkg/mod`.
|
|
38
|
+
*/
|
|
39
|
+
function goModuleCacheDir() {
|
|
40
|
+
const gomodcache = readEnvironmentVariable("GOMODCACHE");
|
|
41
|
+
if (gomodcache) {
|
|
42
|
+
return gomodcache;
|
|
43
|
+
}
|
|
44
|
+
const gopath = readEnvironmentVariable("GOPATH");
|
|
45
|
+
if (gopath) {
|
|
46
|
+
return join(gopath.split(delimiter)[0], "pkg", "mod");
|
|
47
|
+
}
|
|
48
|
+
return join(homedir(), "go", "pkg", "mod");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Escape a module path the way Go's module cache directory names do: every
|
|
53
|
+
* upper-case letter becomes `!` followed by the lower-case one, so the cache
|
|
54
|
+
* stays case-preserving on case-insensitive file systems
|
|
55
|
+
* (`github.com/Azure` is stored as `github.com/!azure`).
|
|
56
|
+
*
|
|
57
|
+
* @param {string} modulePath Full module path
|
|
58
|
+
* @returns {string} The escaped path
|
|
59
|
+
*/
|
|
60
|
+
function escapeGoModulePath(modulePath) {
|
|
61
|
+
let escaped = "";
|
|
62
|
+
for (const ch of modulePath) {
|
|
63
|
+
if (ch >= "A" && ch <= "Z") {
|
|
64
|
+
escaped += `!${ch.toLowerCase()}`;
|
|
65
|
+
} else {
|
|
66
|
+
escaped += ch;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return escaped;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Whether a file name is a licence or copying notice, whose text describes the
|
|
74
|
+
* module's licence without a registry round.
|
|
75
|
+
*
|
|
76
|
+
* @param {string} fileName Candidate file name
|
|
77
|
+
* @returns {boolean}
|
|
78
|
+
*/
|
|
79
|
+
function isGoLicenseFileName(fileName) {
|
|
80
|
+
const lower = fileName.toLowerCase();
|
|
81
|
+
return lower.startsWith("license") || lower.startsWith("copying");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The licence or copying notice of a module that is already on this machine,
|
|
86
|
+
* from the directory `go list` reported, from the project's `vendor/` tree, or
|
|
87
|
+
* from the module cache. Reading it costs no network round trip, so it answers
|
|
88
|
+
* before pkg.go.dev is asked.
|
|
89
|
+
*
|
|
90
|
+
* The directory `go list` reported is always read. The vendor tree and the
|
|
91
|
+
* module cache stand in for pkg.go.dev, so they are read only when licences
|
|
92
|
+
* are being fetched.
|
|
93
|
+
*
|
|
94
|
+
* @param {string} name Full module path
|
|
95
|
+
* @param {string} [version] Module version, for the module cache layout
|
|
96
|
+
* @param {Object} [options] Options
|
|
97
|
+
* @param {string} [options.dir] The module's directory, as `go list` reports it
|
|
98
|
+
* @param {string} [options.projectRoot] Project root holding a `vendor/` tree
|
|
99
|
+
* @returns {string|undefined} The notice text, or undefined when none is held
|
|
100
|
+
* locally
|
|
101
|
+
*/
|
|
102
|
+
export function readLocalGoLicense(name, version, options = {}) {
|
|
103
|
+
if (!name) {
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
const candidateDirs = [];
|
|
107
|
+
if (options.dir) {
|
|
108
|
+
candidateDirs.push(options.dir);
|
|
109
|
+
}
|
|
110
|
+
const fetchingLicenses = shouldFetchLicense();
|
|
111
|
+
if (fetchingLicenses && options.projectRoot) {
|
|
112
|
+
candidateDirs.push(join(options.projectRoot, "vendor", name));
|
|
113
|
+
}
|
|
114
|
+
if (fetchingLicenses && version) {
|
|
115
|
+
const cacheRoot = goModuleCacheDir();
|
|
116
|
+
if (cacheRoot) {
|
|
117
|
+
candidateDirs.push(
|
|
118
|
+
join(cacheRoot, `${escapeGoModulePath(name)}@${version}`),
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
for (const dir of candidateDirs) {
|
|
123
|
+
let entries;
|
|
124
|
+
try {
|
|
125
|
+
entries = readdirSync(dir);
|
|
126
|
+
} catch {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const names = entries
|
|
130
|
+
.filter((e) => isGoLicenseFileName(e))
|
|
131
|
+
.sort((a, b) => a.localeCompare(b));
|
|
132
|
+
// A directory can hold both a licence and a copying notice; the licence
|
|
133
|
+
// notice wins when both are present.
|
|
134
|
+
const chosen =
|
|
135
|
+
names.find((f) => f.toLowerCase().startsWith("license")) ||
|
|
136
|
+
names.find((f) => f.toLowerCase().startsWith("copying"));
|
|
137
|
+
if (!chosen) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
try {
|
|
141
|
+
const text = readFileSync(join(dir, chosen), {
|
|
142
|
+
encoding: "utf-8",
|
|
143
|
+
});
|
|
144
|
+
if (text?.length) {
|
|
145
|
+
return text;
|
|
146
|
+
}
|
|
147
|
+
} catch {
|
|
148
|
+
// An unreadable notice is the same as none: the registry path answers.
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Mark the modules whose licence notice is held on this machine, so the
|
|
156
|
+
* pkg.go.dev batch skips their licence tab but still fetches their module page
|
|
157
|
+
* for the repository URL.
|
|
158
|
+
*
|
|
159
|
+
* @param {Object[]} modules Modules with `name`, `version` and the options
|
|
160
|
+
* {@link readLocalGoLicense} takes
|
|
161
|
+
* @returns {Object[]} Copies of the modules with `licenseKnown` set
|
|
162
|
+
*/
|
|
163
|
+
function withLocalLicenseFlags(modules) {
|
|
164
|
+
return modules.map((m) => ({
|
|
165
|
+
...m,
|
|
166
|
+
licenseKnown: !!readLocalGoLicense(m.name, m.version, m),
|
|
167
|
+
}));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* The licence object for a notice read from disk: its SPDX id when the text
|
|
172
|
+
* names one, else a CUSTOM licence, with the notice as its text either way.
|
|
173
|
+
*
|
|
174
|
+
* @param {string} text Notice text
|
|
175
|
+
* @returns {Object} CycloneDX licence object
|
|
176
|
+
*/
|
|
177
|
+
function localGoLicenseObject(text) {
|
|
178
|
+
const licenseId = guessLicenseId(text);
|
|
179
|
+
const identity = !licenseId
|
|
180
|
+
? { name: "CUSTOM" }
|
|
181
|
+
: licenseId.includes(" ")
|
|
182
|
+
? { name: licenseId }
|
|
183
|
+
: { id: licenseId };
|
|
184
|
+
return {
|
|
185
|
+
...identity,
|
|
186
|
+
text: { contentType: "text/plain", content: text },
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
33
190
|
/**
|
|
34
191
|
* Builds a Go package component object containing purl, bom-ref, integrity hash,
|
|
35
192
|
* and optionally license and VCS external reference information.
|
|
@@ -38,11 +195,31 @@ function toBase64(hexString) {
|
|
|
38
195
|
* @param {string} name Package name (full module path when group is empty)
|
|
39
196
|
* @param {string} version Package version string
|
|
40
197
|
* @param {string} hash Integrity hash (e.g. "sha256-…"), used as _integrity
|
|
198
|
+
* @param {Object} [options] Options
|
|
199
|
+
* @param {boolean} [options.skipRegistryLookups] Leave the licence and VCS
|
|
200
|
+
* URL unset instead of asking pkg.go.dev. Used for the main module, which
|
|
201
|
+
* no registry holds.
|
|
202
|
+
* @param {string} [options.dir] The module's directory, as `go list` reports
|
|
203
|
+
* it, where a local licence notice is read first
|
|
204
|
+
* @param {string} [options.projectRoot] Project root holding a `vendor/` tree
|
|
205
|
+
* the module may be vendored in
|
|
41
206
|
* @returns {Promise<Object>} Component object ready for inclusion in a BOM package list
|
|
42
207
|
*/
|
|
43
|
-
export async function getGoPkgComponent(
|
|
208
|
+
export async function getGoPkgComponent(
|
|
209
|
+
group,
|
|
210
|
+
name,
|
|
211
|
+
version,
|
|
212
|
+
hash,
|
|
213
|
+
options = {},
|
|
214
|
+
) {
|
|
44
215
|
let license;
|
|
45
|
-
|
|
216
|
+
let licenses;
|
|
217
|
+
// A notice held on this machine answers before pkg.go.dev is asked, and the
|
|
218
|
+
// registry is consulted only when there is none.
|
|
219
|
+
const localLicense = readLocalGoLicense(name, version, options);
|
|
220
|
+
if (localLicense) {
|
|
221
|
+
licenses = [{ license: localGoLicenseObject(localLicense) }];
|
|
222
|
+
} else if (shouldFetchLicense() && !options.skipRegistryLookups) {
|
|
46
223
|
if (DEBUG_MODE) {
|
|
47
224
|
console.log(
|
|
48
225
|
`About to fetch go package license information for ${group}:${name}`,
|
|
@@ -51,6 +228,7 @@ export async function getGoPkgComponent(group, name, version, hash) {
|
|
|
51
228
|
license = await getGoPkgLicense({
|
|
52
229
|
group: group,
|
|
53
230
|
name: name,
|
|
231
|
+
version: version,
|
|
54
232
|
});
|
|
55
233
|
}
|
|
56
234
|
// Split the full module path into namespace and name, since the caller
|
|
@@ -76,8 +254,8 @@ export async function getGoPkgComponent(group, name, version, hash) {
|
|
|
76
254
|
version: version || null,
|
|
77
255
|
})?.replace(/%2F/g, "/") || null;
|
|
78
256
|
let vcs;
|
|
79
|
-
if (shouldFetchVCS()) {
|
|
80
|
-
vcs = await getGoPkgVCSUrl(group, name);
|
|
257
|
+
if (shouldFetchVCS() && !options.skipRegistryLookups) {
|
|
258
|
+
vcs = await getGoPkgVCSUrl(group, name, version);
|
|
81
259
|
}
|
|
82
260
|
const packageInfo = {
|
|
83
261
|
group: group,
|
|
@@ -86,6 +264,9 @@ export async function getGoPkgComponent(group, name, version, hash) {
|
|
|
86
264
|
_integrity: hash,
|
|
87
265
|
license: license,
|
|
88
266
|
};
|
|
267
|
+
if (licenses) {
|
|
268
|
+
packageInfo.licenses = licenses;
|
|
269
|
+
}
|
|
89
270
|
applyPurl(packageInfo, purlString);
|
|
90
271
|
if (vcs) {
|
|
91
272
|
packageInfo.externalReferences = [{ type: "vcs", url: vcs }];
|
|
@@ -109,7 +290,9 @@ export async function parseGoModData(goModData, gosumMap) {
|
|
|
109
290
|
return {};
|
|
110
291
|
}
|
|
111
292
|
const { parentComponent, entries } = decodeGoModData(goModData, gosumMap);
|
|
112
|
-
|
|
293
|
+
// A module whose licence notice is read locally is not asked for its
|
|
294
|
+
// licence; getGoPkgComponent reads the same notice for each entry below.
|
|
295
|
+
await prefetchGoPkgMetadata(withLocalLicenseFlags(entries));
|
|
113
296
|
for (const entry of entries) {
|
|
114
297
|
const component = await getGoPkgComponent(
|
|
115
298
|
"",
|
|
@@ -129,8 +312,6 @@ export async function parseGoModData(goModData, gosumMap) {
|
|
|
129
312
|
}
|
|
130
313
|
pkgComponentsList.push(component);
|
|
131
314
|
}
|
|
132
|
-
// Clear the cache
|
|
133
|
-
_clearMetadataCache();
|
|
134
315
|
return {
|
|
135
316
|
parentComponent,
|
|
136
317
|
pkgList: pkgComponentsList.sort((a, b) =>
|
|
@@ -271,18 +452,30 @@ export async function parseGoModulesTxt(txtFile, gosumMap) {
|
|
|
271
452
|
const pkgs = txtData
|
|
272
453
|
.split("\n")
|
|
273
454
|
.filter((p) => p.trim().replace(/["']/g, "").startsWith("# "));
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
)
|
|
278
|
-
for (const l of pkgs) {
|
|
455
|
+
// A modules.txt file lives in the vendor/ tree, so the modules it lists are
|
|
456
|
+
// vendored one directory up from it.
|
|
457
|
+
const projectRoot = dirname(dirname(txtFile));
|
|
458
|
+
const modules = pkgs.map((l) => {
|
|
279
459
|
const tmpA = l.split(" ");
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
460
|
+
return {
|
|
461
|
+
group: "",
|
|
462
|
+
name: tmpA[1],
|
|
463
|
+
version: tmpA[2],
|
|
464
|
+
projectRoot,
|
|
465
|
+
};
|
|
466
|
+
});
|
|
467
|
+
// A module whose vendored licence notice is read is not asked for its
|
|
468
|
+
// licence; getGoPkgComponent reads the same file.
|
|
469
|
+
await prefetchGoPkgMetadata(withLocalLicenseFlags(modules));
|
|
470
|
+
for (const m of modules) {
|
|
471
|
+
const gosumHash = gosumMap[`${m.name}@${m.version}`];
|
|
472
|
+
const component = await getGoPkgComponent(
|
|
473
|
+
"",
|
|
474
|
+
m.name,
|
|
475
|
+
m.version,
|
|
476
|
+
gosumHash,
|
|
477
|
+
{ projectRoot },
|
|
478
|
+
);
|
|
286
479
|
let confidence = 0.7;
|
|
287
480
|
if (gosumHash) {
|
|
288
481
|
component.scope = "required";
|
|
@@ -313,19 +506,44 @@ export async function parseGoListDep(rawOutput, gosumMap) {
|
|
|
313
506
|
// covers every module the loop resolves and nothing else.
|
|
314
507
|
const prefetchSeen = new Set();
|
|
315
508
|
const prefetchModules = [];
|
|
509
|
+
// The directory of the main module row, which is the project root, under
|
|
510
|
+
// which the vendored copies of the dependencies live. `go list` may print
|
|
511
|
+
// the main module last, so its row is found before the batch is built.
|
|
512
|
+
let mainModuleDir;
|
|
513
|
+
for (const l of pkgs) {
|
|
514
|
+
const verArr = l.trim().replace(/["']/g, "").split("|");
|
|
515
|
+
if (verArr?.length >= 10 && verArr[5] === "true" && verArr[9]?.length) {
|
|
516
|
+
mainModuleDir = verArr[9];
|
|
517
|
+
}
|
|
518
|
+
}
|
|
316
519
|
for (const l of pkgs) {
|
|
317
520
|
const verArr = l.trim().replace(/["']/g, "").split("|");
|
|
318
521
|
if (!verArr || verArr.length < 5) {
|
|
319
522
|
continue;
|
|
320
523
|
}
|
|
524
|
+
// The row whose fifth field is true is the main module: the project
|
|
525
|
+
// itself, which no registry holds and which the loop below turns into
|
|
526
|
+
// the parent component.
|
|
527
|
+
if (verArr[5] === "true") {
|
|
528
|
+
continue;
|
|
529
|
+
}
|
|
321
530
|
const key = `${verArr[0]}-${verArr[1]}`;
|
|
322
531
|
if (prefetchSeen.has(key)) {
|
|
323
532
|
continue;
|
|
324
533
|
}
|
|
325
534
|
prefetchSeen.add(key);
|
|
326
|
-
prefetchModules.push({
|
|
535
|
+
prefetchModules.push({
|
|
536
|
+
group: "",
|
|
537
|
+
name: verArr[0],
|
|
538
|
+
version: verArr[1],
|
|
539
|
+
dir: verArr[9]?.length ? verArr[9] : undefined,
|
|
540
|
+
projectRoot: mainModuleDir,
|
|
541
|
+
});
|
|
327
542
|
}
|
|
328
|
-
|
|
543
|
+
// A module whose licence notice is held on this machine is not asked for
|
|
544
|
+
// its licence: getGoPkgComponent reads the same notices, so the batch
|
|
545
|
+
// covers exactly what the loop will look up.
|
|
546
|
+
await prefetchGoPkgMetadata(withLocalLicenseFlags(prefetchModules));
|
|
329
547
|
for (const l of pkgs) {
|
|
330
548
|
const verArr = l.trim().replace(/["']/g, "").split("|");
|
|
331
549
|
if (verArr && verArr.length >= 5) {
|
|
@@ -338,11 +556,17 @@ export async function parseGoListDep(rawOutput, gosumMap) {
|
|
|
338
556
|
if (!gosumHash && verArr.length >= 8 && verArr[8]?.length) {
|
|
339
557
|
gosumHash = `sha256-${verArr[8].replace("h1:", "")}`;
|
|
340
558
|
}
|
|
559
|
+
const isMainModule = verArr[5] === "true";
|
|
341
560
|
const component = await getGoPkgComponent(
|
|
342
561
|
"",
|
|
343
562
|
verArr[0],
|
|
344
563
|
version,
|
|
345
564
|
gosumHash,
|
|
565
|
+
{
|
|
566
|
+
skipRegistryLookups: isMainModule,
|
|
567
|
+
dir: verArr[9]?.length ? verArr[9] : undefined,
|
|
568
|
+
projectRoot: mainModuleDir,
|
|
569
|
+
},
|
|
346
570
|
);
|
|
347
571
|
// This is misusing the scope attribute to represent direct vs indirect
|
|
348
572
|
if (verArr[2] === "false") {
|
|
@@ -385,21 +609,6 @@ export async function parseGoListDep(rawOutput, gosumMap) {
|
|
|
385
609
|
name: "cdx:go:local_dir",
|
|
386
610
|
value: verArr[9],
|
|
387
611
|
});
|
|
388
|
-
if (safeExistsSync(join(verArr[9], "LICENSE"))) {
|
|
389
|
-
const licenseText = readFileSync(join(verArr[9], "LICENSE"), {
|
|
390
|
-
encoding: "utf-8",
|
|
391
|
-
});
|
|
392
|
-
if (licenseText?.length) {
|
|
393
|
-
component.licenses = [
|
|
394
|
-
{
|
|
395
|
-
license: {
|
|
396
|
-
name: "CUSTOM",
|
|
397
|
-
text: { contentType: "text/plain", content: licenseText },
|
|
398
|
-
},
|
|
399
|
-
},
|
|
400
|
-
];
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
612
|
}
|
|
404
613
|
if (verArr.length > 5 && verArr[5] === "true") {
|
|
405
614
|
parentComponent = component;
|
|
@@ -562,11 +771,20 @@ export async function parseGoModGraph(
|
|
|
562
771
|
}
|
|
563
772
|
// Add the source and depends to the pkgList
|
|
564
773
|
if (!addedPkgs[tmpA[0]] && !excludedRefs.includes(sourceRefString)) {
|
|
774
|
+
// go mod graph prints a main module without a version. A main
|
|
775
|
+
// module is the project itself, or a go.work member, which no
|
|
776
|
+
// registry holds, so it is never looked up. It still becomes a
|
|
777
|
+
// component, because the graph's edges hang off it.
|
|
778
|
+
const isParentSource =
|
|
779
|
+
!tmpA[0].includes("@") ||
|
|
780
|
+
sourceName === goModPkgMap?.parentComponent?.name ||
|
|
781
|
+
sourceRefString === parentComponent?.["bom-ref"];
|
|
565
782
|
const component = await getGoPkgComponent(
|
|
566
783
|
"",
|
|
567
784
|
sourceName,
|
|
568
785
|
sourcePurl?.version || tmpA[0].split("@")[1],
|
|
569
786
|
gosumMap[tmpA[0]],
|
|
787
|
+
{ skipRegistryLookups: isParentSource },
|
|
570
788
|
);
|
|
571
789
|
let confidence = 0.7;
|
|
572
790
|
if (goModOptionalDepsMap[component["bom-ref"]]) {
|
|
@@ -722,19 +940,31 @@ export function isGoModWhyNotNeeded(rawOutput) {
|
|
|
722
940
|
}
|
|
723
941
|
|
|
724
942
|
/**
|
|
725
|
-
*
|
|
943
|
+
* Read the `module@version` to hash map out of go.sum contents, without
|
|
944
|
+
* building components or touching the network.
|
|
945
|
+
*
|
|
726
946
|
* @param {string} gosumData Content of go.sum
|
|
727
|
-
* @returns
|
|
947
|
+
* @returns {Object} Map of `module@version` keys to `sha256-…` values
|
|
728
948
|
*/
|
|
729
|
-
export
|
|
730
|
-
const
|
|
731
|
-
|
|
732
|
-
|
|
949
|
+
export function parseGosumHashes(gosumData) {
|
|
950
|
+
const gosumMap = {};
|
|
951
|
+
for (const entry of decodeGosumData(gosumData)) {
|
|
952
|
+
gosumMap[`${entry.name}@${entry.version}`] = entry.hash;
|
|
733
953
|
}
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
954
|
+
return gosumMap;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
/**
|
|
958
|
+
* Decode the go.mod lines of a go.sum file into module entries.
|
|
959
|
+
*
|
|
960
|
+
* @param {string} gosumData Content of go.sum
|
|
961
|
+
* @returns {Object[]} Entries with `name`, `version` and `hash`
|
|
962
|
+
*/
|
|
963
|
+
function decodeGosumData(gosumData) {
|
|
737
964
|
const entries = [];
|
|
965
|
+
if (!gosumData) {
|
|
966
|
+
return entries;
|
|
967
|
+
}
|
|
738
968
|
for (const l of gosumData.split("\n")) {
|
|
739
969
|
const m = l.replaceAll("\r", "");
|
|
740
970
|
// look for lines containing go.mod
|
|
@@ -748,7 +978,20 @@ export async function parseGosumData(gosumData) {
|
|
|
748
978
|
});
|
|
749
979
|
}
|
|
750
980
|
}
|
|
751
|
-
|
|
981
|
+
return entries;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
/**
|
|
985
|
+
* Parse go sum data
|
|
986
|
+
* @param {string} gosumData Content of go.sum
|
|
987
|
+
* @returns package list
|
|
988
|
+
*/
|
|
989
|
+
export async function parseGosumData(gosumData) {
|
|
990
|
+
const pkgList = [];
|
|
991
|
+
const entries = decodeGosumData(gosumData);
|
|
992
|
+
// A module whose licence notice is read locally is not asked for its
|
|
993
|
+
// licence; getGoPkgComponent reads the same notice for each entry below.
|
|
994
|
+
await prefetchGoPkgMetadata(withLocalLicenseFlags(entries));
|
|
752
995
|
for (const entry of entries) {
|
|
753
996
|
pkgList.push(
|
|
754
997
|
await getGoPkgComponent("", entry.name, entry.version, entry.hash),
|
|
@@ -775,13 +1018,17 @@ export async function parseGopkgData(gopkgData) {
|
|
|
775
1018
|
for (const l of pkgs) {
|
|
776
1019
|
let key = null;
|
|
777
1020
|
let value = null;
|
|
778
|
-
|
|
1021
|
+
// Every table header ends the project read so far, and only a
|
|
1022
|
+
// [[projects]] table starts another. The keys of [solve-meta] belong to
|
|
1023
|
+
// no project.
|
|
1024
|
+
if (l.trim().startsWith("[")) {
|
|
779
1025
|
if (pkg) {
|
|
780
|
-
pkgList.push(pkg);
|
|
1026
|
+
pkgList.push(await finalizeGopkgProject(pkg));
|
|
781
1027
|
}
|
|
782
|
-
pkg = {};
|
|
1028
|
+
pkg = l.indexOf("[[projects]]") > -1 ? {} : null;
|
|
1029
|
+
continue;
|
|
783
1030
|
}
|
|
784
|
-
if (l.indexOf("=") > -1) {
|
|
1031
|
+
if (pkg && l.indexOf("=") > -1) {
|
|
785
1032
|
const tmpA = l.split("=");
|
|
786
1033
|
key = tmpA[0].trim();
|
|
787
1034
|
value = tmpA[1].trim().replace(/"/g, "");
|
|
@@ -794,12 +1041,6 @@ export async function parseGopkgData(gopkgData) {
|
|
|
794
1041
|
case "name":
|
|
795
1042
|
pkg.group = "";
|
|
796
1043
|
pkg.name = value;
|
|
797
|
-
if (shouldFetchLicense()) {
|
|
798
|
-
pkg.license = await getGoPkgLicense({
|
|
799
|
-
group: pkg.group,
|
|
800
|
-
name: pkg.name,
|
|
801
|
-
});
|
|
802
|
-
}
|
|
803
1044
|
break;
|
|
804
1045
|
case "version":
|
|
805
1046
|
pkg.version = value;
|
|
@@ -811,9 +1052,31 @@ export async function parseGopkgData(gopkgData) {
|
|
|
811
1052
|
}
|
|
812
1053
|
}
|
|
813
1054
|
}
|
|
1055
|
+
// The last project has no header after it.
|
|
1056
|
+
if (pkg) {
|
|
1057
|
+
pkgList.push(await finalizeGopkgProject(pkg));
|
|
1058
|
+
}
|
|
814
1059
|
return pkgList;
|
|
815
1060
|
}
|
|
816
1061
|
|
|
1062
|
+
/**
|
|
1063
|
+
* Attach the licence of one Gopkg project once its name and version are both
|
|
1064
|
+
* known, so the pkg.go.dev URL names the pinned release.
|
|
1065
|
+
*
|
|
1066
|
+
* @param {Object} pkg The project entry read so far
|
|
1067
|
+
* @returns {Promise<Object>} The same entry, with a licence when one was found
|
|
1068
|
+
*/
|
|
1069
|
+
async function finalizeGopkgProject(pkg) {
|
|
1070
|
+
if (shouldFetchLicense() && pkg.name) {
|
|
1071
|
+
pkg.license = await getGoPkgLicense({
|
|
1072
|
+
group: pkg.group,
|
|
1073
|
+
name: pkg.name,
|
|
1074
|
+
version: pkg.version,
|
|
1075
|
+
});
|
|
1076
|
+
}
|
|
1077
|
+
return pkg;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
817
1080
|
/**
|
|
818
1081
|
* Parses the output of `go version -m` (build info) and returns a list of Go
|
|
819
1082
|
* package components for each "dep" line, including name, version, and integrity hash.
|
|
@@ -846,7 +1109,7 @@ export async function parseGoVersionData(buildInfoData) {
|
|
|
846
1109
|
: "",
|
|
847
1110
|
});
|
|
848
1111
|
}
|
|
849
|
-
await prefetchGoPkgMetadata(entries);
|
|
1112
|
+
await prefetchGoPkgMetadata(withLocalLicenseFlags(entries));
|
|
850
1113
|
for (const entry of entries) {
|
|
851
1114
|
pkgList.push(
|
|
852
1115
|
await getGoPkgComponent("", entry.name, entry.version, entry.hash),
|