扫目录下 Excel/CSV/文本里的身份证、银行卡、税号、手机号、邮箱、口令,只出脱敏值,原文绝不落盘
financial-secret-scanSKILL.md 为准。> **本地运行,零网络、免 API Key、数据不出电脑。**
> 四项诚实承诺:① 算不出就说算不出,绝不编造数字;② 高危动作强制人工确认;③ 幂等断点,绝不重复副作用;④ 全程留痕可审计。
# 财务敏感信息扫描
工资表要发给供应商对账、账套说明要贴到内网、银行流水要提给审计——
发之前你不知道里面躺着多少张身份证号和卡号。人工翻一遍不现实。
本包把这些文件扫一遍,**找出 sensitive 信息并给出位置**,
但**报告里一个原文都不留**:只写脱敏值(`4403**********1234`、`6222 **** **** 7894`)。
命中原文只在内存里用来判定,写完就丢,不进任何输出文件、日志,也不进终端长输出。
三条不可协商的底线:
1. **零原文落盘**:`sec_命中清单.csv` / `sec_异常表.csv` / `sec_校验表.csv` / `fin-log.jsonl`
里只有脱敏值;`fin-log.jsonl` 里同样只写脱敏值(校验表里另有「原文处置」一行明说)
2. **口令类只报位置**:命中就报「文件 / 工作表 / 行号 / 列号 / 关键词」,
口令内容一个字符都不输出
3. **读不了就明说**:`.pdf`、图片、Office 私有格式、xlsx 内嵌图片一律读不了,
不做 OCR,进异常表并提示,不会假装扫过了
## 安全底线(先看这段)
| 项目 | 处理方式 |
|---|---|
| 命中原文 | 只在内存中参与判定,**不写入任何输出文件、日志、终端长输出** |
| 口令 / 密码内容 | 只报「位置 + 关键词」,输出固定为 `(不输出原文)` |
| 源文件 | **只读**,一个字节都不改 |
| 输出位置 | 一律进 `_out/` |
| 默认行为 | **不写盘**,加 `--execute` 才落盘 |
| 无法保证原文不落盘的输入形态 | 直接 refuse,不给半个报告 |
## 输入
| 参数 | 说明 |
|---|---|
| `路径`(位置参数) | 待扫描的目录,默认当前目录 |
| `--recursive` / `--no-recursive` | 递归子目录(默认递归) |
| `--types` | 限定扩展名,逗号分隔,默认 `xlsx,csv,txt,md,json` |
| `--only` | 只查某类:`id,card,tax,phone,mail,pass`,默认全查 |
| `--confirm-unsafe` | 目录里有不可读二进制,显式确认后仍按纯文本行扫(有误报风险) |
| `--out-dir` | 输出目录,默认 `_out` |
| `--execute` | 真正写盘;不加只预览 |
| `--operator` | 写进处理日志的操作人 |
可扫格式:`.xlsx`(逐 sheet 逐单元格)、`.csv` / `.tsv`、`.txt`、`.md`、`.json`。
读不了:`.pdf`、`.doc/.docx/.xls/.ppt/.pptx/.xlsb`、各类图片、压缩包、音视频——全部进异常表。
## 输出四件套(每次运行必有)
| 文件 | 内容 |
|---|---|
| `sec_命中清单.csv` | 文件 / 工作表或行号 / 列号 / **脱敏值** / 类型 / 判定 / 校验依据 / 建议动作 |
| `sec_异常表.csv` | 读不了的文件、Luhn 不合格的长数字串、关键词命中但值缺失,带风险等级 |
| `sec_校验表.csv` | 扫描文件数 / 字节数 / 总单元格或行数、各类命中数、判定分布、读不了的文件清单、原文处置声明 |
| `fin-log.jsonl` | 处理日志(追加),**同样只写脱敏值** |
## 识别规则与判定依据(不是黑箱)
| 类型 | 判定依据 | 判定 | 脱敏示例 |
|---|---|---|---|
| 身份证 | 18 位 + 省级区划码 11–99 + 前 17 位加权 mod 11 校验位(GB 11643) | 校验位对 = **确认**,不对 = **疑似** | `4403**********1234` |
| 统一社会信用代码 / 税号 | 18 位 + 字符集(不含 I/O/S/V/Z)+ 加权 mod 31 校验位 | 同上;含非法字符 = 疑似 | `9133**********1A84` |
| 银行卡 | 13–19 位数字 + **Luhn 校验通过**,另给卡组织前缀提示(Visa / Mastercard / 银联 / Amex / Diners) | Luhn 过 = **疑似真卡**(不确认,可能是账号) | `6222 **** **** 7894` |
| 长数字串疑似 | 13–19 位数字但 Luhn **不通过** | 进**异常表**,疑似账号 / 订单号 / 已作废卡号 | 同上 |
| 手机号 | 11 位且 `1[3-9]` 开头 | 一律 **疑似**(弱识别,未做运营商校验) | `138****8000` |
| 邮箱 | 邮箱正则命中 | 一律 **疑似**(弱识别) | `z*******@example.com` |
| 口令类 | 同行 / 同格出现「密码 / 口令 / 账套口令 / 密钥 / pwd / password / passport / token」等关键词 | **疑似**,只报位置 | `(不输出原文)` |
「确认 / 疑似」的分工:**校验位过的才算确认**,还得人工核是不是本人;
手机号、邮箱、Luhn 过的卡号一律只给疑似,因为这三类的正则本身就可能误命中。
## 用法
```bash
PY="<python 3.8+ 路径>" # 纯标准库,无需 pip install
"$PY" <skill目录>/scripts/secret_scan.py 待扫目录 --execute
```
只在预览模式看命中分布(**默认不写盘**):
```bash
"$PY" secret_scan.py ./导出包 --out-dir _out/9月
```
只查银行卡、不看别的:
```bash
"$PY" secret_scan.py ./工资表目录 --only card --execute
```
目录里混了 PDF / 图片,确认后连二进制一起按文本行扫(**误报风险高,请自己判断**):
```bash
"$PY" secret_scan.py ./附件目录 --confirm-unsafe --execute
```
## 真实输出样例(实测)
自测目录 `selftest/_out/sec` 里放了 7 个文件:工资表.xlsx(xlsx 分支)、
人员.csv、银行流水.csv、账套说明.md、接口.json、备注.txt、附件.pdf。
其中身份证号、卡号、税号、手机、邮箱、口令**全部是形似而非常真的示例值**,
校验位由脚本现场算出,不代表任何真实账户或证件。
```
扫描目录:D:\...\caiyou_rebuild\selftest\_out\sec
递归=是 | 限定扩展名=.xlsx,.csv,.txt,.md,.json | 扫描规则=身份证,银行卡,统一社会信用代码/税号,手机号,邮箱,口令类
可扫文件 6 个 | 2.4 KB | 已扫单元格 / 文本行 52
命中 21 条:口令类 4 / 手机号 5 / 统一社会信用代码/税号 1 / 身份证 4 / 邮箱 3 / 银行卡 4
--- 命中清单(前 12 条,只显示脱敏值)---
1. [确认][身份证] 4403**********1234 行2 B 18 位 + 省级区划码 11-99 + 前 17 位加权 mod 11 校验位通过
2. [疑似][手机号] 138****8000 行2 C 11 位且 1[3-9] 开头(弱识别,未做运营商校验)
3. [疑似][银行卡] 6222 **** **** 7894 行2 D 13-19 位数字 + Luhn 校验通过;银联前缀(62 开头)
4. [疑似][邮箱] z*******@example.com 行2 E 邮箱正则命中(弱识别,未判断是否真实邮箱)
5. [疑似][身份证] 4403**********1230 行3 B 18 位 + 区划码合理,但前 17 位加权 mod 11 校验位不通过
6. [疑似][手机号] 135****5678 行3 C 11 位且 1[3-9] 开头(弱识别,未做运营商校验)
7. [疑似][银行卡] 6222 **** **** **** 9012 行3 D 18 位且校验位不对,但 Luhn 通过;银联前缀(62 开头)
8. [疑似][邮箱] l***@example.com 行3 E 邮箱正则命中(弱识别,未判断是否真实邮箱)
9. [疑似][口令类] (不输出原文) 行1 关键词「密码」命中,只报位置;口令内容不输出、不落盘
10. [疑似][口令类] (不输出原文) 行2 关键词「密码」命中,只报位置;口令内容不输出、不落盘
11. [确认][身份证] 4403**********1234 行2 B 18 位 + 省级区划码 11-99 + 前 17 位加权 mod 11 校验位通过
12. [疑似][银行卡] 6222 **** **** **** 9012 行2 C 18 位且校验位不对,但 Luhn 通过;银联前缀(62 开头)
--- 异常(前 8 条)---
[中] 附件.pdf:不可读文件(pdf),未扫描
[中] 备注.txt:关键词命中但值缺失
[中] 工资表.xlsx:疑似卡号但 Luhn 校验不通过
[中] 银行流水.csv:疑似卡号但 Luhn 校验不通过
已写出到 sec_out/:
sec_命中清单.csv
sec_异常表.csv
sec_校验表.csv
```
`sec_异常表.csv`(前 4 行,脱敏值一列):
```
序号,文件,工作表,行号,列号,问题,风险等级,脱敏值,说明
1,D:/.../sec/附件.pdf,,,,不可读文件(pdf),未扫描,中,,本包不做 OCR、不解析 PDF / 图片 / Office 私有格式;确需连同这些文件一起扫,请加 --confirm-unsafe
2,D:/.../sec/备注.txt,,2,,关键词命中但值缺失,中,(不输出原文),只出现关键词「密码」、关键词后面没有取值内容;口令可能为空或写在别处
3,D:/.../sec/工资表.xlsx,Sheet1,3,C,疑似卡号但 Luhn 校验不通过,中,6222 **** **** 6789,13-19 位数字,Luhn 校验不通过;可能是账号 / 订单号 / 已作废卡号
4,D:/.../sec/银行流水.csv,csv,3,C,疑似卡号但 Luhn 校验不通过,中,6222 **** **** 6789,13-19 位数字,Luhn 校验不通过;可能是账号 / 订单号 / 已作废卡号
```
`sec_校验表.csv`(片段):
```
可扫文件数 | 6
扫描字节数 | 2482
已扫单元格 / 文本行 | 52
命中总数 | 21
命中 · 口令类 | 4
命中 · 手机号 | 5
命中 · 统一社会信用代码/税号 | 1
命中 · 身份证 | 4
命中 · 邮箱 | 3
命中 · 银行卡 | 4
判定分布 · 疑似 | 18
判定分布 · 确认 | 3
异常条目 | 4
读不了 / 未扫描的文件 | 附件.pdf
原文处置 | 命中原文仅在内存中用于判定,未写入任何输出文件与日志
读不了的格式 | .pdf / 图片 / Office 私有格式一律读不了;xlsx 内嵌图片同样读不了,不做 OCR
口令类披露范围 | 只报文件 / 工作表 / 行号 / 列号 / 关键词,不输出口令内容
```
`fin-log.jsonl` 全文(**已确认无任何原文**,只有路径与统计数):
```json
{"ts": "2026-09-27T11:22:10", "skill": "financial-secret-scan", "version": "0.1.0", "mode": "scan", "root": "D:\\...\\_out\\sec", "recursive": true, "types": ".xlsx,.csv,.txt,.md,.json", "rules": "id,card,tax,phone,mail,pass", "n_file": 6, "scan_bytes": 2482, "scan_units": 52, "n_hit": 21, "n_hit_kind": {"身份证": 4, "手机号": 5, "银行卡": 4, "邮箱": 3, "口令类": 4, "统一社会信用代码/税号": 1}, "n_hit_verdict": {"确认": 3, "疑似": 18}, "n_exc": 4, "unsafe_confirmed": false, "raw_values_written": false, "outputs": ["sec_命中清单.csv", "sec_异常表.csv", "sec_校验表.csv", "fin-log.jsonl"]}
```
## 拒绝权(什么情况下算不出)
| 情况 | 处理 |
|---|---|
| 路径不存在 | **拒绝**:列出路径,让确认后再跑 |
| 路径是文件不是目录 | **拒绝**:说明本包按目录扫,且保证只读不整文件复制 |
| 目录里一个可扫文件都没有(空目录 / 只有 PDF 图片) | **拒绝**:`没有可扫的文本 / 表格文件(目录里没有 .xlsx / .csv / .txt / .md / .json)` |
| `--only` 参数不认识 | **拒绝**:列出可选值 `id/card/tax/phone/mail/pass` |
| 无法列目录(权限不足 etc.) | **拒绝**:报出列目录失败原因 |
| 目录里有不可读二进制且用户未确认 | **不崩**:进异常表报「不可读文件(pdf),未扫描」,并提示加 `--confirm-unsafe` |
| `.xlsx` 损坏 / 是旧格式 | **不崩**:进异常表,报出内核读盘原因,该文件未扫描 |
| 一张表读不了但其它表能读 | **不崩**:坏的进异常表,好的照常出报告 |
被拒绝的输入**不会产生任何输出文件**,也不会部分写盘。
## 关键设计(容易踩的坑)
1. **原文只在内存**:`detect()` 返回的「原文」只在调用链上传,交给 `mask_*` 脱敏后就丢,
写盘路径上拿不到原文。校验表里固定有一行「原文处置」声明这件事。
2. **口令类单独处理**:命中后出不输出原文,位置照常给,
另外检查关键词后面有没有取值内容——没有就进异常表提醒「口令可能为空或写在别处」。
3. **长短数字串不互相污染**:13 位以上的数字串先被登记为「已占用」,
里面的 11 位手机号、18 位身份证片段不再重复命中;反过来也不会把长串内部的号码拆成短号码。
4. **身份证 18 位 ≠ 银行卡 18 位**:18 位且区划码合理时优先走身份证,
校验位不对但 Luhn 通过才降级报「疑似银行卡」。
5. **Luhn 过 ≠ 真卡**:Luhn 只证明这串数字符合卡号构造规则,
所以这类一律标「疑似」,并在依据里给卡组织前缀。
6. **手机号 / 邮箱只给疑似**:不做运营商校验、不做邮箱存在性验证,
避免把「13800000000 是个示例值」报成确认。
7. **行号口径**:xlsx / csv 从 2 开始(第 1 行表头),文本按行号从 1 开始;
xlsx 还会给工作表名,多 sheet 不混号。
8. **xlsx 内嵌图片读不了**:图片里的内容本包不做 OCR,扫不到就是扫不到,
会在校验表里写明,不会假装扫过。
9. **`--confirm-unsafe` 的代价**:二进制按文本行解码后扫,误报率极高,
校验表会多一行 `--confirm-unsafe | 已确认,不可读二进制按纯文本行扫描,误报风险高`。
## 人工复核(必读)
- **「确认」不等于「是本人的」**:身份证 / 税号校验位过了只能证明这串号码构造合法,
是不是真发在你手上,必须人工核
- 手机号、邮箱的「疑似」里可能混着示例值、测试号、文档里的占位邮箱,别一上来就当真
- 命中清单里**只有脱敏值**,要拿原文去处理(换卡、报备),请回到源文件自己看,
不要指望从报告里反推
- 口令类命中说明口令很可能写在文档里,**按当前风险等级判断是否需要立即修改并追查外发范围**
- PDF、图片、xlsx 内嵌图片扫不到,需要这些渠道的请自行人工排查,本包不给假命中
- 扫描结果**不能替代**权限管控与访问审计,删掉不等于没泄露过
## 相邻路线转交
| 用户要做的事 | 转交给 |
|---|---|
| 扫完之后要对外发 / 试跑,需要保形脱敏 | `fin-data-mask` |
| 按标准字段模板把扫完的表合并成一张 | `fin-table-merge` |
| 扫完导出的表格式不对、要统一 | `fin-table-clean` |
| 扫描之外还要逐条查业务异常(负数、超阈值) | `fin-anomaly-scan` |
| 想看一张表里各列都有什么敏感字段(不逐格扫) | `fin-data-mask` |
## 可直接复制的说法
```text
把「导出包」这个目录下所有能读的文本和表格扫一遍,看里面有没有身份证号、银行卡号、
统一社会信用代码、手机号、邮箱,还有口令。
报告里只写脱敏值就行,原文不要写进任何文件,口令只告诉我第几行第几列有口令关键词。
PDF 和图片读不了没关系,单独列一张异常表说明就行。
扫完给我一张清单:哪个文件哪一行哪一列、什么类型、判定是确认还是疑似、
判定依据是什么(校验位怎么算的)、建议我怎么处理。
再给我一张校验表:扫了几个文件多少字节、各类命中多少条、确认与疑似各多少。
```
## 做完怎么算完成
- [ ] `sec_命中清单.csv` 的「脱敏值」列确认全部是打星后的值,没有任何一处是原文
- [ ] `sec_命中清单.csv` 的「判定依据」列逐条看过,确认命中的是你要的那类
- [ ] `sec_异常表.csv` 里「读不了」的文件已确认是格式问题还是文件损坏
- [ ] 「关键词命中但值缺失」的行已人工追查(口令可能写在别处)
- [ ] `fin-log.jsonl` 里只出现脱敏值与统计数,没有原文(可全文搜一遍确认)
- [ ] 需要原文处理的(换卡、报备、修改口令)已回到源文件操作
- [ ] 确认扫描范围覆盖完整,没有被 `--types` 或 `--no-recursive` 悄悄漏掉目录
---
本 Skill 采用 BUSL-1.1 许可(源码可见,可自由使用)。
版权所有 © 2026 律令科技(深圳)有限公司(@lexord-technology)。
本技能由律令科技(深圳)有限公司开发,**财友云(caiyouyun.cn)**发布与展示。
允许个人及企业内部免费使用、复制、修改,包括内部生产环境。
未经书面许可,不得将本 Skill 或其修改版作为商业服务对外销售、转售或提供托管服务。
各版本发布之日起 4 年后自动转为 Apache License 2.0。详见随包 `LICENSE.md`。增值税/附加/个税/印花税/所得税预缴,附公式与政策依据
金额大写、含税不含税互算、价税分离、尾差分摊、百分比反算
五险一金 + 个税累计预扣预缴 + 实发,支持多地参数表
直线/双倍余额/年数总和/工作量法,生成折旧明细表
期间生成、周旬季年切换、工作日调整、账期与自然日换算
按年度-凭证号-附件组织,生成目录清单与完整性校验
按人拆成独立文件,可加密打包,不留明文
无形资产与长期待摊费用摊销明细表
新增/折旧/处置/盘点全生命周期台账
等额本息/等额本金/气球贷,含实际利率与 IRR 校验
多币种折算、期末汇兑损益计算,汇率走内置表或导入
合并查询所有财税技能的运行日志,按时间/技能/级别过滤
SKILL.md 为准。