HiroAlleyCat/wdgwars-api-tester
GitHub: HiroAlleyCat/wdgwars-api-tester
wdgwars-api-tester是一款用于HTTP API表面系统探测的工具。
Stars: 0 | Forks: 0
# wdgwars-api-tester
**[WDGoWars](https://wdgwars.pl/)** HTTP API 表面的系统探测。
于 2026-05-29 在大规模 `/api/*` 404 崩溃期间构建。此工具的目的是
通过一条命令回答那天需要用 hourcurl 一个小时才能解决的问题:
- 哪些端点是活跃的,与返回的样式 404 页面相比?
- 未认证的 `/api/me` 返回 401(预期的行为)还是 404(路由未绑定)?
- `/api/stats` 是否暴露了 LiteSpeed 管理遥测泄露?
- 自上次快照以来有什么变化?
仅使用标准库的 Python 3。无需 `pip install`。单个文件。
## 家族
WDGoWars 饲料家族中的兄弟仓库:
- [Muninn](https://github.com/HiroAlleyCat/adsb-to-wdgwars) — ADS-B 饲料
- [Heimdall](https://github.com/HiroAlleyCat/meshcore-to-wdgwars) — MeshCore LoRa 饲料
- [wigle-to-wdgwars](https://github.com/HiroAlleyCat/wigle-to-wdgwars) — WiGLE Wi-Fi/BLE 饲料
- [gungnir](https://github.com/HiroAlleyCat/gungnir) — 共享 HMAC 传输库
## 快速开始
```
# 使用所有三种身份验证变体(无、垃圾、有效)探测 apex
python3 wdgwars_api_tester.py
# 添加 www. 和 api. 子域名
python3 wdgwars_api_tester.py --hosts all
# 探测自定义主机(预发布、分支、本地模拟)——任何以
# http:// 或 https:// 开头的都成为目标,而不是 wdgwars.pl。
python3 wdgwars_api_tester.py --hosts http://127.0.0.1:9999 --variants none
# 机器可读
python3 wdgwars_api_tester.py --json > snapshot.json
# 仅输出总体结论词 + 退出代码(适用于 shell / CI)
python3 wdgwars_api_tester.py --quiet --variants none,garbage
# → 打印 `HEALTHY` / `DEGRADED` / `OUTAGE` / `UNREACHABLE`
# 以及可选的 `+LEAK` 或 `+SENTINEL-DIVERGED` 后缀。
# 每 60 秒轮询一次,在状态变化时打印紧凑的增量。
# 在恢复时刻(首次进入 HEALTHY)打印完整表格。
python3 wdgwars_api_tester.py --watch 60
# 快照一次,然后与未来的运行进行比较
python3 wdgwars_api_tester.py --baseline baseline.json
# 在状态变化时监视 + Telegram 自页(无需桥接)
export TELEGRAM_BOT_TOKEN=123456:ABC...
export TELEGRAM_CHAT_ID=-1001234567890
python3 wdgwars_api_tester.py --watch 60 --alert-telegram
# 在状态变化时监视 + Discord / Slack / n8n / PagerDuty(任何 webhook URL)
python3 wdgwars_api_tester.py --watch 60 \
--alert-webhook https://discord.com/api/webhooks/.../...
# 在状态变化时监视 + 任意 shell 命令
python3 wdgwars_api_tester.py --watch 60 \
--exec-on-change 'echo "$WDGWARS_PREV_OVERALL → $WDGWARS_OVERALL" | mail -s "wdgwars alert" me@example.com'
```
## API 密钥
与 [wigle-to-wdgwars](https://github.com/HiroAlleyCat/wigle-to-wdgwars) 具有相同的优先级:
1. `--key` CLI 标志
2. `$WDGWARS_API_KEY`
3. `~/.config/wigle-to-wdgwars/wdgwars.key`
如果没有找到密钥,则自动删除 `valid` 变体,并且只运行 `none` 和 `garbage` 变体。
## 它探测的内容
| 探测 | 方法 | 路径 | 认证 | 备注 |
|---|---|---|---|---|
| `api-root` | GET | `/api/` | 无 | /api/ 子树的基线形状。 |
| `me` | GET | `/api/me` | 是 | 身份。未认证 → 401,不是 404。 |
| `upload-history` | GET | `/api/upload-history?limit=5` | 是 | 2026-04-27 添加。 |
| `upload-csv` | POST | `/api/upload-csv` | 是 | WiGLE-1.6 多部分,混合类型。 |
| `v2-upload-csv` | POST + GET | `/api/v2/upload-csv` → `/api/v2/upload-job/` | 是 | 异步管道:POST 202 → 轮询直到 `done`/`failed`(6 次轮询,每次 1 秒)。捕获独立于 v1 的 v2-parser 回退。 |
| `signed-upload` | GET | `/api/upload/` | 是 | HMAC JSON 端点。GET → 405 如果健康。 |
| `me-aps` | GET | `/api/me/aps?limit=1` | 是 | 调用者的自己的 AP 反读(支持 `?since=` 增量同步)。 |
| `aircraft` | GET | `/api/aircraft` | 是 | ADS-B 实时快照(顶层数组)。 |
| `meshcore` | GET | `/api/meshcore` | 是 | MeshCore 实时快照(顶层数组)。 |
| `territories` | GET | `/api/territories` | 是 | 全球帮派凸包(顶层数组)。 |
| `member-territories` | GET | `/api/member-territories` | 是 | 基于单元格的网格 + 帮派凸包。5 分钟快照。 |
| `leaderboard` | GET | `/api/leaderboard` | 是 | 5 个排行榜。5 分钟快照。 |
| `bounties` | GET | `/api/bounties` | 是 | 开放悬赏(最多 200 个)。从 2026-06-03 以后,由于与原始五个处理程序相同的正则表达式级联错误,返回 404;2026-06-04 修复。 |
| `team-messages` | GET | `/api/team/messages` | 是 | 调用者的帮派消息列表。 |
| `team-messages-id` | GET | `/api/team/messages/1` | 是 | 根据规范仅支持 DELETE — GET → 405 + `Allow: DELETE`(自 2026-06-04 以来)。健康状态是方法判断。 |
| `health-asked-for` | GET | `/api/health` | 无 | 尚不存在。在错误 #1 中请求。 |
| `stats-leak-check` | GET | `/api/stats` | 无 | 如果正文包含 LSWS 管理遥测指纹,则触发 LEAK。 (locosp 的 2026-05-30 修复已落地 — 端点现在 302 转到登录;v0.6.1 中的规则收紧以检测内容,而不仅仅是状态。) |
| `api-sentinel-404-a/b/c` | GET | `/api/` × 3 | 无 | /api/ 404 页面的多数指纹(需要 2-of-3 多数)。 |
| `non-api-sentinel-404` | GET | `/` | 无 | 打印非-/api/ 404 页面。 |
| `changelog-control` | GET | `/changelog` | 无 | 公共页面可达性控制。 |
## 判决
| 判决 | 含义 |
|---|---|
| `OK` | 2xx 响应,正文与任何 404 哨兵不同。 |
| `AUTH-REQUIRED` | 401。端点是活跃的,并且拒绝具有规范正确的 JSON 形状的密钥。 |
| `AUTH-REDIRECT` | `Location` 指向 `/login...` 的 3xx。认证网关正在工作,但端点是通过 Web 会话流程连接的,而不是返回 401 JSON — 对 API 调用者而言是路由形状回退,但不是安全/可用性问题。不会升级到 DEGRADED。 |
| `REDIRECT-{n}` | `Location` 不匹配 `/login` 的 3xx(通配符,因此意外的重定向不会伪装成 OK)。 |
| `DEAD` | 正文哈希与 /api/ 404 多数哨兵匹配。路由未绑定。 |
| `DEAD-NONAPI` | 正文与非-/api/ 404 哨兵匹配。 |
| `LEAK` | 正文包含 LiteSpeed 管理遥测指纹 (`lsphp_processes` / `top_domains` / `lsphp`)。在 v0.6.1 中通用化 — 在任何探测中都会触发,而不仅仅是 `stats-leak-check`。由于 locosp 的 2026-05-30 修复落地和 `/api/stats` 开始 302 转到 `/login`,从“stats 返回 200”的规则中收紧。 |
| `404` | 404 响应,但正文与哨兵不同。 |
| `METHOD` | 405。健康的端点,错误的动词。 |
| `ERROR` | 网络超时/URL 错误。 |
| `SENTINEL` | 3 个 /api/ 多数哨兵之一,与多数一致。 |
| `SENTINEL-OUTLIER` | 与其他 2 个哨兵不一致的 1 个哨兵(例如 CDN 缓存滑动)。通过 2 票多数检测 DEAD;通过 2 票多数检测 DEAD。 |
| `SENTINEL-DIVERGED` | 所有 3 个哨兵返回不同的正文。禁用受影响主机的 DEAD 检测;在信任结果之前调查诊断。 |
| `SENTINEL-NONAPI` | 非-/api/ 404 指纹探测。 |
总体总结是以下之一:
- `HEALTHY` — 没有DEAD,没有ERROR,没有LEAK。
- `UNREACHABLE` — 所有内容都出错。DNS,没有互联网,主机关闭。
- `DEGRADED` — 至少有一个探测DEAD。
- `OUTAGE` — 使用有效密钥的 `/api/me` 是DEAD。整个 API 表面都关闭。
- `…+LEAK` — 当 `/api/stats` 暴露时附加到上述任何一项。
- `…+SENTINEL-DIVERGED` — 当 3 个多数哨兵无法就指纹达成一致时附加。禁用受影响主机的 DEAD 检测;在信任结果之前调查诊断。
退出代码为 DEGRADED/OUTAGE/UNREACHABLE/LEAK/SENTINEL-DIVERGED 时为 `1`,为 HEALTHY 时为 `0`。
## 按计划运行
将其放入 cron、systemd 定时器或 Windows 任务计划程序。将 `--baseline` 与 `--json` 配对以记录每个快照以供以后趋势分析,或使用 `--watch` 在长时间运行的主机上获取 API 恢复时的单个状态更改通知。
Cron 示例:
```
*/5 * * * * cd /opt/wdgwars-api-tester && \
python3 wdgwars_api_tester.py --baseline /var/log/wdgwars/baseline.json \
--json >> /var/log/wdgwars/snapshots.jsonl
```
## 崩溃感知回退
当 LOCOSP 达到其文档化的每日上限(午夜 UTC 重置)、按 IP 速率限制或传输失败时,`--watch` 模式通过坏判决的份额(`429` 或 `ERROR`)检测到崩溃,并逐步延长轮询之间的睡眠时间,而不是以全速推进。在第一次干净的轮询上重置到正常节奏。
默认:当 ≥30% 的轮询是 `429`/`ERROR` 时触发。连续崩溃轮询的睡眠时间加倍(2×、4×、8×、16×、32× 的 `--watch`),限制在 `--outage-backoff-cap-seconds`(默认 3600 秒)**和**下一个午夜 UTC 的时间。
```
# 完全禁用功能
python3 wdgwars_api_tester.py --watch 1800 --outage-backoff-threshold 1.01
# 更激进:一旦 10% 的扫描结果不良就退避,最多休眠 4 小时
python3 wdgwars_api_tester.py --watch 1800 \
--outage-backoff-threshold 0.10 \
--outage-backoff-cap-seconds 14400
```
`DEAD`、`AUTH-REQUIRED`、`AUTH-REDIRECT` 和其他预期的非 OK 判决不计入崩溃份额 — 只有 `429` 和传输级别的 `ERROR` 才计入。
## 通知渠道
`--watch` 模式支持三个独立的通知路径。使用一个、两个或所有三个同时使用 — 它们不会冲突。
| 标志 | 使用时 |
|---|---|
| `--alert-telegram` | 您有一个 Telegram 机器人 + 聊天。最简单的设置。 |
| `--alert-webhook URL` | 您在 Discord、Slack、n8n、PagerDuty 或任何接受 JSON POST 的服务上。 |
| `--exec-on-change CMD` | 以上都不适用 — 电子邮件、短信、一个 Lambda、写入数据库、管道到记录器。 |
任何路径的失败都会在 stderr 中记录警告,但永远不会崩溃 watch 循环或阻止其他路径。
### Telegram 自分页
在 `--watch` 模式下,工具可以直接在每次状态更改时将帖子发布到 Telegram 聊天。无需外部代理、webhook 服务或警报基础设施 — 使用 stdlib `urllib` 到 Bot API 和聊天 id。
### 设置
1. 与 Telegram 上的 [@BotFather](https://t.me/BotFather) 联系并创建一个机器人。复制令牌。
2. 将机器人添加到您想要警报的聊天中(DM、群组或频道)。
3. 在该聊天中发送任何消息,然后 `GET https://api.telegram.org/bot/getUpdates` 并读取 `result[0].message.chat.id`(或 `result[0].channel_post.chat.id` 对于频道)。
4. 导出这两个值并传递 `--alert-telegram`:
```
export TELEGRAM_BOT_TOKEN=123456:ABC-DEF...
export TELEGRAM_CHAT_ID=-1001234567890
python3 wdgwars_api_tester.py --watch 60 --alert-telegram
```
或者直接传递:`--telegram-bot-token --telegram-chat-id `。
### 消息格式
| 转变 | 标题 |
|---|---|
| 恢复 (`* → HEALTHY`) | `✅ wdgwars API 恢复` |
| 诊断损坏 (`+SENTINEL-DIVERGED` 出现) | `🔧 wdgwars-api-tester 诊断损坏` |
| 回归(任何更糟的情况) | `🚨 wdgwars API ` |
正文包括 `prev_overall → curr_overall` 转变、按探测的增量(由于 Telegram 的 4096 个字符消息限制,最多 30 行)和判决计数汇总。使用 HTML 解析模式,因此 `` / `
` 可以正确渲染。
### 通用 webhook (`--alert-webhook URL`)
在状态更改时将 JSON 有效负载 POST 到任何 HTTP 端点。有效负载包含多个顶级键,因此相同的 URL 可以用于多个服务,而无需为每个服务设置标志。从 v0.10.0 开始,`text` + `content` 字段包含纯英语散文,因此 Discord/Slack 通道的阅读者可以在没有上下文的情况下理解它们:
```
🚨 API status changed: all endpoints healthy → some endpoints down
What changed since the last check:
• team-me/valid: was healthy (HTTP 200), now timing out (>15s) or unreachable
• team-id/valid: was healthy (HTTP 200), now timing out (>15s) or unreachable
Current snapshot:
• 13 endpoints healthy
• 27 correctly rejecting unauthorized callers
• 2 timed out or unreachable
• 2 endpoints missing (404 sentinel match)
• 3 background API 404 sentinel (probe of /api/)
→ Non-upstream probe regressed. Investigate.
```
完整的有效负载:
```
{
"text": "🚨 API status changed: ... (the human-readable string above)",
"content": "",
"text_machine": "🚨 wdgwars-api-tester: HEALTHY → DEGRADED\n\n\n\nverdicts: DEAD=2, ERROR=2, OK=13, ...",
"title": "🚨 API status changed: ...",
"kind": "regression",
"overall": "DEGRADED",
"overall_human": "some endpoints down",
"prev_overall": "HEALTHY",
"prev_overall_human": "all endpoints healthy",
"deltas": ["wdgwars.pl team-me/valid OK/200 -> ERROR/-", "..."],
"deltas_human": ["team-me/valid: was healthy (HTTP 200), now timing out ..."],
"by_verdict": {"OK": 13, "AUTH-REQUIRED": 27, "ERROR": 2, "DEAD": 2},
"by_verdict_human": ["13 endpoints healthy", "27 correctly rejecting ...", "..."],
"action": "Non-upstream probe regressed. Investigate.",
"tool": "wdgwars-api-tester",
"version": "0.10.0"
}
```
- **Discord** 读取 `content`。将任何通道 webhook URL 放入其中。
- **Slack 入站 webhook** 读取 `text`。相同的放入。
- **n8n / Zapier / Make** 可以直接选择结构化字段。
- **PagerDuty Events v2** — 使用 `--exec-on-change`(它期望不同的信封)。
- **自定义 HTTP 处理程序** — 从结构化字段中读取它们需要的任何内容。
- **解析旧语法的工具** — 读取 `text_machine`。格式与 v0.9.0 和更早版本相同。
### 早晨摘要 (`--digest URL`)
一次性模式,每天运行一次探测一次,并将每日摘要 POST 到 webhook。与在您想要摘要到达的本地小时(通常是 08:00)触发的 systemd 定时器配对,以便 Discord/Slack 通道每天一个可读的“昨晚发生了什么”帖子。与 `--watch` 互斥。
```
# 将每个 --watch 状态变化附加到状态日志中
python3 wdgwars_api_tester.py --watch 1800 \
--alert-webhook "$DISCORD_LOUD_WEBHOOK" \
--state-log ~/wdgwars-api-tester/lab/state-log.jsonl
# 一次性触发摘要(通常来自每日的本地 08:00 systemd 计时器)
python3 wdgwars_api_tester.py --digest "$DISCORD_LOUD_WEBHOOK" \
--state-log ~/wdgwars-api-tester/lab/state-log.jsonl
```
摘要读取的内容:
```
Morning report — 2026-06-04
API status right now: **all endpoints healthy**
50 probes ran.
• 14 endpoints healthy
• 27 correctly rejecting unauthorized callers
• 2 endpoints missing (404 sentinel match)
• 3 responding with 405 wrong-verb (endpoint healthy)
• 3 background API 404 sentinel (probe of /api/)
Last 24 hours: 3 state changes (2 loud, 1 suppressed as LOCOSP upstream flap)
• 1× HEALTHY → DEGRADED
• 1× DEGRADED → HEALTHY
Most-flapped probes:
• team-me/valid: 3 transitions
→ No action needed.
```
窗口可使用 `--digest-window-hours N`(默认 24)进行配置。
### 任意命令 (`--exec-on-change CMD`)
在状态更改时运行任何 shell 命令。以下环境变量会导出到子进程:
| 环境变量 | 值 |
|---|---|
| `WDGWARS_OVERALL` | 新的整体判决,例如 `DEGRADED+LEAK` |
| `WDGWARS_PREV_OVERALL` | 上一个整体判决 |
| `WDGWARS_KIND` | `recovery` / `regression` / `diagnostic-broken` |
| `WDGWARS_RECOVERY` | 如果正在过渡到 HEALTHY,则为 `1`,否则为 `0` |
| `WDGWARS_DELTAS` | 按探测增量行的新行连接 |
| `WDGWARS_VERDICTS` | JSON 编码的 `{verdict: count}` 字典 |
示例:
```
# 每次转换时都发送电子邮件
--exec-on-change 'echo "$WDGWARS_DELTAS" | mail -s "wdgwars: $WDGWARS_OVERALL" me@example.com'
# 仅在回归时发送警报(不是恢复,也不是诊断)
--exec-on-change '[ "$WDGWARS_KIND" = "regression" ] && /usr/local/bin/page-me.sh "$WDGWARS_OVERALL"'
# 转发到现有的内部警报服务
--exec-on-change 'curl -X POST -H "Authorization: Bearer $MY_TOKEN" \
-d "{\"summary\":\"$WDGWARS_OVERALL\",\"verdicts\":$WDGWARS_VERDICTS}" \
https://internal.example.com/alert'
```
命令以 `shell=True` 和 15 秒超时运行。非零退出代码记录警告,但不会崩溃 watch 循环。
## 为您自己的服务调整工具
单文件、MIT、仅使用标准库 — 鼓励分叉。结构旨在使这些更改变得容易:
- **探测不同的 API。** 编辑 `build_probes()` 以交换端点、方法和预期状态。`DEFAULT_HOSTS` / `ALL_HOSTS` 在顶部更改哪些主机被探测。
- **添加新的探测。** 将 `Probe(...)` 条目追加到 `build_probes()`。每个都自动获得相同的认证变体矩阵和判决注释。
- **添加新的判决。** 编辑 `annotate_verdicts()` 以添加分支,然后添加判决到 `VERDICT_PRIORITY` 以使表排序正常,并添加到 `summary()` 以使其在相关的情况下汇总到整体判决。
- **自定义哨兵机制。** `SENTINEL_PROBES` 和 `_canonical_sentinel()` 定义了多数逻辑。将 `SENTINEL_PROBES` 更改为使用更多哨兵,或重写 `_canonical_sentinel()` 以使用不同的协议规则。
- **不同的通知格式。** 直接编辑 `_format_telegram_text()` 或 `_format_webhook_payload()`。两者都是纯函数,易于单元测试。
如果您分发分叉,MIT 意味着克隆并重命名即可 — 无需归功于上游。
## 测试
两个套件,都是仅使用标准库。
### 单元测试(离线,快速)
```
python3 -m unittest test_wdgwars_api_tester
```
32 个测试,无网络。涵盖判决注释、多数哨兵逻辑、状态签名稳定性、汇总汇总、探测增量检测、Telegram 消息格式化和 webhook 有效负载形状。在不到一秒的时间内运行。
### 集成测试(默认为离线)
```
python3 integration_test.py # offline — fast, safe, default
python3 integration_test.py --live # also runs the live API check
INTEGRATION_LIVE=1 python3 integration_test.py # env var equivalent
```
21 个端到端场景。默认模式是 **离线** — `integration_test.py` 为每个场景启动本地实例 `mock_wdgwars.py`(每个场景一个,在随机端口上)并将测试器指向它们。实际的 `wdgwars.pl` 标签:404 错误, API 测试, DNS枚举, HTTP API, Python, stdlib-only, TCP/IP协议栈, URL发现, 内核驱动, 单文件程序, 安全检测, 差异比较, 开源框架, 性能监控, 情报分析, 持续集成, 数据快照, 无后门, 杀毒引擎, 状态监控, 系统分析, 系统探测, 系统维护, 网络分析, 网络协议, 网络安全工具, 网络安全平台, 网络安全软件, 网络性能, 网络故障, 网络服务, 网络架构, 网络流量, 网络测试工具, 网络测试平台, 网络测试软件, 网络漏洞, 网络监控工具, 网络监控平台, 网络监控软件, 网络管理工具, 网络管理平台, 网络管理软件, 网络设备, 网络诊断, 错误处理