UNPKG

snyk-nuget-plugin

Version:
144 lines 8.31 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.FILTERED_DEPENDENCY_PREFIX = void 0; exports.parse = parse; const debugModule = require("debug"); const dep_graph_1 = require("@snyk/dep-graph"); const errors_1 = require("../../errors"); const debug = debugModule('snyk'); // Dependencies that starts with these are discarded exports.FILTERED_DEPENDENCY_PREFIX = [ // `runtime` and `runtime.native` are a bit of a hot topic, see more https://github.com/dotnet/core/issues/7568. // For our case, we are already creating the correct dependencies and their respective runtime version numbers based // of our runtime resolution logic. So a dependency will already be `System.Net.Http@8.0.0` if running on .NET 8, thus // removing the need for a `runtime.native.System.Net.Http@8.0.0` as well. From our investigation these runtime native // dependencies are causing noise for the customers and are not of interested. 'runtime', ]; function recursivelyPopulateNodes(depGraphBuilder, resolvedPackages, parentID, dependencies, overrides, visited) { if (!dependencies) { return; } const visitedCopy = new Set(visited); for (const [childName, childResolvedVersion] of Object.entries(dependencies)) { const localVisited = visitedCopy || new Set(); // Ignore packages with specific prefixes, which for one reason or the other are no interesting and pollutes the // graph. Refer to comments on the individual elements in the ignore list for more information. if (exports.FILTERED_DEPENDENCY_PREFIX.some((prefix) => childName.startsWith(prefix))) { debug(`${childName} matched a prefix we ignore, not adding to graph`); continue; } // Find the actual resolved version and target for this package name // NuGet may resolve to a different version than what's declared in transitive dependencies // and use the lowercased name as NuGet packages are case-insensitive const lowercaseChildName = childName.toLowerCase(); const resolvedPackage = resolvedPackages[lowercaseChildName]; if (!resolvedPackage) { debug(`Child package ${childName} not found in lock file packages for framework.`); continue; } const { name: actualPkgName, resolvedVersion: actualResolvedVersion, target: childPkgEntry, } = resolvedPackage; if (childResolvedVersion !== actualResolvedVersion) { debug(`Version mismatch for ${childName}: declared ${childResolvedVersion}, using resolved ${actualResolvedVersion}`); } const childID = `${actualPkgName}@${actualResolvedVersion}`; let finalVersion = actualResolvedVersion; // If we're looking at a runtime assembly version for self-contained dlls, overwrite the dependency version // we've found in the graph with those from the runtime assembly, as they take precedence. if (overrides.overrideVersion && +actualResolvedVersion.split('.')[0] < 6 && actualPkgName in overrides.overridesAssemblies && +overrides.overridesAssemblies[actualPkgName].split('.')[0] < 6) { finalVersion = overrides.overrideVersion; } if (localVisited.has(childID)) { const prunedID = `${childID}:pruned`; depGraphBuilder.addPkgNode({ name: actualPkgName, version: finalVersion }, prunedID, { labels: { pruned: 'true' }, }); depGraphBuilder.connectDep(parentID, prunedID); debug(`Pruning duplicate dependency: ${parentID} -> ${childID}`); continue; } depGraphBuilder.addPkgNode({ name: actualPkgName, version: finalVersion }, childID); depGraphBuilder.connectDep(parentID, childID); localVisited.add(childID); debug(`Adding dependency: ${parentID} -> ${childID}`); recursivelyPopulateNodes(depGraphBuilder, resolvedPackages, childID, childPkgEntry.dependencies, overrides, localVisited); } } function buildDepGraph(projectName, targetFramework, projectAssets, overrides) { const depGraphBuilder = new dep_graph_1.DepGraphBuilder({ name: 'nuget' }, { name: projectName, version: projectAssets.project.version, }); if (!targetFramework) { // This should ideally not happen if validateManifest and parse are called first throw new errors_1.InvalidManifestError('Target framework not found in lock file metadata.'); } const assetsTargetFrameworks = Object.keys(projectAssets.targets); // Match the targetFramework as a prefix or a superstring of the entries in project.assets.json to handle this: // [superstring] net6.0-windows10.0.19041.0 => net6.0-windows10.0.19041 // [prefix] net8.0-windows => net8.0-windows7.0 const partialMatches = assetsTargetFrameworks.filter((target) => target.startsWith(targetFramework) || targetFramework.startsWith(target)); // If no partial match could be found, use the first entry in project.assets.json as a last resort to avoid failing. const assetsTargetFramework = partialMatches.length > 0 ? partialMatches[0] : assetsTargetFrameworks[0]; if (!assetsTargetFramework) { // This should ideally not happen if validateManifest and parse are called first throw new errors_1.InvalidManifestError(`No target framework found in project.assets.json dependencies, ${targetFramework} requested.`); } if (assetsTargetFramework !== targetFramework) { debug(`Using ${assetsTargetFramework} instead of requested ${targetFramework} (partial matches: ${partialMatches.join(',')})`); } const allPackagesForFramework = projectAssets.targets[assetsTargetFramework]; const resolvedPackages = {}; for (const [key, target] of Object.entries(allPackagesForFramework)) { const [name, version] = key.split('/'); // Use the lowercased name for lookups as NuGet packages are case-insensitive. resolvedPackages[name.toLowerCase()] = { name, resolvedVersion: version, target, }; } // Identify direct dependencies for the selected framework const directDependencies = {}; projectAssets.projectFileDependencyGroups[assetsTargetFramework].forEach((dependency) => { const dependencySplit = dependency.split(' '); directDependencies[dependencySplit[0]] = dependencySplit[2]; }); debug(`Direct dependencies found in lock file for ${assetsTargetFramework}: '${Object.keys(directDependencies)}'`); if (Object.keys(directDependencies).length === 0) { debug('No direct dependencies found in project.assets.json for the selected framework.'); // Return a graph with just the root if no direct dependencies return depGraphBuilder.build(); } // Start recursive population from direct dependencies recursivelyPopulateNodes(depGraphBuilder, resolvedPackages, 'root-node', directDependencies, // Pass the direct dependencies object overrides); return depGraphBuilder.build(); } function validateManifest(manifest) { if (!manifest.project) { throw new errors_1.InvalidManifestError('Project field was not found in project.assets.json'); } if (!manifest.project.frameworks) { throw new errors_1.InvalidManifestError('No frameworks were found in project.assets.json'); } if (!manifest.project.frameworks || Object.keys(manifest.project.frameworks).length === 0) { throw new errors_1.InvalidManifestError('0 frameworks were found in project.assets.json'); } if (!manifest.targets) { throw new errors_1.InvalidManifestError('No targets were found in project.assets.json'); } if (!manifest.targets || Object.keys(manifest.targets).length === 0) { throw new errors_1.InvalidManifestError('0 targets were found in project.assets.json'); } } function parse(projectName, targetFramework, projectAssets, overrides) { debug('Trying to parse project.assets.json manifest with v3 depGraph builder'); validateManifest(projectAssets); return buildDepGraph(projectName, targetFramework, projectAssets, overrides); } //# sourceMappingURL=dotnet-core-v3-parser.js.map