Skip to content
Merged
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
34 changes: 31 additions & 3 deletions lib/cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4884,16 +4884,34 @@ export function createCppBom(path, options) {
cmakeLikeFiles = cmakeLikeFiles.concat(cmakeFiles);
}
let pkgList = [];
let parentComponentDependencies = [];
if (conanLockFiles.length) {
for (const f of conanLockFiles) {
if (DEBUG_MODE) {
console.log(`Parsing ${f}`);
}
const conanLockData = readFileSync(f, { encoding: "utf-8" });
const dlist = parseConanLockData(conanLockData);
if (dlist?.length) {
pkgList = pkgList.concat(dlist);
const {
pkgList: conanPkgList,
dependencies: conanDependencies,
parentComponentDependencies: parentCompDeps,
} = parseConanLockData(conanLockData);

if (conanPkgList.length) {
pkgList = pkgList.concat(conanPkgList);
}

if (Object.keys(conanDependencies).length) {
dependencies = mergeDependencies(
dependencies,
Object.keys(conanDependencies).map((dependentBomRef) => ({
ref: dependentBomRef,
dependsOn: conanDependencies[dependentBomRef],
})),
);
}

parentComponentDependencies = parentCompDeps;
}
} else if (conanFiles.length) {
for (const f of conanFiles) {
Expand Down Expand Up @@ -5015,6 +5033,16 @@ export function createCppBom(path, options) {
}
options.parentComponent = parentComponent;
}

if (parentComponent && parentComponentDependencies.length) {
dependencies = mergeDependencies(dependencies, [
{
ref: parentComponent["bom-ref"],
dependsOn: parentComponentDependencies,
},
]);
}

return buildBomNSData(options, pkgList, "generic", {
src: path,
parentComponent,
Expand Down
64 changes: 51 additions & 13 deletions lib/helpers/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -10013,29 +10013,67 @@ export function mapConanPkgRefToPurlStringAndNameAndVersion(conanPkgRef) {

export function parseConanLockData(conanLockData) {
const pkgList = [];
const dependencies = {};
const parentComponentDependencies = [];

if (!conanLockData) {
return pkgList;
return { pkgList, dependencies, parentComponentDependencies };
}

const lockFile = JSON.parse(conanLockData);
if (
(!lockFile || !lockFile.graph_lock || !lockFile.graph_lock.nodes) &&
!lockFile.requires
) {
return pkgList;
return { pkgList, dependencies, parentComponentDependencies };
}

if (lockFile.graph_lock?.nodes) {
const depends = lockFile.graph_lock.nodes;
const nodeKeyToBomRefMap = {};

for (const nk of Object.keys(depends)) {
if (depends[nk].ref) {
const [purl, name, version] =
mapConanPkgRefToPurlStringAndNameAndVersion(depends[nk].ref);
if (purl !== null) {
pkgList.push({
name,
version,
purl,
"bom-ref": decodeURIComponent(purl),
});
if (!depends[nk].ref) continue;

const [purl, name, version] = mapConanPkgRefToPurlStringAndNameAndVersion(
depends[nk].ref,
);
if (purl === null) continue;

const bomRef = decodeURIComponent(purl);
pkgList.push({
name,
version,
purl,
"bom-ref": bomRef,
});

nodeKeyToBomRefMap[nk] = bomRef;
}

for (const nk of Object.keys(depends)) {
let requirementNodeKeys = [];
if (Array.isArray(depends[nk].requires))
requirementNodeKeys = requirementNodeKeys.concat(depends[nk].requires);
if (Array.isArray(depends[nk].build_requires))
requirementNodeKeys = requirementNodeKeys.concat(
depends[nk].build_requires,
);

for (const dependencyNodeKey of requirementNodeKeys) {
const dependencyBomRef = nodeKeyToBomRefMap[dependencyNodeKey];
if (!dependencyBomRef) continue;

const dependentBomRef = nodeKeyToBomRefMap[nk];
if (dependentBomRef) {
if (!(dependentBomRef in dependencies))
dependencies[dependentBomRef] = [];
if (!dependencies[dependentBomRef].includes(dependencyBomRef))
dependencies[dependentBomRef].push(dependencyBomRef);
} else if (nk === "0") {
// parent component for which the conan.lock was generated
if (!parentComponentDependencies.includes(dependencyBomRef))
parentComponentDependencies.push(dependencyBomRef);
}
}
}
Expand All @@ -10056,7 +10094,7 @@ export function parseConanLockData(conanLockData) {
}
}
}
return pkgList;
return { pkgList, dependencies, parentComponentDependencies };
}

export function parseConanData(conanData) {
Expand Down
146 changes: 131 additions & 15 deletions lib/helpers/utils.poku.js
Original file line number Diff line number Diff line change
Expand Up @@ -2185,28 +2185,144 @@ it("parse cabal freeze", () => {
});

it("parse conan data", () => {
assert.deepStrictEqual(parseConanLockData(null), []);
let dep_list = parseConanLockData(
let conanLockData = parseConanLockData(null);
assert.deepStrictEqual(conanLockData.pkgList.length, 0);
assert.deepStrictEqual(Object.keys(conanLockData.dependencies).length, 0);
assert.deepStrictEqual(conanLockData.parentComponentDependencies.length, 0);
conanLockData = parseConanLockData(
readFileSync("./test/data/conan-v1.lock", { encoding: "utf-8" }),
);
assert.deepStrictEqual(dep_list.length, 3);
assert.deepStrictEqual(dep_list[0], {
assert.deepStrictEqual(conanLockData.pkgList.length, 3);
assert.deepStrictEqual(conanLockData.pkgList[0], {
name: "zstd",
version: "1.4.4",
"bom-ref": "pkg:conan/zstd@1.4.4",
purl: "pkg:conan/zstd@1.4.4",
});
dep_list = parseConanLockData(
assert.deepStrictEqual(Object.keys(conanLockData.dependencies).length, 0);
assert.deepStrictEqual(conanLockData.parentComponentDependencies, [
"pkg:conan/zstd@1.4.4",
"pkg:conan/jerryscript@2.2.0",
"pkg:conan/wolfssl@4.4.0",
]);

conanLockData = parseConanLockData(
readFileSync("./test/data/conan-v1-for-reference.lock", {
encoding: "utf-8",
}),
);
assert.deepStrictEqual(Object.keys(conanLockData.pkgList).length, 7);
assert.deepStrictEqual(conanLockData.pkgList[0], {
name: "grpc",
version: "1.50.1",
"bom-ref": "pkg:conan/grpc@1.50.1",
purl: "pkg:conan/grpc@1.50.1",
});
assert.deepStrictEqual(Object.keys(conanLockData.dependencies).length, 3);
assert.deepStrictEqual(conanLockData.dependencies["pkg:conan/grpc@1.50.1"], [
"pkg:conan/abseil@20230802.1",
"pkg:conan/protobuf@3.21.12",
"pkg:conan/c-ares@1.34.1",
"pkg:conan/openssl@3.3.2",
"pkg:conan/re2@20230301",
"pkg:conan/zlib@1.3.1",
]);
assert.deepStrictEqual(
conanLockData.dependencies["pkg:conan/protobuf@3.21.12"],
["pkg:conan/zlib@1.3.1"],
);
assert.deepStrictEqual(
conanLockData.dependencies["pkg:conan/openssl@3.3.2"],
["pkg:conan/zlib@1.3.1"],
);
assert.deepStrictEqual(conanLockData.parentComponentDependencies.length, 0);

conanLockData = parseConanLockData(
readFileSync("./test/data/conan-v1-with-nested-deps.lock", {
encoding: "utf-8",
}),
);
assert.deepStrictEqual(conanLockData.pkgList.length, 9);
assert.deepStrictEqual(conanLockData.pkgList[0], {
name: "grpc",
version: "1.50.1",
"bom-ref": "pkg:conan/grpc@1.50.1",
purl: "pkg:conan/grpc@1.50.1",
});
assert.deepStrictEqual(conanLockData.pkgList[1], {
name: "abseil",
version: "20230802.1",
"bom-ref": "pkg:conan/abseil@20230802.1",
purl: "pkg:conan/abseil@20230802.1",
});
assert.deepStrictEqual(conanLockData.pkgList[2], {
name: "protobuf",
version: "3.21.12",
"bom-ref": "pkg:conan/protobuf@3.21.12",
purl: "pkg:conan/protobuf@3.21.12",
});
assert.deepStrictEqual(conanLockData.pkgList[3], {
name: "zlib",
version: "1.3.1",
"bom-ref": "pkg:conan/zlib@1.3.1",
purl: "pkg:conan/zlib@1.3.1",
});
assert.deepStrictEqual(conanLockData.pkgList[5], {
name: "openssl",
version: "3.3.2",
"bom-ref": "pkg:conan/openssl@3.3.2",
purl: "pkg:conan/openssl@3.3.2",
});
assert.deepStrictEqual(conanLockData.pkgList[8], {
name: "gtest",
version: "1.13.0",
"bom-ref": "pkg:conan/gtest@1.13.0",
purl: "pkg:conan/gtest@1.13.0",
});
assert.deepStrictEqual(Object.keys(conanLockData.dependencies).length, 3);
assert.deepStrictEqual(conanLockData.dependencies["pkg:conan/grpc@1.50.1"], [
"pkg:conan/abseil@20230802.1",
"pkg:conan/protobuf@3.21.12",
"pkg:conan/c-ares@1.34.1",
"pkg:conan/openssl@3.3.2",
"pkg:conan/re2@20230301",
"pkg:conan/zlib@1.3.1",
]);
assert.deepStrictEqual(
conanLockData.dependencies["pkg:conan/protobuf@3.21.12"],
["pkg:conan/zlib@1.3.1"],
);
assert.deepStrictEqual(
conanLockData.dependencies["pkg:conan/openssl@3.3.2"],
["pkg:conan/zlib@1.3.1"],
);
assert.deepStrictEqual(conanLockData.parentComponentDependencies.length, 3);
assert.deepStrictEqual(
conanLockData.parentComponentDependencies[0],
"pkg:conan/grpc@1.50.1",
);
assert.deepStrictEqual(
conanLockData.parentComponentDependencies[1],
"pkg:conan/rapidjson@1.1.0",
);
assert.deepStrictEqual(
conanLockData.parentComponentDependencies[2],
"pkg:conan/gtest@1.13.0",
);

conanLockData = parseConanLockData(
readFileSync("./test/data/conan-v2.lock", { encoding: "utf-8" }),
);
assert.deepStrictEqual(dep_list.length, 2);
assert.deepStrictEqual(dep_list[0], {
assert.deepStrictEqual(conanLockData.pkgList.length, 2);
assert.deepStrictEqual(conanLockData.pkgList[0], {
name: "opensta",
version: "4.0.0",
"bom-ref": "pkg:conan/opensta@4.0.0?rrev=765a7eed989e624c762a73291d712b14",
purl: "pkg:conan/opensta@4.0.0?rrev=765a7eed989e624c762a73291d712b14",
});
dep_list = parseConanData(
assert.deepStrictEqual(Object.keys(conanLockData.dependencies).length, 0);
assert.deepStrictEqual(conanLockData.parentComponentDependencies.length, 0);
let dep_list = parseConanData(
readFileSync("./test/data/conanfile.txt", { encoding: "utf-8" }),
);
assert.deepStrictEqual(dep_list.length, 3);
Expand Down Expand Up @@ -2316,42 +2432,42 @@ it("conan package reference mapper to pURL", () => {
});

it("parse conan data where packages use custom user/channel", () => {
let dep_list = parseConanLockData(
const conanLockData = parseConanLockData(
readFileSync("./test/data/conan.with_custom_pkg_user_channel.lock", {
encoding: "utf-8",
}),
);
assert.deepStrictEqual(dep_list.length, 4);
assert.deepStrictEqual(dep_list[0], {
assert.deepStrictEqual(conanLockData.pkgList.length, 4);
assert.deepStrictEqual(conanLockData.pkgList[0], {
name: "libcurl",
version: "8.1.2",
"bom-ref":
"pkg:conan/libcurl@8.1.2?channel=stable&rrev=25215c550633ef0224152bc2c0556698&user=internal",
purl: "pkg:conan/libcurl@8.1.2?channel=stable&rrev=25215c550633ef0224152bc2c0556698&user=internal",
});
assert.deepStrictEqual(dep_list[1], {
assert.deepStrictEqual(conanLockData.pkgList[1], {
name: "openssl",
version: "3.1.0",
"bom-ref":
"pkg:conan/openssl@3.1.0?channel=stable&rrev=c9c6ab43aa40bafacf8b37c5948cdb1f&user=internal",
purl: "pkg:conan/openssl@3.1.0?channel=stable&rrev=c9c6ab43aa40bafacf8b37c5948cdb1f&user=internal",
});
assert.deepStrictEqual(dep_list[2], {
assert.deepStrictEqual(conanLockData.pkgList[2], {
name: "zlib",
version: "1.2.13",
"bom-ref":
"pkg:conan/zlib@1.2.13?channel=stable&rrev=aee6a56ff7927dc7261c55eb2db4fc5b&user=internal",
purl: "pkg:conan/zlib@1.2.13?channel=stable&rrev=aee6a56ff7927dc7261c55eb2db4fc5b&user=internal",
});
assert.deepStrictEqual(dep_list[3], {
assert.deepStrictEqual(conanLockData.pkgList[3], {
name: "fmt",
version: "10.0.0",
purl: "pkg:conan/fmt@10.0.0?channel=stable&rrev=79e7cc169695bc058fb606f20df6bb10&user=internal",
"bom-ref":
"pkg:conan/fmt@10.0.0?channel=stable&rrev=79e7cc169695bc058fb606f20df6bb10&user=internal",
});

dep_list = parseConanData(
const dep_list = parseConanData(
readFileSync("./test/data/conanfile.with_custom_pkg_user_channel.txt", {
encoding: "utf-8",
}),
Expand Down
Loading
Loading