DreadpiratePickles/threat-tea
GitHub: DreadpiratePickles/threat-tea
将 AbuseIPDB、AlienVault OTX 和 VirusTotal 三个威胁情报源的 IP 信誉评分聚合为统一判定与评分的防御性分析工具。
Stars: 0 | Forks: 0
# 🍵 Threat Tea
### 将三个喧闹的威胁情报源浸泡为一杯平静的茶。
     
*三个情报源,三种判定,三个不同的答案。现在该怎么办?*
`threat-tea` 接收一个 IP 地址,以近似并行的方式向三个信誉提供商查询,
将答案提炼为单一的判定和评分,并缓存结果,让你永远不会为同一次查询重复付费。
它内置了 Rich 终端 UI、Flask 仪表盘、JSON API 以及用于 Grafana 的 Prometheus 指标。
这是一个**防御性**工具:它只*读取*关于你提供的指标(indicator)的公开信誉数据。
它绝不扫描、攻击或触碰目标主机。
## 1. 它的功能与存在原因
威胁情报提供商对同一个问题 —— *"这个 IP 是否恶意?"* —— 给出三种不同的方言:
| 提供商 | 它提供的信号 | threat-tea 如何评分 |
| --- | --- | --- |
| **AbuseIPDB** | 滥用置信度 % + 报告计数 | 直接使用置信度分数 (0–100) |
| **AlienVault OTX** | 引用该 IP 的 pulse 数量 | `min(100, pulses × 20)` |
| **VirusTotal** | 最近分析中的恶意/可疑计数 | `min(100, malicious×15 + suspicious×5)` |
丰富告警的分析师不应该被迫打开三个浏览器标签页,学习三种 JSON 结构,
并粗略估计三种评分标准。threat-tea 将这一切标准化为一个 `Finding`:
```
indicator · type · max_score (0–100) · verdict · [per-source rows with raw evidence]
```
**标题中的 `max_score` 是任何单一来源给出的最严重评价**(分析师需要最响亮的警报),
但每个来源行都被原封不动地保留下来 —— 包括原始的提供商 JSON —— 因此你总能审计*原因*。
UI 还会显示**佐证**(`hits` = 有多少来源独立标记了它),这能区分“一个神经质的情报源”和“所有人都认同”。
**判定阈值**(稳定 —— 有专门的测试固定了这些值):
| 分数 | 判定 | 颜色 |
| --- | --- | --- |
| `≥ 75` | `high` | 红色 |
| `40–74` | `medium` | 黄色 |
| `1–39` | `low` | 青色 |
| `0` | `informational` | 绿色 (干净) |
## 2. 工作原理(数据流)
```
check-ip 45.83.64.1
│
▼
┌─────────────────────────┐ validate.parse_ip()
│ 1. VALIDATE + NORMALIZE │ • must be a real IP (ipaddress) → blocks URL injection
│ │ • must be public (is_global) → won't leak internal hosts
└─────────────────────────┘ • canonicalized (dedupe key)
│
▼
┌─────────────────────────┐ aggregate.aggregate_ip()
│ 2. FAN OUT TO SOURCES │ AbuseIPDB / OTX / VirusTotal
│ │ • unconfigured key → "skipped", not fatal
│ │ • one source erroring → recorded, others continue
└─────────────────────────┘
│
▼
┌─────────────────────────┐ Finding(indicator, max_score, verdict, sources…)
│ 3. NORMALIZE → FINDING │ max_score = worst source; hits = # that flagged it
└─────────────────────────┘
│
▼
┌─────────────────────────┐ store.save_finding() (SQLite, upsert by indicator)
│ 4. CACHE │ findings(indicator PK, type, max_score, verdict,
└─────────────────────────┘ sources_json, observed_at)
│
▼
┌─────────────────────────┐ Rich panel/table (human) · pure JSON (--json) ·
│ 5. RENDER / SERVE │ Flask dashboard (/) · JSON API · /metrics (Grafana)
└─────────────────────────┘
```
网络分发对失败采取**宽容态度**:缺失的 API 密钥或提供商中断会变成一行带有简短原因的“已跳过”,
绝不会导致崩溃。如果完全没有配置密钥,`check-ip` 会执行零网络 I/O 并返回一个
`informational` 查询结果 —— 这正是整个测试套件能够离线运行的原因。
## 3. CLI 用法
全局标志(位于子命令之前):
| 标志 | 效果 |
| --- | --- |
| `--db PATH` | SQLite 缓存位置(环境变量 `THREAT_TEA_DB`,默认为 `threat_tea.sqlite3`) |
| `--plain` / `--no-color` | 禁用颜色/装饰(同时也遵循 `NO_COLOR` 环境变量) |
| `--version` | 打印版本 |
适用于所有场景的输出规则:
- `--json` → **向 stdout 输出纯 JSON**,始终如此,没有任何装饰。
- 通过管道 / `--plain` / `NO_COLOR` / 非 TTY → 输出纯净的文本(与旧脚本已经在解析的行相同)。
- 交互式终端 → 显示 Rich 横幅、面板、表格和加载动画。
### `check-ip` — 查询单个 IP
```
threat-tea check-ip 45.83.64.1 --yes
threat-tea check-ip 8.8.8.8 --yes --json # machine-readable
threat-tea check-ip 10.0.0.5 --yes --allow-private # opt in to private ranges
```
| 标志 | 含义 |
| --- | --- |
| `-y, --yes` | 确认你已获授权查询这些指标(或设置 `THREAT_TEA_ASSUME_YES=1`) |
| `--allow-private` | 允许私有/保留/回环 IP(默认阻止) |
| `--json` | 以纯 JSON 格式输出查询结果 |
示例交互式输出:
```
╭─ 🍵 threat-tea ─────────────────────────────╮
│ steep three noisy feeds into one calm cup │
│ authorized threat-intel lookups only │
╰─────────────────────────────────────────────╯
╭─ finding ───────────────────────────────────────────────────────╮
│ │
│ 45.83.64.1 high (92) 2 source(s) flagged it │
│ │
│ source score summary │
│ ─────────────────────────────────────────────────────────── │
│ abuseipdb 92 Abuse confidence 92; reports 431 │
│ otx 60 OTX pulse references 3 │
│ virustotal 0 VT malicious=0, suspicious=0 │
│ │
╰──────────────────────────────────────────────────────────────────╯
```
纯文本/管道输出保持原有的一行格式:
```
45.83.64.1 high score=92
```
### `collect` — 批量处理包含 IP 的文件
```
threat-tea collect --input iocs.txt --yes
threat-tea collect --input iocs.txt --yes --delay 1.5 # throttle for rate limits
threat-tea collect --input iocs.txt --yes --json > out.json
```
空行和 `#` 注释会被忽略,重复项会被去重(按首次出现的顺序),
无效或私有地址行会被跳过并报告到 stderr —— 处理过程会继续进行。
文件有大小限制(5 MB / 10 万行),因此失控的文件不会耗尽内存。
| 标志 | 含义 |
| --- | --- |
| `--input PATH` | 以换行符分隔的 IP 列表(必填) |
| `--delay SECONDS` | 查询之间的暂停时间,以遵守提供商的速率限制(默认 0) |
| `-y, --yes` / `--allow-private` / `--json` | 同上 |
### `findings` — 显示缓存内容
```
threat-tea findings # table in a terminal, JSON when piped
threat-tea findings --verdict high # filter
threat-tea findings --limit 20
threat-tea findings --json | jq '.[].indicator'
```
只要不是向交互式终端输出,`findings` 就会打印 **JSON**,
因此 `threat-tea findings > report.json` 可以像以前一样正常运行。
在真实的终端中,你会看到一个排序后的表格(最严重的判定排在最前),包含分数、佐证命中数、
来源数量和新鲜度。
### `serve` — 仪表盘、API、指标
```
threat-tea serve # binds 127.0.0.1:5064 by default
threat-tea serve --host 127.0.0.1 --port 8080
```
| 路由 | 用途 |
| --- | --- |
| `GET /` | HTML 仪表盘 |
| `GET /api/findings` | 以 JSON 格式输出缓存的查询结果 |
| `GET /metrics` | 输出用于 Prometheus 的 `threat_tea_risk_score`、`threat_tea_indicators_total` |
每个响应都带有强化安全性的标头(`X-Content-Type-Options: nosniff`、
`X-Frame-Options: DENY`、严格的 `Content-Security-Policy`、`Referrer-Policy`)。
## 4. 安全与授权说明
这是一个防御性、只读的工具,但它仍然会将你提供的每个指标发送给第三方。
threat-tea 将这一点明确化:
- **授权门控。** 网络(外发)命令(`check-ip`、`collect`)在你确认授权前拒绝运行 —— 请传递 `--yes`、设置 `THREAT_TEA_ASSUME_YES=1`,或在交互式终端中回答提示。如果在管道输入下未进行确认,它会拒绝执行(退出码 `2`),而不是默默将他人的 IP 外传。
- **无内部拓扑泄露。** 私有/保留/回环地址**默认被阻止** —— 提供商没有关于它们的数据,提交它们可能会泄露你的内部网络布局。如需覆盖,请使用 `--allow-private` 并保持谨慎。
- **注入安全。** 每个指标在拼接到提供商 URL 之前,都会使用标准库的 `ipaddress` 模块进行解析,因此 `1.2.3.4/../../x` 永远不会脱离当前进程。SQL 全程使用绑定参数。
- **输出中不含机密。** API 密钥存在于请求*标头*中,绝不出现在 URL 或日志中;来源错误会被截断为一小行。
- **安全的 `serve` 默认设置。** 绑定到 `127.0.0.1`;在任何非回环绑定时发出警告;并且**拒绝在非回环绑定上使用 `--debug`**,因为 Werkzeug 的交互式调试器存在远程代码执行风险。
- **有边界的输入。** `collect` 文件具有大小和行数限制。
只查询你已获授权调查的指标。
## 5. 设置、运行和测试
### 安装
```
cd threat-tea
python -m venv .venv && source .venv/bin/activate # optional
pip install -r requirements.txt
cp .env.example .env # then add whatever keys you have
```
配置你拥有的任意密钥(支持任意子集;缺失的会被跳过):
```
ABUSEIPDB_API_KEY=...
OTX_API_KEY=...
VIRUSTOTAL_API_KEY=...
```
### 不安装直接运行(通过 PYTHONPATH)
```
PYTHONPATH=src python -m threat_tea.cli check-ip 8.8.8.8 --yes
PYTHONPATH=src python -m threat_tea.cli findings
PYTHONPATH=src python -m threat_tea.cli serve
```
### 测试
快速、离线、无需网络且无 sleep 延迟:
```
PYTHONPATH=src python -m pytest -q
```
### Grafana 技术栈(可选)
```
docker compose up --build
```
Prometheus 抓取 `/metrics`;Grafana 配置地址为 `http://127.0.0.1:3000`
(`admin` / `threat-tea`)。
## 前置条件
Python 3.11+,以及 `flask`、`requests`、`prometheus-client` 和 `rich`
(均在 `requirements.txt` 中)。Grafana 技术栈需要用到 Docker。
## 参考
- [AbuseIPDB API v2](https://docs.abuseipdb.com/)
- [AlienVault OTX DirectConnect API](https://otx.alienvault.com/api)
- [VirusTotal API v3](https://docs.virustotal.com/reference/overview) ·
[IP 报告端点](https://docs.virustotal.com/reference/ip-info)
## 🏷️ 为什么叫 "Threat Tea"?
浸泡情报源,倒出一杯茶。AbuseIPDB、AlienVault OTX 和 VirusTotal 很少能完全达成一致,而且你碰巧先查到哪一个,都不应该由它来决定你的判定。三个来源的交叉佐证胜过盲目相信任何单一来源 —— 因此输出不仅告诉你一个 IP *有多糟糕*,还指出**有多少独立的来源实际支持这一结论**。
## 🔬 该工具是如何构建的
**目的。** 一个让人心平气和的 IP 信誉聚合器:给它一个指标,它就会将 AbuseIPDB、OTX 和 VirusTotal 浸泡提炼为一个带有颜色编码的判定结果,包含交叉佐证和原始证据 —— 默认安全,为脚本提供纯 JSON。
**工作原理。** 每个 IP 都会经过验证并进行规范化处理,然后对每个已配置的来源进行查询(密钥保留在请求标头中)。结果作为 Finding 缓存在 SQLite 中,其中包含最高分数、判定结果以及统计有多少来源独立标记了该 IP 的 **hits** 计数。`collect` 批量处理指标文件;`findings` 用于过滤和渲染存储内容。
**出色的 CLI。** `check-ip` 查询结果面板带有判定标签、佐证说明行以及各来源的分数/摘要表格;按严重程度排序的 `findings` 表格(最严重的排在最前),包含分数/hits/来源数量/新鲜度;`collect` 进度条和摘要面板;一致的严重程度递进(绿色干净 → 青色低声 → 黄色关注 → 红色立即行动)。
**关键改进。** *功能性:* 严格的 IP 验证,端到端支持 IPv6(正确的 OTX IPv4/IPv6 端点,对压缩与展开格式进行去重);交叉佐证 `hits` 信号;`collect` 进行去重,在继续运行的同时跳过并报告无效/私有行,限制文件大小(5 MB / 10 万行),并支持 `--delay`;参数化的 SQL 过滤器。*安全性:* 外发网络设有授权门控(`--yes` / `THREAT_TEA_ASSUME_YES=1` / 交互式;管道输入未确认则拒绝执行,退出码 2);默认阻止私有/保留/回环 IP(使用 `--allow-private` 覆盖),以免向第三方泄露内部拓扑;每个指标在进入提供商 URL 之前使用标准库 `ipaddress` 进行解析;API 密钥绝不出现在 URL 或日志中,错误摘要截断为 160 个字符。
**示例。**
```
PYTHONPATH=src python -m threat_tea.cli check-ip 45.83.64.1 --yes
```
## ⚖️ 授权使用与安全准则
**这些是防御性工具,适用于你拥有或获得明确授权评估的系统、文件、网络和人员。** 在运行任何内容之前,请阅读以下声明:
- **授权不是可选项。** 钓鱼演练、网络扫描、IP 信誉查询和 Web 应用探测都会触及其他人的系统或数据。请首先获得书面授权。一些工具在你声明授权前*拒绝执行*(`--yes`、`--authorized-training`、`--i-have-authorization`、`--i-am-authorized` 等)。
- **默认安全。** 每个 Web UI 都绑定到 `127.0.0.1`(回环地址)。外发网络请求、实时发送和主动扫描都由显式的标志控制 —— 演练、被动模式或拒绝并警告始终是默认设置。
- **无意外风险。** Flask 的 `--debug`(Werkzeug 交互式调试器在任何堆栈跟踪上都可以进行远程代码执行)会发出强烈的,并在回环地址之外完全拒绝运行。不受信任的输入有大小限制并经过验证,会被安全处理,因此恶意的文件或日志行不会导致你的终端崩溃 —— 或被接管。
- **输出中不含机密。** API 密钥保留在请求标头中,密码来自环境变量,任何敏感信息都不会被记录或打印。
这些不是攻击性工具。它们不包含任何漏洞利用、凭证收集器和 payload。如果某个工具*可能*被滥用,它的构造设计就是为了抵御这种滥用。
## 🧪 开发
```
python -m venv .venv && source .venv/bin/activate
pip install -e .
pytest -q # 24 tests, no network, no sleeps
threat-tea --help
```
## 📄 许可证
基于 **MIT License** 发布。详见 [LICENSE](LICENSE)。
防御性工具。无漏洞利用,无 payload,无凭证收集器。
标签:Flask, GitHub, IP信誉查询, Python, 信息聚合, 威胁情报, 安全运营, 开发者工具, 扫描框架, 无后门, 自定义请求头, 逆向工具