关于 Keyspoor
Keyspoor 是面向 AI 编码工作流的离线密钥扫描器,提供命令行工具、Rust 库和只读 MCP 服务。它扫描文件与 Git 数据,报告不会包含密钥值或源代码片段。
主要功能
- 01离线扫描文件和 Git 数据
- 02扫描暂存更改与本地历史
- 03读取 ZIP、tar 和 gzip 归档
- 04生成脱敏 JSON、JSONL 和 SARIF 报告
- 05自定义规则与指纹基线
适用场景
- 01检查仓库中是否泄露凭据
- 02提交前扫描暂存更改
- 03检查本地可访问的 Git 历史
- 04为编码 Agent 提供扫描工具
MCP 相关
只读 MCP 服务向 Agent 提供 `scan_text` 和 `scan_paths`。它还支持持续 JSONL 请求、进度报告和取消操作。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
2收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| path | string | 否 | 逻辑文件名,不会从磁盘读取;默认值为 stdin |
| text | string | 是 |
详细介绍
Keyspoor
面向 AI 编码工作流的离线密钥扫描工具。 Keyspoor 可扫描文件、已暂存的 Git 更改、本地 Git 历史记录以及 ZIP/tar/gzip 归档,并生成经过脱敏的 JSON、JSONL 和 SARIF 结果。可将其用作原生 CLI、 可复用的 Rust 库或只读 MCP 服务。通过 MCP 连接 Agent; 使用暂存钩子或必需的 CI 检查来阻止提交或合并。
网站 · Rust API · crates.io · npm · 版本发布 · 简体中文
60 秒快速体验
使用 Node.js 20+,将以下内容粘贴到 macOS/Linux 终端中。下面的值是 为本演示编造的,并非凭据:
printf 'password=KspDemo_7zQ2mX9pL4vN6sR8\n' | npx -y [email protected] scan - --format json
echo "exit=$?"
预期结果:exit=1,且发现一项问题。相关报告字段如下:
{"complete":true,"findings":[{"rule_id":"generic-credential-unquoted","path":"stdin","line":1,"column":9,"redacted":"[REDACTED]"}],"errors":[]}
这只是节选;实际报告还包含范围、指纹、统计信息和扫描上下文。报告不包含该值或源代码片段。
在 Windows PowerShell 中,将相同的合成字符串传递给
npx.cmd -y [email protected] scan - --format json,然后检查 $LASTEXITCODE。
观察提交被阻止,然后修复

