snyk-nuget-plugin
Version:
Snyk CLI NuGet plugin
161 lines • 9.56 kB
JavaScript
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 path_1 = require("path");
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, targetDeps, node, runtimeAssembly, useFixForImprovedDotnetFalsePositives, visited) {
const parentId = node.type === 'root' ? 'root-node' : `${node.name}@${node.version}`;
for (const depNode of Object.entries(node.dependencies || {})) {
const localVisited = visited || new Set();
const name = depNode[0];
const version = depNode[1];
// 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) => name.startsWith(prefix))) {
debug(`${name} matched a prefix we ignore, not adding to graph`);
continue;
}
const childNode = {
...targetDeps[`${name}/${version}`],
name,
version,
};
const childId = `${childNode.name}@${childNode.version}`;
// 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.
let assemblyVersion = version;
// The RuntimeAssembly type contains the name with a .dll suffix, as this is how .NET represents them in the
// dependency file. This must be stripped in order to match the elements during depGraph construction.
const dll = `${name}.dll`;
if (dll in runtimeAssembly) {
assemblyVersion = runtimeAssembly[dll];
}
if (localVisited.has(childId)) {
const prunedId = `${childId}:pruned`;
depGraphBuilder.addPkgNode({ name: childNode.name, version: assemblyVersion }, prunedId, {
labels: { pruned: 'true' },
});
depGraphBuilder.connectDep(parentId, prunedId);
continue;
}
depGraphBuilder.addPkgNode({ name: childNode.name, version: assemblyVersion }, childId);
depGraphBuilder.connectDep(parentId, childId);
localVisited.add(childId);
recursivelyPopulateNodes(depGraphBuilder, targetDeps, childNode, runtimeAssembly, useFixForImprovedDotnetFalsePositives, localVisited);
}
}
function getRestoredProjectName(publishedProjectDeps, runtimeTarget, projectName) {
return Object.keys(publishedProjectDeps.targets[runtimeTarget]).find((f) => f.startsWith(projectName));
}
function extractLocalProjects(libs) {
const localPackages = [];
for (const [key, value] of Object.entries(libs)) {
if (!key.includes('runtimepack')) {
// Local projects (.csproj files) don't have values declared for these two properties.
// https://github.com/dotnet/sdk/blob/main/documentation/specs/runtime-configuration-file.md#libraries-section-depsjson
if (!value.serviceable && !value.sha512 && value.type === 'project') {
localPackages.push(key);
}
}
}
return localPackages;
}
function getDllName(depName) {
return `${depName}.dll`;
}
function buildGraph(projectName, projectAssets, publishedProjectDeps, runtimeAssembly, useFixForImprovedDotnetFalsePositives) {
const depGraphBuilder = new dep_graph_1.DepGraphBuilder({ name: 'nuget' }, {
name: projectName,
version: projectAssets.project.version,
});
// We publish to one RID and one only, so we can safely assume that the true dependencies will exist in this key.
// E.g. targets -> .NETCoreApp,Version=v8.0/osx-arm64
const runtimeTarget = publishedProjectDeps.runtimeTarget.name;
// Those dependencies are referenced in the 'targets' member in the same .deps file.
if (Object.keys(publishedProjectDeps.targets).length <= 0) {
throw new errors_1.InvalidManifestError('no target dependencies in found in published deps file (project.deps.json -> targets -> []), cannot continue without that');
}
if (!(runtimeTarget in publishedProjectDeps.targets)) {
throw new errors_1.InvalidManifestError(`no ${runtimeTarget} found in targets object, cannot continue without it`);
}
// What `dotnet` wants to call this project is not always the same as what Snyk wants to call it, and the version
// postfix is not the same as what's defined in `project.assets.json` due to NuGet version normalization, which is
// not applied during publish, only during restore. So we have to rely on the fact that the name is enough.
const csprojPath = projectAssets.project.restore.projectUniqueName;
const csprojFileName = (0, path_1.basename)(csprojPath, (0, path_1.extname)(csprojPath));
const restoreProjectName =
// Attempt to find the target using the .csproj filename.
// <PackageId> property overrides most of the naming when restoring, but when publishing, the actual filename is used as the target.
getRestoredProjectName(publishedProjectDeps, runtimeTarget, csprojFileName) ||
// Attempt to find the target using restored project name.
getRestoredProjectName(publishedProjectDeps, runtimeTarget, projectAssets?.project?.restore?.projectName) ||
// Last attempt to find the target using the Snyk project name.
getRestoredProjectName(publishedProjectDeps, runtimeTarget, projectName);
if (!restoreProjectName) {
throw new errors_1.InvalidManifestError(`no project name containing ${projectAssets.project.restore.projectName} or ${projectName} found in ${runtimeTarget} object, cannot continue without it`);
}
// Find names and versions of all dependencies of the root package. These are already structured correctly in
// the deps.json generated by `dotnet publish`.
const topLevelDepPackages = publishedProjectDeps.targets[runtimeTarget][restoreProjectName]
.dependencies;
// Iterate over all the dependencies found in the target dependency list, and build the depGraph based off of that.
const targetDependencies = Object.entries(publishedProjectDeps.targets[runtimeTarget]).reduce((acc, entry) => {
const [nameWithVersion, pkg] = entry;
return { ...acc, [nameWithVersion]: pkg };
}, {});
const rootNode = {
type: 'root',
dependencies: topLevelDepPackages,
};
if (!useFixForImprovedDotnetFalsePositives) {
// runtimeAssembly doesn't have entries if the target framework is `netstandard`
if (Object.keys(runtimeAssembly).length > 0) {
const localPackagesNames = extractLocalProjects(publishedProjectDeps.libraries);
const targets = publishedProjectDeps.targets[runtimeTarget];
// Overwriting the runtime versions with the values used in local projects.
for (const pgkName of localPackagesNames) {
if (targets[pgkName]?.dependencies) {
for (const [key, value] of Object.entries(targets[pgkName].dependencies)) {
const dllName = getDllName(key);
if (runtimeAssembly[dllName]) {
runtimeAssembly[dllName] = value;
}
}
}
}
// Overwriting the runtime versions with the values used in fetched packages.
for (const [key, value] of Object.entries(targets)) {
if (value && Object.keys(value).length === 0) {
const [depName, depVersion] = key.split('/');
const dllName = getDllName(depName);
// NuGet’s dependency resolution mechanism will choose the higher available version.
if (runtimeAssembly[dllName] &&
depVersion > runtimeAssembly[dllName]) {
runtimeAssembly[dllName] = depVersion;
}
}
}
}
}
recursivelyPopulateNodes(depGraphBuilder, targetDependencies, rootNode, runtimeAssembly, useFixForImprovedDotnetFalsePositives);
return depGraphBuilder.build();
}
function parse(projectName, projectAssets, publishedProjectDeps, runtimeAssembly, useFixForImprovedDotnetFalsePositives) {
debug('Trying to parse .net core manifest with v2 depGraph builder');
const result = buildGraph(projectName, projectAssets, publishedProjectDeps, runtimeAssembly, useFixForImprovedDotnetFalsePositives);
return result;
}
//# sourceMappingURL=dotnet-core-v2-parser.js.map
;