仓库明文机密扫描(scan-secrets.py)¶
位置:
deploy/nfos/tools/scan-secrets.py| 语言:python3 | 是否需要 root:否
工具性质:离线静态扫描(只读,不写任何文件)
运行前提:仓库检出在场即可;扫描根按当前工作目录解析
部署状态:随仓库检出运行,不部署到 /opt/Cy/services/system.d/nfos/
用途¶
扫描仓库里是否残留“真实机密明文”(口令 / webhook / secret / token)。
源码 docstring 记录的动因(逐字引用):
为什么需要:`deploy/nfos/phase12-notification.sh` 曾被写入 4 个邮箱口令 + 2 个飞书 webhook +
1 个个人邮箱的**真实值**;这类值一旦进仓库就等于长期泄露,必须有可复跑的检查。
判据:键名含 PASSWORD/WEBHOOK/SECRET/TOKEN,且值长度 ≥10 且不是占位符/变量引用。
实现细则是可复核的:
| 环节 | 源码行为 |
|---|---|
| 遍历 | os.walk(ROOT),跳过目录名 .git / node_modules / .tools |
| 文件类型 | 仅 .sh .py .env .toml .yml .yaml .ps1 .js .md |
| 正则 | 行首可选空白 + 键名含 PASSWORD/WEBHOOK/SECRET/TOKEN + = 或 : + 可选引号 + 不含空白/引号/# 的 ≥10 字符值 |
| 占位符豁免 | 值以 $ { < YOUR your xxx XXX *** 请 占位 REDACTED CHANGE_ME changeme example EXAMPLE … 开头或包含其中任一项时跳过 |
| 脱敏输出 | 只打印值的前 6 个字符再接 ***(源码写法 v[:6] + "***"),并附长度 |
输出固定两段:扫描根目录:<ROOT> + 命中明细 / 未发现明文机密 ✅,末行 可疑处合计:<N>。
典型场景¶
- 提交前自查:确认新增的 shell / Python / YAML 里没有把真实口令或 webhook 写死;
- 事故复盘:确认历史泄露点(如
phase12-notification.sh类脚本)已清理干净; - 上线前定点扫某个目录,例如只扫
deploy/nfos/tools。
用法与参数¶
源码未提供 usage 行,参数只有一个位置参数(ROOT = sys.argv[1] if len(sys.argv) > 1 else "deploy"):
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
sys.argv[1] |
字符串(目录路径) | deploy |
扫描根目录;相对当前工作目录解析,可指向子目录以缩小范围 |
无参数时的默认行为:扫描 deploy(相对当前工作目录),即整棵 deploy/ 子树。
示例¶
本次promotion后的实测用法与结果:
期望输出(实测:退出码 1,3 处命中,全部为误报):
扫描根目录:deploy/nfos/tools
deploy/nfos/tools\mock-oidc.py:35 CLIENT_SECRET = os.env***(长度 15)
deploy/nfos/tools\mock-oidc.py:37 TOKEN_MODE = os.env***(长度 15)
deploy/nfos/tools\ui-js-refs.py:49 TOKEN = re.com***(长度 12)
可疑处合计:3
上述 3 处均为误报,无需处理:
mock-oidc.py:35/:37:命中的“值”其实是os.environ.get(...)这段环境变量读取表达式,不是机密值(脱敏前缀os.env即为os.environ的开头);ui-js-refs.py:49:命中的“值”是匹配nf-类名词元的正则字面量(脱敏前缀re.com即为re.compile的开头)。
干净目录的期望输出(实测:先设 PYTHONIOENCODING=utf-8):
$env:PYTHONIOENCODING = "utf-8"
python deploy/nfos/tools/scan-secrets.py deploy/docs-site/source/scripts
| 情形 | 期望退出码 |
|---|---|
无命中,且 stdout 可编码 ✅(UTF-8 终端 / PYTHONIOENCODING=utf-8 / Linux) |
0 |
| 无命中,但 stdout 是 GBK 代码页(Windows PowerShell 默认) | 1 —— 见下方“已知缺陷”,这不是发现机密 |
| 有命中(含误报) | 1 |
因为误报也会返回
1,本工具不适合直接当作 CI 门禁的硬性通过条件;应把它当“可疑点清单”,逐条人工确认。
依赖与权限¶
- 语言/版本:Python 3(源码未使用 3.7+ 独有语法,实际按 Python 3 即可)。
- 第三方依赖:无,仅标准库
io/os/re/sys。 - 仓库依赖:无固定文件依赖,但默认扫描根
deploy隐含“在仓库根执行”。 - 权限:不需要 root;只需读取权限(纯只读)。
- 终端编码:源码未对 stdout 做 UTF-8 重配置,因此“无命中”分支在 GBK 控制台会失败(见「风险与注意事项」的已知缺陷);建议固定用
PYTHONIOENCODING=utf-8。
风险与注意事项¶
- 纯离线只读:只读取文件内容,不写文件、不改动任何被扫描文件、不联网。
- 误报面:任何“键名含四个关键字 + 右侧是一段 ≥10 字符的表达式”都会被报出(环境变量读取、正则字面量、函数调用、路径拼接等)。实测在
deploy/nfos/tools上误报率 3/3。 - 漏报面:值长度 <10、值内含空格/单双引号/
#、键名不含PASSWORD/WEBHOOK/SECRET/TOKEN、扩展名不在白名单(如.json.txt.key.pem.ini)、以及二进制文件,都不会被检出。 - 已知缺陷:GBK 控制台下“无命中”会崩(实测)。无命中分支要打印
未发现明文机密 ✅,而✅(U+2705)无法用 GBK 编码,源码又没有像同批的alert-enrich-test.py/log-audit-report-test.py那样做sys.stdout.reconfigure(encoding="utf-8")。因此在中文 Windows PowerShell(默认 GBK)里扫一个干净目录会得到:
UnicodeEncodeError: 'gbk' codec can't encode character '\u2705' in position 10: illegal multibyte sequence
退出码 1,但它并不代表发现了机密。规避方式:执行前设 $env:PYTHONIOENCODING = "utf-8",或换用 UTF-8 终端 / Linux。有命中的分支不打印 ✅,不受此缺陷影响。
- 本页及全部文档不记录任何真实口令、webhook、token 或私钥;上表示例中的脱敏串是工具自身的输出写法照抄,不是还原。
回滚¶
本工具不部署到线上、不改线上系统。它不产生任何产物:
- 无输出文件、无临时文件、无缓存;
- 不改动被扫描的任何文件;
- 无需清理。