From 5effc87570c12ef2dd0f85f4727fbcb47b4416ee Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Wed, 11 Nov 2026 14:47:23 +0000 Subject: [PATCH] address review: a leading hashbang line is a comment for the nesting scan; eval source is checked as it stands --- crates/script/src/runtime.rs | 10 +++++++++- crates/script/src/tests.rs | 12 ++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/crates/script/src/runtime.rs b/crates/script/src/runtime.rs index 5252b7e1b..b8dcaeefb 100644 --- a/crates/script/src/runtime.rs +++ b/crates/script/src/runtime.rs @@ -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::>().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; diff --git a/crates/script/src/tests.rs b/crates/script/src/tests.rs index 1b93bfb48..538ca5680 100644 --- a/crates/script/src/tests.rs +++ b/crates/script/src/tests.rs @@ -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