UNPKG

snyk-nuget-plugin

Version:
188 lines 7.57 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.parse = parse; exports.parseNuspec = parseNuspec; const JSZip = require("jszip"); const fs = require("fs"); const path = require("path"); const parseXML = require("xml2js"); const debugModule = require("debug"); const debug = debugModule('snyk'); const targetFrameworkRegex = /([.a-zA-Z]+)([.0-9]+)/; var SupportedEncodings; (function (SupportedEncodings) { SupportedEncodings["UTF8"] = "utf-8"; SupportedEncodings["UTF16LE"] = "utf-16le"; })(SupportedEncodings || (SupportedEncodings = {})); function extractDepsForPlainGroups(rawDependency) { if (!rawDependency.group) { return []; } return rawDependency.group.filter((group) => { // valid group with no attributes or no `targetFramework` attribute return group && !(group.$ && group.$.targetFramework); }); } function assertNuspecSchema(nuspecContent, parsedNuspec) { if (!parsedNuspec.package?.metadata) { throw new Error('This is an invalid nuspec file. Package or Metadata xml section is missing. This is a required element. See https://docs.microsoft.com/en-us/nuget/reference/nuspec. The nuspec in question: ' + nuspecContent); } // just in case, this should *not* happen if (!Array.isArray(parsedNuspec.package.metadata)) { throw new Error('This is an invalid nuspec file; the metadata tag is supposed to be a collection of objects but it is not! The nuspec in question: ' + nuspecContent); } for (const metadata of parsedNuspec.package.metadata) { // just in case, this shouldn't happen as this would indicate invalid/malformed nuspec file if (metadata == null || typeof metadata !== 'object') { throw new Error('Expected elements in a "metadata" tag to be objects, but they were ' + typeof metadata + ', this is not supposed to happen and is likely due to malformed nuspec file. The nuspec in question: ' + nuspecContent); } if (metadata.dependencies) { // just in case, error would indicate malformed nuspec if (!Array.isArray(metadata.dependencies)) { throw new Error('Expected that "dependencies" tag would be an array but it isn\'t. This is not supposed to happen and is likely due to malformed nuspec file! The nuspec in question: ' + nuspecContent); } } } } function extractDepsForTargetFramework(rawDependency, targetFramework) { if (!rawDependency || !rawDependency.group) { return; } return rawDependency.group .filter((group) => { return (group?.$?.targetFramework && targetFrameworkRegex.test(group.$.targetFramework)); }) .map((group) => { const parts = group.$.targetFramework.split(targetFrameworkRegex); return { framework: parts[1], group, version: parts[2], }; }) .sort((a, b) => { if (a.framework === b.framework) { return Number(b.version) - Number(a.version); } return a.framework > b.framework ? -1 : 1; }) .find((group) => { return (targetFramework.framework === group.framework && targetFramework.version >= group.version); }); } function extractDepsFromRaw(rawDependencies) { if (!rawDependencies) { return []; } const deps = []; rawDependencies.forEach((dep) => { if (dep && dep.$) { deps.push({ dependencies: {}, name: dep.$.id, version: dep.$.version, }); } }); return deps; } function detectNuspecContentEncoding(nuspecContent) { // 65533 is a code for replacement character that is unique to UTF-16 // https://www.unicodepedia.com/unicode/specials/fffd/replacement-character/ if (nuspecContent.charCodeAt(0) === 65533) { return SupportedEncodings.UTF16LE; } return SupportedEncodings.UTF8; } function removePotentialUtf16Characters(input) { return input .replace(/\uFFFD/g, '') .replace(/\uBFEF/g, '') .replace(/\uBDBF/g, '') .replace(/\uEFBD/g, ''); } async function parse(nuspecContent, targetFramework, depName) { const parsedNuspec = await parseXML.parseStringPromise(nuspecContent); let ownDeps = []; // note: this will throw if assertion fails assertNuspecSchema(nuspecContent, parsedNuspec); for (const metadata of parsedNuspec.package.metadata) { metadata.dependencies?.forEach((rawDependency) => { // Find and add target framework version specific dependencies const depsForTargetFramework = extractDepsForTargetFramework(rawDependency, targetFramework); if (depsForTargetFramework && depsForTargetFramework.group) { ownDeps = ownDeps.concat(extractDepsFromRaw(depsForTargetFramework.group.dependency)); } // Find all groups with no targetFramework attribute // add their deps const depsFromPlainGroups = extractDepsForPlainGroups(rawDependency); if (depsFromPlainGroups) { depsFromPlainGroups.forEach((depGroup) => { ownDeps = ownDeps.concat(extractDepsFromRaw(depGroup.dependency)); }); } // Add the default dependencies ownDeps = ownDeps.concat(extractDepsFromRaw(rawDependency.dependency)); }); } return { children: ownDeps, name: depName, }; } async function loadNuspecFromAsync(dep) { const nupkgPath = path.resolve(dep.path, dep.name + '.' + dep.version + '.nupkg'); let nupkgData; try { nupkgData = fs.readFileSync(nupkgPath); } catch (error) { if (error.code == 'ENOENT') { debug('No nupkg file found at ' + nupkgPath); return null; // this is needed not to break existing code flow } throw error; } const nuspecZipData = await JSZip.loadAsync(nupkgData); const nuspecFile = Object.keys(nuspecZipData.files).find((file) => { return path.extname(file) === '.nuspec'; }); if (!nuspecFile) { throw new Error(`failed to read nupkg file from: ${nupkgPath}`); } if (!nuspecZipData) { throw new Error(`failed to open nupkg file as an archive from: ${nupkgPath}`); } const rawNuspecContent = await nuspecZipData.files[nuspecFile].async('text'); const encoding = detectNuspecContentEncoding(rawNuspecContent); const encoder = new TextEncoder(); const encoded = encoder.encode(rawNuspecContent); const decoder = new TextDecoder(encoding); const encodedNuspecContent = decoder.decode(encoded); return removePotentialUtf16Characters(encodedNuspecContent); } async function parseNuspec(dep, targetFramework) { // precaution if (!dep) { throw new Error('expected DependencyInfo parameter to have value but found it undefined'); } // another precaution if (!targetFramework) { throw new Error('expected TargetFramework parameter to have value but found it undefined'); } const nuspecContent = await loadNuspecFromAsync(dep); if (nuspecContent === null) { debug('failed to load nuspec content'); return null; } return await parse(nuspecContent, targetFramework, dep.name); } //# sourceMappingURL=nuspec-parser.js.map