Skip to content

[PPSC-349] feat(output): add --summary-top flag for summary positioning#67

Merged
yiftach-armis merged 2 commits intomainfrom
feat/PPSC-349-summary-top
Feb 4, 2026
Merged

[PPSC-349] feat(output): add --summary-top flag for summary positioning#67
yiftach-armis merged 2 commits intomainfrom
feat/PPSC-349-summary-top

Conversation

@yiftach-armis
Copy link
Collaborator

@yiftach-armis yiftach-armis commented Feb 4, 2026

Related Issue

Type of Change

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

Problem

Users requested the ability to display the summary dashboard at the top of the human format output instead of at the end, especially when redirecting output to a file to see the high-level overview first without scrolling through findings.

Solution

Added a new --summary-top flag to the scan commands that displays the summary dashboard before the findings section. When enabled, the brief status line is also skipped to avoid redundancy since the full summary provides the same information.

Testing

Automated Tests

  • All existing tests passing (778 tests)
  • Pre-commit hooks pass
  • Linter passes (0 issues)

Reviewer Notes

The implementation follows the Options Pattern already used in the codebase (similar to --group-by). The flag is a PersistentFlag on the scanCmd, so it's available for both scan repo and scan image commands.

…fore findings

Add a new --summary-top flag to the human format output that allows the summary dashboard to be displayed at the top of the output (before findings) instead of at the end. When enabled, the brief status line is also skipped to avoid redundancy.
Copilot AI review requested due to automatic review settings February 4, 2026 13:44
@github-actions
Copy link

github-actions bot commented Feb 4, 2026

🛡️ Armis Security Scan Results

✅ No issues

Severity Count

Total: 0

View full results

No security issues found.

@github-actions
Copy link

github-actions bot commented Feb 4, 2026

Test Coverage Report

total: (statements) 79.3%

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:58:			WithHTTPClient				100.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:67:			WithAllowLocalURLs			100.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:79:			NewClient				100.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:129:			IsDebug					100.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:136:			setAuthHeader				55.6%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:167:			StartIngest				75.5%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:257:			GetIngestStatus				89.5%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:290:			WaitForIngest				81.8%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:332:			FetchNormalizedResults			78.6%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:379:			FetchAllNormalizedResults		91.7%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:404:			GetScanResult				73.3%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:432:			WaitForScan				90.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:453:			formatBytes				100.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:475:			FetchArtifactScanResults		75.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:530:			ValidatePresignedURL			100.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:566:			DownloadFromPresignedURL		84.2%
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:27:			init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/scan_image.go:113:		init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/scan_repo.go:108:		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:35:			write					66.7%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:66:			Write					90.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:97:			Format					100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:102:		FormatWithOptions			78.6%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:172:		getSeverityIcon				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:189:		getSeverityColor			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:219:		init					50.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:226:		disableColors				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:239:		sortFindingsBySeverity			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:268:		loadSnippetFromFile			69.4%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:378:		formatCodeSnippet			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:415:		highlightColumns			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:451:		detectLanguage				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:749:		scanDuration				89.5%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:782:		pluralize				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:791:		renderBriefStatus			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:825:		renderSummaryDashboard			61.2%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:914:		renderFindings				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:929:		renderFinding				61.1%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:996:		renderGroupedFindings			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1017:		groupFindings				96.6%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1072:		severityRank				75.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1086:		isGitRepo				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1093:		getGitBlame				14.3%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1130:		parseGitBlame				95.2%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1166:		maskEmail				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1189:		getTopLevelDomain			75.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1198:		formatFixSection			0.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1249:		formatProposedSnippet			0.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1274:		formatDiffWithColors			0.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1294:		formatValidationSection			0.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1330:		getExposureDescription			0.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:33:		GetFormatter				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/output.go:49:		ShouldFail				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/output.go:65:		ExitIfNeeded				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:159:		stripMarkdown				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:170:		Format					100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:197:		buildRules				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:261:		convertToSarifResults			88.5%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:351:		buildMessageText			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:358:		severityToSarifLevel			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:377:		severityToSecurityScore			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:395:		generateHelpURI				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:422:		convertFixToSarif			90.5%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:535:		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					93.8%
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:42:		NewScanner				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:56:		WithPollInterval			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:62:		WithSBOMVEXOptions			0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:68:		ScanImage				0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:100:		ScanTarball				76.1%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:183:		exportImage				0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:224:		isDockerAvailable			42.9%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:238:		getDockerCommand			75.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:247:		validateDockerCommand			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:254:		buildScanResult				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:281:		convertNormalizedFindings		87.9%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:397:		shouldFilterByExploitability		100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:416:		cleanDescription			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:435:		isEmptyFinding				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:448:		mapSeverity				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:463:		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:41:		NewScanner				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:55:		WithPollInterval			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:61:		WithIncludeFiles			0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:67:		WithSBOMVEXOptions			0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:73:		Scan					70.6%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:231:		tarGzDirectory				71.8%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:311:		isPathContained				75.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:320:		tarGzFiles				78.6%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:406:		calculateFilesSize			0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:427:		calculateDirSize			81.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:466:		shouldSkip				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:497:		isTestFile				88.9%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:540:		buildScanResult				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:567:		convertNormalizedFindings		76.8%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:682:		shouldFilterByExploitability		100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:701:		cleanDescription			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:722:		generateFindingTitle			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:776:		isEmptyFinding				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:789:		mapSeverity				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:804:		formatElapsed				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/sbom_vex.go:36:		NewSBOMVEXDownloader			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/sbom_vex.go:48:		Download				85.2%
github.com/ArmisSecurity/armis-cli/internal/scan/sbom_vex.go:100:		downloadAndSave				76.5%
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/format.go:7:			FormatCategory				100.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)				79.3%

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a --summary-top flag to display the summary dashboard at the top of the output (before findings) instead of at the end. This addresses user feedback requesting the ability to see high-level scan results first, particularly when redirecting output to a file.

Changes:

  • Added SummaryTop boolean field to FormatOptions struct
  • Modified HumanFormatter.FormatWithOptions to conditionally render summary at top or bottom based on flag
  • Added --summary-top persistent flag to the scan command, making it available for both scan repo and scan image subcommands

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
internal/output/output.go Added SummaryTop field to FormatOptions struct with consistent alignment
internal/output/human.go Implemented conditional logic to display summary at top when flag is enabled; skips brief status line when full summary is shown at top
internal/cmd/scan_repo.go Passes SummaryTop option from flag to formatter with consistent field alignment
internal/cmd/scan_image.go Passes SummaryTop option from flag to formatter with consistent field alignment
internal/cmd/scan.go Declares summaryTop package variable and registers --summary-top persistent flag

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…WE-770)

Add bounds on memory allocation when loading code snippets to prevent
potential denial-of-service from maliciously crafted files:
- Maximum 10KB per line
- Maximum 100KB total snippet size
- Graceful truncation when limits are exceeded
@yiftach-armis yiftach-armis merged commit 61138e6 into main Feb 4, 2026
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