Skip to content

Commit

Permalink
Merge pull request #834 from cvgw/u/cgwippern/ISSUE_439_strip_arg_quotes
Browse files Browse the repository at this point in the history
Issue #439 Strip out double quotes in ARG value
  • Loading branch information
tejal29 authored Nov 7, 2019
2 parents 56e048e + 0ce287d commit 5bbb40e
Show file tree
Hide file tree
Showing 6 changed files with 222 additions and 0 deletions.
12 changes: 12 additions & 0 deletions integration/dockerfiles/Dockerfile_test_arg_blank_with_quotes
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
ARG FILE_NAME=""

FROM busybox:latest AS builder
ARG FILE_NAME

RUN echo $FILE_NAME && touch /$FILE_NAME.txt && stat /$FILE_NAME.txt;

FROM busybox:latest
ARG FILE_NAME

RUN echo $FILE_NAME && touch /$FILE_NAME.txt && stat /$FILE_NAME.txt;
COPY --from=builder /$FILE_NAME.txt /
3 changes: 3 additions & 0 deletions integration/dockerfiles/Dockerfile_test_arg_from_quotes
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ARG IMAGE_NAME="busybox:latest"

FROM $IMAGE_NAME
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ARG IMAGE_NAME='busybox:latest'

FROM $IMAGE_NAME
12 changes: 12 additions & 0 deletions integration/dockerfiles/Dockerfile_test_arg_multi_with_quotes
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
ARG FILE_NAME="myFile"

FROM busybox:latest AS builder
ARG FILE_NAME

RUN echo $FILE_NAME && touch /$FILE_NAME.txt && stat /$FILE_NAME.txt;

FROM busybox:latest
ARG FILE_NAME

RUN echo $FILE_NAME && touch /$FILE_NAME.txt && stat /$FILE_NAME.txt;
COPY --from=builder /$FILE_NAME.txt /
35 changes: 35 additions & 0 deletions pkg/dockerfile/dockerfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,44 @@ func Parse(b []byte) ([]instructions.Stage, []instructions.ArgCommand, error) {
if err != nil {
return nil, nil, err
}

metaArgs, err = stripEnclosingQuotes(metaArgs)
if err != nil {
return nil, nil, err
}

return stages, metaArgs, nil
}

// stripEnclosingQuotes removes quotes enclosing the value of each instructions.ArgCommand in a slice
// if the quotes are escaped it leaves them
func stripEnclosingQuotes(metaArgs []instructions.ArgCommand) ([]instructions.ArgCommand, error) {
dbl := byte('"')
sgl := byte('\'')

for i := range metaArgs {
arg := metaArgs[i]
v := arg.Value
if v != nil {
val := *v
fmt.Printf("val %s\n", val)
if (val[0] == dbl && val[len(val)-1] == dbl) || (val[0] == sgl && val[len(val)-1] == sgl) {
val = val[1 : len(val)-1]
} else if val[:2] == `\"` && val[len(val)-2:] == `\"` {
continue
} else if val[:2] == `\'` && val[len(val)-2:] == `\'` {
continue
} else if val[0] == dbl || val[0] == sgl || val[len(val)-1] == dbl || val[len(val)-1] == sgl {
return nil, errors.New("quotes wrapping arg values must be matched")
}

arg.Value = &val
metaArgs[i] = arg
}
}
return metaArgs, nil
}

