this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

cue/scanner: reject closing quotes of multiline strings not after a newline

The spec grammar requires that multiline strings end with a newline
right before the closing quote:

multiline_string_lit = `"""` newline
{ unicode_value | interpolation | newline }
newline `"""` .
multiline_bytes_lit = "'''" newline
{ unicode_value | interpolation | byte_value | newline }
newline "'''" .

The scanner was not enforcing this, accepting inputs like `"""\n0"""`
where the closing `"""` appears on the same line as content.
This caused a disagreement between cue/parser and cue/literal,
as literal.Unquote correctly rejects such strings.

Fix scanString to track whether we are at the start of a line
(after a newline and optional whitespace indentation), and only
attempt to match the closing quotes of a multiline string when
at the start of a line.

Found by the fuzzer changes in the following commit.

Signed-off-by: Daniel Martí <mvdan@mvdan.cc>
Change-Id: Ib1bba21c3083e8c30b1614ef7f20dc6e474a311a
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1234634
Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>
Reviewed-by: Matthew Sackman <matthew@cue.works>

+17 -3
+15 -3
cue/scanner/scanner.go
··· 417 417 418 418 hasCR := false 419 419 extra := 0 420 + // For multiline strings, the closing quotes must follow a newline 421 + // (with optional whitespace indentation). The opening newline was already consumed. 422 + atLineStart := quote.numChar == 3 420 423 for { 421 424 ch := s.ch 422 425 if (quote.numChar != 3 && ch == '\n') || ch < 0 { ··· 429 432 } 430 433 431 434 s.next() 432 - ch, ok := s.consumeStringClose(ch, quote) 433 - if ok { 434 - break 435 + if quote.numChar != 3 || atLineStart { 436 + if _, ok := s.consumeStringClose(ch, quote); ok { 437 + break 438 + } 439 + } 440 + switch { 441 + case ch == '\n': 442 + atLineStart = true 443 + case quote.numChar == 3 && atLineStart && (ch == ' ' || ch == '\t'): 444 + // preserve atLineStart 445 + default: 446 + atLineStart = false 435 447 } 436 448 if ch == '\r' && quote.numChar == 3 { 437 449 hasCR = true
+2
cue/scanner/scanner_test.go
··· 725 725 {`"abc`, token.STRING, 0, `"abc`, "string literal not terminated"}, 726 726 {`""abc`, token.STRING, 0, `""`, ""}, 727 727 {"\"\"\"\nabc", token.STRING, 0, "\"\"\"\nabc", "string literal not terminated"}, 728 + {"\"\"\"\n0\"\"\"", token.STRING, 0, "\"\"\"\n0\"\"\"", "string literal not terminated"}, 728 729 {"'''\nabc", token.STRING, 0, "'''\nabc", "string literal not terminated"}, 730 + {"'''\n0'''", token.STRING, 0, "'''\n0'''", "string literal not terminated"}, 729 731 {"\"abc\n", token.STRING, 0, `"abc`, "string literal not terminated"}, 730 732 {"\"abc\n ", token.STRING, 0, `"abc`, "string literal not terminated"}, 731 733 {"\"abc\r\n ", token.STRING, 0, "\"abc\r", "string literal not terminated"},