From cc7a3ded6f9495da1d61c4f06453e1cbecfd02bd Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 7 Jul 2020 13:44:19 +0200 Subject: [PATCH] Dockerfile: fix parsing of trailing backslash If the line-continuation marker (`\`) is escaped, it should not be treated as such, but as a literal backslash. Signed-off-by: Sebastiaan van Stijn --- frontend/dockerfile/parser/parser.go | 20 +++++++++++++++---- .../testfiles/trailing-backslash/Dockerfile | 17 ++++++++++++++++ .../testfiles/trailing-backslash/result | 8 ++++++++ 3 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 frontend/dockerfile/parser/testfiles/trailing-backslash/Dockerfile create mode 100644 frontend/dockerfile/parser/testfiles/trailing-backslash/result diff --git a/frontend/dockerfile/parser/parser.go b/frontend/dockerfile/parser/parser.go index 465eebe44edd..1e30e208bf99 100644 --- a/frontend/dockerfile/parser/parser.go +++ b/frontend/dockerfile/parser/parser.go @@ -108,13 +108,25 @@ type directives struct { seen map[string]struct{} // Whether the escape directive has been seen } -// setEscapeToken sets the default token for escaping characters in a Dockerfile. +// setEscapeToken sets the default token for escaping characters and as line- +// continuation token in a Dockerfile. Only ` (backtick) and \ (backslash) are +// allowed as token. func (d *directives) setEscapeToken(s string) error { - if s != "`" && s != "\\" { + if s != "`" && s != `\` { return errors.Errorf("invalid escape token '%s' does not match ` or \\", s) } d.escapeToken = rune(s[0]) - d.lineContinuationRegex = regexp.MustCompile(`\` + s + `[ \t]*$`) + // The escape token is used both to escape characters in a line and as line + // continuation token. If it's the last non-whitespace token, it is used as + // line-continuation token, *unless* preceded by an escape-token. + // + // The second branch in the regular expression handles line-continuation + // tokens on their own line, which don't have any character preceding them. + // + // Due to Go lacking negative look-ahead matching, this regular expression + // does not currently handle a line-continuation token preceded by an *escaped* + // escape-token ("foo \\\"). + d.lineContinuationRegex = regexp.MustCompile(`([^\` + s + `])\` + s + `[ \t]*$|^\` + s + `[ \t]*$`) return nil } @@ -339,7 +351,7 @@ var utf8bom = []byte{0xEF, 0xBB, 0xBF} func trimContinuationCharacter(line string, d *directives) (string, bool) { if d.lineContinuationRegex.MatchString(line) { - line = d.lineContinuationRegex.ReplaceAllString(line, "") + line = d.lineContinuationRegex.ReplaceAllString(line, "$1") return line, false } return line, true diff --git a/frontend/dockerfile/parser/testfiles/trailing-backslash/Dockerfile b/frontend/dockerfile/parser/testfiles/trailing-backslash/Dockerfile new file mode 100644 index 000000000000..90c651006482 --- /dev/null +++ b/frontend/dockerfile/parser/testfiles/trailing-backslash/Dockerfile @@ -0,0 +1,17 @@ +# https://github.com/docker/for-win/issues/5254 + +FROM hello-world + +ENV A path +ENV B another\\path +ENV C trailing\\backslash\\ +ENV D This should not be appended to C +ENV E hello\ +\ +world +ENV F hello\ + \ +world +ENV G hello \ +\ +world diff --git a/frontend/dockerfile/parser/testfiles/trailing-backslash/result b/frontend/dockerfile/parser/testfiles/trailing-backslash/result new file mode 100644 index 000000000000..bb6757c79a46 --- /dev/null +++ b/frontend/dockerfile/parser/testfiles/trailing-backslash/result @@ -0,0 +1,8 @@ +(from "hello-world") +(env "A" "path") +(env "B" "another\\\\path") +(env "C" "trailing\\\\backslash\\\\") +(env "D" "This should not be appended to C") +(env "E" "helloworld") +(env "F" "hello world") +(env "G" "hello world")