caddyfile: Fix heredoc fuzz crasher, drop trailing newline (#5404)

Co-authored-by: Mohammed Al Sahaf <msaa1990@gmail.com>
This commit is contained in:
Francis Lavoie 2023-02-26 16:56:48 -05:00 committed by GitHub
parent 960150bb03
commit f3379f650a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 77 additions and 17 deletions

View file

@ -284,15 +284,17 @@ func (l *lexer) next() (bool, error) {
// and processes the text to strip leading whitespace, returning the final // and processes the text to strip leading whitespace, returning the final
// value without the leading whitespace. // value without the leading whitespace.
func (l *lexer) finalizeHeredoc(val []rune, marker string) ([]rune, error) { func (l *lexer) finalizeHeredoc(val []rune, marker string) ([]rune, error) {
stringVal := string(val)
// find the last newline of the heredoc, which is where the contents end // find the last newline of the heredoc, which is where the contents end
lastNewline := strings.LastIndex(string(val), "\n") lastNewline := strings.LastIndex(stringVal, "\n")
// collapse the content, then split into separate lines // collapse the content, then split into separate lines
lines := strings.Split(string(val[:lastNewline+1]), "\n") lines := strings.Split(stringVal[:lastNewline+1], "\n")
// figure out how much whitespace we need to strip from the front of every line // figure out how much whitespace we need to strip from the front of every line
// by getting the string that precedes the marker, on the last line // by getting the string that precedes the marker, on the last line
paddingToStrip := string(val[lastNewline+1 : len(val)-len(marker)]) paddingToStrip := stringVal[lastNewline+1 : len(stringVal)-len(marker)]
// iterate over each line and strip the whitespace from the front // iterate over each line and strip the whitespace from the front
var out string var out string
@ -310,6 +312,11 @@ func (l *lexer) finalizeHeredoc(val []rune, marker string) ([]rune, error) {
out += strings.ReplaceAll(lineText[len(paddingToStrip):]+"\n", "\r", "") out += strings.ReplaceAll(lineText[len(paddingToStrip):]+"\n", "\r", "")
} }
// Remove the trailing newline from the loop
if len(out) > 0 && out[len(out)-1] == '\n' {
out = out[:len(out)-1]
}
// return the final value // return the final value
return []rune(out), nil return []rune(out), nil
} }
@ -340,9 +347,9 @@ func (t Token) NumLineBreaks() int {
lineBreaks := strings.Count(t.Text, "\n") lineBreaks := strings.Count(t.Text, "\n")
if t.wasQuoted == '<' { if t.wasQuoted == '<' {
// heredocs have an extra linebreak because the opening // heredocs have an extra linebreak because the opening
// delimiter is on its own line and is not included in // delimiter is on its own line and is not included in the
// the token Text itself // token Text itself, and the trailing newline is removed.
lineBreaks++ lineBreaks += 2
} }
return lineBreaks return lineBreaks
} }

View file

