Skip to content

Commit

Permalink
Merge pull request #317 from snyk/feat/support-docker-binaries
Browse files Browse the repository at this point in the history
feat: fixed in key in binaries output
  • Loading branch information
karniwl committed Jan 1, 2019
2 parents ee43b4a + 80daaf5 commit bb31229
Show file tree
Hide file tree
Showing 4 changed files with 305 additions and 97 deletions.
185 changes: 109 additions & 76 deletions src/cli/commands/test.js
@@ -1,6 +1,7 @@
module.exports = test;

var _ = require('lodash');
var semver = require('semver');
var chalk = require('chalk');
var debug = require('debug')('snyk');
var snyk = require('../../lib/');
Expand Down Expand Up @@ -195,7 +196,6 @@ function summariseErrorResults(errorResults) {
function displayResult(res, options) {
var meta = metaForDisplay(res, options) + '\n\n';
var dockerAdvice = dockerRemediationForDisplay(res);
var binariesIssues = binariesIssuesForDisplay(res);
var packageManager = options.packageManager;
var prefix = chalk.bold.white('\nTesting ' + options.path + '...\n\n');

Expand Down Expand Up @@ -236,7 +236,6 @@ function displayResult(res, options) {
return (
prefix + meta + summaryOKText + (
isCI ? '' :
binariesIssues +
dockerAdvice +
nextStepsText +
dockerSuggestion)
Expand Down Expand Up @@ -288,58 +287,118 @@ function displayResult(res, options) {
['metadata.severityValue', 'metadata.name'],
['asc', 'desc']
);
var groupedVulnInfoOutput = sortedGroupedVulns.map(function (vuln) {
var vulnID = vuln.list[0].id;
var uniquePackages = _.uniq(
vuln.list.map(function (i) {
if (i.from[1]) {
return i.from && i.from[1];
}
return i.from;
}))
.join(', ');

var vulnOutput = {
issueHeading: createSeverityBasedIssueHeading(
vuln.metadata.severity,
vuln.metadata.type,
vuln.metadata.name
),
introducedThrough: ' Introduced through: ' + uniquePackages,
description: ' Description: ' + vuln.title,
info: ' Info: ' + chalk.underline(config.ROOT + '/vuln/' + vulnID),
fromPaths: options.showVulnPaths
? createTruncatedVulnsPathsText(vuln.list) : '',
extraInfo: vuln.note ? chalk.bold('\n Note: ' + vuln.note) : '',
remediationInfo: vuln.metadata.type !== 'license'
? createRemediationText(vuln, packageManager)
: '',
fixedIn: options.docker ? createFixedInText(vuln) : '',
};
return (
vulnOutput.issueHeading + '\n' +
vulnOutput.description + '\n' +
vulnOutput.info + '\n' +
vulnOutput.introducedThrough + '\n' +
vulnOutput.fromPaths +
// Optional - not always there
vulnOutput.remediationInfo +
vulnOutput.fixedIn +
vulnOutput.extraInfo
);
var filteredSortedGroupedVulns = sortedGroupedVulns.filter(function (vuln) {
return (vuln.metadata.packageManager !== 'upstream');
});
var binariesSortedGroupedVulns = sortedGroupedVulns.filter(function (vuln) {
return (vuln.metadata.packageManager === 'upstream');
});
var groupedVulnInfoOutput = filteredSortedGroupedVulns.map(vuln => formatIssues(vuln, options));
var groupedDockerBinariesVulnInfoOutput = (res.docker && res.docker.binariesVulns) ?
formatDockerBinariesIssues(binariesSortedGroupedVulns, res.docker.binariesVulns, options) : [];
var body =
groupedVulnInfoOutput.join('\n\n') + '\n\n\n' + binariesIssues + '\n\n' + meta + summary;
groupedVulnInfoOutput.join('\n\n') + '\n\n\n' +
groupedDockerBinariesVulnInfoOutput.join('\n\n') + '\n\n' + meta + summary;
return prefix + body + dockerAdvice + dockerSuggestion;
};

function createFixedInText(groupedVuln) {
var vulnerableRange = groupedVuln.list[0].semver.vulnerable[0];
if (/^<\S+$/.test(vulnerableRange)) {
// removing the first char from the version. For example: <7.50.1-1
return chalk.bold('\n Fixed in: ' + vulnerableRange.substr(1));
function formatDockerBinariesIssues(dockerBinariesSortedGroupedVulns, binariesVulns, options) {
const binariesIssuesOutput = [];
for (const pkgInfo of _.values(binariesVulns.affectedPkgs)) {
binariesIssuesOutput.push(createDockerBinaryHeading(pkgInfo));
let binaryIssues = dockerBinariesSortedGroupedVulns.filter(function (vuln) {
return (vuln.metadata.name === pkgInfo.pkg.name);
});
const formattedBinaryIssues = binaryIssues.map(vuln => formatIssues(vuln, options));
binariesIssuesOutput.push(formattedBinaryIssues.join('\n\n'));
}
return '';
return binariesIssuesOutput;
}

function createDockerBinaryHeading(pkgInfo) {
const binaryName = pkgInfo.pkg.name;
const binaryVersion = pkgInfo.pkg.version;
const numOfVulns = _.values(pkgInfo.issues).length;
return numOfVulns ?
chalk.bold.white(`------------ Detected ${numOfVulns} vulnerabilities`+
` for ${binaryName}@${binaryVersion} ------------`, '\n') : '';
}

function formatIssues(vuln, options) {
var vulnID = vuln.list[0].id;
var packageManager = options.packageManager;
var uniquePackages = _.uniq(
vuln.list.map(function (i) {
if (i.from[1]) {
return i.from && i.from[1];
}
return i.from;
}))
.join(', ');

var version = undefined;
var vulnerableRange = vuln.list[0].semver.vulnerable;
if (vuln.metadata.packageManager.toLowerCase() === 'upstream') {
version = vuln.metadata.version;
};

var vulnOutput = {
issueHeading: createSeverityBasedIssueHeading(
vuln.metadata.severity,
vuln.metadata.type,
vuln.metadata.name
),
introducedThrough: ' Introduced through: ' + uniquePackages,
description: ' Description: ' + vuln.title,
info: ' Info: ' + chalk.underline(config.ROOT + '/vuln/' + vulnID),
fromPaths: options.showVulnPaths
? createTruncatedVulnsPathsText(vuln.list) : '',
extraInfo: vuln.note ? chalk.bold('\n Note: ' + vuln.note) : '',
remediationInfo: vuln.metadata.type !== 'license'
? createRemediationText(vuln, packageManager)
: '',
fixedIn: options.docker ? createFixedInText(vulnerableRange, version) : '',
};
return (
vulnOutput.issueHeading + '\n' +
vulnOutput.description + '\n' +
vulnOutput.info + '\n' +
vulnOutput.introducedThrough + '\n' +
vulnOutput.fromPaths +
// Optional - not always there
vulnOutput.remediationInfo +
vulnOutput.fixedIn +
vulnOutput.extraInfo
);
}

function createFixedInText(versionRangeList, pkgVersion) {
let fixedVersion = '';
let fixedVersionCandidate = '';
const lesserThan = /^<\S+$/;
// pkgVersion is undefined for OS packages vulns
if (!pkgVersion) {
if (versionRangeList && versionRangeList.length) {
// OS packages vulns versionRangeList includes a single upper bound version
fixedVersionCandidate = versionRangeList[0];
// trim relational operator `<` from first version in list
fixedVersion = lesserThan.test(fixedVersionCandidate) ?
fixedVersionCandidate.substr(1) : '';
}
} else {
for (const versionRange of versionRangeList) {
if (!semver.valid(pkgVersion) || !semver.satisfies(pkgVersion, versionRange)) {
continue;
}
// e.g. extract '5.1.0' from version range: '>=4.1.0 <5.1.0'
fixedVersionCandidate = versionRange.split(' ')[1];
if (lesserThan.test(fixedVersionCandidate)) {
fixedVersion = fixedVersionCandidate.substr(1);
break;
}
}
}
return fixedVersion ? chalk.bold('\n Fixed in: ' + fixedVersion): '';
}

function createRemediationText(vuln, packageManager) {
Expand Down Expand Up @@ -523,34 +582,6 @@ function dockerRemediationForDisplay(res) {
return '\n\n' + out.join('\n');
}

function binariesIssuesForDisplay(res) {
const issues = [];
const dockerRes = res.docker;
if (dockerRes && dockerRes.binariesVulns) {
const binariesVulns = dockerRes.binariesVulns;
for (const pkgInfo of _.values(binariesVulns.affectedPkgs)) {
issues.push(chalk.bold.white(
`------------ Detected ${_.values(pkgInfo.issues).length} vulnerabilities`+
` for ${pkgInfo.pkg.name}@${pkgInfo.pkg.version} ------------`, '\n'));
for (const pkgIssue of _.values(pkgInfo.issues)) {
const issueID = pkgIssue.issueId;
const issueData = binariesVulns.issuesData[issueID];
const issueHeading = createSeverityBasedIssueHeading(
issueData.severity,
issueData.type,
issueData.packageName
);
issues.push(
issueHeading +
'\n Description: ' + issueData.title +
'\n Info: ' + chalk.underline(config.ROOT + '/vuln/' + issueID) + '\n'
);
}
}
}
return issues.join('\n');
}

function validateSeverityThreshold(severityThreshold) {
return SEVERITIES
.map(function (s) {
Expand Down Expand Up @@ -626,5 +657,7 @@ function metadataForVuln(vuln) {
severity: vuln.severity,
severityValue: getSeverityValue(vuln.severity),
isNew: isNewVuln(vuln),
version: vuln.version,
packageManager: vuln.packageManager,
};
}
9 changes: 7 additions & 2 deletions test/acceptance/cli.acceptance.test.ts
Expand Up @@ -1629,6 +1629,9 @@ test('`test foo:latest --docker with binaries vulnerabilities`', async (t) => {
'bzip2/libbz2-1.0': {
version: '1.0.6-8.1',
},
'bzr/libbz2-1.0': {
version: '1.0.6-8.1',
},
},
docker: {
binaries: {
Expand Down Expand Up @@ -1656,14 +1659,16 @@ test('`test foo:latest --docker with binaries vulnerabilities`', async (t) => {
t.fail('should have found vuln');
} catch (err) {
const msg = err.message;
t.match(msg, 'Tested 2 dependencies for known vulnerabilities, found 2 vulnerabilities');
t.match(msg, 'Tested 3 dependencies for known vulnerabilities, found 3 vulnerabilities');
t.match(msg, 'From: bzip2/libbz2-1.0@1.0.6-8.1');
t.match(msg, 'From: apt/libapt-pkg5.0@1.6.3ubuntu0.1 > bzip2/libbz2-1.0@1.0.6-8.1');
t.match(msg, 'Info: http://localhost:12345/vuln/SNYK-UPSTREAM-NODE-72359');
t.false(msg.includes('vulnerable paths'),
'docker should not includes number of vulnerable paths');
t.match(msg, 'Detected 1 vulnerabilities for node@5.10.1');
t.match(msg, 'Detected 2 vulnerabilities for node@5.10.1');
t.match(msg, 'High severity vulnerability found in node');
t.match(msg, 'Fixed in: 5.13.1');
t.match(msg, 'Fixed in: 5.15.1');
}
});

Expand Down

0 comments on commit bb31229

Please sign in to comment.