从 0 到 GitHub Marketplace:一个安全工具的开源全记录
这是我做 miniapp-scanner(一个微信小程序 + Web 安全扫描器)的完整历程。从想法到上架 GitHub Marketplace,一共 37 次 commit、25 个单元测试、2 篇技术文章。文章分两条线:技术实现 + 开源运营。
项目地址:
- GitHub: github.com/lathuninh/m…
- Marketplace: github.com/marketplace…
- PyPI: pypi.org/project/min…
一、起点:为什么做这个
1.1 微信小程序的安全问题
微信小程序已成为移动应用的重要形态,但针对小程序的安全审计工具非常缺乏:
- 传统 SAST(Fortify、Checkmarx)不支持小程序特有 API
- 通用 JS 扫描器无法追踪小程序特有的污点源(
e.detail.value、wx.request) - 手工审计效率低、遗漏多
1.2 现有工具的不足
| 工具 | 问题 |
|---|---|
| ESLint security | 通用 JS,不认小程序 API |
| Fortify | 商业贵,不支持小程序 |
| SonarQube | 需要服务器,配置复杂 |
结论:没有"开箱即用"的小程序安全扫描器。
决定:自己做一个。
二、技术实现:四引擎架构
2.1 整体架构
miniapp-scanner/
├── src/miniapp_scanner/
│ ├── engine/
│ │ ├── js_parser.py # AST 解析 + 常量折叠
│ │ ├── alias_tracker.py # 别名分析(并查集)
│ │ ├── module_graph.py # 跨文件模块图
│ │ ├── taint_analyzer.py # 污点分析引擎
│ │ ├── ast_analyzer.py # AST 规则引擎
│ │ ├── rule_dsl.py # DSL 规则引擎
│ │ └── wxapkg_unpacker.py # .wxapkg 解包
│ ├── llm/ # LLM 研判 + 报告
│ ├── rules/ # YAML 规则库
│ └── scanner.py # 扫描器主流程
text
2.2 四个引擎
为什么需要四个引擎?
| 引擎 | 适用场景 | 例子 |
|---|---|---|
| 正则 | 快速匹配已知模式 | 硬编码 AK/SK |
| AST | 语法级精准匹配 | `innerHTML = ...` |
| DSL | 自定义规则语言 | `arg_tainted: [0]` |
| 污点 | 追踪数据流传播 | `e.detail.value → eval` |
四个引擎互补:
- 正则 快但粗糙
- AST 精准但写规则麻烦
- DSL 灵活但需要学习语法
- 污点 深入但慢
2.3 污点分析:核心引擎
原理:从"污点源"(用户输入)出发,追踪数据经过的每一次赋值、拼接、函数调用,直到"危险汇聚点"(sink)。
例子:
```javascript
Page({
onLoad(options) {
const id = options.id; // 污点源
const a = id; // 传播
const b = a; // 传播
request("/api?id=" + b, {}); // 汇聚点
}
});
扫描器输出:
text
🔴 [1] 污点传播: 用户输入 → wx.request (high)
🧬 污点路径:
→ onLoad 的参数 options(用户可控)
→ 读取 options.id(用户可控)
→ 存入变量 id
→ 存入变量 a
→ 存入变量 b
→ 字符串拼接
→ 跨文件传入 utils/request.js::url
→ 经 wx.request() 传递
2.4 关键技术点
(1)别名分析(并查集)
python
class UnionFind:
def find(self, x):
if x not in self.parent:
self.parent[x] = x
return x
# 路径压缩
root = x
while self.parent[root] != root:
root = self.parent[root]
while self.parent[x] != root:
self.parent[x], x = root, self.parent[x]
return root
def union(self, a, b):
ra, rb = self.find(a), self.find(b)
if ra != rb:
self.parent[ra] = rb
效果:
javascript
const a = userInput;
const b = a;
const c = b;
eval(c); // c 通过并查集归到 a 的等价类 → 污染
(2)跨文件追踪
pages/index.js 调用 utils/request.js 的 request() —— 用户输入跨文件传播。
方案:
- 构建模块依赖图(
module_graph.py) - 解析所有
require/module.exports - 遇到导入函数调用时,跳进被调用文件的函数体
(3)链式调用传播
javascript
const x = location.search.slice(1).trim();
eval(x);
修复:在 _eval_call 里,检查 callee.object 是否被污染。
(4)控制流敏感
javascript
const DEBUG = false;
if (DEBUG) {
eval(userInput); // 不可达
}
方案:常量折叠 + 条件求值。
(5)AST 缓存(性能)
python
_PARSE_CACHE = {}
def parse_js(source):
if source in _PARSE_CACHE:
return _PARSE_CACHE[source]
tree = esprima.parseScript(source)
_PARSE_CACHE[source] = tree
return tree
效果:9.6s → 8s(52 文件)。
2.5 LLM 集成
为什么需要 LLM:静态分析误报多 —— AI 可以判断真假阳性。
prompt 设计:
我是一名资深微信小程序安全审计专家,判断报警是真阳性(TP)、假阳性(FP)还是无法确定(UNCERTAIN)。
研判要点:
- 代码是否真的会执行到危险逻辑
- 输入源是否真的可控
- 是否已通过净化函数
- 触发条件是否可达
输出必须严格为 JSON: {"verdict": "TP|FP|UNCERTAIN", "confidence": 0-100, "reason": "...", "fix": "..."}
效果:从 30 条误报优化到 5 条真问题(降噪 83% )。
三、工程化:从"能跑"到"可用"
3.1 25 个单元测试
tests/ ├── test_alias_tracker.py # 别名分析
├── test_js_parser.py # AST 解析
├── test_new_rules.py # 新规则
├── test_rule_dsl.py # DSL 引擎
├── test_scanner_e2e.py # 端到端
└── test_taint_analyzer.py # 污点分析
覆盖率:核心引擎 80%+ 。
3.2 GitHub Actions CI
yaml
name: CI on: [push, pull_request] jobs: test: runs-on: ubuntu-latest strategy: matrix: python-version: ["3.9", "3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v5 - uses: actions/setup-python@v6 - run: pip install -e ".[dev]" - run: pytest -v
效果:每次 push 自动跑 4 个 Python 版本的测试。
3.3 MIT License
用 GitHub 模板一键生成。
3.4 用户级配置
~/.miniapp-scanner.yaml —— LLM key 不随项目走。
yaml
llm: api_key: sk-xxx base_url: api.deepseek.com/v1 model: deepseek-chat
好处:
- 一份配置,所有项目共享
- 换 key 只改一个文件
- 密钥不进 git
四、发行:从"本地项目"到"全球工具"
4.1 上线 GitHub
bash
git init
git add .
git commit -m "miniapp-scanner v5"
git remote add origin github.com/lathuninh/m…
git push -u origin main
踩坑:
- 网络不稳定 → 重试 3-5 次
- 认证问题 → 用 Personal Access Token
- SSH 太老 → 用 HTTPS
4.2 发布 PyPI
bash
pip install build twine
python -m build
python -m twine upload dist/*
踩坑:
- 2FA 必须开启(PyPI 强制要求)
- Token 权限要选
Entire account - 首次上传必选
Entire account
效果:全世界 pip install miniapp-scanner。
4.3 GitHub Release
版本号 v0.5.1 —— Latest 徽章。
4.4 GitHub Marketplace
创建 action.yml:
yaml
name: 'MiniApp Security Scan'
description: 'Scan WeChat mini-program / Web app for security issues'
branding:
icon: 'shield'
color: 'red'
inputs:
target:
required: true
default: '.'
runs:
using: 'composite'
steps:
- uses: actions/setup-python@v6
- run: pip install miniapp-scanner
- run: miniapp-scanner "${{ inputs.target }}"
发布 Release → 勾选 Marketplace → 填 Category → Publish。
效果:任何人可以 uses: lathuninh/miniapp-scanner@v0.5.1。
踩坑:
- 必须开启 2FA(GitHub 强制)
- 2FA APP 比 SMS 好(中国手机号收不到 GitHub 短信)
- 恢复代码保存好(丢了账号找不回)
五、传播:让更多人看到
5.1 掘金文章
第一篇:《从零构建微信小程序安全扫描器》
第二篇:《深入浅出:如何用 Python 实现一个小程序污点分析引擎》
第三篇:《从 0 到 GitHub Marketplace:一个安全工具的开源全记录》
5.2 README 徽章
markdown
[]
[]
[]
[]
效果:一眼看出项目质量。
六、数据
项目现状(截至发文):
| 指标 | 数据 |
|---|---|
| Commits | 37 |
| 代码行数 | 3000+ |
| 规则数 | 50 |
| 单元测试 | 25 |
| CI 运行 | 15+ 次全绿 |
| Python 版本 | 3.9 - 3.12 |
| GitHub Stars | 1 |
| PyPI 版本 | 0.5.0 |
| Release | v0.5.1(Latest) |
| Marketplace | 已上架 |
七、心得
7.1 技术上的收获
- 污点分析不难,难在细节(别名、跨文件、链式、控制流)
- 四引擎互补(正则快、AST 精准、DSL 灵活、污点深入)
- LLM 是"降噪神器" (误报过滤 83%)
- 缓存很重要(AST 缓存提升 13%)
7.2 工程上的收获
- 测试驱动开发(25 个测试发现了很多 bug)
- CI 是"质量门禁" (每次 push 全绿)
- 用户级配置(密钥不进 git)
- 版本化发布(PyPI + Release + Marketplace)
7.3 运营上的收获
- 技术文章是最好的传播(掘金带来流量)
- README 是"门面" (徽章 + 文档)
- Marketplace 是"官方认证" (同
actions/checkout同级) - 开源需要"持续维护" (Issue / PR / 新功能)
7.4 踩过的坑
_resolve单层路径问题("value"被当字面量)editable install缓存(改代码不生效 →--force-reinstall)colorama编码崩溃(GBK → UTF-8)deepcopy抵消缓存收益(去掉后 +13%)git add .误提交临时文件(.gitignore要加)- SSH 太老连不上 GitHub(改用 HTTPS)
- PyPI 强制 2FA(必须开启)
- GitHub 网络不稳定(重试 3-5 次)
八、下一步
继续优化:
- 支持 Taro / uni-app(跨端框架)
- 更多规则(越权、SDK 滥用、CSRF)
- 性能优化(换
tree-sitter解析器) - 多语言支持(英文文档)
让更多人用到我的工具
九、开源地址
- GitHub: github.com/lathuninh/m…
- Marketplace: github.com/marketplace…
- PyPI: pypi.org/project/min…
- 第一篇: juejin.cn/post/769459…
- 第二篇: juejin.cn/spost/76956…
欢迎 Star / Issue / PR。
十、总结
从一个想法到 GitHub Marketplace 上架:
- 技术:四引擎 + 污点分析 + LLM
- 工程:测试 + CI + License + 用户配置
- 发行:GitHub + PyPI + Marketplace
- 传播:掘金文章 + README 徽章
这不是"教程代码" —— 是一个真正的、企业可用的、全球可访问的开源工具。
整个过程 10 天 —— 如果你也想做开源,从今天开始。
记住:
- 从一个小工具开始(不要一开始就做大的)
- 写测试(防止改坏)
- 加 CI(自动化验证)
- 发文章(让更多人看到你的作品)
- 上 Marketplace(官方认证)
加油! 我们一起努力🚀