Skip to content

Commit

Permalink
feat: read target framework from project.assets.json
Browse files Browse the repository at this point in the history
  • Loading branch information
orsagie committed Feb 27, 2019
1 parent e7a3a28 commit b8cc7fd
Show file tree
Hide file tree
Showing 22 changed files with 1,776 additions and 198 deletions.
3 changes: 2 additions & 1 deletion lib/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use strict';
const path = require('path');
const nugetParser = require('./nuget-parser/index');
const nugetParser = require('./nuget-parser');
const paketParser = require('snyk-paket-parser');

function determineManifestType(filename) {
Expand Down
23 changes: 12 additions & 11 deletions lib/nuget-parser/csproj-parser.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
'use strict';
const fs = require('fs');
const path = require('path');
const parseXML = require('xml2js').parseString;
const debug = require('debug')('snyk');
const _ = require('lodash');

function determineDotnetVersions(rootDir) {
function getTargetFrameworksFromProjFile(rootDir) {
debug('Looking for your .csproj file in ' + rootDir);
const csprojPath = findFile(rootDir, /.*\.csproj$/);
if (!csprojPath) {
Expand All @@ -15,7 +16,7 @@ function determineDotnetVersions(rootDir) {

const csprojContents = fs.readFileSync(csprojPath);

var frameworks = [];
let frameworks = [];
parseXML(csprojContents, function (err, parsedCsprojContents) {
if (err) {
throw err;
Expand All @@ -27,16 +28,16 @@ function determineDotnetVersions(rootDir) {
_.get(versionLoc, 'TargetFrameworks[0]', '').split(';')));

if (versions.length < 1) {
throw new Error('Could not find TargetFrameworkVersion/TargetFramework' +
debug('Could not find TargetFrameworkVersion/TargetFramework' +
'/TargetFrameworks defined in the Project.PropertyGroup field of ' +
'your .csproj file');
}
frameworks = _.compact(_.map(versions, toReadableFramework));
if (versions.length > 1 && frameworks.length < 1) {
throw new Error('Could not find valid/supported .NET version in csproj file located at' + csprojPath);
}
});
if (frameworks.length < 1) {
throw new Error('Could not find valid/supported .NET version');
}
return frameworks;
return frameworks[0];
}

function toReadableFramework(targetFramework) {
Expand All @@ -47,7 +48,7 @@ function toReadableFramework(targetFramework) {
netcoreapp: '.NETCore',
};

for (var type in typeMapping) {
for (const type in typeMapping) {
if (new RegExp(type + /\d.?\d(.?\d)?$/.source).test(targetFramework)) {
return {
framework: typeMapping[type],
Expand All @@ -63,13 +64,13 @@ function findFile(rootDir, filter) {
throw new Error('No such path: ' + rootDir);
}
const files = fs.readdirSync(rootDir);
for (var i = 0; i < files.length; i++) {
var filename = path.resolve(rootDir, files[i]);
for (let i = 0; i < files.length; i++) {
const filename = path.resolve(rootDir, files[i]);

if (filter.test(filename)) {
return filename;
}
}
}

module.exports = determineDotnetVersions;
module.exports = getTargetFrameworksFromProjFile;
1 change: 1 addition & 0 deletions lib/nuget-parser/dependency.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use strict';
var debug = require('debug')('snyk');

function Dependency(name, version) {
Expand Down
65 changes: 38 additions & 27 deletions lib/nuget-parser/dotnet-core-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,36 +84,19 @@ function buildTreeRecursive(targetDeps, depName, parent, treeDepth) {
});
}


function getFrameworkObjToRun(manifest) {
function getFrameworkToRun(manifest) {
const frameworks = _.get(manifest, 'project.frameworks');
if (!frameworks) {
throw new Error('No frameworks were found in project.assets.json');
}

if (_.isEmpty(frameworks)) {
throw new Error('0 frameworks were found in project.assets.json');
}

debug(`Available frameworks: '${Object.keys(frameworks)}'`);

// not yet supporting multiple frameworks in the same assets file ->
// taking only the first 1
const selectedFrameworkKey = Object.keys(frameworks)[0];
debug(`Selected framework: '${selectedFrameworkKey}'`);
return frameworks[selectedFrameworkKey];
return selectedFrameworkKey;
}


function getTargetObjToRun(manifest) {
if (!manifest.targets) {
throw new Error('No targets were found in project.assets.json');
}

if (_.isEmpty(manifest.targets)) {
throw new Error('0 targets were found in project.assets.json');
}

debug(`Available targets: '${Object.keys(manifest.targets)}'`);

let selectedTargetKey = Object.keys(manifest.targets)[0];
Expand All @@ -123,21 +106,51 @@ function getTargetObjToRun(manifest) {
return manifest.targets[selectedTargetKey];
}

function validateManifest(manifest) {
if (!manifest.project) {
throw new Error('Project field was not found in project.assets.json');
}

if (!manifest.project.frameworks) {
throw new Error('No frameworks were found in project.assets.json');
}

if (_.isEmpty(manifest.project.frameworks)) {
throw new Error('0 frameworks were found in project.assets.json');
}

if (!manifest.targets) {
throw new Error('No targets were found in project.assets.json');
}

if (_.isEmpty(manifest.targets)) {
throw new Error('0 targets were found in project.assets.json');
}
}

module.exports = {
parse: function (tree, fileContent, fileContentParser) {
return new Promise(function parseFileContents(resolve) {
parse: function (tree, manifest) {
return new Promise(function parseFileContents(resolve, reject) {
debug('Trying to parse dot-net-cli manifest');
const manifest = fileContentParser.parse(fileContent);

if (!manifest.project) {
throw new Error('Project field was not found in project.assets.json');
try {
validateManifest(manifest);
} catch (err) {
debug('Invalid project.assets.json manifest file');
reject(err);
}

if (manifest.project.version) {
tree.version = manifest.project.version;
}

const selectedFrameworkObj = getFrameworkObjToRun(manifest);
// If a targetFramework was not found in the proj file, we will extract it from the lock file
if (!tree.meta.targetFramework) {
tree.meta.targetFramework = getFrameworkToRun(manifest);
}
const selectedFrameworkObj = manifest.project.frameworks[tree.meta.targetFramework];

// We currently ignore the found targetFramework when looking for target dependencies
const selectedTargetObj = getTargetObjToRun(manifest);

initFreqDepsDict();
Expand All @@ -157,8 +170,6 @@ module.exports = {
// JSON parse/stringify is used
tree.dependencies = JSON.parse(JSON.stringify(tree.dependencies));
resolve(tree);
}).catch(function (err) {
throw (err);
});
},
};
52 changes: 29 additions & 23 deletions lib/nuget-parser/dotnet-framework-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const debug = require('debug')('snyk');
const path = require('path');
const Dependency = require('./dependency');
const parseNuspec = require('./nuspec-parser');
const _ = require('lodash');

function injectPath(dep, packagesFolder) {
dep.path = dep.localPath ?
Expand All @@ -25,24 +26,30 @@ function scanInstalled(installedPackages, packagesFolder) {
});
try {
debug('Scanning local installed folders');
debug('Trying to read from installed packages folder: ' +
packagesFolder);
debug('Trying to read from installed packages folder: ' + packagesFolder);
fs.readdirSync(packagesFolder)
.map(function (folderName) {
return Dependency.from.folderName(folderName);
try {
return Dependency.from.folderName(folderName);
} catch (err) {
debug('Unable to parse dependency from folder');
debug(err);
}
})
.forEach(function (dep) {
injectPath(dep, packagesFolder);
// only add a package from packages folder if version is different
if (flattenedPackageList[dep.name] &&
flattenedPackageList[dep.name].version !== dep.version) {
// prefer found from packages folder (dep) over existing
debug('For package ' + dep.name + ' the version ' +
flattenedPackageList[dep.name].version +
' was extracted from manifest file.' +
'\nWe are overwriting it with version ' + dep.version +
' from the packages folder');
flattenedPackageList[dep.name] = dep;
if (dep) {
injectPath(dep, packagesFolder);
// only add a package from packages folder if version is different
if (flattenedPackageList[dep.name] &&
flattenedPackageList[dep.name].version !== dep.version) {
// prefer found from packages folder (dep) over existing
debug('For package ' + dep.name + ' the version ' +
flattenedPackageList[dep.name].version +
' was extracted from manifest file.' +
'\nWe are overwriting it with version ' + dep.version +
' from the packages folder');
flattenedPackageList[dep.name] = dep;
}
}
});
} catch (err) {
Expand All @@ -52,14 +59,14 @@ function scanInstalled(installedPackages, packagesFolder) {
return flattenedPackageList;
}

function fetchNugetInformationFromPackages(flattenedPackageList, dotnetVersions) {
function fetchNugetInformationFromPackages(flattenedPackageList, targetFramework) {
const nuspecParserChain = [];
// begin collecting information from .nuget files on installed packages
debug('Trying to analyze .nuspec files');
for (const name in flattenedPackageList) {
const dep = flattenedPackageList[name];
debug('...' + name);
nuspecParserChain.push(parseNuspec(dep, dotnetVersions));
nuspecParserChain.push(parseNuspec(dep, targetFramework));
}
return Promise.all(nuspecParserChain);
}
Expand All @@ -77,12 +84,13 @@ function processNugetInformation(nuspecResolutionChain) {
}

module.exports = {
parse: function (tree, fileContent, fileContentParser, dotnetVersions, packagesFolder) {
tree.meta.targetFramework = dotnetVersions[0].original;
parse: function (tree, manifest, targetFramework, packagesFolder) {
if (!targetFramework) {
throw new Error('No valid Dotnet target framework found');
}

const installedPackages = fileContentParser.parse(fileContent, tree);
const flattenedPackageList = scanInstalled(installedPackages, packagesFolder);
return fetchNugetInformationFromPackages(flattenedPackageList, dotnetVersions)
const flattenedPackageList = scanInstalled(manifest, packagesFolder);
return fetchNugetInformationFromPackages(flattenedPackageList, targetFramework)
.then(processNugetInformation)
.then(function buildDependencyTree(nuspecResolutions) {
// .nuget parsing is complete, returned as array of promise resolutions
Expand Down Expand Up @@ -127,8 +135,6 @@ module.exports = {
}
}
return tree;
}).catch(function (err) {
throw (err);
});
},
};
19 changes: 10 additions & 9 deletions lib/nuget-parser/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
'use strict';
const fs = require('fs');
const path = require('path');
const debug = require('debug')('snyk');
const determineDotnetVersions = require('./csproj-parser');
const getTargetFrameworkFromProjFile = require('./csproj-parser');

const dotnetCoreParser = require('./dotnet-core-parser');
const dotnetFrameworkParser = require('./dotnet-framework-parser');
Expand Down Expand Up @@ -42,13 +43,13 @@ module.exports = {
const projectRootFolder = path.resolve(fileContentPath, '../../');
const packagesFolder = getPackagesFolder(packagesFolderPath, projectRootFolder);

let dotnetVersions;
let targetFramework;
try {
if (manifestType === 'dotnet-core') {
dotnetVersions = determineDotnetVersions(projectRootFolder);
targetFramework = getTargetFrameworkFromProjFile(projectRootFolder);
} else {
// .csproj is in the same directory as packages.config or project.json
dotnetVersions = determineDotnetVersions(path.resolve(fileContentPath, '../'));
targetFramework = getTargetFrameworkFromProjFile(path.resolve(fileContentPath, '../'));
}
} catch (error) {
return Promise.reject(error);
Expand All @@ -62,15 +63,15 @@ module.exports = {
packageFormatVersion: 'nuget:0.0.0',
dependencies: {},
meta: {
targetFramework: dotnetVersions[0].original, //TODO implement for more than one TF
targetFramework: targetFramework ? targetFramework.original : undefined, //TODO implement for more than one TF
},
};

const parser = PARSERS[manifestType];
return parser.depParser.parse(tree,
fileContent,
parser.fileContentParser,
dotnetVersions,
return parser.depParser.parse(
tree,
parser.fileContentParser.parse(fileContent, tree),
targetFramework,
packagesFolder);
},
};
11 changes: 6 additions & 5 deletions lib/nuget-parser/nuspec-parser.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use strict';
const JSZip = require('jszip');
const fs = require('fs');
const path = require('path');
Expand All @@ -8,7 +9,7 @@ const debug = require('debug')('snyk');

const targetFrameworkRegex = /([.a-zA-Z]+)([.0-9]+)/;

function parseNuspec(dep, targetFrameworks, sep) {
function parseNuspec(dep, targetFramework, sep) {
return Promise.resolve()
.then(function () {
const pathSep = sep || '.';
Expand Down Expand Up @@ -40,7 +41,7 @@ function parseNuspec(dep, targetFrameworks, sep) {

// Find and add target framework version specific dependencies
const depsForTargetFramework =
extractDepsForTargetFrameworks(rawDependency, targetFrameworks);
extractDepsForTargetFramework(rawDependency, targetFramework);

if (depsForTargetFramework && depsForTargetFramework.group) {
ownDeps = _.concat(ownDeps,
Expand Down Expand Up @@ -87,7 +88,7 @@ function extractDepsForPlainGroups(rawDependency) {
});
}

function extractDepsForTargetFrameworks(rawDependency, targetFrameworks) {
function extractDepsForTargetFramework(rawDependency, targetFramework) {
return rawDependency && _(rawDependency.group)
.filter(function (group) {
return group && group.$ && group.$.targetFramework &&
Expand All @@ -103,8 +104,8 @@ function extractDepsForTargetFrameworks(rawDependency, targetFrameworks) {
})
.orderBy(['framework', 'version'], ['asc', 'desc'])
.find(function (group) {
return targetFrameworks[0].framework === group.framework &&
targetFrameworks[0].version >= group.version;
return targetFramework.framework === group.framework &&
targetFramework.version >= group.version;
});
}

Expand Down
Loading

0 comments on commit b8cc7fd

Please sign in to comment.