security: fix command injection bypass in isAllowedCommand()#46
Merged
security: fix command injection bypass in isAllowedCommand()#46
Conversation
- Add shellInjectionPatterns guard to reject commands containing ;, \n,
backtick, $(, or ${ before any allowlist evaluation
- Replace strings.HasPrefix with exact first-token matching
(strings.Fields(command)[0]) to prevent prefix bypass attacks
- Fix operator precedence: check && before & and || before | (previously
dead code due to early HasPrefix return)
- Explicitly trim whitespace from each part before recursive calls
- Fix data bug in allowedCmdDefault: split "networksetup, git" into two
separate entries "networksetup" and "git"
- Add TestIsAllowedCommandInjection covering all five PoC attack vectors
plus legitimate command variants
Co-authored-by: cfc4n <709947+cfc4n@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Fix security vulnerability: command injection
security: fix command injection bypass in isAllowedCommand()
Mar 15, 2026
There was a problem hiding this comment.
Pull request overview
This PR fixes a command injection vulnerability in isAllowedCommand() where strings.HasPrefix() allowed bypassing the allowlist by appending shell metacharacters (;, $(), backticks, etc.) to whitelisted command prefixes.
Changes:
- Replace prefix-based allowlist matching with exact first-token matching and add a blocklist of shell injection patterns
- Fix a data bug where
"networksetup, git"was a single entry instead of two separate entries - Add test coverage for injection vectors and legitimate command patterns
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| pkg/services/command/command.go | Rewrite isAllowedCommand() with injection pattern blocklist, exact command-name matching, and correct operator precedence for &&/` |
| pkg/services/command/command_config.go | Split "networksetup, git" into separate slice entries |
| pkg/services/command/command_exec_test.go | Add TestIsAllowedCommandInjection covering injection vectors and legitimate commands |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
Comment on lines
200
to
208
| if strings.Contains(command, "&") { | ||
| parts := strings.Split(command, "&") | ||
| for _, part := range parts { | ||
| part = strings.TrimSpace(part) | ||
| if !cs.isAllowedCommand(part) { | ||
| if !cs.isAllowedCommand(strings.TrimSpace(part)) { | ||
| return false | ||
| } | ||
| } | ||
| return true | ||
| } |
|
|
||
| // shellInjectionPatterns are substrings that indicate potential command injection attempts. | ||
| // Commands containing any of these patterns are rejected before allowlist evaluation. | ||
| var shellInjectionPatterns = []string{";", "\n", "`", "$(", "${"} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
isAllowedCommand()usedstrings.HasPrefix()for allowlist validation, causing an immediatetruereturn on any prefix match — making the downstream|and&checks dead code and allowing arbitrary shell execution via;,$(), backticks, or\nappended to any whitelisted command prefix (e.g.echo x; id).Changes
command.go—isAllowedCommand()shellInjectionPatternsearly-exit guard rejecting any command containing;,\n,`,$(, or${before allowlist evaluationstrings.HasPrefix(command, allowed)with exact first-token matching: extractstrings.Fields(command)[0]and compare againststrings.TrimSpace(allowed)&&before&, and||before|; explicitly trim each part before recursive callscommand_config.go— Fix data bug:"networksetup, git"was a single slice entry; split into"networksetup"and"git"sogitis properly matched after the exact-name fixcommand_exec_test.go— AddTestIsAllowedCommandInjectioncovering all five PoC vectors (;,$(), backtick,\n,${}) as blocked, and legitimate pipe/&&/||commands as allowedWarning
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
ifconfig.me/usr/bin/curl curl ifconfig.me /home/REDACTED/go/pkg/mod/github.com/golangci/plugin-module-register@v0.1.1/register/register.go(dns block)/usr/bin/curl curl ifconfig.me /home/REDACTED/go/pkg/mod/github.com/santhosh-tekuri/jsonschema/v6@v6.0.1/kind/kind.go(dns block)If you need me to access, download, or install something from one of these locations, you can either:
Original prompt
This section details on the original issue you should resolve
<issue_title>security vulnerability: command injection</issue_title>
<issue_description>This vulnerability is found by Songwu security researcher,Zeyu Luo security researcher, Dr. CAO Yinfeng, Kevin(The Hong Kong Polytechnic University / HKCT Institute of Higher Education)
vulnerability description
In command.go line 150, strings.HasPrefix() returns true as soon as an allowlisted command prefix is matched, without validating shell metacharacters such as ;, $(), or backticks. Because the input is later executed via sh -c, the shell interprets the full command string, allowing any command appended after a semicolon to be executed.
poc