DreadpiratePickles/patch-peeker
GitHub: DreadpiratePickles/patch-peeker
一款授权 Web 应用安全扫描器,以礼貌爬虫和良性探测标记常见输入输出处理弱点,生成按优先级排序的修复报告。
Stars: 0 | Forks: 0
# 🩹 Patch Peeker
### 爬取已授权的 Web 应用并标记常见的输入和输出处理弱点 —— 然后为你提供一份带有证据支持且按优先级排序的修复报告。
     
*一个对什么都大呼小叫的扫描器,就是没人看的扫描器。*
Patch Peeker 是一个小巧、诚实且*防御性*的初步 Web 评估工具。它会礼貌地进行访问(可识别的 User-Agent、仅限同源、有限速),记录所有内容,并且绝不会进行任何破坏性操作。它专为**你拥有或获得明确授权**的系统而设计。
它不能替代深入的手动测试或完整的 DAST 套件。它是你首先运行的工具,用于将“有人应该看看这个应用”的念头,转化为一份按优先级排序且具体的、可修复的漏洞清单。
## 检查内容
| 类别 | 检查项 | 方式 |
| --- | --- | --- |
| `headers` | 缺失 Content-Security-Policy | 被动(响应头) |
| `headers` | 缺失点击劫持保护(X-Frame-Options / CSP `frame-ancestors`) | 被动 |
| `headers` | HTTPS 下缺失 HSTS | 被动 |
| `headers` | 缺失 `X-Content-Type-Options: nosniff` | 被动 |
| `headers` | 缺失 Referrer-Policy | 被动 |
| `headers` | 版本信息泄露(`Server`, `X-Powered-By`, …) | 被动 |
| `cookies` | `Set-Cookie` 缺失 HttpOnly / Secure / SameSite | 被动 |
| `csrf` | 没有类似 CSRF token 字段的 POST 表单 | 被动 |
| `forms` | 没有命名输入的表单(无法测试) | 被动 |
| `injection` | 单引号探测后的 SQL 错误泄露 | **主动**(良性) |
| `injection` | 反射型 HTML 注入(未编码的标记) | **主动**(良性) |
被动检查仅读取服务器已发送的内容。主动检查会将*良性*值(一个单引号 `'` 和一个无害的标记标签)提交到表单字段中,并查看响应。使用 `--passive` 时会完全跳过主动检查。
## 工作原理(数据流)
```
--i-have-authorization (required for non-local targets)
│
validate_url ──▶ require_authorization ──▶ crawl ──▶ [CrawledPage, …] ──▶ checks ──▶ dedupe+rank ──▶ Findings
(scheme/host (consent gate) (same-origin, │ ├─ assess_headers │
allowlist) bounded, polite) │ ├─ assess_cookies ├─▶ rich table + summary
│ ├─ assess_forms ├─▶ --json (pure)
│ └─ probe_form (unless └─▶ --out report.md
│ --passive)
▼
html · headers · Set-Cookie
```
1. **`validate_url`** —— 拒绝任何非 `http`/`https` 的内容(拒绝 `file://`, `ftp://`, `gopher://`, …)以及任何无主机的 URL,*在打开套接字之前*。
2. **`require_authorization`** —— 允许 localhost/私有 IP 目标;其他任何目标都需要 `--i-have-authorization`。位置判断仅基于字面主机名,绝不依赖 DNS 解析(保守的选择)。
3. **`crawl`** —— 广度优先,**仅限同源**,受 `--max-pages` 限制。每个请求都通过 `--delay` 进行限速,在 `--timeout` 后超时,最多跟随 5 次重定向,如果落在源之外则被丢弃。响应体最多读取 3 MB 上限,因此恶意目标无法耗尽内存。死链/缓慢/阻塞的页面会被跳过,绝不致命。
4. **checks** —— 每个爬取的页面都会输入到被动的标头/cookie/表单检查中;除非使用 `--passive`,否则表单还将额外进行主动探测。
5. **dedupe + rank** —— 跨页面的相同发现合并为一个;结果按严重程度排序:critical → high → medium → low → info。
6. **render** —— 为人类提供颜色编码的表格 + 摘要面板,为机器提供纯 JSON,和/或 Markdown 报告文件。
**数据模型**(无数据库):`CrawledPage`(url, final_url, status, headers, html, 原始 Set-Cookie 行)和 `Finding`(severity, title, url, evidence, remediation, confidence, category)。
## 安装
```
cd patch-peeker
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
pip install -e . # provides the `patch-peeker` command
cp .env.example .env # optional defaults
```
没有可编辑安装?一切也可以通过 `PYTHONPATH=src python -m patch_peeker.cli …` 运行。
## CLI 用法
全局标志(在子命令之前或之后使用):`--plain` / `--no-color`(也遵循 `NO_COLOR` 环境变量和非 TTY 输出),`--version`。
### `scan`
```
patch-peeker scan --url URL [--i-have-authorization] [--max-pages N] [--delay S]
[--timeout S] [--passive] [--user-agent UA] [--out FILE] [--json]
```
| 标志 | 默认值 | 含义 |
| --- | --- | --- |
| `--url` | *(必填)* | 目标 URL(仅限 `http`/`https`)。 |
| `--i-have-authorization` | off | 对于**非本地**目标是必填的。你声明你拥有它或拥有书面许可。 |
| `--max-pages` | 12 | 爬取的最大页面数(环境变量 `PATCH_PEEKER_MAX_PAGES`)。 |
| `--delay` | 0.2 | 每次请求之间的礼貌延迟(秒)。 |
| `--timeout` | 10 | 每次请求的超时时间(秒)。 |
| `--passive` | off | 跳过主动表单探测;仅进行被动检查。 |
| `--user-agent` | `patch-peeker/2.0 (authorized web assessment)` | 覆盖发送的 UA。 |
| `--out FILE` | — | 同时写入一份 Markdown 报告。 |
| `--json` | off | 向 stdout 输出**纯** JSON(绝无任何装饰)。 |
**退出代码:** `0` 干净 / 仅 low 级别,`2` 至少有一个 high/critical 发现(在 CI 中很有用),`1` 发生错误(错误输入,拒绝授权)。
**示例**
```
# Localhost — 不需要 authorization flag:
patch-peeker scan --url http://127.0.0.1:5000
# 你拥有 / 被授权测试的远程目标:
patch-peeker scan --url https://staging.example.com --i-have-authorization --max-pages 20
# 温和的 passive-only pass(不提交 payloads):
patch-peeker scan --url http://127.0.0.1:8080 --passive
# 适用于 pipeline 的 machine-readable 格式:
patch-peeker scan --url http://127.0.0.1:5000 --json > findings.json
# 咨询级的 Markdown 交付物:
patch-peeker scan --url http://127.0.0.1:5000 --out report.md
```
**输出示例**
```
┌──────────────── 🩹 patch-peeker ─────────────────┐
│ crawl authorized web apps, flag input-handling weaknesses │
└───────────────────────────────────────────────────┘
Findings for http://127.0.0.1:8791
┌────┬──────────┬──────────┬─────────────────────────────────────────┬───────────────────────┐
│ # │ Severity │ Category │ Title │ URL │
├────┼──────────┼──────────┼─────────────────────────────────────────┼───────────────────────┤
│ 1 │ MEDIUM │ headers │ Missing Content-Security-Policy header │ http://127.0.0.1:8791 │
│ 2 │ MEDIUM │ headers │ Missing clickjacking protection │ http://127.0.0.1:8791 │
│ 3 │ MEDIUM │ csrf │ POST form missing obvious CSRF token │ http://127.0.0.1:8791/│
│ 4 │ LOW │ headers │ Missing X-Content-Type-Options: nosniff │ http://127.0.0.1:8791 │
│ 5 │ INFO │ headers │ Missing Referrer-Policy header │ http://127.0.0.1:8791 │
└────┴──────────┴──────────┴─────────────────────────────────────────┴───────────────────────┘
┌───────────────────────── summary ──────────────────────────┐
│ 3 medium · 1 low · 1 info │
│ 5 findings — 0 high/critical needing attention │
└─────────────────────────────────────────────────────────────┘
```
### `serve`
本地 Flask UI:粘贴一个 URL,勾选授权框,在浏览器中阅读报告。
```
patch-peeker serve [--host HOST] [--port PORT] [--debug]
```
| 标志 | 默认值 | 备注 |
| --- | --- | --- |
| `--host` | `127.0.0.1` | 绑定到 loopback。**非 loopback 绑定会受到警告** —— 此 UI 会抓取任意 URL(属于 SSRF 攻击面);不要将其暴露在外。 |
| `--port` | `5070` | 环境变量 `PATCH_PEEKER_PORT`。 |
| `--debug` | off | **不安全** —— 启用 Werkzeug 的交互式调试器(可导致任意代码执行)。会发出警告;切勿在暴露的端口上使用。 |
```
patch-peeker serve # http://127.0.0.1:5070
```
## 安全与授权
- **仅限已授权目标。** 远程扫描需要 `--i-have-authorization`;UI 需要勾选所有权复选框。这是你声明你拥有目标或持有书面许可的凭证。未经授权的扫描可能是违法的。
- **天生防御性。** 仅进行同源爬取;主动探测仅提交*良性*值(`'` 和一个惰性标记)—— 没有破坏性输入,没有利用,没有数据泄露。
- **有界且礼貌。** `--max-pages`,`--delay`,`--timeout`,5 次重定向限制,3 MB 响应限制,以及诚实可识别的 User-Agent。
- **感知恶意输入。** 不受信任的响应字节有大小限制,被宽松解码,并以纯文本形式呈现(绝不重新解释为终端标记)。输出流被强制为 UTF-8,因此奇怪的字节不会导致 Windows 控制台崩溃。
- **无秘密泄露。** 绝不打印 Cookie 的*值* —— 仅打印 Cookie 名称和标志状态。
- **安全的默认设置。** `serve` 绑定到 `127.0.0.1`;调试模式是可选的,并且会发出强烈警告。
已知局限性(设计使然):CSRF/标头检测是启发式的,无法看到未向其展示的服务器端标头/SameSite 控制;认证区域需要你扩展会话处理;仅被动模式无法找到反射/SQL 错误问题。
## 开发与测试
```
cd patch-peeker
PYTHONPATH=src python -m pytest -q
PYTHONPATH=src python -m patch_peeker.cli --help
```
测试快速且封闭 —— 没有真实网络,没有 sleep。网络相关代码(爬虫、探测、Cookie 提取)通过内存中的 fake 进行测试,因此整套测试可以在不到一秒的时间内运行完毕。
**前提条件:** Python 3.11+,以及 `flask`,`requests`,`beautifulsoup4`,`rich`(参见 `requirements.txt`)。
## 🏷️ 为什么叫 "Patch Peeker"?
Peek(偷看),而不是 Poke(捅破)。它会礼貌地爬取,安全地探测,并且在你声明已获授权之前拒绝运行。返回的结果根据你应该首先修复的内容进行排序,并在每个发现后附上证据 —— 因为真正的漏洞和扫描器的白日梦之间的区别,就在于它是否能向你展示其推导过程。
## 🔬 这个工具是如何构建的
**目的。** 这是你首先运行的工具:将其指向你拥有的 Web 应用,它会礼貌地爬取、检查和探测它,然后返回一份按优先级排序、有据可查的修复报告 —— 在终端中看起来很漂亮,在 pipeline 中是纯 JSON,天生安全。
**工作原理。** 它爬取同源链接(每次抓取各自容错),运行被动检查(安全标头、Cookie 标志、版本披露),并且 —— 在 `--passive` 模式之外 —— 运行温和的主动探测,将跨页面的发现折叠到各个类别中,并按严重性对它们进行排名。`--out` 会编写一份咨询级别的 Markdown 报告,同时保持 stdout 整洁。
**出色的 CLI。** 一个显示当前目标和流逝时间的实时进度旋转器,一个颜色编码的发现表格(红色代表 high/critical,黄色代表 medium,青色代表 low,灰色代表 info),带有按严重性划分的摘要面板和结论行,`--passive` 模式,`--timeout`/`--user-agent`/`--delay` 标志,以及保留的退出代码契约(0 干净,2 high/critical,1 错误)。
**关键改进。** *功能性:* 修复了一个爬虫崩溃问题,即一个无法访问或缓慢的链接会导致整个爬取过程中止;修复了 Flask `/scan` 在格式错误的 `max_pages` 上报 500 错误的问题(现在限制在 1..25);修复了 Windows 上在横幅/爬取文本上的 `UnicodeEncodeError`;添加了被动标头和 Cookie 标志检查以及 `--passive` 模式。*安全性:* 一个协议/主机白名单在任何套接字打开之前拒绝 `file://`, `ftp://`, `gopher://`, `javascript:` 和无主机的 URL;许可关卡(`--i-have-authorization`,位置由字面主机判断,而非 DNS);一个同源重定向范围守卫阻止开放重定向驱动的 SSRF/范围蔓延;3 MB 的流式单次响应正文限制;不受信任的爬取文本作为惰性 `rich.Text` 渲染;绝不打印 Cookie *值*,仅打印名称和标志状态。
**示例。**
```
PYTHONPATH=src python -m patch_peeker.cli scan --url http://127.0.0.1:8791 --delay 0
```
## ⚖️ 授权使用与安全章程
**这些是用于你拥有或获得明确授权进行评估的系统、文件、网络和人员的防御性工具。** 在运行任何内容之前阅读本文:
- **授权不是可选的。** 网络钓鱼模拟、网络扫描、IP 信誉查询和 Web 应用探测都会触及他人的系统或数据。请先获得书面授权。有几个工具在你声明之前*拒绝行动*(`--yes`,`--authorized-training`,`--i-have-authorization`,`--i-am-authorized` 等)。
- **默认安全。** 每个 Web UI 都绑定到 `127.0.0.1` (loopback)。出口流量、实时发送和主动扫描都受显式标志控制 —— 演练、被动或拒绝并警告始终是默认设置。
- **没有意外的自毁行为。** Flask `--debug`(Werkzeug 交互式调试器在任何回溯上都是远程代码执行)会受到强烈警告,并且在离开 loopback 时被完全拒绝。不受信任的输入有大小限制,经过验证,并被惰性渲染,因此恶意文件或日志行不会导致你的终端 —— 或接管。
- **输出中没有秘密。** API key 保留在请求标头中,密码来自环境,并且不会记录或打印任何敏感信息。
这些不是攻击性工具。它们不包含漏洞利用、不包含凭证收集器、也不包含 payload。如果某个工具*可能*被滥用,那么它天生就被设计为能够抵御这种滥用。
## 🧪 开发
```
python -m venv .venv && source .venv/bin/activate
pip install -e .
pytest -q # 38 tests, no network, no sleeps
patch-peeker --help
```
## 📄 许可证
在 **MIT License** 下发布。参见 [LICENSE](LICENSE)。
防御性工具。无漏洞利用,无 payload,无凭证收集器。
标签:BeautifulSoup, Python, Splunk, Web应用扫描, 主机安全, 安全合规检测, 文档结构分析, 无后门, 逆向工具