内容
# mcp-secret-sentinel

**MCP 服务器,扫描代码中暴露的密钥 — API 密钥、令牌、私钥和高熵字符串 — 具有占位符感知允许列表和编辑报告。**
密钥很少通过黑客泄露;它们通常通过提交泄露。代理(或匆忙的人)将 webhook URL 粘贴到配置文件中,暂存它,然后推送 — 从那一刻起,凭证就被泄露了,即使下一个提交删除了它,因为历史记录保留了每个添加的行。mcp-secret-sentinel 为代理提供了一个预提交检查点:扫描代码片段、文件、整个树、暂存差异或最近的历史记录,并返回一个严重性排名、完全编辑的报告,以便在**提交前**采取行动。完整的值从未出现在工具输出中,因此也从未进入对话记录中。
## 工具
| 工具 | 参数 | 返回 |
|---|---|---|
| `scan_text` | `text`, `source_name="input"` | 原始代码片段(代码、配置、差异、日志)的发现 |
| `scan_file` | `path` | 一个文件的发现;跳过二进制文件(空字节启发式)和超过 5 MB 的文件 |
| `scan_directory` | `path`, `max_files=500` | 递归扫描;跳过 `.git`、`node_modules`、虚拟环境、`__pycache__`、`dist`、`build`、缩小版 JS、锁定文件,并遵守简单的 `.gitignore` 模式 |
| `scan_git_staged` | `repo_path` | 仅扫描 `git diff --cached` 中添加的行 — 下一个提交将发布的确切内容 |
| `scan_git_history` | `repo_path`, `max_commits=50` | 扫描最后 N 次提交中添加的行,并使用提交哈希标记每个发现 |
| `list_patterns` | — | 具有严重性和补救建议的活动检测器,以及允许列表规则 |
所有扫描工具返回相同的格式:
```json
{
"clean": false,
"findings": [
{
"file": "config/notify.yaml",
"line": 14,
"pattern": "Slack incoming webhook",
"severity": "high",
"redacted": "hook…(77 chars)",
"advice": "任何拥有此 URL 的人都可以向您的 workspace 发送消息。请在 Slack 应用设置中重新生成 webhook,并从环境变量中加载 URL。"
}
],
"files_scanned": 6,
"summary": "在 6 个扫描文件中发现 1 个潜在密钥:1 个高级别。请勿在删除或替换这些密钥之前提交或推送。"
}
```
### 它能检测什么
十九种正则表达式检测器:GitHub 令牌(经典和细粒度)、OpenAI / Anthropic / NVIDIA / Google / Stripe(实时)/ Twilio 密钥、AWS 访问密钥 ID 和秘密访问密钥、Slack 令牌和传入 webhook、Discord webhook、JWT、私钥块(RSA / EC / OPENSSH / PGP)、数据库和队列连接字符串(带有嵌入式凭证)(Postgres、MySQL、MongoDB、AMQP、Redis),以及引用代码中引号内的 `password` / `secret` / `token` 样式赋值和 dotenv 样式 `UPPER_CASE=value` 行。
在正则表达式之上,Shannon 熵检测器会标记引用字符串中 20 个字符以上且经验熵达到 **4.5 位/字符** 的字符串 — 随机凭证材料的签名 — 但前提是没有特定的模式声称该跨度。
### 它故意忽略的内容(允许列表)
在报告之前,每个候选值都会根据以下占位符启发式进行检查:
- **太短** — 值小于 8 个字符,太短而不能成为真正的凭证。
- **被掩码** — 值大部分(≥ 80%)为 `X`、`x`、`*` 或点:已经被人类编辑过,包括供应商前缀后跟 `XXXX…` 运行。
- **`example`** — 任何包含 `example` 的值(任何情况):涵盖 `example.com` / `example.org` 域 *和* 供应商文档的示例密钥,如 AWS 文档中以 `EXAMPLE` 结尾的密钥。
- **`placeholder`**、`**changeme**` (也包括 `change-me` / `change_me`)— 约定的填充标记。
- **your-…-here** — 填空标记。
- **`<angle brackets>`** — 文档样式占位符。
- **`${TEMPLATE_VARIABLES}`** — 秘密在其他地方注入,而不是存储在此处。
- **环境查找** — 引用 `os.environ` 或 `process.env` 的值:环境查找是修复程序,而不是泄漏。
### 删减保证
每个发现仅显示前 4 个字符和总长度 — 例如 `"hook…(77 chars)"`。完整值从不出现在输出、记录或日志中。这在代码中(单个 `redact()` 阻塞点)和测试套件中强制执行,测试套件断言原始值在序列化结果中不存在。
## 它是如何工作的
```mermaid
flowchart TD
A[代理调用扫描工具] --> B{源}
B -->|scan_text / scan_file| C[分割成行]
B -->|scan_directory| D[遍历树,跳过 .git, node_modules,<br/>二进制文件,锁定文件,.gitignore 匹配] --> C
B -->|scan_git_staged / scan_git_history| E[git diff / git log 带有零上下文,<br/>仅保留添加的行] --> C
C --> F[19 个正则表达式检测器,<br/>特定模式优先]
C --> G[香农熵 >= 4.5 位/字符<br/>在 20 个字符以上的字符串中]
F --> H{允许列表检查:<br/>占位符,掩码值,<br/>示例域,环境查找}
G --> H
H -->|占位符| I[丢弃,不报告]
H -->|实际候选人| J[编辑:前 4 个字符 + 长度]
J --> K[报告:文件,行,模式,<br/>严重性,编辑,建议]
```
## 快速入门
```bash
pip install -e .
```
**Claude Desktop** — 添加到 `claude_desktop_config.json`:
```json
{
"mcpServers": {
"secret-sentinel": {
"command": "python",
"args": ["/absolute/path/to/mcp-secret-sentinel/server.py"]
}
}
}
```
**Claude 代码**:
```bash
claude mcp add secret-sentinel -- python /absolute/path/to/mcp-secret-sentinel/server.py
```
## 示例会话
> **用户:** 我即将推送 acme-app — 确保我没有泄漏任何内容。
>
> **Claude** 调用 `scan_git_staged(repo_path="C:/dev/acme-app")`:
>
> ```json
> {
> "clean": false,
> "findings": [
> {
> "file": "config/notify.yaml",
> "line": 14,
> "pattern": "Slack incoming webhook",
> "severity": "high",
> "redacted": "hook…(77 chars)",
> "advice": "任何拥有此 URL 的人都可以向您的 workspace 发送消息。请在 Slack 应用设置中重新生成 webhook,并从环境变量中加载 URL。"
> }
> ],
> "files_scanned": 6,
> "summary": "在 6 个扫描文件中发现 1 个潜在密钥:1 个高级别。请勿在删除或替换这些密钥之前提交或推送。"
> }
> ```
>
> **Claude:** 暂停推送 — `config/notify.yaml` 第 14 行在暂存差异中包含一个实际的 Slack webhook URL。三个步骤:
> 1. 用环境查找替换字面 URL,例如 `SLACK_WEBHOOK_URL`。
> 2. 在您的 Slack 应用设置中重新生成 webhook — 将当前 webhook 视为已泄露。
> 3. 重新暂存文件,我将进行验证。
>
> **用户:** 完成 — 再次检查。
>
> **Claude** 调用 `scan_git_staged(...)` → `"clean": true, "summary": "暂存更改是干净的 — 6 个文件(带有添加内容)已扫描,无密钥在添加的行中。"` → `scan_git_history(repo_path="C:/dev/acme-app", max_commits=20)` → `"clean": true`。
>
> **Claude:** 清洁 — 暂存更改和最近的 20 次提交不包含可检测的密钥。可以推送。
## 限制
- **`.gitignore` 支持是尽力而为**:普通名称、`*.ext` 通配符、`dir/` 和 `/anchored` 模式仅来自根 `.gitignore`。没有 `!` 否定、`**` 通配符,也没有嵌套忽略文件。
- **熵需要多样性**:经验字符串熵最多为 log2(不同字符),因此候选字符串需要至少 23 个不同字符才能清除 4.5 位/字符。短随机字符串由正则表达式检测器覆盖。
- **未引用通用赋值** 仅在 dotenv 样式 `UPPER_CASE=value` 行中检测到 — 故意与普通代码中的假阳性进行权衡。
- **不是 CI 替换**:专用扫描器(gitleaks、trufflehog)带有数百条规则,应在您的管道中进行。 此服务器是代理可以在提交存在之前运行的快速本地检查点。
## 开发
```bash
pip install -e ".[dev]"
python -m pytest
```
测试套件演练每个检测器、允许列表、熵、编辑、目录遍历和真正的临时 git 仓库 — 并且从不包含一个现实的密钥文字:每个积极的固定装置都在运行时通过连接来组装。
## 许可证
MIT