From 77fb40506f22d1a6b5f1131491b1e0343abf8370 Mon Sep 17 00:00:00 2001 From: Just want to protect you <108321411+Ahaohaohao@users.noreply.github.com> Date: Fri, 13 Jun 2025 22:59:48 +0800 Subject: [PATCH] fix the JS script error when escaping special characters to prevent special airport names from causing the script to fail to work (#3717) * fix the JS script error * update logs * remove unused references * update logs * fix special escape test * optimize:unescape of special characters in JS strings * fix unaligned annotations --------- Co-authored-by: Ahao <108321411+xuanyuan0408@users.noreply.github.com> --- UPDATELOG.md | 1 + src-tauri/src/enhance/script.rs | 121 +++++++++++++++++++++++++++++--- 2 files changed, 113 insertions(+), 9 deletions(-) diff --git a/UPDATELOG.md b/UPDATELOG.md index a36a11415..0db4fc6d2 100644 --- a/UPDATELOG.md +++ b/UPDATELOG.md @@ -37,6 +37,7 @@ - 修复编辑器中连字符处理问题 - 修复提权漏洞,改用带认证的 IPC 通信机制 - 修复静默启动无法使用自动轻量模式 +- 修复 JS 脚本转义特殊字符报错 --- diff --git a/src-tauri/src/enhance/script.rs b/src-tauri/src/enhance/script.rs index 98919794a..40789f07a 100644 --- a/src-tauri/src/enhance/script.rs +++ b/src-tauri/src/enhance/script.rs @@ -33,37 +33,50 @@ pub fn use_script( } let _ = context.eval(Source::from_bytes( r#"var console = Object.freeze({ - log(data){__verge_log__("log",JSON.stringify(data))}, - info(data){__verge_log__("info",JSON.stringify(data))}, - error(data){__verge_log__("error",JSON.stringify(data))}, - debug(data){__verge_log__("debug",JSON.stringify(data))}, + log(data){__verge_log__("log",JSON.stringify(data, null, 2))}, + info(data){__verge_log__("info",JSON.stringify(data, null, 2))}, + error(data){__verge_log__("error",JSON.stringify(data, null, 2))}, + debug(data){__verge_log__("debug",JSON.stringify(data, null, 2))}, + warn(data){__verge_log__("warn",JSON.stringify(data, null, 2))}, + table(data){__verge_log__("table",JSON.stringify(data, null, 2))}, });"#, )); let config = use_lowercase(config.clone()); let config_str = serde_json::to_string(&config)?; + // 处理 name 参数中的特殊字符 + let safe_name = escape_js_string(&name); + let code = format!( r#"try{{ {script}; - JSON.stringify(main({config_str},'{name}')||'') + JSON.stringify(main({config_str},'{safe_name}')||'') }} catch(err) {{ `__error_flag__ ${{err.toString()}}` }}"# ); + if let Ok(result) = context.eval(Source::from_bytes(code.as_str())) { if !result.is_string() { anyhow::bail!("main function should return object"); } let result = result.to_string(&mut context).unwrap(); let result = result.to_std_string().unwrap(); - if result.starts_with("__error_flag__") { - anyhow::bail!(result[15..].to_owned()); + + // 处理 JS 执行结果中的特殊字符 + let unescaped_result = unescape_js_string(&result); + + if unescaped_result.starts_with("__error_flag__") { + anyhow::bail!(unescaped_result[15..].to_owned()); } - if result == "\"\"" { + if unescaped_result == "\"\"" { anyhow::bail!("main function should return object"); } - let res: Result = Ok(serde_json::from_str::(result.as_str())?); + + // 安全地解析 JSON 结果 + let res: Result = parse_json_safely(&unescaped_result); + let mut out = outputs.lock().unwrap(); match res { Ok(config) => Ok((use_lowercase(config), out.to_vec())), @@ -77,6 +90,74 @@ pub fn use_script( } } +// 解析 JSON 字符串,处理可能的转义字符 +fn parse_json_safely(json_str: &str) -> Result { + // 移除可能的引号包裹 + let json_str = if json_str.starts_with('"') && json_str.ends_with('"') { + &json_str[1..json_str.len() - 1] + } else { + json_str + }; + + // 处理可能的 JSON 字符串中的转义字符 + let json_str = json_str.replace("\\\"", "\""); + + Ok(serde_json::from_str::(&json_str)?) +} + +// 转义 JS 字符串中的特殊字符 +fn escape_js_string(s: &str) -> String { + let mut result = String::with_capacity(s.len()); + for c in s.chars() { + match c { + '\'' => result.push_str("\\'"), + '"' => result.push_str("\\\""), + '\\' => result.push_str("\\\\"), + '\n' => result.push_str("\\n"), + '\r' => result.push_str("\\r"), + '\t' => result.push_str("\\t"), + '\0' => result.push_str("\\0"), + _ => result.push(c), + } + } + result +} + +// 反转义 JS 字符串中的特殊字符 +fn unescape_js_string(s: &str) -> String { + let mut result = String::with_capacity(s.len()); + let mut chars = s.chars(); + + while let Some(c) = chars.next() { + if c == '\\' { + match chars.next() { + Some('n') => result.push('\n'), + Some('r') => result.push('\r'), + Some('t') => result.push('\t'), + Some('0') => result.push('\0'), + Some('\\') => result.push('\\'), + Some('\'') => result.push('\''), + Some('"') => result.push('"'), + Some('u') => { + // 处理转义序列 + let hex = chars.by_ref().take(4).collect::(); + if let Ok(codepoint) = u32::from_str_radix(&hex, 16) { + if let Some(ch) = char::from_u32(codepoint) { + result.push(ch); + } + } + } + Some(other) => result.push(other), + None => break, + } + } else { + result.push(c); + } + } + + result +} + #[test] fn test_script() { let script = r#" @@ -111,3 +192,25 @@ fn test_script() { dbg!(results); assert!(box_yaml_config_size < yaml_config_size); } + +// 测试特殊字符转义功能 +#[test] +fn test_escape_unescape() { + let test_string = r#"Hello "World"!\nThis is a test with \u00A9 copyright symbol."#; + let escaped = escape_js_string(test_string); + let unescaped = unescape_js_string(&escaped); + + assert_eq!(test_string, unescaped); + + let json_str = r#"{"key":"value","nested":{"key":"value"}}"#; + let parsed = parse_json_safely(json_str).unwrap(); + + assert!(parsed.contains_key("key")); + assert!(parsed.contains_key("nested")); + + let quoted_json_str = r#""{"key":"value","nested":{"key":"value"}}""#; + let parsed_quoted = parse_json_safely(quoted_json_str).unwrap(); + + assert!(parsed_quoted.contains_key("key")); + assert!(parsed_quoted.contains_key("nested")); +}