diff --git a/src/providers/java_maven.js b/src/providers/java_maven.js index 2f0d79c..4b67245 100644 --- a/src/providers/java_maven.js +++ b/src/providers/java_maven.js @@ -72,36 +72,62 @@ function dotGraphToPurl(root) { let name = parts[1] let version = parts[3].replaceAll("\"","") return new PackageURL('maven',group,name,version,undefined,undefined); - - } /** * - * @param {String} dotGraphList Dot Graph tree String of the pom.xml manifest + * @param {String} dotGraphList Text graph String of the pom.xml manifest * @param {[String]} ignoredDeps List of ignored dependencies to be omitted from sbom * @return {String} formatted sbom Json String with all dependencies * @private */ -function createSbomFileFromDotGraphFormat(dotGraphList, ignoredDeps) { +function createSbomFileFromTextFormat(dotGraphList, ignoredDeps) { + let lines = dotGraphList.split(EOL); // get root component - let lines = dotGraphList.replaceAll(";","").split(EOL); - let root = lines[0].split("\"")[1]; + let root = lines[0]; let rootPurl = dotGraphToPurl(root); - lines.splice(0,1); - let sbom = new Sbom() - sbom.addRoot(rootPurl) - lines.forEach(pair => { - if(pair.trim() !== "}") { - let thePair = pair.split("->") - if(thePair.length === 2) { - let from = dotGraphToPurl(thePair[0].trim()) - let to = dotGraphToPurl(thePair[1].trim()) - sbom.addDependency(sbom.purlToComponent(from), to) - } - } - }) - return sbom.filterIgnoredDepsIncludingVersion(ignoredDeps).getAsJsonString() + let sbom = new Sbom(); + sbom.addRoot(rootPurl); + calculateTree(root, 0, lines.slice(1), sbom); + console.log(sbom.getAsJsonString()); + return sbom.filterIgnoredDepsIncludingVersion(ignoredDeps).getAsJsonString(); +} + +const DEP_REGEX = /([\-a-zA-Z0-9\.]+:[\-a-zA-Z0-9\.]+:[\-a-zA-Z0-9\.]+:[\-a-zA-Z0-9\.]+:[\-a-zA-Z0-9\.]+)/g; + +function calculateTree(src, srcDepth, lines, sbom) { + if(lines.length === 0) { + return + } + let index = 0; + let target = lines[index]; + let targetDepth = getDepth(target); + while(targetDepth > srcDepth && index < lines.length) { + if(targetDepth == srcDepth + 1) { + let from = dotGraphToPurl(parseDep(src)) + let to = dotGraphToPurl(parseDep(target)) + sbom.addDependency(sbom.purlToComponent(from), to) + } else { + calculateTree(lines[index-1], getDepth(lines[index-1]), lines.slice(index), sbom) + } + target = lines[++index]; + targetDepth = getDepth(target); + } +} + +function getDepth(line) { + if(line === undefined) { + return -1; + } + return ((line.indexOf('-') - 1) / 3) + 1; +} + +function parseDep(line) { + let match = line.match(DEP_REGEX); + if(match) { + return match[0]; + } + return line; } /** @@ -129,8 +155,8 @@ function createSbomStackAnalysis(manifest, opts = {}) { // create dependency graph in a temp file let tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'exhort_')) let tmpDepTree = path.join(tmpDir, 'mvn_deptree.txt') - // build initial command - let depTreeCmd = `${mvn} -q dependency:tree -DoutputType=dot -DoutputFile=${tmpDepTree} -f ${manifest}` + // build initial command (dot outputType is not available for verbose mode) + let depTreeCmd = `${mvn} -q dependency:tree -Dverbose -DoutputType=text -DoutputFile=${tmpDepTree} -f ${manifest}` // exclude ignored dependencies, exclude format is groupId:artifactId:scope:version. // version and scope are marked as '*' if not specified (we do not use scope yet) let ignoredDeps = new Array() @@ -148,7 +174,7 @@ function createSbomStackAnalysis(manifest, opts = {}) { }) // read dependency tree from temp file let content= fs.readFileSync(`${tmpDepTree}`) - let sbom = createSbomFileFromDotGraphFormat(content.toString(),ignoredDeps); + let sbom = createSbomFileFromTextFormat(content.toString(),ignoredDeps); // delete temp file and directory fs.rmSync(tmpDir, {recursive: true, force: true}) // return dependency graph as string diff --git a/test/providers/java_maven.test.js b/test/providers/java_maven.test.js index ed2ca11..b66c7bd 100644 --- a/test/providers/java_maven.test.js +++ b/test/providers/java_maven.test.js @@ -25,7 +25,8 @@ suite('testing the java-maven data provider', () => { "pom_deps_with_ignore_on_wrong", "pom_deps_with_no_ignore", "poms_deps_with_ignore_long", - "poms_deps_with_no_ignore_long" + "poms_deps_with_no_ignore_long", + "poms_deps_with_no_ignore_common_paths" ].forEach(testCase => { let scenario = testCase.replace('pom_deps_', '').replaceAll('_', ' ') // test(`custom adhoc test`, async () => { diff --git a/test/providers/tst_manifests/maven/pom_deps_with_no_ignore_common_paths/component_analysis_expected_sbom.json b/test/providers/tst_manifests/maven/pom_deps_with_no_ignore_common_paths/component_analysis_expected_sbom.json new file mode 100644 index 0000000..17ba54d --- /dev/null +++ b/test/providers/tst_manifests/maven/pom_deps_with_no_ignore_common_paths/component_analysis_expected_sbom.json @@ -0,0 +1,59 @@ +{ + "bomFormat": "CycloneDX", + "specVersion": "1.4", + "version": 1, + "metadata": { + "timestamp": "2023-09-01T15:54:21.462Z", + "component": { + "group": "pom-with-deps-no-ignore", + "name": "pom-with-dependency-not-ignored-for-tests", + "version": "0.0.1", + "purl": "pkg:maven/pom-with-deps-no-ignore/pom-with-dependency-not-ignored-for-tests@0.0.1", + "type": "application", + "bom-ref": "pkg:maven/pom-with-deps-no-ignore/pom-with-dependency-not-ignored-for-tests@0.0.1" + } + }, + "components": [ + { + "group": "pom-with-deps-no-ignore", + "name": "pom-with-dependency-not-ignored-for-tests", + "version": "0.0.1", + "purl": "pkg:maven/pom-with-deps-no-ignore/pom-with-dependency-not-ignored-for-tests@0.0.1", + "type": "application", + "bom-ref": "pkg:maven/pom-with-deps-no-ignore/pom-with-dependency-not-ignored-for-tests@0.0.1" + }, + { + "group": "com.fasterxml.jackson.core", + "name": "jackson-databind", + "version": "2.14.1", + "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.14.1", + "type": "library", + "bom-ref": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.14.1" + }, + { + "group": "org.infinispan.protostream", + "name": "protostream", + "version": "4.6.4.Final", + "purl": "pkg:maven/org.infinispan.protostream/protostream@4.6.4.Final", + "type": "library", + "bom-ref": "pkg:maven/org.infinispan.protostream/protostream@4.6.4.Final" + } + ], + "dependencies": [ + { + "ref": "pkg:maven/pom-with-deps-no-ignore/pom-with-dependency-not-ignored-for-tests@0.0.1", + "dependsOn": [ + "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.14.1", + "pkg:maven/org.infinispan.protostream/protostream@4.6.4.Final" + ] + }, + { + "ref": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.14.1", + "dependsOn": [] + }, + { + "ref": "pkg:maven/org.infinispan.protostream/protostream@4.6.4.Final", + "dependsOn": [] + } + ] +} \ No newline at end of file diff --git a/test/providers/tst_manifests/maven/pom_deps_with_no_ignore_common_paths/pom.xml b/test/providers/tst_manifests/maven/pom_deps_with_no_ignore_common_paths/pom.xml new file mode 100644 index 0000000..0fa6d21 --- /dev/null +++ b/test/providers/tst_manifests/maven/pom_deps_with_no_ignore_common_paths/pom.xml @@ -0,0 +1,21 @@ + + + 4.0.0 + pom-with-deps-no-ignore + pom-with-dependency-not-ignored-for-tests + 0.0.1 + + + com.fasterxml.jackson.core + jackson-databind + 2.14.1 + + + org.infinispan.protostream + protostream + 4.6.4.Final + + + \ No newline at end of file diff --git a/test/providers/tst_manifests/maven/pom_deps_with_no_ignore_common_paths/stack_analysis_expected_sbom.json b/test/providers/tst_manifests/maven/pom_deps_with_no_ignore_common_paths/stack_analysis_expected_sbom.json new file mode 100644 index 0000000..469389d --- /dev/null +++ b/test/providers/tst_manifests/maven/pom_deps_with_no_ignore_common_paths/stack_analysis_expected_sbom.json @@ -0,0 +1,127 @@ +{ + "bomFormat": "CycloneDX", + "specVersion": "1.4", + "version": 1, + "metadata": { + "timestamp": "2023-09-01T15:54:21.462Z", + "component": { + "group": "pom-with-deps-no-ignore", + "name": "pom-with-dependency-not-ignored-for-tests", + "version": "0.0.1", + "purl": "pkg:maven/pom-with-deps-no-ignore/pom-with-dependency-not-ignored-for-tests@0.0.1", + "type": "application", + "bom-ref": "pkg:maven/pom-with-deps-no-ignore/pom-with-dependency-not-ignored-for-tests@0.0.1" + } + }, + "components": [ + { + "group": "pom-with-deps-no-ignore", + "name": "pom-with-dependency-not-ignored-for-tests", + "version": "0.0.1", + "purl": "pkg:maven/pom-with-deps-no-ignore/pom-with-dependency-not-ignored-for-tests@0.0.1", + "type": "application", + "bom-ref": "pkg:maven/pom-with-deps-no-ignore/pom-with-dependency-not-ignored-for-tests@0.0.1" + }, + { + "group": "com.fasterxml.jackson.core", + "name": "jackson-databind", + "version": "2.14.1", + "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.14.1", + "type": "library", + "bom-ref": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.14.1" + }, + { + "group": "com.fasterxml.jackson.core", + "name": "jackson-annotations", + "version": "2.14.1", + "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-annotations@2.14.1", + "type": "library", + "bom-ref": "pkg:maven/com.fasterxml.jackson.core/jackson-annotations@2.14.1" + }, + { + "group": "com.fasterxml.jackson.core", + "name": "jackson-core", + "version": "2.14.1", + "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-core@2.14.1", + "type": "library", + "bom-ref": "pkg:maven/com.fasterxml.jackson.core/jackson-core@2.14.1" + }, + { + "group": "org.infinispan.protostream", + "name": "protostream", + "version": "4.6.4.Final", + "purl": "pkg:maven/org.infinispan.protostream/protostream@4.6.4.Final", + "type": "library", + "bom-ref": "pkg:maven/org.infinispan.protostream/protostream@4.6.4.Final" + }, + { + "group": "org.jboss.logging", + "name": "jboss-logging", + "version": "3.5.0.Final", + "purl": "pkg:maven/org.jboss.logging/jboss-logging@3.5.0.Final", + "type": "library", + "bom-ref": "pkg:maven/org.jboss.logging/jboss-logging@3.5.0.Final" + }, + { + "group": "com.squareup", + "name": "protoparser", + "version": "4.0.3", + "purl": "pkg:maven/com.squareup/protoparser@4.0.3", + "type": "library", + "bom-ref": "pkg:maven/com.squareup/protoparser@4.0.3" + }, + { + "group": "org.javassist", + "name": "javassist", + "version": "3.29.1-GA", + "purl": "pkg:maven/org.javassist/javassist@3.29.1-GA", + "type": "library", + "bom-ref": "pkg:maven/org.javassist/javassist@3.29.1-GA" + } + ], + "dependencies": [ + { + "ref": "pkg:maven/pom-with-deps-no-ignore/pom-with-dependency-not-ignored-for-tests@0.0.1", + "dependsOn": [ + "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.14.1", + "pkg:maven/org.infinispan.protostream/protostream@4.6.4.Final" + ] + }, + { + "ref": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.14.1", + "dependsOn": [ + "pkg:maven/com.fasterxml.jackson.core/jackson-annotations@2.14.1", + "pkg:maven/com.fasterxml.jackson.core/jackson-core@2.14.1" + ] + }, + { + "ref": "pkg:maven/com.fasterxml.jackson.core/jackson-annotations@2.14.1", + "dependsOn": [] + }, + { + "ref": "pkg:maven/com.fasterxml.jackson.core/jackson-core@2.14.1", + "dependsOn": [] + }, + { + "ref": "pkg:maven/org.infinispan.protostream/protostream@4.6.4.Final", + "dependsOn": [ + "pkg:maven/org.jboss.logging/jboss-logging@3.5.0.Final", + "pkg:maven/com.squareup/protoparser@4.0.3", + "pkg:maven/com.fasterxml.jackson.core/jackson-core@2.14.1", + "pkg:maven/org.javassist/javassist@3.29.1-GA" + ] + }, + { + "ref": "pkg:maven/org.jboss.logging/jboss-logging@3.5.0.Final", + "dependsOn": [] + }, + { + "ref": "pkg:maven/com.squareup/protoparser@4.0.3", + "dependsOn": [] + }, + { + "ref": "pkg:maven/org.javassist/javassist@3.29.1-GA", + "dependsOn": [] + } + ] +} \ No newline at end of file