Skip to content

[PPSC-720] feat(scan): add client-side finding suppression via .armisignore#162

Open
yiftach-armis wants to merge 4 commits intomainfrom
feat/PPSC-720-cli-finding-suppression
Open

[PPSC-720] feat(scan): add client-side finding suppression via .armisignore#162
yiftach-armis wants to merge 4 commits intomainfrom
feat/PPSC-720-cli-finding-suppression

Conversation

@yiftach-armis
Copy link
Copy Markdown
Collaborator

@yiftach-armis yiftach-armis commented May 7, 2026

Related Issue

Type of Change

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

Problem

The .armisignore parser from PPSC-705 can parse suppression directives (severity:, category:, cwe:, rule:) but no downstream code consumes them. Findings matching these directives still appear in output and trigger --fail-on exits, making the suppression system non-functional.

Solution

Wire the suppression directive parser into the scan results pipeline as a client-side post-filter. After findings are fetched from Armis Cloud, they're matched against suppression directives and marked as suppressed. Suppressed findings are excluded from --fail-on evaluation, hidden from human/JUnit output (unless --show-suppressed), and annotated with SARIF 2.1.0 suppressions[] metadata for GitHub Code Scanning integration.

Key design choices:

  • Mutate-in-place: findings are marked with Suppressed=true + SuppressionInfo rather than removed, so all formatters can decide how to present them
  • Priority order: severity → category → CWE → rule (rule no-ops until scanner_rule_id lands per ADR-0007)
  • Category matching: matches against finding.Type (deterministic, set by DeriveFindingType) rather than the freeform FindingCategory API field
  • Summary recompute: after suppression, Summary.Total reflects only active findings while Summary.Suppressed tracks the suppressed count
  • CRITICAL warning: stderr warning when CRITICAL findings are suppressed, as a safety guardrail

Testing

Automated Tests

  • Unit tests added/updated (30+ new test cases)
  • All tests passing locally

Manual Testing

  1. Built binary, created test repo with .armisignore containing severity:LOW, severity:INFO, cwe:79, category:secrets
  2. Ran scan — confirmed 3 findings suppressed, 2 active in output
  3. Verified --show-suppressed displays all findings with [SUPPRESSED] labels
  4. Verified --fail-on LOW returns exit 0 when LOW findings are suppressed
  5. Verified SARIF output has suppressions array with kind: "inSource" and justification
  6. Verified JSON output has suppressed: true + suppression_info on suppressed findings
  7. Verified JUnit excludes suppressed findings from test cases

Reviewer Notes

  • rule: directive matching is intentionally a no-op — blocked on scanner_rule_id from ADR-0007
  • SARIF suppressions[] will cause GitHub Code Scanning to auto-dismiss matching findings — this is intended behavior
  • The LoadArmisIgnore call was hoisted above the if/else branch so suppression config is loaded in both full-scan and targeted-file modes
  • Also fixes DeriveFindingType to classify LICENSE_COMPLIANCE_RISK as LICENSE (D9 from plan review), enabling category:license to work end-to-end

Checklist

  • Code follows project style guidelines
  • Pre-commit hooks pass
  • Self-review performed
  • No new warnings generated

…ignore

Wire the suppression directive parser from PPSC-705 into the scan results
pipeline. Findings matching severity/category/cwe directives are marked as
suppressed and excluded from --fail-on evaluation, human/JUnit output, while
SARIF and JSON include them with proper suppression metadata.

- Add Suppressed/SuppressionInfo fields to model.Finding, Suppressed to Summary
- Create matcher (MatchFinding, ApplySuppression) with priority: severity > category > CWE > rule
- Hoist LoadArmisIgnore above scan mode branch so suppression applies to all modes
- Add --show-suppressed flag on scanRepoCmd
- Update ShouldFail to skip suppressed findings
- Human formatter: suppression count in brief/summary, CRITICAL warning, [SUPPRESSED] labels
- SARIF: suppressions[] array with kind "inSource" per SARIF 2.1.0
- JUnit: exclude suppressed from test cases
- JSON: suppressed fields flow via omitempty struct tags (zero code changes)
- Fix DeriveFindingType to classify LICENSE_COMPLIANCE_RISK as LICENSE
- Add 30+ test cases across matcher, output, and finding_type packages
Copilot AI review requested due to automatic review settings May 7, 2026 08:35
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 7, 2026

Armis AppSecArmis AppSec Security Scan Results

🟠 HIGH issues found

Severity Count
🟠 HIGH 2

Total: 2

View all 2 findings

🟠 HIGH (2)

CWE-770 - The `stripMarkdown` function takes a piece of text that may contain markdown formatting and runs a series of regular‑expression replacements to turn it into plain text

Location: internal/output/sarif.go:195

