从微信小程序到全端:一个安全扫描器的演进之路(v0.5.0 → v0.6.0)

从微信小程序到全端:一个安全扫描器的演进之路(v0.5.0 → v0.6.0)

上一篇讲了"从 0 到 GitHub Marketplace",这一篇讲"从专用到通用"。一个只能扫微信原生小程序的工具,如何通过 3 次技术迭代,支持 Taro、uni-app、React、Vue,最终变成一个"全端前端安全扫描器"。

项目地址:


一、起点:v0.5.0 的能力边界

v0.5.0 发布时,工具能扫:

  • ✅ 微信原生小程序 (wx.request / e.detail.value / wx.setStorageSync)
  • ✅ 普通 Web 应用 (location.search / innerHTML)

但扫不了:

  • ❌ Taro (Taro.request / export default)
  • ❌ uni-app (uni.request)
  • ❌ React 组件 (export default function App())
  • ❌ Vue 单文件组件 (.vue 文件的 <script>)

这 4 个场景,覆盖了 80% 的中国前端项目。

决定 :把工具从"微信专用"扩展成"全端通用"。


二、第一次迭代:Taro 支持

2.1 加规则:Taro API 到 taint.yaml

Taro 的 API 和微信原生几乎一样 (Taro.request vs wx.request)------只要把 Taro 的 API 加到 sink 和 source 列表:

yaml 复制代码
sinks:
  - name: "Taro.request"
    dangerous_arg: [0, 1]
    severity: medium
    message: "Tainted data into Taro.request"
  - name: "Taro.setStorageSync"
    dangerous_arg: [1]
    severity: high
sources:
  - member: "Taro.getStorageSync"
  - member: "Taro.getStorage"

这一步很简单。

2.2 加项目类型识别

_detect_project_type 读 package.json:

python

kotlin 复制代码
if (root / "package.json").exists():
    try:
        import json
        pkg = json.loads((root / "package.json").read_text(encoding="utf-8-sig"))
        deps = {**pkg.get("dependencies", {}), **pkg.get("devDependencies", {})}
        if "@tarojs/taro" in deps:
            return "taro"
        if "@dcloudio/uni-app" in deps:
            return "uni-app"
    except Exception:
        pass

2.3 🐛 踩坑 1:PowerShell 写 package.json 带 BOM

用 Set-Content -Encoding UTF8 写的文件带 BOM (开头几个不可见字节 )------json.loads 读到就报错:

text

arduino 复制代码
json.decoder.JSONDecodeError: Unexpected UTF-8 BOM (decode using utf-8-sig)

修复 :utf-8 → utf-8-sig (自动去 BOM)。

2.4 🐛 踩坑 2:export default 没被处理

Taro 项目用 export default:

javascript

javascript 复制代码
export default function Pay() {
  const handlePay = (e) => {
    const amount = e.detail.value.amount;
    Taro.request({ url: '...' + amount });
  };
}

_exec_stmt 只处理 FunctionDeclaration ------但 export default function Pay() 的 AST 是 ExportDefaultDeclaration ------Pay 函数体根本没被执行 ------所以 Taro.request 没扫到。

修复 :在 _exec_stmt 里加 ExportDefaultDeclaration 和 ExportNamedDeclaration:

python

csharp 复制代码
elif t == "ExportDefaultDeclaration":
    decl = node.get("declaration", {})
    dt = decl.get("type")
    if dt == "FunctionDeclaration":
        self._handle_function_like(decl, decl.get("id", {}).get("name", ""), file_path)
    elif dt in ("ArrowFunctionExpression", "FunctionExpression"):
        self._handle_function_like(decl, "__default__", file_path)

elif t == "ExportNamedDeclaration":
    decl = node.get("declaration")
    if decl:
        self._exec_stmt(decl, file_path)

这个 bug 影响很大 ------不只是 Taro ------所有用 ES Module 的项目 (React / Vue / 现代前端 )都受影响。

2.5 🐛 踩坑 3: _handle_var_decl 的缩进错误

const handlePay = (e) => { ... } 是 VariableDeclaration------需要处理 ArrowFunctionExpression ------但你加 elif 时缩进错了:

python

ini 复制代码
elif it == "MemberExpression":
    obj = self._member_name(init.get("object"))
    prop = self._member_name(init.get("property"))
    if obj and prop and obj != "this":
        self.aliases.record_prop_alias(name, obj, prop)
    elif it == "ObjectExpression":      # ← 错误!这个 elif 缩进太深了
        ...

ObjectExpression 和 FunctionExpression 的 elif 被错误地嵌套在了 if it == "MemberExpression" 里面 ------永远进不去。

修复 :统一缩进:

python

python 复制代码
elif it == "MemberExpression":
    obj = self._member_name(init.get("object"))
    prop = self._member_name(init.get("property"))
    if obj and prop and obj != "this":
        self.aliases.record_prop_alias(name, obj, prop)
elif it == "ObjectExpression":          # ← 正确:和 if 平级
    ...
elif it in ("FunctionExpression", "ArrowFunctionExpression"):
    self._handle_function_like(init, name, file_path)

2.6 结果

修复后 ------Taro 100% 支持 ------并且顺手修好了 export default (顺便覆盖 React / Vue)。


三、第二次迭代:uni-app 支持

