Skip to content

[PPSC-323] feat(output): add hybrid scan summary with brief status at top#57

Merged
yiftach-armis merged 1 commit intomainfrom
feat/PPSC-323-scan-summary-placement
Jan 29, 2026
Merged

[PPSC-323] feat(output): add hybrid scan summary with brief status at top#57
yiftach-armis merged 1 commit intomainfrom
feat/PPSC-323-scan-summary-placement

Conversation

@yiftach-armis
Copy link
Collaborator

@yiftach-armis yiftach-armis commented Jan 29, 2026

Related Issue

Type of Change

  • New feature (non-breaking change which adds functionality)

Problem

Scan output summary was displayed at the top, which is not aligned with common CLI best practices for security scanners. Users need immediate orientation but also benefit from a comprehensive summary at the end for reference.

Solution

Implemented a hybrid approach:

  • Brief status line at the top showing findings count by severity (e.g., "⚠️ Found 5 issues: 2 critical, 1 high, 2 medium")
  • Detailed summary dashboard moved to the end for comprehensive review
  • Added extra spacing between severity emoji and labels for better visual clarity

Testing

  • Unit tests added/updated
  • All tests passing locally
  • Verified output structure with multiple scenarios (no findings, single finding, multiple severities)

Reviewer Notes

The brief status provides instant visual feedback at the top, while the detailed dashboard remains available at the end for those who want the complete breakdown of findings by category and severity.

… top

Move detailed summary dashboard to end of output and add concise brief status line
at top for immediate orientation. Brief status shows count breakdown by severity
(e.g., "Found 5 issues: 2 critical, 1 high, 2 medium"). Adds extra spacing between
severity emoji and label for better visual clarity.
@github-actions
Copy link

🛡️ Armis Security Scan Results

🟠 HIGH issues found

Severity Count
🟠 HIGH 1

Total: 1

View all 1 findings

🟠 HIGH (1)

CWE-78_armis-cli_internal/output/human.go_1057_2_1057_103 - exec.Command is constructed with file path and line number arguments derived from external input without adequate validation, allowing an attacker to influence the executed git command.

Location: internal/output/human.go:1057

exec.Command is constructed with file path and line number arguments derived from external input without adequate validation, allowing an attacker to influence the executed git command.

CWEs: CWE-78: OS Command Injection

@github-actions
Copy link

Test Coverage Report

total: (statements) 75.0%

