mcpField

happy-app-audit

github.com/iamzhihuix安全127 次安装
代码仓库

关于 happy-app-audit

Happy App Audit 是用于静态审计 macOS 应用遥测行为的技能,适合调查已安装的 .app 包及其隐私痕迹。它只读分析二进制字符串、SDK 指纹、端点和本地数据,并生成 Markdown 报告。

主要功能

  1. 01静态检查 macOS 应用包
  2. 02识别第三方 SDK 指纹
  3. 03整理端点与推断用途
  4. 04盘点本地数据路径
  5. 05生成 Markdown 审计报告
  6. 06限定只读操作并脱敏

适用场景

  1. 01检查已安装应用的遥测痕迹
  2. 02梳理应用联系的服务端点
  3. 03盘点应用留下的本地文件

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/iamzhihuix/happy-claude-skills --skill happy-app-audit

安装后在 Claude Code 中输入/happy-app-audit确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

lib/
card.ts
shell.ts
references/
data_locations.md
known_endpoints.md
methodology_examples.md
safe_commands.md
sdk_fingerprints.md
scripts/
classify_strings.ts
inventory_data.ts
match_fingerprints.ts
render_report.ts
run.ts
snapshot_app.ts
templates/
card_prompt.md.tmpl
report.md.tmpl

详细介绍

Happy App 审计

仅通过静态方式审计 macOS 应用遥测。生成一份 Markdown 报告,说明已安装的 .app 包会报告什么、报告给谁、频率如何(推断所得),以及会在磁盘上留下什么。

何时调用

当用户将以下任一词语与 .app 路径或应用名称结合使用时调用:「审计 / 调查 / 看看 / 拆 / 逆向 / 上报 / 埋点 / 隐私 / 抓 SDK」。当收到 /Applications、~/Applications、/Library/Input Methods 或 /Library/PrivilegedHelperTools 下的路径时,也调用。

不要用于:源代码仓库、网站、移动端(iOS/Android)软件包——此技能专用于 macOS 应用包。

硬性规则(不可协商)

  • 只读。 不得对发现的端点使用 curl/wget/nc/dig。不得使用 lldb attach、dtrace、fs_usage、tcpdump、mitmproxy、frida。不得读取 Keychain。不得绕过 DRM。不得转储内存。
  • 仅允许使用指定命令。 参见 references/safe_commands.md。如果某一步似乎需要使用白名单以外的命令,请停止并告知用户,不要自行变通。
  • 默认保护隐私。 在每个输出文件中,将 device_id、uid、session_id、email、IDFV、IDFA、JWT 以及任何长度为 16+ 的十六进制字符串替换为 <redacted:N>(保留长度,删除内容)。
  • 范围上限。 单次调用最多可针对 5 个应用。拒绝处理 /System/、/usr/libexec/、/private/var/db/com.apple.* 下的路径。这些是操作系统组件,不是第三方遥测审计目标。

运行时

{baseDir} = 此 SKILL.md 所在目录。

所有脚本均使用 bun + TypeScript。按以下方式确定运行时:优先使用 PATH 中的 bun,否则使用 npx -y bun。如果两者都不存在,则中止并用一行提示如何安装。

# Smoke check
bun --version  ||  npx --version  ||  echo "Need bun (recommended) or npx"

工作流 — 分 6 个阶段,依次执行

每个阶段包含:目标 → 输入 → 命令 → 输出 → 停止条件。不要跳过阶段。不要交叉执行。

阶段 0 — 确认范围

目标。 将目标列表限定为 ≤5 个有效的 .app 路径。

输入。 用户提供的任何信息——可能是路径、名称,或“我安装的输入法”。

命令。

  • 如果用户提供了路径 → 验证路径存在且以 .app 结尾
  • 如果用户提供了名称 → 在固定列表中搜索:
    /Applications         (depth 2)
    ~/Applications        (depth 2)
    /Library/Input Methods (depth 1)
    /Library/PrivilegedHelperTools  (depth 1)
    
  • 拒绝处理 /System/、/usr/libexec/、/private/var/db/com.apple.* 下的任何路径

