跳转至

仓库明文机密扫描(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后的实测用法与结果:

python deploy/nfos/tools/scan-secrets.py deploy/nfos/tools

期望输出(实测:退出码 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
扫描根目录:deploy/docs-site/source/scripts
  未发现明文机密 ✅
  可疑处合计:0
情形 期望退出码
无命中,且 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 或私钥;上表示例中的脱敏串是工具自身的输出写法照抄,不是还原。

回滚

本工具不部署到线上、不改线上系统。它不产生任何产物:

  • 无输出文件、无临时文件、无缓存;
  • 不改动被扫描的任何文件;
  • 无需清理。