Skip to content

[PSC-711] feat: add install copilot command for MCP server#146

Open
shb7628 wants to merge 8 commits intomainfrom
feat/PSC-711-install-mcp-on-copilot
Open

[PSC-711] feat: add install copilot command for MCP server#146
shb7628 wants to merge 8 commits intomainfrom
feat/PSC-711-install-mcp-on-copilot

Conversation

@shb7628
Copy link
Copy Markdown
Collaborator

@shb7628 shb7628 commented Apr 23, 2026

Summary

  • Extract shared plugin download/setup logic (GitHub release fetch, tar extraction, venv creation) from ClaudeInstaller into a reusable PluginInstaller in internal/install/plugin.go
  • Add armis-cli install copilot subcommand that installs the Armis AppSec MCP server for GitHub Copilot in VS Code
  • Registers the MCP server in VS Code's user-level mcp.json with stdio transport, pointing to the plugin's venv Python and server.py

Test plan

  • All existing internal/install tests pass (23 tests)
  • New tests for CopilotInstaller: constructor, env detection, version tracking, MCP server registration, preserving existing MCP config
  • New tests for PluginInstaller: release fetch, download/extract, version tracking, GitHub URL validation
  • Security scan: 1 pre-existing HIGH (CWE-426 findPython PATH lookup) — mitigated by symlink resolution, absolute path check, and hardcoded name allowlist; same pattern shipped in [PPSC-648] feat: add install feature #143
  • Manual: armis-cli install copilot creates ~/.armis/plugins/armis-appsec-mcp/ and registers in VS Code mcp.json
  • Manual: armis-cli install copilot --version reports installed version
  • Manual: existing armis-cli install claude still works after refactor

🤖 Generated with Claude Code

Extract shared plugin download/setup logic into PluginInstaller and add
a new `install copilot` subcommand that registers the Armis AppSec MCP
server in VS Code's user-level mcp.json for GitHub Copilot.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment thread internal/install/plugin.go Fixed
Comment thread internal/install/plugin.go Fixed
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 23, 2026

Armis AppSecArmis AppSec Security Scan Results

🟠 HIGH issues found

Severity Count
🟠 HIGH 3
🟡 MEDIUM 2

Total: 5

View all 5 findings

🟠 HIGH (3)

CWE-522_armis-cli_38295677_internal/install/plugin.go_347_2_347_150 - Insecure Design (CWE-522

Location: internal/install/plugin.go:347

Insufficiently Protected Credentials): The function writeEnvFromEnvironment reads two environment variables, ARMIS_CLIENT_ID and ARMIS_CLIENT_SECRET. If both are set, it builds a text string that contains the secret value and writes that string to a file (the ".env" file) on disk. The file is created with permissions that allow only the file owner to read or write it (mode 0600). Even though the permissions are restrictive, the secret is now stored on the filesystem. Any other program or user that can run as the same system user, or that can gain access to the file (for example, through a backup, a mis‑configured backup script, or a compromised account), could read the secret. This means the credential is no longer kept only in memory or in the environment where it was originally set, increasing the risk of it being leaked. An attacker who can access the file could obtain the ARMIS_CLIENT_SECRET and use it to impersonate the application or access protected resources. The code does not perform any additional checks or encryption before writing the secret, so the risk is real. To reduce this risk, avoid writing the secret to a file altogether, or encrypt it before storing it. If a file must be used, consider stronger protection mechanisms (such as using a dedicated secret manager) and ensure the file is stored in a location that only trusted processes can reach.

Code snippet is redacted as it contains secrets.

CWEs: CWE-522: Insufficiently Protected Credentials

