Skip to content

Commit 05290e0

Browse files
authored
Emit dependency info from Conan v1 lock files (#3019)
Emit dependency edges from Conan v1 lock files Signed-off-by: Khubajsn <khubajsn@gmail.com>
1 parent 2faa695 commit 05290e0

File tree

5 files changed

+383
-31
lines changed

5 files changed

+383
-31
lines changed

lib/cli/index.js

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4884,16 +4884,34 @@ export function createCppBom(path, options) {
48844884
cmakeLikeFiles = cmakeLikeFiles.concat(cmakeFiles);
48854885
}
48864886
let pkgList = [];
4887+
let parentComponentDependencies = [];
48874888
if (conanLockFiles.length) {
48884889
for (const f of conanLockFiles) {
48894890
if (DEBUG_MODE) {
48904891
console.log(`Parsing ${f}`);
48914892
}
48924893
const conanLockData = readFileSync(f, { encoding: "utf-8" });
4893-
const dlist = parseConanLockData(conanLockData);
4894-
if (dlist?.length) {
4895-
pkgList = pkgList.concat(dlist);
4894+
const {
4895+
pkgList: conanPkgList,
4896+
dependencies: conanDependencies,
4897+
parentComponentDependencies: parentCompDeps,
4898+
} = parseConanLockData(conanLockData);
4899+
4900+
if (conanPkgList.length) {
4901+
pkgList = pkgList.concat(conanPkgList);
4902+
}
4903+
4904+
if (Object.keys(conanDependencies).length) {
4905+
dependencies = mergeDependencies(
4906+
dependencies,
4907+
Object.keys(conanDependencies).map((dependentBomRef) => ({
4908+
ref: dependentBomRef,
4909+
dependsOn: conanDependencies[dependentBomRef],
4910+
})),
4911+
);
48964912
}
4913+
4914+
parentComponentDependencies = parentCompDeps;
48974915
}
48984916
} else if (conanFiles.length) {
48994917
for (const f of conanFiles) {
@@ -5015,6 +5033,16 @@ export function createCppBom(path, options) {
50155033
}
50165034
options.parentComponent = parentComponent;
50175035
}
5036+
5037+
if (parentComponent && parentComponentDependencies.length) {
5038+
dependencies = mergeDependencies(dependencies, [
5039+
{
5040+
ref: parentComponent["bom-ref"],
5041+
dependsOn: parentComponentDependencies,
5042+
},
5043+
]);
5044+
}
5045+
50185046
return buildBomNSData(options, pkgList, "generic", {
50195047
src: path,
50205048
parentComponent,

lib/helpers/utils.js

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10013,29 +10013,67 @@ export function mapConanPkgRefToPurlStringAndNameAndVersion(conanPkgRef) {
1001310013

1001410014
export function parseConanLockData(conanLockData) {
1001510015
const pkgList = [];
10016+
const dependencies = {};
10017+
const parentComponentDependencies = [];
10018+
1001610019
if (!conanLockData) {
10017-
return pkgList;
10020+
return { pkgList, dependencies, parentComponentDependencies };
1001810021
}
10022+
1001910023
const lockFile = JSON.parse(conanLockData);
1002010024
if (
1002110025
(!lockFile || !lockFile.graph_lock || !lockFile.graph_lock.nodes) &&
1002210026
!lockFile.requires
1002310027
) {
10024-
return pkgList;
10028+
return { pkgList, dependencies, parentComponentDependencies };
1002510029
}
10030+
1002610031
if (lockFile.graph_lock?.nodes) {
1002710032
const depends = lockFile.graph_lock.nodes;
10033+
const nodeKeyToBomRefMap = {};
10034+
1002810035
for (const nk of Object.keys(depends)) {
10029-
if (depends[nk].ref) {
10030-
const [purl, name, version] =
10031-
mapConanPkgRefToPurlStringAndNameAndVersion(depends[nk].ref);
10032-
if (purl !== null) {
10033-
pkgList.push({
10034-
name,
10035-
version,
10036-
purl,
10037-
"bom-ref": decodeURIComponent(purl),
10038-
});
10036+
if (!depends[nk].ref) continue;
10037+
10038+
const [purl, name, version] = mapConanPkgRefToPurlStringAndNameAndVersion(
10039+
depends[nk].ref,
10040+
);
10041+
if (purl === null) continue;
10042+
10043+
const bomRef = decodeURIComponent(purl);
10044+
pkgList.push({
10045+
name,
10046+
version,
10047+
purl,
10048+
"bom-ref": bomRef,
10049+
});
10050+
10051+
nodeKeyToBomRefMap[nk] = bomRef;
10052+
}
10053+
10054+
for (const nk of Object.keys(depends)) {
10055+
let requirementNodeKeys = [];
10056+
if (Array.isArray(depends[nk].requires))
10057+
requirementNodeKeys = requirementNodeKeys.concat(depends[nk].requires);
10058+
if (Array.isArray(depends[nk].build_requires))
10059+
requirementNodeKeys = requirementNodeKeys.concat(
10060+
depends[nk].build_requires,
10061+
);
10062+
10063+
for (const dependencyNodeKey of requirementNodeKeys) {
10064+
const dependencyBomRef = nodeKeyToBomRefMap[dependencyNodeKey];
10065+
if (!dependencyBomRef) continue;
10066+
10067+
const dependentBomRef = nodeKeyToBomRefMap[nk];
10068+
if (dependentBomRef) {
10069+
if (!(dependentBomRef in dependencies))
10070+
dependencies[dependentBomRef] = [];
10071+
if (!dependencies[dependentBomRef].includes(dependencyBomRef))
10072+
dependencies[dependentBomRef].push(dependencyBomRef);
10073+
} else if (nk === "0") {
10074+
// parent component for which the conan.lock was generated
10075+
if (!parentComponentDependencies.includes(dependencyBomRef))
10076+
parentComponentDependencies.push(dependencyBomRef);
1003910077
}
1004010078
}
1004110079
}
@@ -10056,7 +10094,7 @@ export function parseConanLockData(conanLockData) {
1005610094
}
1005710095
}
1005810096
}
10059-
return pkgList;
10097+
return { pkgList, dependencies, parentComponentDependencies };
1006010098
}
1006110099

1006210100
export function parseConanData(conanData) {

lib/helpers/utils.poku.js

Lines changed: 131 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2185,28 +2185,144 @@ it("parse cabal freeze", () => {
21852185
});
21862186

21872187
it("parse conan data", () => {
2188-
assert.deepStrictEqual(parseConanLockData(null), []);
2189-
let dep_list = parseConanLockData(
2188+
let conanLockData = parseConanLockData(null);
2189+
assert.deepStrictEqual(conanLockData.pkgList.length, 0);
2190+
assert.deepStrictEqual(Object.keys(conanLockData.dependencies).length, 0);
2191+
assert.deepStrictEqual(conanLockData.parentComponentDependencies.length, 0);
2192+
conanLockData = parseConanLockData(
21902193
readFileSync("./test/data/conan-v1.lock", { encoding: "utf-8" }),
21912194
);
2192-
assert.deepStrictEqual(dep_list.length, 3);
2193-
assert.deepStrictEqual(dep_list[0], {
2195+
assert.deepStrictEqual(conanLockData.pkgList.length, 3);
2196+
assert.deepStrictEqual(conanLockData.pkgList[0], {
21942197
name: "zstd",
21952198
version: "1.4.4",
21962199
"bom-ref": "pkg:conan/zstd@1.4.4",
21972200
purl: "pkg:conan/zstd@1.4.4",
21982201
});
2199-
dep_list = parseConanLockData(
2202+
assert.deepStrictEqual(Object.keys(conanLockData.dependencies).length, 0);
2203+
assert.deepStrictEqual(conanLockData.parentComponentDependencies, [
2204+
"pkg:conan/zstd@1.4.4",
2205+
"pkg:conan/jerryscript@2.2.0",
2206+
"pkg:conan/wolfssl@4.4.0",
2207+
]);
2208+
2209+
conanLockData = parseConanLockData(
2210+
readFileSync("./test/data/conan-v1-for-reference.lock", {
2211+
encoding: "utf-8",
2212+
}),
2213+
);
2214+
assert.deepStrictEqual(Object.keys(conanLockData.pkgList).length, 7);
2215+
assert.deepStrictEqual(conanLockData.pkgList[0], {
2216+
name: "grpc",
2217+
version: "1.50.1",
2218+
"bom-ref": "pkg:conan/grpc@1.50.1",
2219+
purl: "pkg:conan/grpc@1.50.1",
2220+
});
2221+
assert.deepStrictEqual(Object.keys(conanLockData.dependencies).length, 3);
2222+
assert.deepStrictEqual(conanLockData.dependencies["pkg:conan/grpc@1.50.1"], [
2223+
"pkg:conan/abseil@20230802.1",
2224+
"pkg:conan/protobuf@3.21.12",
2225+
"pkg:conan/c-ares@1.34.1",
2226+
"pkg:conan/openssl@3.3.2",
2227+
"pkg:conan/re2@20230301",
2228+
"pkg:conan/zlib@1.3.1",
2229+
]);
2230+
assert.deepStrictEqual(
2231+
conanLockData.dependencies["pkg:conan/protobuf@3.21.12"],
2232+
["pkg:conan/zlib@1.3.1"],
2233+
);
2234+
assert.deepStrictEqual(
2235+
conanLockData.dependencies["pkg:conan/openssl@3.3.2"],
2236+
["pkg:conan/zlib@1.3.1"],
2237+
);
2238+
assert.deepStrictEqual(conanLockData.parentComponentDependencies.length, 0);
2239+
2240+
conanLockData = parseConanLockData(
2241+
readFileSync("./test/data/conan-v1-with-nested-deps.lock", {
2242+
encoding: "utf-8",
2243+
}),
2244+
);
2245+
assert.deepStrictEqual(conanLockData.pkgList.length, 9);
2246+
assert.deepStrictEqual(conanLockData.pkgList[0], {
2247+
name: "grpc",
2248+
version: "1.50.1",
2249+
"bom-ref": "pkg:conan/grpc@1.50.1",
2250+
purl: "pkg:conan/grpc@1.50.1",
2251+
});
2252+
assert.deepStrictEqual(conanLockData.pkgList[1], {
2253+
name: "abseil",
2254+
version: "20230802.1",
2255+
"bom-ref": "pkg:conan/abseil@20230802.1",
2256+
purl: "pkg:conan/abseil@20230802.1",
2257+
});
2258+
assert.deepStrictEqual(conanLockData.pkgList[2], {
2259+
name: "protobuf",
2260+
version: "3.21.12",
2261+
"bom-ref": "pkg:conan/protobuf@3.21.12",
2262+
purl: "pkg:conan/protobuf@3.21.12",
2263+
});
2264+
assert.deepStrictEqual(conanLockData.pkgList[3], {
2265+
name: "zlib",
2266+
version: "1.3.1",
2267+
"bom-ref": "pkg:conan/zlib@1.3.1",
2268+
purl: "pkg:conan/zlib@1.3.1",
2269+
});
2270+
assert.deepStrictEqual(conanLockData.pkgList[5], {
2271+
name: "openssl",
2272+
version: "3.3.2",
2273+
"bom-ref": "pkg:conan/openssl@3.3.2",
2274+
purl: "pkg:conan/openssl@3.3.2",
2275+
});
2276+
assert.deepStrictEqual(conanLockData.pkgList[8], {
2277+
name: "gtest",
2278+
version: "1.13.0",
2279+
"bom-ref": "pkg:conan/gtest@1.13.0",
2280+
purl: "pkg:conan/gtest@1.13.0",
2281+
});
2282+
assert.deepStrictEqual(Object.keys(conanLockData.dependencies).length, 3);
2283+
assert.deepStrictEqual(conanLockData.dependencies["pkg:conan/grpc@1.50.1"], [
2284+
"pkg:conan/abseil@20230802.1",
2285+
"pkg:conan/protobuf@3.21.12",
2286+
"pkg:conan/c-ares@1.34.1",
2287+
"pkg:conan/openssl@3.3.2",
2288+
"pkg:conan/re2@20230301",
2289+
"pkg:conan/zlib@1.3.1",
2290+
]);
2291+
assert.deepStrictEqual(
2292+
conanLockData.dependencies["pkg:conan/protobuf@3.21.12"],
2293+
["pkg:conan/zlib@1.3.1"],
2294+
);
2295+
assert.deepStrictEqual(
2296+
conanLockData.dependencies["pkg:conan/openssl@3.3.2"],
2297+
["pkg:conan/zlib@1.3.1"],
2298+
);
2299+
assert.deepStrictEqual(conanLockData.parentComponentDependencies.length, 3);
2300+
assert.deepStrictEqual(
2301+
conanLockData.parentComponentDependencies[0],
2302+
"pkg:conan/grpc@1.50.1",
2303+
);
2304+
assert.deepStrictEqual(
2305+
conanLockData.parentComponentDependencies[1],
2306+
"pkg:conan/rapidjson@1.1.0",
2307+
);
2308+
assert.deepStrictEqual(
2309+
conanLockData.parentComponentDependencies[2],
2310+
"pkg:conan/gtest@1.13.0",
2311+
);
2312+
2313+
conanLockData = parseConanLockData(
22002314
readFileSync("./test/data/conan-v2.lock", { encoding: "utf-8" }),
22012315
);
2202-
assert.deepStrictEqual(dep_list.length, 2);
2203-
assert.deepStrictEqual(dep_list[0], {
2316+
assert.deepStrictEqual(conanLockData.pkgList.length, 2);
2317+
assert.deepStrictEqual(conanLockData.pkgList[0], {
22042318
name: "opensta",
22052319
version: "4.0.0",
22062320
"bom-ref": "pkg:conan/opensta@4.0.0?rrev=765a7eed989e624c762a73291d712b14",
22072321
purl: "pkg:conan/opensta@4.0.0?rrev=765a7eed989e624c762a73291d712b14",
22082322
});
2209-
dep_list = parseConanData(
2323+
assert.deepStrictEqual(Object.keys(conanLockData.dependencies).length, 0);
2324+
assert.deepStrictEqual(conanLockData.parentComponentDependencies.length, 0);
2325+
let dep_list = parseConanData(
22102326
readFileSync("./test/data/conanfile.txt", { encoding: "utf-8" }),
22112327
);
22122328
assert.deepStrictEqual(dep_list.length, 3);
@@ -2316,42 +2432,42 @@ it("conan package reference mapper to pURL", () => {
23162432
});
23172433

23182434
it("parse conan data where packages use custom user/channel", () => {
2319-
let dep_list = parseConanLockData(
2435+
const conanLockData = parseConanLockData(
23202436
readFileSync("./test/data/conan.with_custom_pkg_user_channel.lock", {
23212437
encoding: "utf-8",
23222438
}),
23232439
);
2324-
assert.deepStrictEqual(dep_list.length, 4);
2325-
assert.deepStrictEqual(dep_list[0], {
2440+
assert.deepStrictEqual(conanLockData.pkgList.length, 4);
2441+
assert.deepStrictEqual(conanLockData.pkgList[0], {
23262442
name: "libcurl",
23272443
version: "8.1.2",
23282444
"bom-ref":
23292445
"pkg:conan/libcurl@8.1.2?channel=stable&rrev=25215c550633ef0224152bc2c0556698&user=internal",
23302446
purl: "pkg:conan/libcurl@8.1.2?channel=stable&rrev=25215c550633ef0224152bc2c0556698&user=internal",
23312447
});
2332-
assert.deepStrictEqual(dep_list[1], {
2448+
assert.deepStrictEqual(conanLockData.pkgList[1], {
23332449
name: "openssl",
23342450
version: "3.1.0",
23352451
"bom-ref":
23362452
"pkg:conan/openssl@3.1.0?channel=stable&rrev=c9c6ab43aa40bafacf8b37c5948cdb1f&user=internal",
23372453
purl: "pkg:conan/openssl@3.1.0?channel=stable&rrev=c9c6ab43aa40bafacf8b37c5948cdb1f&user=internal",
23382454
});
2339-
assert.deepStrictEqual(dep_list[2], {
2455+
assert.deepStrictEqual(conanLockData.pkgList[2], {
23402456
name: "zlib",
23412457
version: "1.2.13",
23422458
"bom-ref":
23432459
"pkg:conan/zlib@1.2.13?channel=stable&rrev=aee6a56ff7927dc7261c55eb2db4fc5b&user=internal",
23442460
purl: "pkg:conan/zlib@1.2.13?channel=stable&rrev=aee6a56ff7927dc7261c55eb2db4fc5b&user=internal",
23452461
});
2346-
assert.deepStrictEqual(dep_list[3], {
2462+
assert.deepStrictEqual(conanLockData.pkgList[3], {
23472463
name: "fmt",
23482464
version: "10.0.0",
23492465
purl: "pkg:conan/fmt@10.0.0?channel=stable&rrev=79e7cc169695bc058fb606f20df6bb10&user=internal",
23502466
"bom-ref":
23512467
"pkg:conan/fmt@10.0.0?channel=stable&rrev=79e7cc169695bc058fb606f20df6bb10&user=internal",
23522468
});
23532469

2354-
dep_list = parseConanData(
2470+
const dep_list = parseConanData(
23552471
readFileSync("./test/data/conanfile.with_custom_pkg_user_channel.txt", {
23562472
encoding: "utf-8",
23572473
}),

0 commit comments

Comments
 (0)