aznikline/harmony-security
GitHub: aznikline/harmony-security
一款用于扫描和修复 HarmonyOS NEXT 应用中安全漏洞的 CLI 与 TypeScript SDK,支持确定性规则检测、LLM 验证及自动补丁。
Stars: 0 | Forks: 0
# harmony-security
[](https://github.com/aznikline/harmony-security/actions/workflows/ci.yml)
[](https://www.npmjs.com/package/@wizout/harmony-security)
[](./LICENSE)
English | [简体中文](./README.zh-CN.md)
一个用于**查找、验证和修复** [HarmonyOS NEXT](https://www.harmonyos.com/) 应用 (ArkTS / ArkBytecode `.abc`) 中**安全漏洞**的 CLI 和 TypeScript SDK。
`harmony-security` 是一个**编排器**:它驱动 [harmonyos-reverse-skill](https://github.com/aznikline/harmonyos-reverse-skill) 反编译 pipeline,对产物运行确定性的规则引擎,可选地使用 LLM 来验证候选项并生成/应用补丁,并验证扫描契约 —— 最终为 CI/IDE 消耗生成 SARIF。它针对 HarmonyOS 特化了编排器模式,因为目前不存在成熟的 jadx 等价物,且通用 LLM 对 ArkBytecode 的知识较弱 —— 因此确定性规则负责高置信度的发现,而 LLM 仅用于抑制误报和编写补丁。
## 工作原理
```
scan
│
├─ decompile pipeline (abcde + dayu) → structured artifacts
│ ├─ fingerprint — abc vs dex routing, encrypted-ABC gate
│ ├─ unpack — module.json, ets/*.abc, libs/*.so
│ ├─ string-sweep — URLs / crypto / auth / JSON markers
│ ├─ dump-classes — class enumeration
│ ├─ xref (v0.2) — sink caller cross-reference (source→sink evidence)
│ └─ abc-inspect (v0.2) — PANDA magic + version + structural validity
├─ rule engine (6 families) → candidate findings w/ evidence
├─ LLM verify (optional) → confirm / false-positive / needs-runtime
├─ LLM patch (source targets) → diff → git apply → build check → FAIL_TO_PASS → rollback
└─ contract validation → manifest / findings / coverage / SARIF
```
### 规则家族
| 家族 | 检测内容 |
|---|---|
| `HMOS-CRYPTO-*` | 硬编码的密钥/IV、ECB、用于签名的弱哈希、缺少 HMAC |
| `HMOS-WEBVIEW-*` | 带有不可信输入的 `runJavaScript`、敏感的 jsBridge 方法 |
| `HMOS-PERMS-*` | 危险权限、相对于声明的 abilities 的过度授权 |
| `HMOS-WEBVIEW-*` | 带有不可信输入的 `runJavaScript`、敏感的 jsBridge 方法 |
| `HMOS-PERMS-*` | 危险权限、相对于声明的 abilities 的过度授权 |
| `HMOS-STOR-*` | preferences/文件中的敏感数据、日志中的机密信息 |
| `HMOS-NET-*` | 明文 HTTP、禁用 TLS 验证、缺少 pinning |
| `HMOS-INTENT-*` | 没有权限保护的导出 abilities、带有机密的隐式 intents |
规则是确定性的,并在反编译产物(类、方法 pcode、字符串命中、xref 调用者)上运行。webview 规则是**流感知的**:它使用 xref 确认 sink 具有真实的调用者(`callarg*`/`callthis*`),当仅发现 `definefunc` 引用时,会将其降级为中等置信度 —— 这是一种无需完整污点层的轻量级 source→sink 检查。
## 快速开始
### 前置条件
- **Node.js** ≥ 22.13(支持 24.x / 26.x)
- **Python** ≥ 3.10(用于 dayu)
- **JDK 17**(用于 abcde)
- harmonyos 工具链:[abcde](https://github.com/Yricky/abcde) + [dayu](https://github.com/hx1997/dayu),构建在同一个根目录下
- (可选)来自 OpenHarmony SDK 的 `ark_disasm` —— 启用 dayu pcode 反编译和对 Panda Assembly 的 xref
- (可选,用于 LLM 验证/补丁)任何兼容 OpenAI 的 Chat Completions endpoint 的 API key
#### 一次性构建工具链
```
export TOOLCHAIN_ROOT=~/harmonyos-tools
mkdir -p "$TOOLCHAIN_ROOT" && cd "$TOOLCHAIN_ROOT"
# JDK 17 (abcde 要求 17+)
brew install openjdk@17
export JAVA_HOME=/opt/homebrew/opt/openjdk@17 # adjust for your platform
# abcde — ArkBytecode disassembler/decompiler + HAP browser (Kotlin/JVM)
git clone --depth 1 https://github.com/Yricky/abcde.git
cd abcde && ./gradlew :abcdecoder:packageReleaseUberJarForCurrentOS --no-daemon && cd ..
# dayu — ArkBytecode parser/decompiler (Python)
git clone --depth 1 https://github.com/hx1997/dayu.git
python3 -m venv .venv && .venv/bin/pip install -r dayu/requirements.txt
```
有关 `ark_disasm`、`hdc` 和平台说明,请参阅 [harmonyos-reverse-skill 设置指南](https://github.com/aznikline/harmonyos-reverse-skill)。
### 安装 + 扫描
```
npm install @wizout/harmony-security
# rule-only mode (无 LLM,无 patch) — 最快,完全离线
npx harmony-security scan app.hap --no-llm
# full mode: rule engine + LLM verify + patch (源代码目标)
export HARMONY_SECURITY_LLM_KEY=... # your provider's API key
export HARMONY_SECURITY_LLM_BASE=https://your-provider.example.com/v1
npx harmony-security scan app.hap --model your-model --effort high
# 扫描源代码目录 (white-box; patch 应用于目录树)
npx harmony-security scan ./my-arkts-project --failure-severity high
```
将 SDK 指向你的工具链(如果在默认根目录下则会自动检测):
```
export HARMONYOS_TOOLCHAIN_ROOT=~/harmonyos-tools
```
### TypeScript SDK
```
import { HarmonySecurity } from "@wizout/harmony-security";
const security = new HarmonySecurity({
model: "your-model", // empty/omitted + no key → rule-only mode
});
const result = await security.run("app.hap", {
target: "app.hap",
mode: "standard",
auth: "auto",
onProgress: (phase, detail) => console.log(`[${phase}] ${detail}`),
});
console.log(result.reportPath); // .../report.md
console.log(result.sarifPath); // .../exports/results.sarif
await security.close();
```
## 命令
```
scan Scan a .hap/.app/.abc or source directory.
preflight Dry-run: validate target + auth without scanning.
validate Re-run contract validation on an existing scan dir.
scans list List recent scans (from persistent state).
scans show Show a scan summary by scan-id.
findings show Show a finding by its fingerprint id.
findings dismiss [reason] Persistently dismiss a finding (skips LLM on re-scan).
findings undismiss Undo a dismissal.
findings list List dismissed findings.
info Show toolchain + version metadata.
login / logout Configure ambient LLM auth.
completions Emit shell completions (bash/zsh).
```
### 处理误报
消除流程是持久化且跨扫描的:一个被消除的发现(通过其
SHA-256 指纹)在后续的每次扫描中都会被跳过 —— 无 LLM 成本,也
不会在 CI 中重新报告。用户的消除操作会在 SARIF 中显示为抑制状态
`accepted`(人工确认),这不同于 LLM 判定的误报,后者为
`underReview`(模型推导得出,绝不能自动消除真实的漏洞)。
```
harmony-security scan app.hap --no-llm
harmony-security scans list # find the scan-id
harmony-security scans show # see severity breakdown
harmony-security findings show # inspect before dismissing
harmony-security findings dismiss "doc URL, not a real key"
# 下次扫描:此 finding 被自动抑制,无 LLM 调用
```
### 扫描选项
| 标志 | 用途 |
|---|---|
| `--model ` | 用于验证 + 补丁的 LLM 模型(设置为你的提供商的模型) |
| `--effort ` | 推理力度:low \| medium \| high |
| `--auth ` | auto \| api-key \| none |
| `--mode ` | standard \| deep |
| `--no-llm` | 仅规则模式(跳过 LLM 验证/补丁) |
| `--output-dir ` | 覆盖扫描输出目录(默认:`~/.harmony-security/state/scans/…`,持久化) |
| `--max-cost-usd ` | LLM 成本上限 |
| `--failure-severity ` | 在达到/低于以下级别时使扫描失败:critical \| high \| medium \| low |
| `--archive-existing` | 归档现有输出目录内容 |
### 环境变量
| 变量 | 用途 |
|---|---|
| `HARMONY_SECURITY_LLM_KEY`(首选)—— 或 `OPENAI_API_KEY`(兼容) | LLM API key(仅保留在内存中,从不持久化) |
| `HARMONY_SECURITY_LLM_BASE` | LLM base URL(你的提供商的兼容 OpenAI 的 endpoint) |
| `HARMONY_SECURITY_STATE_DIR` | 覆盖扫描状态目录 |
| `HARMONYOS_TOOLCHAIN_ROOT` | 覆盖 abcde/dayu 工具链根目录 |
| `HARMONYOS_SKILL_ROOT` | 覆盖 harmonyos-reverse-skill 仓库根目录 |
| `HARMONY_SECURITY_TRUST_SKILL_DEV` | 跳过 skill-script 完整性检查(仅限本地开发) |
## 输出契约
每次扫描都会写入一个私有的扫描目录:
```
/
├── scan-manifest.json # target, mode, producer, toolchain
├── findings.json # confirmed + false-positive findings (SHA-256 fingerprinted)
├── coverage.json # which files were processed
├── report.md # human-readable report
├── exports/results.sarif # SARIF 2.1.0 (CI/IDE consumable)
├── decompile/ # pipeline artifacts
├── triage/triage-cache.json # LLM verdict cache (fingerprint-keyed)
└── patches/*.diff # applied patches (source targets)
```
**发现结果基于 `(ruleId, location, evidence)` 生成 SHA-256 指纹**。契约验证器会拒绝任何指纹不匹配、`scanId` 在不同文档间不一致,或文件包含符号链接/路径遍历的扫描 —— 被篡改、截断或伪造的扫描无法被认定为完整。
SARIF 输出携带稳定的 `primaryLocationLineHash` 指纹(以便 GitHub Code Scanning 在多次运行中去重),填充的 `fixes`(来自应用的补丁)、规则 `properties`(安全严重性、精确度、标签),以及用于 LLM 误报判定的 `suppressions`(标记为 `underReview`,**而不是** `accepted` —— LLM 判定并不等同于人工消除)。
## 补丁安全性
补丁仅应用于**源目标**(`.ets`)。应用 pipeline 是深度防御的:
1. 在触及工作树之前对其进行**检查点操作**(`git stash create+store`)—— 失败的补丁永远不会丢弃你未提交的工作。
2. **漂移级联**应用:clean → `--3way --recount` → 已应用视为 no-op → `old_str` 唯一性保护。
3. **构建验证** —— 当没有可用的项目验证器(tsc/hvigorw/pnpm/npm)时拒绝补丁,而不是盲目乐观地接受。
4. **FAIL_TO_PASS 门控** —— 重新运行发起规则以确认发现已消失且未引入新的同规则发现。当源→字节码重新运行不可用时,会诚实跳过(并附带原因)。
5. **单补丁回滚** —— 仅触及已打补丁的文件,绝不单纯使用 `git restore .`。
二进制(`.hap`/`.abc`)目标以 ArkTS 伪代码的形式接收**修复建议** —— 绝不进行自动修改(契约禁止修补二进制文件)。
## 安全模型
请阅读 [`SECURITY.md`](./SECURITY.md)。简而言之:
- 该工具在你的本地 OS 账户下运行。仅扫描**受信任的**包/仓库。
- 它**不**在共享同一账户的任务/仓库之间提供 OS 级别的隔离。请使用 [容器运行时](./docker/) 或单独的账户进行隔离。
- `trusted-executable` 通过**文件系统位置**而非加密签名来解析工具链二进制文件 —— 它可以防止从被扫描的树内部进行的 PATH 注入,但不能防止恶意的、可写的受信任目录。
- **Skill-script 完整性**:harmonyos-reverse-skill 脚本由 SHA-256 清单锁定,并在每次生成前进行验证,因此被篡改的脚本无法伪造喂给规则的反编译输出。(`HARMONY_SECURITY_TRUST_SKILL_DEV=1` 可在本地开发时跳过此步骤。)
- LLM 凭证仅保留在内存中 —— 绝不持久化到磁盘,绝不通过 argv 传递,也绝不会被记录。
- LLM 输出仅供参考;规则 + 契约验证会门控每一个发现结果。
## 诚实的局限性
- **ArkBytecode 没有成熟的 jadx 等价物。** dayu 只是一个部分反编译器;pcode 可能包含 `goto`/伪函数。请对照 abcde 反汇编来验证反编译输出。
- **`ark_disasm` 未被捆绑。** dayu pcode 反编译和 xref 需要来自 `ark_disasm` 的 Panda Assembly (`.pa`) 文件;如果没有它,pipeline 将回退到 abcde 反汇编 + 字符串扫描(对于加密/endpoint 仍然具有高信号)。
- **加密的 ABC。** AppGallery 代码保护的 `.abc`(PANDA magic 不匹配)会在阶段 0 被检测到并阻止 —— abcde/dayu 无法解析它。请获取非 AppGallery / OpenHarmony 构建,或从设备内存中转储。**AppGallery 审查不会检查此工具发现的漏洞** —— 加密分发和代码安全是两个独立的问题。
- **规则引擎读取的是字符串,而非执行路径。** capabilities JSON 中的类似 `"aes-128-ecb"` 的常量会触发 `HMOS-CRYPTO-002`,但该应用可能实际上从未构造过该密码。引擎报告的是代码*引用*的内容,而不一定是它*执行*的内容。请使用 .pa + xref 进行交叉验证。
- **混合架构的盲点。** 使用 Cangjie、React Native 或 Flutter 的应用将业务逻辑放在原生的 `.so` 文件中,这对 ABC 分析是不可见的。该工具只能看到 ArkTS / ArkBytecode 层 —— 并且一些大型应用(例如携程)只有不到 10% 的安全相关代码位于 ABC 中。
- **xref 是文本 grep,而非数据流。** 交叉引用过程是在 Panda Assembly 上执行 `grep` —— 它可以找到调用点,但不跟踪污点传播。source→sink 链是近似的,未经证实。
- **动态分析是手动的。** `hdc`/`hilog` 确认超出了自动化扫描的范围。
## 真实案例研究:Ctrip (携程) HAP
为了演示该工具能做什么和不能做什么,我们分析了 **Ctrip V8.94.4** —— 一个 215 MB 的旅行应用 HAP,具有混合架构(ArkTS + Cangjie + React Native + Flutter)。
### 目标概况
| 属性 | 值 |
|----------|-------|
| Bundle | `com.ctrip.harmonynext` |
| 构建模式 | `debug`(代码保护**已禁用**) |
| ABC 大小 | 50 MB(1 个文件:`ets/modules.abc`) |
| 原生代码 | 183 个 `.so` 文件 (arm64-v8a) |
| 框架 | Cangjie 标准库、React Native (`librnoh_*.so`)、Flutter (`libflutter.so`) |
| Source map | 9 MB `ets/sourceMaps.map`(debug 构建) |
| ARC 版本 | `ark12.0.6.0` |
### 发现的内容
#### 仅使用字符串扫描(无 `.pa`)
| 规则 | 发现 | 判定 |
|------|---------|---------|
| `HMOS-CRYPTO-001` | miniProgram URL 中的硬编码敏感参数 | ⚠️ 部分正确 —— `token=null` 表明这些值是空的占位符 |
| `HMOS-CRYPTO-002` | 常量池中的 ECB 密码模式字符串 | ⚠️ 可能是误报 —— 它是一个 capabilities 字典,而非构造点 |
| `HMOS-CRYPTO-003` | 弱哈希算法列表 | ⚠️ 同上 —— 算法枚举,而非实际使用 |
#### 使用 `.pa` + xref(在 `arkasm` 反编译之后)
| Sink 类别 | 调用点 | 含义 |
|---------------|-----------|---------------|
| `runJavaScript` | 10 | WebView JS 注入面 —— 部分调用传递了动态字符串 |
| `loadUrl` | 189 | URL 加载 —— 大多数是合法的页面加载;如果接受了不可信输入则有风险 |
| `httpRequest` / `fetch` | 32,302 | 网络调用 —— 绝大多数是正常的业务流量,而非漏洞 |
| `.encrypt` / `.decrypt` / `.sign` | 762 | 加密操作 —— 密钥管理审查的候选项 |
| `writeFileSync` / `openFile` | 254 | 文件 I/O —— 检查全局可读路径中是否有敏感数据 |
| `startAbility` / `want` | 222 | Intent 启动 —— 验证导出的 abilities 和隐式 intent 处理 |
### 架构分布
```
Total HAP size: 215 MB
├─ Native .so (Cangjie/RN/Flutter): ~160 MB ← invisible to ABC analysis
├─ ets/modules.abc: 50 MB ← what this tool can analyze
├─ resources/assets: ~4 MB
└─ module.json + metadata: ~1 MB
```
**预估的业务逻辑分布:**
| 层级 | 份额 | 可分析? |
|-------|-------|------------|
| ArkTS (ABC) | ~5–10% | ✅ —— 字符串扫描、dump-classes、xref |
| Cangjie (native .so) | ~20% | ❌ —— 闭源原生代码 |
| React Native (.so 中的 JS bundle) | ~40% | ❌ —— 编译为 `.so` 中的 Hermes 字节码 |
| Flutter (.so 中的 Dart AOT) | ~30% | ❌ —— `.so` 中的 Dart AOT |
### 关键要点
1. **该工具实现了端到端的工作。** 它可以解包、反编译、运行规则并生成 SARIF。字符串扫描得出的 3 个发现是常量池中的真实字符串 —— 至于它们是否属于漏洞,取决于代码如何使用它们。
2. **如果没有 `ark_disasm`,敏感度会很低。** 仅靠字符串扫描会遗漏所有的交叉引用数据。有了 `.pa`,该工具能找到数百个 sink 调用点,指明了该去*哪里*查看 —— 但它们需要人工分类处理。
3. **纯 ArkTS 应用是最佳场景。** 一个 Hello World 或中等规模的纯 ArkTS 应用将产生高得多的信噪比。当目标是**带有纯 ArkTS 代码和 debug 符号的 OpenHarmony / 第三方 HAP** 时,该工具的全部潜力才能得到发挥。
4. **AppGallery 代码保护的构建是不可见的。** 这是一个平台约束,而不是工具限制。如果 `.abc` 在内核级别被加密,则没有用户空间工具可以读取它 —— 本工具也不例外。
5. **通过 AppGallery 审查 ≠ 安全。** AppGallery 检查的是恶意软件和隐私合规性,而不是硬编码的密钥、弱加密、WebView 注入面或不安全的 intent 处理。而这些正是该工具检测的内容。
## 容器化扫描
为 CI/隔离提供了一个非 root、非交互式的容器:
```
# 将您的 .hap 挂载到 /input (只读),从 ./output 读取结果
HARMONY_SECURITY_LLM_KEY=... docker compose run --rm scan scan /input/app.hap --no-llm
```
请参阅 [`docker/compose.yaml`](./docker/compose.yaml)。该镜像以 uid 10001 运行,禁用 git 凭证提示,并将写入范围限制在 `/output`。
## 开发
```
cd sdk/typescript
pnpm install
pnpm lint # tsc --noEmit
pnpm build
pnpm test # bun test, 69 tests
pnpm test:package # build + npm pack + import smoke
# 更新 skill 后重新生成 skill-script hash manifest
node scripts/generate-skill-hashes.mjs /path/to/harmonyos-reverse-skill
```
真实技能集成测试仅在 `HARMONYOS_SKILL_ROOT` 指向本地 skill checkout 时运行;在 CI 上它们会被跳过(固定的清单是开发人员本地的)。
## 路线图
**已完成**
- 编排器核心:targets、runtime、contract、SARIF、6 个规则家族
- 补丁安全:checkpoint、漂移级联、构建验证、FAIL_TO_PASS
- LLM 验证:triage 缓存、4 桶判定、结构化输出
- v0.2 skill 集成:加密-ABC 门控、xref、abc-inspect
- 发布来源:npm provenance、SBOM、构建证明
- 跨扫描分类持久化 (`/triage-cache.json`) + 用户消除
- 扫描历史记录:持久化存储上的 `scans list/show`、`findings show`
- 遵循 `--output-dir`;修复了 scanId 冲突(重复扫描不会发生冲突)
**计划中(Tier 2)**
- 规则即数据 DSL(规则作为可加载的 YAML,而非编译的 TS)
- 轻量级方法内数据流 + SARIF codeFlows
- 迭代式 LLM 上下文收集验证循环
- 声明式 ArkTS source/sink/sanitizer 目录
- MASVS/MASTG 类别扩展 (AUTH/RESILIENCE/CODE/PRIVACY)
- 基准语料库(DroidBench 风格的标记 `.abc`)
## 许可证
Apache-2.0。请参阅 [`LICENSE`](./LICENSE)。
此包仅通过 CLI **调用**外部工具 [abcde](https://github.com/Yricky/abcde) (Apache-2.0) 和 [dayu](https://github.com/hx1997/dayu) (AGPL-3.0) —— 它们并未被捆绑或重新分发。请按照设置指南构建它们并遵守其许可证。harmonyos-reverse-skill 脚本通过路径调用并由 SHA-256 锁定;它们同样未被捆绑。
标签:ArkTS, C2, GraphQL安全矩阵, LNA, MITM代理, TypeScript, 云资产清单, 安全扫描器, 安全插件, 自动化攻击, 逆向工具, 逆向工程, 错误基检测, 静态代码分析, 鸿蒙应用