address review: a leading hashbang line is a comment for the nesting scan; eval source is checked as it stands

This commit is contained in:
Dylan M. Taylor
2026-11-11 14:47:23 +00:00
parent 71d2bbd6ac
commit 5effc87570
2 changed files with 21 additions and 1 deletions
+9 -1
View File
@@ -545,7 +545,11 @@ impl boa_engine::context::HostHooks for Hooks {
_context: &mut Context,
) -> JsResult<()> {
let mut code: String = parameters.iter().map(|p| p.to_std_string_escaped()).collect::<Vec<_>>().join(",");
code.push('\n');
// With no parameters (`eval`) the body is the source as it stands, so a leading `#!` line
// is still its first line; with parameters it sits inside a function, where it is no comment.
if !parameters.is_empty() {
code.push('\n');
}
code.push_str(&body.to_std_string_escaped());
check_nesting(&code).map_err(|e| JsNativeError::syntax().with_message(e).into())
}
@@ -870,6 +874,10 @@ pub fn check_nesting(code: &str) -> Result<(), String> {
let (mut i, mut depth, mut max) = (0usize, 0usize, 0usize);
// The last significant byte (not space or comment), and where it is.
let (mut prev, mut prev_at) = (0u8, 0usize);
// A `#!` line at the very start is a comment (Boa reads it so): its text is not scanned.
if b.starts_with(b"#!") {
i = b.iter().position(|d| *d == b'\n').unwrap_or(b.len());
}
// Whether the word that ends at `prev_at` came right after a `.` token (comments and
// whitespace between them do not matter).
let mut word_after_dot = false;
+12
View File
@@ -1950,6 +1950,18 @@ fn setting_comp_width_and_height_leaves_the_layers_where_they_are() {
assert_eq!(layer(c, "s").props.prop("transform/position").unwrap().value, KV::Vec3([100.0, 100.0, 0.0]));
}
#[test]
fn a_hashbang_line_does_not_hide_deep_nesting_from_eval() {
// Boa reads a leading `#!` line of eval source as a comment, so the backtick on it opens no
// template: the brackets after it are counted and refused.
let mut s = session();
let code = "var deep = new Array(5000).join('(') + '1' + new Array(5000).join(')'); var r; \
try { eval('#! `\\n' + deep); r = 'ran'; } catch (e) { r = 'refused'; } r";
assert_eq!(ok(&mut s, code).result.as_str(), Some("refused"));
assert!(crate::runtime::check_nesting(&format!("#! `\n{}1{}", "(".repeat(5000), ")".repeat(5000))).is_err());
assert!(crate::runtime::check_nesting("#! `\n(1)").is_ok());
}
#[test]
fn code_compiled_at_run_time_passes_the_nesting_check() {
// `eval` and `new Function` of a string nested past the limit are refused with a catchable