@ -256,7 +256,7 @@ EOF same-line-arg
`), `),
expected: []Token{ expected: []Token{
{Line: 1, Text: `heredoc`}, {Line: 1, Text: `heredoc`},
{Line: 1, Text: "content\n"}, {Line: 1, Text: "content"},
{Line: 3, Text: `same-line-arg`}, {Line: 3, Text: `same-line-arg`},
}, },
}, },
@ -267,18 +267,40 @@ VERY-LONG-MARKER same-line-arg
`), `),
expected: []Token{ expected: []Token{
{Line: 1, Text: `heredoc`}, {Line: 1, Text: `heredoc`},
{Line: 1, Text: "content\n"}, {Line: 1, Text: "content"},
{Line: 3, Text: `same-line-arg`}, {Line: 3, Text: `same-line-arg`},
}, },
}, },
{ {
input: []byte(`heredoc <<EOF input: []byte(`heredoc <<EOF
extra-newline
EOF same-line-arg
`),
expected: []Token{
{Line: 1, Text: `heredoc`},
{Line: 1, Text: "extra-newline\n"},
{Line: 4, Text: `same-line-arg`},
},
},
{
input: []byte(`heredoc <<EOF
EOF same-line-arg
`),
expected: []Token{
{Line: 1, Text: `heredoc`},
{Line: 1, Text: ""},
{Line: 2, Text: `same-line-arg`},
},
},
{
input: []byte(`heredoc <<EOF
content content
EOF same-line-arg EOF same-line-arg
`), `),
expected: []Token{ expected: []Token{
{Line: 1, Text: `heredoc`}, {Line: 1, Text: `heredoc`},
{Line: 1, Text: "content\n"}, {Line: 1, Text: "content"},
{Line: 3, Text: `same-line-arg`}, {Line: 3, Text: `same-line-arg`},
}, },
}, },
@ -294,7 +316,7 @@ VERY-LONG-MARKER same-line-arg
expected: []Token{ expected: []Token{
{Line: 1, Text: `prev-line`}, {Line: 1, Text: `prev-line`},
{Line: 2, Text: `heredoc`}, {Line: 2, Text: `heredoc`},
{Line: 2, Text: "\tmulti\n\tline\n\tcontent\n"}, {Line: 2, Text: "\tmulti\n\tline\n\tcontent"},
{Line: 6, Text: `same-line-arg`}, {Line: 6, Text: `same-line-arg`},
{Line: 7, Text: `next-line`}, {Line: 7, Text: `next-line`},
}, },
@ -312,6 +334,37 @@ VERY-LONG-MARKER same-line-arg
{Line: 3, Text: `same-line-arg`}, {Line: 3, Text: `same-line-arg`},
}, },
}, },
{
input: []byte(`heredoc <<s
<EFBFBD>
s
`),
expected: []Token{
{Line: 1, Text: `heredoc`},
{Line: 1, Text: "<22>"},
},
},
{
input: []byte("\u000Aheredoc \u003C\u003C\u0073\u0073\u000A\u00BF\u0057\u0001\u0000\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u003D\u001F\u000A\u0073\u0073\u000A\u00BF\u0057\u0001\u0000\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u003D\u001F\u000A\u00BF\u00BF\u0057\u0001\u0000\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u003D\u001F"),
expected: []Token{
{
Line: 2,
Text: "heredoc",
},
{
Line: 2,
Text: "\u00BF\u0057\u0001\u0000\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u003D\u001F",
},
{
Line: 5,
Text: "\u00BF\u0057\u0001\u0000\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u003D\u001F",
},
{
Line: 6,
Text: "\u00BF\u00BF\u0057\u0001\u0000\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u003D\u001F",
},
},
},
{ {
input: []byte(`heredoc <<HERE SAME LINE input: []byte(`heredoc <<HERE SAME LINE
content content
@ -357,17 +410,17 @@ VERY-LONG-MARKER same-line-arg
actual, err := Tokenize(testCase.input, "") actual, err := Tokenize(testCase.input, "")
if testCase.expectErr { if testCase.expectErr {
if err == nil { if err == nil {
t.Errorf("expected error, got actual: %v", actual) t.Fatalf("expected error, got actual: %v", actual)
continue continue
} }
if err.Error() != testCase.errorMessage { if err.Error() != testCase.errorMessage {
t.Errorf("expected error '%v', got: %v", testCase.errorMessage, err) t.Fatalf("expected error '%v', got: %v", testCase.errorMessage, err)
} }
continue continue
} }
if err != nil { if err != nil {
t.Errorf("%v", err) t.Fatalf("%v", err)
} }
lexerCompare(t, i, testCase.expected, actual) lexerCompare(t, i, testCase.expected, actual)
} }
@ -375,17 +428,17 @@ VERY-LONG-MARKER same-line-arg
func lexerCompare(t *testing.T, n int, expected, actual []Token) { func lexerCompare(t *testing.T, n int, expected, actual []Token) {
if len(expected) != len(actual) { if len(expected) != len(actual) {
t.Errorf("Test case %d: expected %d token(s) but got %d", n, len(expected), len(actual)) t.Fatalf("Test case %d: expected %d token(s) but got %d", n, len(expected), len(actual))
} }
for i := 0; i < len(actual) && i < len(expected); i++ { for i := 0; i < len(actual) && i < len(expected); i++ {
if actual[i].Line != expected[i].Line { if actual[i].Line != expected[i].Line {
t.Errorf("Test case %d token %d ('%s'): expected line %d but was line %d", t.Fatalf("Test case %d token %d ('%s'): expected line %d but was line %d",
n, i, expected[i].Text, expected[i].Line, actual[i].Line) n, i, expected[i].Text, expected[i].Line, actual[i].Line)
break break
} }
if actual[i].Text != expected[i].Text { if actual[i].Text != expected[i].Text {
t.Errorf("Test case %d token %d: expected text '%s' but was '%s'", t.Fatalf("Test case %d token %d: expected text '%s' but was '%s'",
n, i, expected[i].Text, actual[i].Text) n, i, expected[i].Text, actual[i].Text)
break break
} }

View file

@ -31,7 +31,7 @@ example.com {
{ {
"handle": [ "handle": [
{ {
"body": "\u003chtml\u003e\n \u003chead\u003e\u003ctitle\u003eFoo\u003c/title\u003e\n \u003cbody\u003eFoo\u003c/body\u003e\n\u003c/html\u003e\n", "body": "\u003chtml\u003e\n \u003chead\u003e\u003ctitle\u003eFoo\u003c/title\u003e\n \u003cbody\u003eFoo\u003c/body\u003e\n\u003c/html\u003e",
"handler": "static_response", "handler": "static_response",
"status_code": 200 "status_code": 200
} }