CWE-22_armis-cli_38295677_internal/install/editors.go_215_3_215_29 - Broken Access Control (CWE-22

Location: internal/install/editors.go:215

Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')): The program builds a file path by reading the APPDATA environment variable and then appending sub‑folders such as "Code/User/mcp.json". An attacker who can set the APPDATA variable before the program runs can change the base of that path. Because the code does not check or clean the value, the final path may point to any location on the file system. Later, the program writes configuration data to that path (through functions like writeJSON). This means a malicious user could cause the program to write files outside the intended application‑support directory, potentially overwriting important files or placing malicious files where they will be read later. No validation is performed on the environment variable, so the risky operation is reachable from attacker‑controlled input. To fix the issue, the code should verify that the APPDATA value points to a trusted directory or use a safe, fixed location instead of trusting the environment variable directly.

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

CWE-22_armis-cli_38295677_internal/install/editors.go_206_3_206_37 - Broken Access Control (CWE-22

Location: internal/install/editors.go:206

Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')): The code builds a file path by reading the XDG_CONFIG_HOME environment variable and then joining it with other directory names. Because the environment variable can be set by anyone who runs the program, an attacker could change its value to point to a location they control. The resulting path is later used to read or write configuration files. Since there is no check that the path stays inside the intended configuration directory, an attacker could cause the program to read from or write to files outside the safe area, potentially exposing or overwriting sensitive data. No validation or restriction is applied to the environment‑provided value, so the risky operation is reachable from user‑controlled input.

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

🟡 MEDIUM (2)

CWE-253_armis-cli_38295677_internal/install/claude.go_24_2_24_28 - The code calls `os.UserHomeDir()` to find the user's home directory, but it completely ignores the error value that the function returns

Location: internal/install/claude.go:24

The code calls os.UserHomeDir() to find the user's home directory, but it completely ignores the error value that the function returns. If the call fails (for example, if the operating system cannot determine the home directory), the variable home will be an empty string. The next line builds a file path with filepath.Join(home, ".claude"). When home is empty, the path becomes just ".claude", which points to a location relative to the current working directory instead of the intended user’s home folder. Because the error is not checked, the program may unintentionally read from or write to the wrong place on the file system. This could cause the installer to create or modify files in an unexpected directory, potentially overwriting other data or leaving configuration files where they don’t belong. No external user input reaches this part of the code, so an attacker cannot directly control the path. However, the bug is still a problem because the program makes an unsafe assumption about a system call’s success. Adding a proper error check after os.UserHomeDir() and handling the failure case (e.g., returning an error) would fix the issue.

CWEs: CWE-253: Incorrect Check of Function Return Value

CWE-22_armis-cli_38295677_internal/install/editors.go_302_2_302_61 - Broken Access Control (CWE-22

Location: internal/install/editors.go:302

Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')): The function readJSONFileAsMap opens a file whose name is passed in as the path argument. It does not check whether the supplied path stays inside a safe directory, so if someone could control that argument they could cause the program to read any file on the system. In the surrounding code, the only places that call this function build the file name from known configuration locations, not from user input. Because the path is not influenced by external data, an attacker cannot currently reach the risky operation, which keeps the actual danger low. However, the lack of validation means that if the function were ever called with a value that came from outside the program, it could be exploited to read arbitrary files.

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

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 23, 2026

Test Coverage Report

total: (statements) 77.6%

Coverage by function
github.com/ArmisSecurity/armis-cli/cmd/armis-cli/main.go:19:			main					0.0%
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/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:51:			init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/install.go:56:			runInstall				0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/install.go:73:			showInstalledVersions			0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/install.go:93:			installAll				0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/install.go:141:			installTargets				0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/install.go:218:			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:188:		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					15.4%
github.com/ArmisSecurity/armis-cli/internal/install/claude.go:65:		pluginCacheDir				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/claude.go:70:		EnvFilePath				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/claude.go:75:		GetInstalledVersion			76.2%
github.com/ArmisSecurity/armis-cli/internal/install/claude.go:106:		HasExistingEnv				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/claude.go:111:		registerMarketplace			83.3%
github.com/ArmisSecurity/armis-cli/internal/install/claude.go:127:		registerPlugin				75.0%
github.com/ArmisSecurity/armis-cli/internal/install/claude.go:155:		enablePlugin				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:48:		EditorByID				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:61:		ConfigPath				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:72:		IsDetected				80.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:82:		Register				75.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:91:		DetectedEditors				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:108:		NewEditorInstaller			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:117:		InstalledVersion			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:120:		PluginDir				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:123:		EnvFilePath				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:126:		HasExistingEnv				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:133:		FetchPlugin				0.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:144:		GetInstalledVersion			80.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:154:		RegisterJetBrains			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:160:		defaultConfigPath			84.6%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:188:		homeDir					75.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:196:		appSupportPath				29.4%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:227:		registerEditor				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:241:		registerMCPServersFormat		100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:255:		registerVSCodeFormat			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:274:		registerZedFormat			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:293:		stdServerEntry				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:300:		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:332:		writeEnvFromEnvironment			83.3%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:355:		writeHelperScript			0.0%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:401:		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:54:			wrapText				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:77:			wrapLine				91.7%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:115:		formatRecommendations			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:185:		wrapTextWithFirstLinePrefix		90.9%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:224:		write					66.7%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:255:		Write					89.5%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:285:		Format					100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:290:		FormatWithOptions			84.4%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:360:		SyncColors				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:364:		sortFindingsBySeverity			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:375:		loadSnippetFromFile			69.4%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:487:		formatCodeSnippetWithFrame		91.1%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:580:		truncatePlainLine			0.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:592:		highlightColumns			93.5%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:637:		scanDuration				89.5%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:670:		pluralize				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:679:		renderBriefStatus			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:719:		renderSummaryDashboard			56.4%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:800:		renderFindings				88.9%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:829:		renderFinding				69.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:919:		renderGroupedFindings			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:943:		groupFindings				96.8%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1000:		severityRank				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1007:		isGitRepo				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1014:		getGitBlame				38.1%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1051:		parseGitBlame				95.2%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1087:		maskEmail				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1110:		getTopLevelDomain			75.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1122:		getHumanDisplayTitle			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1136:		wrapTitle				93.9%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1195:		maskFixForDisplay			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1230:		formatFixSection			0.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1295:		formatProposedSnippet			0.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1378:		limitHunkContext			64.7%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1454:		parseDiffHunk				91.7%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1476:		parseDiffLines				94.6%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1567:		findInlineChanges			73.5%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1638:		computeLCS				92.3%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1690:		buildTokenPositions			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1706:		tokenizeLine				92.9%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1734:		isWordChar				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1741:		formatDiffWithColorsStyled		77.1%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1815:		extractDiffFilename			80.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1837:		formatDiffHunkLine			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1857:		formatDiffContextLine			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1868:		formatDiffRemoveLine			86.4%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1909:		formatDiffAddLine			86.4%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1951:		applyInlineHighlights			81.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1993:		truncateDiffLine			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:2000:		truncateDiffLineWithFlag		66.7%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:2014:		adjustHighlightSpans			83.3%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:2036:		groupDiffHunks				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:2067:		collectRenderOps			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:2110:		renderChangeBlock			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:2169:		formatDiffHunkSeparator			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:2184:		formatValidationSection			0.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:2241:		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			83.3%
github.com/ArmisSecurity/armis-cli/internal/output/junit.go:88:			isFailureSeverity			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/junit.go:98:			convertToJUnitCasesWithSeverities	91.7%
github.com/ArmisSecurity/armis-cli/internal/output/junit.go:130:		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:55:		GetFormatter				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/output.go:71:		ShouldFail				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/output.go:89:		CheckExit				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:539:		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:23:		LoadIgnorePatterns			88.9%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/ignore.go:60:		loadIgnoreFile				88.5%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/ignore.go:105:		Match					100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/ignore.go:117:		shouldSkipDir				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					72.7%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:267:		tarGzDirectory				71.8%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:350:		isPathContained				75.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:359:		tarGzFiles				78.6%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:450:		safeAddSize				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:457:		calculateFilesSize			78.6%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:482:		calculateDirSize			79.2%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:526:		shouldSkip				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:557:		isTestFile				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:603:		isRetryableError			75.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:612:		buildScanResult				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:639:		convertNormalizedFindings		73.3%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:762:		shouldFilterByExploitability		100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:781:		cleanDescription			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:802:		generateFindingTitle			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:806:		isEmptyFinding				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)				77.6%

Replace separate install claude/copilot commands with a unified
`armis-cli install [editor...]` that downloads the plugin once and
registers it across all detected editors.

Supported editors: VS Code, Cursor, Windsurf, Zed, Cline, Amazon Q,
Continue, Claude Code. JetBrains requires per-project .jb-mcp.json.

Also auto-configures credentials from ARMIS_CLIENT_ID/ARMIS_CLIENT_SECRET
environment variables into the shared .env file (0600 permissions).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment thread internal/install/editors.go Fixed
Comment thread internal/install/editors.go Fixed
Comment thread internal/install/plugin.go Fixed
…nfig paths

Add Antigravity editor (~/.gemini/antigravity/mcp_config.json) with
standard mcpServers format. Update Amazon Q path from Application Support
to ~/.aws/amazonq/mcp.json to match current CLI. Migrate Continue from
deprecated config.json to ~/.continue/mcpServers/ directory-based
discovery, which works with both config.yaml and legacy config.json.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment thread internal/install/editors.go Fixed
Comment thread internal/install/editors.go Fixed
shb7628 and others added 2 commits April 24, 2026 15:26
Run gofmt to fix const block alignment. Skip Unix file permission
assertion on Windows where 0600 is not enforced.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use 0750 directory permissions in test to satisfy gosec G301. Add nosec
directive for G703 false positive on envPath which is internally
constructed from pluginDir + ".env".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment thread internal/install/plugin.go Fixed
Comment thread internal/install/plugin.go Fixed
Comment thread internal/install/plugin.go Fixed
Comment thread internal/install/plugin.go Fixed
Comment thread internal/install/plugin.go Fixed
Comment thread internal/install/plugin.go Fixed
Comment thread internal/install/editors.go Fixed
@shb7628 shb7628 requested a review from yiftach-armis April 27, 2026 18:37
}
base = filepath.Join(home, "Library", "Application Support")
case "linux":
base = os.Getenv("XDG_CONFIG_HOME")
base = filepath.Join(home, ".config")
}
case osWindows:
base = os.Getenv("APPDATA")

func readJSONFileAsMap(path string) map[string]interface{} {
data := make(map[string]interface{})
if b, err := os.ReadFile(filepath.Clean(path)); err == nil {
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