diff --git a/crates/oxide_builtins/src/json.rs b/crates/oxide_builtins/src/json.rs index 390de9d5..f4edd923 100644 --- a/crates/oxide_builtins/src/json.rs +++ b/crates/oxide_builtins/src/json.rs @@ -168,9 +168,15 @@ struct JsonNode { /// /// # 边界与前提 /// - 前导零数字、尾随逗号、溢出(1e400)、孤立 surrogate 均报 SyntaxError。 +/// - 嵌套 object/array 深度上限 `MAX_JSON_DEPTH` 层,超限报 SyntaxError +/// (进入时先计入再判定,防深嵌套在递归展开前耗尽栈)。 /// - 重复对象键:末写胜(键序由 build_js_value 统一)。 fn parse_json(text: &str) -> Result { - let mut p = JsonParser { text, pos: 0 }; + let mut p = JsonParser { + text, + pos: 0, + depth: 0, + }; p.skip_ws(); let node = p.parse_value()?; p.skip_ws(); @@ -180,9 +186,13 @@ fn parse_json(text: &str) -> Result { Ok(node) } +/// 嵌套容器深度上限:object/array 进入时计入,超限报 SyntaxError。 +const MAX_JSON_DEPTH: usize = 128; + struct JsonParser<'a> { text: &'a str, pos: usize, + depth: usize, } impl<'a> JsonParser<'a> { @@ -243,14 +253,27 @@ impl<'a> JsonParser<'a> { Ok(self.prim(JsonKind::String(s), start)) } b'[' => { + // 深度计数:进入时先 +1 再判上限,超限在递归展开前拒绝。 + self.depth += 1; + if self.depth > MAX_JSON_DEPTH { + self.depth -= 1; + return Err("recursion limit exceeded".into()); + } let items = self.parse_array()?; + self.depth -= 1; Ok(JsonNode { kind: JsonKind::Array(items), source: None, }) } b'{' => { + self.depth += 1; + if self.depth > MAX_JSON_DEPTH { + self.depth -= 1; + return Err("recursion limit exceeded".into()); + } let entries = self.parse_object()?; + self.depth -= 1; Ok(JsonNode { kind: JsonKind::Object(entries), source: None, diff --git a/crates/oxide_builtins/tests/builtins_json_tests.rs b/crates/oxide_builtins/tests/builtins_json_tests.rs index cc1e8105..52ffa567 100644 --- a/crates/oxide_builtins/tests/builtins_json_tests.rs +++ b/crates/oxide_builtins/tests/builtins_json_tests.rs @@ -740,6 +740,42 @@ fn replacer_whitelist_undefined_value_skipped() { assert_eq!(out, r#"{"a":1}"#); } +// --- 嵌套深度上限(递归限制)--- + +// 深度 128 层恰好过:128 层嵌套数组正常解析。 +#[test] +fn parse_depth_128_ok() { + let out = eval_str( + "(() => { const v = JSON.parse('['.repeat(128) + ']'.repeat(128)); \ + return String(Array.isArray(v) && v.length === 1); })()", + ) + .unwrap(); + assert_eq!(out, "true"); +} + +// 深度 129 层超限:抛 SyntaxError(递归限制),而非进程 abort。 +#[test] +fn parse_depth_129_throws() { + let out = eval_str( + "(() => { try { JSON.parse('['.repeat(129) + ']'.repeat(129)); return 'no-throw'; } \ + catch (e) { return e.constructor.name + '|' + e.message; } })()", + ) + .unwrap(); + assert_eq!(out, "SyntaxError|recursion limit exceeded"); +} + +// 深度计数复位:深嵌套解析超限失败后,随后普通嵌套解析不受残留计数影响。 +#[test] +fn parse_depth_count_resets() { + let out = eval_str( + "(() => { try { JSON.parse('['.repeat(129) + ']'.repeat(129)); } catch (e) {} \ + const v = JSON.parse('{\"a\":{\"b\":[1]}}'); \ + return String(v.a.b[0]); })()", + ) + .unwrap(); + assert_eq!(out, "1"); +} + fn eval_str(source: &str) -> Result { let (vm, result) = eval(source)?; vm.lookup_str(result)