Skip to content

Commit

Permalink
feat: extract formatting and add types
Browse files Browse the repository at this point in the history
  • Loading branch information
lili2311 committed Dec 31, 2019
1 parent cad38e3 commit 062ff50
Show file tree
Hide file tree
Showing 9 changed files with 217 additions and 169 deletions.
38 changes: 38 additions & 0 deletions src/cli/commands/test/formatters/docker/format-docker-advice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import chalk from 'chalk';
import {
TestResult,
BaseImageRemediationAdvice,
} from '../../../../../lib/snyk-test/legacy';

export function dockerRemediationForDisplay(res: TestResult) {
if (!res.docker || !res.docker.baseImageRemediation) {
return '';
}
const { advice, message } = res.docker.baseImageRemediation;
const out = [] as any[];

if (advice) {
for (const item of advice) {
out.push(getTerminalStringFormatter(item)(item.message));
}
} else if (message) {
out.push(message);
} else {
return '';
}
return `\n\n${out.join('\n')}`;
}

function getTerminalStringFormatter({
color,
bold,
}: BaseImageRemediationAdvice) {
let formatter = chalk;
if (color && formatter[color]) {
formatter = formatter[color];
}
if (bold) {
formatter = formatter.bold;
}
return formatter;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import * as _ from 'lodash';
import chalk from 'chalk';

export function createDockerBinaryHeading(pkgInfo): string {
const binaryName = pkgInfo.pkg.name;
const binaryVersion = pkgInfo.pkg.version;
const numOfVulns = _.values(pkgInfo.issues).length;
const vulnCountText = numOfVulns > 1 ? 'vulnerabilities' : 'vulnerability';
return numOfVulns
? chalk.bold.white(
`------------ Detected ${numOfVulns} ${vulnCountText}` +
` for ${binaryName}@${binaryVersion} ------------`,
'\n',
)
: '';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import * as _ from 'lodash';
import { createDockerBinaryHeading } from './format-docker-binary-heading';
import { Options, TestOptions } from '../../../../../lib/types';
import { formatIssues } from '../legacy-format-issue';

export function formatDockerBinariesIssues(
dockerBinariesSortedGroupedVulns,
binariesVulns,
options: Options & TestOptions,
): string[] {
const binariesIssuesOutput = [] as string[];
for (const pkgInfo of _.values(binariesVulns.affectedPkgs)) {
binariesIssuesOutput.push(createDockerBinaryHeading(pkgInfo));
const binaryIssues = dockerBinariesSortedGroupedVulns.filter(
(vuln) => vuln.metadata.name === pkgInfo.pkg.name,
);
const formattedBinaryIssues = binaryIssues.map((vuln) =>
formatIssues(vuln, options),
);
binariesIssuesOutput.push(formattedBinaryIssues.join('\n\n'));
}
return binariesIssuesOutput;
}
11 changes: 11 additions & 0 deletions src/cli/commands/test/formatters/format-error-result-summary.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export function summariseErrorResults(errorResultsLength: number): string {
const projects = errorResultsLength > 1 ? 'projects' : 'project';
if (errorResultsLength > 0) {
return (
` Failed to test ${errorResultsLength} ${projects}.\n` +
'Run with `-d` for debug output and contact support@snyk.io'
);
}

return '';
}
72 changes: 72 additions & 0 deletions src/cli/commands/test/formatters/format-test-meta.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import chalk from 'chalk';
import { rightPadWithSpaces } from '../../../../lib/right-pad';
import { TestOptions, Options } from '../../../../lib/types';
import { TestResult } from '../../../../lib/snyk-test/legacy';

export function formatTestMeta(
res: TestResult,
options: Options & TestOptions,
): string {
const padToLength = 19; // chars to align
const packageManager = res.packageManager || options.packageManager;
const targetFile = res.targetFile || options.file;
const openSource = res.isPrivate ? 'no' : 'yes';
const meta = [
chalk.bold(rightPadWithSpaces('Organization: ', padToLength)) + res.org,
chalk.bold(rightPadWithSpaces('Package manager: ', padToLength)) +
packageManager,
];
if (targetFile) {
meta.push(
chalk.bold(rightPadWithSpaces('Target file: ', padToLength)) + targetFile,
);
}
if (res.projectName) {
meta.push(
chalk.bold(rightPadWithSpaces('Project name: ', padToLength)) +
res.projectName,
);
}
if (options.docker) {
meta.push(
chalk.bold(rightPadWithSpaces('Docker image: ', padToLength)) +
options.path,
);
} else {
meta.push(
chalk.bold(rightPadWithSpaces('Open source: ', padToLength)) + openSource,
);
meta.push(
chalk.bold(rightPadWithSpaces('Project path: ', padToLength)) +
options.path,
);
}
if (res.docker && res.docker.baseImage) {
meta.push(
chalk.bold(rightPadWithSpaces('Base image: ', padToLength)) +
res.docker.baseImage,
);
}

if (res.filesystemPolicy) {
meta.push(
chalk.bold(rightPadWithSpaces('Local Snyk policy: ', padToLength)) +
chalk.green('found'),
);
if (res.ignoreSettings && res.ignoreSettings.disregardFilesystemIgnores) {
meta.push(
chalk.bold(
rightPadWithSpaces('Local Snyk policy ignored: ', padToLength),
) + chalk.red('yes'),
);
}
}
if (res.licensesPolicy) {
meta.push(
chalk.bold(rightPadWithSpaces('Licenses: ', padToLength)) +
chalk.green('enabled'),
);
}

return meta.join('\n');
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { TestOptions } from '../../../../lib/types';

export function summariseVulnerableResults(
vulnerableResults,
options: TestOptions,
): string {
const vulnsLength = vulnerableResults.length;
if (vulnsLength) {
if (options.showVulnPaths) {
return `, ${vulnsLength} contained vulnerable paths.`;
}
return `, ${vulnsLength} had issues.`;
}

if (options.showVulnPaths) {
return ', no vulnerable paths were found.';
}

return ', no issues were found.';
}
174 changes: 7 additions & 167 deletions src/cli/commands/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ import {
import * as analytics from '../../../lib/analytics';
import { isFeatureFlagSupportedForOrg } from '../../../lib/feature-flags';
import { FailOnError } from '../../../lib/errors/fail-on-error.ts';
import { formatTestMeta } from './formatters/format-test-meta';
import { dockerRemediationForDisplay } from './formatters/docker/format-docker-advice';
import { summariseVulnerableResults } from './formatters/format-vulnerable-result-summary';
import { summariseErrorResults } from './formatters/format-error-result-summary';
import { formatDockerBinariesIssues } from './formatters/docker/format-docker-binary-issues';

const debug = Debug('snyk');
const SEPARATOR = '\n-------------------------------------------------------\n';
Expand Down Expand Up @@ -217,7 +222,7 @@ async function test(...args: MethodArgs): Promise<string> {
summaryMessage =
`\n\n\nTested ${results.length} ${projects}` +
summariseVulnerableResults(vulnerableResults, options) +
summariseErrorResults(errorResults) +
summariseErrorResults(errorResults.length) +
'\n';
}

Expand Down Expand Up @@ -325,36 +330,8 @@ function isVulnFixable(vuln) {
return isVulnUpgradable(vuln) || isVulnPatchable(vuln);
}

function summariseVulnerableResults(vulnerableResults, options: TestOptions) {
const vulnsLength = vulnerableResults.length;
if (vulnsLength) {
if (options.showVulnPaths) {
return `, ${vulnsLength} contained vulnerable paths.`;
}
return `, ${vulnsLength} had issues.`;
}

if (options.showVulnPaths) {
return ', no vulnerable paths were found.';
}

return ', no issues were found.';
}

function summariseErrorResults(errorResults) {
const projects = errorResults.length > 1 ? 'projects' : 'project';
if (errorResults.length > 0) {
return (
` Failed to test ${errorResults.length} ${projects}.\n` +
'Run with `-d` for debug output and contact support@snyk.io'
);
}

return '';
}

function displayResult(res: TestResult, options: Options & TestOptions) {
const meta = metaForDisplay(res, options);
const meta = formatTestMeta(res, options);
const dockerAdvice = dockerRemediationForDisplay(res);
const packageManager =
(res.packageManager as SupportedPackageManagers) || options.packageManager;
Expand Down Expand Up @@ -530,143 +507,6 @@ function displayResult(res: TestResult, options: Options & TestOptions) {
);
}

function formatDockerBinariesIssues(
dockerBinariesSortedGroupedVulns,
binariesVulns,
options: Options & TestOptions,
) {
const binariesIssuesOutput = [] as string[];
for (const pkgInfo of _.values(binariesVulns.affectedPkgs)) {
binariesIssuesOutput.push(createDockerBinaryHeading(pkgInfo));
const binaryIssues = dockerBinariesSortedGroupedVulns.filter(
(vuln) => vuln.metadata.name === pkgInfo.pkg.name,
);
const formattedBinaryIssues = binaryIssues.map((vuln) =>
formatIssues(vuln, options),
);
binariesIssuesOutput.push(formattedBinaryIssues.join('\n\n'));
}
return binariesIssuesOutput;
}

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

function rightPadWithSpaces(s, desiredLength) {
const padLength = desiredLength - s.length;
if (padLength <= 0) {
return s;
}

return s + ' '.repeat(padLength);
}

function metaForDisplay(res, options) {
const padToLength = 19; // chars to align
const packageManager = res.packageManager || options.packageManager;
const targetFile = res.targetFile || options.file;
const openSource = res.isPrivate ? 'no' : 'yes';
const meta = [
chalk.bold(rightPadWithSpaces('Organization: ', padToLength)) + res.org,
chalk.bold(rightPadWithSpaces('Package manager: ', padToLength)) +
packageManager,
];
if (targetFile) {
meta.push(
chalk.bold(rightPadWithSpaces('Target file: ', padToLength)) + targetFile,
);
}
if (res.projectName) {
meta.push(
chalk.bold(rightPadWithSpaces('Project name: ', padToLength)) +
res.projectName,
);
}
if (options.docker) {
meta.push(
chalk.bold(rightPadWithSpaces('Docker image: ', padToLength)) +
options.path,
);
} else {
meta.push(
chalk.bold(rightPadWithSpaces('Open source: ', padToLength)) + openSource,
);
meta.push(
chalk.bold(rightPadWithSpaces('Project path: ', padToLength)) +
options.path,
);
}
if (res.docker && res.docker.baseImage) {
meta.push(
chalk.bold(rightPadWithSpaces('Base image: ', padToLength)) +
res.docker.baseImage,
);
}

if (res.filesystemPolicy) {
meta.push(
chalk.bold(rightPadWithSpaces('Local Snyk policy: ', padToLength)) +
chalk.green('found'),
);
if (res.ignoreSettings && res.ignoreSettings.disregardFilesystemIgnores) {
meta.push(
chalk.bold(
rightPadWithSpaces('Local Snyk policy ignored: ', padToLength),
) + chalk.red('yes'),
);
}
}
if (res.licensesPolicy) {
meta.push(
chalk.bold(rightPadWithSpaces('Licenses: ', padToLength)) +
chalk.green('enabled'),
);
}

return meta.join('\n');
}

function dockerRemediationForDisplay(res) {
if (!res.docker || !res.docker.baseImageRemediation) {
return '';
}
const { advice, message } = res.docker.baseImageRemediation;
const out = [] as any[];

if (advice) {
for (const item of advice) {
out.push(getTerminalStringFormatter(item)(item.message));
}
} else if (message) {
out.push(message);
} else {
return '';
}
return `\n\n${out.join('\n')}`;
}

function getTerminalStringFormatter({ color, bold }) {
let formatter = chalk;
if (color && formatter[color]) {
formatter = formatter[color];
}
if (bold) {
formatter = formatter.bold;
}
return formatter;
}

function validateSeverityThreshold(severityThreshold) {
return SEVERITIES.map((s) => s.verboseName).indexOf(severityThreshold) > -1;
}
Expand Down
8 changes: 8 additions & 0 deletions src/lib/right-pad.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export function rightPadWithSpaces(s: string, padding: number) {
const padLength = padding - s.length;
if (padLength <= 0) {
return s;
}

return s + ' '.repeat(padLength);
}

0 comments on commit 062ff50

Please sign in to comment.