Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 7 additions & 8 deletions src/providers/base_java.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export default class Base_Java {
}
let index = 0;
let target = lines[index];
let targetDepth = this.#getDepth(target);
let targetDepth = this._getDepth(target);
while (targetDepth > srcDepth && index < lines.length) {
if (targetDepth === srcDepth + 1) {
let from = this.parseDep(src);
Expand All @@ -71,23 +71,22 @@ export default class Base_Java {
sbom.addDependency(from, to)
}
} else {
this.parseDependencyTree(lines[index - 1], this.#getDepth(lines[index - 1]), lines.slice(index), sbom)
this.parseDependencyTree(lines[index - 1], this._getDepth(lines[index - 1]), lines.slice(index), sbom)
}
target = lines[++index];
targetDepth = this.#getDepth(target);
targetDepth = this._getDepth(target);
}
}

/**
* Calculates how deep in the graph is the given line
* @param {string} line - line to calculate the depth from
* @returns {number} The calculated depth
* @private
* @protected
*/
#getDepth(line) {
if (line === undefined) {
return -1;
}
_getDepth(line) {
if (!line || line.trim() === '') { return -1; }
if (line.match(/^\w/)) { return 0; }
return ((line.indexOf('-') - 1) / 3) + 1;
}

Expand Down
77 changes: 77 additions & 0 deletions src/providers/java_maven.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ export default class Java_maven extends Base_java {
/** @type [Dependency] */
let dependencies = this.#getDependencies(tmpEffectivePom)
.filter(d => !this.#dependencyIn(d, ignored))
dependencies = this.#resolveVersionRanges(dependencies, manifestPath, opts)
let sbom = new Sbom();
let rootDependency = this.#getRootFromPom(tmpEffectivePom, manifestPath);
let purlRoot = this.toPurl(rootDependency.groupId, rootDependency.artifactId, rootDependency.version)
Expand Down Expand Up @@ -309,6 +310,82 @@ export default class Java_maven extends Base_java {
#dependencyIn(dep, deps) {
return deps.filter(d => dep.artifactId === d.artifactId && dep.groupId === d.groupId && dep.scope === d.scope).length > 0
}

/**
* Returns true if the given version string is a Maven version range
* (starts with '[' or '(').
* @param {string} version
* @returns {boolean}
* @private
*/
#isVersionRange(version) {
return typeof version === 'string' && (version.startsWith('[') || version.startsWith('('))
}

/**
* Resolves Maven version ranges in the given dependency list by running
* maven-dependency-plugin:tree and reading the concrete versions it selects.
* If no dependency uses a version range, returns the list unchanged.
* @param {Dependency[]} dependencies
* @param {string} manifestPath
* @param {object} opts
* @returns {Dependency[]}
* @private
*/
#resolveVersionRanges(dependencies, manifestPath, opts = {}) {
// short-circuit if no dependency has a version range
if (!dependencies.some(dep => this.#isVersionRange(dep.version))) {
return dependencies
}

const mvn = this.selectToolBinary(manifestPath, opts)
const mvnArgs = JSON.parse(getCustom('TRUSTIFY_DA_MVN_ARGS', '[]', opts));
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'trustify_da_range_'))
const tmpDepTree = path.join(tmpDir, 'mvn_deptree_ranges.txt')

try {
this._invokeCommand(mvn, [
'-q',
'org.apache.maven.plugins:maven-dependency-plugin:3.6.0:tree',
'-Dscope=compile',
'-DoutputType=text',
`-DoutputFile=${tmpDepTree}`,
...mvnArgs
], { cwd: path.dirname(manifestPath) })

const content = fs.readFileSync(tmpDepTree)
const lines = content.toString().split(EOL).filter(l => l.trim() !== '')

// Build a map of groupId:artifactId -> resolved version from depth-1 entries
/** @type {Map<string, string>} */
const resolvedVersions = new Map()
for (const line of lines) {
if (this._getDepth(line) === 1) {
const purl = this.parseDep(line)
resolvedVersions.set(`${purl.namespace}:${purl.name}`, purl.version)
}
}

// Replace version ranges with resolved concrete versions
return dependencies.map(dep => {
if (this.#isVersionRange(dep.version)) {
const key = `${dep.groupId}:${dep.artifactId}`
const resolved = resolvedVersions.get(key)
if (resolved) {
return { ...dep, version: resolved }
}
}
return dep
})
} catch (error) {
if (process.env["TRUSTIFY_DA_DEBUG"] === "true") {
console.error("Failed to resolve Maven version ranges: " + error.message)
}
return dependencies
} finally {
fs.rmSync(tmpDir, { recursive: true, force: true })
}
}
}

const DEFAULT_MAVEN_DISCOVERY_IGNORE = [
Expand Down
3 changes: 2 additions & 1 deletion test/providers/java_maven.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ suite('testing the java-maven data provider', async () => {
"pom_deps_with_no_ignore",
"poms_deps_with_ignore_long",
"poms_deps_with_no_ignore_long",
"pom_deps_with_no_ignore_common_paths"
"pom_deps_with_no_ignore_common_paths",
"pom_deps_with_version_range"
].forEach(testCase => {
let scenario = testCase.replace('pom_deps_', '').replaceAll('_', ' ')

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.4",
"version": 1,
"metadata": {
"timestamp": "2023-08-07T00:00:00.000Z",
"component": {
"group": "pom-with-deps-version-range",
"name": "pom-with-version-range-for-tests",
"version": "0.0.1",
"purl": "pkg:maven/pom-with-deps-version-range/pom-with-version-range-for-tests@0.0.1",
"type": "application",
"bom-ref": "pkg:maven/pom-with-deps-version-range/pom-with-version-range-for-tests@0.0.1"
}
},
"components": [
{
"group": "log4j",
"name": "log4j",
"version": "1.2.17",
"purl": "pkg:maven/log4j/log4j@1.2.17",
"type": "library",
"bom-ref": "pkg:maven/log4j/log4j@1.2.17"
},
{
"group": "org.xerial.snappy",
"name": "snappy-java",
"version": "1.1.10.0",
"purl": "pkg:maven/org.xerial.snappy/snappy-java@1.1.10.0",
"type": "library",
"bom-ref": "pkg:maven/org.xerial.snappy/snappy-java@1.1.10.0"
}
],
"dependencies": [
{
"ref": "pkg:maven/pom-with-deps-version-range/pom-with-version-range-for-tests@0.0.1",
"dependsOn": [
"pkg:maven/log4j/log4j@1.2.17",
"pkg:maven/org.xerial.snappy/snappy-java@1.1.10.0"
]
},
{
"ref": "pkg:maven/log4j/log4j@1.2.17",
"dependsOn": []
},
{
"ref": "pkg:maven/org.xerial.snappy/snappy-java@1.1.10.0",
"dependsOn": []
}
]
}
Loading
Loading