// targetStage returns the index of the target stage kaniko is trying to build
func targetStage(stages []instructions.Stage, target string) (int, error) {
if target == "" {
Expand Down
157 changes: 157 additions & 0 deletions pkg/dockerfile/dockerfile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,170 @@ limitations under the License.
package dockerfile

import (
"io/ioutil"
"os"
"strconv"
"testing"

"github.com/GoogleContainerTools/kaniko/pkg/config"
"github.com/GoogleContainerTools/kaniko/testutil"
"github.com/moby/buildkit/frontend/dockerfile/instructions"
)

func Test_Stages_ArgValueWithQuotes(t *testing.T) {
dockerfile := `
ARG IMAGE="ubuntu:16.04"
FROM ${IMAGE}
RUN echo hi > /hi
FROM scratch AS second
COPY --from=0 /hi /hi2
FROM scratch
COPY --from=second /hi2 /hi3
`
tmpfile, err := ioutil.TempFile("", "Dockerfile.test")
if err != nil {
t.Fatal(err)
}

defer os.Remove(tmpfile.Name())

if _, err := tmpfile.Write([]byte(dockerfile)); err != nil {
t.Fatal(err)
}
if err := tmpfile.Close(); err != nil {
t.Fatal(err)
}

stages, err := Stages(&config.KanikoOptions{DockerfilePath: tmpfile.Name()})
if err != nil {
t.Fatal(err)
}

if len(stages) == 0 {
t.Fatal("length of stages expected to be greater than zero, but was zero")

}

if len(stages[0].MetaArgs) == 0 {
t.Fatal("length of stage[0] meta args expected to be greater than zero, but was zero")
}

expectedVal := "ubuntu:16.04"

arg := stages[0].MetaArgs[0]
if arg.ValueString() != expectedVal {
t.Fatalf("expected stages[0].MetaArgs[0] val to be %s but was %s", expectedVal, arg.ValueString())
}
}

func Test_stripEnclosingQuotes(t *testing.T) {
type testCase struct {
name string
inArgs []instructions.ArgCommand
expected []string
success bool
}

newArgCommand := func(key, val string) instructions.ArgCommand {
return instructions.ArgCommand{
KeyValuePairOptional: instructions.KeyValuePairOptional{Key: key, Value: &val},
}
}

cases := []testCase{{
name: "value with no enclosing quotes",
inArgs: []instructions.ArgCommand{newArgCommand("MEOW", "Purr")},
expected: []string{"Purr"},
success: true,
}, {
name: "value with unmatched leading double quote",
inArgs: []instructions.ArgCommand{newArgCommand("MEOW", "\"Purr")},
}, {
name: "value with unmatched trailing double quote",
inArgs: []instructions.ArgCommand{newArgCommand("MEOW", "Purr\"")},
}, {
name: "value with enclosing double quotes",
inArgs: []instructions.ArgCommand{newArgCommand("MEOW", "\"mrow\"")},
expected: []string{"mrow"},
success: true,
}, {
name: "value with unmatched leading single quote",
inArgs: []instructions.ArgCommand{newArgCommand("MEOW", "'Purr")},
}, {
name: "value with unmatched trailing single quote",
inArgs: []instructions.ArgCommand{newArgCommand("MEOW", "Purr'")},
}, {
name: "value with enclosing single quotes",
inArgs: []instructions.ArgCommand{newArgCommand("MEOW", "'mrow'")},
expected: []string{"mrow"},
success: true,
}, {
name: "blank value with enclosing double quotes",
inArgs: []instructions.ArgCommand{newArgCommand("MEOW", "\"\"")},
expected: []string{""},
success: true,
}, {
name: "blank value with enclosing single quotes",
inArgs: []instructions.ArgCommand{newArgCommand("MEOW", "''")},
expected: []string{""},
success: true,
}, {
name: "value with escaped, enclosing double quotes",
inArgs: []instructions.ArgCommand{newArgCommand("MEOW", `\"Purr\"`)},
expected: []string{`\"Purr\"`},
success: true,
}, {
name: "value with escaped, enclosing single quotes",
inArgs: []instructions.ArgCommand{newArgCommand("MEOW", `\'Purr\'`)},
expected: []string{`\'Purr\'`},
success: true,
}, {
name: "multiple values enclosed with single quotes",
inArgs: []instructions.ArgCommand{
newArgCommand("MEOW", `'Purr'`),
newArgCommand("MEW", `'Mrow'`),
},
expected: []string{"Purr", "Mrow"},
success: true,
}, {
name: "no values",
success: true,
}}

for _, test := range cases {
t.Run(test.name, func(t *testing.T) {
inArgs := test.inArgs
expected := test.expected
success := test.success

out, err := stripEnclosingQuotes(inArgs)
if success && err != nil {
t.Fatal(err)
}

if !success && err == nil {
t.Fatal("expected an error but none received")
}

if len(expected) != len(out) {
t.Fatalf("Expected %d args but got %d", len(expected), len(out))
}

for i := range out {
if expected[i] != out[i].ValueString() {
t.Errorf(
"Expected arg at index %d to equal %v but instead equaled %v",
i,
expected[i],
out[i].ValueString())
}
}
})
}
}

func Test_resolveStages(t *testing.T) {
dockerfile := `
FROM scratch
Expand Down

0 comments on commit 5bbb40e

Please sign in to comment.