mirror of
https://github.com/nushell/nushell.git
synced 2025-05-09 04:24:26 +02:00
Fix unterminated loop in parse_record (#15246)
Fixes #15243 # Description As noted in #15243, a record with more characters after it (e.g., `{a:b}/`) will cause an OOM due to an infinite loop, introduced by #15023. This happens because the entire string `{a:b}/` is lexed as one token and passed to `parse_record`, where it repeatedly lexes until it hits the closing `}`. This PR detects such extra characters and reports an error. # User-Facing Changes `{a:b}/` and other such constructions will no longer cause infinite loops. Before #15023, you would've seen an "Unclosed delimiter" error message, but this PR changes that to "Invalid characters." ``` Error: nu::parser::extra_token_after_closing_delimiter × Invalid characters after closing delimiter ╭─[entry #5:1:7] 1 │ {a:b}/ · ┬ · ╰── invalid characters ╰──── help: Try removing them. ``` # Tests + Formatting # After Submitting
This commit is contained in:
parent
122bcff356
commit
b1e591f84c
@ -5995,10 +5995,12 @@ pub fn parse_record(working_set: &mut StateWorkingSet, span: Span) -> Expression
|
|||||||
return garbage(working_set, span);
|
return garbage(working_set, span);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut unclosed = false;
|
||||||
|
let mut extra_tokens = false;
|
||||||
if bytes.ends_with(b"}") {
|
if bytes.ends_with(b"}") {
|
||||||
end -= 1;
|
end -= 1;
|
||||||
} else {
|
} else {
|
||||||
working_set.error(ParseError::Unclosed("}".into(), Span::new(end, end)));
|
unclosed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let inner_span = Span::new(start, end);
|
let inner_span = Span::new(start, end);
|
||||||
@ -6009,7 +6011,12 @@ pub fn parse_record(working_set: &mut StateWorkingSet, span: Span) -> Expression
|
|||||||
error: None,
|
error: None,
|
||||||
span_offset: start,
|
span_offset: start,
|
||||||
};
|
};
|
||||||
loop {
|
while !lex_state.input.is_empty() {
|
||||||
|
if lex_state.input[0] == b'}' {
|
||||||
|
extra_tokens = true;
|
||||||
|
unclosed = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
let additional_whitespace = &[b'\n', b'\r', b','];
|
let additional_whitespace = &[b'\n', b'\r', b','];
|
||||||
if lex_n_tokens(&mut lex_state, additional_whitespace, &[b':'], true, 1) < 1 {
|
if lex_n_tokens(&mut lex_state, additional_whitespace, &[b':'], true, 1) < 1 {
|
||||||
break;
|
break;
|
||||||
@ -6038,6 +6045,15 @@ pub fn parse_record(working_set: &mut StateWorkingSet, span: Span) -> Expression
|
|||||||
}
|
}
|
||||||
let (tokens, err) = (lex_state.output, lex_state.error);
|
let (tokens, err) = (lex_state.output, lex_state.error);
|
||||||
|
|
||||||
|
if unclosed {
|
||||||
|
working_set.error(ParseError::Unclosed("}".into(), Span::new(end, end)));
|
||||||
|
} else if extra_tokens {
|
||||||
|
working_set.error(ParseError::ExtraTokensAfterClosingDelimiter(Span::new(
|
||||||
|
lex_state.span_offset + 1,
|
||||||
|
end,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(err) = err {
|
if let Some(err) = err {
|
||||||
working_set.error(err);
|
working_set.error(err);
|
||||||
}
|
}
|
||||||
|
@ -2818,4 +2818,16 @@ mod record {
|
|||||||
_ => panic!("Expected full cell path"),
|
_ => panic!("Expected full cell path"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Regression test for https://github.com/nushell/nushell/issues/15243
|
||||||
|
#[test]
|
||||||
|
fn record_terminate_loop() {
|
||||||
|
let engine_state = EngineState::new();
|
||||||
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
|
parse(&mut working_set, None, b"{a:b}/", false);
|
||||||
|
assert_eq!(
|
||||||
|
working_set.parse_errors.first().map(|e| e.to_string()),
|
||||||
|
Some("Invalid characters after closing delimiter".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user