mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-27 06:03:48 +03:00
caddyfile: Fix heredoc fuzz crasher, drop trailing newline (#5404)
Co-authored-by: Mohammed Al Sahaf <msaa1990@gmail.com>
This commit is contained in:
parent
960150bb03
commit
f3379f650a
3 changed files with 77 additions and 17 deletions
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue