@cdxgen/cdxgen 13.2.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 +33 -14
- package/bin/cdxgen.js +310 -138
- package/bin/evinse.js +22 -0
- package/bin/repl.js +86 -0
- package/bin/sign.js +119 -15
- package/bin/tracebom.js +69 -5
- package/bin/validate.js +107 -19
- package/bin/verify.js +172 -63
- package/data/helpers/init.gradle +46 -21
- package/data/remediations.json +85 -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/bomAssembly.js +105 -15
- package/lib/cli/cliOptions.js +93 -1
- package/lib/cli/index.js +156 -58
- package/lib/cli/jsBom.js +99 -53
- package/lib/cli/jvmBom.js +708 -147
- package/lib/cli/managedBom.js +415 -224
- package/lib/cli/nativeBom.js +264 -44
- package/lib/core/activity.js +12 -0
- package/lib/core/env.js +22 -15
- package/lib/core/fs.js +176 -7
- package/lib/core/httpClient.js +159 -24
- package/lib/core/metadataProperties.js +97 -0
- package/lib/core/pluginRun.js +184 -0
- package/lib/core/processTree.js +100 -0
- package/lib/core/projectConfig.js +31 -4
- 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 +274 -108
- package/lib/ecosystems/cargoCache.js +231 -8
- package/lib/ecosystems/caxa.js +434 -2
- package/lib/ecosystems/cmakeResolver.js +76 -21
- package/lib/ecosystems/cppBuildContext.js +631 -0
- package/lib/ecosystems/cppEvidence.js +399 -51
- package/lib/ecosystems/cppIncludeResolver.js +294 -0
- package/lib/ecosystems/denoutils.js +93 -66
- package/lib/ecosystems/ecosystems.js +1983 -793
- package/lib/ecosystems/golem.js +28 -12
- package/lib/ecosystems/gradleutils.js +356 -37
- package/lib/ecosystems/jsEvidence.js +104 -44
- package/lib/ecosystems/kosi.js +31 -7
- package/lib/ecosystems/npmutils.js +174 -9
- package/lib/ecosystems/parsers-dotnet.js +616 -159
- package/lib/ecosystems/parsers-elm.js +46 -28
- package/lib/ecosystems/parsers-gleam.js +3 -4
- package/lib/ecosystems/parsers-go.js +371 -70
- package/lib/ecosystems/parsers-js.js +263 -76
- package/lib/ecosystems/parsers-julia.js +2 -3
- package/lib/ecosystems/parsers-jvm.js +204 -12
- package/lib/ecosystems/parsers-misc.js +929 -216
- package/lib/ecosystems/parsers-python.js +167 -24
- package/lib/ecosystems/parsers-rust.js +28 -22
- 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 +1492 -182
- package/lib/ecosystems/rusi.js +54 -10
- 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 +1008 -191
- package/lib/evinser/swiftsem.js +1914 -302
- package/lib/helpers/agenticToolutils.js +2 -2
- package/lib/helpers/bomSigner.js +1101 -222
- package/lib/inventory/aiCollector.js +4 -1
- package/lib/inventory/analyzer.js +110 -127
- package/lib/inventory/atomUtils.js +703 -43
- package/lib/inventory/auditCategories.js +7 -0
- package/lib/inventory/buildToolRateLimit.js +144 -0
- package/lib/inventory/cacheCommands.js +2 -1
- package/lib/inventory/cdxrs.js +103 -12
- package/lib/inventory/cmakeBuildDirs.js +254 -0
- package/lib/inventory/deps.js +501 -124
- package/lib/inventory/depsUtils.js +183 -11
- package/lib/inventory/display.js +4 -5
- package/lib/inventory/dosai.js +798 -57
- package/lib/inventory/dosaiParsers.js +310 -15
- package/lib/inventory/dynamic.js +90 -6
- package/lib/inventory/evidenceUtils.js +166 -113
- package/lib/inventory/fetchBatch.js +690 -55
- 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/protobom.js +8 -1
- package/lib/inventory/purl.js +152 -3
- package/lib/inventory/rustFormulationParser.js +3 -4
- package/lib/inventory/sbomDocument.js +2 -70
- package/lib/inventory/scalaCoords.js +153 -0
- package/lib/inventory/source.js +45 -27
- package/lib/inventory/spdx.js +101 -0
- package/lib/inventory/traceRunner.js +295 -25
- package/lib/inventory/tracedCrypto.js +294 -0
- package/lib/managers/binary.js +115 -24
- package/lib/managers/docker.js +467 -46
- package/lib/managers/oci.js +63 -3
- package/lib/parsers/cmakeCache.js +69 -0
- package/lib/parsers/cmakeLists.js +466 -0
- package/lib/parsers/cmakePresets.js +400 -0
- package/lib/parsers/compileCommands.js +557 -0
- package/lib/parsers/largeJson.js +454 -0
- package/lib/parsers/npmrc.js +19 -1
- 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 +83 -16
- package/lib/stages/postgen/specVersionCompat.js +32 -0
- package/lib/validator/bomValidator.js +6 -16
- package/lib/validator/index.js +64 -11
- package/lib/validator/reporters/console.js +3 -0
- package/lib/validator/reporters/json.js +2 -0
- package/lib/validator/reporters/sarif.js +7 -0
- package/package.json +23 -23
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/cli/bomAssembly.d.ts +2 -1
- package/types/lib/cli/bomAssembly.d.ts.map +1 -1
- package/types/lib/cli/cliOptions.d.ts +33 -1
- 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.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 +55 -0
- 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/metadataProperties.d.ts +39 -0
- package/types/lib/core/metadataProperties.d.ts.map +1 -0
- 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/projectConfig.d.ts +6 -2
- package/types/lib/core/projectConfig.d.ts.map +1 -1
- 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 +12 -5
- 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/caxa.d.ts +14 -0
- package/types/lib/ecosystems/caxa.d.ts.map +1 -1
- package/types/lib/ecosystems/cmakeResolver.d.ts +28 -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 +17 -1
- 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 +70 -4
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
- package/types/lib/ecosystems/jsEvidence.d.ts +5 -2
- package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/kosi.d.ts +13 -0
- package/types/lib/ecosystems/kosi.d.ts.map +1 -1
- package/types/lib/ecosystems/npmutils.d.ts +57 -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 +54 -2
- package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-js.d.ts +16 -5
- 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 +70 -4
- 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-rust.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 +164 -3
- package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rusi.d.ts +13 -0
- 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 +132 -15
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/evinser/swiftsem.d.ts +280 -15
- package/types/lib/evinser/swiftsem.d.ts.map +1 -1
- package/types/lib/helpers/bomSigner.d.ts +182 -13
- 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 +219 -5
- 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/cdxrs.d.ts +8 -3
- package/types/lib/inventory/cdxrs.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/depsUtils.d.ts +15 -0
- package/types/lib/inventory/depsUtils.d.ts.map +1 -1
- package/types/lib/inventory/display.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +121 -9
- 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/plugins.d.ts.map +1 -1
- package/types/lib/inventory/protobom.d.ts.map +1 -1
- package/types/lib/inventory/purl.d.ts +71 -2
- package/types/lib/inventory/purl.d.ts.map +1 -1
- package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
- package/types/lib/inventory/sbomDocument.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 +59 -1
- 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/binary.d.ts +30 -1
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts +29 -0
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/managers/oci.d.ts +17 -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 +77 -0
- package/types/lib/parsers/cmakeLists.d.ts.map +1 -0
- 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/largeJson.d.ts +52 -0
- package/types/lib/parsers/largeJson.d.ts.map +1 -0
- package/types/lib/parsers/npmrc.d.ts +5 -0
- package/types/lib/parsers/npmrc.d.ts.map +1 -1
- 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/bomValidator.d.ts.map +1 -1
- package/types/lib/validator/index.d.ts +18 -1
- package/types/lib/validator/index.d.ts.map +1 -1
- package/types/lib/validator/reporters/console.d.ts.map +1 -1
- package/types/lib/validator/reporters/json.d.ts.map +1 -1
- package/types/lib/validator/reporters/sarif.d.ts.map +1 -1
- package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +0 -462
package/lib/inventory/deps.js
CHANGED
|
@@ -1,17 +1,12 @@
|
|
|
1
|
-
import { lstatSync, readFileSync } from "node:fs";
|
|
1
|
+
import { lstatSync, readFileSync, statSync } from "node:fs";
|
|
2
2
|
import { homedir, tmpdir } from "node:os";
|
|
3
|
-
import {
|
|
4
|
-
delimiter as _delimiter,
|
|
5
|
-
sep as _sep,
|
|
6
|
-
basename,
|
|
7
|
-
dirname,
|
|
8
|
-
join,
|
|
9
|
-
} from "node:path";
|
|
3
|
+
import { delimiter as _delimiter, basename, dirname, join } from "node:path";
|
|
10
4
|
|
|
11
5
|
import { build, Purl } from "@cdxgen/cdx-purl";
|
|
12
6
|
import StreamZip from "node-stream-zip";
|
|
13
7
|
|
|
14
8
|
import { DEBUG_MODE, readEnvironmentVariable } from "../core/activity.js";
|
|
9
|
+
import { commandOutputText } from "../core/buildLedger.js";
|
|
15
10
|
import { parseMavenArgs } from "../core/env.js";
|
|
16
11
|
import {
|
|
17
12
|
getAllFiles,
|
|
@@ -23,9 +18,82 @@ import {
|
|
|
23
18
|
} from "../core/fs.js";
|
|
24
19
|
import { isWin } from "../core/paths.js";
|
|
25
20
|
import { xml2js } from "../parsers/xml.js";
|
|
21
|
+
import { noteBuildToolRateLimit } from "./buildToolRateLimit.js";
|
|
22
|
+
import {
|
|
23
|
+
inferMavenCoordinatesFromPath,
|
|
24
|
+
mavenLocalRepositories,
|
|
25
|
+
splitArtifactFileName,
|
|
26
|
+
} from "./jvmLocalRepos.js";
|
|
26
27
|
import { pypiBomRef } from "./purl.js";
|
|
28
|
+
import { findLicenseId, spdxLicenses } from "./spdx.js";
|
|
29
|
+
|
|
30
|
+
const jarNSMapping_cache = new Map();
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Whether a jar holds sources or javadoc rather than classes.
|
|
34
|
+
*
|
|
35
|
+
* @param {string} jarName Jar file name.
|
|
36
|
+
* @returns {boolean}
|
|
37
|
+
*/
|
|
38
|
+
export function isDocumentationJar(jarName) {
|
|
39
|
+
return jarName.endsWith("-sources.jar") || jarName.endsWith("-javadoc.jar");
|
|
40
|
+
}
|
|
27
41
|
|
|
28
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Size and modification time of a file, used to tell whether a memoised
|
|
44
|
+
* reading of it is still current.
|
|
45
|
+
*
|
|
46
|
+
* @param {string} file File path.
|
|
47
|
+
* @returns {string} Stamp, or an empty string when the file cannot be read.
|
|
48
|
+
*/
|
|
49
|
+
function jarFileStamp(file) {
|
|
50
|
+
try {
|
|
51
|
+
const stats = statSync(file);
|
|
52
|
+
return `${stats.size}:${stats.mtimeMs}`;
|
|
53
|
+
} catch (_err) {
|
|
54
|
+
return "";
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Build the purl of a jar. Eclipse p2 artifacts mirrored into a Maven
|
|
60
|
+
* repository keep their dedicated purl type qualifiers.
|
|
61
|
+
*
|
|
62
|
+
* @param {string} group groupId.
|
|
63
|
+
* @param {string} name artifactId.
|
|
64
|
+
* @param {string} version Version.
|
|
65
|
+
* @param {string} [classifier] Classifier.
|
|
66
|
+
* @returns {string} Purl string.
|
|
67
|
+
*/
|
|
68
|
+
function jarPurl(group, name, version, classifier = undefined) {
|
|
69
|
+
let namespace = group || null;
|
|
70
|
+
let type = "jar";
|
|
71
|
+
// See https://github.com/CycloneDX/cyclonedx-maven-plugin/issues/137
|
|
72
|
+
// and https://github.com/cdxgen/cdxgen/pull/510#issuecomment-1702551615
|
|
73
|
+
for (const [prefix, p2Type] of [
|
|
74
|
+
["p2.osgi.bundle", "osgi-bundle"],
|
|
75
|
+
["p2.eclipse.plugin", "eclipse-plugin"],
|
|
76
|
+
["p2.binary", "eclipse-executable"],
|
|
77
|
+
["p2.org.eclipse.update.feature", "eclipse-feature"],
|
|
78
|
+
]) {
|
|
79
|
+
if (namespace?.startsWith(prefix)) {
|
|
80
|
+
namespace = prefix;
|
|
81
|
+
type = p2Type;
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const qualifiers = { type };
|
|
86
|
+
if (classifier) {
|
|
87
|
+
qualifiers.classifier = classifier;
|
|
88
|
+
}
|
|
89
|
+
return new Purl({
|
|
90
|
+
type: "maven",
|
|
91
|
+
namespace,
|
|
92
|
+
name,
|
|
93
|
+
version: version || null,
|
|
94
|
+
qualifiers,
|
|
95
|
+
}).toString();
|
|
96
|
+
}
|
|
29
97
|
|
|
30
98
|
/**
|
|
31
99
|
* Collect maven dependencies
|
|
@@ -42,14 +110,20 @@ export async function collectMvnDependencies(
|
|
|
42
110
|
includeCacheDir = false,
|
|
43
111
|
) {
|
|
44
112
|
let jarNSMapping = {};
|
|
113
|
+
// MAVEN_CACHE_DIR when set, otherwise the local repository Maven itself
|
|
114
|
+
// uses for this project.
|
|
45
115
|
const MAVEN_CACHE_DIR =
|
|
46
116
|
readEnvironmentVariable("MAVEN_CACHE_DIR") ||
|
|
117
|
+
mavenLocalRepositories({ projectDir: basePath || undefined }).find((repo) =>
|
|
118
|
+
safeExistsSync(repo),
|
|
119
|
+
) ||
|
|
47
120
|
join(homedir(), ".m2", "repository");
|
|
48
121
|
const tempDir = safeMkdtempSync(join(tmpdir(), "mvn-deps-"));
|
|
122
|
+
// No -U: forcing update checks re-requests every maven-metadata.xml and
|
|
123
|
+
// every artifact Maven already knows is missing, once per module.
|
|
49
124
|
let copyArgs = [
|
|
50
125
|
"dependency:copy-dependencies",
|
|
51
126
|
`-DoutputDirectory=${tempDir}`,
|
|
52
|
-
"-U",
|
|
53
127
|
"-Dmdep.copyPom=true",
|
|
54
128
|
"-Dmdep.useRepositoryLayout=true",
|
|
55
129
|
"-Dmdep.includeScope=compile",
|
|
@@ -66,27 +140,41 @@ export async function collectMvnDependencies(
|
|
|
66
140
|
cwd: basePath,
|
|
67
141
|
shell: isWin,
|
|
68
142
|
});
|
|
143
|
+
const rateLimited = noteBuildToolRateLimit(
|
|
144
|
+
"maven",
|
|
145
|
+
commandOutputText(result),
|
|
146
|
+
{
|
|
147
|
+
command: `${basename(mavenCmd)} ${copyArgs.join(" ")}`,
|
|
148
|
+
exitCode: typeof result.status === "number" ? result.status : undefined,
|
|
149
|
+
},
|
|
150
|
+
);
|
|
69
151
|
if (result.status !== 0 || result.error) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
152
|
+
if (!rateLimited) {
|
|
153
|
+
console.error(result.stderr, result.error);
|
|
154
|
+
console.log(
|
|
155
|
+
"You can try the following remediation tips to resolve this error:\n",
|
|
156
|
+
);
|
|
157
|
+
console.log(
|
|
158
|
+
"1. Check if the correct version of maven is installed and available in the PATH. Check if the environment variable MVN_ARGS needs to be set.",
|
|
159
|
+
);
|
|
160
|
+
console.log(
|
|
161
|
+
"2. Perform 'mvn compile package' before invoking this command. Fix any errors found during this invocation.",
|
|
162
|
+
);
|
|
163
|
+
console.log(
|
|
164
|
+
"3. Ensure the temporary directory is available and has sufficient disk space to copy all the artifacts.",
|
|
165
|
+
);
|
|
166
|
+
}
|
|
83
167
|
} else {
|
|
84
168
|
jarNSMapping = await collectJarNS(tempDir);
|
|
85
169
|
}
|
|
86
170
|
}
|
|
87
171
|
if (includeCacheDir || basePath === MAVEN_CACHE_DIR) {
|
|
88
|
-
// slow operation
|
|
89
|
-
|
|
172
|
+
// slow operation. The project's own dependencies, copied above, take
|
|
173
|
+
// precedence over the rest of the cache.
|
|
174
|
+
jarNSMapping = {
|
|
175
|
+
...(await collectJarNS(MAVEN_CACHE_DIR)),
|
|
176
|
+
...jarNSMapping,
|
|
177
|
+
};
|
|
90
178
|
}
|
|
91
179
|
|
|
92
180
|
// Clean up
|
|
@@ -122,11 +210,18 @@ export async function collectJarNS(jarPath, pomPathMap = {}) {
|
|
|
122
210
|
: getAllFiles(jarPath, "**/*.jar");
|
|
123
211
|
if (jarFiles?.length) {
|
|
124
212
|
for (const jf of jarFiles) {
|
|
213
|
+
const jarName = basename(jf);
|
|
214
|
+
// Sources and javadoc jars hold no runtime classes. Reading them would
|
|
215
|
+
// attach their hashes to the main artifact's purl.
|
|
216
|
+
if (isDocumentationJar(jarName)) {
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
125
219
|
let pomname =
|
|
126
|
-
pomPathMap[
|
|
220
|
+
pomPathMap[jarName.replace(".jar", ".pom")] ||
|
|
127
221
|
jf.replace(".jar", ".pom");
|
|
128
222
|
let pomData;
|
|
129
223
|
let purl;
|
|
224
|
+
let manifestLicenses;
|
|
130
225
|
// In some cases, the pom name might be slightly different to the jar name
|
|
131
226
|
if (!safeExistsSync(pomname)) {
|
|
132
227
|
let searchDir = dirname(jf);
|
|
@@ -144,92 +239,54 @@ export async function collectJarNS(jarPath, pomPathMap = {}) {
|
|
|
144
239
|
// TODO: Replace with parsePom which contains pomPurl
|
|
145
240
|
pomData = parsePomXml(readFileSync(pomname, { encoding: "utf-8" }));
|
|
146
241
|
if (pomData) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
242
|
+
purl = jarPurl(
|
|
243
|
+
pomData.groupId,
|
|
244
|
+
pomData.artifactId,
|
|
245
|
+
pomData.version,
|
|
246
|
+
pomData.version
|
|
247
|
+
? splitArtifactFileName(
|
|
248
|
+
jarName,
|
|
249
|
+
pomData.artifactId,
|
|
250
|
+
pomData.version,
|
|
251
|
+
)?.classifier
|
|
252
|
+
: undefined,
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
} else {
|
|
256
|
+
const coordinates = inferMavenCoordinatesFromPath(jf);
|
|
257
|
+
if (coordinates?.extension === "jar") {
|
|
258
|
+
purl = jarPurl(
|
|
259
|
+
coordinates.group,
|
|
260
|
+
coordinates.name,
|
|
261
|
+
coordinates.version,
|
|
262
|
+
coordinates.classifier,
|
|
263
|
+
);
|
|
155
264
|
}
|
|
156
|
-
|
|
157
|
-
//
|
|
158
|
-
// .
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
jarVersion = tmpDirParts.pop();
|
|
169
|
-
if (jarVersion === "eclipse") {
|
|
170
|
-
jarVersion = tmpDirParts.pop();
|
|
265
|
+
// No POM sits beside this jar, but the jar carries its own Maven
|
|
266
|
+
// descriptor, so its metadata and manifest licence are read here,
|
|
267
|
+
// before any remote lookup. Only a jar whose path names its
|
|
268
|
+
// coordinates is read: they choose its descriptor, and an entry
|
|
269
|
+
// without a purl never becomes a component.
|
|
270
|
+
if (purl) {
|
|
271
|
+
const embedded = await readEmbeddedJarMetadata(jf, coordinates);
|
|
272
|
+
if (embedded?.pomXml) {
|
|
273
|
+
try {
|
|
274
|
+
pomData = parsePomXml(embedded.pomXml);
|
|
275
|
+
} catch (_err) {
|
|
276
|
+
pomData = undefined;
|
|
171
277
|
}
|
|
172
278
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
let qualifierType = "jar";
|
|
176
|
-
// Support for p2 bundles and plugins
|
|
177
|
-
// See https://github.com/CycloneDX/cyclonedx-maven-plugin/issues/137
|
|
178
|
-
// See https://github.com/cdxgen/cdxgen/pull/510#issuecomment-1702551615
|
|
179
|
-
if (jarGroupName.startsWith("p2.osgi.bundle")) {
|
|
180
|
-
jarGroupName = "p2.osgi.bundle";
|
|
181
|
-
qualifierType = "osgi-bundle";
|
|
182
|
-
} else if (jarGroupName.startsWith("p2.eclipse.plugin")) {
|
|
183
|
-
jarGroupName = "p2.eclipse.plugin";
|
|
184
|
-
qualifierType = "eclipse-plugin";
|
|
185
|
-
} else if (jarGroupName.startsWith("p2.binary")) {
|
|
186
|
-
jarGroupName = "p2.binary";
|
|
187
|
-
qualifierType = "eclipse-executable";
|
|
188
|
-
} else if (jarGroupName.startsWith("p2.org.eclipse.update.feature")) {
|
|
189
|
-
jarGroupName = "p2.org.eclipse.update.feature";
|
|
190
|
-
qualifierType = "eclipse-feature";
|
|
191
|
-
}
|
|
192
|
-
const purlObj = new Purl({
|
|
193
|
-
type: "maven",
|
|
194
|
-
namespace: jarGroupName || null,
|
|
195
|
-
name: jarFileName.replace(`-${jarVersion}`, ""),
|
|
196
|
-
version: jarVersion || null,
|
|
197
|
-
qualifiers: { type: qualifierType } || null,
|
|
198
|
-
});
|
|
199
|
-
purl = purlObj.toString();
|
|
200
|
-
}
|
|
201
|
-
} else if (jf.includes(join(".gradle", "caches"))) {
|
|
202
|
-
// Let's try our best to construct a purl for gradle cache entries of the form
|
|
203
|
-
// .gradle/caches/modules-2/files-2.1/org.xmlresolver/xmlresolver/4.2.0/f4dbdaa83d636dcac91c9003ffa7fb173173fe8d/xmlresolver-4.2.0-data.jar
|
|
204
|
-
const tmpA = jf.split(join("files-2.1", ""));
|
|
205
|
-
if (tmpA?.length) {
|
|
206
|
-
const tmpJarPath = tmpA[tmpA.length - 1];
|
|
207
|
-
// This would yield xmlresolver-4.2.0-data.jar
|
|
208
|
-
const jarFileName = basename(tmpJarPath).replace(".jar", "");
|
|
209
|
-
const tmpDirParts = dirname(tmpJarPath).split(_sep);
|
|
210
|
-
// This would remove the hash from the end of the directory name
|
|
211
|
-
tmpDirParts.pop();
|
|
212
|
-
// Retrieve the version
|
|
213
|
-
const jarVersion = tmpDirParts.pop();
|
|
214
|
-
const pkgName = jarFileName.replace(`-${jarVersion}`, "");
|
|
215
|
-
// The result would form the group name
|
|
216
|
-
let jarGroupName = tmpDirParts.join(".").replace(/^\./, "");
|
|
217
|
-
if (jarGroupName.includes(pkgName)) {
|
|
218
|
-
jarGroupName = jarGroupName.replace(`.${pkgName}`, "");
|
|
279
|
+
if (embedded?.manifestLicenses?.length) {
|
|
280
|
+
manifestLicenses = embedded.manifestLicenses;
|
|
219
281
|
}
|
|
220
|
-
const purlObj = new Purl({
|
|
221
|
-
type: "maven",
|
|
222
|
-
namespace: jarGroupName || null,
|
|
223
|
-
name: pkgName,
|
|
224
|
-
version: jarVersion || null,
|
|
225
|
-
qualifiers: { type: "jar" } || null,
|
|
226
|
-
});
|
|
227
|
-
purl = purlObj.toString();
|
|
228
282
|
}
|
|
229
283
|
}
|
|
230
|
-
// If we have a hit from the cache, use it.
|
|
231
|
-
|
|
232
|
-
|
|
284
|
+
// If we have a hit from the cache, use it. The entry is reused only
|
|
285
|
+
// for the same file, unchanged, so a rebuilt SNAPSHOT is read again.
|
|
286
|
+
const fileStamp = jarFileStamp(jf);
|
|
287
|
+
const cached = purl ? jarNSMapping_cache.get(purl) : undefined;
|
|
288
|
+
if (cached && cached.jarFile === jf && cached.stamp === fileStamp) {
|
|
289
|
+
jarNSMapping[purl] = cached.entry;
|
|
233
290
|
} else {
|
|
234
291
|
if (DEBUG_MODE) {
|
|
235
292
|
console.log(`Parsing ${jf}`);
|
|
@@ -254,10 +311,15 @@ export async function collectJarNS(jarPath, pomPathMap = {}) {
|
|
|
254
311
|
pom: pomData,
|
|
255
312
|
namespaces: nsList,
|
|
256
313
|
hashes,
|
|
314
|
+
...(manifestLicenses?.length ? { manifestLicenses } : {}),
|
|
257
315
|
};
|
|
258
316
|
// Retain in the global cache to speed up future lookups
|
|
259
317
|
if (purl) {
|
|
260
|
-
jarNSMapping_cache
|
|
318
|
+
jarNSMapping_cache.set(purl, {
|
|
319
|
+
jarFile: jf,
|
|
320
|
+
stamp: fileStamp,
|
|
321
|
+
entry: jarNSMapping[purl],
|
|
322
|
+
});
|
|
261
323
|
}
|
|
262
324
|
}
|
|
263
325
|
}
|
|
@@ -398,15 +460,24 @@ export function parseJarManifest(jarMetadata) {
|
|
|
398
460
|
if (!jarMetadata) {
|
|
399
461
|
return metadata;
|
|
400
462
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
463
|
+
// Manifests wrap at 72 bytes: a line that starts with a single space
|
|
464
|
+
// continues the header above it.
|
|
465
|
+
let name;
|
|
466
|
+
for (const rawLine of jarMetadata.split("\n")) {
|
|
467
|
+
const l = rawLine.replaceAll("\r", "");
|
|
468
|
+
if (l.startsWith(" ")) {
|
|
469
|
+
if (name !== undefined) {
|
|
470
|
+
metadata[name] += l.slice(1);
|
|
407
471
|
}
|
|
472
|
+
continue;
|
|
408
473
|
}
|
|
409
|
-
|
|
474
|
+
name = undefined;
|
|
475
|
+
const at = l.indexOf(": ");
|
|
476
|
+
if (at > 0) {
|
|
477
|
+
name = l.slice(0, at);
|
|
478
|
+
metadata[name] = l.slice(at + 2);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
410
481
|
return metadata;
|
|
411
482
|
}
|
|
412
483
|
|
|
@@ -505,22 +576,328 @@ export function parsePomProperties(pomProperties) {
|
|
|
505
576
|
/**
|
|
506
577
|
* Method to get pom properties from maven directory
|
|
507
578
|
*
|
|
579
|
+
* A shaded or fat jar carries a `pom.properties` for every artifact folded
|
|
580
|
+
* into it. When the jar's file name is known, the descriptor whose
|
|
581
|
+
* `artifactId-version` (or `artifactId`) starts the file name is the jar's
|
|
582
|
+
* own; otherwise the first descriptor is used, as before.
|
|
583
|
+
*
|
|
508
584
|
* @param {string} mavenDir Path to maven directory
|
|
585
|
+
* @param {string} [jarName] File name of the jar the directory came from
|
|
509
586
|
*
|
|
510
587
|
* @return array with pom properties
|
|
511
588
|
*/
|
|
512
|
-
export function getPomPropertiesFromMavenDir(mavenDir) {
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
589
|
+
export function getPomPropertiesFromMavenDir(mavenDir, jarName = undefined) {
|
|
590
|
+
return selectEmbeddedDescriptor(mavenDir, jarName)?.properties ?? {};
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Choose the embedded Maven descriptor of one jar from every descriptor an
|
|
595
|
+
* extracted `META-INF/maven` directory holds.
|
|
596
|
+
*
|
|
597
|
+
* @param {string} mavenDir Path to the extracted META-INF/maven directory
|
|
598
|
+
* @param {string} [jarName] File name of the jar the directory came from
|
|
599
|
+
* @returns {{file: string, properties: Object}|undefined} The chosen
|
|
600
|
+
* descriptor, or undefined when the directory holds none.
|
|
601
|
+
*/
|
|
602
|
+
function selectEmbeddedDescriptor(mavenDir, jarName = undefined) {
|
|
603
|
+
if (!safeExistsSync(mavenDir) || !lstatSync(mavenDir).isDirectory()) {
|
|
604
|
+
return undefined;
|
|
605
|
+
}
|
|
606
|
+
const pomPropertiesFiles = getAllFiles(mavenDir, "**/pom.properties").sort();
|
|
607
|
+
if (!pomPropertiesFiles?.length) {
|
|
608
|
+
return undefined;
|
|
609
|
+
}
|
|
610
|
+
const candidates = pomPropertiesFiles.map((f) => ({
|
|
611
|
+
file: f,
|
|
612
|
+
properties: parsePomProperties(readFileSync(f, { encoding: "utf-8" })),
|
|
613
|
+
}));
|
|
614
|
+
const parsed = candidates.map((c) => c.properties);
|
|
615
|
+
if (parsed.length > 1 && jarName) {
|
|
616
|
+
const stem = jarName.replace(/\.[jwe]ar$/, "");
|
|
617
|
+
const exact = candidates.find(
|
|
618
|
+
(c) =>
|
|
619
|
+
c.properties.artifactId &&
|
|
620
|
+
c.properties.version &&
|
|
621
|
+
(stem === `${c.properties.artifactId}-${c.properties.version}` ||
|
|
622
|
+
stem.startsWith(
|
|
623
|
+
`${c.properties.artifactId}-${c.properties.version}-`,
|
|
624
|
+
)),
|
|
625
|
+
);
|
|
626
|
+
if (exact) {
|
|
627
|
+
return exact;
|
|
628
|
+
}
|
|
629
|
+
// The longest matching artifactId wins: netty-common-4.1.jar belongs to
|
|
630
|
+
// netty-common, not to an embedded netty descriptor.
|
|
631
|
+
const byName = candidates
|
|
632
|
+
.filter(
|
|
633
|
+
(c) =>
|
|
634
|
+
c.properties.artifactId &&
|
|
635
|
+
(stem === c.properties.artifactId ||
|
|
636
|
+
stem.startsWith(`${c.properties.artifactId}-`)),
|
|
637
|
+
)
|
|
638
|
+
.sort(
|
|
639
|
+
(a, b) =>
|
|
640
|
+
b.properties.artifactId.length - a.properties.artifactId.length,
|
|
641
|
+
);
|
|
642
|
+
if (
|
|
643
|
+
byName.length === 1 ||
|
|
644
|
+
(byName.length > 1 &&
|
|
645
|
+
byName[0].properties.artifactId.length >
|
|
646
|
+
byName[1].properties.artifactId.length)
|
|
647
|
+
) {
|
|
648
|
+
return byName[0];
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
return candidates[0];
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/** Per-entry size bounds for the Maven descriptor files read out of a jar.
|
|
655
|
+
* The jar is untrusted input, and its declared entry sizes are the only cheap
|
|
656
|
+
* defence against an archive that expands without bound. */
|
|
657
|
+
const EMBEDDED_POM_PROPERTIES_LIMIT = 64 * 1024;
|
|
658
|
+
const EMBEDDED_POM_XML_LIMIT = 1024 * 1024;
|
|
659
|
+
const EMBEDDED_MANIFEST_LIMIT = 256 * 1024;
|
|
660
|
+
/** A shaded jar can fold in thousands of descriptors; reading a bounded
|
|
661
|
+
* number keeps a hostile archive from turning one zip listing into
|
|
662
|
+
* unbounded work. */
|
|
663
|
+
const EMBEDDED_DESCRIPTOR_LIMIT = 500;
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Read a file, refusing anything larger than the bound, so a jar whose
|
|
667
|
+
* descriptor declares a huge size is never fully read.
|
|
668
|
+
*
|
|
669
|
+
* @param {string} file File path.
|
|
670
|
+
* @param {number} limit Size bound in bytes.
|
|
671
|
+
* @returns {string|undefined} File contents, or undefined when missing or too large.
|
|
672
|
+
*/
|
|
673
|
+
function readBoundedFile(file, limit) {
|
|
674
|
+
try {
|
|
675
|
+
if (!safeExistsSync(file) || statSync(file).size > limit) {
|
|
676
|
+
return undefined;
|
|
677
|
+
}
|
|
678
|
+
return readFileSync(file, { encoding: "utf-8" });
|
|
679
|
+
} catch (_err) {
|
|
680
|
+
return undefined;
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* Split a manifest header value on a separator that is not inside double
|
|
686
|
+
* quotes. Quoted strings hold commas and semicolons of their own, as in
|
|
687
|
+
* `description="Apache License, Version 2.0"`.
|
|
688
|
+
*
|
|
689
|
+
* @param {string} value Header value.
|
|
690
|
+
* @param {string} separator One character.
|
|
691
|
+
* @returns {string[]} The parts, quotes kept.
|
|
692
|
+
*/
|
|
693
|
+
function splitOutsideQuotes(value, separator) {
|
|
694
|
+
const parts = [];
|
|
695
|
+
let current = "";
|
|
696
|
+
let quoted = false;
|
|
697
|
+
for (const ch of value) {
|
|
698
|
+
if (ch === '"') {
|
|
699
|
+
quoted = !quoted;
|
|
700
|
+
} else if (ch === separator && !quoted) {
|
|
701
|
+
parts.push(current);
|
|
702
|
+
current = "";
|
|
703
|
+
continue;
|
|
704
|
+
}
|
|
705
|
+
current += ch;
|
|
706
|
+
}
|
|
707
|
+
parts.push(current);
|
|
708
|
+
return parts;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
* Trim a manifest value and remove one pair of surrounding double quotes.
|
|
713
|
+
*
|
|
714
|
+
* @param {string} value Value.
|
|
715
|
+
* @returns {string} The bare value.
|
|
716
|
+
*/
|
|
717
|
+
function unquoteManifestValue(value) {
|
|
718
|
+
const trimmed = value.trim();
|
|
719
|
+
return trimmed.length >= 2 && trimmed.startsWith('"') && trimmed.endsWith('"')
|
|
720
|
+
? trimmed.slice(1, -1).trim()
|
|
721
|
+
: trimmed;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* The licences a MANIFEST.MF `Bundle-License` value names. The value is a
|
|
726
|
+
* comma-separated list; each item is an SPDX id, a licence name or a URL,
|
|
727
|
+
* optionally followed by `;link=` and `;description=` attributes, and any
|
|
728
|
+
* part may be quoted. Each item is mapped through findLicenseId; an item it
|
|
729
|
+
* cannot map is replaced by its `link` URL when one is given, which the
|
|
730
|
+
* licence URL lookup resolves later. `<<EXTERNAL>>` names no licence.
|
|
731
|
+
*
|
|
732
|
+
* @param {string} value Raw Bundle-License value.
|
|
733
|
+
* @returns {string[]} Licence ids, names or URLs, without duplicates.
|
|
734
|
+
*/
|
|
735
|
+
export function parseManifestLicenseList(value) {
|
|
736
|
+
if (!value || typeof value !== "string") {
|
|
737
|
+
return [];
|
|
738
|
+
}
|
|
739
|
+
const licenses = [];
|
|
740
|
+
for (const clause of splitOutsideQuotes(value, ",")) {
|
|
741
|
+
const [rawName, ...attributes] = splitOutsideQuotes(clause, ";");
|
|
742
|
+
const name = unquoteManifestValue(rawName);
|
|
743
|
+
if (!name || name === "<<EXTERNAL>>") {
|
|
744
|
+
continue;
|
|
745
|
+
}
|
|
746
|
+
let link;
|
|
747
|
+
for (const attribute of attributes) {
|
|
748
|
+
const at = attribute.indexOf("=");
|
|
749
|
+
if (at > 0 && attribute.slice(0, at).trim() === "link") {
|
|
750
|
+
link = unquoteManifestValue(attribute.slice(at + 1));
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
const id = findLicenseId(name);
|
|
754
|
+
const mapped = id !== name || spdxLicenses.includes(name);
|
|
755
|
+
const license = mapped ? id : link?.startsWith("http") ? link : name;
|
|
756
|
+
if (license && !licenses.includes(license)) {
|
|
757
|
+
licenses.push(license);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
return licenses;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
/**
|
|
764
|
+
* The Maven descriptor a jar carries for itself, read from an extracted
|
|
765
|
+
* `META-INF/maven` directory: the pom.properties that names the jar's own
|
|
766
|
+
* coordinates and the pom.xml stored beside it.
|
|
767
|
+
*
|
|
768
|
+
* @param {string} mavenDir Path to the extracted META-INF/maven directory
|
|
769
|
+
* @param {string} [jarName] File name of the jar the directory came from
|
|
770
|
+
* @returns {{properties: Object, pomXml?: string}|undefined} The chosen
|
|
771
|
+
* descriptor, with its pom.xml text when one is stored beside it.
|
|
772
|
+
*/
|
|
773
|
+
export function readEmbeddedMavenDescriptor(mavenDir, jarName = undefined) {
|
|
774
|
+
const selected = selectEmbeddedDescriptor(mavenDir, jarName);
|
|
775
|
+
if (!selected) {
|
|
776
|
+
return undefined;
|
|
777
|
+
}
|
|
778
|
+
const pomXml = readBoundedFile(
|
|
779
|
+
join(dirname(selected.file), "pom.xml"),
|
|
780
|
+
EMBEDDED_POM_XML_LIMIT,
|
|
781
|
+
);
|
|
782
|
+
return pomXml
|
|
783
|
+
? { properties: selected.properties, pomXml }
|
|
784
|
+
: { properties: selected.properties };
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
/**
|
|
788
|
+
* Choose the descriptor of the jar itself from the descriptors held inside
|
|
789
|
+
* its zip: the one whose pom.properties names the jar's own group and
|
|
790
|
+
* artifact, the matching version first. A shaded jar holds many descriptors
|
|
791
|
+
* whose coordinates belong to the folded-in artifacts, so when none matches,
|
|
792
|
+
* nothing is chosen rather than a dependency's descriptor.
|
|
793
|
+
*
|
|
794
|
+
* @param {Array<{name: string, properties: Object}>} descriptors Descriptors found in the zip.
|
|
795
|
+
* @param {{group: string, name: string, version: string}} coordinates The jar's own coordinates.
|
|
796
|
+
* @returns {{name: string, properties: Object}|undefined}
|
|
797
|
+
*/
|
|
798
|
+
function chooseZipDescriptor(descriptors, coordinates) {
|
|
799
|
+
if (!coordinates?.group || !coordinates.name) {
|
|
800
|
+
return undefined;
|
|
801
|
+
}
|
|
802
|
+
const byCoordinates = descriptors.filter(
|
|
803
|
+
(d) =>
|
|
804
|
+
d.properties.groupId === coordinates.group &&
|
|
805
|
+
d.properties.artifactId === coordinates.name,
|
|
806
|
+
);
|
|
807
|
+
const byVersion = byCoordinates.find(
|
|
808
|
+
(d) => coordinates.version && d.properties.version === coordinates.version,
|
|
809
|
+
);
|
|
810
|
+
return byVersion || byCoordinates[0];
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* The Maven descriptor and manifest licence a jar carries for itself, read
|
|
815
|
+
* from its zip without extracting it. Entry names are only matched and
|
|
816
|
+
* paired, never used as paths, and each entry is read only under a size
|
|
817
|
+
* bound, because the archive is untrusted input.
|
|
818
|
+
*
|
|
819
|
+
* @param {string} jarFile Path to the jar.
|
|
820
|
+
* @param {{group: string, name: string, version: string}} coordinates The jar's own coordinates, which choose its descriptor.
|
|
821
|
+
* @returns {Promise<{properties?: Object, pomXml?: string, manifestLicenses?: string[]}|undefined>}
|
|
822
|
+
*/
|
|
823
|
+
export async function readEmbeddedJarMetadata(jarFile, coordinates) {
|
|
824
|
+
let zip;
|
|
825
|
+
try {
|
|
826
|
+
zip = new StreamZip.async({ file: jarFile });
|
|
827
|
+
const entries = await zip.entries();
|
|
828
|
+
const decoder = new TextDecoder("utf-8");
|
|
829
|
+
const descriptors = [];
|
|
830
|
+
const pomEntries = new Map();
|
|
831
|
+
let manifestEntry;
|
|
832
|
+
for (const entry of Object.values(entries)) {
|
|
833
|
+
if (entry.isDirectory) {
|
|
834
|
+
continue;
|
|
835
|
+
}
|
|
836
|
+
if (
|
|
837
|
+
!manifestEntry &&
|
|
838
|
+
entry.name === "META-INF/MANIFEST.MF" &&
|
|
839
|
+
entry.size <= EMBEDDED_MANIFEST_LIMIT
|
|
840
|
+
) {
|
|
841
|
+
manifestEntry = entry;
|
|
842
|
+
continue;
|
|
843
|
+
}
|
|
844
|
+
if (!entry.name.startsWith("META-INF/maven/")) {
|
|
845
|
+
continue;
|
|
846
|
+
}
|
|
847
|
+
if (entry.name.endsWith("/pom.properties")) {
|
|
848
|
+
if (
|
|
849
|
+
descriptors.length >= EMBEDDED_DESCRIPTOR_LIMIT ||
|
|
850
|
+
entry.size > EMBEDDED_POM_PROPERTIES_LIMIT
|
|
851
|
+
) {
|
|
852
|
+
continue;
|
|
853
|
+
}
|
|
854
|
+
const text = decoder.decode(
|
|
855
|
+
Buffer.from(await zip.entryData(entry.name)),
|
|
856
|
+
);
|
|
857
|
+
descriptors.push({
|
|
858
|
+
name: entry.name,
|
|
859
|
+
properties: parsePomProperties(text),
|
|
860
|
+
});
|
|
861
|
+
} else if (entry.name.endsWith("/pom.xml")) {
|
|
862
|
+
const dir = entry.name.slice(0, -"pom.xml".length);
|
|
863
|
+
if (!pomEntries.has(dir)) {
|
|
864
|
+
pomEntries.set(dir, entry);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
const chosen = chooseZipDescriptor(descriptors, coordinates);
|
|
869
|
+
const result = {};
|
|
870
|
+
if (chosen) {
|
|
871
|
+
result.properties = chosen.properties;
|
|
872
|
+
const dir = chosen.name.slice(0, -"pom.properties".length);
|
|
873
|
+
const pomEntry = pomEntries.get(dir);
|
|
874
|
+
if (pomEntry && pomEntry.size <= EMBEDDED_POM_XML_LIMIT) {
|
|
875
|
+
result.pomXml = decoder.decode(
|
|
876
|
+
Buffer.from(await zip.entryData(pomEntry.name)),
|
|
877
|
+
);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
if (manifestEntry) {
|
|
881
|
+
const manifest = parseJarManifest(
|
|
882
|
+
decoder.decode(
|
|
883
|
+
Buffer.from(await zip.entryData("META-INF/MANIFEST.MF")),
|
|
884
|
+
),
|
|
885
|
+
);
|
|
886
|
+
const licenses = parseManifestLicenseList(manifest["Bundle-License"]);
|
|
887
|
+
if (licenses.length) {
|
|
888
|
+
result.manifestLicenses = licenses;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
return chosen || manifestEntry ? result : undefined;
|
|
892
|
+
} catch (_err) {
|
|
893
|
+
return undefined;
|
|
894
|
+
} finally {
|
|
895
|
+
try {
|
|
896
|
+
await zip.close();
|
|
897
|
+
} catch (_err) {
|
|
898
|
+
// An already-failed zip reports its close as well.
|
|
521
899
|
}
|
|
522
900
|
}
|
|
523
|
-
return pomProperties;
|
|
524
901
|
}
|
|
525
902
|
|
|
526
903
|
/**
|