Skip to content

Commit

Permalink
shell: handle empty string for var replacements
Browse files Browse the repository at this point in the history
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit de3e9c4)
  • Loading branch information
tonistiigi committed Jun 18, 2024
1 parent b45ab30 commit dedaef0
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 3 deletions.
3 changes: 2 additions & 1 deletion frontend/dockerfile/shell/lex.go
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,8 @@ func (sw *shellWord) processDollar() (string, error) {
case '%', '#':
// %/# matches the shortest pattern expansion, %%/## the longest
greedy := false
if word[0] == byte(ch) {

if len(word) > 0 && word[0] == byte(ch) {
greedy = true
word = word[1:]
}
Expand Down
107 changes: 105 additions & 2 deletions frontend/dockerfile/shell/lex_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,21 @@ func TestProcessWithMatches(t *testing.T) {
matches: map[string]struct{}{"FOO": {}, "BAR": {}},
unmatched: map[string]struct{}{"BAZ": {}},
},
{
input: "${FOO:-}",
envs: map[string]string{
"FOO": "xxx",
"BAR": "",
},
expected: "xxx",
matches: map[string]struct{}{"FOO": {}},
},
{
input: "${FOO:-}",
envs: map[string]string{},
expected: "",
unmatched: map[string]struct{}{"FOO": {}},
},

{
input: "${FOO+aaa} ${BAR+bbb} ${BAZ+ccc}",
Expand Down Expand Up @@ -388,6 +403,15 @@ func TestProcessWithMatches(t *testing.T) {
expectedErr: true,
unmatched: map[string]struct{}{"BAZ": {}},
},
{
input: "${BAZ:?}",
envs: map[string]string{
"FOO": "xxx",
"BAR": "",
},
expectedErr: true,
unmatched: map[string]struct{}{"BAZ": {}},
},

{
input: "${FOO=aaa}",
Expand All @@ -405,6 +429,11 @@ func TestProcessWithMatches(t *testing.T) {
},
expectedErr: true,
},
{
input: "${FOO=}",
envs: map[string]string{},
expectedErr: true,
},
{
// special characters in regular expressions
// } needs to be escaped so it doesn't match the
Expand All @@ -426,12 +455,42 @@ func TestProcessWithMatches(t *testing.T) {
expected: "y",
matches: map[string]struct{}{"FOO": {}},
},
{
input: "${FOO#*}",
envs: map[string]string{"FOO": "xxyy"},
expected: "xxyy",
matches: map[string]struct{}{"FOO": {}},
},
{
input: "${FOO#$BAR}",
envs: map[string]string{"FOO": "xxyy", "BAR": "x"},
expected: "xyy",
matches: map[string]struct{}{"FOO": {}, "BAR": {}},
},
{
input: "${FOO#$BAR}",
envs: map[string]string{"FOO": "xxyy", "BAR": ""},
expected: "xxyy",
matches: map[string]struct{}{"FOO": {}, "BAR": {}},
},
{
input: "${FOO#}",
envs: map[string]string{"FOO": "xxyy"},
expected: "xxyy",
matches: map[string]struct{}{"FOO": {}},
},
{
input: "${FOO##*x}",
envs: map[string]string{"FOO": "xxyy"},
expected: "yy",
matches: map[string]struct{}{"FOO": {}},
},
{
input: "${FOO##}",
envs: map[string]string{"FOO": "xxyy"},
expected: "xxyy",
matches: map[string]struct{}{"FOO": {}},
},
{
input: "${FOO#?\\?}",
envs: map[string]string{"FOO": "???y"},
Expand All @@ -451,6 +510,18 @@ func TestProcessWithMatches(t *testing.T) {
expected: "a",
matches: map[string]struct{}{"FOO": {}},
},
{
input: "${FOO%}",
envs: map[string]string{"FOO": "xxyy"},
expected: "xxyy",
matches: map[string]struct{}{"FOO": {}},
},
{
input: "${FOO%%$BAR}",
envs: map[string]string{"FOO": "xxyy", "BAR": ""},
expected: "xxyy",
matches: map[string]struct{}{"FOO": {}, "BAR": {}},
},
{
// test: wildcards
input: "${FOO/$NEEDLE/.} - ${FOO//$NEEDLE/.}",
Expand Down Expand Up @@ -484,6 +555,38 @@ func TestProcessWithMatches(t *testing.T) {
expected: "\\/tmp\\/foo.txt",
matches: map[string]struct{}{"FOO": {}},
},

// Following cases with empty/partial values are currently not
// guaranteed behavior. Tests are provided to make sure partial
// input does not cause runtime error.
{
input: "${FOO/$BAR/ww}",
envs: map[string]string{"FOO": "xxyy", "BAR": ""},
expected: "wwxxyy",
matches: map[string]struct{}{"FOO": {}, "BAR": {}},
},
{
input: "${FOO//ww}",
envs: map[string]string{"FOO": "xxyy"},
expectedErr: true,
},
{
input: "${FOO//}",
envs: map[string]string{"FOO": "xxyy"},
expectedErr: true,
},
{
input: "${FOO///}",
envs: map[string]string{"FOO": "xxyy"},
expected: "xxyy",
matches: map[string]struct{}{"FOO": {}},
},
{
input: "${FOO///}",
envs: map[string]string{},
expected: "",
unmatched: map[string]struct{}{"FOO": {}},
},
}

for _, c := range tc {
Expand All @@ -500,12 +603,12 @@ func TestProcessWithMatches(t *testing.T) {
require.NoError(t, err)
require.Equal(t, c.expected, w)

require.Equal(t, len(c.matches), len(matches))
require.Len(t, matches, len(c.matches), c.matches)
for k := range c.matches {
require.Contains(t, matches, k)
}

require.Equal(t, len(c.unmatched), len(unmatched))
require.Len(t, unmatched, len(c.unmatched), c.unmatched)
for k := range c.unmatched {
require.Contains(t, unmatched, k)
}
Expand Down

0 comments on commit dedaef0

Please sign in to comment.