| name | opensource-preflight |
| description | 开源发布前综合扫描——一次调用检测密钥泄露、个人信息、内部引用、许可证合规、规范文件缺失和 Git 历史泄露,输出结构化报告并给出修复建议。 |
Opensource Preflight — 开源发布前飞行检查
何时使用
当用户准备将代码仓库开源发布,或要求进行开源前检查、安全扫描、合规检查时触发此 skill。
参数
| 参数 | 可选值 | 默认值 | 说明 |
|---|
| mode | quick / standard / strict | standard | 扫描深度 |
| path | 目录路径 | 当前工作目录 | 扫描目标 |
| exclude | glob 列表 | node_modules, vendor, *.min.js | 排除路径 |
执行流程
- 读取项目根目录的
.opensource-preflight.yml 配置(如有)
- 确定扫描模式(参数 > 配置文件 > 默认 standard)
- 检测外部工具可用性(gitleaks、trufflehog)
- 按模式并行执行各扫描层
- 汇总所有发现,去重,按严重等级排序
- 输出终端摘要
- 生成完整报告文件
opensource-preflight-report.md
- 针对 CRITICAL/HIGH 问题给出具体修复建议,询问是否自动修复
严重等级
| 等级 | 含义 | 结论影响 |
|---|
| CRITICAL | 必须修复才能开源 | ✗ 未就绪 |
| HIGH | 强烈建议修复 | ⚠ 需审查 |
| MEDIUM | 建议修复 | ✓ 就绪(附建议) |
| LOW | 可选优化 | ✓ 就绪 |
第 1 层:密钥与凭证扫描
等级: 全部 CRITICAL
工具检测
先运行以下命令检测外部工具:
which gitleaks 2>/dev/null && echo "GITLEAKS_AVAILABLE=true"
which trufflehog 2>/dev/null && echo "TRUFFLEHOG_AVAILABLE=true"
策略 A:有外部工具
如果 gitleaks 可用:
gitleaks detect --source . --no-git --report-format json --report-path /tmp/gitleaks-report.json
如果 trufflehog 可用(作为补充):
trufflehog filesystem . --json > /tmp/trufflehog-report.json
解析 JSON 报告,提取:文件路径、行号、规则名称、匹配内容(脱敏显示前4后4字符)。
策略 B:Claude 原生扫描
依次用 Grep 工具搜索以下模式(排除 exclude 路径):
| 类别 | 正则模式 |
|---|
| AWS Access Key | AKIA[0-9A-Z]{16} |
| AWS Secret | aws_secret_access_key\s*[=:]\s*['"]?[A-Za-z0-9/+=]{40} |
| GCP API Key | AIza[0-9A-Za-z\-_]{35} |
| Azure | DefaultEndpointsProtocol=https;AccountName= |
| GitHub Token | (ghp|gho|ghs|github_pat)_[A-Za-z0-9_]{36,} |
| Stripe Live | (sk|pk)_live_[A-Za-z0-9]{24,} |
| Slack Token | xox[bpras]-[A-Za-z0-9\-]{10,} |
| 私钥 | -----BEGIN (RSA|EC|DSA|OPENSSH|PGP) PRIVATE KEY----- |
| 数据库连接串 | (mysql|postgres|mongodb(\+srv)?):\/\/[^:]+:[^@]+@ |
| 通用密码赋值 | (password|passwd|pwd|secret)\s*[=:]\s*['"][^'"]{8,}['"] |
| 高熵字符串 | 在变量赋值上下文中出现的 40+ 字符 Base64/Hex 串 |
误报过滤
对每个匹配结果,检查是否命中以下白名单,命中则排除:
- 值为占位符:
xxx、your-.*-here、<.*>、example、changeme、TODO
- 文件路径含
.example、.sample、.template
- Stripe 测试密钥:
sk_test_、pk_test_
- 值完全由重复字符组成(如
AAAAAAA...)
- 在注释中且紧跟 "example" 或 "示例" 字样
输出格式
每个发现记录为:
{layer: 1, severity: "CRITICAL", category: "密钥", file: "path", line: N, summary: "AWS Access Key 泄露", detail: "AKIA****XXXX", fix: "移至环境变量,添加到 .gitignore"}
第 2 层:个人信息(PII)扫描
扫描模式
用 Grep 工具依次搜索:
| 类别 | 正则模式 | 等级 |
|---|
| 中国身份证 | \b\d{17}[\dXx]\b | CRITICAL |
| 美国 SSN | \b\d{3}-\d{2}-\d{4}\b | CRITICAL |
| 银行卡号 | \b[3-6]\d{12,18}\b(后续 Luhn 校验) | CRITICAL |
| 中国手机号 | \b1[3-9]\d{9}\b | HIGH |
| 国际电话 | \+\d{7,15} | HIGH |
| 邮箱地址 | [a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,} | HIGH |
| 公网 IPv4 | \b(?!10\.|127\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.)\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b | HIGH |
| 中文地址 | 含"省""市""区""街""号"的连续中文文本 | MEDIUM |
上下文调整
对每个匹配结果,根据上下文调整等级:
- 文件路径含
test/、mock/、fixture/、__tests__/ → 降一级但保留
- 出现在
package.json 的 author/contributors 字段 → 标记为 LOW(预期行为)
- 出现在日志语句(
console.log、logger.、log.)→ 附加"运行时泄露风险"警告
- 出现在
// TODO 或 # FIXME 注释中 → 保持原等级
误报过滤
排除以下情况:
- 邮箱域名为:
example.com、test.com、test.org、localhost、placeholder.com
- 手机号为已知假号:
13800138000、10000000000
- SSN 为全零:
000-00-0000、123-45-6789
- 身份证号在格式说明文本旁边(同行含"格式""示例""example")
- 银行卡号未通过 Luhn 校验算法
Luhn 校验逻辑
对疑似银行卡号,执行以下验证:
- 从右向左,奇数位不变,偶数位乘2
- 乘2后大于9的减去9
- 所有位数求和,能被10整除则为有效卡号
输出格式
{layer: 2, severity: "HIGH", category: "PII", file: "path", line: N, summary: "真实手机号", detail: "138****1234", fix: "替换为假数据或移至环境配置"}
第 3 层:内部引用扫描
扫描模式
用 Grep 工具依次搜索:
| 类别 | 正则模式 | 等级 |
|---|
| 内网域名 | [a-zA-Z0-9-]+\.(internal|local|corp|intranet|private)\b | HIGH |
| RFC 1918 URL | https?:\/\/(10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.)\d{1,3}\.\d{1,3} | HIGH |
| K8s 内部服务 | [a-z-]+\.svc\.cluster\.local | MEDIUM |
| 内部工具 | (jira|confluence|gitlab|jenkins|grafana|kibana)\.[a-zA-Z0-9-]+\.(com|net|org|io) | MEDIUM |
| 硬编码主机名 | (prod|staging|dev|qa)-(db|redis|api|web|app|cache)-\d+ | HIGH |
| 内部邮件组 | (team|dev|eng|ops|infra)-[a-z]+@ | MEDIUM |
| VPN/内网路径 | (vpn|intranet|internal)\.[a-zA-Z] | HIGH |
配置增强
如果 .opensource-preflight.yml 中定义了 internal_domains,额外匹配:
- 精确匹配用户声明的域名后缀
- 匹配含这些域名的完整 URL
上下文调整
- 出现在
.example、.sample、.template 文件中 → 降为 LOW
localhost:3000 类开发端口出现在 README/文档中 → LOW
- K8s service 名出现在
*.yaml、*.yml(Helm/K8s manifests)→ MEDIUM(预期但仍标记)
- 出现在
docker-compose.yml 的 service 名 → LOW(本地开发配置)
输出格式
{layer: 3, severity: "HIGH", category: "内部引用", file: "path", line: N, summary: "内网域名泄露", detail: "api.mycompany.internal", fix: "替换为环境变量或配置文件,从代码中移除硬编码"}
第 4 层:许可证与法律合规
模式要求: standard 及以上
检查步骤
4.1 LICENSE 文件检查
用 Glob 工具搜索:LICENSE*、COPYING*、LICENCE*
- 文件不存在 → CRITICAL(strict)/ MEDIUM(standard)
- 文件存在但内容无法识别为标准开源许可证 → MEDIUM
- 识别方法:检查是否包含以下关键词之一:
- "MIT License" / "Permission is hereby granted"
- "Apache License" / "Version 2.0"
- "GNU GENERAL PUBLIC LICENSE"
- "BSD 2-Clause" / "BSD 3-Clause"
- "ISC License"
- "Mozilla Public License"
4.2 包声明一致性
读取项目 manifest 文件(package.json、Cargo.toml、pyproject.toml、setup.py、go.mod),检查:
license 字段是否存在
- 字段值是否与 LICENSE 文件内容一致
- 不一致 → MEDIUM
4.3 依赖 license 兼容性
Node 项目:
npx license-checker --json --production > /tmp/licenses.json 2>/dev/null
如果命令不可用,读取 node_modules/*/package.json 的 license 字段(仅扫描直接依赖)。
Python 项目:
pip-licenses --format=json > /tmp/licenses.json 2>/dev/null
如果命令不可用,从 requirements.txt 列出依赖名,逐个检查已知 license。
Rust 项目:
cargo-license --json > /tmp/licenses.json 2>/dev/null
兼容性判定矩阵:
项目 License 为 MIT 时,以下依赖 license 不兼容(HIGH):
- GPL-2.0, GPL-3.0, AGPL-3.0
项目 License 为 Apache-2.0 时,以下不兼容(HIGH):
- GPL-2.0, AGPL-3.0
项目 License 为 GPL-3.0 时,以下不兼容(HIGH):
- 专有许可 (proprietary)
需要注意但不阻断(MEDIUM):
- LGPL(需确认链接方式)
- MPL-2.0(文件级 copyleft)
4.4 版权年份(strict 模式)
检查 LICENSE 文件中的年份,如果比当前年份早 2 年以上 → LOW
输出格式
{layer: 4, severity: "HIGH", category: "许可证", file: "package.json", line: null, summary: "依赖 license 不兼容", detail: "lodash-gpl (GPL-3.0) 与项目 MIT 不兼容", fix: "寻找替代包或更换项目 license"}
第 5 层:开源规范文件
模式要求: standard 及以上
检查步骤
5.1 必备文件检查
用 Glob 工具检查以下文件是否存在:
| 文件 | standard | strict | 缺失等级 |
|---|
| README.md | 必须 | 必须 | MEDIUM |
| .gitignore | 必须 | 必须 | MEDIUM |
| CONTRIBUTING.md | 建议 | 必须 | LOW / MEDIUM |
| CODE_OF_CONDUCT.md | 可选 | 建议 | LOW |
| CHANGELOG.md | 可选 | 建议 | LOW |
| SECURITY.md | 建议 | 必须 | LOW / MEDIUM |
| .github/ISSUE_TEMPLATE/ | 可选 | 建议 | LOW |
| .github/PULL_REQUEST_TEMPLATE.md | 可选 | 建议 | LOW |
5.2 README 质量检查
读取 README.md 内容,检查:
- 是否为空或少于 100 字符 → MEDIUM
- 是否包含脚手架占位内容("This project was bootstrapped"、"Getting Started with Create React App")→ MEDIUM
- 是否缺少以下关键段落(至少需要 3/5):
- 项目描述(前 3 行应说明项目是什么)
- 安装说明(含 "install"、"安装"、"setup")
- 使用示例(含 "usage"、"使用"、"example"、代码块)
- 许可证(含 "license"、"许可")
- 贡献方式(含 "contributing"、"贡献")
- 缺少 3 个以上关键段落 → MEDIUM;缺少 1-2 个 → LOW
5.3 .gitignore 覆盖度
检测项目语言(通过文件扩展名),验证 .gitignore 是否覆盖:
| 语言标志 | 必须忽略 |
|---|
*.js/*.ts | node_modules/、dist/、.env* |
*.py | __pycache__/、*.pyc、.venv/、.env* |
*.rs | target/、.env* |
*.go | vendor/(如使用)、.env* |
| 通用 | .DS_Store、Thumbs.db、*.log、.idea/、.vscode/(settings) |
缺少条目 → LOW(建议添加)
5.4 包元数据检查
读取 manifest 文件,检查是否缺少:
description → LOW
repository / homepage → LOW
private: true 仍存在 → MEDIUM(开源应移除)
输出格式
{layer: 5, severity: "MEDIUM", category: "规范", file: "README.md", line: null, summary: "README 缺少安装说明", detail: "未找到 install/setup 相关段落", fix: "添加 ## 安装 段落,说明如何安装和运行项目"}
第 6 层:Git 历史扫描
模式要求: standard(最近 50 次 commit)/ strict(全部历史)
quick 模式跳过此层。
工具检测
which trufflehog 2>/dev/null && echo "TRUFFLEHOG_AVAILABLE=true"
which gitleaks 2>/dev/null && echo "GITLEAKS_AVAILABLE=true"
策略 A:有外部工具
如果 trufflehog 可用:
trufflehog git file://. --only-verified --json > /tmp/trufflehog-history.json
如果 gitleaks 可用:
gitleaks detect --source . --log-opts="--all" --report-format json --report-path /tmp/gitleaks-history.json
策略 B:Claude 原生扫描
根据模式确定范围:
- standard:最近 50 次 commit
- strict:
--all
执行以下扫描命令:
git log --all --diff-filter=D --name-only --pretty=format:"%H %ai" -- '*.env' '*.env.*' '*.pem' '*.key' '*.p12' '*.pfx' 'credentials.json' '*secret*'
git log --all -p -S "AKIA" --pretty=format:"COMMIT:%H DATE:%ai" | head -200
git log --all -p -S "BEGIN RSA PRIVATE KEY" --pretty=format:"COMMIT:%H DATE:%ai" | head -200
git log --all -p -S "password" --pretty=format:"COMMIT:%H DATE:%ai" -- '*.env' '*.yml' '*.yaml' '*.json' '*.toml' | head -200
作者邮箱检查
git log --all --format="%ae" | sort -u
检查是否有内部邮箱(匹配 internal_domains 配置,或通用模式如含 corp、internal 的域名)→ MEDIUM
大文件历史检查(strict 模式)
git rev-list --objects --all | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | awk '/^blob/ && $3 > 10485760 {print $3, $4}'
发现 >10MB 文件 → LOW
修复建议
对历史泄露问题,给出具体修复命令:
清理敏感文件历史:
git filter-repo --path <敏感文件路径> --invert-paths
bfg --delete-files <文件名> .
git reflog expire --expire=now --all && git gc --prune=now
重写作者邮箱:
echo "New Name <public@email.com> Old Name <internal@corp.com>" > .mailmap
git filter-repo --mailmap .mailmap
全新开源(从内部 repo 迁移):
git checkout --orphan clean-main
git add -A
git commit -m "Initial open-source release"
git branch -D main
git branch -m main
输出格式
{layer: 6, severity: "CRITICAL", category: "历史", file: null, line: null, summary: "历史 commit 中发现 AWS 密钥", detail: "commit a3f2c1d (2025-03-14) 曾包含 AKIA****XXXX", fix: "使用 git-filter-repo 清理历史,或 squash 为干净的 initial commit"}
报告输出
终端摘要模板
扫描完成后,按以下格式输出终端摘要:
═══════════════════════════════════════════════
Opensource Preflight 扫描报告 — 模式: {mode}
═══════════════════════════════════════════════
CRITICAL {n} 个问题(必须修复才能开源)
HIGH {n} 个问题
MEDIUM {n} 个问题
LOW {n} 个问题
─── CRITICAL ───────────────────────────────────
[{category}] {file}:{line} — {summary}
...
─── HIGH ───────────────────────────────────────
[{category}] {file}:{line} — {summary}
...
─── MEDIUM ─────────────────────────────────────
[{category}] {file}:{line} — {summary}
...
═══════════════════════════════════════════════
结论: {verdict}
完整报告: ./opensource-preflight-report.md
═══════════════════════════════════════════════
结论判定
- 存在任何 CRITICAL →
✗ 未就绪 — 不可开源发布
- 无 CRITICAL 但有 HIGH →
⚠ 需审查 — 建议修复后再发布
- 仅 MEDIUM/LOW →
✓ 就绪(附建议)
- 零问题 →
✓ 完全就绪
完整报告文件
将完整报告写入项目根目录 opensource-preflight-report.md,结构如下:
# Opensource Preflight 扫描报告
**扫描时间:** YYYY-MM-DD HH:mm
**扫描模式:** {mode}
**扫描路径:** {path}
**结论:** {verdict}
## 摘要
| 等级 | 数量 |
|------|------|
| CRITICAL | N |
| HIGH | N |
| MEDIUM | N |
| LOW | N |
## 第 1 层:密钥与凭证
| # | 等级 | 文件 | 行 | 问题 | 修复建议 |
|---|------|------|-----|------|----------|
| 1 | CRITICAL | src/config.ts | 14 | 硬编码数据库密码 | 移至环境变量 |
## 第 2 层:个人信息
(同上表格结构)
## 第 3 层:内部引用
(同上表格结构)
## 第 4 层:许可证合规
(同上表格结构)
## 第 5 层:开源规范文件
(同上表格结构)
## 第 6 层:Git 历史
(同上表格结构)
## 推荐操作
按优先级排列的修复步骤:
1. **[CRITICAL]** 立即移除 `.env.production` 并添加到 .gitignore
2. **[CRITICAL]** 清理 git 历史中的 AWS 密钥
3. **[HIGH]** 替换测试数据中的真实手机号为假数据
...
修复建议与自动修复
扫描完成并输出报告后,针对 CRITICAL 和 HIGH 级别问题,逐个给出修复方案并询问用户:
可自动修复的操作(安全、可逆)
以下操作可在用户确认后自动执行:
- 将
.env* 文件添加到 .gitignore
- 将硬编码的密钥替换为环境变量引用(如
process.env.DB_PASSWORD)
- 将测试数据中的真实 PII 替换为假数据
- 添加缺失的 LICENSE 文件(询问用户选择哪种许可证)
- 补充 .gitignore 缺失条目
- 移除
package.json 中的 private: true
需手动执行的操作(不可逆/高风险)
以下操作只提供命令,不自动执行:
- git 历史清理(
git-filter-repo)
- 作者邮箱重写
- 删除 git 中的大文件历史
- 更换项目 license
交互流程
对每个可自动修复的问题,询问:
"发现 {N} 个可自动修复的问题。是否逐个确认修复?(Y/n)"
如果用户同意,逐个展示修复内容并执行。每次修复后提交一个 commit。
附录:模式对比
| 扫描层 | quick | standard | strict |
|---|
| 1. 密钥与凭证 | ✓ | ✓ | ✓ |
| 2. 个人信息 | ✓ | ✓ | ✓ |
| 3. 内部引用 | ✓ | ✓ | ✓ |
| 4. 许可证合规 | ✗ | ✓ | ✓(含兼容性矩阵) |
| 5. 开源规范 | ✗ | ✓ | ✓(含全部可选文件) |
| 6. Git 历史 | ✗ | 最近 50 次 commit | 全部历史 |