uni-app 的 API 更简单 ------uni.request / uni.setStorageSync ------只要加规则:

yaml

yaml 复制代码
sinks:
  - name: "uni.request"
    dangerous_arg: [0, 1]
    severity: medium
  - name: "uni.setStorageSync"
    dangerous_arg: [1]
    severity: high

sources:
  - member: "uni.getStorageSync"
  - member: "uni.getStorage"

10 分钟搞定 ------因为项目类型识别已经在 Taro 那次做好了。


四、修复 _resolve 单层路径(重要)

4.1 问题

你的 DSL 规则用了 match: [value, ...] ------但 value 被当字面量返回 ------导致规则不命中。

4.2 根因

_resolve 方法:

python

lua 复制代码
if "." not in path:
    return path      # ← 单层路径一律当字面量

"value" 不含 . ------返回字符串 "value" ------而不是 ctx.node["value"] 。

4.3 修复

python

lua 复制代码
if "." not in path:
    # 单层路径:先尝试从 node 取
    if isinstance(ctx.node, dict) and path in ctx.node:
        return ctx.node[path]
    # 否则当字面量(比如 "eval"、"debug")
    return path

这个修复让 DSL 规则表达能力提升了一个档次 ------可以写 eq: [value, "..."] 这种细粒度规则。


五、新增规则:信息泄露(补天场景)

加了 3 条"补天场景"规则:

  • DSL-060 :API 路径泄露(/api/admin/xxx / /api/internal/xxx)
  • DSL-061 :Debug 模式开启(debug: true)
  • DSL-062 :测试凭据硬编码(test_user: "admin")

这些规则能帮你在补天上快速发现"信息泄露"线索。


六、踩过的所有坑(完整清单)

坑 原因 修复
export default 没扫到 AST 是 ExportDefaultDeclaration 加节点处理
package.json BOM 报错 Set-Content 写文件带 BOM 用 utf-8-sig
elif 缩进错 粘贴时缩进多了 统一缩进
_resolve 单层路径 不含 . 一律当字面量 先查 ctx.node
Taro.request 不命中 ObjectExpression 没返回污点 修 _handle_var_decl
editable install 缓存 改代码不生效 --force-reinstall
CLI 编码崩溃 GBK 不支持 emoji colorama.just_fix_windows_console

每一个坑都值得写一篇文章。


七、数据:v0.5.0 → v0.6.0

指标 v0.5.0 v0.6.0 变化
支持框架 微信 / Web 微信 / Taro / uni-app / React / Vue / Web +4
规则数 50 55+ +5
污点 sink 15 23 +8
污点 source 25 35 +10
测试 25 25(全绿) 不变
Commits 37 42+ +5

核心变化 :从"微信专用"到"全端通用"。


八、为什么这件事很重要

前端生态是"多框架"的:

  • 小程序:微信原生 / Taro / uni-app
  • Web:React / Vue / Angular / Svelte

一个只支持"微信原生"的工具 ------只能覆盖 20% 的项目。

一个支持"全端"的工具 ------能覆盖 80% 的项目。

从 20% 到 80% ------只需要加几个 API 规则 + 修复 export default ------ROI 极高。


九、下一步

继续优化:

  • 支持 Vue SFC 完整解析 (.vue 文件的 <template>)
  • 支持 React JSX 解析
  • 加更多规则(越权 / SDK 滥用 / CSRF)

让工具从"能扫"到"扫得准"。


十、开源地址

欢迎 Star / Issue / PR。


十一、总结

从 v0.5.0 到 v0.6.0 ------核心是"三个修复 + 两个规则" :

  • 修 export default (覆盖 React / Vue / Taro)
  • 修 _resolve 单层路径 (提升 DSL 表达能力)
  • 修 _handle_var_decl 缩进 (修复箭头函数处理)
  • 加 Taro API 规则 (支持 Taro)
  • 加 uni-app API 规则 (支持 uni-app)

每一个修复都让工具更通用 ------每一个规则都让工具更精准。

开源项目的演进 ------不是一次写完 ------是持续迭代 ------每次解决一个真实问题。

加油! 大家加油,努力复盘总结🚀

相关推荐
seconp1 小时前
计算机毕设做什么项目?整理了 80 个 Web、小程序、AI 与数据方向
前端·小程序·课程设计
代码小学僧1 小时前
如何低成本部署网站?Vercel 和 Cloudflare 怎么选?
前端·后端·serverless
Csvn1 小时前
加载性能
前端
行者全栈架构师1 小时前
【鸿蒙心迹】让应用“悬浮“起来——闪控球(floatingBall)从接入到实战全链路(HarmonyOS 7.x)
前端·算法·架构
萧瑟余晖1 小时前
Spring Security综合实战与安全加固详解
java·安全·spring
广东帝工1 小时前
如何保障智慧航道的安全
安全
用户6353300254792 小时前
Day 6|ES6+ 语法:把现代 JS 写顺手,从解构到 map / filter / reduce
前端
铁皮饭盒2 小时前
CPU就行, 老照片变清晰, 本地部署582mb腾讯的模型, 本地部署, 过节了给长辈们变个小魔术
前端·javascript·后端
其实防守也摸鱼2 小时前
Linux文本处理命令理论学习
linux·服务器·前端·数据库·sql·学习·安全