Files
mailgo/internal/web/jsonify_test.go
T
kevin 8ea4a623a9 fix(security): 修复 P3 低危项(开放重定向/配额TOCTOU/safeJS/会话治理)
- Referer 开放重定向:safeRedirectPath 仅放行同站相对路径,
  外部 URL/协议跳转一律回退 /inbox
- 发信配额 TOCTOU:新增 TryReserveQuota 原子预扣
  (UPDATE ... WHERE used_bytes + n <= quota_bytes),超配额即拒发;
  附件保存失败按大小补偿回退
- 移除危险模板函数 safeHTML/safeJS:新增 jsonify(json.Marshal,
  < > & 转义为 \u003c 等,无法逃出 </script>),compose 页
  quill.innerHTML 改用 jsonify;srcdoc 改回默认属性转义
- 会话治理:登录成功后 session.Clear() 清旧状态;记录 loginAt,
  绝对过期 7 天 + 滑动续期(活跃会话 12h 写回刷新)
- 确认 #15 Content-Disposition 编码随 P1 #4 已完成
- 新增 12 个测试:重定向路径矩阵、配额原子性(含超额不部分扣费)、
  jsonify 逃逸防护、会话绝对过期/有效访问(签名会话构造)

至此 16 项安全审计项(P0-P3)全部修复完成。
2026-08-19 16:56:23 +08:00

44 lines
1.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package web
// P3 #14jsonify 模板函数在 <script> 上下文中必须能阻止
// </script> 逃逸(encoding/json 默认转义 < > &)。
import (
"html/template"
"strings"
"testing"
)
func TestJsonifyEscapesScriptBreakout(t *testing.T) {
jsonify, ok := templateFuncs()["jsonify"].(func(interface{}) template.JS)
if !ok {
t.Fatal("template funcs must include jsonify")
}
payloads := []string{
`x</script><script>alert(1)</script>`,
`"><img src=x onerror=alert(1)>`,
"line1\nline2\ttab",
"中文内容",
`quill "quotes" 'single'`,
}
for _, p := range payloads {
out := jsonify(p)
if !strings.HasPrefix(string(out), `"`) || !strings.HasSuffix(string(out), `"`) {
t.Errorf("jsonify(%q) = %s, want a quoted JS string literal", p, out)
}
if strings.Contains(string(out), "</script>") || strings.Contains(string(out), "</SCRIPT>") {
t.Errorf("jsonify(%q) must not emit raw </script>: %s", p, out)
}
if strings.ContainsAny(string(out), "\r\n") {
t.Errorf("jsonify(%q) must escape control chars: %q", p, out)
}
}
// 特殊值:nil -> null
out := jsonify(nil)
if string(out) != "null" {
t.Errorf("jsonify(nil) = %s, want null", out)
}
}