Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,13 @@ Scan a given SBOM for EOL data

```
USAGE
$ hd scan eol [--json] [-f <value> | -d <value>] [-s] [--saveSbom] [--saveTrimmedSbom] [--version]
$ hd scan eol [--json] [-f <value> | -d <value>] [-s] [--saveSbom] [--saveTrimmedSbom] [--hideReportUrl] [--version]

FLAGS
-d, --dir=<value> [default: <current directory>] The directory to scan in order to create a cyclonedx SBOM
-f, --file=<value> The file path of an existing cyclonedx SBOM to scan for EOL
-s, --save Save the generated report as herodevs.report.json in the scanned directory
--hideReportUrl Hide the generated web report URL for this scan
--saveSbom Save the generated SBOM as herodevs.sbom.json in the scanned directory
--saveTrimmedSbom Save the trimmed SBOM as herodevs.sbom-trimmed.json in the scanned directory
--version Show CLI version.
Expand Down
7 changes: 7 additions & 0 deletions e2e/scan/eol.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,13 @@ describe('scan:eol e2e', () => {
const { stdout } = await run(cmd);
doesNotMatch(stdout, /View your full EOL report/, 'Should not show web report text in JSON output');
});

it('shows save hint when --hideReportUrl flag is used', async () => {
const cmd = `scan:eol --file ${simpleSbom} --hideReportUrl`;
const { stdout } = await run(cmd);
doesNotMatch(stdout, /View your full EOL report/, 'Should not show web report text when hidden');
match(stdout, /To save your detailed JSON report, use the --save flag/, 'Should show save hint message');
});
});

describe('privacy and transparency', () => {
Expand Down
20 changes: 16 additions & 4 deletions src/commands/scan/eol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { createSbom } from '../../service/cdx.svc.ts';
import {
countComponentsByStatus,
formatDataPrivacyLink,
formatReportSaveHint,
formatScanResults,
formatWebReportUrl,
} from '../../service/display.svc.ts';
Expand Down Expand Up @@ -68,6 +69,11 @@ export default class ScanEol extends Command {
default: false,
description: `Save the trimmed SBOM as ${filenamePrefix}.sbom-trimmed.json in the scanned directory`,
}),
hideReportUrl: Flags.boolean({
aliases: ['hide-report-url'],
default: false,
description: 'Hide the generated web report URL for this scan',
}),
version: Flags.version(),
};

Expand Down Expand Up @@ -125,7 +131,8 @@ export default class ScanEol extends Command {
sbom_created: !flags.file,
scan_load_time: (scanEndTime - scanStartTime) / 1000,
scanned_ecosystems: componentCounts.ECOSYSTEMS,
web_report_link: scan.id ? `${config.eolReportUrl}/${scan.id}` : undefined,
web_report_link: !flags.hideReportUrl && scan.id ? `${config.eolReportUrl}/${scan.id}` : undefined,
web_report_hidden: flags.hideReportUrl,
}));

if (flags.save) {
Expand All @@ -139,7 +146,7 @@ export default class ScanEol extends Command {
}

if (!this.jsonEnabled()) {
this.displayResults(scan);
this.displayResults(scan, flags.hideReportUrl);
}

return scan;
Expand Down Expand Up @@ -225,17 +232,22 @@ export default class ScanEol extends Command {
}
}

private displayResults(report: EolReport): void {
private displayResults(report: EolReport, hideReportUrl: boolean): void {
const lines = formatScanResults(report);
for (const line of lines) {
this.log(line);
}

if (report.id) {
if (!hideReportUrl && report.id) {
const lines = formatWebReportUrl(report.id, config.eolReportUrl);
for (const line of lines) {
this.log(line);
}
} else if (hideReportUrl) {
const lines = formatReportSaveHint();
for (const line of lines) {
this.log(line);
}
}

const privacyLines = formatDataPrivacyLink();
Expand Down
13 changes: 11 additions & 2 deletions src/service/display.svc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ const STATUS_COLORS: Record<ComponentStatus, string> = {
EOL_UPCOMING: 'yellow',
};

const SEPARATOR_WIDTH = 40;

/**
* Formats status row text with appropriate color and icon
*/
Expand Down Expand Up @@ -72,7 +74,7 @@ export function formatScanResults(report: EolReport): string[] {

return [
ux.colorize('bold', 'Scan results:'),
ux.colorize('bold', '-'.repeat(40)),
ux.colorize('bold', '-'.repeat(SEPARATOR_WIDTH)),
ux.colorize('bold', `${report.components.length.toLocaleString()} total packages scanned`),
getStatusRowText.EOL(`${EOL.toLocaleString().padEnd(5)} End-of-Life (EOL)`),
getStatusRowText.EOL_UPCOMING(`${EOL_UPCOMING.toLocaleString().padEnd(5)} EOL Upcoming`),
Expand All @@ -93,7 +95,7 @@ export function formatWebReportUrl(id: string, reportCardUrl: string): string[]
terminalLink(new URL(reportCardUrl).hostname, `${reportCardUrl}/${id}`, { fallback: (_, url) => url }),
);

return [ux.colorize('bold', '-'.repeat(40)), `🌐 View your full EOL report at: ${url}\n`];
return [ux.colorize('bold', '-'.repeat(SEPARATOR_WIDTH)), `🌐 View your full EOL report at: ${url}\n`];
}

/**
Expand All @@ -108,3 +110,10 @@ export function formatDataPrivacyLink(): string[] {

return [`🔒 ${link}\n`];
}

/**
* Formats the report save hint for console display when the web report URL is hidden
*/
export function formatReportSaveHint(): string[] {
return [ux.colorize('bold', '-'.repeat(SEPARATOR_WIDTH)), 'To save your detailed JSON report, use the --save flag'];
}
11 changes: 11 additions & 0 deletions test/service/display.svc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { EolReport } from '@herodevs/eol-shared';
import {
countComponentsByStatus,
formatDataPrivacyLink,
formatReportSaveHint,
formatScanResults,
formatWebReportUrl,
} from '../../src/service/display.svc.ts';
Expand Down Expand Up @@ -169,4 +170,14 @@ describe('display.svc', () => {
assert.ok(lines[0].includes('docs.herodevs.com/eol-ds/data-privacy-and-security'));
});
});

describe('formatReportSaveHint', () => {
it('should provide a save hint message', () => {
const lines = formatReportSaveHint();

assert.strictEqual(lines.length, 2);
assert.ok(lines[0].includes('-'.repeat(40)));
assert.ok(lines[1].includes('--save'));
});
});
});