Revel模板不自动转义,直接输出JS字符串会触发XSS;必须用template.JS包装传入,否则浏览器当HTML解析执行。

Revel 模板不自动转义,直接写 {{.JSCode}} 会触发 XSS;必须用 template.JS 类型包装后传入,否则浏览器当 HTML 解析执行。
为什么不能直接输出 JS 字符串
Revel 默认不对变量做任何 HTML 或 JS 转义。若你把一段 JS 字符串(比如 "alert('xss')")直接放进模板:{{.InlineScript}},它会被原样插入 HTML,变成可执行脚本。即使内容来自后端,只要没经过可信校验,就存在风险。
常见错误现象包括:
- 页面加载时弹窗、重定向或窃取
document.cookie - 控制台报错
Uncaught SyntaxError: Unexpected token '(因 JS 字符串含未转义的'或换行) - JS 代码被截断或解析失败,功能异常
正确做法:用 template.JS 包装后传入
Go 标准 html/template 提供了类型安全机制:template.JS 是一个带标记的字符串类型,告诉模板引擎“这段内容已由开发者确认为合法 JS”,渲染时不加 HTML 转义,但会做 JS 上下文所需的最小化转义(如引号、反斜杠、换行符)。
控制器中需显式转换:
使用约定式提交(Conventional Commits)生成标准化的 Git 提交信息。适用于以下场景: (1) 暂存更改时需要提交信息; (2) 审查 diff 时希望获得结构化摘要; (3) 编写 PR 描述或变更日志(changelog)条目; (4) 用户明确要求“为本次更改编写提交信息”。 该功能会分析已暂存/未暂存的 diff,并生成按类型(type)划分范围的提交信息。
func (c App) Index() revel.Result {
script := `console.log("Hello", ` + strconv.Quote(c.Params.Get("name")) + `);`
c.RenderArgs["inlineJS"] = template.JS(script)
return c.Render()
}
模板中直接使用:
<script>
{{.inlineJS}}
</script>
注意要点:
-
template.JS不做输入校验,仅做输出编码——你传什么进去,它就信什么;务必确保字符串来源可信(如拼接固定逻辑 + 已清洗的参数) - 不要在模板里拼接 JS:禁止
{{printf "console.log('%s')" .Name | js}},js函数是 Gotext/template的,Revel 模板未内置,且语义不匹配 - 避免从用户输入直接构造 JS 字符串;优先用 JSON 序列化传数据,再在独立 JS 文件里消费
更安全的替代方案:用 data- 属性 + 外部 JS
绝大多数场景下,真正需要的是“把数据传给前端 JS”,而不是“动态生成 JS 代码”。这时应拆离逻辑:
- 控制器中把结构体序列化为 JSON:
c.RenderArgs["pageData"] = map[string]interface{}{"user": user, "config": cfg} - 模板中写:
<div id="app" data-page-data="{{.pageData | json}}"></div>(需提前注册json自定义函数) - 外部 JS(
/public/js/app.js)读取并初始化:JSON.parse(document.getElementById('app').dataset.pageData)
这种方式完全规避了内联脚本风险,也利于缓存与调试。只有极少数情况(如统计埋点 snippet、A/B 测试加载器)才需真内联 JS,此时必须严格走 template.JS 路径。
最容易被忽略的一点:Revel 模板没有 js 这个内置函数,别在模板里写 {{.Script | js}}——它不会生效,也不会报错,只是静默输出原始字符串,等于没防护。

