安装 Git 和 keyspoor CLI 后,从检出目录运行完整演示:
sh examples/first-scan.sh
演示会创建并删除自己的临时仓库。此录制来自一次实际的 0.1.3 运行,使用的是编造的输入:发现问题时退出码为 1,提交被阻止;修复后退出码为 0,提交被接受。此钩子演示不涉及任何 Agent。 MCP 设置为 Agent 提供工具;但不能保证它们会在每次提交前调用这些工具。CI 和钩子提供强制检查。
| 你的目标 | 从这里开始 |
|---|---|
| 扫描仓库或已暂存的更改 | CLI 命令 |
| 阻止拉取请求并保存报告 | 完整 CI 设置 · 可直接复制的工作流 |
| 为编码 Agent 提供扫描工具 | Codex、Claude Code 和 Cursor 设置 |
| 在 Rust 中嵌入扫描器 | 库示例 · Rust API |
安装
# Rust toolchain (1.96 or newer)
cargo install keyspoor --version 0.1.4 --locked
# Or Node.js 20+: the npm package runs the native Rust CLI
npm install -g [email protected]
# Or Homebrew (macOS / Linux)
brew install majiayu000/tap/keyspoor
keyspoor scan . --format jsonl
当前版本:0.1.4。Rust API 尚未达到 1.0,可能会发生变化。npm
软件包包含适用于 macOS(Apple Silicon/Intel)、Linux GNU
(ARM64/x64)和 Windows x64 的原生二进制文件;它是 CLI 启动器,而非 JavaScript 扫描 SDK。
安装时不会运行钩子,也不会在运行时下载二进制文件。首次通过 npm/npx
安装需要连接软件包注册表;扫描本身可离线进行。
也可使用独立二进制文件。
要从源代码构建,请运行 cargo build --release --locked,并使用
target/release/keyspoor。
为什么选择 Keyspoor
- 专为离线设计: 扫描不会调用凭据提供方,报告也不会包含原始密钥或源代码片段。
- 可复用的 Rust 引擎: 规则只需编译一次,即可在多次调用和多个线程之间共享
Engine;无需依赖扫描器 SDK。 - Agent 接口: MCP
scan_text/scan_paths、持久化 JSONL 请求、进度报告与取消功能,以及明确的不完整扫描报告。 - 了解仓库内容的输入: 暂存索引内容、本地 Git 历史、有界的归档展开、忽略文件和指纹基线。
- 可检查的证据: 有版本控制的规则、记录在案的排除项,以及可复现的质量、吞吐量和内存分配测量结果。
匹配、关键词分派、过滤、解码、位置映射、Git 获取和 Agent 接口均在此实现。本项目不依赖 Kingfisher 或其他密钥扫描器 SDK,而是使用通用的 Rust 正则表达式、Aho–Corasick、哈希和归档库。
内置目录包含 221 条改编自采用 MIT 许可证的 Gitleaks v8.30.1 目录的规则,以及另外独立编写的 4 条规则(三条通用赋值规则和一条 URI 密码规则),默认引擎共包含 225 条规则。 请参阅规则来源与排除项和第三方声明。这不是与 Gitleaks 兼容的引擎:无法保留语义的规则均已明确排除。 实现决策记录了构建与改编的边界,以及被否决的 SDK 和包装器替代方案。
CLI 和仓库扫描
keyspoor scan /path/to/project --format json
keyspoor staged /path/to/repository
keyspoor history /path/to/repository
keyspoor history /path/to/repository --range main..HEAD
keyspoor scan - --format jsonl
keyspoor scan /path/to/project --write-baseline baseline.json
keyspoor scan /path/to/project --baseline baseline.json
keyspoor rules
keyspoor serve
keyspoor mcp --root /path/to/project
版本 0.1.4 更改了凭据上下文匹配和引擎配置标识。请检查并重新创建旧基线。参见修正结果和基准测试。
退出代码:0 表示所选扫描已完成且未报告发现项;1 表示已完成且发现了问题;2 表示发生错误或扫描未完成。你可以使用合成输入检查另外两种结果:
printf 'ordinary configuration\n' | npx -y [email protected] scan - --format json
echo "exit=$?" # 0: complete=true, findings=[]
printf 'ordinary configuration\n' | npx -y [email protected] scan - --max-bytes 4 --format json
echo "exit=$?" # 2: complete=false, errors contains "input exceeds 4 byte limit"
发现问题时,请在本地检查其路径和位置,从受跟踪文件中移除暴露的凭据,并轮换任何已暴露的真实凭据。记录基线前,请检查已知的有意保留问题。将每个 2 结果调查清楚后,再将扫描视为已完成。
被忽略的发现项并不能证明原始输入中没有秘密。基线会抑制现有指纹,但会保留已更改的秘密值。未完成的扫描不能替代基线。基线架构 2 会绑定扫描模式、规范根目录、生效的忽略策略(包括 Git/Jujutsu 仓库边界)、大小预算和引擎设置(包括规则内容和指纹密钥标识)。不同的范围或策略不能与该基线进行比较,也不能覆盖该基线。若有意更改策略,请创建单独的基线。架构 1 的基线会被拒绝。规范文件系统标识可确保 ./ 等别名保持稳定;报告的路径和规则路径谓词仍相对于所选源根目录。
每个发现项都会脱敏,并包含规则、路径、字节范围、从 1 开始计数的行号、从 0 开始计数的字节列号、置信度、说明和指纹。不会序列化原始秘密或源代码片段。默认的无密钥指纹是摘要,不是加密,也无法防止对低熵秘密进行猜测。对于带密钥的 BLAKE3 标识,请通过 --fingerprint-key-file 提供恰好 32 个私有字节。比较基线时,请使用相同的私钥。
同一解码视图中,秘密字节范围完全相同的发现项会合并。rule_id 用于标识主要规则:优先选择非 generic- ID,其次选择置信度更高的规则,最后按规则 ID 的字典序选择。合并后的发现项包含排序后的 matched_rule_ids,其中包括主要规则和其他匹配规则;仅匹配单条规则的发现项不包含此字段。相邻出现项、部分重叠项以及同一 Base64 容器中位置不同的项仍会分别保留。主要规则决定指纹。此语义更新会更改引擎配置标识,因此必须显式重新创建旧基线,而不能静默比较。
扫描不会联系凭据提供方,也不会验证凭据是否有效。文件系统扫描默认遵循忽略文件;--no-ignore 会纳入被忽略的文件,但仍会排除 Git 元数据。超出大小限制的输入会报告为未完成,而不是干净无问题。默认文件和归档展开限制为 64 MiB;--max-bytes 可更改预算。遍历归档时绝不会将成员写入磁盘。ZIP、tar 和 gzip 共用展开预算,嵌套层级最多为四层。加密或损坏的成员仍会作为错误显示。
暂存模式读取每个已更改文件的 索引 版本,而不是工作树中的版本。它会扫描整个已暂存文件,以保留凭据对上下文。历史模式扫描本地可访问的提交,每个唯一 Blob 只读取一次,并保留提交/路径出现记录。它不会获取 LFS、子模块或远程引用,也会解包 Git Blob 中受支持的归档,并保留提交/成员路径。stats.files/bytes 统计唯一输入;detection_passes 统计实际调用引擎并进行路径敏感检测的次数。
GitHub Action
将完整的拉取请求工作流
复制到 .github/workflows/keyspoor.yml,或者参阅 CI 设置指南
了解报告、权限、基线和失败处理。其扫描作业可以设为分支保护的必需检查。核心步骤如下:
permissions:
contents: read
steps:
- uses: actions/checkout@v7
- uses: majiayu000/keyspoor@v1
id: secrets
with:
path: .
format: sarif
- uses: actions/upload-artifact@v7
if: always() && steps.secrets.outputs.report-path != ''
with:
name: keyspoor-report
path: ${{ steps.secrets.outputs.report-path }}
将这些步骤放入一个作业中。该 Action 会安装其源引用中记录的确切 npm 包版本,扫描所选路径,并将经过脱敏的报告保存到检出目录之外。report-path 指向该文件;exit-code 保留 0(无问题)、1(发现问题)或 2(出错/未完成)。发现问题或出错都会导致步骤失败;出错后,报告可能不完整。使用完整提交 SHA 固定 Action 的版本,确保不可变,而不要使用维护中的 v1 引用。支持的报告格式包括 SARIF(默认)、JSON 和 JSONL。安装需要访问 npm 注册表;检测本身仍可离线进行。该 Action 扫描文件;如需扫描暂存内容/历史记录模式和自定义引擎选项,请使用 CLI。
发布通过 GitHub Actions 和 npm、crates.io 的可信发布功能进行。现有 tap 中的 Homebrew 更新每小时运行一次,可能因 GitHub 调度而延迟。请参阅发布指南,了解触发条件和验证方法。
库和自定义规则
如需使用此示例,请在依赖项中添加 keyspoor = "0.1" 和 anyhow = "1"。
use keyspoor::{Engine, EngineConfig};
fn main() -> anyhow::Result<()> {
let engine = Engine::new(EngineConfig::default())?;
let findings = engine.scan_bytes("config.env", b"ordinary configuration")?;
assert!(findings.is_empty());
Ok(())
}
在多次调用和多个线程之间复用引擎,以避免重复编译。它不会安装全局运行时、日志记录器或执行器。
Finding.path 和 Finding.explanation 是共享的 Arc<str> 值。相较于 String,这是一个破坏性 Rust API 变更:分配自有字符串时使用 .into(),读取 &str 时使用 .as_ref(),要更改文本则替换该字段。JSON 中仍包含普通字符串;指纹、扫描上下文和基线标识均未改变。在单次引擎扫描中生成的发现会共享路径;使用同一引擎进行多次调用时,生成的发现会共享说明。反序列化不会合并重复字符串。
自定义 JSON 规则文件使用
{"rules": [...]},可通过 --rules path.json 或规则目录提供。--no-builtin 仅选择自定义规则。每条规则包含 id、name、pattern,以及可选的 secret_group、keywords、min_entropy、confidence、path、allowlist 和 exclude_paths。关键字不区分大小写,并按 OR 逻辑组合,作为必要条件;没有关键字的规则始终会被考虑。允许列表组包含 condition(or/and)、target(secret/match/line)、regexes、paths 和 stopwords。各组按 OR 逻辑组合;同一类别中的谓词按 OR 逻辑组合,随后将有值的类别按照 condition 组合。停用词是密钥中的不区分大小写的子字符串。空组验证失败。省略或置为 null 的 secret_group 会选择第一个非空捕获组(或整个匹配项);显式设置为 0 则选择整个匹配项。这些规则架构变更具有破坏性。未知规则字段和无效正则表达式会明显报错。编译错误中绝不会回显规则模式。自定义规则说明和标识符属于可信配置,不应包含真实密钥。
通用 API/访问/认证令牌赋值会抑制较长的解释性文本(至少八个单词,且带有句子/从句标点)。此启发式不会抑制自然语言形式的密码/密钥赋值,但格式为长篇带标点短语的真实令牌仍可能被漏检。提供方规则保留上游限制;请参阅规则来源文档。
Agent 接口
请先阅读 Codex、Claude Code 和 Cursor 的已测试配置指南。 MCP 提供只读工具;Agent 是否调用这些工具取决于客户端、 权限和任务。若扫描必须在更改获准前运行,请使用必需的 CI 检查或 Git 钩子。
serve 每行接受一个 JSON 对象:{"id":1,"path":"file.txt","text":"..."}。
响应包含相同的 id 和完整的脱敏扫描报告。规则只编译一次。格式错误的 JSON 会返回错误,但不会回显负载;
超大的请求会终止这个简单流。
MCP stdio 服务支持初始化、ping、工具列表以及对 scan_text 和 scan_paths 的调用。文件路径必须解析到配置的根目录之下。服务不会执行写入或网络验证。根目录检查并非 OS 沙箱,无法防止另一个本地进程并发替换路径。
MCP 错误与工具失败彼此不同,格式错误或超大的帧不会变成成功的无问题结果。stdout 仅包含协议消息。
一次只运行一个扫描,同时 ping 和取消操作仍能及时响应。客户端可以提供进度令牌并按请求 id 取消;已取消的请求不会收到最终响应,会话仍可继续使用。取消检查发生在文件、Git blob 和归档成员边界(也会检查归档读取块),而不会在单次正则操作内部检查。工具结果最多包含 100 条发现和 100 条错误,二者共享 512 KiB 条目预算,另附总数和
output_truncated;截断与扫描是否完整无关。
支持 JSON、JSONL 和 SARIF。普通文件系统/Git JSONL 扫描会在扫描期间输出 finding、error 和 progress 记录,最后输出包含计数和完整性信息的 summary。
有界工作线程队列可避免收集所有文件报告;输出错误会停止继续获取数据。每个工作线程仍会缓冲一个输入及其发现,Git 历史元数据也会常驻内存。JSON/SARIF、stdin,以及使用基线的 JSONL 扫描会收集报告;必须先完成基线验证,才能输出筛选后的结果。SDK 调用方可以使用带有 ScanControl 和可失败事件接收器的 scan_*_stream API。常规进度最多每 100 ms 输出一次,并保留首次和最终快照。这些是事件驱动的更新,并非单次长时间正则操作期间的心跳。
CLI 会立即刷新首条发现和每条错误;后续发现使用缓冲写入,进度/最终记录会刷新剩余输出。
验证与比较
cargo fmt --check
cargo test --locked
cargo clippy --all-targets --locked -- -D warnings
python3 -m unittest discover -s bench -p test_run.py
python3 bench/fixtures/generate.py --self-test
python3 scripts/import_rules.py --check
python3 -m unittest discover -s bench -p test_holdout.py
python3 -m unittest discover -s scripts -p test_stress_benchmark.py
python3 scripts/check_package.py --allow-dirty
独立合成评估记录了一个冻结的 88 个样本留出集、其中的漏报和误报,以及基线工作流的限制。基准测试方法与复现将合成标注质量与未标注真实来源的吞吐量分开。保留了版本、命令、硬件、二进制指纹、时间、内存、解析器完整性和不支持的功能。在线验证已禁用。这些测量结果不能证明生产环境中的准确率,也不能证明其普遍领先于其他工具。
最新的 v6 内存分配与性能报告在 Apple M2 Max/macOS 上测量了 20 组交替的改动前后对比。对于一个包含 100,000 条发现的合成 JSONL 工作负载,扫描器的峰值 RSS 从 99.86 MiB 降至 59.59 MiB,扫描器 CPU 时间减少了 8.74%。常规吞吐量和 Git 工作负载基本未变。这些是本项目内部的变化,并非与竞品的速度排名。此前观测到的合成回归集保留了 600 个真正例、25 个假正例和零个假负例;这并非真实世界的精确率估算。
请参阅测量结果、按版本整理的跨工具比较、比较方法和逐项功能研究。该研究涵盖 22 个外部项目;对于不支持、不可用和依赖云端的项目,均予以标明,而非计为失败。历史产物将本项目称为 secret-scan,这是 Keyspoor 更名前的名称;记录的命令和二进制哈希均已保留。
功能实现矩阵将研究目录中的每个项目映射为已实现、部分实现或未实现状态。存在 CLI 或 MCP 服务并不意味着提供云连接器、凭据验证或撤销、GPU/ML 后端或语言绑定。
许可证与支持
Keyspoor 采用 Apache-2.0 许可证。改编的规则数据保留其上游 MIT 许可证,并在 THIRD_PARTY_NOTICES 中注明出处。请通过 GitHub Issues 报告可复现的错误或请求功能;请使用合成示例,切勿包含真实凭据。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。