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
}