aznikline/harmony-security

GitHub: aznikline/harmony-security

一款用于扫描和修复 HarmonyOS NEXT 应用中安全漏洞的 CLI 与 TypeScript SDK,支持确定性规则检测、LLM 验证及自动补丁。

Stars: 0 | Forks: 0

# harmony-security [![CI](https://github.com/aznikline/harmony-security/actions/workflows/ci.yml/badge.svg)](https://github.com/aznikline/harmony-security/actions/workflows/ci.yml) [![npm](https://img.shields.io/npm/v/@wizout/harmony-security.svg)](https://www.npmjs.com/package/@wizout/harmony-security) [![license](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](./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, 云资产清单, 安全扫描器, 安全插件, 自动化攻击, 逆向工具, 逆向工程, 错误基检测, 静态代码分析, 鸿蒙应用