From 2080fa0cd1e2b860c4730aa65cd94c3af1faf571 Mon Sep 17 00:00:00 2001 From: Kirill Efimov Date: Sun, 30 Dec 2018 18:14:43 +0200 Subject: [PATCH] refactor: test command rewritten to TS --- src/cli/commands/{test.js => test.ts} | 471 ++++++++++++-------------- 1 file changed, 221 insertions(+), 250 deletions(-) rename src/cli/commands/{test.js => test.ts} (55%) diff --git a/src/cli/commands/test.js b/src/cli/commands/test.ts similarity index 55% rename from src/cli/commands/test.js rename to src/cli/commands/test.ts index daadc15ad6..773f10cb97 100644 --- a/src/cli/commands/test.js +++ b/src/cli/commands/test.ts @@ -1,26 +1,27 @@ module.exports = test; -var _ = require('lodash'); -var semver = require('semver'); -var chalk = require('chalk'); -var debug = require('debug')('snyk'); -var snyk = require('../../lib/'); -var config = require('../../lib/config'); -var isCI = require('../../lib/is-ci'); -var apiTokenExists = require('../../lib/api-token').exists; -var SEVERITIES = require('../../lib/snyk-test/common').SEVERITIES; -var WIZARD_SUPPORTED_PMS = - require('../../lib/snyk-test/common').WIZARD_SUPPORTED_PMS; -var docker = require('../../lib/docker-promotion'); -var SEPARATOR = '\n-------------------------------------------------------\n'; +import * as _ from 'lodash'; +import * as semver from 'semver'; +import chalk from 'chalk'; +import * as snyk from '../../lib/'; +import * as config from '../../lib/config'; +import * as isCI from '../../lib/is-ci'; +import {exists as apiTokenExists} from '../../lib/api-token'; +import {SEVERITIES, WIZARD_SUPPORTED_PMS} from '../../lib/snyk-test/common'; +import * as docker from '../../lib/docker-promotion'; +import * as Debug from 'debug'; + +const debug = Debug('snyk'); +const SEPARATOR = '\n-------------------------------------------------------\n'; + +// TODO: avoid using `as any` whenever it's possible // arguments array is 0 or more `path` strings followed by // an optional `option` object -function test() { - var args = [].slice.call(arguments, 0); - var options = {}; - var results = []; - var resultOptions = []; +async function test(...args) { + const resultOptions = [] as any[]; + let results = [] as any[]; + let options = {} as any; if (typeof args[args.length - 1] === 'object') { options = args.pop(); @@ -42,137 +43,124 @@ function test() { return Promise.reject(new Error('INVALID_SEVERITY_THRESHOLD')); } - return apiTokenExists('snyk test') - .then(function () { - // Promise waterfall to test all other paths sequentially - var testsPromises = args.reduce(function (acc, path) { - return acc.then(function () { - // Create a copy of the options so a specific test can - // modify them i.e. add `options.file` etc. We'll need - // these options later. - var testOpts = _.cloneDeep(options); - testOpts.path = path; - resultOptions.push(testOpts); - - // run the actual test - return snyk.test(path, testOpts) - .catch(function (error) { - // Possible error cases: - // - the test found some vulns. `error.message` is a - // JSON-stringified - // test result. - // - the flow failed, `error` is a real Error object. - // - the flow failed, `error` is a number or string - // describing the problem. - // - // To standardise this, make sure we use the best _object_ to - // describe the error. - if (error instanceof Error) { - return error; - } - - if (typeof error !== 'object') { - return new Error(error); - } - - try { - return JSON.parse(error.message); - } catch (unused) { - return error; - } - }) - .then(function (res) { - // add the tested path to the result of the test (or error) - results.push(_.assign(res, {path: path})); - }); - }); - }, Promise.resolve()); - - return testsPromises; - }).then(function () { - // resultOptions is now an array of 1 or more options used for - // the tests results is now an array of 1 or more test results - // values depend on `options.json` value - string or object - if (options.json) { - results = results.map(function (result) { - // add json for when thrown exception - if (result instanceof Error) { - return { - ok: false, - error: result.message, - path: result.path, - }; - } - return result; - }); - - // backwards compat - strip array IFF only one result - var dataToSend = results.length === 1 ? results[0] : results; - var json = JSON.stringify(dataToSend, '', 2); - - if (results.every(function (res) { - return res.ok; - })) { - return json; + await apiTokenExists('snyk test'); + + // Promise waterfall to test all other paths sequentially + for (const path of args) { + // Create a copy of the options so a specific test can + // modify them i.e. add `options.file` etc. We'll need + // these options later. + const testOpts = _.cloneDeep(options); + testOpts.path = path; + resultOptions.push(testOpts); + + let res; + + try { + res = await snyk.test(path, testOpts); + } catch (error) { + // Possible error cases: + // - the test found some vulns. `error.message` is a + // JSON-stringified + // test result. + // - the flow failed, `error` is a real Error object. + // - the flow failed, `error` is a number or string + // describing the problem. + // + // To standardise this, make sure we use the best _object_ to + // describe the error. + if (error instanceof Error) { + res = error; + } else if (typeof error !== 'object') { + res = new Error(error); + } else { + try { + res = JSON.parse(error.message); + } catch (unused) { + res = error; } - - throw new Error(json); } + } + + // add the tested path to the result of the test (or error) + results.push(_.assign(res, {path})); + } - var response = results.map(function (unused, i) { - return displayResult(results[i], resultOptions[i]); - }).join('\n' + SEPARATOR); - - var vulnerableResults = results.filter(res => { - return res.vulnerabilities && res.vulnerabilities.length; - }); - var errorResults = results.filter(function (res) { - return res instanceof Error; - }); - - if (errorResults.length > 0) { - debug('Failed to test ' + errorResults.length + ' projects, errors:'); - errorResults.forEach(function (err) { - var errString = err.stack ? err.stack.toString() : err.toString(); - debug('error: %s', errString); - }); + // resultOptions is now an array of 1 or more options used for + // the tests results is now an array of 1 or more test results + // values depend on `options.json` value - string or object + if (options.json) { + results = results.map((result) => { + // add json for when thrown exception + if (result instanceof Error) { + return { + ok: false, + error: result.message, + path: (result as any).path, + }; } + return result; + }); - var summaryMessage = ''; + // backwards compat - strip array IFF only one result + const dataToSend = results.length === 1 ? results[0] : results; + const json = JSON.stringify(dataToSend, null, 2); - if (results.length > 1) { - var projects = results.length === 1 ? ' project' : ' projects'; - summaryMessage = '\n\n' + '\nTested ' + results.length + projects + - summariseVulnerableResults(vulnerableResults, options) + - summariseErrorResults(errorResults) + '\n'; - } + if (results.every((res) => res.ok)) { + return json; + } - var notSuccess = vulnerableResults.length > 0 || errorResults.length > 0; - - if (notSuccess) { - response += chalk.bold.red(summaryMessage); - const error = new Error(response); - // take the code of the first problem to go through error - // translation - // HACK as there can be different errors, and we pass only the - // first one - error.code = (vulnerableResults[0] || errorResults[0]).code; - error.userMessage = (vulnerableResults[0] || errorResults[0]).userMessage; - throw error; - } + throw new Error(json); + } + + let response = results.map((unused, i) => displayResult(results[i], resultOptions[i])) + .join(`\n${SEPARATOR}`); - response += chalk.bold.green(summaryMessage); - return response; + const vulnerableResults = results.filter((res) => res.vulnerabilities && res.vulnerabilities.length); + const errorResults = results.filter((res) => res instanceof Error); + + if (errorResults.length > 0) { + debug(`Failed to test ${errorResults.length} projects, errors:`); + errorResults.forEach((err) => { + const errString = err.stack ? err.stack.toString() : err.toString(); + debug('error: %s', errString); }); + } + + let summaryMessage = ''; + + if (results.length > 1) { + const projects = results.length === 1 ? 'project' : 'projects'; + summaryMessage = `\n\n\nTested ${results.length} ${projects}` + + summariseVulnerableResults(vulnerableResults, options) + + summariseErrorResults(errorResults) + '\n'; + } + + const notSuccess = vulnerableResults.length > 0 || errorResults.length > 0; + + if (notSuccess) { + response += chalk.bold.red(summaryMessage); + const error = new Error(response) as any; + // take the code of the first problem to go through error + // translation + // HACK as there can be different errors, and we pass only the + // first one + error.code = (vulnerableResults[0] || errorResults[0]).code; + error.userMessage = (vulnerableResults[0] || errorResults[0]).userMessage; + throw error; + } + + response += chalk.bold.green(summaryMessage); + return response; } function summariseVulnerableResults(vulnerableResults, options) { - var vulnsLength = vulnerableResults.length; + const vulnsLength = vulnerableResults.length; if (vulnsLength) { if (options.showVulnPaths) { - return ', ' + vulnsLength + ' contained vulnerable paths.'; + return `, ${vulnsLength} contained vulnerable paths.`; } - return ', ' + vulnsLength + ' had issues.'; + return `, ${vulnsLength} had issues.`; } if (options.showVulnPaths) { @@ -183,36 +171,34 @@ function summariseVulnerableResults(vulnerableResults, options) { } function summariseErrorResults(errorResults) { - const projects = - errorResults.length > 1 ? ' projects' : ' project'; + const projects = errorResults.length > 1 ? 'projects' : 'project'; if (errorResults.length > 0) { - return ' Failed to test ' + errorResults.length + projects + - '.\nRun with `-d` for debug output and contact support@snyk.io'; + return ` Failed to test ${errorResults.length} ${projects}.\n` + + 'Run with `-d` for debug output and contact support@snyk.io'; } return ''; } function displayResult(res, options) { - var meta = metaForDisplay(res, options) + '\n\n'; - var dockerAdvice = dockerRemediationForDisplay(res); - var packageManager = options.packageManager; - var prefix = chalk.bold.white('\nTesting ' + options.path + '...\n\n'); + const meta = metaForDisplay(res, options) + '\n\n'; + const dockerAdvice = dockerRemediationForDisplay(res); + const packageManager = options.packageManager; + const prefix = chalk.bold.white('\nTesting ' + options.path + '...\n\n'); // handle errors by extracting their message if (res instanceof Error) { return prefix + res.message; } - var issuesText = res.licensesPolicy ? 'issues' : 'vulnerabilities'; - var pathOrDepsText = ''; + const issuesText = res.licensesPolicy ? 'issues' : 'vulnerabilities'; + let pathOrDepsText = ''; if (res.hasOwnProperty('dependencyCount')) { pathOrDepsText += res.dependencyCount + ' dependencies'; } else { pathOrDepsText += options.path; } - var testedInfoText = - 'Tested ' + pathOrDepsText + ' for known ' + issuesText; + const testedInfoText = `Tested ${pathOrDepsText} for known ${issuesText}`; let dockerSuggestion = ''; if (docker.shouldSuggestDocker(options)) { @@ -221,13 +207,11 @@ function displayResult(res, options) { // OK => no vulns found, return if (res.ok && res.vulnerabilities.length === 0) { - var vulnPathsText = options.showVulnPaths ? + const vulnPathsText = options.showVulnPaths ? ', no vulnerable paths found.' : ', none were found.'; - var summaryOKText = chalk.green( - '✓ ' + testedInfoText + vulnPathsText - ); - var nextStepsText = + const summaryOKText = chalk.green(`✓ ${testedInfoText} ${vulnPathsText}`); + const nextStepsText = '\n\nNext steps:' + '\n- Run `snyk monitor` to be notified ' + 'about new related vulnerabilities.' + @@ -243,18 +227,18 @@ function displayResult(res, options) { } // NOT OK => We found some vulns, let's format the vulns info - var vulnCount = res.vulnerabilities && res.vulnerabilities.length; - var singleVulnText = res.licensesPolicy ? 'issue' : 'vulnerability'; - var multipleVulnsText = res.licensesPolicy ? 'issues' : 'vulnerabilities'; + const vulnCount = res.vulnerabilities && res.vulnerabilities.length; + const singleVulnText = res.licensesPolicy ? 'issue' : 'vulnerability'; + const multipleVulnsText = res.licensesPolicy ? 'issues' : 'vulnerabilities'; // Text will look like so: // 'found 232 vulnerabilities, 404 vulnerable paths.' - var vulnCountText = 'found ' + res.uniqueCount + ' ' + let vulnCountText = `found ${res.uniqueCount} ` + (res.uniqueCount === 1 ? singleVulnText : multipleVulnsText); // Docker is currently not supported as num of paths is inaccurate due to trimming of paths to reduce size. if (options.showVulnPaths && !options.docker) { - vulnCountText += ', ' + vulnCount + ' vulnerable '; + vulnCountText += `, ${vulnCount} vulnerable `; if (vulnCount === 1) { vulnCountText += 'path.'; @@ -264,12 +248,10 @@ function displayResult(res, options) { } else { vulnCountText += '.'; } - var summary = testedInfoText + ', ' + chalk.red.bold(vulnCountText); + let summary = testedInfoText + ', ' + chalk.red.bold(vulnCountText); if (WIZARD_SUPPORTED_PMS.indexOf(packageManager) > -1) { - summary += chalk.bold.green( - '\n\nRun `snyk wizard` to address these issues.' - ); + summary += chalk.bold.green('\n\nRun `snyk wizard` to address these issues.'); } if (options.docker && @@ -280,36 +262,35 @@ function displayResult(res, options) { '\n\nTo remove this message in the future, please run `snyk config set disableSuggestions=true`'); } - var vulns = res.vulnerabilities || []; - var groupedVulns = groupVulnerabilities(vulns); - var sortedGroupedVulns = _.orderBy( + const vulns = res.vulnerabilities || []; + const groupedVulns = groupVulnerabilities(vulns); + const sortedGroupedVulns = _.orderBy( groupedVulns, ['metadata.severityValue', 'metadata.name'], - ['asc', 'desc'] + ['asc', 'desc'], ); - 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) ? + const filteredSortedGroupedVulns = sortedGroupedVulns + .filter((vuln) => (vuln.metadata.packageManager !== 'upstream')); + const binariesSortedGroupedVulns = sortedGroupedVulns + .filter((vuln) => (vuln.metadata.packageManager === 'upstream')); + const groupedVulnInfoOutput = filteredSortedGroupedVulns.map((vuln) => formatIssues(vuln, options)); + const groupedDockerBinariesVulnInfoOutput = (res.docker && res.docker.binariesVulns) ? formatDockerBinariesIssues(binariesSortedGroupedVulns, res.docker.binariesVulns, options) : []; - var body = + + const body = groupedVulnInfoOutput.join('\n\n') + '\n\n\n' + groupedDockerBinariesVulnInfoOutput.join('\n\n') + '\n\n' + meta + summary; + return prefix + body + dockerAdvice + dockerSuggestion; -}; +} function formatDockerBinariesIssues(dockerBinariesSortedGroupedVulns, binariesVulns, options) { - const binariesIssuesOutput = []; + const binariesIssuesOutput = [] as string[]; 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)); + 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; @@ -320,15 +301,15 @@ function createDockerBinaryHeading(pkgInfo) { const binaryVersion = pkgInfo.pkg.version; const numOfVulns = _.values(pkgInfo.issues).length; return numOfVulns ? - chalk.bold.white(`------------ Detected ${numOfVulns} vulnerabilities`+ - ` for ${binaryName}@${binaryVersion} ------------`, '\n') : ''; + 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) { + const vulnID = vuln.list[0].id; + const packageManager = options.packageManager; + const uniquePackages = _.uniq( + vuln.list.map((i) => { if (i.from[1]) { return i.from && i.from[1]; } @@ -336,17 +317,18 @@ function formatIssues(vuln, options) { })) .join(', '); - var version = undefined; - var vulnerableRange = vuln.list[0].semver.vulnerable; + let version; + const vulnerableRange = vuln.list[0].semver.vulnerable; if (vuln.metadata.packageManager.toLowerCase() === 'upstream') { version = vuln.metadata.version; - }; + } - var vulnOutput = { + const vulnOutput = { issueHeading: createSeverityBasedIssueHeading( vuln.metadata.severity, vuln.metadata.type, - vuln.metadata.name + vuln.metadata.name, + false, ), introducedThrough: ' Introduced through: ' + uniquePackages, description: ' Description: ' + vuln.title, @@ -362,10 +344,10 @@ function formatIssues(vuln, options) { }; return ( - vulnOutput.issueHeading + '\n' + - vulnOutput.description + '\n' + - vulnOutput.info + '\n' + - vulnOutput.introducedThrough + '\n' + + `${vulnOutput.issueHeading}\n` + + `${vulnOutput.description}\n` + + `${vulnOutput.info}\n` + + `${vulnOutput.introducedThrough}\n` + vulnOutput.fromPaths + // Optional - not always there vulnOutput.remediationInfo + @@ -413,18 +395,18 @@ function createFixedInText(versionRangeList, pkgVersion) { } } } - return fixedVersion ? chalk.bold('\n Fixed in: ' + fixedVersion): ''; + return fixedVersion ? chalk.bold('\n Fixed in: ' + fixedVersion) : ''; } function createRemediationText(vuln, packageManager) { - var packageName = vuln.metadata.name; - var wizardHintText = ''; + const packageName = vuln.metadata.name; + let wizardHintText = ''; if (WIZARD_SUPPORTED_PMS.indexOf(packageManager) > -1) { wizardHintText = 'Run `snyk wizard` to explore remediation options.'; } if (vuln.isOutdated === true) { - var packageManagerOutdatedText = { + const packageManagerOutdatedText = { npm: '\n Try deleting node_modules, reinstalling ' + 'and running `snyk test` again. If the problem persists, ' + 'one of your dependencies may be bundling outdated modules.', @@ -443,37 +425,34 @@ function createRemediationText(vuln, packageManager) { } if (vuln.isFixable === true) { - var upgradePathsArray = _.uniq(vuln.list.map(function (v) { - var shouldUpgradeItself = !!v.upgradePath[0]; - var shouldUpgradeDirectDep = !!v.upgradePath[1]; + const upgradePathsArray = _.uniq(vuln.list.map((v) => { + const shouldUpgradeItself = !!v.upgradePath[0]; + const shouldUpgradeDirectDep = !!v.upgradePath[1]; if (shouldUpgradeItself) { // If we are testing a library/package like express // Then we can suggest they get the latest version // Example command: snyk test express@3 - var selfUpgradeInfo = (v.upgradePath.length > 0) - ? ' (triggers upgrades to ' + v.upgradePath.join(' > ') + ')' + const selfUpgradeInfo = (v.upgradePath.length > 0) + ? ` (triggers upgrades to ${ v.upgradePath.join(' > ')})` : ''; - var testedPackageName = v.upgradePath[0].split('@'); - return 'You\'ve tested an outdated version of ' + - testedPackageName[0] + '. Upgrade to ' + - v.upgradePath[0] + selfUpgradeInfo; + const testedPackageName = v.upgradePath[0].split('@'); + return `You've tested an outdated version of ${testedPackageName[0]}.` + + + ` Upgrade to ${v.upgradePath[0]}${selfUpgradeInfo}`; } if (shouldUpgradeDirectDep) { - var formattedUpgradePath = v.upgradePath.slice(1).join(' > '); - var upgradeTextInfo = (v.upgradePath.length) - ? ' (triggers upgrades to ' + formattedUpgradePath + ')' + const formattedUpgradePath = v.upgradePath.slice(1).join(' > '); + const upgradeTextInfo = (v.upgradePath.length) + ? ` (triggers upgrades to ${formattedUpgradePath})` : ''; - return 'Upgrade direct dependency ' + v.from[1] + ' to ' + - v.upgradePath[1] + upgradeTextInfo; + return `Upgrade direct dependency ${v.from[1]} to ${v.upgradePath[1]}${upgradeTextInfo}`; } return 'Some paths have no direct dependency upgrade that' + - ' can address this issue. ' + wizardHintText; + ` can address this issue. ${wizardHintText}`; })); - return chalk.bold('\n Remediation: \n ' + - upgradePathsArray.join('\n ')); + return chalk.bold(`\n Remediation: \n ${upgradePathsArray.join('\n ')}`); } return ''; @@ -481,20 +460,20 @@ function createRemediationText(vuln, packageManager) { function createSeverityBasedIssueHeading(severity, type, packageName, isNew) { // Example: ✗ Medium severity vulnerability found in xmldom - var vulnTypeText = type === 'license' ? 'issue' : 'vulnerability'; - var severitiesColourMapping = { + const vulnTypeText = type === 'license' ? 'issue' : 'vulnerability'; + const severitiesColourMapping = { low: { - colorFunc: function (text) { + colorFunc(text) { return chalk.bold.blue(text); }, }, medium: { - colorFunc: function (text) { + colorFunc(text) { return chalk.bold.yellow(text); }, }, high: { - colorFunc: function (text) { + colorFunc(text) { return chalk.bold.red(text); }, }, @@ -506,13 +485,11 @@ function createSeverityBasedIssueHeading(severity, type, packageName, isNew) { } function createTruncatedVulnsPathsText(vulnList) { - var numberOfPathsToDisplay = 3; - var fromPathsArray = vulnList.map(function (i) { - return i.from; - }); + const numberOfPathsToDisplay = 3; + const fromPathsArray = vulnList.map((i) => i.from); - var formatedFromPathsArray = fromPathsArray.map(function (i) { - var fromWithoutBaseProject = i.slice(1); + const formatedFromPathsArray = fromPathsArray.map((i) => { + const fromWithoutBaseProject = i.slice(1); // If more than one From path if (fromWithoutBaseProject.length) { return i.slice(1).join(' > '); @@ -521,21 +498,20 @@ function createTruncatedVulnsPathsText(vulnList) { return i; }); - var notShownPathsNumber = fromPathsArray.length - numberOfPathsToDisplay; - var shouldTruncatePaths = fromPathsArray.length > 3; - var truncatedText = '\n and ' + notShownPathsNumber + ' more...'; - var formattedPathsText = formatedFromPathsArray + const notShownPathsNumber = fromPathsArray.length - numberOfPathsToDisplay; + const shouldTruncatePaths = fromPathsArray.length > 3; + const truncatedText = `\n and ${notShownPathsNumber} more...`; + const formattedPathsText = formatedFromPathsArray .slice(0, numberOfPathsToDisplay) .join('\n From: '); if (fromPathsArray.length > 0) { - return ' From: ' + formattedPathsText + - (shouldTruncatePaths ? truncatedText : ''); + return ' From: ' + formattedPathsText + (shouldTruncatePaths ? truncatedText : ''); } } function rightPadWithSpaces(s, desiredLength) { - var padLength = desiredLength - s.length; + const padLength = desiredLength - s.length; if (padLength <= 0) { return s; } @@ -544,10 +520,10 @@ function rightPadWithSpaces(s, desiredLength) { } function metaForDisplay(res, options) { - var padToLength = 19; // chars to align - var packageManager = options.packageManager || res.packageManager; - var openSource = res.isPrivate ? 'no' : 'yes'; - var meta = [ + const padToLength = 19; // chars to align + const packageManager = options.packageManager || res.packageManager; + const openSource = res.isPrivate ? 'no' : 'yes'; + const meta = [ chalk.bold(rightPadWithSpaces('Organisation: ', padToLength)) + res.org, chalk.bold(rightPadWithSpaces('Package manager: ', padToLength)) + packageManager, ]; @@ -582,7 +558,7 @@ function dockerRemediationForDisplay(res) { return ''; } const {advice, message} = res.docker.baseImageRemediation; - const out = []; + const out = [] as any[]; if (advice) { for (const item of advice) { @@ -594,23 +570,17 @@ function dockerRemediationForDisplay(res) { return ''; } - return '\n\n' + out.join('\n'); + return `\n\n${out.join('\n')}`; } function validateSeverityThreshold(severityThreshold) { return SEVERITIES - .map(function (s) { - return s.verboseName; - }) + .map((s) => s.verboseName) .indexOf(severityThreshold) > -1; } function getSeverityValue(severity) { - return SEVERITIES.find(function (severityObj) { - if (severityObj['verboseName'] === severity) { - return severityObj; - } - }).value; + return SEVERITIES.find((severityObj) => severityObj.verboseName === severity)!.value; } function titleCaseText(text) { @@ -623,7 +593,7 @@ function isVulnFixable(vuln) { } function groupVulnerabilities(vulns) { - return vulns.reduce(function (map, curr) { + return vulns.reduce((map, curr) => { if (!map[curr.id]) { map[curr.id] = {}; map[curr.id].list = []; @@ -659,8 +629,9 @@ function groupVulnerabilities(vulns) { } // check if vuln was published in the last month function isNewVuln(vuln) { - var MONTH = 30 * 24 * 60 * 60 * 1000; - return new Date(vuln.publicationTime) > Date.now() - MONTH; + const MONTH = 30 * 24 * 60 * 60 * 1000; + const publicationTime = (new Date(vuln.publicationTime)).getTime(); + return publicationTime > Date.now() - MONTH; } function metadataForVuln(vuln) {