从 0 到 GitHub Marketplace:一个安全工具的开源全记录

117 阅读7分钟

从 0 到 GitHub Marketplace:一个安全工具的开源全记录

这是我做 miniapp-scanner(一个微信小程序 + Web 安全扫描器)的完整历程。从想法到上架 GitHub Marketplace,一共 37 次 commit、25 个单元测试、2 篇技术文章。文章分两条线:技术实现 + 开源运营。

项目地址:


一、起点:为什么做这个

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() —— 用户输入跨文件传播。

方案:

  1. 构建模块依赖图(module_graph.py)
  2. 解析所有 require / module.exports
  3. 遇到导入函数调用时,跳进被调用文件的函数体

(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)。

研判要点:

  1. 代码是否真的会执行到危险逻辑
  2. 输入源是否真的可控
  3. 是否已通过净化函数
  4. 触发条件是否可达

输出必须严格为 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

[![Marketplace](https://img.shields.io/badge/Marketplace-...)]
[![CI](https://github.com/.../badge.svg)]
[![PyPI](https://img.shields.io/pypi/v/...)]
[![License](https://img.shields.io/badge/License-MIT-yellow.svg)]

效果:一眼看出项目质量。


六、数据

项目现状(截至发文):

指标数据
Commits37
代码行数3000+
规则数50
单元测试25
CI 运行15+ 次全绿
Python 版本3.9 - 3.12
GitHub Stars1
PyPI 版本0.5.0
Releasev0.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 踩过的坑

  1. _resolve 单层路径问题("value" 被当字面量)
  2. editable install 缓存(改代码不生效 → --force-reinstall)
  3. colorama 编码崩溃(GBK → UTF-8)
  4. deepcopy 抵消缓存收益(去掉后 +13%)
  5. git add . 误提交临时文件(.gitignore 要加)
  6. SSH 太老连不上 GitHub(改用 HTTPS)
  7. PyPI 强制 2FA(必须开启)
  8. GitHub 网络不稳定(重试 3-5 次)

八、下一步

继续优化:

  • 支持 Taro / uni-app(跨端框架)
  • 更多规则(越权、SDK 滥用、CSRF)
  • 性能优化(换 tree-sitter 解析器)
  • 多语言支持(英文文档)

让更多人用到我的工具

九、开源地址

欢迎 Star / Issue / PR。


十、总结

从一个想法到 GitHub Marketplace 上架:

  • 技术:四引擎 + 污点分析 + LLM
  • 工程:测试 + CI + License + 用户配置
  • 发行:GitHub + PyPI + Marketplace
  • 传播:掘金文章 + README 徽章

这不是"教程代码"  —— 是一个真正的、企业可用的、全球可访问的开源工具。

整个过程 10 天 —— 如果你也想做开源,从今天开始。

记住:

  • 从一个小工具开始(不要一开始就做大的)
  • 写测试(防止改坏)
  • 加 CI(自动化验证)
  • 发文章(让更多人看到你的作品)
  • 上 Marketplace(官方认证)

加油!  我们一起努力🚀