Skip to content

Commit

Permalink
feat: adding manifest file to vuln card if scanning multi-project
Browse files Browse the repository at this point in the history
  • Loading branch information
dotkas committed Dec 5, 2023
1 parent 6a578dc commit 140a2a9
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 37 deletions.
98 changes: 62 additions & 36 deletions src/lib/snyk-to-html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@ import * as isEmpty from 'lodash.isempty';
import * as orderBy from 'lodash.orderby';
import chalk from 'chalk';
import * as debugModule from 'debug';
import { addIssueDataToPatch, getUpgrades, IacProjectType, severityMap } from './vuln';
import { processSourceCode, } from './codeutil';
import fs = require('fs');
import Handlebars = require('handlebars');
import marked = require('marked');
import moment = require('moment');
import path = require('path');
import { addIssueDataToPatch, getUpgrades, severityMap, IacProjectType } from './vuln';
import {
processSourceCode,
} from './codeutil';

const debug = debugModule('snyk-to-html');

Expand All @@ -31,8 +29,8 @@ function readFile(filePath: string, encoding: string): Promise<string> {

function handleInvalidJson(reason: any) {
if (reason.isInvalidJson) {
reason.message = reason.message + 'Error running `snyk-to-html`. Please check you are providing the correct parameters. ' +
'Is the issue persists contact [email protected]';
reason.message = reason.message + 'Error running `snyk-to-html`. Please check you are providing the correct parameters. ' +
'Is the issue persists contact [email protected]';
}
console.log(reason.message);
}
Expand All @@ -43,7 +41,7 @@ function promisedParseJSON(json) {
resolve(JSON.parse(json));
} catch (error) {
error.message = chalk.red.bold('The source provided is not a valid json! Please validate that the input provided to the CLI is an actual JSON\n\n' +
'Tip: To find more information, try running `snyk-to-html` in debug mode by appending to the CLI the `-d` parameter\n\n');
'Tip: To find more information, try running `snyk-to-html` in debug mode by appending to the CLI the `-d` parameter\n\n');
debug(`Input provided to the CLI: \n${json}\n\n`);
error.isInvalidJson = true;
reject(error);
Expand Down Expand Up @@ -99,7 +97,7 @@ class SnykToHtml {
export { SnykToHtml };

function metadataForVuln(vuln: any) {
let {cveSpaced, cveLineBreaks} = concatenateCVEs(vuln)
let { cveSpaced, cveLineBreaks } = concatenateCVEs(vuln)

return {
id: vuln.id,
Expand All @@ -126,18 +124,18 @@ function concatenateCVEs(vuln: any) {
let cveLineBreaks = ''

if (vuln.identifiers) {
vuln.identifiers.CVE.forEach(function(c) {
vuln.identifiers.CVE.forEach(function (c) {
let cveLink = `<a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=${c}">${c}</a>`
cveSpaced += `${cveLink}&nbsp;`
cveLineBreaks += `${cveLink}</br>`
})
}

return {cveSpaced, cveLineBreaks}
return { cveSpaced, cveLineBreaks }
}

function dateFromDateTimeString(dateTimeString: string) {
return dateTimeString.substr(0,10);
return dateTimeString.substr(0, 10);
}

function groupVulns(vulns) {
Expand All @@ -148,7 +146,7 @@ function groupVulns(vulns) {
if (vulns && Array.isArray(vulns)) {
vulns.map(vuln => {
if (!result[vuln.id]) {
result[vuln.id] = {list: [vuln], metadata: metadataForVuln(vuln)};
result[vuln.id] = { list: [vuln], metadata: metadataForVuln(vuln) };
pathsCount++;
uniqueCount++;
} else {
Expand Down Expand Up @@ -180,12 +178,12 @@ async function generateTemplate(data: any,
template: string,
showRemediation: boolean,
summary: boolean):
Promise<string> {
Promise<string> {
if (showRemediation && data.remediation) {
data.showRemediations = showRemediation;
const {upgrade, pin, unresolved, patch} = data.remediation;
const { upgrade, pin, unresolved, patch } = data.remediation;
data.anyRemediations = !isEmpty(upgrade) ||
!isEmpty(patch) || !isEmpty(pin);
!isEmpty(patch) || !isEmpty(pin);
data.anyUnresolved = !!unresolved?.vulnerabilities;
data.unresolved = groupVulns(unresolved);
data.upgrades = getUpgrades(upgrade, data.vulnerabilities);
Expand All @@ -206,7 +204,7 @@ async function generateTemplate(data: any,
data.uniqueCount = vulnMetadata.vulnerabilitiesUniqueCount;
data.summary = vulnMetadata.vulnerabilitiesPathsCount + ' vulnerable dependency paths';
data.showSummaryOnly = summary;
if(data.paths?.length === 1){
if (data.paths?.length === 1) {
data.packageManager = data.paths[0].packageManager;
}

Expand Down Expand Up @@ -256,15 +254,34 @@ async function generateCodeTemplate(
}

function mergeData(dataArray: any[]): any {
const vulnsArrays = dataArray.map(project => project.vulnerabilities || []);
const vulnsArrays = dataArray.map((project) => {
if (!project.vulnerabilities) {
return [];
}

// Add project data to each of the vulnerabilities to display more
// details on each vulnerability card, in order to properly distinguish
// from which project a vuln is connected, in case of displaying multiple
// projects.
const vulns = project.vulnerabilities.map((vuln) => ({
...vuln,
displayTargetFile: project.displayTargetFile,
path: project.path
}));
return vulns;
});
const aggregateVulnerabilities = [].concat(...vulnsArrays);

const totalUniqueCount =
dataArray.reduce((acc, item) => acc + item.vulnerabilities.length || 0, 0);
const totalDepCount =
dataArray.reduce((acc, item) => acc + item.dependencyCount || 0, 0);

const paths = dataArray.map(project => ({ path: project.path, packageManager: project.packageManager }));
const paths = dataArray.map(project => ({
path: project.path,
packageManager: project.packageManager,
displayTargetFile: project.displayTargetFile,
}));

return {
vulnerabilities: aggregateVulnerabilities,
Expand All @@ -285,7 +302,7 @@ async function processIacData(data: any, template: string, summary: boolean): Pr
return generateIacTemplate(data, template);
}

const dataArray = Array.isArray(data)? data : [data];
const dataArray = Array.isArray(data) ? data : [data];
dataArray.forEach(project => {
project.infrastructureAsCodeIssues.forEach(issue => {
issue.severityValue = severityMap[issue.severity];
Expand Down Expand Up @@ -325,7 +342,7 @@ async function processCodeData(
const dataArray = Array.isArray(data) ? data : [data];

const OrderedIssuesArray = await processSourceCode(dataArray);

const totalIssues = dataArray[0].runs[0].results.length;
const processedData = {
projects: OrderedIssuesArray,
Expand Down Expand Up @@ -368,34 +385,43 @@ const hh = {
// block helpers
/* tslint:disable:only-arrow-functions */
/* tslint:disable:object-literal-shorthand */
isDoubleArray: function(data, options) {
isDoubleArray: function (data, options) {
return Array.isArray(data[0]) ? options.fn(data) : options.inverse(data);
},
if_eq: function(this: void, a, b, opts) {
if_eq: function (this: void, a, b, opts) {
return (a === b) ? opts.fn(this) : opts.inverse(this);
},
if_gt: function(this: void, a, b, opts) {
if_gt: function (this: void, a, b, opts) {
return (a > b) ? opts.fn(this) : opts.inverse(this);
},
if_not_eq: function(this: void, a, b, opts) {
if_not_eq: function (this: void, a, b, opts) {
return (a !== b) ? opts.fn(this) : opts.inverse(this);
},
if_any: function(this: void, opts, ...args) {
if_any: function (this: void, opts, ...args) {
return args.some(v => !!v) ? opts.fn(this) : opts.inverse(this);
},
ifCond: function(this: void, v1, operator, v2, options) {
ifCond: function (this: void, v1, operator, v2, options) {
const choose = (pred: boolean) => pred ? options.fn(this) : options.inverse(this);
switch (operator) {
// tslint:disable-next-line:triple-equals
case '==': return choose(v1 == v2);
case '===': return choose(v1 === v2);
case '<': return choose(v1 < v2);
case '<=': return choose(v1 <= v2);
case '>': return choose(v1 > v2);
case '>=': return choose(v1 >= v2);
case '&&': return choose(v1 && v2);
case '||': return choose(v1 || v2);
default: return choose(false);
case '==':
return choose(v1 == v2);
case '===':
return choose(v1 === v2);
case '<':
return choose(v1 < v2);
case '<=':
return choose(v1 <= v2);
case '>':
return choose(v1 > v2);
case '>=':
return choose(v1 >= v2);
case '&&':
return choose(v1 && v2);
case '||':
return choose(v1 || v2);
default:
return choose(false);
}
},
getRemediation: (description, fixedIn) => {
Expand All @@ -416,7 +442,7 @@ const hh = {
severityLabel: (severity: string) => {
return severity[0].toUpperCase();
},
startsWith: function(str, start, options) {
startsWith: function (str, start, options) {
return str.startsWith(start) ? options.fn(this) : options.inverse(this);
},
};
Expand Down
12 changes: 11 additions & 1 deletion template/test-report.header.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,25 @@
<div class="source-panel">
<span>Scanned the following paths:</span>
<ul>
{{#each paths}}<li class="paths">{{path}} ({{packageManager}})</li>{{/each}}
{{#each paths}}
{{#if_not_eq packageManager "nuget"}}
<li class="paths">{{path}} ({{packageManager}})</li>
{{else}}
<li class="paths">{{path}}/{{displayTargetFile}} ({{packageManager}})</li>
{{/if_not_eq}}
{{/each}}
</ul>
</div>
{{/if}}
{{#if path}}
<div class="source-panel">
<span>Scanned the following path:</span>
<ul>
{{#if_not_eq packageManager "nuget"}}
<li class="paths">{{path}} ({{packageManager}})</li>
{{else}}
<li class="paths">{{path}}/{{dislpayTargetFile}} ({{packageManager}})</li>
{{/if_not_eq}}
</ul>
</div>
{{/if}}
Expand Down
5 changes: 5 additions & 0 deletions template/test-report.vuln-card.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
<hr/>

<ul class="card__meta">
{{#if list.[0].displayTargetFile }}
<li class="card__meta__item">
Manifest file: {{list.[0].path}} <span class="list-paths__item__arrow">›</span> {{list.[0].displayTargetFile}}
</li>
{{/if}}
<li class="card__meta__item">
Package Manager: {{metadata.packageManager}}
</li>
Expand Down

0 comments on commit 140a2a9

Please sign in to comment.