The stripMarkdown function takes a piece of text that may contain markdown formatting and runs a series of regular‑expression replacements to turn it into plain text. It does this without checking how large the input is or how complex the markdown might be. If someone (or some automated process) supplies a very large markdown string, the function will keep allocating more memory and CPU time to process it. This can cause the program to slow down or run out of memory, which is the problem described by CWE‑770. In this code the function is used when building the SARIF report, converting the markdown description of a finding into plain text for the Help.Text field. Because there is no limit on the size of the markdown that can be passed in, an attacker who can influence that description could cause excessive resource use. No special checks or limits are present, so the risky operation is reachable from the data that fills the finding description. Adding size checks or processing the markdown in smaller chunks would help prevent this kind of exhaustion.

CWEs: CWE-770: Allocation of Resources Without Limits or Throttling

CWE-22 - Broken Access Control (CWE-22

Location: internal/scan/repo/ignore.go:80

Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')): The function LoadArmisIgnore builds a file path by appending ".armisignore" to the repoRoot value it receives. Because repoRoot comes from outside the function and is not checked or cleaned, an attacker who can set this value could point the code to any location on the file system. The program then walks that path and opens files it finds, which means the attacker could cause the program to read files outside the intended repository directory. No part of the code removes dangerous parts like ".." or otherwise restricts the path, so the risky operation can be reached with attacker‑controlled input. Because this code is part of a command‑line tool rather than a network service, the exposure is limited to the tool’s execution context, but the vulnerability still exists and can be exploited if the tool is run with a malicious repoRoot argument.

CWEs: CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 7, 2026

Test Coverage Report

total: (statements) 76.9%

Coverage by function
github.com/ArmisSecurity/armis-cli/cmd/armis-cli/main.go:19:			main					0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/agent.go:34:		Registry				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/agentdetect.go:29:	FlatResults				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/agentdetect.go:44:	NewScanner				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/agentdetect.go:52:	Scan					82.4%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:12:		resolvePath				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:23:		isUnderDir				81.8%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:44:		dirExists				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:53:		fileExists				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:63:		hasExtensionPrefix			80.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:82:		findExtensionVersion			64.3%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:104:	readVersionFromPackageJSON		71.4%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:119:	hasJetBrainsPlugin			100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:132:	Name					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:134:	Detect					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:139:	CheckMCP				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:143:	DetectVersion				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:151:	Name					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:153:	Detect					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:163:	CheckMCP				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:167:	DetectVersion				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:175:	Name					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:177:	Detect					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:181:	CheckMCP				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:185:	DetectVersion				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:193:	Name					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:195:	Detect					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:205:	CheckMCP				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:210:	DetectVersion				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:218:	Name					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:220:	Detect					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:227:	CheckMCP				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:231:	DetectVersion				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:239:	Name					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:241:	Detect					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:248:	CheckMCP				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:254:	DetectVersion				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:262:	Name					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:264:	Detect					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:271:	CheckMCP				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:275:	DetectVersion				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:283:	Name					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:285:	Detect					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:292:	CheckMCP				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:296:	DetectVersion				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:304:	Name					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:306:	Detect					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:310:	CheckMCP				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:314:	DetectVersion				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:322:	Name					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:324:	Detect					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:328:	CheckMCP				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:332:	DetectVersion				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:340:	Name					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:342:	Detect					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:352:	CheckMCP				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:356:	DetectVersion				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:364:	Name					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:366:	Detect					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:381:	CheckMCP				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:385:	DetectVersion				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:393:	Name					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:395:	Detect					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:403:	CheckMCP				75.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:411:	DetectVersion				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:419:	Name					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:421:	Detect					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:425:	CheckMCP				83.3%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:445:	DetectVersion				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:453:	Name					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:455:	Detect					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:459:	CheckMCP				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:463:	DetectVersion				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/format.go:13:		FormatPlain				81.8%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/format.go:49:		FormatJSON				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/mcpconfig.go:19:	HasArmisMCP				83.3%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/mcpconfig.go:39:	HasArmisMCPInClaudeSettings		86.7%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/mcpconfig.go:66:	HasArmisMCPInZedSettings		66.7%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/mcpconfig.go:95:	HasArmisMCPInVSCodeFormat		66.7%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/mcpconfig.go:118:	hasArmisMCPInData			100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/platform_linux.go:13:	NewPlatform				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/platform_linux.go:17:	UserHomeDirs				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/platform_linux.go:24:	VSCodeExtensionsDir			0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/platform_linux.go:28:	JetBrainsPluginDirs			0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/platform_linux.go:32:	VSCodeUserConfigDir			0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/platform_linux.go:36:	CursorAppExists				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/platform_linux.go:40:	JunieBinaryPaths			0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/platform_linux.go:47:	ZedConfigDir				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/platform_linux.go:51:	IsRoot					0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/userprofile.go:12:	enumerateUserDirs			0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/userprofile.go:39:	currentUserOnly				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/userprofile.go:53:	globJetBrainsPluginDirs			0.0%
github.com/ArmisSecurity/armis-cli/internal/api/agents.go:29:			ReportAgentInventory			78.9%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:29:			Error					0.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:72:			copyWithContext				70.4%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:145:			WithHTTPClient				100.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:154:			WithUploadHTTPClient			100.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:162:			WithAllowLocalURLs			100.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:174:			NewClient				100.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:222:			IsDebug					100.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:236:			setAuthHeader				77.8%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:270:			StartIngest				72.3%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:429:			GetIngestStatus				82.6%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:470:			WaitForIngest				84.6%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:521:			FetchNormalizedResults			74.2%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:576:			FetchAllNormalizedResults		91.7%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:601:			GetScanResult				68.4%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:636:			WaitForScan				90.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:657:			formatBytes				100.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:679:			FetchArtifactScanResults		75.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:734:			ValidatePresignedURL			100.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:770:			DownloadFromPresignedURL		84.2%
github.com/ArmisSecurity/armis-cli/internal/auth/auth.go:58:			NewAuthProvider				95.2%
github.com/ArmisSecurity/armis-cli/internal/auth/auth.go:104:			GetAuthorizationHeader			100.0%
github.com/ArmisSecurity/armis-cli/internal/auth/auth.go:124:			GetTenantID				85.7%
github.com/ArmisSecurity/armis-cli/internal/auth/auth.go:141:			GetRegion				85.7%
github.com/ArmisSecurity/armis-cli/internal/auth/auth.go:156:			IsLegacy				100.0%
github.com/ArmisSecurity/armis-cli/internal/auth/auth.go:169:			GetRawToken				85.7%
github.com/ArmisSecurity/armis-cli/internal/auth/auth.go:196:			exchangeCredentials			87.9%
github.com/ArmisSecurity/armis-cli/internal/auth/auth.go:267:			refreshIfNeeded				100.0%
github.com/ArmisSecurity/armis-cli/internal/auth/auth.go:297:			parseJWTClaims				93.3%
github.com/ArmisSecurity/armis-cli/internal/auth/client.go:29:			Error					100.0%
github.com/ArmisSecurity/armis-cli/internal/auth/client.go:41:			NewAuthClient				100.0%
github.com/ArmisSecurity/armis-cli/internal/auth/client.go:97:			Authenticate				77.4%
github.com/ArmisSecurity/armis-cli/internal/auth/region_cache.go:34:		NewRegionCache				100.0%
github.com/ArmisSecurity/armis-cli/internal/auth/region_cache.go:40:		Load					82.4%
github.com/ArmisSecurity/armis-cli/internal/auth/region_cache.go:74:		Save					76.9%
github.com/ArmisSecurity/armis-cli/internal/auth/region_cache.go:104:		Clear					75.0%
github.com/ArmisSecurity/armis-cli/internal/auth/region_cache.go:114:		getFilePath				83.3%
github.com/ArmisSecurity/armis-cli/internal/auth/region_cache.go:131:		loadCachedRegion			100.0%
github.com/ArmisSecurity/armis-cli/internal/auth/region_cache.go:135:		saveCachedRegion			100.0%
github.com/ArmisSecurity/armis-cli/internal/auth/region_cache.go:139:		clearCachedRegion			100.0%
github.com/ArmisSecurity/armis-cli/internal/cli/color.go:60:			InitColors				85.2%
github.com/ArmisSecurity/armis-cli/internal/cli/color.go:107:			ColorsEnabled				100.0%
github.com/ArmisSecurity/armis-cli/internal/cli/color.go:113:			ColorsForced				100.0%
github.com/ArmisSecurity/armis-cli/internal/cli/color.go:119:			SetOutputToFile				100.0%
github.com/ArmisSecurity/armis-cli/internal/cli/color.go:125:			GetOutputToFile				0.0%
github.com/ArmisSecurity/armis-cli/internal/cli/color.go:129:			enableColors				100.0%
github.com/ArmisSecurity/armis-cli/internal/cli/color.go:136:			disableColors				100.0%
github.com/ArmisSecurity/armis-cli/internal/cli/color.go:151:			parseErrorMessage			92.9%
github.com/ArmisSecurity/armis-cli/internal/cli/color.go:182:			PrintError				100.0%
github.com/ArmisSecurity/armis-cli/internal/cli/color.go:195:			PrintErrorf				0.0%
github.com/ArmisSecurity/armis-cli/internal/cli/color.go:201:			PrintWarning				100.0%
github.com/ArmisSecurity/armis-cli/internal/cli/color.go:206:			PrintWarningf				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/agent_detection.go:36:		init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/agent_detection.go:41:		runAgentDetection			0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/agent_detection_collect.go:29:	init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/agent_detection_collect.go:33:	runAgentDetectionCollect		0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/agent_detection_collect.go:84:	buildInventoryPayload			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/auth.go:33:			init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/auth.go:39:			runAuth					92.9%
github.com/ArmisSecurity/armis-cli/internal/cmd/context.go:24:			NewSignalContext			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/context.go:33:			handleScanError				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/help.go:30:			SetupHelp				91.7%
github.com/ArmisSecurity/armis-cli/internal/cmd/help.go:58:			styledUsageTemplate			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/help.go:101:			defaultUsageTemplate			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/help.go:108:			initColorsForHelp			35.3%
github.com/ArmisSecurity/armis-cli/internal/cmd/help.go:149:			styleHelpOutput				83.3%
github.com/ArmisSecurity/armis-cli/internal/cmd/install.go:54:			init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/install.go:59:			runInstall				0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/install.go:76:			showInstalledVersions			0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/install.go:96:			installAll				0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/install.go:144:			installTargets				0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/install.go:225:			printCredentialStatus			0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/output_helper.go:27:		Cleanup					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/output_helper.go:53:		ResolveOutput				96.4%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:149:			SetVersion				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:157:			Execute					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:161:			init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:192:			PrintUpdateNotification			81.2%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:234:			printUpdateNotificationOnce		75.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:247:			getEnvOrDefault				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:254:			getEnvOrDefaultInt			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:264:			getAPIBaseURL				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:277:			getAuthProvider				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:289:			getPageLimit				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:296:			validatePageLimit			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:306:			validateFailOn				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:324:			getFailOn				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/scan.go:92:			init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/scan_image.go:152:		init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/scan_repo.go:192:		init					100.0%
github.com/ArmisSecurity/armis-cli/internal/httpclient/client.go:31:		NewClient				100.0%
github.com/ArmisSecurity/armis-cli/internal/httpclient/client.go:57:		Do					86.1%
github.com/ArmisSecurity/armis-cli/internal/install/claude.go:23:		NewClaudeInstaller			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/claude.go:32:		InstalledVersion			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/claude.go:37:		Install					14.3%
github.com/ArmisSecurity/armis-cli/internal/install/claude.go:67:		pluginCacheDir				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/claude.go:72:		EnvFilePath				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/claude.go:77:		GetInstalledVersion			76.2%
github.com/ArmisSecurity/armis-cli/internal/install/claude.go:108:		HasExistingEnv				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/claude.go:113:		registerMarketplace			83.3%
github.com/ArmisSecurity/armis-cli/internal/install/claude.go:129:		registerPlugin				75.0%
github.com/ArmisSecurity/armis-cli/internal/install/claude.go:157:		enablePlugin				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:52:		EditorByID				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:65:		ConfigPath				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:76:		IsDetected				80.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:86:		Register				75.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:95:		DetectedEditors				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:112:		NewEditorInstaller			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:121:		InstalledVersion			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:124:		PluginDir				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:127:		EnvFilePath				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:130:		HasExistingEnv				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:137:		FetchPlugin				0.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:150:		GetInstalledVersion			80.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:160:		RegisterJetBrains			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:166:		defaultConfigPath			86.7%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:198:		homeDir					75.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:206:		appSupportPath				29.4%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:237:		registerEditor				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:251:		registerMCPServersFormat		100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:265:		registerVSCodeFormat			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:284:		registerZedFormat			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:303:		stdServerEntry				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:310:		readJSONFileAsMap			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:47:		newPluginInstaller			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:55:		InstalledVersion			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:60:		FetchAndInstall				0.0%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:86:		fetchLatestRelease			69.6%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:126:		downloadAndExtract			73.6%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:242:		createVenv				0.0%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:271:		validateGitHubURL			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:285:		extractFile				57.1%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:297:		writeJSON				66.7%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:308:		findPython				69.2%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:333:		writeEnvFromEnvironment			85.7%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:358:		writeHelperScript			0.0%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:404:		venvPython				66.7%
github.com/ArmisSecurity/armis-cli/internal/output/errno_unix.go:12:		isSyncNotSupported			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:55:			wrapText				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:78:			wrapLine				91.7%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:116:		formatRecommendations			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:186:		wrapTextWithFirstLinePrefix		90.9%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:225:		write					66.7%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:256:		Write					89.5%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:286:		Format					100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:291:		FormatWithOptions			88.1%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:380:		SyncColors				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:384:		sortFindingsBySeverity			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:395:		loadSnippetFromFile			69.4%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:507:		formatCodeSnippetWithFrame		91.1%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:600:		truncatePlainLine			0.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:612:		highlightColumns			93.5%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:657:		scanDuration				89.5%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:690:		pluralize				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:699:		renderBriefStatus			87.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:756:		renderSummaryDashboard			59.5%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:843:		renderFindings				88.9%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:872:		renderFinding				57.7%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:980:		renderGroupedFindings			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1004:		groupFindings				96.8%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1061:		severityRank				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1068:		isGitRepo				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1075:		getGitBlame				38.1%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1112:		parseGitBlame				95.2%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1148:		maskEmail				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1171:		getTopLevelDomain			75.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1183:		getHumanDisplayTitle			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1197:		wrapTitle				93.9%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1256:		maskFixForDisplay			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1291:		formatFixSection			0.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1356:		formatProposedSnippet			0.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1439:		limitHunkContext			64.7%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1515:		parseDiffHunk				91.7%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1537:		parseDiffLines				94.6%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1628:		findInlineChanges			73.5%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1699:		computeLCS				92.3%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1751:		buildTokenPositions			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1767:		tokenizeLine				92.9%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1795:		isWordChar				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1802:		formatDiffWithColorsStyled		77.1%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1876:		extractDiffFilename			80.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1898:		formatDiffHunkLine			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1918:		formatDiffContextLine			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1929:		formatDiffRemoveLine			86.4%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1970:		formatDiffAddLine			86.4%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:2012:		applyInlineHighlights			81.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:2054:		truncateDiffLine			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:2061:		truncateDiffLineWithFlag		66.7%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:2075:		adjustHighlightSpans			83.3%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:2097:		groupDiffHunks				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:2128:		collectRenderOps			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:2171:		renderChangeBlock			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:2230:		formatDiffHunkSeparator			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:2245:		formatValidationSection			0.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:2302:		getExposureDescription			0.0%
github.com/ArmisSecurity/armis-cli/internal/output/icons.go:24:			GetConfidenceIcon			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/json.go:15:			Format					100.0%
github.com/ArmisSecurity/armis-cli/internal/output/json.go:24:			FormatWithOptions			66.7%
github.com/ArmisSecurity/armis-cli/internal/output/json.go:32:			formatWithDebug				0.0%
github.com/ArmisSecurity/armis-cli/internal/output/json.go:58:			maskScanResultForOutput			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/json.go:78:			maskFindingSecrets			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/junit.go:48:			Format					100.0%
github.com/ArmisSecurity/armis-cli/internal/output/junit.go:55:			FormatWithOptions			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/junit.go:63:			formatWithSeverities			85.7%
github.com/ArmisSecurity/armis-cli/internal/output/junit.go:89:			isFailureSeverity			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/junit.go:99:			convertToJUnitCasesWithSeverities	100.0%
github.com/ArmisSecurity/armis-cli/internal/output/junit.go:131:		countFailuresWithSeverities		100.0%
github.com/ArmisSecurity/armis-cli/internal/output/output.go:24:		Error					0.0%
github.com/ArmisSecurity/armis-cli/internal/output/output.go:35:		Error					0.0%
github.com/ArmisSecurity/armis-cli/internal/output/output.go:56:		GetFormatter				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/output.go:73:		ShouldFail				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/output.go:92:		FilterActiveFindings			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/output.go:105:		CheckExit				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:174:		normalizeCWE				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:183:		normalizeCVE				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:193:		stripMarkdown				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:204:		Format					100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:231:		firstNonEmpty				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:250:		stableRuleID				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:265:		buildRules				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:330:		convertToSarifResults			91.2%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:439:		buildMessageText			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:446:		severityToSarifLevel			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:465:		severityToSecurityScore			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:484:		generateHelpURI				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:508:		convertFixToSarif			90.5%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:625:		FormatWithOptions			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/styles.go:138:		DefaultStyles				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/styles.go:276:		NoColorStyles				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/styles.go:353:		GetStyles				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/styles.go:361:		SyncStylesWithColorMode			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/styles.go:386:		GetSeverityText				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/styles.go:414:		TerminalWidth				33.3%
github.com/ArmisSecurity/armis-cli/internal/output/syntax.go:21:		GetLexer				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/syntax.go:32:		GetChromaStyle				80.0%
github.com/ArmisSecurity/armis-cli/internal/output/syntax.go:45:		HighlightCode				81.2%
github.com/ArmisSecurity/armis-cli/internal/output/syntax.go:79:		HighlightLine				75.0%
github.com/ArmisSecurity/armis-cli/internal/output/syntax.go:88:		getTerminalFormatter			60.0%
github.com/ArmisSecurity/armis-cli/internal/output/syntax.go:103:		HighlightLineWithBackground		87.5%
github.com/ArmisSecurity/armis-cli/internal/output/syntax.go:126:		getBackgroundANSI			58.3%
github.com/ArmisSecurity/armis-cli/internal/output/syntax.go:158:		rgbToANSI256				0.0%
github.com/ArmisSecurity/armis-cli/internal/output/syntax.go:171:		parseHexColor				76.9%
github.com/ArmisSecurity/armis-cli/internal/output/writer.go:51:		validateOutputPath			92.3%
github.com/ArmisSecurity/armis-cli/internal/output/writer.go:88:		NewFileOutput				88.2%
github.com/ArmisSecurity/armis-cli/internal/output/writer.go:142:		Writer					100.0%
github.com/ArmisSecurity/armis-cli/internal/output/writer.go:147:		Close					100.0%
github.com/ArmisSecurity/armis-cli/internal/output/writer.go:164:		FormatFromExtension			100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:32:		IsCI					100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:60:		isTerminalWriter			100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:68:		NewReader				100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:83:		NewWriter				50.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:117:		NewSpinner				100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:125:		NewSpinnerWithTimeout			100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:141:		NewSpinnerWithContext			100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:149:		SetWriter				100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:158:		Start					89.8%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:268:		Stop					100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:303:		Update					100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:310:		GetElapsed				100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:317:		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:48:		NewScanner				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:63:		WithPollInterval			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:69:		WithFetchRetryInterval			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:75:		WithSBOMVEXOptions			0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:82:		WithPullPolicy				0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:88:		ScanImage				0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:119:		ScanTarball				77.8%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:230:		exportImage				0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:280:		isDockerAvailable			42.9%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:294:		getDockerCommand			75.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:303:		validateDockerCommand			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:311:		imageExistsLocally			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:319:		determinePullBehavior			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:337:		isRetryableError			75.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:345:		buildScanResult				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:372:		convertNormalizedFindings		85.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:495:		shouldFilterByExploitability		100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:514:		cleanDescription			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:533:		isEmptyFinding				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:548:		generateFindingTitle			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/validate.go:11:		validateImageName			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/mask.go:21:			MaskFixSecrets				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/gitchanges.go:52:		GitChangedFiles				82.6%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/gitchanges.go:102:	gitRepoRoot				80.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/gitchanges.go:126:	changedUncommitted			41.7%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/gitchanges.go:155:	changedStaged				75.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/gitchanges.go:168:	validateRef				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/gitchanges.go:181:	changedSinceRef				75.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/gitchanges.go:204:	filterToScanPath			94.1%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/gitchanges.go:239:	runGit					91.7%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/gitchanges.go:265:	parseLines				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/gitchanges.go:285:	combineAndDedupe			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/ignore.go:28:		LoadIgnorePatterns			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/ignore.go:36:		LoadSuppressionConfig			0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/ignore.go:77:		LoadArmisIgnore				92.9%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/ignore.go:135:		parseArmisIgnoreFile			92.5%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/ignore.go:201:		Match					100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/ignore.go:213:		shouldSkipDir				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/matcher.go:28:		MatchFinding				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/matcher.go:61:		cweMatches				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/matcher.go:77:		ApplySuppression			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/matcher.go:99:		recomputeSummary			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:46:		NewScanner				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:61:		WithPollInterval			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:67:		WithFetchRetryInterval			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:73:		WithIncludeFiles			0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:79:		WithSBOMVEXOptions			0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:85:		Scan					68.5%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:283:		tarGzDirectory				71.8%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:366:		isPathContained				75.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:375:		tarGzFiles				78.6%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:466:		safeAddSize				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:473:		calculateFilesSize			78.6%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:498:		calculateDirSize			76.9%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:545:		shouldSkip				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:576:		isTestFile				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:622:		isRetryableError			75.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:631:		buildScanResult				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:658:		convertNormalizedFindings		73.3%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:781:		shouldFilterByExploitability		100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:800:		cleanDescription			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:821:		generateFindingTitle			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:825:		isEmptyFinding				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/suppression.go:58:	NewSuppressionConfig			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/suppression.go:63:	IsEmpty					100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/suppression.go:77:	Add					100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/suppression.go:99:	CategoryMapping				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/suppression.go:112:	parseDirectiveLine			93.5%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/suppression.go:171:	hasDirectivePrefix			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/suppression.go:185:	validateCWE				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/sbom_vex.go:38:		NewSBOMVEXDownloader			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/sbom_vex.go:50:		Download				85.2%
github.com/ArmisSecurity/armis-cli/internal/scan/sbom_vex.go:102:		downloadAndSave				77.8%
github.com/ArmisSecurity/armis-cli/internal/scan/status.go:16:			FormatScanStatus			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/status.go:35:			FormatElapsed				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/status.go:48:			MapSeverity				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/scan/title.go:14:			GenerateFindingTitle			0.0%
github.com/ArmisSecurity/armis-cli/internal/update/update.go:63:		NewChecker				100.0%
github.com/ArmisSecurity/armis-cli/internal/update/update.go:79:		CheckCached				100.0%
github.com/ArmisSecurity/armis-cli/internal/update/update.go:97:		CheckInBackground			100.0%
github.com/ArmisSecurity/armis-cli/internal/update/update.go:117:		check					85.7%
github.com/ArmisSecurity/armis-cli/internal/update/update.go:160:		fetchLatestVersion			89.5%
github.com/ArmisSecurity/armis-cli/internal/update/update.go:193:		getCacheFilePath			66.7%
github.com/ArmisSecurity/armis-cli/internal/update/update.go:208:		readCache				84.6%
github.com/ArmisSecurity/armis-cli/internal/update/update.go:231:		writeCache				76.9%
github.com/ArmisSecurity/armis-cli/internal/update/update.go:254:		IsNewer					100.0%
github.com/ArmisSecurity/armis-cli/internal/update/update.go:277:		parseVersion				100.0%
github.com/ArmisSecurity/armis-cli/internal/update/update.go:300:		FormatNotification			100.0%
github.com/ArmisSecurity/armis-cli/internal/update/update.go:319:		getUpdateCommand			40.0%
github.com/ArmisSecurity/armis-cli/internal/util/cache.go:21:			GetCacheDir				75.0%
github.com/ArmisSecurity/armis-cli/internal/util/cache.go:41:			GetCacheFilePath			80.0%
github.com/ArmisSecurity/armis-cli/internal/util/format.go:7:			FormatCategory				100.0%
github.com/ArmisSecurity/armis-cli/internal/util/mask.go:109:			MaskSecretInLine			86.4%
github.com/ArmisSecurity/armis-cli/internal/util/mask.go:163:			maskValue				83.3%
github.com/ArmisSecurity/armis-cli/internal/util/mask.go:189:			MaskSecretInLines			100.0%
github.com/ArmisSecurity/armis-cli/internal/util/mask.go:203:			MaskSecretInMultiLineString		100.0%
github.com/ArmisSecurity/armis-cli/internal/util/mask.go:217:			MaskSecretsInStringMap			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)				76.9%

