关于 happy-app-audit
Happy App Audit 是用于静态审计 macOS 应用遥测行为的技能,适合调查已安装的 .app 包及其隐私痕迹。它只读分析二进制字符串、SDK 指纹、端点和本地数据,并生成 Markdown 报告。
主要功能
- 01静态检查 macOS 应用包
- 02识别第三方 SDK 指纹
- 03整理端点与推断用途
- 04盘点本地数据路径
- 05生成 Markdown 审计报告
- 06限定只读操作并脱敏
适用场景
- 01检查已安装应用的遥测痕迹
- 02梳理应用联系的服务端点
- 03盘点应用留下的本地文件
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ iamzhihuix/ happy-claude-skills --skill happy-app-audit安装后在 Claude Code 中输入/happy-app-audit确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
lib/card.tsshell.tsreferences/data_locations.mdknown_endpoints.mdmethodology_examples.mdsafe_commands.mdsdk_fingerprints.mdscripts/classify_strings.tsinventory_data.tsmatch_fingerprints.tsrender_report.tsrun.tssnapshot_app.tstemplates/card_prompt.md.tmplreport.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.plistcodesign -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 ... FROMevents— 看起来像事件名称(/^[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 时,编排器还会:
- 调用
lib/card.ts::renderCardPrompt(),提取排名前 6 的 SDK(按体积排序,将涉及隐私的 SDK 标红)、排名前 6 的端点(优先采用endpoints.md中确认匹配的项,附上中文用途标签,并为quic/-ws.主机合成协议),以及排名前 5 的本地数据目录(按父目录归并并附注,合并DoubaoIme/doubaoime大小写变体)。 - 将填充后的提示词写入
<workdir>/card_prompt.md。 - 自动查找
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-provider | baoyu-imagine 提供方(google、openai、replicate、dashscope 等) | 自动选择提供方 |
--image-model | 提供方内的模型 ID | 提供方默认值 |
--image-ar | 宽高比 | 4:5 |
--image-size | 明确指定 WxH | 根据 --image-quality / 提供方确定 |
--image-quality | normal 或 2k | 2k |
--image-imageSize | Google/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。