# 实现与实测结果
> 下列原始数据对应提交 `2113800`(221 条规则),不是当前改进版本的成绩。新版结果将在本文件后续章节独立记录;旧数据保留供复核。
本轮实现了独立 Rust 扫描核心、库 API 和 CLI,没有采用 Kingfisher 或其他扫描器 SDK。底层依赖是通用 regex、Aho–Corasick、压缩和序列化库。默认 221 条规则中,217 条是固定版本 Gitleaks 的 MIT 规则数据,4 条为本仓库编写;不能称为全部规则原创。
研究覆盖 22 个开源项目或规则资产,实际启动了 19 个竞品和本项目,共 20 个扫描器。Deepfence 因本机缺 YARA/autoreconf 未完成构建;ggshield 依赖外部服务;Secrets Patterns DB 不是独立扫描器。未测项目不记零分。Talisman 只使用已确认的 Git/index 适配器,并单列文件级定位。
## 实际运行的比较
Apple M2 Max,12 个逻辑 CPU,96 GiB 内存,macOS arm64。工具串行运行,安装和编译任务已停止;每组包含首次新进程、一次预热、三次新进程测量。下表取三次中位数,包含启动、规则编译、读取、扫描和报告。文件缓存未清空,不是冷磁盘测试。各工具规则和默认并发不同。
质量列来自 34 个合成正例和 10 个反例。该语料已参与开发诊断,因此是**回归数据,不是独立 holdout**。其中 3 个 AWS 候选含不满足专用规则的字符,保留原始标签;数据不代表已验证的真实 AWS 凭据或活性召回率。
| 工具 | 合成 TP / FP / FN | 16 MiB 毫秒(注入命中) | 128 MiB 毫秒(注入命中) | 真实源码毫秒 |
|---|---|---:|---:|---:|
| secret-scan | 31 / 0 / 3 | 55.5(16/16) | 205.9(128/128) | 69.1 |
| gitleaks | 31 / 0 / 3 | 316.5(16/16) | 740.3(128/128) | 377.3 |
| detect-secrets | 34 / 4 / 0 | 5987.1(16/16) | 未测 | 3883.5 |
| kingfisher | 24 / 0 / 10 | 575.8(16/16) | 504.6(128/128) | 539.4 |
| betterleaks | 33 / 1 / 1 | 124.6(16/16) | 327.6(128/128) | 148.8 |
| trivy | 24 / 0 / 10 | 127.8(16/16) | 214.7(128/128) | 182.4 |
| scratch-scanner-rs | 30 / 1 / 4 | 148.8(15/16) | 191.7(114/128) | 147.8 |
128 MiB 语料下本项目约 622 输入 MiB/s,并命中全部 128 处注入。scratch 的耗时更低,但漏掉 14 处注入,不能视为等价检测任务的胜出。Betterleaks 的本组合成召回更高;detect-secrets 检出全部正例,也有更多误报。当前证据不能证明本项目普遍最快、最全或生产准确率最高。
真实源码为固定 commit 的 rust-lang/regex,8,434,814 字节、451 个文件。它没有人工秘密标签,只比较时间和内存,不计算准确率。Git 历史与部分暂存场景属于小型正确性样例,没有证明大规模 Git 历史吞吐。完整工具版本、命令、哈希、重复运行、内存、错误和功能探针见 [完整对比表](../bench/results/final.md) 与 [原始结构化证据](../bench/results/final.json)。
## Agent 调用与内存
同一 980 字节无秘密合成请求:10 次独立 CLI 的延迟中位数为 **35.564 ms**;同一常驻进程的 100 次请求中位数为 **0.0543 ms**,p95 为 **0.3008 ms**;常驻进程启动到首次结果约 38.9 ms。测量包含 IPC 和 JSON,不是裸扫描内核计时,也不是其他工具或模型 token 成本比较。见 [数据](../bench/results/agent-latency.json)。
16 MiB UTF-16、有一个末尾命中的用例,进程峰值 RSS 中位数从 **176.5 MiB 降至 43.3 MiB**,约降 75.5%;原始字节范围及指纹一致。命中用例的时间中位数从 83.38 ms 到 84.67 ms,没有宣称提速。无命中用例从 81.79 ms 到 69.91 ms。源码已取消逐字节坐标表,改为按命中边界回映;实测比较的是两个完整版本,不能将所有时间差归因于单一改动。见 [数据](../bench/results/utf16-memory.json)。
## 实现边界与检查
已实现复用引擎、关键词预筛、路径/熵/占位符过滤、自定义规则、Base64、UTF-16、递归归档、文件树、Git history/index、指纹基线、脱敏 JSON/JSONL/SARIF、常驻 JSONL 和只读 MCP。错误与不完整结果不会作为成功 clean,损坏归档、超限、无效 ignore 和基线异常都有回归覆盖。
[60 项功能矩阵与各竞品优势、许可](FEATURES.md) 当前为 **20 项实现、17 项部分实现、23 项未实现**。云连接器、在线验证/撤销、GPU/ML、跨函数分析和多语言绑定等仍未实现。这是一版可运行的核心和接口,尚未实现研究清单中的全部候选功能。
本次实际完成的检查:
- `cargo fmt --check` 通过。
- `cargo test --locked -j 2`:59 项集成测试通过。
- `cargo clippy --all-targets --locked -j 2 -- -D warnings` 通过。
- `cargo build --release --locked -j 2` 通过。
- Benchmark runner:15 项单元测试通过。
- 语料生成器 self-test 通过,含 54 个有效字节标签、归档、Git/index/worktree 与确定性检查。
- 固定来源规则导入 `--check` 通过。
- 常驻延迟与 UTF-16 内存脚本完成,均核验了结果与脱敏。
最终自研二进制 SHA-256:`d454d96bbb98fb5223c788afaa9fd5b2708a7c8605b729a1592d1c7c5509c5c2`。初版及适配错误数据均保留;最终表剔除了空扫的 Secretlint 旧记录和遗漏 SARIF 字节坐标的 scratch 旧记录。复现入口见 [bench/README.md](../bench/README.md)。
## 后续改进版本(schema 2 基线 / schema 2 评分)
本次保持自研 Rust 核心,默认规则变为 **225 条(221 条固定上游数据 + 4 条独立规则)**,增加以下已实现能力:
| 方向 | 改动 | 验证与边界 |
|---|---|---|
| 基线安全 | 绑定 canonical roots、模式、ignore 策略、字节限额、规则内容及引擎配置;schema 1 拒绝 | 缩小范围、换配置、改 ignore 均不能覆盖原基线;SDK 构造报告需显式 context |
| 稳定来源 | 显示/规则路径与指纹身份分离,文件路径别名归一 | Git 根及子目录入口归一,包括 bare repo;staged/history 结果和 context 相同 |
| 规则语义 | 首个非空捕获、secret/match/line allowlist、AND/OR、stopwords | 恢复 4 条内容规则;不实现完整 YAML 语义,保留上游已知边界 |
| 内存与流水线 | UTF-8 按 finding 起点定位;文件任务和结果使用有界队列 | 单文件及其 findings 仍缓冲;Git 元数据仍驻内存 |
| Agent | JSONL 真正文件级流式;MCP 进度、取消、忙碌响应及限额预览 | 单次正则不可抢占;baseline JSONL 先收集再验证;MCP 取消不发送最终响应 |
| Git | 按 root tree 共享 commit 组;新增 revision range | 扫完整提交快照,不是仅变更行;不获取远程对象 |
| 验证 | v2 评分将 unlocalized 与 FP 分开;冻结独立值/上下文的合成 holdout;三 OS CI | 合成格式来自固定上游,不等于真实世界准确率;Linux/Windows 尚未远程执行 |
最终测量二进制 SHA-256:`9c6c2052d33d7479ae9ce998e0565e9faa9daf617826ed9b4cb1f1624872e85d`。本节以下只引用这个二进制对应的新测量;不将原版成绩归给新版。
### 新版质量与竞品数据
本轮有 filesystem adapter 的 **19 个扫描器(含本项目)**全部完成回归集和冻结合成留出集;Talisman 无本轮 FS adapter,另外三项仍为构建未完成、需要外部服务或非可执行资产。未测项不记零分。六个工具重新测了 16 MiB、128 MiB 和固定真实源码集,各含预热及三次测量。最后的基线范围修复未改检测引擎、规则或留出集;随后本项目五组数据全部重新执行,新旧二进制结果不混入同一工具的当前表。
| 工具 | 留出集 TP / FP / FN | 无法定位 | 16 MiB ms(命中) | 128 MiB ms(命中) | 真实源码 ms |
|---|---|---:|---:|---:|---:|
| secret-scan | 599 / 25 / 1 | 0 | 58.32(16/16) | 213.59(128/128) | 72.01 |
| Gitleaks | 493 / 14 / 107 | 0 | 721.10(16/16) | 1300.68(128/128) | 613.53 |
| Betterleaks | 553 / 14 / 47 | 0 | 101.88(16/16) | 241.53(128/128) | 110.60 |
| Kingfisher | 409 / 0 / 191 | 0 | 617.26(16/16) | 766.06(128/128) | 647.68 |
| Trivy | 376 / 0 / 224 | 0 | 95.99(16/16) | 165.23(128/128) | 122.72 |
| scratch-scanner-rs | 470 / 25 / 130 | 0 | 154.79(15/16) | 201.35(114/128) | 154.61 |
| detect-secrets | 400 / 75 / 200 | 0 | 本轮未重测 | 本轮未重测 | 本轮未重测 |
留出集为 **600 个唯一合成正例值 + 300 个负例位置**,由未读取本项目检测源码的独立线程生成并冻结。480 个格式样本与本项目规则共享固定上游格式来源,另外 120 个是泛化赋值样本;这只隔离了值和上下文,不能推导真实生产环境准确率。格式有效不代表服务端有效或账号存在。本项目确认召回 99.83%,已定位独立 finding 的 precision 95.99%;25/300 个负样本被标记。599 个 TP 均为精确字节坐标,另外 **421 个重复 finding**单列,未充当额外 TP。本轮不依据留出集成绩调规则。
原 34 个正例的回归集现在为 34 TP / 0 FP / 0 FN,仍有 28 条跨规则重复输出。质量改善不等于成本全面下降;**Trivy 在 128 MiB 用例中更快且命中全部注入**,本项目不能宣称普遍最快。规则覆盖、默认并发和结果数量不同;缓存未清空,时间包括进程启动与输出。Whispers 留出集的 120 个无法定位输出单列,不能把它们当作零误报或确定漏检的证据。
完整证据:[当前留出集表](../bench/results/v2/holdout-current.md)、[逐条脱敏位置和评分](../bench/results/v2/holdout-current.json)、[当前回归与性能表](../bench/results/v2/regression-current.md)、[原始运行记录](../bench/results/v2/regression-current.json)。各工具版本、命令、输入/二进制摘要和重复测量均保留;旧 `final.json` 的 v1 评分数据未覆盖。
### 整体版本压力比较
提交 `2113800` 的备份二进制与本次最终二进制交替运行,每组前后各三次,合计 36 次。默认规则、关闭 Base64、缓存未清空;时间包含 JSONL 管道传输、消费者验证和背压。以下是完整版本比较,不能将所有差异归因于某一个优化。结果按规则、规范化来源及原始坐标核对一致;指纹因路径身份策略变化不作逐字比较。
| 场景 | 耗时 ms,旧 → 新 | 峰值 RSS MiB,旧 → 新 |
|---|---:|---:|
| 密集换行 16 MiB | 77.46 → 73.09 | 163.52 → 36.89 |
| 10,000 文件 | 158.50 → 151.19 | 21.94 → 20.27 |
| 10,000 findings | 212.11 → 171.61 | 27.73 → 28.25 |
| 100,000 findings | 1957.74 → 1440.71 | 121.88 → 97.42 |
| Git 1 commit,16 固定 blobs | 67.35 → 92.37 | 17.17 → 18.86 |
| Git 200 commits,16 固定 blobs | 2297.38 → 94.56 | 17.63 → 18.72 |
10,000 文件首条 finding 延迟从 156.59 ms 降到 34.52 ms。单文件密集输出仍需等待该文件完成:10,000 findings 首条从 41.05 ms 变为 52.96 ms,100,000 findings 从 158.20 ms 变为 230.28 ms。单 commit Git 的额外根目录定位增加了启动开销。进度也有成本:10,000 文件产生 10,001 条 JSONL progress,输出从约 58.6 KB 增至 1.144 MB。**没有宣称所有场景都更快、更省内存或更省 Agent token。**
36 次结果均完成并通过数量、精确坐标、脱敏和跨版本检测结果一致性检查。原始运行、首条结果、RSS 和二进制哈希见 [stress-comparison.json](../bench/results/v2/stress-comparison.json);复现命令:
```sh
python3 scripts/stress_benchmark.py --before bench/tools/secret-scan-2113800 --after target/release/secret-scan --output bench/results/v2/stress-reproduced.json
```
### 当前检查与剩余边界
- `cargo test --locked --all-targets -j 2`:93 项测试通过。
- `cargo fmt --all --check`、`cargo clippy --all-targets --locked -j 2 -- -D warnings`、release 构建通过。
- 评分/合成留出集测试 32 项、压力脚本测试 15 项通过;固定上游规则导入及原语料生成器自检通过。
- `cargo package --locked --allow-dirty` 与真实 `.crate` 独立消费端验证通过,覆盖 clean/finding/missing 的退出码 0/1/2 及脱敏;尚未发布。
- 三 OS CI 已配置;本机只实测 macOS,不宣称 Linux/Windows 运行成功。Rust 1.96.1 本机检查通过;精确 1.96.0 交 CI。
主要未解决项是留出集的 25 个负例命中、跨规则重复输出、逐文件 progress 量,以及单文件/单次正则和 Git 元数据的内存或取消边界;这些比扩展 GPU、ML 或大量云连接器更值得下一轮优先处理。所有 60 项候选的范围仍按 [FEATURES.md](FEATURES.md) 区分已实现、部分实现和未实现。
## 重复发现与进度输出改进(v3)
本轮围绕误报、重复命中和进度输出量继续处理:
- 同一个解码视图中,只有**秘密字节范围完全相同**的跨规则命中才合并。非 `generic-` 规则、较高置信度、规则 ID 字典序依次决定主规则;合并记录保存排序后的 `matched_rule_ids`。相邻位置、部分重叠、Base64 内不同位置的同值都保留。
- 首条与末条进度保留,常规进度间隔至少 100ms。CLI 独立刷新首条 finding 和每条错误,之后仍使用缓冲写入;进度是事件触发,不承诺长 regex 运行期间的心跳。
- 旧留出集的 25 个 FP 均是 token 类键上的长说明文本。现有正例同时要求保留真实的长 phrase 凭证。仅从字节无法可靠判断用途,因此**没有为了消除这 25 个 FP 扩大过滤**,也没有按测试目录、具体值或键的连字符形式排除。
- 引擎配置身份升级,旧基线需要显式重建;schema 仍为 2。单一规则命中省略 `matched_rule_ids`,不增加无用的序列化字段。
原留出集已经用于诊断,本轮将它作为 regression。独立线程在检测实现冻结前生成了新的合成值/上下文集:600 正位置、300 负位置、580 个唯一正值,加入同一行多秘密和同值不同位置;格式与类别仍共享公开生成逻辑,不代表真实代码分布或凭证活性。
首轮 v3 测量发现单规则密集命中的临时候选集合增加耗时和内存。因此只增加一条实现快速路径:关键词预筛只选出一个规则时,直接构造结果;多候选仍按完全相同的合并语义运行。UTF-8、Base64、UTF-16 的两条路径输出一致性已有回归验证,未修改检测规则或过滤启发式。新留出集已观察,快速路径之后的同集运行明确属于复核,不能重新称为盲测。
最终二进制 SHA-256:`b607dd486f9a62670b0f42bb93e3739346ae4a27f1728f0be8e00851f5590239`。基线为提交 `9da912a` 的备份二进制,前后均重新实测:
| 数据集 | 旧 → 新 TP / FP / FN | 重复发现旧 → 新 |
|---|---|---:|
| 原始回归集 | 34 / 0 / 0 → 34 / 0 / 0 | 28 → 0 |
| 旧留出集,现为回归 | 599 / 25 / 1 → 599 / 25 / 1 | 421 → 0 |
| 新值/上下文合成集 | 595 / 25 / 5 → 595 / 25 / 5 | 369 → 0 |
这些数据上的 TP/FP/FN 未变,合并没有增加召回率,也没有消除 25 个歧义告警。新集首轮共实际运行 19 种当前 filesystem 扫描器及本项目旧版本;最终实现复核只重跑本项目前后版本,竞品成绩保留原测量来源。行号型输出在同一行多候选时记为 unlocalized,因此确认定位召回率不等于纯检测能力排名。
最终压力复测中,10,000 文件的 progress 从 **10,001 降到 3**,stdout 从 **1,144,455 降到 50,527 字节(减少 95.6%)**;首条结果约 33.93 ms,前后相同,整体耗时 156.58 → 148.72 ms。100,000 findings 的耗时为 1416.58 → 1387.89 ms,区间重叠,不能宣称显著加速;峰值 RSS 98.80 → 100.09 MiB,仍约多 1.3 MiB。首轮实现的明显耗时退步未在最终复测中出现,但原始数据完整保留。
常规 16 MiB / 128 MiB / 真实源码耗时分别为 68.28 → 60.18 ms、213.44 → 221.01 ms、71.21 → 71.26 ms。128 MiB 用例约慢 3.5%,不把减小输出量等同于全场景提速;三次重复和未清空缓存也不支持统计显著性或单一优化因果结论。
最终检查:**104 项 Rust 测试、53 项 Python 测试**通过,fmt、Clippy、Rust 1.96.1 检查、release 构建、真实 `.crate` 独立消费端退出码与脱敏验证通过。Linux/Windows CI 仍未远程执行,未发布。完整新旧运行、所有竞品质量、输出量、内存及退步见 [本轮结果索引](../bench/results/v3/README.md)。
## 候选漏检、JSONL 分配与测量修复(v4)
本轮修正三处具体问题:Airtable 规则使用格式必需的 `pat` 前缀筛选候选,规则导入脚本同步保留该适配;JSONL 直接序列化借用字段,去除中间 JSON 对象及收集报告输出时的克隆;压力脚本不再对已无管道的 selector 等待 100 ms,保留超时、资源统计和退出后的缓冲输出读取。
已观察的新合成集从 **595 TP / 25 FP / 5 FN** 改善为 **600 / 25 / 0**,Airtable 专用规则从 0/24 变为 24/24;旧合成集从 599 / 25 / 1 变为 600 / 25 / 0,原始回归集保持 34 / 0 / 0。所有这些集合现在都是回归集,不是本轮盲测。25 个歧义描述告警未消除,通用停用词语义未改。本轮重新执行 19 个当前 filesystem 扫描器及本项目旧版的质量测试,无法定位结果仍独立记录。
修正后的同一压力脚本交替执行新旧版本各 20 次:10 万条结果的扫描器 CPU 总时间中位数 **376.91 → 290.14 ms,降低约 23%**,但包含消费者校验的墙钟耗时仅 **1436.16 → 1424.66 ms**,RSS 约 **100.05 → 99.85 MiB**,不能宣称全链路显著提速或内存明显下降。
另外完成 20 对交替的常规 CLI 测量,JSON 解析位于计时之外。128 MiB 场景 CPU 总时间 **826.73 → 869.44 ms,增加约 5.17%**,20 对运行均增加;墙钟中位数 309.36 → 342.44 ms,存在明显调度波动。该语料的 512 个文件都有 `pat`,合计约 198 万处,却都没有 `airtable`。新版正确执行了旧版错误跳过的规则,候选增多与 CPU 成本增加一致,尚未做函数级归因。下一步应针对频繁前缀的候选定位探索算法,并验证与完整正则等价。
本轮完成 **110 项 Rust、59 项 Python 测试**,fmt、Clippy、Rust 1.96.1、release、规则再生成、语料自检及打包独立消费端验证。JSONL 字段语义不变,但对象键顺序改变;规则内容身份自动改变,已有基线需要显式重建。没有引入 Kingfisher SDK,也没有实现通用 HIR 预筛选或紧凑 Finding 布局。Linux/Windows CI 尚未执行。
候选二进制 SHA-256:`2f8029c58d3ad2ce1525cc2b3ab81e5799013057d9854a6b7e2e1cc6a51c0555`。完整原始数据、竞品表、测量边界和复现入口见 [v4 结果索引](../bench/results/v4/README.md)。
## 固定偏移候选筛选(v5)
复用 AC 的关键词位置,从正则 HIR 推导必需前缀之后固定字节偏移的必需字面量。检查不通过则不激活该次候选;存在通过的位置时仍用完整正则扫描原始内容。无法证明的结构沿用原路径,没有厂商 ID 硬编码、局部匹配窗口或新配置项。推导结果在引擎内首次关键词命中时缓存,避免每次构造引擎都额外解析全部规则。当前 225 条规则中有 7 条符合条件。
相对 v4,最终 20 对交替测量中:16 MiB CPU 总时间中位数 **133.70 → 130.24 ms(降低 2.59%)**,128 MiB **813.31 → 784.68 ms(降低 3.52%)**,两组均为 20/20 对降低。128 MiB 墙钟中位数 **236.40 → 228.63 ms(降低 3.29%)**;真实源码 CPU 和墙钟基本持平。不同批次的百分比不能相减,本轮不能证明已完全收回 v4 历史测量中的 5.17% 成本。内存没有明显改善,密集结果和 Git 压力场景大致持平。
两份已观察合成集均保持 **600 TP / 25 FP / 0 FN**,原始回归保持 34 / 0 / 0,能力标签保持 6 / 0 / 0。另行逐条比较完整 Finding(含指纹及规则证据)和 ScanContext,四个集合全部相同,v4 基线身份保持兼容。六种压力场景各 20 对、共 240 次运行的位置及规则一致。竞品未在本轮重跑,其数据仍引用 v4 的实际测量。
完成 **116 项 Rust、59 项 Python 测试**,fmt、Clippy、Rust 1.96.1、release 与最终打包消费端验证。保留了首轮立即解析 HIR 的实验及最终按需解析结果;前者不能当作最终成绩。最终二进制 SHA-256:`aca1a038f1faaf485e054dc7e208ce49027624fd63542019afe8b29e8a3412e5`。详细数据、算法边界、首条延迟与复现入口见 [v5 结果索引](../bench/results/v5/README.md)。
## 共享结果路径与解释文本(v6)
仅将 Finding 的 `path` 和 `explanation` 改为 `Arc<str>`:一次扫描首次命中才分配路径;同规则按普通、Base64、UTF-16、两种解码组合缓存四种解释文本。公开 Rust 字段类型发生破坏性变化,调用方需使用 `.into()` 构造、`.as_ref()` 读取;JSON 字符串、指纹和基线上下文保持一致。没有全局字符串池,Git commit 显示路径仍逐结果构造。
独立单线程预热分配探针显示,10 万条结果的分配调用 **1,200,007 → 1,000,008**,扩容调用 **200,017 → 17**,返回时增量存活字节 **76,308,704 → 38,711,592(减少 49.27%)**;Finding 结构体大小 **232 → 216 字节**。这是 allocator 请求布局的字节统计,排除输入、Engine 和已预热缓存,不是 RSS;计数器只用于分配分析,不用于耗时结论。四组探针完整输出摘要一致,释放结果后的存活增量均归零。
普通 release 二进制的 20 对交替测量中,单文件 10 万条结果的 **RSS 99.86 → 59.59 MiB(减少 40.33%)**,CPU **287.37 → 262.26 ms(减少 8.74%)**,首条结果 **229.28 → 209.81 ms**。包含 JSONL 消费者的总耗时只减少约 2.01%,输出量保持不变。额外四线程、16 文件共 10 万条结果的测量中,CPU 减少 8.55%,RSS 减少 35.74%,没有在该场景观察到共享引用计数造成的并行退步。普通吞吐及 Git 场景基本持平,稀疏结果场景的 RSS 中位数略高,不能宣称所有输入都更省内存。
两份合成回归集均保持 **600 TP / 25 FP / 0 FN**,原始集 34 / 0 / 0、能力标签 6 / 0 / 0;四个集合的完整 Finding 与 ScanContext 逐条一致。完成 **117 项 Rust、59 项 Python 测试**以及 fmt、Clippy、release、Rust 1.96.1、打包独立消费端验证。本轮竞品未重新测量,Linux/Windows CI 未执行。最终二进制 SHA-256:`94e3b936287ec45e44f37e596e67553d7a4762f5f287e78fa2a0a04391d7938a`。方法边界、完整原始数据和复现入口见 [v6 结果索引](../bench/results/v6/README.md)。