From f717f05ab33eecf2f359d6ae8cfc83db11d39087 Mon Sep 17 00:00:00 2001 From: Robert Liias <44269772+robaliias@users.noreply.github.com> Date: Sun, 15 Oct 2023 17:52:57 +0300 Subject: [PATCH 1/2] feat: Dependency tree for paket.lock files Signed-off-by: Robert Liias <44269772+robaliias@users.noreply.github.com> --- README.md | 2 +- index.js | 7 +++- utils.js | 89 +++++++++++++++++++++++++++++++++++++++++++-------- utils.test.js | 17 ++++++++-- 4 files changed, 96 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index a71bb0a19..d938e6d41 100644 --- a/README.md +++ b/README.md @@ -338,7 +338,7 @@ cdxgen can retain the dependency tree under the `dependencies` attribute for a s - Gradle - Scala SBT - Python (requirements.txt, setup.py, pyproject.toml, poetry.lock) -- csharp (projects.assets.json) +- .NET (project.assets.json, paket.lock) - Go (go.mod) ## Environment variables diff --git a/index.js b/index.js index 894944bd1..167400f21 100644 --- a/index.js +++ b/index.js @@ -4144,10 +4144,15 @@ export const createCsharpBom = async ( console.log(`Parsing ${f}`); } pkgData = readFileSync(f, { encoding: "utf-8" }); - const dlist = await parsePaketLockData(pkgData); + const results = await parsePaketLockData(pkgData); + const dlist = results.pkgList; + const deps = results.dependenciesList; if (dlist && dlist.length) { pkgList = pkgList.concat(dlist); } + if (deps && deps.length) { + dependencies = dependencies.concat(deps); + } } } if (!parentComponent) { diff --git a/utils.js b/utils.js index 77b880738..811501135 100644 --- a/utils.js +++ b/utils.js @@ -4889,24 +4889,85 @@ export const parseCsPkgLockData = async function (csLockData) { export const parsePaketLockData = async function (paketLockData) { const pkgList = []; + const dependenciesList = []; + const dependenciesMap = {}; + const pkgNameVersionMap = {}; + let group = null; let pkg = null; if (!paketLockData) { - return pkgList; + return { pkgList, dependenciesList }; } - const pkgRegex = /\s+([a-zA-Z0-9-.]+) \(((?=.*?\.)[a-zA-Z0-9-.]+)\)/g; - for (const [, name, version] of paketLockData.matchAll(pkgRegex)) { - const purl = decodeURIComponent( - new PackageURL("nuget", "", name, version, null, null).toString() - ); - pkg = { - group: "", - name: name, - version: version, - purl: purl - }; - pkgList.push(pkg); + + const packages = paketLockData.split("\n"); + const groupRegex = /^GROUP\s(\S*)$/; + const pkgRegex = /^\s{4}([\w.-]+) \(((?=.*?\.)[\w.-]+)\)/; + const depRegex = /^\s{6}([\w.-]+) \([><= \w.-]+\)/; + + // Gather all packages + packages.forEach((l) => { + let match = l.match(groupRegex); + if (match) { + group = match[1]; + return; + } + + const [, name, version] = l.match(pkgRegex) || [, null, null]; + if (name) { + const purl = decodeURIComponent( + new PackageURL("nuget", "", name, version, null, null).toString() + ); + pkg = { + group: "", + name: name, + version: version, + purl: purl + }; + pkgList.push(pkg); + dependenciesMap[purl] = new Set(); + pkgNameVersionMap[name + group] = version; + } + }); + + let purl = null; + group = null; + + // Construct the dependency tree + packages.forEach((l) => { + let match = l.match(groupRegex); + if (match) { + group = match[1]; + return; + } + + const [, pkgName, pkgVersion] = l.match(pkgRegex) || [, null, null]; + if (pkgName) { + purl = decodeURIComponent( + new PackageURL("nuget", "", pkgName, pkgVersion, null, null).toString() + ); + return; + } + + const [, depName] = l.match(depRegex) || [, null]; + if (depName) { + const depVersion = pkgNameVersionMap[depName + group]; + const dpurl = decodeURIComponent( + new PackageURL("nuget", "", depName, depVersion, null, null).toString() + ); + dependenciesMap[purl].add(dpurl); + } + }); + + for (const ref in dependenciesMap) { + dependenciesList.push({ + ref: ref, + dependsOn: Array.from(dependenciesMap[ref]) + }); } - return pkgList; + + return { + pkgList, + dependenciesList + }; }; /** diff --git a/utils.test.js b/utils.test.js index 4289728ae..ceb44f67f 100644 --- a/utils.test.js +++ b/utils.test.js @@ -1299,17 +1299,28 @@ test("parse packages.lock.json", async () => { }); test("parse paket.lock", async () => { - expect(await parsePaketLockData(null)).toEqual([]); + expect(await parsePaketLockData(null)).toEqual({ + pkgList: [], + dependenciesList: [] + }); const dep_list = await parsePaketLockData( readFileSync("./test/data/paket.lock", { encoding: "utf-8" }) ); - expect(dep_list.length).toEqual(13); - expect(dep_list[0]).toEqual({ + expect(dep_list.pkgList.length).toEqual(13); + expect(dep_list.pkgList[0]).toEqual({ group: "", name: "0x53A.ReferenceAssemblies.Paket", version: "0.2", purl: "pkg:nuget/0x53A.ReferenceAssemblies.Paket@0.2" }); + expect(dep_list.dependenciesList.length).toEqual(13); + expect(dep_list.dependenciesList[2]).toEqual({ + ref: "pkg:nuget/FSharp.Compiler.Service@17.0.1", + dependsOn: [ + "pkg:nuget/System.Collections.Immutable@1.4", + "pkg:nuget/System.Reflection.Metadata@1.5" + ] + }); }); test("parse .net cs proj", async () => { From a00955abae6d3a18247a951d30b84b31e1713746 Mon Sep 17 00:00:00 2001 From: Robert Liias <44269772+robaliias@users.noreply.github.com> Date: Sun, 15 Oct 2023 18:55:04 +0300 Subject: [PATCH 2/2] Fix linter errors Signed-off-by: Robert Liias <44269772+robaliias@users.noreply.github.com> --- utils.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/utils.js b/utils.js index 811501135..b5ac06b18 100644 --- a/utils.js +++ b/utils.js @@ -4911,8 +4911,10 @@ export const parsePaketLockData = async function (paketLockData) { return; } - const [, name, version] = l.match(pkgRegex) || [, null, null]; - if (name) { + match = l.match(pkgRegex); + if (match) { + const name = match[1]; + const version = match[2]; const purl = decodeURIComponent( new PackageURL("nuget", "", name, version, null, null).toString() ); @@ -4939,16 +4941,19 @@ export const parsePaketLockData = async function (paketLockData) { return; } - const [, pkgName, pkgVersion] = l.match(pkgRegex) || [, null, null]; - if (pkgName) { + match = l.match(pkgRegex); + if (match) { + const pkgName = match[1]; + const pkgVersion = match[2]; purl = decodeURIComponent( new PackageURL("nuget", "", pkgName, pkgVersion, null, null).toString() ); return; } - const [, depName] = l.match(depRegex) || [, null]; - if (depName) { + match = l.match(depRegex); + if (match) { + const depName = match[1]; const depVersion = pkgNameVersionMap[depName + group]; const dpurl = decodeURIComponent( new PackageURL("nuget", "", depName, depVersion, null, null).toString()