Copy link
Copy Markdown

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

Adds end-to-end client-side consumption of .armisignore suppression directives for repo scans by marking matched findings as suppressed, excluding them from --fail-on/human/JUnit output by default, and emitting SARIF suppression metadata for GitHub Code Scanning.

Changes:

  • Load .armisignore suppression directives during repo scans and apply them post-fetch to mark findings as suppressed, then recompute summary totals/counts.
  • Exclude suppressed findings from failure evaluation (--fail-on) and from JUnit/human output unless explicitly shown; add --show-suppressed flag.
  • Extend outputs/models to represent suppression state (suppressed, suppression_info, SARIF suppressions[]) and improve finding type derivation for license compliance.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
internal/scan/repo/repo.go Loads .armisignore earlier and applies suppression + summary recompute after fetching findings.
internal/scan/repo/matcher.go Implements directive-to-finding matching and in-place suppression mutation + summary recompute helper.
internal/scan/repo/matcher_test.go Unit tests for matching, CWE matching, suppression application, and helpers.
internal/scan/finding_type.go Classifies LICENSE_COMPLIANCE_RISK/LICENSE as FindingTypeLicense.
internal/scan/finding_type_test.go Adds tests for license category classification.
internal/output/sarif.go Adds SARIF suppressions[] emission for suppressed findings.
internal/output/sarif_test.go Adds SARIF tests validating suppressions[] behavior.
internal/output/output.go Excludes suppressed findings from ShouldFail; adds FilterActiveFindings.
internal/output/output_test.go Adds tests for suppressed-skipping ShouldFail and filtering helper.
internal/output/junit.go Filters suppressed findings out of JUnit suite stats/cases.
internal/output/junit_test.go Adds tests ensuring suppressed findings are excluded from JUnit output.
internal/output/json_test.go Adds tests for JSON suppression fields serialization behavior.
internal/output/human.go Hides suppressed findings by default; adds suppressed counts to summary/status; warns on suppressed CRITICAL.
internal/output/human_test.go Adds tests for suppressed messaging, warning behavior, and hiding suppressed findings.
internal/model/finding.go Adds Suppressed, SuppressionInfo, and Summary.Suppressed to the result model.
internal/cmd/scan_repo.go Adds --show-suppressed flag wiring into output options.

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

Comment thread internal/scan/repo/repo.go Outdated
Comment on lines +105 to +110
// Load .armisignore: suppression config applies in ALL scan modes,
// IgnoreMatcher only used in full-scan mode for tar filtering.
ignoreMatcher, suppressionConfig, err := LoadArmisIgnore(absPath)
if err != nil {
return nil, fmt.Errorf("failed to load ignore patterns: %w", err)
}
Comment thread internal/scan/repo/matcher.go Outdated
Comment on lines +10 to +70
var cweIntPattern = regexp.MustCompile(`^CWE-(\d+)`)

// categoryToFindingType maps .armisignore category directive values to FindingType.
var categoryToFindingType = map[string]model.FindingType{
"sast": model.FindingTypeVulnerability,
"secrets": model.FindingTypeSecret,
"iac": model.FindingTypeMisconfig,
"sca": model.FindingTypeSCA,
"license": model.FindingTypeLicense,
}

// MatchResult describes the first directive that matched a finding.
type MatchResult struct {
Matched bool
Directive SuppressionDirective
}

// MatchFinding returns the first matching suppression directive for a finding.
func MatchFinding(finding model.Finding, config *SuppressionConfig) MatchResult {
if config == nil || config.IsEmpty() {
return MatchResult{}
}

for _, d := range config.Severities {
if strings.EqualFold(d.Value, string(finding.Severity)) {
return MatchResult{Matched: true, Directive: d}
}
}

for _, d := range config.Categories {
expected, ok := categoryToFindingType[d.Value]
if ok && finding.Type == expected {
return MatchResult{Matched: true, Directive: d}
}
}

for _, d := range config.CWEs {
if cweMatches(finding.CWEs, d.Value) {
return MatchResult{Matched: true, Directive: d}
}
}

// rule: directives are no-ops until scanner_rule_id is available (ADR-0007)

return MatchResult{}
}

// cweMatches checks if any of the finding's CWE identifiers match the directive value.
// The directive value is a bare integer string (e.g. "89").
// Finding CWEs may be in the format "CWE-89: Improper..." or just "89".
func cweMatches(findingCWEs []string, directiveValue string) bool {
for _, cwe := range findingCWEs {
if matches := cweIntPattern.FindStringSubmatch(cwe); len(matches) == 2 {
if matches[1] == directiveValue {
return true
}
} else if strings.TrimSpace(cwe) == directiveValue {
return true
}
}
return false
Comment thread internal/output/human.go Outdated
Comment on lines +876 to +881
// Show suppression label if finding is suppressed and --show-suppressed is active
if finding.Suppressed {
suppLabel := s.MutedText.Render("[SUPPRESSED]")
var reason string
if finding.SuppressionInfo != nil {
reason = fmt.Sprintf("%s:%s", finding.SuppressionInfo.Type, finding.SuppressionInfo.Value)
- Add LoadSuppressionConfig for targeted mode to avoid unnecessary tree walk
- Make CWE regex case-insensitive and whitespace-tolerant
- Fix misleading comment in renderFinding
Comment thread internal/scan/repo/ignore.go Fixed
Satisfies static analysis path-traversal check on repoRoot parameter.
Copilot AI review requested due to automatic review settings May 7, 2026 09:01
Comment thread internal/scan/repo/ignore.go Fixed
Comment thread internal/scan/repo/repo.go Fixed
Copy link
Copy Markdown

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

Copilot reviewed 17 out of 17 changed files in this pull request and generated 3 comments.

Comment thread internal/output/human.go Outdated
Comment on lines +347 to +360
// CRITICAL suppression warning
if result.Summary.Suppressed > 0 {
criticalSuppressed := 0
for _, f := range result.Findings {
if f.Suppressed && f.Severity == model.SeverityCritical {
criticalSuppressed++
}
}
if criticalSuppressed > 0 {
ew.write("\n")
ew.write("%s\n", s.WarningText.Render(
fmt.Sprintf("WARNING: %d CRITICAL %s suppressed by .armisignore",
criticalSuppressed, pluralize("finding", criticalSuppressed))))
}
Comment thread internal/scan/repo/repo.go Outdated
// Full directory scanning mode — walk tree for both ignore patterns and directives.
ignoreMatcher, suppCfg, loadErr := LoadArmisIgnore(absPath)
if loadErr != nil {
return nil, fmt.Errorf("failed to load ignore patterns: %w", loadErr)
Comment thread internal/scan/repo/matcher.go Outdated
Comment on lines +123 to +127
// filterActiveFindings returns only non-suppressed findings.
func filterActiveFindings(findings []model.Finding) []model.Finding {
active := make([]model.Finding, 0, len(findings))
for _, f := range findings {
if !f.Suppressed {
- Move CRITICAL suppression warning to stderr via cli.PrintWarningf (#1)
- Add early-exit in calculateDirSize when MaxRepoSize exceeded (#2)
- Remove duplicate filterActiveFindings from matcher.go (#3)
- Add filepath.IsAbs validation in LoadSuppressionConfig (#4)
- Update error message for LoadArmisIgnore to be accurate (#5)
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.

3 participants