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>
This commit is contained in:
Just want to protect you
2025-06-13 22:59:48 +08:00
committed by GitHub
parent 5858f05c13
commit 77fb40506f
2 changed files with 113 additions and 9 deletions

View File

@@ -37,6 +37,7 @@
- 修复编辑器中连字符处理问题 - 修复编辑器中连字符处理问题
- 修复提权漏洞,改用带认证的 IPC 通信机制 - 修复提权漏洞,改用带认证的 IPC 通信机制
- 修复静默启动无法使用自动轻量模式 - 修复静默启动无法使用自动轻量模式
- 修复 JS 脚本转义特殊字符报错
--- ---

View File

@@ -33,37 +33,50 @@ pub fn use_script(
} }
let _ = context.eval(Source::from_bytes( let _ = context.eval(Source::from_bytes(
r#"var console = Object.freeze({ r#"var console = Object.freeze({
log(data){__verge_log__("log",JSON.stringify(data))}, log(data){__verge_log__("log",JSON.stringify(data, null, 2))},
info(data){__verge_log__("info",JSON.stringify(data))}, info(data){__verge_log__("info",JSON.stringify(data, null, 2))},
error(data){__verge_log__("error",JSON.stringify(data))}, error(data){__verge_log__("error",JSON.stringify(data, null, 2))},
debug(data){__verge_log__("debug",JSON.stringify(data))}, 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 = use_lowercase(config.clone());
let config_str = serde_json::to_string(&config)?; let config_str = serde_json::to_string(&config)?;
// 处理 name 参数中的特殊字符
let safe_name = escape_js_string(&name);
let code = format!( let code = format!(
r#"try{{ r#"try{{
{script}; {script};
JSON.stringify(main({config_str},'{name}')||'') JSON.stringify(main({config_str},'{safe_name}')||'')
}} catch(err) {{ }} catch(err) {{
`__error_flag__ ${{err.toString()}}` `__error_flag__ ${{err.toString()}}`
}}"# }}"#
); );
if let Ok(result) = context.eval(Source::from_bytes(code.as_str())) { if let Ok(result) = context.eval(Source::from_bytes(code.as_str())) {
if !result.is_string() { if !result.is_string() {
anyhow::bail!("main function should return object"); anyhow::bail!("main function should return object");
} }
let result = result.to_string(&mut context).unwrap(); let result = result.to_string(&mut context).unwrap();
let result = result.to_std_string().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"); anyhow::bail!("main function should return object");
} }
let res: Result<Mapping, Error> = Ok(serde_json::from_str::<Mapping>(result.as_str())?);
// 安全地解析 JSON 结果
let res: Result<Mapping, Error> = parse_json_safely(&unescaped_result);
let mut out = outputs.lock().unwrap(); let mut out = outputs.lock().unwrap();
match res { match res {
Ok(config) => Ok((use_lowercase(config), out.to_vec())), 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<Mapping, Error> {
// 移除可能的引号包裹
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::<Mapping>(&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::<String>();
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] #[test]
fn test_script() { fn test_script() {
let script = r#" let script = r#"
@@ -111,3 +192,25 @@ fn test_script() {
dbg!(results); dbg!(results);
assert!(box_yaml_config_size < yaml_config_size); 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"));
}