Signed-off-by: John Howard <jhoward@microsoft.com>
| ... | ... |
@@ -169,13 +169,13 @@ func (b *Builder) dispatch(stepN int, stepTotal int, ast *parser.Node) error {
|
| 169 | 169 |
var words []string |
| 170 | 170 |
|
| 171 | 171 |
if allowWordExpansion[cmd] {
|
| 172 |
- words, err = ProcessWords(str, envs) |
|
| 172 |
+ words, err = ProcessWords(str, envs, b.directive.EscapeToken) |
|
| 173 | 173 |
if err != nil {
|
| 174 | 174 |
return err |
| 175 | 175 |
} |
| 176 | 176 |
strList = append(strList, words...) |
| 177 | 177 |
} else {
|
| 178 |
- str, err = ProcessWord(str, envs) |
|
| 178 |
+ str, err = ProcessWord(str, envs, b.directive.EscapeToken) |
|
| 179 | 179 |
if err != nil {
|
| 180 | 180 |
return err |
| 181 | 181 |
} |
| ... | ... |
@@ -14,19 +14,21 @@ import ( |
| 14 | 14 |
) |
| 15 | 15 |
|
| 16 | 16 |
type shellWord struct {
|
| 17 |
- word string |
|
| 18 |
- scanner scanner.Scanner |
|
| 19 |
- envs []string |
|
| 20 |
- pos int |
|
| 17 |
+ word string |
|
| 18 |
+ scanner scanner.Scanner |
|
| 19 |
+ envs []string |
|
| 20 |
+ pos int |
|
| 21 |
+ escapeToken rune |
|
| 21 | 22 |
} |
| 22 | 23 |
|
| 23 | 24 |
// ProcessWord will use the 'env' list of environment variables, |
| 24 | 25 |
// and replace any env var references in 'word'. |
| 25 |
-func ProcessWord(word string, env []string) (string, error) {
|
|
| 26 |
+func ProcessWord(word string, env []string, escapeToken rune) (string, error) {
|
|
| 26 | 27 |
sw := &shellWord{
|
| 27 |
- word: word, |
|
| 28 |
- envs: env, |
|
| 29 |
- pos: 0, |
|
| 28 |
+ word: word, |
|
| 29 |
+ envs: env, |
|
| 30 |
+ pos: 0, |
|
| 31 |
+ escapeToken: escapeToken, |
|
| 30 | 32 |
} |
| 31 | 33 |
sw.scanner.Init(strings.NewReader(word)) |
| 32 | 34 |
word, _, err := sw.process() |
| ... | ... |
@@ -40,11 +42,12 @@ func ProcessWord(word string, env []string) (string, error) {
|
| 40 | 40 |
// this splitting is done **after** the env var substitutions are done. |
| 41 | 41 |
// Note, each one is trimmed to remove leading and trailing spaces (unless |
| 42 | 42 |
// they are quoted", but ProcessWord retains spaces between words. |
| 43 |
-func ProcessWords(word string, env []string) ([]string, error) {
|
|
| 43 |
+func ProcessWords(word string, env []string, escapeToken rune) ([]string, error) {
|
|
| 44 | 44 |
sw := &shellWord{
|
| 45 |
- word: word, |
|
| 46 |
- envs: env, |
|
| 47 |
- pos: 0, |
|
| 45 |
+ word: word, |
|
| 46 |
+ envs: env, |
|
| 47 |
+ pos: 0, |
|
| 48 |
+ escapeToken: escapeToken, |
|
| 48 | 49 |
} |
| 49 | 50 |
sw.scanner.Init(strings.NewReader(word)) |
| 50 | 51 |
_, words, err := sw.process() |
| ... | ... |
@@ -138,8 +141,8 @@ func (sw *shellWord) processStopOn(stopChar rune) (string, []string, error) {
|
| 138 | 138 |
// Not special, just add it to the result |
| 139 | 139 |
ch = sw.scanner.Next() |
| 140 | 140 |
|
| 141 |
- if ch == '\\' {
|
|
| 142 |
- // '\' escapes, except end of line |
|
| 141 |
+ if ch == sw.escapeToken {
|
|
| 142 |
+ // '\' (default escape token, but ` allowed) escapes, except end of line |
|
| 143 | 143 |
|
| 144 | 144 |
ch = sw.scanner.Next() |
| 145 | 145 |
|
| ... | ... |
@@ -179,7 +182,7 @@ func (sw *shellWord) processSingleQuote() (string, error) {
|
| 179 | 179 |
|
| 180 | 180 |
func (sw *shellWord) processDoubleQuote() (string, error) {
|
| 181 | 181 |
// All chars up to the next " are taken as-is, even ', except any $ chars |
| 182 |
- // But you can escape " with a \ |
|
| 182 |
+ // But you can escape " with a \ (or ` if escape token set accordingly) |
|
| 183 | 183 |
var result string |
| 184 | 184 |
|
| 185 | 185 |
sw.scanner.Next() |
| ... | ... |
@@ -198,7 +201,7 @@ func (sw *shellWord) processDoubleQuote() (string, error) {
|
| 198 | 198 |
result += tmp |
| 199 | 199 |
} else {
|
| 200 | 200 |
ch = sw.scanner.Next() |
| 201 |
- if ch == '\\' {
|
|
| 201 |
+ if ch == sw.escapeToken {
|
|
| 202 | 202 |
chNext := sw.scanner.Peek() |
| 203 | 203 |
|
| 204 | 204 |
if chNext == scanner.EOF {
|
| ... | ... |
@@ -40,7 +40,7 @@ func TestShellParser4EnvVars(t *testing.T) {
|
| 40 | 40 |
words[0] = strings.TrimSpace(words[0]) |
| 41 | 41 |
words[1] = strings.TrimSpace(words[1]) |
| 42 | 42 |
|
| 43 |
- newWord, err := ProcessWord(words[0], envs) |
|
| 43 |
+ newWord, err := ProcessWord(words[0], envs, '\\') |
|
| 44 | 44 |
|
| 45 | 45 |
if err != nil {
|
| 46 | 46 |
newWord = "error" |
| ... | ... |
@@ -83,7 +83,7 @@ func TestShellParser4Words(t *testing.T) {
|
| 83 | 83 |
test := strings.TrimSpace(words[0]) |
| 84 | 84 |
expected := strings.Split(strings.TrimLeft(words[1], " "), ",") |
| 85 | 85 |
|
| 86 |
- result, err := ProcessWords(test, envs) |
|
| 86 |
+ result, err := ProcessWords(test, envs, '\\') |
|
| 87 | 87 |
|
| 88 | 88 |
if err != nil {
|
| 89 | 89 |
result = []string{"error"}
|
| ... | ... |
@@ -6884,6 +6884,42 @@ func (s *DockerSuite) TestBuildShellWindowsPowershell(c *check.C) {
|
| 6884 | 6884 |
} |
| 6885 | 6885 |
} |
| 6886 | 6886 |
|
| 6887 |
+// Verify that escape is being correctly applied to words when escape directive is not \. |
|
| 6888 |
+// Tests WORKDIR, ADD |
|
| 6889 |
+func (s *DockerSuite) TestBuildEscapeNotBackslashWordTest(c *check.C) {
|
|
| 6890 |
+ testRequires(c, DaemonIsWindows) |
|
| 6891 |
+ name := "testbuildescapenotbackslashwordtesta" |
|
| 6892 |
+ _, out, err := buildImageWithOut(name, |
|
| 6893 |
+ `# escape= `+"`"+` |
|
| 6894 |
+ FROM `+minimalBaseImage()+` |
|
| 6895 |
+ WORKDIR c:\windows |
|
| 6896 |
+ RUN dir /w`, |
|
| 6897 |
+ true) |
|
| 6898 |
+ if err != nil {
|
|
| 6899 |
+ c.Fatal(err) |
|
| 6900 |
+ } |
|
| 6901 |
+ if !strings.Contains(strings.ToLower(out), "[system32]") {
|
|
| 6902 |
+ c.Fatalf("Line with '[windows]' not found in output %q", out)
|
|
| 6903 |
+ } |
|
| 6904 |
+ |
|
| 6905 |
+ name = "testbuildescapenotbackslashwordtestb" |
|
| 6906 |
+ _, out, err = buildImageWithOut(name, |
|
| 6907 |
+ `# escape= `+"`"+` |
|
| 6908 |
+ FROM `+minimalBaseImage()+` |
|
| 6909 |
+ SHELL ["powershell.exe"] |
|
| 6910 |
+ WORKDIR c:\foo |
|
| 6911 |
+ ADD Dockerfile c:\foo\ |
|
| 6912 |
+ RUN dir Dockerfile`, |
|
| 6913 |
+ true) |
|
| 6914 |
+ if err != nil {
|
|
| 6915 |
+ c.Fatal(err) |
|
| 6916 |
+ } |
|
| 6917 |
+ if !strings.Contains(strings.ToLower(out), "-a----") {
|
|
| 6918 |
+ c.Fatalf("Line with '-a----' not found in output %q", out)
|
|
| 6919 |
+ } |
|
| 6920 |
+ |
|
| 6921 |
+} |
|
| 6922 |
+ |
|
| 6887 | 6923 |
// #22868. Make sure shell-form CMD is marked as escaped in the config of the image |
| 6888 | 6924 |
func (s *DockerSuite) TestBuildCmdShellArgsEscaped(c *check.C) {
|
| 6889 | 6925 |
testRequires(c, DaemonIsWindows) |