snyk-nuget-plugin
Version:
Snyk CLI NuGet plugin
114 lines • 5.92 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.PACKS_PATH = exports.PACKAGE_OVERRIDES_FILE = void 0;
exports.extractSdkInfo = extractSdkInfo;
exports.findLatestMatchingVersion = findLatestMatchingVersion;
exports.generateRuntimeAssemblies = generateRuntimeAssemblies;
const errors_1 = require("../errors");
const debugModule = require("debug");
const dotnet = require("./cli/dotnet");
const fs = require("fs");
const path = require("path");
const debug = debugModule('snyk');
const DOTNET_DEPS_JSON = 'dotnet.deps.json';
exports.PACKAGE_OVERRIDES_FILE = 'data/PackageOverrides.txt';
exports.PACKS_PATH = '/packs/Microsoft.NETCore.App.Ref/';
// Relying on dotnet to fetch the right version that the project will use.
// Details: https://learn.microsoft.com/en-us/dotnet/core/versions/selection#the-sdk-uses-the-latest-installed-version
// And here: https://learn.microsoft.com/en-us/dotnet/core/tools/global-json#matching-rules
async function extractSdkInfo(projectPath) {
const infoOutput = await dotnet.execute(['--info'], projectPath);
const regex = /Version:\s*([\d.]+).*?\.NET SDKs installed:\s*([\s\S]*?)(?:\n\s*\1\s+\[(.*?)\])/s;
const match = infoOutput.match(regex);
if (!match) {
throw new errors_1.CliCommandError(`Could not fetch details about the dotnet SDK. Cannot continue without it.
Dotnet info output: ${infoOutput}`);
}
return { sdkVersion: match[1], sdkPath: match[3] };
}
function findLatestMatchingVersion(input, sdkVersion) {
const majorSdkVersion = sdkVersion.split('.')[0];
const regex = new RegExp(`Microsoft\\.NETCore\\.App ${majorSdkVersion}\\.(\\d+\\.\\d+) \\[`, 'g');
let lastMatchVersion = null;
let match;
while ((match = regex.exec(input)) !== null) {
lastMatchVersion = `${majorSdkVersion}.${match[1]}`;
}
if (!lastMatchVersion) {
throw new errors_1.CliCommandError(`Could not fetch details about the dotnet runtime. Cannot continue without it.
Dotnet list-runtimes output: ${input}`);
}
return lastMatchVersion;
}
function getDllName(name) {
return `${name}.dll`;
}
function shouldOverrideAssembly(version) {
const overrideMajorVersion = +version.split('.')[0];
return overrideMajorVersion > 5;
}
// The Nuget dependency resolution rule of lowest applicable version
// (see https://learn.microsoft.com/en-us/nuget/concepts/dependency-resolution#lowest-applicable-version)
// does not apply to runtime dependencies. If you resolve a dependency graph of some package, that depends on
// System.Http.Net 4.0.0, you might still very well end up using System.Http.Net 7.0.0 if you are running your
// executable on .net7.0.
// The libraries and package overrides defined in the current sdk will give a good estimate of what runtime dependencies are going to be used,
// so we inspect that for information.
// See https://natemcmaster.com/blog/2017/12/21/netcore-primitives/ for a good overview.
// And https://github.com/dotnet/sdk/blob/main/documentation/specs/runtime-configuration-file.md for the official
// explanation of what the `deps.json` file is doing that we are traversing.
async function generateRuntimeAssemblies(projectPath, assemblyVersions) {
debug(`Extracting runtime assemblies`);
const sdkAssemblies = {};
const { sdkVersion, sdkPath } = await extractSdkInfo(projectPath);
try {
const sdkDataPath = `${sdkPath}/${sdkVersion}/${DOTNET_DEPS_JSON}`;
const sdkData = fs.readFileSync(sdkDataPath, 'utf-8');
const assemblies = JSON.parse(sdkData);
for (const [assemblyName, value] of Object.entries(assemblies.libraries)) {
// We're only insterested in packages that are part of the NuGet Gallery
// https://github.com/dotnet/sdk/blob/main/documentation/specs/runtime-configuration-file.md#libraries-section-depsjson
if (value.serviceable && value.sha512) {
const [name, version] = assemblyName.split('/');
sdkAssemblies[getDllName(name)] = version;
}
}
}
catch (err) {
throw new errors_1.FileNotProcessableError(`Failed to process dotnet.deps.json, error: ${err}`);
}
const localRuntimes = await dotnet.execute(['--list-runtimes'], projectPath);
const runtimeVersion = findLatestMatchingVersion(localRuntimes, sdkVersion);
const overridesAssemblies = {};
try {
const overridesPath = `${path.dirname(sdkPath)}${exports.PACKS_PATH}${runtimeVersion}/${exports.PACKAGE_OVERRIDES_FILE}`;
const overridesText = fs.readFileSync(overridesPath, 'utf-8');
for (const pkg of overridesText.split('\n')) {
if (pkg) {
const [name, version] = pkg.split('|');
// Trim any carriage return
overridesAssemblies[getDllName(name)] = version.trim();
}
}
}
catch (err) {
throw new errors_1.FileNotProcessableError(`Failed to read PackageOverrides.txt, error: ${err}`);
}
// Override the versions just for the OOB packages.
for (const assemblyName in assemblyVersions) {
// OOB packages are deeply inside the namespace. Avoid updating the version for the upper ones.
const shouldUpdateVersion = (assemblyName.match(/\./g) || []).length > 2;
if (assemblyName in sdkAssemblies &&
(!(assemblyName in overridesAssemblies) ||
shouldOverrideAssembly(overridesAssemblies[assemblyName])) &&
shouldUpdateVersion) {
assemblyVersions[assemblyName] = sdkAssemblies[assemblyName];
}
}
if (Object.keys(assemblyVersions).length === 0) {
throw new errors_1.FileNotProcessableError('Runtime assembly versions collection is empty');
}
debug(`Finished extracting runtime assemblies`);
return assemblyVersions;
}
//# sourceMappingURL=runtime-assembly-v2.js.map
;