输出。 包含绝对路径的列表 target_apps[]。

停止。 如果列表为空,询问用户一次。如果超过 5 个,询问用户要保留哪些。

阶段 1 — 元数据快照

目标。 为每个应用捕获不可变的外部特征:包 ID、版本、签名、授权、网络策略和内嵌框架。

输入。 阶段 0 中的 target_apps[]。

命令。 运行 scripts/snapshot_app.ts:

bun {baseDir}/scripts/snapshot_app.ts <app-path> --out <workdir>/meta.json

脚本会收集:

  • plutil -p <app>/Contents/Info.plist
  • codesign -dv --entitlements - <app>(stderr)
  • find <app>/Contents/Frameworks -maxdepth 3 -name '*.dylib' -o -name '*.framework'
  • otool -L <main-binary>
  • 使用 file <main-binary> 获取架构信息
  • 使用 du -sh 获取大小

输出。 <workdir>/meta.json,包含:bundle_id、version、sandboxed、arbitrary_loads、ats_exceptions[]、entitlements_summary[]、frameworks[](每项包含:name、path、size_bytes、archs)。

停止。 如果无法读取 bundle_id → 中止,该应用包格式错误。

阶段 2 — 字符串预处理

目标。 将每个内嵌二进制文件中的原始 strings 转换为适合放入上下文的分桶 Markdown。

输入。 meta.json::frameworks[]。

命令。

bun {baseDir}/scripts/classify_strings.ts <workdir>/meta.json --out <workdir>/strings/

对于每个二进制文件,脚本运行 strings -a -n 6,并将每一行分类到以下类别之一:

  • urls — 匹配 https?:// 的内容
  • domains — 裸主机名
  • paths — /Library/...、~/Library/...、相对于容器的路径
  • sql — CREATE TABLE、INSERT INTO、SELECT ... FROM
  • events — 看起来像事件名称(/^[a-z][a-z0-9_]{8,80}$/,且至少包含一个下划线)
  • keys — 长度 ≥ 24 个字符的 base64 / 十六进制字符串(仅保留数量和前 12 个字符,绝不保留完整内容)
  • noise — 丢弃

输出。 <workdir>/strings/<binary-name>.{urls,domains,paths,sql,events}.md(keys 类别仅保存数量和脱敏后的预览)。

停止条件。 如果二进制文件大于 200 MB → 跳过并输出一条警告,避免内存溢出。

阶段 3 — SDK 指纹匹配

目标。 识别存在的第三方 SDK,并评估置信度。

输入。 <workdir>/strings/,以及 references/sdk_fingerprints.md。

命令。

bun {baseDir}/scripts/match_fingerprints.ts <workdir>/strings/ \
  --fingerprints {baseDir}/references/sdk_fingerprints.md \
  --out <workdir>/matched.md

脚本将每个指纹的 tell-tale strings 正则表达式集应用于分桶后的字符串。达到 min_hits 阈值(每个指纹单独定义)时,指纹即被判定为已确认。

输出。 <workdir>/matched.md,每个 SDK 占一行,包含名称、供应商、命中数、证据文件行、状态(已确认 / 部分匹配 / 不存在)。

停止条件。 如果没有任何指纹得到确认,且应用未嵌入第三方 .framework → 写入一行“未检测到遥测”的报告,并跳过阶段 4–5。

阶段 4 — 端点映射

目标。 构建一张表,回答“它与哪些端点通信、使用什么协议、出于什么目的、频率如何?”

