fix(安全): 修复HTML导出中的XSS漏洞并清理调试日志
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled

refactor(日志): 替换console.log为tracing日志系统
style(代码): 移除未使用的代码和依赖项

feat(测试): 添加端到端测试文档和CI工作流
docs(变更日志): 更新CHANGELOG.md记录0.1.0版本变更

perf(构建): 更新依赖版本并优化CI流程
This commit is contained in:
iven
2026-03-26 19:49:03 +08:00
parent b8d565a9eb
commit 978dc5cdd8
79 changed files with 3953 additions and 5724 deletions

View File

@@ -103,6 +103,22 @@ fn render_markdown(data: &Value) -> String {
md
}
/// Escape HTML special characters to prevent XSS
fn escape_html(s: &str) -> String {
let mut escaped = String::with_capacity(s.len());
for ch in s.chars() {
match ch {
'<' => escaped.push_str("&lt;"),
'>' => escaped.push_str("&gt;"),
'&' => escaped.push_str("&amp;"),
'"' => escaped.push_str("&quot;"),
'\'' => escaped.push_str("&#39;"),
_ => escaped.push(ch),
}
}
escaped
}
/// Render data to HTML
fn render_html(data: &Value) -> String {
let mut html = String::from(r#"<!DOCTYPE html>
@@ -123,11 +139,11 @@ fn render_html(data: &Value) -> String {
"#);
if let Some(title) = data.get("title").and_then(|v| v.as_str()) {
html.push_str(&format!("<h1>{}</h1>", title));
html.push_str(&format!("<h1>{}</h1>", escape_html(title)));
}
if let Some(description) = data.get("description").and_then(|v| v.as_str()) {
html.push_str(&format!("<p>{}</p>", description));
html.push_str(&format!("<p>{}</p>", escape_html(description)));
}
if let Some(outline) = data.get("outline") {
@@ -135,7 +151,7 @@ fn render_html(data: &Value) -> String {
if let Some(items) = outline.get("items").and_then(|v| v.as_array()) {
for item in items {
if let Some(text) = item.get("title").and_then(|v| v.as_str()) {
html.push_str(&format!("<li>{}</li>", text));
html.push_str(&format!("<li>{}</li>", escape_html(text)));
}
}
}
@@ -147,10 +163,10 @@ fn render_html(data: &Value) -> String {
for scene in scenes {
html.push_str("<div class=\"scene\">");
if let Some(title) = scene.get("title").and_then(|v| v.as_str()) {
html.push_str(&format!("<h3>{}</h3>", title));
html.push_str(&format!("<h3>{}</h3>", escape_html(title)));
}
if let Some(content) = scene.get("content").and_then(|v| v.as_str()) {
html.push_str(&format!("<p>{}</p>", content));
html.push_str(&format!("<p>{}</p>", escape_html(content)));
}
html.push_str("</div>");
}