package template import ( "strings" "testing" "github.com/tamakyi/TamaBox/internal/conf" ) func TestMarkdownToHTML_Basic(t *testing.T) { cases := []struct { name string in string want string // 必须包含 }{ {"bold", "**bold**", "bold"}, {"italic", "*italic*", "italic"}, {"link", "[baidu](https://baidu.com)", `href="https://baidu.com"`}, {"list", "- item1\n- item2", "
  • item1
  • "}, {"code", "`code`", "code"}, {"newline", "line1\nline2", "
    "}, {"font-red", `文本`, `文本`}, {"font-hex", `红`, `红`}, {"font-rgb", `红`, `红`}, {"font-mixed", `**粗体** 和 蓝`, `蓝`}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { got := string(MarkdownToHTML(tc.in)) if !strings.Contains(got, tc.want) { t.Errorf("MarkdownToHTML(%q) = %q, 期望包含 %q", tc.in, got, tc.want) } }) } } func TestMarkdownToHTML_XSS(t *testing.T) { cases := []string{ `<script>alert(1)</script>`, `&lt;script&gt;alert(1)&lt;/script&gt;`, ``, ``, `[x](javascript:alert(1))`, `x`, ``, "", `
    x
    `, ``, } for _, in := range cases { got := string(MarkdownToHTML(in)) if strings.Contains(got, "x`, "x"}, {"quote-inject", `x`, "x"}, {"url-inject", `x`, "x"}, {"onattr-inject", `x`, "x"}, {"long-value", `x`, "x"}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { got := string(MarkdownToHTML(tc.in)) if !strings.Contains(got, tc.want) { t.Errorf("输出 %q 应包含 %q", got, tc.want) } // 注入的 color 值不得出现 for _, evil := range []string{";", "position:", "onclick", "javascript:", "红字`, "红字"}, {"link", "[百度](https://baidu.com)", "百度"}, {"code", "`code`", "code"}, {"list", "- 第一项\n- 第二项", "第一项"}, {"block", "# 标题\n内容", "标题"}, {"mixed", "问题:**重要** 详情 蓝", "重要"}, {"empty", "", ""}, // 危险标签被剥离:script 的内容是纯文本(绘制无风险),img 自闭合无文本则输出空 {"xss-script", "", "alert(1)"}, {"xss-img", ``, ""}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { got := strings.Join(strings.Fields(MarkdownToPlainText(tc.in)), " ") if tc.want == "" { if got != "" { t.Errorf("MarkdownToPlainText(%q) = %q, 期望空", tc.in, got) } return } if !strings.Contains(got, tc.want) { t.Errorf("MarkdownToPlainText(%q) = %q, 期望包含 %q", tc.in, got, tc.want) } // 输出不得残留任何标签(< 必须被剥离) if strings.Contains(got, "<") { t.Errorf("MarkdownToPlainText(%q) = %q, 不应包含标签字符", tc.in, got) } }) } } // TestSafeURL 是 P2-1 的回归测试:markdown 链接白名单必须拒绝协议相对 URL // (//evil.com 等),否则会被误判为站内相对路径而渲染成指向外站的钓鱼 / 外链埋点链接。 // 同时验证 javascript:/data:/ftp: 等危险协议与大小写变体均被拒绝,http/https/mailto // 与站内相对路径被放行。 func TestSafeURL(t *testing.T) { allow := []string{ "https://example.com", "http://example.com", "HTTPS://EXAMPLE.COM", // 大小写不敏感 "mailto:foo@bar.com", "/path/to/page", "./relative", "../up", "#fragment", } for _, u := range allow { if !safeURL(u) { t.Errorf("safeURL(%q) = false, 期望放行", u) } } deny := []string{ "//evil.com", // 协议相对 URL(核心回归点) `\\evil.com`, // 反斜杠变体 `/\evil.com`, // 反斜杠变体 "javascript:alert(1)", // 危险协议 "JavaScript:alert(1)", // 大小写变体 "data:text/html,