diff options
author | Francis Lavoie <lavofr@gmail.com> | 2023-02-26 16:56:48 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-26 16:56:48 -0500 |
commit | f3379f650a3de0e83903e3aa34d39b4ec647ee50 (patch) | |
tree | fc605900a78bca21e6628c04d8e0d936b8316da2 /caddyconfig | |
parent | 960150bb034dc9a549ee7289b1a4eb4abafeb30a (diff) |
caddyfile: Fix heredoc fuzz crasher, drop trailing newline (#5404)
Co-authored-by: Mohammed Al Sahaf <msaa1990@gmail.com>
Diffstat (limited to 'caddyconfig')
-rw-r--r-- | caddyconfig/caddyfile/lexer.go | 19 | ||||
-rw-r--r-- | caddyconfig/caddyfile/lexer_test.go | 73 |
2 files changed, 76 insertions, 16 deletions
diff --git a/caddyconfig/caddyfile/lexer.go b/caddyconfig/caddyfile/lexer.go index ba8b879..1f531f4 100644 --- a/caddyconfig/caddyfile/lexer.go +++ b/caddyconfig/caddyfile/lexer.go @@ -284,15 +284,17 @@ func (l *lexer) next() (bool, error) { // and processes the text to strip leading whitespace, returning the final // value without the leading whitespace. 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 - lastNewline := strings.LastIndex(string(val), "\n") + lastNewline := strings.LastIndex(stringVal, "\n") // 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 // 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 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", "") } + // 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 []rune(out), nil } @@ -340,9 +347,9 @@ func (t Token) NumLineBreaks() int { lineBreaks := strings.Count(t.Text, "\n") if t.wasQuoted == '<' { // heredocs have an extra linebreak because the opening - // delimiter is on its own line and is not included in - // the token Text itself - lineBreaks++ + // delimiter is on its own line and is not included in the + // token Text itself, and the trailing newline is removed. + lineBreaks += 2 } return lineBreaks } diff --git a/caddyconfig/caddyfile/lexer_test.go b/caddyconfig/caddyfile/lexer_test.go index 3c7e157..801d81e 100644 --- a/caddyconfig/caddyfile/lexer_test.go +++ b/caddyconfig/caddyfile/lexer_test.go @@ -256,7 +256,7 @@ EOF same-line-arg `), expected: []Token{ {Line: 1, Text: `heredoc`}, - {Line: 1, Text: "content\n"}, + {Line: 1, Text: "content"}, {Line: 3, Text: `same-line-arg`}, }, }, @@ -267,18 +267,40 @@ VERY-LONG-MARKER same-line-arg `), expected: []Token{ {Line: 1, Text: `heredoc`}, - {Line: 1, Text: "content\n"}, + {Line: 1, Text: "content"}, {Line: 3, Text: `same-line-arg`}, }, }, { 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 EOF same-line-arg `), expected: []Token{ {Line: 1, Text: `heredoc`}, - {Line: 1, Text: "content\n"}, + {Line: 1, Text: "content"}, {Line: 3, Text: `same-line-arg`}, }, }, @@ -294,7 +316,7 @@ VERY-LONG-MARKER same-line-arg expected: []Token{ {Line: 1, Text: `prev-line`}, {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: 7, Text: `next-line`}, }, @@ -313,6 +335,37 @@ VERY-LONG-MARKER same-line-arg }, }, { + input: []byte(`heredoc <<s + � + s + `), + expected: []Token{ + {Line: 1, Text: `heredoc`}, + {Line: 1, Text: "�"}, + }, + }, + { + 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 content HERE same-line-arg @@ -357,17 +410,17 @@ VERY-LONG-MARKER same-line-arg actual, err := Tokenize(testCase.input, "") if testCase.expectErr { if err == nil { - t.Errorf("expected error, got actual: %v", actual) + t.Fatalf("expected error, got actual: %v", actual) continue } 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 } if err != nil { - t.Errorf("%v", err) + t.Fatalf("%v", err) } 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) { 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++ { 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) break } 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) break } |