@marcel91200 / caddy Mirror · read-only / commits / f3379f65
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 13, 05:30 UTC

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

Co-authored-by: Mohammed Al Sahaf <msaa1990@gmail.com>

Francis Lavoie committed Feb 26, 2023 at 16:56 UTC f3379f650a3de0e83903e3aa34d39b4ec647ee50
3 files changed +77 -17
caddyconfig/caddyfile/lexer.go
+13 -6
@@ -284,15 +284,17 @@ func (l *lexer) next() (bool, error) {
284 // and processes the text to strip leading whitespace, returning the final
285 // value without the leading whitespace.
286 func (l *lexer) finalizeHeredoc(val []rune, marker string) ([]rune, error) {
287 + stringVal := string(val)
288 +
289 // find the last newline of the heredoc, which is where the contents end
288 - lastNewline := strings.LastIndex(string(val), "\n")
290 + lastNewline := strings.LastIndex(stringVal, "\n")
291
292 // collapse the content, then split into separate lines
291 - lines := strings.Split(string(val[:lastNewline+1]), "\n")
293 + lines := strings.Split(stringVal[:lastNewline+1], "\n")
294
295 // figure out how much whitespace we need to strip from the front of every line
296 // by getting the string that precedes the marker, on the last line
295 - paddingToStrip := string(val[lastNewline+1 : len(val)-len(marker)])
297 + paddingToStrip := stringVal[lastNewline+1 : len(stringVal)-len(marker)]
298
299 // iterate over each line and strip the whitespace from the front
300 var out string
@@ -310,6 +312,11 @@ func (l *lexer) finalizeHeredoc(val []rune, marker string) ([]rune, error) {
312 out += strings.ReplaceAll(lineText[len(paddingToStrip):]+"\n", "\r", "")
313 }
314
315 + // Remove the trailing newline from the loop
316 + if len(out) > 0 && out[len(out)-1] == '\n' {
317 + out = out[:len(out)-1]
318 + }
319 +
320 // return the final value
321 return []rune(out), nil
322 }
@@ -340,9 +347,9 @@ func (t Token) NumLineBreaks() int {
347 lineBreaks := strings.Count(t.Text, "\n")
348 if t.wasQuoted == '<' {
349 // heredocs have an extra linebreak because the opening
343 - // delimiter is on its own line and is not included in
344 - // the token Text itself
345 - lineBreaks++
350 + // delimiter is on its own line and is not included in the
351 + // token Text itself, and the trailing newline is removed.
352 + lineBreaks += 2
353 }
354 return lineBreaks
355 }
caddyconfig/caddyfile/lexer_test.go
+63 -10
@@ -256,7 +256,7 @@ EOF same-line-arg
256 `),
257 expected: []Token{
258 {Line: 1, Text: `heredoc`},
259 - {Line: 1, Text: "content\n"},
259 + {Line: 1, Text: "content"},
260 {Line: 3, Text: `same-line-arg`},
261 },
262 },
@@ -267,18 +267,40 @@ VERY-LONG-MARKER same-line-arg
267 `),
268 expected: []Token{
269 {Line: 1, Text: `heredoc`},
270 - {Line: 1, Text: "content\n"},
270 + {Line: 1, Text: "content"},
271 {Line: 3, Text: `same-line-arg`},
272 },
273 },
274 {
275 input: []byte(`heredoc <<EOF
276 +extra-newline
277 +
278 +EOF same-line-arg
279 + `),
280 + expected: []Token{
281 + {Line: 1, Text: `heredoc`},
282 + {Line: 1, Text: "extra-newline\n"},
283 + {Line: 4, Text: `same-line-arg`},
284 + },
285 + },
286 + {
287 + input: []byte(`heredoc <<EOF
288 + EOF same-line-arg
289 + `),
290 + expected: []Token{
291 + {Line: 1, Text: `heredoc`},
292 + {Line: 1, Text: ""},
293 + {Line: 2, Text: `same-line-arg`},
294 + },
295 + },
296 + {
297 + input: []byte(`heredoc <<EOF
298 content
299 EOF same-line-arg
300 `),
301 expected: []Token{
302 {Line: 1, Text: `heredoc`},
281 - {Line: 1, Text: "content\n"},
303 + {Line: 1, Text: "content"},
304 {Line: 3, Text: `same-line-arg`},
305 },
306 },
@@ -294,7 +316,7 @@ VERY-LONG-MARKER same-line-arg
316 expected: []Token{
317 {Line: 1, Text: `prev-line`},
318 {Line: 2, Text: `heredoc`},
297 - {Line: 2, Text: "\tmulti\n\tline\n\tcontent\n"},
319 + {Line: 2, Text: "\tmulti\n\tline\n\tcontent"},
320 {Line: 6, Text: `same-line-arg`},
321 {Line: 7, Text: `next-line`},
322 },
@@ -312,6 +334,37 @@ VERY-LONG-MARKER same-line-arg
334 {Line: 3, Text: `same-line-arg`},
335 },
336 },
337 + {
338 + input: []byte(`heredoc <<s
339 + �
340 + s
341 + `),
342 + expected: []Token{
343 + {Line: 1, Text: `heredoc`},
344 + {Line: 1, Text: "�"},
345 + },
346 + },
347 + {
348 + 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"),
349 + expected: []Token{
350 + {
351 + Line: 2,
352 + Text: "heredoc",
353 + },
354 + {
355 + Line: 2,
356 + Text: "\u00BF\u0057\u0001\u0000\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u003D\u001F",
357 + },
358 + {
359 + Line: 5,
360 + Text: "\u00BF\u0057\u0001\u0000\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u003D\u001F",
361 + },
362 + {
363 + Line: 6,
364 + Text: "\u00BF\u00BF\u0057\u0001\u0000\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u003D\u001F",
365 + },
366 + },
367 + },
368 {
369 input: []byte(`heredoc <<HERE SAME LINE
370 content
@@ -357,17 +410,17 @@ VERY-LONG-MARKER same-line-arg
410 actual, err := Tokenize(testCase.input, "")
411 if testCase.expectErr {
412 if err == nil {
360 - t.Errorf("expected error, got actual: %v", actual)
413 + t.Fatalf("expected error, got actual: %v", actual)
414 continue
415 }
416 if err.Error() != testCase.errorMessage {
364 - t.Errorf("expected error '%v', got: %v", testCase.errorMessage, err)
417 + t.Fatalf("expected error '%v', got: %v", testCase.errorMessage, err)
418 }
419 continue
420 }
421
422 if err != nil {
370 - t.Errorf("%v", err)
423 + t.Fatalf("%v", err)
424 }
425 lexerCompare(t, i, testCase.expected, actual)
426 }
@@ -375,17 +428,17 @@ VERY-LONG-MARKER same-line-arg
428
429 func lexerCompare(t *testing.T, n int, expected, actual []Token) {
430 if len(expected) != len(actual) {
378 - t.Errorf("Test case %d: expected %d token(s) but got %d", n, len(expected), len(actual))
431 + t.Fatalf("Test case %d: expected %d token(s) but got %d", n, len(expected), len(actual))
432 }
433
434 for i := 0; i < len(actual) && i < len(expected); i++ {
435 if actual[i].Line != expected[i].Line {
383 - t.Errorf("Test case %d token %d ('%s'): expected line %d but was line %d",
436 + t.Fatalf("Test case %d token %d ('%s'): expected line %d but was line %d",
437 n, i, expected[i].Text, expected[i].Line, actual[i].Line)
438 break
439 }
440 if actual[i].Text != expected[i].Text {
388 - t.Errorf("Test case %d token %d: expected text '%s' but was '%s'",
441 + t.Fatalf("Test case %d token %d: expected text '%s' but was '%s'",
442 n, i, expected[i].Text, actual[i].Text)
443 break
444 }
caddytest/integration/caddyfile_adapt/heredoc.txt
+1 -1
@@ -31,7 +31,7 @@ example.com {
31 {
32 "handle": [
33 {
34 - "body": "\u003chtml\u003e\n \u003chead\u003e\u003ctitle\u003eFoo\u003c/title\u003e\n \u003cbody\u003eFoo\u003c/body\u003e\n\u003c/html\u003e\n",
34 + "body": "\u003chtml\u003e\n \u003chead\u003e\u003ctitle\u003eFoo\u003c/title\u003e\n \u003cbody\u003eFoo\u003c/body\u003e\n\u003c/html\u003e",
35 "handler": "static_response",
36 "status_code": 200
37 }