Skip to content

Commit 5745233

Browse files
authored
Restrict strings beginning with quote should also ending with quote (#13131)
# Description Closes: #13010 It adds an additional check inside `parse_string`, and returns `unbalanced quote` if input string is unbalanced # User-Facing Changes After this pr, the following is no longer allowed: ```nushell ❯ "asdfasdf"asdfasdf Error: nu::parser::extra_token_after_closing_delimiter × Invaild characters after closing delimiter ╭─[entry #1:1:11] 1 │ "asdfasdf"asdfasdf · ────┬─── · ╰── invalid characters ╰──── help: Try removing them. ❯ 'asdfasd'adsfadf Error: nu::parser::extra_token_after_closing_delimiter × Invaild characters after closing delimiter ╭─[entry #2:1:10] 1 │ 'asdfasd'adsfadf · ───┬─── · ╰── invalid characters ╰──── help: Try removing them. ``` # Tests + Formatting Added 1 test
1 parent 1f1f581 commit 5745233

File tree

3 files changed

+44
-0
lines changed

3 files changed

+44
-0
lines changed

crates/nu-parser/src/parser.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2828,6 +2828,36 @@ pub fn parse_string(working_set: &mut StateWorkingSet, span: Span) -> Expression
28282828
if bytes[0] != b'\'' && bytes[0] != b'"' && bytes[0] != b'`' && bytes.contains(&b'(') {
28292829
return parse_string_interpolation(working_set, span);
28302830
}
2831+
// Check for unbalanced quotes:
2832+
{
2833+
if bytes.starts_with(b"\"")
2834+
&& (bytes.iter().filter(|ch| **ch == b'"').count() > 1 && !bytes.ends_with(b"\""))
2835+
{
2836+
let close_delimiter_index = bytes
2837+
.iter()
2838+
.skip(1)
2839+
.position(|ch| *ch == b'"')
2840+
.expect("Already check input bytes contains at least two double quotes");
2841+
// needs `+2` rather than `+1`, because we have skip 1 to find close_delimiter_index before.
2842+
let span = Span::new(span.start + close_delimiter_index + 2, span.end);
2843+
working_set.error(ParseError::ExtraTokensAfterClosingDelimiter(span));
2844+
return garbage(working_set, span);
2845+
}
2846+
2847+
if bytes.starts_with(b"\'")
2848+
&& (bytes.iter().filter(|ch| **ch == b'\'').count() > 1 && !bytes.ends_with(b"\'"))
2849+
{
2850+
let close_delimiter_index = bytes
2851+
.iter()
2852+
.skip(1)
2853+
.position(|ch| *ch == b'\'')
2854+
.expect("Already check input bytes contains at least two double quotes");
2855+
// needs `+2` rather than `+1`, because we have skip 1 to find close_delimiter_index before.
2856+
let span = Span::new(span.start + close_delimiter_index + 2, span.end);
2857+
working_set.error(ParseError::ExtraTokensAfterClosingDelimiter(span));
2858+
return garbage(working_set, span);
2859+
}
2860+
}
28312861

28322862
let (s, err) = unescape_unquote_string(bytes, span);
28332863
if let Some(err) = err {

crates/nu-protocol/src/errors/parse_error.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ pub enum ParseError {
1717
#[diagnostic(code(nu::parser::extra_tokens), help("Try removing them."))]
1818
ExtraTokens(#[label = "extra tokens"] Span),
1919

20+
#[error("Invalid characters after closing delimiter")]
21+
#[diagnostic(
22+
code(nu::parser::extra_token_after_closing_delimiter),
23+
help("Try removing them.")
24+
)]
25+
ExtraTokensAfterClosingDelimiter(#[label = "invalid characters"] Span),
26+
2027
#[error("Extra positional argument.")]
2128
#[diagnostic(code(nu::parser::extra_positional), help("Usage: {0}"))]
2229
ExtraPositional(String, #[label = "extra positional argument"] Span),
@@ -577,6 +584,7 @@ impl ParseError {
577584
ParseError::LabeledErrorWithHelp { span: s, .. } => *s,
578585
ParseError::RedirectingBuiltinCommand(_, s, _) => *s,
579586
ParseError::UnexpectedSpreadArg(_, s) => *s,
587+
ParseError::ExtraTokensAfterClosingDelimiter(s) => *s,
580588
}
581589
}
582590
}

tests/repl/test_strings.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ fn non_string_in_record() -> TestResult {
3636
)
3737
}
3838

39+
#[test]
40+
fn unbalance_string() -> TestResult {
41+
fail_test(r#""aaaab"cc"#, "invalid characters")?;
42+
fail_test(r#"'aaaab'cc"#, "invalid characters")
43+
}
44+
3945
#[test]
4046
fn string_in_valuestream() -> TestResult {
4147
run_test(

0 commit comments

Comments
 (0)