Coverage by function
github.com/ArmisSecurity/armis-cli/cmd/armis-cli/main.go:16:			main					0.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:35:			WithHTTPClient				100.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:48:			NewClient				100.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:98:			IsDebug					100.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:103:			StartIngest				78.4%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:170:			GetIngestStatus				84.2%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:203:			WaitForIngest				0.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:245:			FetchNormalizedResults			78.6%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:292:			FetchAllNormalizedResults		91.7%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:317:			GetScanResult				66.7%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:345:			WaitForScan				0.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:366:			formatBytes				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/context.go:14:			NewSignalContext			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/context.go:21:			handleScanError				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:41:			SetVersion				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:49:			Execute					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:53:			init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:65:			getEnvOrDefault				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:72:			getEnvOrDefaultInt			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:82:			getAPIBaseURL				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:89:			getToken				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:96:			getTenantID				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:103:			getPageLimit				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:110:			validatePageLimit			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:120:			validateFailOn				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:134:			getFailOn				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/scan.go:22:			init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/scan_image.go:100:		init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/scan_repo.go:95:		init					100.0%
github.com/ArmisSecurity/armis-cli/internal/httpclient/client.go:30:		NewClient				100.0%
github.com/ArmisSecurity/armis-cli/internal/httpclient/client.go:56:		Do					85.3%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:31:			write					66.7%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:62:			Write					90.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:93:			Format					100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:98:			FormatWithOptions			93.8%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:152:		getSeverityIcon				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:169:		getSeverityColor			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:198:		init					50.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:205:		disableColors				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:217:		sortFindingsBySeverity			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:246:		loadSnippetFromFile			75.5%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:328:		formatCodeSnippet			0.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:365:		highlightColumns			0.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:401:		detectLanguage				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:699:		scanDuration				26.3%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:732:		pluralize				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:741:		renderBriefStatus			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:775:		renderSummaryDashboard			61.2%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:864:		renderFindings				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:879:		renderFinding				62.5%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:936:		renderGroupedFindings			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:957:		groupFindings				96.6%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1012:		severityRank				75.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1026:		isGitRepo				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1033:		getGitBlame				0.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1070:		parseGitBlame				85.7%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1106:		maskEmail				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1129:		getTopLevelDomain			75.0%
github.com/ArmisSecurity/armis-cli/internal/output/json.go:14:			Format					100.0%
github.com/ArmisSecurity/armis-cli/internal/output/json.go:21:			FormatWithOptions			66.7%
github.com/ArmisSecurity/armis-cli/internal/output/json.go:29:			formatWithDebug				0.0%
github.com/ArmisSecurity/armis-cli/internal/output/junit.go:43:			Format					83.3%
github.com/ArmisSecurity/armis-cli/internal/output/junit.go:67:			convertToJUnitCases			91.7%
github.com/ArmisSecurity/armis-cli/internal/output/junit.go:99:			countFailures				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/junit.go:112:		FormatWithOptions			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/output.go:32:		GetFormatter				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/output.go:48:		ShouldFail				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/output.go:64:		ExitIfNeeded				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:96:			Format					100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:123:		buildRules				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:152:		convertToSarifResults			82.4%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:212:		severityToSarifLevel			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:231:		severityToSecurityScore			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:249:		FormatWithOptions			100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:25:		IsCI					100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:47:		NewReader				100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:62:		NewWriter				50.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:96:		NewSpinner				100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:104:		NewSpinnerWithTimeout			100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:120:		NewSpinnerWithContext			100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:128:		SetWriter				100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:137:		Start					89.6%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:225:		Stop					100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:260:		UpdateMessage				100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:267:		Update					100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:274:		GetElapsed				100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:281:		formatDuration				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/finding_type.go:9:		DeriveFindingType			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:41:		NewScanner				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:55:		WithPollInterval			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:61:		ScanImage				0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:93:		ScanTarball				93.1%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:141:		exportImage				0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:174:		isDockerAvailable			42.9%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:188:		getDockerCommand			75.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:197:		validateDockerCommand			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:204:		buildScanResult				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:231:		convertNormalizedFindings		89.1%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:323:		shouldFilterByExploitability		100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:342:		cleanDescription			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:361:		isEmptyFinding				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:374:		mapSeverity				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:389:		formatElapsed				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/validate.go:11:		validateImageName			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/files.go:26:		ParseFileList				87.5%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/files.go:41:		addFile					87.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/files.go:93:		Files					100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/files.go:98:		RepoRoot				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/files.go:103:		ValidateExistence			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/ignore.go:18:		LoadIgnorePatterns			75.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/ignore.go:52:		loadIgnoreFile				89.5%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/ignore.go:86:		Match					100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/ignore.go:98:		shouldSkipDir				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:40:		NewScanner				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:54:		WithPollInterval			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:60:		WithIncludeFiles			0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:66:		Scan					73.7%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:201:		tarGzDirectory				71.8%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:281:		isPathContained				0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:290:		tarGzFiles				0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:376:		calculateFilesSize			0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:397:		calculateDirSize			81.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:436:		shouldSkip				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:467:		isTestFile				88.9%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:510:		buildScanResult				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:537:		convertNormalizedFindings		89.1%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:629:		shouldFilterByExploitability		100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:648:		cleanDescription			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:667:		isEmptyFinding				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:680:		mapSeverity				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:695:		formatElapsed				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/testhelpers/findings.go:9:	CreateNormalizedFinding			0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/testhelpers/findings.go:14:	CreateNormalizedFindingWithLabels	0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/testhelpers/findings.go:19:	CreateNormalizedFindingFull		0.0%
github.com/ArmisSecurity/armis-cli/internal/util/mask.go:54:			MaskSecretInLine			81.2%
github.com/ArmisSecurity/armis-cli/internal/util/mask.go:93:			maskValue				83.3%
github.com/ArmisSecurity/armis-cli/internal/util/mask.go:119:			MaskSecretInLines			100.0%
github.com/ArmisSecurity/armis-cli/internal/util/path.go:13:			SanitizePath				90.9%
github.com/ArmisSecurity/armis-cli/internal/util/path.go:51:			SafeJoinPath				87.5%
github.com/ArmisSecurity/armis-cli/test/sample-repo/src/main.go:6:		main					0.0%
total:										(statements)				75.0%

@yiftach-armis yiftach-armis merged commit 658caa5 into main Jan 29, 2026
8 of 9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant