Skip to content

fix: address security vulnerabilities in CLI and workflows#41

Merged
yiftach-armis merged 8 commits intomainfrom
fix/review-comments
Jan 15, 2026
Merged

fix: address security vulnerabilities in CLI and workflows#41
yiftach-armis merged 8 commits intomainfrom
fix/review-comments

Conversation

@yiftach-armis
Copy link
Collaborator

Description

Fix 6 security vulnerabilities across the CLI and GitHub Actions workflows, addressing issues raised in security audit.

Changes

  • mask.go: Mask secrets completely without revealing identifying prefixes (JWT, GitHub tokens, AWS keys, Stripe keys)
  • reusable-security-scan.yml: Use env: block to prevent command injection via GitHub Actions inputs
  • install.ps1: Normalize paths before validation to prevent path traversal bypasses
  • install.sh: Add missing patterns and realpath validation for stronger path traversal defense
  • client.go: Log warnings when URL parsing fails instead of silently ignoring errors
  • output.go: Check os.Stdout.Sync() error and log failures to stderr

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • Security improvement

Testing

  • Unit tests pass locally (all tests in internal/util, internal/api, internal/output passing)
  • Build passes with no TypeScript errors
  • Lint check passes (0 issues)

Checklist

  • Code follows project style
  • Self-reviewed and tested
  • No new warnings generated
  • All existing tests pass

- Fix path traversal bypass in install.ps1 by normalizing paths before validation
- Add missing path traversal patterns in install.sh with realpath normalization
- Fix command injection in GitHub Actions workflow by using env: block
- Improve secret masking in mask.go to prevent prefix exposure (JWT, API keys)
- Add URL parse error handling in API client with warning output
- Check os.Stdout.Sync() error and log to stderr before exit

All fixes maintain backward compatibility and follow security best practices.
@github-actions
Copy link

github-actions bot commented Jan 13, 2026

Test Coverage Report

total: (statements) 77.6%

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:36:			WithHTTPClient				100.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:44:			NewClient				86.7%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:92:			IsDebug					100.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:97:			StartIngest				78.4%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:164:			GetIngestStatus				84.2%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:197:			WaitForIngest				0.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:239:			FetchNormalizedResults			78.6%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:286:			FetchAllNormalizedResults		91.7%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:311:			GetScanResult				66.7%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:339:			WaitForScan				0.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:360:			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:21:			init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/scan_image.go:97:		init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/scan_repo.go:75:		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					87.5%
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			96.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:136:		getSeverityIcon				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:153:		getSeverityColor			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:182:		init					50.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:189:		disableColors				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:201:		sortFindingsBySeverity			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:230:		loadSnippetFromFile			75.5%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:312:		formatCodeSnippet			0.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:349:		highlightColumns			0.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:385:		detectLanguage				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:683:		scanDuration				26.3%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:715:		renderSummaryDashboard			61.2%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:804:		renderFindings				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:819:		renderFinding				62.5%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:876:		renderGroupedFindings			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:897:		groupFindings				96.6%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:952:		severityRank				75.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:966:		isGitRepo				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:973:		getGitBlame				0.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1009:		parseGitBlame				85.7%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1045:		maskEmail				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1068:		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:110:		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:64:			Format					100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:87:			convertToSarifResults			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:124:		severityToSarifLevel			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:138:		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: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		93.2%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:319:		shouldFilterByExploitability		100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:338:		cleanDescription			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:357:		isEmptyFinding				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:370:		mapSeverity				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:385:		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/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:39:		NewScanner				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:53:		WithPollInterval			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:59:		Scan					84.6%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:146:		tarGzDirectory				71.4%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:225:		calculateDirSize			81.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:264:		shouldSkip				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:295:		isTestFile				88.9%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:338:		buildScanResult				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:365:		convertNormalizedFindings		89.1%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:457:		shouldFilterByExploitability		100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:476:		cleanDescription			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:495:		isEmptyFinding				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:508:		mapSeverity				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:523:		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:44:			MaskSecretInLine			81.2%
github.com/ArmisSecurity/armis-cli/internal/util/mask.go:83:			maskValue				83.3%
github.com/ArmisSecurity/armis-cli/internal/util/mask.go:109:			MaskSecretInLines			100.0%
github.com/ArmisSecurity/armis-cli/internal/util/path.go:12:			SanitizePath				90.9%
github.com/ArmisSecurity/armis-cli/internal/util/path.go:41:			SafeJoinPath				84.2%
github.com/ArmisSecurity/armis-cli/test/sample-repo/src/main.go:6:		main					0.0%
total:										(statements)				77.6%

- Remove redundant validation block after realpath normalization (realpath
  already resolves all '..' segments, so the case statement was dead code)
- Add comment explaining GNU-specific -m flag for realpath portability
- Use length ranges instead of exact lengths in maskValue to prevent
  token type fingerprinting (e.g., GitHub PATs ~40 chars, AWS keys 40 chars)
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 addresses 6 security vulnerabilities across the CLI and GitHub Actions workflows. The changes strengthen input validation, prevent information leakage through secrets, and improve error handling.

Changes:

  • Enhanced secret masking to prevent prefix leakage that could identify secret types
  • Added command injection prevention in GitHub Actions workflow via environment variables
  • Strengthened path traversal defenses in installation scripts with normalization and extended pattern matching

Reviewed changes

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

Show a summary per file
File Description
internal/util/mask.go Complete secret masking without revealing identifying prefixes or partial content
.github/workflows/reusable-security-scan.yml Prevents command injection by using env block instead of inline expression interpolation
scripts/install.ps1 Path normalization before validation to prevent path traversal bypasses
scripts/install.sh Additional pattern matching and realpath validation for path traversal defense
internal/api/client.go Explicit URL parsing error logging instead of silent failures
internal/output/output.go Error handling for stdout sync failures with stderr logging

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

- Add package-level variables for os.Exit, stdout sync, and stderr in
  output.go to enable mocking in tests
- Add tests for ExitIfNeeded covering exit behavior, code normalization,
  and stdout sync error handling
- Add tests verifying secret prefixes don't leak in masked output
- Fix path validation pattern in install.sh (remove redundant case)
- Align stdoutSyncer variable with other variables in the block
- Explicitly ignore fmt.Fprintf return values to satisfy errcheck
@yiftach-armis yiftach-armis requested a review from Copilot January 14, 2026 11:33
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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

The normalized_dir variable was calculated but never used. Now the
normalized path is also validated for invalid characters and path
traversal segments as an additional defense-in-depth measure.
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

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


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

Change pattern from `/..*` to `/..` to only match the exact `/..` path
traversal, not valid directory names like `/..foo`.
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

Copilot reviewed 8 out of 10 changed files in this pull request and generated 1 comment.


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

Change `length <= 8` to `length < 10` so that 9-character values
show exact asterisks instead of the misleading "10-20" range.
@yiftach-armis yiftach-armis merged commit d4edbf1 into main Jan 15, 2026
6 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.

2 participants