Skip to content

Commit

Permalink
Improve partial dependency tree detection (#1356)
Browse files Browse the repository at this point in the history
Signed-off-by: Prabhu Subramanian <[email protected]>
  • Loading branch information
prabhu authored Sep 5, 2024
1 parent e0da319 commit 0c272a5
Show file tree
Hide file tree
Showing 9 changed files with 42 additions and 20 deletions.
2 changes: 1 addition & 1 deletion deno.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cyclonedx/cdxgen",
"version": "10.9.7",
"version": "10.9.8",
"exports": "./index.js",
"compilerOptions": {
"allowJs": true,
Expand Down
2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3187,7 +3187,7 @@ export async function createPythonBom(path, options) {
if (
isFeatureEnabled(options, "safe-pip-install") &&
pkgList.length &&
isPartialTree(dependencies)
isPartialTree(dependencies, pkgList.length)
) {
// Trim the current package list first
pkgList = trimComponents(pkgList);
Expand Down
2 changes: 1 addition & 1 deletion jsr.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cyclonedx/cdxgen",
"version": "10.9.7",
"version": "10.9.8",
"exports": "./index.js",
"include": ["*.js", "bin/**", "data/**", "types/**"],
"exclude": ["test/", "docs/", "contrib/", "ci/", "tools_config/"]
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cyclonedx/cdxgen",
"version": "10.9.7",
"version": "10.9.8",
"description": "Creates CycloneDX Software Bill of Materials (SBOM) from source or container image",
"homepage": "http://github.com/cyclonedx/cdxgen",
"author": "Prabhu Subramanian <[email protected]>",
Expand Down
3 changes: 2 additions & 1 deletion types/utils.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1267,9 +1267,10 @@ export function isValidIriReference(iri: string): boolean;
* Method to check if a given dependency tree is partial or not.
*
* @param {Array} dependencies List of dependencies
* @param {Number} componentsCount Number of components
* @returns {Boolean} True if the dependency tree lacks any non-root parents without children. False otherwise.
*/
export function isPartialTree(dependencies: any[]): boolean;
export function isPartialTree(dependencies: any[], componentsCount?: number): boolean;
/**
* Re-compute and set the scope based on the dependency tree
*
Expand Down
2 changes: 1 addition & 1 deletion types/utils.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -10517,7 +10517,7 @@ export function getPipFrozenTree(
);
} else {
console.log(
"The version or the version specifiers used for a dependency is invalid. Resolve the below error to improve SBOM accuracy.\n",
"The version or the version specifiers used for a dependency is invalid. Try with a different python type such as -t python310 or -t python39.\nOriginal error from pip:\n",
);
}
console.log(result.stderr);
Expand Down Expand Up @@ -12194,9 +12194,10 @@ export function isValidIriReference(iri) {
* Method to check if a given dependency tree is partial or not.
*
* @param {Array} dependencies List of dependencies
* @param {Number} componentsCount Number of components
* @returns {Boolean} True if the dependency tree lacks any non-root parents without children. False otherwise.
*/
export function isPartialTree(dependencies) {
export function isPartialTree(dependencies, componentsCount = 1) {
if (dependencies?.length <= 1) {
return true;
}
Expand All @@ -12206,7 +12207,7 @@ export function isPartialTree(dependencies) {
parentsWithChildsCount++;
}
}
return parentsWithChildsCount <= 1;
return parentsWithChildsCount <= Math.max(Math.round(componentsCount / 3), 1);
}

/**
Expand Down
40 changes: 30 additions & 10 deletions utils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3199,7 +3199,9 @@ test("parseYarnLock", async () => {
parsedList = await parseYarnLock("./test/data/yarn_locks/yarn-multi.lock");
expect(parsedList.pkgList.length).toEqual(1909);
expect(parsedList.dependenciesList.length).toEqual(1909);
expect(isPartialTree(parsedList.dependenciesList)).toBeFalsy();
expect(
isPartialTree(parsedList.dependenciesList, parsedList.pkgList.length),
).toBeFalsy();
expect(parsedList.pkgList[0]).toEqual({
_integrity:
"sha512-zpruxnFMz6K94gs2pqc3sidzFDbQpKT5D6P/J/I9s8ekHZ5eczgnRp6pqXC86Bh7+44j/btpmOT0kwiboyqTnA==",
Expand Down Expand Up @@ -3232,7 +3234,9 @@ test("parseYarnLock", async () => {
parsedList = await parseYarnLock("./test/data/yarn_locks/yarn-light.lock");
expect(parsedList.pkgList.length).toEqual(315);
expect(parsedList.dependenciesList.length).toEqual(315);
expect(isPartialTree(parsedList.dependenciesList)).toBeFalsy();
expect(
isPartialTree(parsedList.dependenciesList, parsedList.pkgList.length),
).toBeFalsy();
expect(parsedList.pkgList[0]).toEqual({
_integrity:
"sha512-rZ1k9kQvJX21Vwgx1L6kSQ6yeXo9cCMyqURSnjG+MRoJn+Mr3LblxmVdzScHXRzv0N9yzy49oG7Bqxp9Knyv/g==",
Expand Down Expand Up @@ -3265,7 +3269,9 @@ test("parseYarnLock", async () => {
parsedList = await parseYarnLock("./test/data/yarn_locks/yarn3.lock");
expect(parsedList.pkgList.length).toEqual(5);
expect(parsedList.dependenciesList.length).toEqual(5);
expect(isPartialTree(parsedList.dependenciesList)).toBeFalsy();
expect(
isPartialTree(parsedList.dependenciesList, parsedList.pkgList.length),
).toBeFalsy();
expect(parsedList.pkgList[1]).toEqual({
_integrity:
"sha512-+X9Jn4mPI+RYV0ITiiLyJSYlT9um111BocJSaztsxXR+9ZxWErpzdfQqyk+EYZUOklugjJkerQZRtJGLfJeClw==",
Expand Down Expand Up @@ -3298,7 +3304,9 @@ test("parseYarnLock", async () => {
parsedList = await parseYarnLock("./test/data/yarn_locks/yarnv2.lock");
expect(parsedList.pkgList.length).toEqual(1088);
expect(parsedList.dependenciesList.length).toEqual(1088);
expect(isPartialTree(parsedList.dependenciesList)).toBeFalsy();
expect(
isPartialTree(parsedList.dependenciesList, parsedList.pkgList.length),
).toBeFalsy();
expect(parsedList.pkgList[0]).toEqual({
_integrity:
"sha512-G0U5NjBUYIs39l1J1ckgpVfVX2IxpzRAIT4/2An86O2Mcri3k5xNu7/RRkfObo12wN9s7BmnREAMhH7252oZiA==",
Expand Down Expand Up @@ -3330,7 +3338,9 @@ test("parseYarnLock", async () => {
parsedList = await parseYarnLock("./test/data/yarn_locks/yarnv3.lock");
expect(parsedList.pkgList.length).toEqual(363);
expect(parsedList.dependenciesList.length).toEqual(363);
expect(isPartialTree(parsedList.dependenciesList)).toBeFalsy();
expect(
isPartialTree(parsedList.dependenciesList, parsedList.pkgList.length),
).toBeFalsy();
expect(parsedList.pkgList[0]).toEqual({
_integrity:
"sha512-vtU+q0TmdIDmezU7lKub73vObN6nmd3lkcKWz7R9hyNI8gz5o7grDb+FML9nykOLW+09gGIup2xyJ86j5vBKpg==",
Expand Down Expand Up @@ -3362,7 +3372,9 @@ test("parseYarnLock", async () => {
parsedList = await parseYarnLock("./test/data/yarn_locks/yarn4.lock");
expect(parsedList.pkgList.length).toEqual(1);
expect(parsedList.dependenciesList.length).toEqual(1);
expect(isPartialTree(parsedList.dependenciesList)).toBeTruthy();
expect(
isPartialTree(parsedList.dependenciesList, parsedList.pkgList.length),
).toBeTruthy();
parsedList = await parseYarnLock("./test/data/yarn_locks/yarn-at.lock");
expect(parsedList.pkgList.length).toEqual(4);
expect(parsedList.dependenciesList.length).toEqual(4);
Expand Down Expand Up @@ -3394,7 +3406,9 @@ test("parseYarnLock", async () => {
parsedList = await parseYarnLock("./test/data/yarn_locks/yarn5.lock");
expect(parsedList.pkgList.length).toEqual(1962);
expect(parsedList.dependenciesList.length).toEqual(1962);
expect(isPartialTree(parsedList.dependenciesList)).toBeFalsy();
expect(
isPartialTree(parsedList.dependenciesList, parsedList.pkgList.length),
).toBeFalsy();
expect(parsedList.pkgList[0].purl).toEqual(
"pkg:npm/%40ampproject/[email protected]",
);
Expand All @@ -3408,7 +3422,9 @@ test("parseYarnLock", async () => {
parsedList = await parseYarnLock("./test/data/yarn_locks/yarn6.lock");
expect(parsedList.pkgList.length).toEqual(1472);
expect(parsedList.dependenciesList.length).toEqual(1472);
expect(isPartialTree(parsedList.dependenciesList)).toBeFalsy();
expect(
isPartialTree(parsedList.dependenciesList, parsedList.pkgList.length),
).toBeFalsy();
expect(parsedList.pkgList[0].purl).toEqual(
"pkg:npm/%40aashutoshrathi/[email protected]",
);
Expand All @@ -3425,7 +3441,9 @@ test("parseYarnLock", async () => {
parsedList = await parseYarnLock("./test/data/yarn_locks/yarn7.lock");
expect(parsedList.pkgList.length).toEqual(1350);
expect(parsedList.dependenciesList.length).toEqual(1347);
expect(isPartialTree(parsedList.dependenciesList)).toBeFalsy();
expect(
isPartialTree(parsedList.dependenciesList, parsedList.pkgList.length),
).toBeFalsy();
expect(parsedList.pkgList[0].purl).toEqual(
"pkg:npm/%40aashutoshrathi/[email protected]",
);
Expand Down Expand Up @@ -3478,7 +3496,9 @@ test("parseYarnLock", async () => {
parsedList = await parseYarnLock("./test/data/yarn_locks/yarnv1-empty.lock");
expect(parsedList.pkgList.length).toEqual(770);
expect(parsedList.dependenciesList.length).toEqual(770);
expect(isPartialTree(parsedList.dependenciesList)).toBeFalsy();
expect(
isPartialTree(parsedList.dependenciesList, parsedList.pkgList.length),
).toBeFalsy();
expect(parsedList.pkgList[0].purl).toEqual(
"pkg:npm/%40ampproject/[email protected]",
);
Expand Down
2 changes: 1 addition & 1 deletion validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ export const validateRefs = (bomJson) => {
const warningsList = [];
const refMap = buildRefs(bomJson);
if (bomJson?.dependencies) {
if (isPartialTree(bomJson.dependencies)) {
if (isPartialTree(bomJson.dependencies, bomJson?.components?.length)) {
warningsList.push("Dependency tree is partial lacking child nodes.");
}
for (const dep of bomJson.dependencies) {
Expand Down

0 comments on commit 0c272a5

Please sign in to comment.