taq25/templ-lint

GitHub: taq25/templ-lint

templ-lint 是一款针对 Go templ 模板引擎的安全 linter,通过直接解析 .templ 文件检测 htmx 和 Alpine.js 使用中容易引发 XSS 的模式及其他前端安全问题。

Stars: 0 | Forks: 0

# templ-lint [![CI](https://static.pigsec.cn/wp-content/uploads/repos/cas/ad/ad5834178f7599af9fdda11629d49cae07f2997beec49821b2920eff5bfd50e7.svg)](https://github.com/taq25/templ-lint/actions/workflows/ci.yaml) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) 一个用于 [a-h/templ](https://github.com/a-h/templ) 模板的安全 linter。 它使用 templ 官方的 `parser/v2` 直接解析 `.templ` 文件,并检测当 templ 结合 **htmx** 和 **Alpine.js** 时会导致 XSS 的模式。 日本語ドキュメントは [docs/README.ja.md](docs/README.ja.md) を参照してください。 ## 为什么需要 templ 会对所有插入的文本和属性值进行 HTML 转义。但当前端从属性中读取行为时,这是不够的: ```
``` Alpine.js **会将属性值作为 JavaScript 执行**。虽然该值会被 templ 进行 HTML 转义,但这无关紧要 —— Alpine 会从 DOM 中读回该字符串并执行它。这同样适用于 `hx-on:*`、`hx-vals`(`js:` 前缀)以及普通的 HTML `on*` 处理程序。 这类 bug 对现有工具是不可见的:golangci-lint 和 gosec 会跳过生成的代码,且不了解 templ 的语义;而 templ 自身的 LSP 仅检查语法。templ-lint 填补了这一空白。检测结果会直接指向 `.templ` 源码行,而不是生成的 Go 代码。 除了 XSS,templ-lint 还会标记一些在模板结构中可见的非 XSS 问题:反向标签劫持(`target="_blank"` 而没有 `rel="noopener"`)以及跨域脚本和样式表缺少子资源完整性。存在于处理程序逻辑中的漏洞(SQL 注入、身份验证、CSRF token 验证)不在范围内 —— 它们在 `.templ` 文件中是不可见的。 ## 安装 ``` go install github.com/taq25/templ-lint@latest ``` 或者从 [Releases](https://github.com/taq25/templ-lint/releases) 下载二进制文件。 ## 用法 ``` # 递归 lint ./views 下的所有 *.templ 文件 templ-lint ./views # 允许将你的项目的 sanitizer wrapper 作为 templ.Raw 参数 templ-lint -allow-sanitizer sanitize.HTML ./views # JSON 输出;将 warnings 视为 failures templ-lint -format json -strict ./views ``` | 标志 | 描述 | |---|---| | `-allow-sanitizer pkg.Func` | 允许 `templ.Raw(pkg.Func(x))` 等。可重复使用。 | | `-format text\|json` | 输出格式(默认为 `text`)。 | | `-strict` | 遇到 WARNING 结果时也以非零状态退出。 | | `-version` | 打印版本并退出。 | 退出代码:`0` 无检测结果 / `1` 有检测结果(始终包含 ERROR;WARNING 仅在使用 `-strict` 时包含) / `2` 执行错误。 ## 规则 | 规则 | 检测内容 | 严重程度 | |---|---|---| | `js-eval-attr` | 在作为 JavaScript 执行的属性中的动态值:HTML `on*`;Alpine `x-data`, `x-init`, `x-html`, `x-text`, `x-show`, `x-if`, `x-for`, `x-effect`, `x-on:*`, `@*`, `:*`, `x-model*`;htmx `hx-on:*`, `hx-vals`, `hx-headers` | ERROR | | `escape-bypass` | 传递给 `templ.Raw`, `templ.SafeURL`, `templ.JSExpression`, `templ.JSUnsafeFuncCall` 的非字面量参数 —— 包括在 `{{ ... }}` Go 代码块内部 | ERROR | | `dyn-attr-name` | 动态属性名(`{ name }={ value }`):攻击者控制的名称可能会成为事件处理程序 | ERROR | | `url-attr` | 动态请求目标(`hx-get/post/put/patch/delete`)。静态同源的相对路径(带有 `"/x…"` 前缀的字符串字面量,例如 `"/posts/" + id`)会被排除;协议相对的 `//host` 和变量构建的 URL 仍会被标记 | WARNING | | `spread-attr` | 展开属性(`{ attrs... }`):名称和值在运行时决定 | WARNING | | `unsafe-target-blank` | 没有配置 `rel="noopener"` 的 `target="_blank"` —— 反向标签劫持 (CWE-1022) | WARNING | | `missing-sri` | 没有 `integrity` 属性的跨域 `