mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Windows: Honour escape directive fully
Signed-off-by: John Howard <jhoward@microsoft.com>
This commit is contained in:
parent
bc52939b04
commit
9127818414
4 changed files with 59 additions and 20 deletions
|
@ -169,13 +169,13 @@ func (b *Builder) dispatch(stepN int, stepTotal int, ast *parser.Node) error {
|
||||||
var words []string
|
var words []string
|
||||||
|
|
||||||
if allowWordExpansion[cmd] {
|
if allowWordExpansion[cmd] {
|
||||||
words, err = ProcessWords(str, envs)
|
words, err = ProcessWords(str, envs, b.directive.EscapeToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
strList = append(strList, words...)
|
strList = append(strList, words...)
|
||||||
} else {
|
} else {
|
||||||
str, err = ProcessWord(str, envs)
|
str, err = ProcessWord(str, envs, b.directive.EscapeToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,15 +18,17 @@ type shellWord struct {
|
||||||
scanner scanner.Scanner
|
scanner scanner.Scanner
|
||||||
envs []string
|
envs []string
|
||||||
pos int
|
pos int
|
||||||
|
escapeToken rune
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProcessWord will use the 'env' list of environment variables,
|
// ProcessWord will use the 'env' list of environment variables,
|
||||||
// and replace any env var references in 'word'.
|
// and replace any env var references in 'word'.
|
||||||
func ProcessWord(word string, env []string) (string, error) {
|
func ProcessWord(word string, env []string, escapeToken rune) (string, error) {
|
||||||
sw := &shellWord{
|
sw := &shellWord{
|
||||||
word: word,
|
word: word,
|
||||||
envs: env,
|
envs: env,
|
||||||
pos: 0,
|
pos: 0,
|
||||||
|
escapeToken: escapeToken,
|
||||||
}
|
}
|
||||||
sw.scanner.Init(strings.NewReader(word))
|
sw.scanner.Init(strings.NewReader(word))
|
||||||
word, _, err := sw.process()
|
word, _, err := sw.process()
|
||||||
|
@ -40,11 +42,12 @@ func ProcessWord(word string, env []string) (string, error) {
|
||||||
// this splitting is done **after** the env var substitutions are done.
|
// this splitting is done **after** the env var substitutions are done.
|
||||||
// Note, each one is trimmed to remove leading and trailing spaces (unless
|
// Note, each one is trimmed to remove leading and trailing spaces (unless
|
||||||
// they are quoted", but ProcessWord retains spaces between words.
|
// they are quoted", but ProcessWord retains spaces between words.
|
||||||
func ProcessWords(word string, env []string) ([]string, error) {
|
func ProcessWords(word string, env []string, escapeToken rune) ([]string, error) {
|
||||||
sw := &shellWord{
|
sw := &shellWord{
|
||||||
word: word,
|
word: word,
|
||||||
envs: env,
|
envs: env,
|
||||||
pos: 0,
|
pos: 0,
|
||||||
|
escapeToken: escapeToken,
|
||||||
}
|
}
|
||||||
sw.scanner.Init(strings.NewReader(word))
|
sw.scanner.Init(strings.NewReader(word))
|
||||||
_, words, err := sw.process()
|
_, words, err := sw.process()
|
||||||
|
@ -138,8 +141,8 @@ func (sw *shellWord) processStopOn(stopChar rune) (string, []string, error) {
|
||||||
// Not special, just add it to the result
|
// Not special, just add it to the result
|
||||||
ch = sw.scanner.Next()
|
ch = sw.scanner.Next()
|
||||||
|
|
||||||
if ch == '\\' {
|
if ch == sw.escapeToken {
|
||||||
// '\' escapes, except end of line
|
// '\' (default escape token, but ` allowed) escapes, except end of line
|
||||||
|
|
||||||
ch = sw.scanner.Next()
|
ch = sw.scanner.Next()
|
||||||
|
|
||||||
|
@ -179,7 +182,7 @@ func (sw *shellWord) processSingleQuote() (string, error) {
|
||||||
|
|
||||||
func (sw *shellWord) processDoubleQuote() (string, error) {
|
func (sw *shellWord) processDoubleQuote() (string, error) {
|
||||||
// All chars up to the next " are taken as-is, even ', except any $ chars
|
// All chars up to the next " are taken as-is, even ', except any $ chars
|
||||||
// But you can escape " with a \
|
// But you can escape " with a \ (or ` if escape token set accordingly)
|
||||||
var result string
|
var result string
|
||||||
|
|
||||||
sw.scanner.Next()
|
sw.scanner.Next()
|
||||||
|
@ -198,7 +201,7 @@ func (sw *shellWord) processDoubleQuote() (string, error) {
|
||||||
result += tmp
|
result += tmp
|
||||||
} else {
|
} else {
|
||||||
ch = sw.scanner.Next()
|
ch = sw.scanner.Next()
|
||||||
if ch == '\\' {
|
if ch == sw.escapeToken {
|
||||||
chNext := sw.scanner.Peek()
|
chNext := sw.scanner.Peek()
|
||||||
|
|
||||||
if chNext == scanner.EOF {
|
if chNext == scanner.EOF {
|
||||||
|
|
|
@ -40,7 +40,7 @@ func TestShellParser4EnvVars(t *testing.T) {
|
||||||
words[0] = strings.TrimSpace(words[0])
|
words[0] = strings.TrimSpace(words[0])
|
||||||
words[1] = strings.TrimSpace(words[1])
|
words[1] = strings.TrimSpace(words[1])
|
||||||
|
|
||||||
newWord, err := ProcessWord(words[0], envs)
|
newWord, err := ProcessWord(words[0], envs, '\\')
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
newWord = "error"
|
newWord = "error"
|
||||||
|
@ -83,7 +83,7 @@ func TestShellParser4Words(t *testing.T) {
|
||||||
test := strings.TrimSpace(words[0])
|
test := strings.TrimSpace(words[0])
|
||||||
expected := strings.Split(strings.TrimLeft(words[1], " "), ",")
|
expected := strings.Split(strings.TrimLeft(words[1], " "), ",")
|
||||||
|
|
||||||
result, err := ProcessWords(test, envs)
|
result, err := ProcessWords(test, envs, '\\')
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
result = []string{"error"}
|
result = []string{"error"}
|
||||||
|
|
|
@ -6884,6 +6884,42 @@ func (s *DockerSuite) TestBuildShellWindowsPowershell(c *check.C) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify that escape is being correctly applied to words when escape directive is not \.
|
||||||
|
// Tests WORKDIR, ADD
|
||||||
|
func (s *DockerSuite) TestBuildEscapeNotBackslashWordTest(c *check.C) {
|
||||||
|
testRequires(c, DaemonIsWindows)
|
||||||
|
name := "testbuildescapenotbackslashwordtesta"
|
||||||
|
_, out, err := buildImageWithOut(name,
|
||||||
|
`# escape= `+"`"+`
|
||||||
|
FROM `+minimalBaseImage()+`
|
||||||
|
WORKDIR c:\windows
|
||||||
|
RUN dir /w`,
|
||||||
|
true)
|
||||||
|
if err != nil {
|
||||||
|
c.Fatal(err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(strings.ToLower(out), "[system32]") {
|
||||||
|
c.Fatalf("Line with '[windows]' not found in output %q", out)
|
||||||
|
}
|
||||||
|
|
||||||
|
name = "testbuildescapenotbackslashwordtestb"
|
||||||
|
_, out, err = buildImageWithOut(name,
|
||||||
|
`# escape= `+"`"+`
|
||||||
|
FROM `+minimalBaseImage()+`
|
||||||
|
SHELL ["powershell.exe"]
|
||||||
|
WORKDIR c:\foo
|
||||||
|
ADD Dockerfile c:\foo\
|
||||||
|
RUN dir Dockerfile`,
|
||||||
|
true)
|
||||||
|
if err != nil {
|
||||||
|
c.Fatal(err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(strings.ToLower(out), "-a----") {
|
||||||
|
c.Fatalf("Line with '-a----' not found in output %q", out)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// #22868. Make sure shell-form CMD is marked as escaped in the config of the image
|
// #22868. Make sure shell-form CMD is marked as escaped in the config of the image
|
||||||
func (s *DockerSuite) TestBuildCmdShellArgsEscaped(c *check.C) {
|
func (s *DockerSuite) TestBuildCmdShellArgsEscaped(c *check.C) {
|
||||||
testRequires(c, DaemonIsWindows)
|
testRequires(c, DaemonIsWindows)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue