diff --git a/CHANGELOG.md b/CHANGELOG.md index 86dbf45..65cc69c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # [Unreleased](https://github.com/G-Rath/audit-app/compare/v0.5.3...HEAD) (YYYY-MM-DD) +### Features + +- sort the order of vulnerability paths when using the `paths` output ([#13][]) + # [0.5.3](https://github.com/G-Rath/audit-app/compare/v0.5.2...v0.5.3) (2021-06-11) ### Bug fixes @@ -94,6 +98,7 @@ to be updated. Initial Release 🎉 +[#13]: https://github.com/G-Rath/audit-app/pull/13 [#11]: https://github.com/G-Rath/audit-app/pull/11 [#10]: https://github.com/G-Rath/audit-app/pull/10 [191652d8]: https://github.com/G-Rath/audit-app/commit/191652d8 diff --git a/src/formatReport.ts b/src/formatReport.ts index 6dd684c..6feafca 100644 --- a/src/formatReport.ts +++ b/src/formatReport.ts @@ -2,6 +2,7 @@ import chalk from 'chalk'; import stripAnsi from 'strip-ansi'; import wrapAnsi from 'wrap-ansi'; import { AuditReport } from './generateReport'; +import { sortVulnerabilityPaths } from './sortVulnerabilityPaths'; import { Finding, Severity, SeverityCounts } from './types'; export const SupportedReportFormats = [ @@ -230,7 +231,8 @@ const buildReportSummary = (report: AuditReport): string[] => { const formatters: Record = { json: JSON.stringify, - paths: (report): string => report.vulnerable.join('\n'), + paths: (report): string => + sortVulnerabilityPaths(report.vulnerable).join('\n'), summary: (report): string => buildReportSummary(report).join('\n'), tables: (report): string => [ diff --git a/src/sortVulnerabilityPaths.ts b/src/sortVulnerabilityPaths.ts new file mode 100644 index 0000000..5b28b93 --- /dev/null +++ b/src/sortVulnerabilityPaths.ts @@ -0,0 +1,16 @@ +const splitPath = (fullPath: string): [id: number, path: string] => { + const [id, path] = fullPath.split('|'); + + return [parseInt(id), path]; +}; + +export const sortVulnerabilityPaths = ( + vulnerabilityPaths: readonly string[] +): string[] => { + return Array.from(vulnerabilityPaths).sort((a, b) => { + const [idA, pathA] = splitPath(a); + const [idB, pathB] = splitPath(b); + + return idA - idB || pathA.localeCompare(pathB); + }); +}; diff --git a/test/src/formatReport.spec.ts b/test/src/formatReport.spec.ts index 528c74f..bcd5474 100644 --- a/test/src/formatReport.spec.ts +++ b/test/src/formatReport.spec.ts @@ -392,15 +392,16 @@ describe('formatReport', () => { }); describe('when the format is "paths"', () => { - it('returns a list containing each vulnerable path', () => { + it('returns a sorted list containing each vulnerable path', () => { const vulnerable = [ + '782|gulp>vinyl-fs>glob-watcher>gaze>globule>lodash', '118|gulp>vinyl-fs>glob-watcher>gaze>globule>glob>minimatch', - '118|gulp>vinyl-fs>glob-watcher>gaze>globule>minimatch', '577|gulp>vinyl-fs>glob-watcher>gaze>globule>lodash', - '782|gulp>vinyl-fs>glob-watcher>gaze>globule>lodash', + '1500|webpack>yargs>yargs-parser', '1065|gulp>vinyl-fs>glob-watcher>gaze>globule>lodash', + '1065|gulp>vinyl-fs>glob-watcher>gaze>lodash', + '118|gulp>vinyl-fs>glob-watcher>gaze>globule>minimatch', '1084|webpack>yargs>os-locale>mem', - '1500|webpack>yargs>yargs-parser', '1500|webpack-dev-server>yargs>yargs-parser' ]; @@ -409,7 +410,19 @@ describe('formatReport', () => { ...emptyReport, vulnerable }) - ).toBe(vulnerable.join('\n')); + ).toBe( + [ + '118|gulp>vinyl-fs>glob-watcher>gaze>globule>glob>minimatch', + '118|gulp>vinyl-fs>glob-watcher>gaze>globule>minimatch', + '577|gulp>vinyl-fs>glob-watcher>gaze>globule>lodash', + '782|gulp>vinyl-fs>glob-watcher>gaze>globule>lodash', + '1065|gulp>vinyl-fs>glob-watcher>gaze>globule>lodash', + '1065|gulp>vinyl-fs>glob-watcher>gaze>lodash', + '1084|webpack>yargs>os-locale>mem', + '1500|webpack-dev-server>yargs>yargs-parser', + '1500|webpack>yargs>yargs-parser' + ].join('\n') + ); }); describe('when there are no vulnerable paths', () => { diff --git a/test/src/sortVulnerabilityPaths.spec.ts b/test/src/sortVulnerabilityPaths.spec.ts new file mode 100644 index 0000000..c00c9a1 --- /dev/null +++ b/test/src/sortVulnerabilityPaths.spec.ts @@ -0,0 +1,53 @@ +import { sortVulnerabilityPaths } from '../../src/sortVulnerabilityPaths'; + +describe('sortVulnerabilityPaths', () => { + it('sorts different ids smallest to biggest', () => { + expect( + sortVulnerabilityPaths([ + '25|name', + '1234|name', + '1|name', + '1000|name', + '250|name', + '2|name', + '1432|name' + ]) + ).toStrictEqual([ + '1|name', + '2|name', + '25|name', + '250|name', + '1000|name', + '1234|name', + '1432|name' + ]); + }); + + it('sorts paths with the same id alphabetically', () => { + expect( + sortVulnerabilityPaths([ + '1|abc>def', + '11|abc>def', + '11|def>ghi', + '11|abc', + '11|abc>def>ghi', + '250|abc', + '1432|xyz>def', + '1432|abc>def>ghi', + '1432|xyz>abc', + '1432|abc' + ]) + ).toStrictEqual([ + '1|abc>def', + '11|abc', + '11|abc>def', + '11|abc>def>ghi', + '11|def>ghi', + '250|abc', + '1432|abc', + '1432|abc>def>ghi', + '1432|xyz>abc', + '1432|xyz>def' + ]); + }); +});