输入。 <workdir>/strings/*.urls.md + <workdir>/strings/*.domains.md + <workdir>/matched.md + references/known_endpoints.md。

命令。 此阶段主要由 Claude 读取文件。唯一的机械步骤:

bun {baseDir}/scripts/match_fingerprints.ts <workdir>/strings/ \
  --fingerprints {baseDir}/references/known_endpoints.md \
  --out <workdir>/endpoints.md

然后由 Claude 编写 <workdir>/endpoint_table.md:

端点SDK协议推断用途频率依据

频率依据 必须引用以下任一项:在 <workdir>/strings/ 中找到的字面时间间隔、阶段 5 中找到的配置文件,或“未知——静态证据未说明。”绝不猜测。

停止条件。 如果 endpoints.md 为空,但阶段 3 确认了某个 SDK → 在报告中标记(可能使用了运行时解析的主机)。

阶段 5 — 本地数据深入检查

目标。 清点应用在磁盘上写入的数据。

输入。 meta.json::bundle_id,以及 references/data_locations.md。

命令。

bun {baseDir}/scripts/inventory_data.ts <bundle_id> --out <workdir>/local_data.md

脚本使用 find 查找:

  • ~/Library/Application Support/<bundle>/
  • ~/Library/Containers/<bundle>/Data/
  • ~/Library/Group Containers/group.<bundle-prefix>.*/
  • ~/Library/Caches/<bundle>/
  • ~/Library/Preferences/<bundle>.plist
  • ~/Library/Logs/<bundle>/

对于每个 .sqlite* 文件:仅运行 sqlite3 <file> '.schema' 和 .tables——绝不执行 SELECT。对于每个 .mmkv / .json 配置文件:列出路径和大小,不要打开。

输出。 <workdir>/local_data.md,包含相关路径树、文件大小和 SQLite 架构。

停止条件。 如果当前用户不是文件所有者 → 跳过并附注说明(不要提示使用 sudo)。

阶段 6 — 报告渲染(+ 可选的 4:5 卡片)

目标。 汇总面向用户的报告。可选地渲染一张 4:5 信息图卡片以供分享。

输入。 所有前序阶段的输出。

命令。

bun {baseDir}/scripts/render_report.ts <workdir> \
  --template {baseDir}/templates/report.md.tmpl \
  --out ~/Documents/app-telemetry-audit/<YYYY-MM-DD>_<bundle-id>/report.md

向 scripts/run.ts 传入 --card 时,编排器还会:

  1. 调用 lib/card.ts::renderCardPrompt(),提取排名前 6 的 SDK(按体积排序,将涉及隐私的 SDK 标红)、排名前 6 的端点(优先采用 endpoints.md 中确认匹配的项,附上中文用途标签,并为 quic/-ws. 主机合成协议),以及排名前 5 的本地数据目录(按父目录归并并附注,合并 DoubaoIme/doubaoime 大小写变体)。
  2. 将填充后的提示词写入 <workdir>/card_prompt.md。
  3. 自动查找 baoyu-imagine(位于 ~/.claude/skills/baoyu-imagine/scripts/main.ts,或通过 BAOYU_IMAGINE_SCRIPT 环境变量指定),使用默认参数 --ar 4:5 --quality 2k 调用,并将结果写入 <workdir>/card.png。

传入 --no-image 仅写入提示词并跳过图像调用。传入任意 --image-* 标志(--image-provider、--image-model、--image-imageSize 等)可覆盖默认值——例如,使用 --image-provider google --image-model gemini-3-pro-image-preview --image-imageSize 4K 生成原生 nano-banana-pro 4K 输出。

如果未安装 baoyu-imagine,阶段 6 仍会写入 card_prompt.md 并显示安装提示,但会跳过 PNG。未安装该技能时,其他功能仍可正常使用。

输出。 最终 Markdown 报告。打印其绝对路径。如果传入 --card,还会输出 card_prompt.md 和 card.png。

输出布局(每个应用)

~/Documents/app-telemetry-audit/<YYYY-MM-DD>_<bundle-id>/
├── meta.json
├── matched.md
├── endpoints.md
├── endpoint_table.md
├── local_data.md
├── strings/
│   └── <binary>.{urls,domains,paths,sql,events}.md
├── card_prompt.md          # only if --card requested
└── report.md               # the deliverable

