Skip to content

Commit deeae2d

Browse files
committed
improve flexibility of limactl shell cmd
Signed-off-by: olalekan odukoya <[email protected]>
1 parent 7dcdf6a commit deeae2d

File tree

3 files changed

+275
-56
lines changed

3 files changed

+275
-56
lines changed

hack/bats/tests/preserve-env.bats

Lines changed: 168 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,13 @@ local_setup() {
9393
assert_line BAR=bar
9494
}
9595

96-
@test 'wildcard does only work at the end of the pattern' {
96+
@test 'wildcard works at the start of the pattern' {
9797
export LIMA_SHELLENV_BLOCK="*FOO"
9898
export FOO=foo
9999
export BARFOO=barfoo
100100
run -0 limactl shell --preserve-env "$NAME" printenv
101-
assert_line FOO=foo
102-
assert_line BARFOO=barfoo
101+
refute_line --regexp '^BARFOO='
102+
refute_line --regexp '^FOO='
103103
}
104104

105105
@test 'block list can use a , separated list with whitespace ignored' {
@@ -114,16 +114,6 @@ local_setup() {
114114
assert_line BARBAZ=barbaz
115115
}
116116

117-
@test 'allow list overrides block list but blocks everything else' {
118-
export LIMA_SHELLENV_ALLOW=SSH_FOO
119-
export SSH_FOO=ssh_foo
120-
export SSH_BAR=ssh_bar
121-
export BAR=bar
122-
run -0 limactl shell --preserve-env "$NAME" printenv
123-
assert_line SSH_FOO=ssh_foo
124-
refute_line --regexp '^SSH_BAR='
125-
refute_line --regexp '^BAR='
126-
}
127117

128118
@test 'allow list can use a , separated list with whitespace ignored' {
129119
export LIMA_SHELLENV_ALLOW="FOO*, , BAR"
@@ -135,16 +125,174 @@ local_setup() {
135125
assert_line FOO=foo
136126
assert_line FOOBAR=foobar
137127
assert_line BAR=bar
138-
refute_line --regexp '^BARBAZ='
128+
assert_line BARBAZ=barbaz
139129
}
140130

141-
@test 'setting both allow list and block list generates a warning' {
142-
export LIMA_SHELLENV_ALLOW=FOO
143-
export LIMA_SHELLENV_BLOCK=BAR
131+
@test 'wildcard works in the middle of the pattern' {
132+
export LIMA_SHELLENV_BLOCK="FOO*BAR"
144133
export FOO=foo
145-
run -0 --separate-stderr limactl shell --preserve-env "$NAME" printenv FOO
146-
assert_output foo
147-
assert_stderr --regexp 'level=warning msg="Both LIMA_SHELLENV_BLOCK and LIMA_SHELLENV_ALLOW are set'
134+
export FOOBAR=foobar
135+
export FOOXYZBAR=fooxyzbar
136+
export FOOBAZ=foobaz
137+
export BAZBAR=bazbar
138+
run -0 limactl shell --preserve-env "$NAME" printenv
139+
refute_line --regexp '^FOOBAR='
140+
refute_line --regexp '^FOOXYZBAR='
141+
assert_line FOO=foo
142+
assert_line FOOBAZ=foobaz
143+
assert_line BAZBAR=bazbar
144+
}
145+
146+
@test 'multiple wildcards work in complex patterns' {
147+
export LIMA_SHELLENV_BLOCK="*FOO*BAR*"
148+
export FOO=foo
149+
export BAR=bar
150+
export FOOBAR=foobar
151+
export XFOOYBARZDOTCOM=xfooybarzdotcom
152+
export FOOBAZ=foobaz
153+
export BAZBAR=bazbar
154+
export UNRELATED=unrelated
155+
run -0 limactl shell --preserve-env "$NAME" printenv
156+
refute_line --regexp '^FOOBAR='
157+
refute_line --regexp '^XFOOYBARZDOTCOM='
158+
assert_line FOO=foo
159+
assert_line BAR=bar
160+
assert_line FOOBAZ=foobaz
161+
assert_line BAZBAR=bazbar
162+
assert_line UNRELATED=unrelated
163+
}
164+
165+
@test 'wildcards at beginning, middle, and end all work together' {
166+
export LIMA_SHELLENV_BLOCK="*TEST*,FOO*BAR,*SUFFIX"
167+
export PREFIX_TEST_VAR=prefix_test_var
168+
export FOOBAR=foobar
169+
export FOOXYZBAR=fooxyzbar
170+
export VAR_SUFFIX=var_suffix
171+
export NORMAL_VAR=normal_var
172+
run -0 limactl shell --preserve-env "$NAME" printenv
173+
refute_line --regexp '^PREFIX_TEST_VAR='
174+
refute_line --regexp '^FOOBAR='
175+
refute_line --regexp '^FOOXYZBAR='
176+
refute_line --regexp '^VAR_SUFFIX='
177+
assert_line NORMAL_VAR=normal_var
178+
}
179+
180+
@test 'complex allow/block interaction with default block list' {
181+
export LIMA_SHELLENV_ALLOW="SSH_FOO,CUSTOM*"
182+
export LIMA_SHELLENV_BLOCK="+*TOKEN"
183+
export SSH_FOO=ssh_foo
184+
export SSH_BAR=ssh_bar
185+
export CUSTOM_VAR=custom_var
186+
export MY_TOKEN=my_token
187+
export NORMAL_VAR=normal_var
188+
run -0 limactl shell --preserve-env "$NAME" printenv
189+
assert_line SSH_FOO=ssh_foo
190+
refute_line --regexp '^SSH_BAR='
191+
assert_line CUSTOM_VAR=custom_var
192+
refute_line --regexp '^MY_TOKEN='
193+
assert_line NORMAL_VAR=normal_var
194+
}
195+
196+
@test 'allow list with * block list blocks everything not explicitly allowed' {
197+
export LIMA_SHELLENV_ALLOW="FOO,BAR*"
198+
export LIMA_SHELLENV_BLOCK="*"
199+
export FOO=foo
200+
export BAR=bar
201+
export BARBAZ=barbaz
202+
export OTHER_VAR=other_var
203+
run -0 limactl shell --preserve-env "$NAME" printenv
204+
assert_line FOO=foo
205+
assert_line BAR=bar
206+
assert_line BARBAZ=barbaz
207+
refute_line --regexp '^OTHER_VAR='
208+
}
209+
210+
@test 'allow list supports wildcards in all positions' {
211+
export LIMA_SHELLENV_ALLOW="*PREFIX,MIDDLE*PATTERN,SUFFIX*"
212+
export LIMA_SHELLENV_BLOCK="*"
213+
export TEST_PREFIX=test_prefix
214+
export MIDDLE_TEST_PATTERN=middle_test_pattern
215+
export SUFFIX_TEST=suffix_test
216+
export OTHER=other
217+
run -0 limactl shell --preserve-env "$NAME" printenv
218+
assert_line TEST_PREFIX=test_prefix
219+
assert_line MIDDLE_TEST_PATTERN=middle_test_pattern
220+
assert_line SUFFIX_TEST=suffix_test
221+
refute_line --regexp '^OTHER='
222+
}
223+
224+
@test 'invalid characters in patterns cause fatal errors' {
225+
export LIMA_SHELLENV_BLOCK="FOO-BAR"
226+
run ! limactl shell --preserve-env "$NAME" printenv
227+
assert_output --partial "Invalid LIMA_SHELLENV_BLOCK pattern"
228+
assert_output --partial "contains invalid character"
229+
}
230+
231+
@test 'allow list with wildcards allows only matching patterns' {
232+
export LIMA_SHELLENV_ALLOW="FOO*,BAR"
233+
export FOO=foo
234+
export FOOBAR=foobar
235+
export BAR=bar
236+
export BARBAZ=barbaz
237+
export OTHER_VAR=OTHER_VAR
238+
run -0 limactl shell --preserve-env "$NAME" printenv
239+
assert_line FOO=foo
240+
assert_line FOOBAR=foobar
241+
assert_line BAR=bar
242+
assert_line BARBAZ=barbaz
243+
assert_line OTHER_VAR=OTHER_VAR
244+
}
245+
246+
@test 'allow list overrides block list entries but block list still applies to others' {
247+
export LIMA_SHELLENV_BLOCK="FOO,*TOKEN"
248+
export FOO=foo
249+
export MY_TOKEN=my_token
250+
export SECRET_TOKEN=secret_token
251+
export OTHER_VAR=other_var
252+
run -0 limactl shell --preserve-env "$NAME" printenv
253+
refute_line --regexp '^FOO='
254+
refute_line --regexp '^MY_TOKEN='
255+
refute_line --regexp '^SECRET_TOKEN='
256+
assert_line OTHER_VAR=other_var
257+
}
258+
259+
@test 'allow/block interaction with default block list' {
260+
export LIMA_SHELLENV_ALLOW="SSH_FOO,CUSTOM*"
261+
export LIMA_SHELLENV_BLOCK="+*TOKEN"
262+
export SSH_FOO=ssh_foo
263+
export SSH_BAR=ssh_bar
264+
export CUSTOM_VAR=custom_var
265+
export MY_TOKEN=my_token
266+
export NORMAL_VAR=normal_var
267+
run -0 limactl shell --preserve-env "$NAME" printenv
268+
assert_line SSH_FOO=ssh_foo
269+
refute_line --regexp '^SSH_BAR='
270+
assert_line CUSTOM_VAR=custom_var
271+
refute_line --regexp '^MY_TOKEN='
272+
assert_line NORMAL_VAR=normal_var
273+
}
274+
275+
@test 'universal block list blocks everything' {
276+
export LIMA_SHELLENV_BLOCK="*"
277+
export FOO=foo
278+
export BAR=bar
279+
export OTHER_VAR=other_var
280+
run -0 limactl shell --preserve-env "$NAME" printenv
281+
refute_line --regexp '^FOO='
282+
refute_line --regexp '^BAR='
283+
refute_line --regexp '^OTHER_VAR='
284+
}
285+
286+
@test 'universal block list blocks everything but with an allow' {
287+
export LIMA_SHELLENV_BLOCK="*"
288+
export LIMA_SHELLENV_ALLOW="FOO*"
289+
export FOO=foo
290+
export BAR=bar
291+
export OTHER_VAR=other_var
292+
run -0 limactl shell --preserve-env "$NAME" printenv
293+
assert_line FOO=foo
294+
refute_line --regexp '^BAR='
295+
refute_line --regexp '^OTHER_VAR='
148296
}
149297

150298
@test 'limactl info includes the default block list' {

pkg/envutil/envutil.go

Lines changed: 52 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
package envutil
55

66
import (
7+
"fmt"
78
"os"
9+
"regexp"
810
"slices"
911
"strings"
1012

@@ -39,30 +41,58 @@ var defaultBlockList = []string{
3941
"XDG_*",
4042
"ZDOTDIR",
4143
"ZSH*",
44+
"*PASSWORD*",
45+
"*TOKEN*",
46+
"*SECRET*",
4247
"_*", // Variables starting with underscore are typically internal
4348
}
4449

50+
func validatePattern(pattern string) error {
51+
invalidChar := regexp.MustCompile(`([^a-zA-Z0-9_*])`)
52+
if matches := invalidChar.FindStringSubmatch(pattern); matches != nil {
53+
invalidCharacter := matches[1]
54+
pos := strings.Index(pattern, invalidCharacter)
55+
return fmt.Errorf("pattern %q contains invalid character %q at position %d",
56+
pattern, invalidCharacter, pos)
57+
}
58+
return nil
59+
}
60+
4561
// getBlockList returns the list of environment variable patterns to be blocked.
46-
// The second return value indicates whether the list was explicitly set via LIMA_SHELLENV_BLOCK.
47-
func getBlockList() ([]string, bool) {
62+
func getBlockList() []string {
4863
blockEnv := os.Getenv("LIMA_SHELLENV_BLOCK")
4964
if blockEnv == "" {
50-
return defaultBlockList, false
65+
return defaultBlockList
66+
}
67+
68+
shouldAppend := strings.HasPrefix(blockEnv, "+")
69+
patterns := parseEnvList(strings.TrimPrefix(blockEnv, "+"))
70+
71+
for _, pattern := range patterns {
72+
if err := validatePattern(pattern); err != nil {
73+
logrus.Fatalf("Invalid LIMA_SHELLENV_BLOCK pattern: %v", err)
74+
}
5175
}
52-
after, found := strings.CutPrefix(blockEnv, "+")
53-
if !found {
54-
return parseEnvList(blockEnv), true
76+
77+
if shouldAppend {
78+
return slices.Concat(defaultBlockList, patterns)
5579
}
56-
return slices.Concat(defaultBlockList, parseEnvList(after)), true
80+
return patterns
5781
}
5882

5983
// getAllowList returns the list of environment variable patterns to be allowed.
60-
// The second return value indicates whether the list was explicitly set via LIMA_SHELLENV_ALLOW.
61-
func getAllowList() ([]string, bool) {
84+
func getAllowList() []string {
6285
if allowEnv := os.Getenv("LIMA_SHELLENV_ALLOW"); allowEnv != "" {
63-
return parseEnvList(allowEnv), true
86+
patterns := parseEnvList(allowEnv)
87+
88+
for _, pattern := range patterns {
89+
if err := validatePattern(pattern); err != nil {
90+
logrus.Fatalf("Invalid LIMA_SHELLENV_ALLOW pattern: %v", err)
91+
}
92+
}
93+
return patterns
6494
}
65-
return nil, false
95+
return nil
6696
}
6797

6898
func parseEnvList(envList string) []string {
@@ -82,8 +112,14 @@ func matchesPattern(name, pattern string) bool {
82112
return true
83113
}
84114

85-
prefix, found := strings.CutSuffix(pattern, "*")
86-
return found && strings.HasPrefix(name, prefix)
115+
regexPattern := strings.ReplaceAll(pattern, "*", ".*")
116+
regexPattern = "^" + regexPattern + "$"
117+
118+
match, err := regexp.MatchString(regexPattern, name)
119+
if err != nil {
120+
return false
121+
}
122+
return match
87123
}
88124

89125
func matchesAnyPattern(name string, patterns []string) bool {
@@ -96,17 +132,10 @@ func matchesAnyPattern(name string, patterns []string) bool {
96132
// It returns a slice of environment variables that are not blocked by the current configuration.
97133
// The filtering is controlled by LIMA_SHELLENV_BLOCK and LIMA_SHELLENV_ALLOW environment variables.
98134
func FilterEnvironment() []string {
99-
allowList, isAllowListSet := getAllowList()
100-
blockList, isBlockListSet := getBlockList()
101-
102-
if isBlockListSet && isAllowListSet {
103-
logrus.Warn("Both LIMA_SHELLENV_BLOCK and LIMA_SHELLENV_ALLOW are set. Block list will be ignored.")
104-
blockList = nil
105-
}
106135
return filterEnvironmentWithLists(
107136
os.Environ(),
108-
allowList,
109-
blockList,
137+
getAllowList(),
138+
getBlockList(),
110139
)
111140
}
112141

@@ -121,10 +150,7 @@ func filterEnvironmentWithLists(env, allowList, blockList []string) []string {
121150

122151
name := parts[0]
123152

124-
if len(allowList) > 0 {
125-
if !matchesAnyPattern(name, allowList) {
126-
continue
127-
}
153+
if len(allowList) > 0 && matchesAnyPattern(name, allowList) {
128154
filtered = append(filtered, envVar)
129155
continue
130156
}

0 commit comments

Comments
 (0)