Skip to content

Commit

Permalink
feat: Ignore parent component for types with empty components (#1399)
Browse files Browse the repository at this point in the history
* Bug fix

Signed-off-by: Prabhu Subramanian <[email protected]>

* Add tests

Signed-off-by: Prabhu Subramanian <[email protected]>

---------

Signed-off-by: Prabhu Subramanian <[email protected]>
  • Loading branch information
prabhu authored Oct 1, 2024
1 parent 39bcb3c commit 6aa3175
Show file tree
Hide file tree
Showing 7 changed files with 319 additions and 30 deletions.
57 changes: 30 additions & 27 deletions lib/cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1315,6 +1315,7 @@ export async function createJavaBom(path, options) {
mvnArgs = mvnArgs.concat("-DschemaVersion=1.4");
}
const firstPom = pomFiles.length ? pomFiles[0] : undefined;
let mavenCmd = getMavenCommand(path, path);
for (const f of pomFiles) {
const basePath = dirname(f);
const settingsXml = join(basePath, "settings.xml");
Expand All @@ -1323,7 +1324,9 @@ export async function createJavaBom(path, options) {
`maven settings.xml found in ${basePath}. Please set the MVN_ARGS environment variable based on the full mvn build command used for this project.\nExample: MVN_ARGS='--settings ${settingsXml}'`,
);
}
const mavenCmd = getMavenCommand(basePath, path);
if (mavenCmd?.endsWith("mvn")) {
mavenCmd = getMavenCommand(basePath, path);
}
// Should we attempt to resolve class names
if (options.resolveClass || options.deep) {
const tmpjarNSMapping = await collectMvnDependencies(
Expand Down Expand Up @@ -5693,7 +5696,7 @@ export async function createMultiXBom(pathList, options) {
}
if (hasAnyProjectType(["oci", "rust"], options)) {
bomData = await createRustBom(path, options);
if (bomData?.bomJson?.components) {
if (bomData?.bomJson?.components?.length) {
if (DEBUG_MODE) {
console.log(
`Found ${bomData.bomJson.components.length} rust packages at ${path}`,
Expand All @@ -5717,7 +5720,7 @@ export async function createMultiXBom(pathList, options) {
}
if (hasAnyProjectType(["oci", "php"], options)) {
bomData = createPHPBom(path, options);
if (bomData?.bomJson?.components) {
if (bomData?.bomJson?.components?.length) {
if (DEBUG_MODE) {
console.log(
`Found ${bomData.bomJson.components.length} php packages at ${path}`,
Expand All @@ -5735,7 +5738,7 @@ export async function createMultiXBom(pathList, options) {
}
if (hasAnyProjectType(["oci", "ruby"], options)) {
bomData = await createRubyBom(path, options);
if (bomData?.bomJson?.components) {
if (bomData?.bomJson?.components?.length) {
if (DEBUG_MODE) {
console.log(
`Found ${bomData.bomJson.components.length} ruby packages at ${path}`,
Expand Down Expand Up @@ -5775,7 +5778,7 @@ export async function createMultiXBom(pathList, options) {
}
if (hasAnyProjectType(["oci", "dart"], options)) {
bomData = await createDartBom(path, options);
if (bomData?.bomJson?.components) {
if (bomData?.bomJson?.components?.length) {
if (DEBUG_MODE) {
console.log(
`Found ${bomData.bomJson.components.length} pub packages at ${path}`,
Expand All @@ -5793,7 +5796,7 @@ export async function createMultiXBom(pathList, options) {
}
if (hasAnyProjectType(["oci", "haskell"], options)) {
bomData = createHaskellBom(path, options);
if (bomData?.bomJson?.components) {
if (bomData?.bomJson?.components?.length) {
if (DEBUG_MODE) {
console.log(
`Found ${bomData.bomJson.components.length} hackage packages at ${path}`,
Expand All @@ -5811,7 +5814,7 @@ export async function createMultiXBom(pathList, options) {
}
if (hasAnyProjectType(["oci", "elixir"], options)) {
bomData = createElixirBom(path, options);
if (bomData?.bomJson?.components) {
if (bomData?.bomJson?.components?.length) {
if (DEBUG_MODE) {
console.log(
`Found ${bomData.bomJson.components.length} mix packages at ${path}`,
Expand All @@ -5829,7 +5832,7 @@ export async function createMultiXBom(pathList, options) {
}
if (hasAnyProjectType(["oci", "c"], options)) {
bomData = createCppBom(path, options);
if (bomData?.bomJson?.components) {
if (bomData?.bomJson?.components?.length) {
if (DEBUG_MODE) {
console.log(
`Found ${bomData.bomJson.components.length} cpp packages at ${path}`,
Expand All @@ -5847,7 +5850,7 @@ export async function createMultiXBom(pathList, options) {
}
if (hasAnyProjectType(["oci", "clojure"], options)) {
bomData = createClojureBom(path, options);
if (bomData?.bomJson?.components) {
if (bomData?.bomJson?.components?.length) {
if (DEBUG_MODE) {
console.log(
`Found ${bomData.bomJson.components.length} clojure packages at ${path}`,
Expand All @@ -5865,7 +5868,7 @@ export async function createMultiXBom(pathList, options) {
}
if (hasAnyProjectType(["oci", "github"], options)) {
bomData = createGitHubBom(path, options);
if (bomData?.bomJson?.components) {
if (bomData?.bomJson?.components?.length) {
if (DEBUG_MODE) {
console.log(
`Found ${bomData.bomJson.components.length} GitHub action packages at ${path}`,
Expand All @@ -5883,7 +5886,7 @@ export async function createMultiXBom(pathList, options) {
}
if (hasAnyProjectType(["oci", "cloudbuild"], options)) {
bomData = createCloudBuildBom(path, options);
if (bomData?.bomJson?.components) {
if (bomData?.bomJson?.components?.length) {
if (DEBUG_MODE) {
console.log(
`Found ${bomData.bomJson.components.length} CloudBuild configuration at ${path}`,
Expand Down Expand Up @@ -5917,22 +5920,22 @@ export async function createMultiXBom(pathList, options) {
}
}
}
// Jar scanning is enabled by default
// See #330
bomData = await createJarBom(path, options);
if (bomData?.bomJson?.components?.length) {
if (DEBUG_MODE) {
console.log(
`Found ${bomData.bomJson.components.length} jar packages at ${path}`,
);
}
components = components.concat(bomData.bomJson.components);
dependencies = dependencies.concat(bomData.bomJson.dependencies);
if (
bomData.parentComponent &&
Object.keys(bomData.parentComponent).length
) {
parentSubComponents.push(bomData.parentComponent);
if (hasAnyProjectType(["oci", "jar", "war", "ear"], options)) {
bomData = await createJarBom(path, options);
if (bomData?.bomJson?.components?.length) {
if (DEBUG_MODE) {
console.log(
`Found ${bomData.bomJson.components.length} jar packages at ${path}`,
);
}
components = components.concat(bomData.bomJson.components);
dependencies = dependencies.concat(bomData.bomJson.dependencies);
if (
bomData.parentComponent &&
Object.keys(bomData.parentComponent).length
) {
parentSubComponents.push(bomData.parentComponent);
}
}
}
// Collect any crypto keys
Expand Down
32 changes: 31 additions & 1 deletion lib/helpers/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,23 @@ export function hasAnyProjectType(projectTypes, options, defaultStatus = true) {
) {
return defaultStatus;
}
// If only exclude type is specified, then do not allow oci type
if (
(projectTypes?.length === 1 || !defaultStatus) &&
!options.projectType?.length &&
options.excludeType?.length
) {
const ret =
!projectTypes.includes("oci") &&
!projectTypes.includes("oci-dir") &&
!projectTypes.includes("os") &&
!projectTypes.includes("docker") &&
!options.excludeType.includes("oci");
if (ret && DEBUG_MODE) {
console.log("Project types", projectTypes);
}
return ret;
}
const allProjectTypes = [...projectTypes];
// Convert the project types into base types
const baseProjectTypes = [];
Expand Down Expand Up @@ -439,6 +456,9 @@ export function hasAnyProjectType(projectTypes, options, defaultStatus = true) {
.length
);
}
if (shouldInclude && DEBUG_MODE) {
console.log("Project types", projectTypes);
}
return shouldInclude;
}

Expand Down Expand Up @@ -2433,6 +2453,7 @@ export function parseMavenTree(rawOutput, pomFile) {
const tmpA = rawOutput.split("\n");
let last_level = 0;
let last_purl = "";
let first_purl = undefined;
const stack = [];
tmpA.forEach((l) => {
l = l.replace("\r", "");
Expand Down Expand Up @@ -2485,6 +2506,9 @@ export function parseMavenTree(rawOutput, pomFile) {
null,
).toString();
const key = purlString;
if (!first_purl) {
first_purl = purlString;
}
if (!keys_cache[key]) {
keys_cache[key] = key;
const properties = [];
Expand Down Expand Up @@ -2540,7 +2564,9 @@ export function parseMavenTree(rawOutput, pomFile) {
for (let i = level; i <= last_level; i++) {
stack.pop();
}
const last_stack = stack[stack.length - 1];
const last_stack = stack.length
? stack[stack.length - 1]
: first_purl;
const cnodes = level_trees[last_stack] || [];
cnodes.push(purlString);
level_trees[last_stack] = cnodes;
Expand All @@ -2558,7 +2584,11 @@ export function parseMavenTree(rawOutput, pomFile) {
dependsOn: level_trees[lk],
});
}
const parentComponent = deps?.length
? { ...deps[0], type: "application" }
: {};
return {
parentComponent,
pkgList: deps,
dependenciesList,
};
Expand Down
96 changes: 96 additions & 0 deletions lib/helpers/utils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,56 @@ test("parse maven tree", () => {
"pkg:maven/example.group/[email protected]?type=p2-installable-unit",
],
});
parsedList = parseMavenTree(
readFileSync("./test/data/mvn-metrics-tree.txt", {
encoding: "utf-8",
}),
);
expect(parsedList.pkgList.length).toEqual(58);
expect(parsedList.parentComponent["bom-ref"]).toEqual(
"pkg:maven/org.apache.dubbo/[email protected]?type=pom",
);
expect(parsedList.dependenciesList.length).toEqual(58);
expect(parsedList.dependenciesList[0]).toEqual({
ref: "pkg:maven/org.apache.dubbo/[email protected]?type=pom",
dependsOn: [
"pkg:maven/org.apache.dubbo/[email protected]?type=jar",
"pkg:maven/org.junit.jupiter/[email protected]?type=jar",
"pkg:maven/org.junit.jupiter/[email protected]?type=jar",
"pkg:maven/org.awaitility/[email protected]?type=jar",
"pkg:maven/org.hamcrest/[email protected]?type=jar",
"pkg:maven/org.mockito/[email protected]?type=jar",
"pkg:maven/org.mockito/[email protected]?type=jar",
],
});
parsedList = parseMavenTree(
readFileSync("./test/data/mvn-sbstarter-tree.txt", {
encoding: "utf-8",
}),
);
expect(parsedList.pkgList.length).toEqual(90);
expect(parsedList.parentComponent["bom-ref"]).toEqual(
"pkg:maven/org.apache.dubbo/[email protected]?type=jar",
);
expect(parsedList.dependenciesList.length).toEqual(90);
expect(parsedList.dependenciesList[0]).toEqual({
ref: "pkg:maven/org.apache.dubbo/[email protected]?type=jar",
dependsOn: [
"pkg:maven/org.yaml/[email protected]?type=jar",
"pkg:maven/org.apache.dubbo/[email protected]?type=jar",
"pkg:maven/org.junit.vintage/[email protected]?type=jar",
"pkg:maven/net.bytebuddy/[email protected]?type=jar",
"pkg:maven/net.bytebuddy/[email protected]?type=jar",
"pkg:maven/org.apache.dubbo/[email protected]?type=jar",
"pkg:maven/org.apache.logging.log4j/[email protected]?type=jar",
"pkg:maven/org.junit.jupiter/[email protected]?type=jar",
"pkg:maven/org.junit.jupiter/[email protected]?type=jar",
"pkg:maven/org.awaitility/[email protected]?type=jar",
"pkg:maven/org.hamcrest/[email protected]?type=jar",
"pkg:maven/org.mockito/[email protected]?type=jar",
"pkg:maven/org.mockito/[email protected]?type=jar",
],
});
});

// Slow test
Expand Down Expand Up @@ -4917,4 +4967,50 @@ test("hasAnyProjectType tests", () => {
excludeType: ["js"],
}),
).toBeFalsy();
expect(
hasAnyProjectType(["universal"], {
projectType: undefined,
excludeType: ["github"],
}),
).toBeTruthy();
expect(
hasAnyProjectType(["oci"], {
projectType: undefined,
excludeType: ["github"],
}),
).toBeFalsy();
expect(
hasAnyProjectType(["os"], {
projectType: undefined,
excludeType: ["jar"],
}),
).toBeFalsy();
expect(
hasAnyProjectType(["docker"], {
projectType: undefined,
excludeType: ["jar"],
}),
).toBeFalsy();
expect(
hasAnyProjectType(["oci", "java"], {
projectType: undefined,
excludeType: ["jar"],
}),
).toBeTruthy();
expect(
hasAnyProjectType(["oci", "ear"], {
projectType: undefined,
excludeType: ["jar"],
}),
).toBeFalsy();
expect(
hasAnyProjectType(
["docker", "oci", "container", "os"],
{
projectType: undefined,
excludeType: ["github"],
},
false,
),
).toBeFalsy();
});
58 changes: 58 additions & 0 deletions test/data/mvn-metrics-tree.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
org.apache.dubbo:dubbo-metrics:pom:3.3.0
+- org.apache.dubbo:dubbo-test-check:jar:3.3.0:test
| +- org.apache.dubbo:dubbo-common:jar:3.3.0:test
| | +- org.javassist:javassist:jar:3.30.2-GA:test
| | +- com.alibaba.fastjson2:fastjson2:jar:2.0.52:test
| | +- commons-io:commons-io:jar:2.16.1:test
| | \- javax.annotation:javax.annotation-api:jar:1.3.2:test
| +- org.junit.platform:junit-platform-launcher:jar:1.9.3:test
| +- org.apache.commons:commons-lang3:jar:3.17.0:test
| +- org.apache.zookeeper:zookeeper:jar:3.7.2:test
| | +- org.apache.zookeeper:zookeeper-jute:jar:3.7.2:test
| | \- org.slf4j:slf4j-api:jar:1.7.36:test
| +- org.apache.curator:curator-framework:jar:5.1.0:test
| | \- org.apache.curator:curator-client:jar:5.1.0:test
| | \- com.google.guava:guava:jar:27.0.1-jre:test
| | +- com.google.guava:failureaccess:jar:1.0.1:test
| | +- com.google.guava:listenablefuture:jar:9999.0-empty-to-avoid-conflict-with-guava:test
| | +- com.google.code.findbugs:jsr305:jar:3.0.2:test
| | +- org.checkerframework:checker-qual:jar:2.5.2:test
| | +- com.google.errorprone:error_prone_annotations:jar:2.2.0:test
| | +- com.google.j2objc:j2objc-annotations:jar:1.1:test
| | \- org.codehaus.mojo:animal-sniffer-annotations:jar:1.17:test
| +- org.apache.commons:commons-compress:jar:1.27.1:test
| | \- commons-codec:commons-codec:jar:1.16.0:test
| +- org.apache.commons:commons-exec:jar:1.4.0:test
| \- org.asynchttpclient:async-http-client:jar:2.12.3:test
| +- org.asynchttpclient:async-http-client-netty-utils:jar:2.12.3:test
| | \- io.netty:netty-buffer:jar:4.1.112.Final:test
| +- io.netty:netty-codec-http:jar:4.1.112.Final:test
| | +- io.netty:netty-common:jar:4.1.112.Final:test
| | +- io.netty:netty-transport:jar:4.1.112.Final:test
| | \- io.netty:netty-codec:jar:4.1.112.Final:test
| +- io.netty:netty-handler:jar:4.1.112.Final:test
| | +- io.netty:netty-resolver:jar:4.1.112.Final:test
| | \- io.netty:netty-transport-native-unix-common:jar:4.1.112.Final:test
| +- io.netty:netty-codec-socks:jar:4.1.112.Final:test
| +- io.netty:netty-handler-proxy:jar:4.1.112.Final:test
| +- io.netty:netty-transport-native-epoll:jar:linux-x86_64:4.1.112.Final:test
| | \- io.netty:netty-transport-classes-epoll:jar:4.1.112.Final:test
| +- io.netty:netty-transport-native-kqueue:jar:osx-x86_64:4.1.112.Final:test
| | \- io.netty:netty-transport-classes-kqueue:jar:4.1.112.Final:test
| +- org.reactivestreams:reactive-streams:jar:1.0.4:test
| +- com.typesafe.netty:netty-reactive-streams:jar:2.0.4:test
| \- com.sun.activation:jakarta.activation:jar:1.2.2:test
+- org.junit.jupiter:junit-jupiter-engine:jar:5.9.3:test
| +- org.junit.platform:junit-platform-engine:jar:1.9.3:test
| | +- org.opentest4j:opentest4j:jar:1.2.0:test
| | \- org.junit.platform:junit-platform-commons:jar:1.9.3:test
| +- org.junit.jupiter:junit-jupiter-api:jar:5.9.3:test
| \- org.apiguardian:apiguardian-api:jar:1.1.2:test
+- org.junit.jupiter:junit-jupiter-params:jar:5.9.3:test
+- org.awaitility:awaitility:jar:4.2.0:test
+- org.hamcrest:hamcrest:jar:2.2:test
+- org.mockito:mockito-core:jar:4.11.0:test
| +- net.bytebuddy:byte-buddy:jar:1.15.1:test
| +- net.bytebuddy:byte-buddy-agent:jar:1.15.1:test
| \- org.objenesis:objenesis:jar:3.3:test
\- org.mockito:mockito-inline:jar:4.11.0:test
Loading

0 comments on commit 6aa3175

Please sign in to comment.