From 57452337ff4e228102433e99b4c6000700a9b3b2 Mon Sep 17 00:00:00 2001 From: Wind Date: Fri, 28 Jun 2024 09:47:12 +0800 Subject: [PATCH] Restrict strings beginning with quote should also ending with quote (#13131) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # 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 --- crates/nu-parser/src/parser.rs | 30 ++++++++++++++++++++ crates/nu-protocol/src/errors/parse_error.rs | 8 ++++++ tests/repl/test_strings.rs | 6 ++++ 3 files changed, 44 insertions(+) diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index f6bbd84f6c..c3d18b55ac 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -2828,6 +2828,36 @@ pub fn parse_string(working_set: &mut StateWorkingSet, span: Span) -> Expression if bytes[0] != b'\'' && bytes[0] != b'"' && bytes[0] != b'`' && bytes.contains(&b'(') { return parse_string_interpolation(working_set, span); } + // Check for unbalanced quotes: + { + if bytes.starts_with(b"\"") + && (bytes.iter().filter(|ch| **ch == b'"').count() > 1 && !bytes.ends_with(b"\"")) + { + let close_delimiter_index = bytes + .iter() + .skip(1) + .position(|ch| *ch == b'"') + .expect("Already check input bytes contains at least two double quotes"); + // needs `+2` rather than `+1`, because we have skip 1 to find close_delimiter_index before. + let span = Span::new(span.start + close_delimiter_index + 2, span.end); + working_set.error(ParseError::ExtraTokensAfterClosingDelimiter(span)); + return garbage(working_set, span); + } + + if bytes.starts_with(b"\'") + && (bytes.iter().filter(|ch| **ch == b'\'').count() > 1 && !bytes.ends_with(b"\'")) + { + let close_delimiter_index = bytes + .iter() + .skip(1) + .position(|ch| *ch == b'\'') + .expect("Already check input bytes contains at least two double quotes"); + // needs `+2` rather than `+1`, because we have skip 1 to find close_delimiter_index before. + let span = Span::new(span.start + close_delimiter_index + 2, span.end); + working_set.error(ParseError::ExtraTokensAfterClosingDelimiter(span)); + return garbage(working_set, span); + } + } let (s, err) = unescape_unquote_string(bytes, span); if let Some(err) = err { diff --git a/crates/nu-protocol/src/errors/parse_error.rs b/crates/nu-protocol/src/errors/parse_error.rs index 28c865b841..4d59821f7d 100644 --- a/crates/nu-protocol/src/errors/parse_error.rs +++ b/crates/nu-protocol/src/errors/parse_error.rs @@ -17,6 +17,13 @@ pub enum ParseError { #[diagnostic(code(nu::parser::extra_tokens), help("Try removing them."))] ExtraTokens(#[label = "extra tokens"] Span), + #[error("Invalid characters after closing delimiter")] + #[diagnostic( + code(nu::parser::extra_token_after_closing_delimiter), + help("Try removing them.") + )] + ExtraTokensAfterClosingDelimiter(#[label = "invalid characters"] Span), + #[error("Extra positional argument.")] #[diagnostic(code(nu::parser::extra_positional), help("Usage: {0}"))] ExtraPositional(String, #[label = "extra positional argument"] Span), @@ -577,6 +584,7 @@ impl ParseError { ParseError::LabeledErrorWithHelp { span: s, .. } => *s, ParseError::RedirectingBuiltinCommand(_, s, _) => *s, ParseError::UnexpectedSpreadArg(_, s) => *s, + ParseError::ExtraTokensAfterClosingDelimiter(s) => *s, } } } diff --git a/tests/repl/test_strings.rs b/tests/repl/test_strings.rs index 62960fed13..1c573f03ae 100644 --- a/tests/repl/test_strings.rs +++ b/tests/repl/test_strings.rs @@ -36,6 +36,12 @@ fn non_string_in_record() -> TestResult { ) } +#[test] +fn unbalance_string() -> TestResult { + fail_test(r#""aaaab"cc"#, "invalid characters")?; + fail_test(r#"'aaaab'cc"#, "invalid characters") +} + #[test] fn string_in_valuestream() -> TestResult { run_test(