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..b5ac06b18 100644 --- a/utils.js +++ b/utils.js @@ -4889,24 +4889,90 @@ 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; + } + + 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() + ); + 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; + } + + 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; + } + + 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() + ); + 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 () => {