默认保留工作文件(strings/、中间 *.md 文件),作为审计记录。写入 report.md 后,传入 --clean 可删除这些文件。

快速开始

# Single app, full audit (markdown report only)
bun {baseDir}/scripts/run.ts /Library/Input\ Methods/DoubaoIme.app

# Add a 4:5 share card (prompt + PNG via baoyu-imagine)
bun {baseDir}/scripts/run.ts /Library/Input\ Methods/DoubaoIme.app --card

# Card prompt only — skip image generation
bun {baseDir}/scripts/run.ts /Library/Input\ Methods/DoubaoIme.app --card --no-image

# Card with Google nano-banana-pro at 4K
bun {baseDir}/scripts/run.ts /Library/Input\ Methods/DoubaoIme.app --card \
  --image-provider google \
  --image-model gemini-3-pro-image-preview \
  --image-imageSize 4K

# Multiple apps in one go (capped at 5)
bun {baseDir}/scripts/run.ts /Library/Input\ Methods/DoubaoIme.app /Applications/Foo.app

scripts/run.ts 是一个轻量编排器,会依次调用阶段 1→6。常见情况请使用它。只有在迭代时才使用各阶段的独立脚本。

--card 标志说明
标志用途默认值
--card渲染 card_prompt.md 和 card.png关闭
--no-image配合 --card:写入提示词,跳过 PNG关闭
--image-providerbaoyu-imagine 提供方(google、openai、replicate、dashscope 等)自动选择提供方
--image-model提供方内的模型 ID提供方默认值
--image-ar宽高比4:5
--image-size明确指定 WxH根据 --image-quality / 提供方确定
--image-qualitynormal 或 2k2k
--image-imageSizeGoogle/OpenRouter 的 1K/2K/4K根据 --image-quality 确定
--out覆盖输出根目录~/Documents/app-telemetry-audit/

任何 --image-* 标志都会隐式启用 --card。

相关参考资料

  • references/safe_commands.md — 命令白名单及其理由
  • references/sdk_fingerprints.md — SDK 检测规则(MVP 支持 12+ 个 SDK)
  • references/known_endpoints.md — 域名 → 产品反向查找
  • references/data_locations.md — 各供应商典型的磁盘目录结构
  • references/methodology_examples.md — 两个完整示例(WeType、DoubaoIme)
  • templates/report.md.tmpl — 最终报告框架
  • templates/card_prompt.md.tmpl — 4:5 视觉卡片提示词框架

Claude 不应采取的错误做法

  • 不要将字符串改述为“看起来像 X”,却不引用逐字证据所在的行和文件路径
  • 不要根据 SDK 名称推断频率——只能依据字符串或配置文件中的明确数字
  • 不要运行目标应用中的任何二进制文件
  • 不要打开 .sqlite 内容——只能查看架构
  • 如果阶段 3 未发现任何内容,不要撰写报告——应改为写简短的“未检测到遥测”说明
  • 不要根据训练记忆编造端点;如果 URL 不在 <workdir>/strings/ 中,就不要将其加入表格

验证(开发此技能时)

在 /Library/Input Methods/DoubaoIme.app 上进行冒烟测试,并确认报告涵盖:

  • 框架:applogrs、Parfait、bytenn、onnxruntime、sscronet、TTNet、ime_net_sdk、sami
  • 端点:以下至少 3 个:ime.doubao.com/obric/ime/cloud/convert、log-klink.zijieapi.com、ime-gw.oceancloudapi.com、frontier-audio
  • 本地数据:~/Library/Application Support/DoubaoIme/Parfait/ready/685343/0/

回归测试:在 WeType 输入法应用包上运行,并确认出现 wetype.weixin.qq.com 和 CACHE_LOG_TBL 架构。

负向测试:在一个没有第三方遥测的小型应用上运行——必须输出简短的“未检测到遥测”,不得编造 SDK。

评论

支持 Markdown