前端项目里怎么集成SAST工具做代码扫描?
我们团队最近要落地安全开发生命周期,领导让在前端项目里加上SAST(静态应用安全测试)工具。但我试了几个,比如 ESLint 的 security 插件,还有 SonarQube,配置起来特别迷糊。
比如我用 eslint-plugin-security 装上了,但跑的时候根本没报任何安全问题,哪怕我故意写了个 eval('xxx') 它也视而不见。是不是我漏了什么配置?
另外,有没有适合 React + TypeScript 项目的轻量级 SAST 工具推荐?最好是能塞进 CI 流程里的,别太重。
现在 CI 用的是 GitHub Actions,本地开发用的是 Vite。下面是我 .eslintrc.js 的部分配置:
module.exports = {
plugins: ['security'],
extends: ['plugin:security/recommended'],
rules: {
'security/detect-eval-with-expression': 'error'
}
};
eslint-plugin-security默认只检测一些运行时风险,比如eval、new Function、setTimeout(string)这类,不过它对 TypeScript 的支持有点鸡肋——很多规则是基于 JS AST 的,TS 里写法稍微变一下它就识别不出来,尤其是你用了 Vite + React,可能还开启了noEmit或者 Babel 编译路径绕过了 ESLint 的某些检查。先解决你本地不报错的问题:
1. 确保你装了
eslint-plugin-security(不是@eslint/security,那个是新出的内置插件,但还没覆盖全),版本最好 ≥1.7.02. 检查你有没有把
.ts和.tsx加进 ESLint 的处理范围,比如extends里加了plugin:@typescript-eslint/recommended,但没处理冲突规则3. 重点:
security/detect-eval-with-expression这个 rule 是检测eval(someVar),但如果你写的是eval('xxx'),它其实走的是detect-eval-with-expression的兄弟规则:security/detect-eval(注意没with-expression)你试试改成
'security/detect-eval': 'error',这个是管字面量eval('xxx')的另外你可能漏了 ESLint 的
parserOptions配置,比如:不过说实话,纯靠 ESLint 做 SAST 真的很局限,尤其前端代码里很多逻辑是运行时拼接的(比如 React 的
dangerouslySetInnerHTML),ESLint 根本跑不全。推荐几个轻量级、适合 CI 的方案,按优先级排:
1.
eslint-plugin-security+eslint-plugin-react(专门抓dangerouslySetInnerHTML、script-src等)这个组合已经能覆盖 60% 的常见前端 XSS 风险,配合 GitHub Actions 很简单:
2. 如果想再加一层,用
sonarqube-scanner,但别用它整个大套件,只跑静态分析即可。不过 SonarQube 对前端支持一般,得自己写sonar-project.properties,而且它默认只扫 JS,TS 需要额外装typescript插件,容易踩坑。3. 真正适合 React + TS 的轻量 SAST 是
tslint-security(注意不是 TSLint 旧版,是单独的安全插件),它基于 TypeScript AST,能准确识别eval、localStorage滥用、fetch的不安全参数等,而且和eslint不冲突,可以并行跑。不过它只支持 TSLint,而 TSLint 已经废弃了,所以更推荐迁移到typescript-eslint的替代方案——但如果你项目还没升级到 ESLint 8+,这个插件确实香。4. 最近比较火的是
codeql(GitHub 官方的),你直接在 Actions 里加个 step:它能识别 XSS、SSRF、路径遍历等,连 React 的
dangerouslySetInnerHTML都能 trace 到源头,不过第一次跑可能有点慢,需要下载数据库。一般这样处理:先用
eslint-plugin-security+eslint-plugin-react起步,确保 CI 能跑通,再逐步引入 CodeQL 做深度分析。别一开始就想用 SonarQube,它对前端项目配置成本高,维护起来很累,尤其是团队人少的时候。顺带一提,你故意写
eval('xxx')但没触发,大概率是 ESLint 没扫到这个文件——检查下eslint.config.js(如果你用的是新格式)或者.eslintrc.js里的files/exclude配置,或者干脆用npx eslint src/test-file.ts手动跑一下看看有没有输出。