从微信小程序到全端:一个安全扫描器的演进之路(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)

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

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

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

相关推荐
去伪存真29 分钟前
AI Agent Harness 深度测评与技术选型实战
前端·agent
FRing033 小时前
vue2干货总结7(自定义指令、插槽、单页应用程序、路由的使用(VueRouter))
开发语言·前端·vue.js·vue2
疯华整猫6 小时前
被杀掉之后:我把 Android 的「唤起 + 常驻」重做了一遍
前端·面试·架构
不庸.7 小时前
theHarvester 入门教程:从装好到看懂输出
安全·web安全·网络安全·开源·安全性测试
严肃小白兔7 小时前
Cloudflare 更新 API Token 管理:从最小权限到自动化凭据安全
网络·安全·自动化
IT_陈寒8 小时前
Java开发实战:从入门到精通
前端·人工智能·后端
风骏时光牛马8 小时前
生成一个AI转岗经历标题,只需要一个标题,不需要其他的任何文本。
前端
Csvn18 小时前
Monorepo
前端
Csvn18 小时前
流水线
前端