SpyEyes

All-in-One OSINT Toolkit · 10 commands · 3164 platforms · Subdomain (7 dim) + Email (6 free sources) + Diff + Batch · 8 Editorial reports · zh+en bilingual · v1.6.12

View on GitHub

更新日志 / Changelog

本项目遵循 Keep a Changelog 规范,版本号遵循 语义化版本

This project adheres to Keep a Changelog and Semantic Versioning.


Unreleased

Planned


[1.8.2] — 2026-05-13

一键升级 — v1.8.0 做了启动版本检查 + stderr 通知,v1.8.2 补全升级闭环:用户选 Y 自动升级,不再需要手动复制命令。

三条升级通道

通道 触发 行为
菜单启动自动 prompt 进交互菜单 + 后台 24h 缓存检测到新版 + TTY 🆕 v1.8.2 available. Upgrade now? [Y/n]:。选 Y 自动跑 → exit 0 提示重启;选 N 跳过本次继续菜单。
菜单 [12] Check & Upgrade 用户在菜单点 [12] 强刷缓存 (绕 24h) → 显示版本对比 → 有新版 prompt Y/N → 升级
CLI spyeyes upgrade 命令行 --yes / -y 跳 prompt 直接升;--check 只查不升

安装方式自适应 + 完全清理

安装方式 升级行为 旧版本去向
pip install git+URL subprocesspip install --upgrade git+URL pip 内部 uninstall + 重装,site-packages/spyeyes/ 彻底清除旧 .py
pipx install ... subprocesspipx upgrade spyeyes 独立 venv ~/.local/pipx/venvs/spyeyes/ 内完全替换,不污染系统
git clone + pip install -e . 只显示 git pull && pip install -e . (git 冲突风险,不自动跑) git 用户自行处理

用户数据 ~/.spyeyes/ (config / history / cache) 保留不动,升级后用户的语言偏好/查询历史/API key 全在。

边界处理

跨平台

subprocess 命令用 list args (非 shell 字符串),sys.executable -m pip 而非 pip 字面调用 — Windows/Linux/macOS 都正确。pipx 路径识别同时支持正反斜杠(pipx/venvs + pipx\venvs)。CI 矩阵 9 个平台组合验证。

设计参考

验收


[1.8.1] — 2026-05-13

🐛 CI lint 修复:v1.8.0 引入的 3 处代码质量问题(ruff 阻断 CI 红牌),无功能变更。

修复

影响


[1.8.0] — 2026-05-11

智能默认报告目录 + 启动版本检查 + investigate 提速 3-4× + 全程进度反馈 — 三组影响每次启动 / 长任务体验的改进。

用户故事

问题 1:v1.6.4 把默认报告目录改成 <cwd>/Downloads/,本意是”所见即所得”。 但 pip install -e . + 软链到 /usr/local/bin/spyeyes 之后,用户在 / 或随便哪个目录敲 spyeyes, 报告就建在那里 —— 服务器根目录冒出 /Downloads/,用户找不到、心里也膈应。

问题 2:用户 git clone 后再没回去看仓库,我们改了代码 push 了 release,他们永远不知道。 唯一的”通知渠道”是用户主动去 GitHub Watch,绝大多数人不会做。

改进 1:智能默认目录路由

# 优先级:
# 1. SPYEYES_REPORTS_DIR=/path     ← 显式覆盖,始终最高
# 2. 源码运行(__file__ 不在 site-packages) → <项目根>/Downloads/
# 3. 打包安装(pip/pipx/brew/conda)        → ~/Downloads/spyeyes/
# 4. 兜底 <cwd>/Downloads/, 最后 cwd

判定方式:__file__ 路径里有没有 site-packages / dist-packages 标记。

改进 2:启动 GitHub Release 版本检查

$ spyeyes ip 8.8.8.8

🆕 SpyEyes v1.8.1 可用(当前 1.8.0)
   更新: 进入仓库目录,执行 `git pull && pip install -e .`
   更新内容: https://github.com/Akxan/SpyEyes/releases/tag/v1.8.1
   (关闭提示: SPYEYES_NO_UPDATE_CHECK=1 或 --no-update-check)

 ========== IP Info ==========
 ...

设计要点(参考 yt-dlp / gh CLI):

CLI

spyeyes ip 8.8.8.8                         # 默认开启版本检查
spyeyes ip 8.8.8.8 --no-update-check       # 单次禁用
SPYEYES_NO_UPDATE_CHECK=1 spyeyes ip 8.8.8.8   # env var 持久禁用(推荐 ~/.spyeyes/env)

内部

改进 3:investigate Phase 2b 并行化 + 全程进度反馈

背景:v1.7.0 的 investigate 在深度 1 时,Phase 2b(邮箱→用户名扫描)是串行循环。 15 个邮箱 × 单用户名扫描 ~14s = ~210s(3.5 分钟)用户黑屏等待,且没有任何进度提示。

优化:

# 新常量
INVESTIGATE_IP_PIVOT_WORKERS = 20           # Phase 2a: 10 → 20
INVESTIGATE_USER_PIVOT_OUTER_WORKERS = 4    # Phase 2b: 串行 → 4 并行
INVESTIGATE_USER_PIVOT_INNER_WORKERS = 50   # 每邮箱内部 worker 数(4×50=200, 持平单 user 150)
场景 提速
Phase 2b 15 邮箱串行 (单 user 150 worker) ~210s ~50-80s 3-4×
Phase 2a 10 个 IP 富化 ~5s ~3s 2s

单平台 burst 风险评估:200 总线程,但 4 个 outer 同时打同一平台(如 GitHub) 概率 ≈ 4 × (1/1700) = 0.2%,远低于单 user 模式 — 安全。

进度反馈(5 处新增,所有 i18n 双语):

 开始综合调查: akxan.com
 阶段 1: 4 个原子任务并发 (whois / mx / subdomain / domain-emails)…
   [0s]  ✓ mx                                          ← 实时 elapsed 戳
   [0s]  ✓ whois
   [45s] ✓ subdomain
   [51s] ✓ emails

 阶段 2a: 接力查询 N 个 IP (子域 → IP)…
   [1/N] ✓ 8.8.8.8 → US GOOGLE                         ← [N/M] 计数 + 摘要

 阶段 2b: 接力扫描 N 个邮箱本地部分 (邮箱 → 用户名, 4 并发)…
   [1/N] ✓ john.doe → 8 hits                          ← 同上

 总耗时: 56s                                            ← 收尾

TTY 安全:_stage_log 仅在 sys.stderr.isatty() 时输出 — CI / 管道场景完全静默, 不污染日志或 jq pipeline。

i18n 新增 5 个键(中英各):

测试:tests/test_investigate.py 新增 3 个测试:

仓库清理

测试

541 passed / 7 skipped(538 base + 3 new investigate 并行测试 + 7 已有可选依赖跳过)

兼容性


[1.7.0] — 2026-05-11

新增 investigate 子命令 — 综合调查(一次输入域名,出整合档案)

用户故事

之前每个维度要单独跑:whois example.commx example.comsubdomain example.comdomain-emails example.com → 翻出有价值邮箱后再 user john.doe,翻 IP 后再 ip 1.2.3.4。 6 条命令、6 份报告、自己脑补关系。

现在一条命令搞定:

spyeyes investigate example.com --save dossier.html

打开 dossier.html 一份报告里 6 个 section:WHOIS / MX / 子域名 / 子域→IP 富化 / 域名邮箱 / 邮箱→平台账号足迹。

架构(为什么不会死循环 / 不会信息爆炸)

物理上无环 — pivot 是单向 DAG,不是图:

domain (根)
  ├── whois        → 终止
  ├── mx           → 终止
  ├── subdomain    → 每个子域查 IP → 终止 (IP 不反查域名)
  └── domain-emails → 每个邮箱查 user → 终止 (user 不反查域名)

四道闸防爆炸:

邮箱挑人:noreply@ / info@ / admin@ 等 role-account 自动跳过,只对看似真人名 (john.doe@ / zhangsan@ 等)做 username 反查。

CLI

spyeyes investigate example.com                            # 默认:深度 1, 5 分钟预算
spyeyes investigate example.com --depth 0                  # 只跑 4 原子, 不接力
spyeyes investigate example.com --budget 60                # 1 分钟硬超时
spyeyes investigate example.com --max-pivot-emails 5       # 只对前 5 个真人邮箱反查
spyeyes investigate example.com --save report.html         # HTML 整合报告
spyeyes investigate example.com --save report.md           # Markdown 报告
spyeyes investigate example.com --save report.csv          # 4 列扁平 CSV (section/kind/key/value)

交互菜单同步加 [10] 综合调查 入口,问”接力深度?”(标准/仅原子)。语言切换从 [10] 让位到 [11]

内部

设计决策


[1.6.13] — 2026-05-09

🐛 修复 CSV 在 Excel/Numbers 打开中文乱码(用户反馈截图)

背景

用户截图反馈 CSV 报告在 Excel 打开:中文表头 主机名/标题 显示成 涓绘満銉/鏍囬,列宽溢出。

根因

经典 Excel 不认 UTF-8 CSV:

修复

CSV 文件头加 UTF-8 BOM(EF BB BF),Excel/Numbers 看到 BOM 立刻识别 UTF-8:

# 之前
encoding='utf-8'

# 现在 v1.6.13
encoding='utf-8-sig'   # 自动写 BOM 头

验证

$ xxd report.csv | head -1
00000000: efbb bfe5 ad97 e6ae b5 2c e580 bc0a ...
        ↑ UTF-8 BOM    ↑ "字段,值"(中文)

Excel macOS / Excel Windows / Numbers / WPS / LibreOffice / Google Sheets 全部识别。

Tests / Lint

无新测试(改一个 encoding 参数)。488 全绿。ruff 0 / mypy 0 / bandit 0。

Packaging


[1.6.12] — 2026-05-09

交互菜单 [9] 域名邮箱 加爬取深度选项(用户反馈)

背景

v1.6.11 把默认 max_pages 从 500 → 200 提速。但交互菜单里没有让用户选,要更深爬取必须 CLI --max-pages 500。用户反馈”交互菜单里也想能选”。

改动

[9] 域名邮箱 现在多问一题:

爬取深度?
   [ 1 ] 标准 200 页(默认,约 1 分钟)
   [ 2 ] 深度 500 页(约 3-4 分钟)
   [ 3 ] 极速 50 页(约 20 秒)
   请选择 [1/2/3,默认 1] :
选项 max_pages 适用场景
1 标准(默认) 200 大多数域
2 深度 500 学术研究 / 大型企业站 / 论坛
3 极速 50 快速验证 / 域名扫描

中英双语

prompt 字符串 prompt.demails_max_pages 中英都加了。

注意

CLI --max-pages N flag 仍然可用(无变化),交互菜单是补全 UX。

Tests / Lint

无变化(只加交互问题分支)。488 全绿。ruff 0 / mypy 0 / bandit 0。

Packaging


[1.6.11] — 2026-05-09

域名邮箱深度爬取修复”看着卡死”+ 实际提速 2×(用户反馈)

背景

用户截图显示 domain-emails 跑到 “阶段 3/4:深度爬取 2 个目标” 后完全静默,1+ 分钟感觉卡死。

实际不是卡死,是两个原因叠加:

  1. max_pages=500 太大 — 2 target × 250 页 × 500ms 速率 = 理论最低 4 分钟
  2. v1.6.6 多 target 并行时,内部 show_progress=False — 期间没有任何反馈

修复 1:降低默认 max_pages

DOMAIN_EMAIL_DEFAULT_MAX_PAGES:  500 → 200
DOMAIN_EMAIL_PER_TARGET_CAP:    新增 100(单 target 最多 100 页)

实践:典型企业域 contact / about / team 等高邮箱密度页 < 100 页。 长尾页面邮箱密度急剧降到几乎 0,纯花时间不出货。

效果:

修复 2:并行 target 也保留进度反馈

之前 v1.6.6 为防输出交织把内部 show_progress=False。改成:

效果:

之前(silent 1+ 分钟):
  阶段 3/4:深度爬取 2 个目标 ...
  (1+ 分钟无任何输出 ← 用户以为卡死)

现在:
  阶段 3/4:深度爬取 2 个目标 ...
     [api.example.com] pages=20/100 emails=3 queue=45
     [www.example.com] pages=20/100 emails=2 queue=23
     [api.example.com] pages=40/100 emails=5 queue=67
     [www.example.com] pages=40/100 emails=4 queue=12
     ...

用户可覆盖

如果你需要更深度的爬取(论文研究 / 大型站):

spyeyes domain-emails example.com --max-pages 500

Tests / Lint

无变化(只调常量)。488 全绿。ruff 0 / mypy 0 / bandit 0。

Packaging


[1.6.10] — 2026-05-09

🐛 PDF 西文带变音符字符渲染修复(用户反馈截图)

背景

PDF 报告里西班牙语 / 法语 / 德语等带变音符的拉丁字符渲染异常:

正确:  Comparador de Envíos | Logística Inteligente
之前:  Comparador de Enví os | Logí stic Inteligente
                  ↑ 多余空格        ↑ 字符被切

根因

_PDF_LATIN_RUN_RE 之前是 [\x20-\x7E]+,只匹配 ASCII。í ñ é ü ç 等 Latin-1 Supplement 字符(U+00A0-U+00FF)fall through 到 STSong-Light 中文字体 — 该字体不含这些字形,reportlab 字体回退失败 → 插入诡异空格。

修复

regex 扩展到完整 Western 字符集:

# v1.6.10:扩展到 Latin-1 Supplement + 常用标点
r'[\x20-\x7E\xa0-\xff‐-―‘-”…]+'

覆盖范围:

Helvetica 的 WinAnsi 编码完整支持上述区域。

实测

原文: Comparador de Envíos | Logística Inteligente
处理后: <font name="Helvetica">Comparador de Envíos | Logística Inteligente</font>
       ↑ 整体被一个 Helvetica 标签包住,不再切分

影响范围

修复任何含西文带变音符的 PDF 内容:

注意

Tests / Lint

无变化(只改正则)。488 全绿。ruff 0 / mypy 0 / bandit 0。

Packaging


[1.6.9] — 2026-05-09

🐛 修复 PDF 报告里 6 源状态行 emoji 乱码(用户反馈截图)

背景

v1.6.8 引入”完整 6 源状态”用了 emoji ✅⊘❌,在终端 / HTML 显示正常,但 PDF 用 STSong-Light 中文字体不支持 emoji 字符,reportlab 字体回退失败,渲染成随机汉字:

之前 PDF:  丅 certspotter: 21  丅 crtsh: 20  謁 hackertarget: 0  ...
                                              ↑ 乱码

修复

emoji 换成所有 CJK 字体都支持的 Unicode 文本符号:

之前 现在 含义
(U+2713 CHECK MARK) 源成功
(U+25CB WHITE CIRCLE) 源返空
(U+2717 BALLOT X) 源出错
现在 PDF:  ✓ certspotter: 21  ✓ crtsh: 20  ○ hackertarget: 0  ...
                                              ↑ 正常显示

跨终端 / HTML / PDF / Markdown / TXT 全部一致渲染。

注意

PDF 里其它已有的 ✓✗⚠ 字符(SMTP 验证 / wildcard 警告等)早就在用,这版只补了 v1.6.8 新加的 source breakdown。

Tests / Lint

无变化(只改字符,逻辑不动)。488 全绿。ruff 0 / mypy 0 / bandit 0。

Packaging


[1.6.8] — 2026-05-09

~/.spyeyes/env 自动加载 + 报告显示完整 6 源状态(用户连续反馈)

改动 1:~/.spyeyes/env 文件自动加载 API keys

之前用 macOS LaunchAgent (com.akxan.spyeyes.envvars.plist) 持久化 env vars,但:

改用 SpyEyes 项目内自管的 ~/.spyeyes/env(KEY=VALUE 格式),模块加载时自动读:

# ~/.spyeyes/env(权限 600,仅本用户)
SPYEYES_OTX_API_KEY=...
SPYEYES_CERTSPOTTER_API_KEY=...
PDCP_API_KEY=...

特性:

改动 2:报告显示完整 6 源状态(✅/⊘/❌)

用户多次反馈”为什么数据源数字一会 2 一会 3,看不到内部”。

之前所有报告(HTML/PDF/Markdown/TXT)只显示总数:

共发现 273 个 · 活跃 33 个 · 来自 2 个数据源     ← 哪 2 个?

现在多打一行完整 6 源状态:

共发现 273 个 · 活跃 33 个 · 来自 2 个数据源
数据源:✅ certspotter: 36  ⊘ crtsh: 0  ❌ hackertarget (错误)  ✅ otx: 23  ⊘ wayback: 0  ✅ subfinder: 45
符号 含义
✅ N 源成功返 N 个 hosts
⊘ 0 源跑 OK 但返空(API 限速 / 域无数据 / 无 key)
❌ (错误) 源抛异常(连接失败 / 超时)

9 轮循环测试结果(用户要求)

跨 3 个域 × 3 轮验证源稳定性:

跨 9 次活跃率 类型
certspotter 9/9 (100%) 🟢 骨干
subfinder 9/9 (100%) 🟢 骨干
otx 8/9 (89%) 🟢 骨干
crtsh 3/9 (33%) 🟡 机会
wayback 2/9 (22%) 🟡 机会
hackertarget 0/9 (0%) 🔴 quota 耗尽,需等

结论:3 个骨干源数据完全稳定,3 个机会源是赠品。看到 “X 个数据源” 在 3-6 间浮动 = 骨干 3 + 机会 0~3 是正常的。

应用范围

报告生成器都加了完整 6 源状态:

Tests

Code Quality

ruff 0 / mypy 0 / bandit 0

Packaging

升级 / 迁移指南

如果你之前用了 v1.6.5/6 的 LaunchAgent 方案:

# 1. 卸载 LaunchAgent
launchctl unload ~/Library/LaunchAgents/com.akxan.spyeyes.envvars.plist
rm ~/Library/LaunchAgents/com.akxan.spyeyes.envvars.plist

# 2. 创建 ~/.spyeyes/env(把 key 写进去,KEY=VALUE 格式)
mkdir -p ~/.spyeyes
nano ~/.spyeyes/env
chmod 600 ~/.spyeyes/env

升级后系统设置→登录项里那个”sh”会消失。


[1.6.7] — 2026-05-09

HTTP probe 抓全部状态码 title + CNAME 完整 chain(用户反馈 2 件)

改动 1:<title> 不再跳过 4xx/5xx

之前 if 200 <= status < 400 才提取,理由是”404 Not Found 是噪声”。 但用户反馈截图(example.org 全是 CF 403):title 列空一片,而真实情况是:

状态码 之前 现在 信息价值
403 (Cloudflare 挑战) Just a moment... 知道被 WAF 挡了
403 (其它 WAF) Attention Required! 同上
404 Page Not Found / 真站点 404 区分 nginx 默认 vs 真 404
401 Sign in / Login 看到登录页存在
500 错误信息 服务器实际情况

实测:ads.example.org 状态 403,title 现在显示 Just a moment...(Cloudflare 挑战页)。

注:JS / HTML body host 提取(v1.4.9)仍只对 2xx/3xx 做 — CF 错误页不会含真实业务 host 引用,避免噪声进 extracted_hosts。

改动 2:CNAME 完整 chain(最多 5 跳)

之前只抓第一级 CNAME。多级链路被吃:

www.x.com  CNAME  cdn1.x.com
cdn1.x.com CNAME  cdn-real.cloudflare.net
                  ↑ 看不到

现在递归跟到底,用 ` → ` 连接:

cname: cdn1.x.com → cdn-real.cloudflare.net

实测:

autodiscover.akxan.com  → adsredir.ionos.info  (单级,跟之前一样)
www.github.com          → github.com           (单级)
某些站                  → cdn1.x → cdn2.x → cdn-real.cf.net  (新支持)

防循环:最多 5 跳,seen set 自我检测。

Tests

替换之前 test_probe_skips_title_for_4xx(behavior 反了)。

481 全绿(+2 新,−1 旧调整)。ruff 0 / mypy 0 / bandit 0。

Packaging


[1.6.6] — 2026-05-09

域名邮箱挖掘提速 3-4× — HTTP probe 过滤 + 多 target 并行(用户反馈)

背景

用户截图显示 example.org 跑 domain-emails 时,33 个 alive 子域大部分是 pages=0 emails=0(像 pop.example.org / smtp.example.org / dns.example.org)。这些根本没 HTTP 服务,但代码挨个发请求等超时,5+ 分钟。

优化(三件)

1. enumerate_subdomains 调用改 probe=True

之前 probe=False 拿不到 HTTP 状态,33 个 DNS-alive 子域全爬。 现在 probe=True + 过滤 http_status is not None:

2. 多 target 并行爬虫(TARGET_PARALLEL_WORKERS = 3)

之前串行 for target in targets,每个等完才下一个。 现在 ThreadPoolExecutor(workers=3):

3. 进度反馈 — 内部 show_progress=False

并行场景下多个 target 的进度条会输出交织变乱。所以单 target 内部静默,主流程在每个 target 完成后打一行总结:

[3/10] api.example.org  pages=87 emails=12
[5/10] cdn.example.org  pages=23 emails=0
[7/10] www.example.org  pages=156 emails=8
...

颜色编码:有邮箱 = 绿,空 = 蓝(信息性,不算错误)。

实测

场景 v1.6.5 v1.6.6 提速
example.org 33 alive(其中 ~10 真 web) ~5.5 分钟 ~1.5 分钟 ~3.5×
小域(< 5 alive) ~30s ~20s ~1.5×
单域(--no-include-subdomains) 不变 不变

Tests

无新测试(行为是性能优化,既有测试覆盖核心逻辑)。479 全绿

Code Quality

ruff 0 / mypy 0 / bandit 0

Packaging


[1.6.5] — 2026-05-09

🐛 --alive-only 智能升级 — 在 wildcard DNS / 劫持环境下自动严格过滤

背景

用户截图显示 akxan.com 报告中 259 个”alive”子域,但实际只有 1 个真实站点。原因:用户机器开了 WARP / VPN / 公司代理拦截 DNS,所有 *.akxan.com 查询都被劫持到 198.18.1.x(TEST-NET-2),导致每个查询都”DNS 解析成功”→ alive=True。

之前 --alive-only 的过滤标准只看 alive(DNS 解析),无法识别这种 fake “活”。

修复

抽出 _filter_alive_only(data) 助手函数,自动根据 wildcard 检测结果切换过滤标准:

场景 过滤标准 mode 字段
wildcard_suspect=False(正常) alive=True alive_only
wildcard_suspect=True(劫持/wildcard) alive=True AND (HTTP 响应 OR 真实 CNAME) alive_only_strict

为什么严格模式用 HTTP 响应或 CNAME:

实测对比(用户的 akxan.com 截图场景)

  v1.6.4 v1.6.5
报告显示子域数 259(全是 fake) ~1-2(只剩真实 akxan.com)
用户体验 “为什么没过滤?” 报告整洁,wildcard 警告依旧显示

三处统一接入

CLI handler / 交互菜单 / _run_subdomain_batch 都从内联过滤代码切到调用 _filter_alive_only(data),DRY + 行为一致。

_filtered 元数据扩展

{
  "_filtered": {
    "mode": "alive_only_strict",   // 之前只有 'alive_only'
    "hidden": 257,
    "wildcard_suspect": true        // 新字段,报告生成器可显示警告
  }
}

Tests

Code Quality

ruff 0 / mypy 0 / bandit 0

Packaging


[1.6.4] — 2026-05-09

🐛 报告默认目录改回英文 Downloads/(用户反馈)

背景

v1.6.3 用了中文 下载/,用户反馈太”中文化”,在某些环境不友好:

修复

下载/Downloads/(单数大写,沿袭 macOS / Windows 标准命名)

cd ~/work
spyeyes subdomain example.com --save report.html
# v1.6.3:→ ~/work/下载/report.html  ← 中文
# v1.6.4:→ ~/work/Downloads/report.html  ← 英文(现在)

已经存的旧报告怎么办

留在 下载/ 文件夹里的旧报告不会被自动迁移。如果需要,手动 mv ./下载/* ./Downloads/ 即可。

Tests

5 个测试更新断言从 '下载' 改成 'Downloads',共 473 全绿

Packaging


[1.6.3] — 2026-05-09

报告默认保存目录跨平台统一 — <cwd>/下载/(用户反馈)

背景

用户反馈:”如果项目装在 Linux 服务器,默认存到 ~/Downloads,但服务器上没这个文件夹”。

之前(v1.2.0+)的优先级:~/Downloads → ~/Download → ~/spyeyes-reports → cwd,导致:

修复 — 统一行为

所有平台默认都用 <cwd>/下载/:你在哪跑命令,就在哪建文件夹,所见即所得。

新优先级:
1. SPYEYES_REPORTS_DIR (用户显式配置,如 /var/log/spyeyes)
2. <cwd>/下载/ (默认,自动创建)
3. <cwd> (兜底,极少见)

用户场景

# 普通使用 — 报告就在你跑命令的目录下的 下载/ 子文件夹
cd ~/work
spyeyes subdomain example.com --save report.html
# → ~/work/下载/report.html

# 服务器场景 — 用 env var 指定固定位置
export SPYEYES_REPORTS_DIR=/var/log/spyeyes
spyeyes ...
# → /var/log/spyeyes/...

# 也支持 systemd unit 里 Environment= 指定

移除的行为

Tests

Code Quality

ruff 0 / mypy 0 / bandit 0

Packaging


[1.6.2] — 2026-05-09

🐛 Housekeeping + 修复 CI 6 连失败 + 全文档同步

修复 CI(6 次连续失败的根本原因)

发现自 v1.4.9 起最近 6 次 GitHub Actions CI 全部失败。根因:

TestSubdomainProgressFeedback 类的 3 个测试只 mock 了 4 个旧源(crtsh / hackertarget / otx / certspotter),没 mock v1.4.8 加的 subfinder 和 v1.4.9 加的 wayback。CI 环境跑真实 wayback CDX API → 等 30s+ → 被 pytest-timeout 杀。

修复:用 for name in gt.SUBDOMAIN_SOURCES: setitem(... lambda d: set()) 把所有源默认 stub 成空,然后只 override 需要返值的源。本地 + CI 都立即过。

全文档同步到 v1.6.2

之前 docs 大量 v1.4.6 / v1.4.9 stale 引用:

GitHub repo 元数据同步

Tests

468 全绿(本地 + CI 都过 — CI 修复后首次绿)。

Packaging


[1.6.1] — 2026-05-09

🐛 进度条 100% 全功能审计 — 修复 3 处遗漏(用户反馈)

背景

用户连续两次提问”是否所有功能每一步都加了进度条”,要求 100% 仔细检查不许有遗漏。我对 12 个核心函数 + 6+6 个被动源逐一审计,发现 3 处真实遗漏:

修复 #1:domain-emails 阶段 2 子域名扫描静默

enumerate_subdomains(show_progress=False) 静默 1-2 分钟。改为透传 show_progress,子流程 4 阶段进度直接给用户看。

修复 #2:recursive_track_username profile 抓取阶段静默

递归扫描挖关联用户名时,会抓 8 个 profile 页面(每页 5s timeout,最坏 40s),期间完全静默。用户以为卡了。

加 3 层进度反馈:

修复 #3:domain-emails 多 target 爬虫缺 [N/M] 标记

include_subdomains=True 时若找到 5 个 alive 子域,会逐个爬,但用户看不到”现在在第几个 target”。加 [3/5] 爬取目标:api.example.com 标记。

全功能进度审计结果(完整清单)

✅ 已齐全(无修):

❌ 修复(本版):

新增 i18n key(中英双语)

Tests

468 全绿(改动仅在进度反馈,核心逻辑未动,既有测试覆盖)。

Packaging


[1.6.0] — 2026-05-09

域名邮箱挖掘:从 2 源 → 6 源 + 全并发,免费无注册

背景

调研对标 theHarvester / Photon / EmailHarvester / EmailFinder / h8mail / holehe / Hunter.io 后,SpyEyes domain-emails 缺的就是”被动 API 多样性”和”SERP dorking”。这版补齐完全免费 + 无需注册的 4 个新源,顺序执行 → 全并发,总耗时 ≈ 最慢源(2-3 倍提速)。

新增 4 个免费数据源

1. Bing SERP dorking(_emails_from_bing)

2. DuckDuckGo HTML SERP(_emails_from_ddg)

3. Wayback Machine 历史归档(_emails_from_wayback)

4. GitHub commit emails(_emails_from_github)

性能 — 顺序 → 并发(2-3× 提速)

# v1.5.0:顺序跑 2 源
crtsh (5s)  whois (3s) =  8s

# v1.6.0:并发跑 6 源
{crtsh, whois, bing, ddg, wayback, github} 同时启动
总耗时 = max(5s, 3s, 10s, 8s, 30s, 5s) = ~30s(瓶颈是 wayback)
比顺序累加(60s+) 2× 以上

实测 python.org:

新设计:DOMAIN_EMAIL_SOURCES dict

SUBDOMAIN_SOURCES 同一架构哲学:

DOMAIN_EMAIL_SOURCES = {
    'crtsh':   _emails_from_crtsh,
    'whois':   _emails_from_whois,
    'bing':    _emails_from_bing,    # v1.6.0
    'ddg':     _emails_from_ddg,     # v1.6.0
    'wayback': _emails_from_wayback, # v1.6.0
    'github':  _emails_from_github,  # v1.6.0
}

并发执行,任何一源失败 silent 跳过 + 落到 errors 字段。

Tests

Code Quality

ruff 0 / mypy 0 / bandit 0 / pytest 468 全绿

Packaging

与 GitHub 同类工具对比定位

工具 数据源数 免费 注册 报告格式
theHarvester 30+(含商业) 部分 多数需 1-2
EmailHarvester 7 SERP 1
EmailFinder 5 商业 API 1
h8mail 6 breach API 部分 1
SpyEyes v1.6.0 6(全免费) 8

定位:免费层最强、报告格式最丰富的中文 OSINT 邮箱枚举工具。


[1.5.0] — 2026-05-09

三大新功能 + 4 工具全清的”硬性收官”版本

新功能

1. 子域名 Diff 模式(spyeyes diff old.json new.json)

OSINT 持续监控刚需 — 对比两次扫描挖出新增 / 消失 / 状态变更的子域。

spyeyes subdomain example.com --json > monday.json
# ... 几天后 ...
spyeyes subdomain example.com --json > friday.json
spyeyes diff monday.json friday.json   # 差异报告
spyeyes diff monday.json friday.json --save diff_report.html  # 8 种格式可导

输出结构(_stats):

字段对比包括:alive / a / aaaa / cname / http_status / title,列表顺序无关(['1.1.1.1', '2.2.2.2']['2.2.2.2', '1.1.1.1'] 视为同一)。

2. 批量域名输入(spyeyes subdomain --batch domains.txt)

echo -e "example.com\nexample.org\n# 注释行\nakxan.com" > targets.txt
spyeyes subdomain --batch targets.txt --batch-save-dir reports/ --alive-only

每个域独立扫描 + 独立报告,自动跳过 # 注释行 / 空行。--batch-save-dir 创建目录(若不存在)+ 每个域写 subdomain_<domain>.html(扩展名取 --save 或默认 .html)。

--alive-only 在 batch 模式下也对每个域独立生效。Ctrl+C 中断时显示”已完成 N/M”,已跑的不丢。

3. PyPI 构建产物 + twine 验证全过

pyproject.toml 配齐 setuptools build,本地 python -m build 产 wheel + sdist:

spyeyes-1.5.0-py3-none-any.whl   198K
spyeyes-1.5.0.tar.gz             245K

二者均通过 twine check实际上传 PyPI 需要你的 API token:

twine upload dist/*

成功后任何人 pip install spyeyes 直接用。

代码质量(4 工具全清)

工具 状态 备注
ruff ✅ 0 issues 全项目 + 测试
mypy ✅ 0 errors 含新加的 diff/batch 函数完整类型注解
bandit ✅ 0 issues B110/B112(silent 降级)加项目级 skip + 注释;B603(subprocess)加 nosec + 完整理由
pytest ✅ 456 passed +13 新测试(TestSubdomainDiff × 8, TestSubdomainBatch × 5)

修复

性能(实测)

操作 时长
5000-vs-5000 host diff 12 ms
_extract_hosts_from_body 16KB body 1.18 ms/call
_clean_subdomain_candidates 10K hosts 4.6 ms
_generate_bruteforce_candidates (220 词) 0.14 ms

Packaging


[1.4.11] — 2026-05-09

HTTP probe 阶段进度条 + 提速 ~3×(用户反馈”baidu.com 卡死不动”)

背景

用户对大型域(baidu.com,1200+ 活跃子域)反馈”阶段 4/4 看不到进度,以为卡了”。实际是在跑,但阶段 4(HTTP probe)和 4b(JS extract)没有进度反馈,体感像死机。

Fixes

进度条 — 阶段 4 / 4b 都加上

之前只有阶段 3(DNS 解析)有进度条;阶段 4(HTTP probe)和 v1.4.9 加的 4b(JS extract 第二轮 DNS+probe)都是干等。现在:

提速 — 三处合一,实测 ~3× 加速

改动 之前 现在
HTTP probe worker 池 30(与 DNS 共享) 80(独立池,I/O bound 可高并发)
HTTP 总超时 5s 4s
HTTP connect 超时 与读超时共用 5s(死站要 5s 才放弃) 拆出 2s(死站 2s 即放弃)

死站快速失败的效果(以 1203 个 host、含 30% 死站为例):

v1.4.10:  1203 / 30 workers × 5s = ~200s 最坏
v1.4.11:  1203 / 80 workers × 4s = ~60s 最坏 (含 2s connect 早失败)
                                    实测约 80s(含 stage 4b 二轮)

实测(baidu.com,完整流程 + JS 提取)

v1.4.10 v1.4.11
总耗时 ~3 分钟 + 看着像卡死 ~2 分钟 + 实时进度条
阶段 4 是否显示进度
体感 “卡了?要不要 Ctrl+C?” “在跑,89/1290 已 probe”

配置

新增模块级常量(高级用户可改):

SUBDOMAIN_HTTP_WORKERS = 80   # v1.4.11:HTTP probe 独立 worker 池
SUBDOMAIN_HTTP_PROBE_TIMEOUT = 4.0  # v1.4.11: 5.0 → 4.0

CLI 用户仍可 --workers N --timeout S 覆盖(--workers 现在仅控 DNS,HTTP probe 自动用 max(N, 80))。

Tests

443 全绿(无新功能,仅性能/UX 改进,不需要新测试)。

Packaging


[1.4.10] — 2026-05-09

--alive-only 现在也过滤导出报告(用户反馈:bruteforce 后 dead 子域占满 HTML/PDF)

Fixes / UX

之前 --alive-only flag 只影响终端打印,导出的 HTML / PDF / JSON / CSV 报告仍含 dead 子域(那时设计是”完整数据更有价值”)。但 v1.4.9 加 bruteforce 后,小域名(如个人博客 akxan.com)的报告里 200+ dead 子域占满几屏,用户反馈”看着太卡而且占地方”。

改动:

CLI

# 现在三种使用全部过滤 dead:
spyeyes subdomain example.com --alive-only --save report.html  # 报告中只有 alive
spyeyes subdomain example.com --alive-only --save report.pdf
spyeyes subdomain example.com --alive-only --json | jq '.subdomains'

# 不传 --alive-only 时保持原行为(完整数据)

Tests

Packaging


[1.4.9] — 2026-05-09

子域名收集三大新维度 — Wayback Machine + DNS 字典爆破 + JS/HTML host 提取

Features

1. Wayback Machine 历史归档源(自动启用)

2. DNS 字典爆破(opt-in)

3. JS / HTML body host 提取(默认启用)

CLI 改动

新增 subdomain 子命令两个参数:

--bruteforce        Enable DNS dictionary bruteforce (~220 prefixes; SPYEYES_DNS_WORDLIST=path 覆盖)
--no-js-extract     Skip JS/HTML body extraction (default: enabled)

实测对比(anthropic.com)

配置 总 host 数 alive 时间
v1.4.8(5 源) 207 ~50 ~3s
v1.4.9 默认(6 源 + JS 提取) 207 + 内嵌引用 ~55 ~5s
v1.4.9 --bruteforce(6 源 + 字典 + JS) 207 + 220 字典 + 引用 ~60 ~12s

Tests

Packaging


[1.4.8] — 2026-05-09

可选集成 ProjectDiscovery subfinder(自动检测 + 30+ 数据源接力)

Features

安装方式(可选,推荐)

# macOS
brew install subfinder

# Linux
go install -v github.com/projectdiscovery/subfinder/v2/cmd/subfinder@latest

# 配置 ProjectDiscovery PDCP key 解锁更多源
export PDCP_API_KEY="your-pdcp-key"

实测对比

=== 5 源全测 anthropic.com ===
  crtsh         ->     0 hosts in   291ms (临时数据源问题)
  certspotter   ->    50 hosts in   830ms
  hackertarget  ->    50 hosts in   594ms
  otx           ->     0 hosts in   475ms (临时)
  subfinder     ->   207 hosts in  1751ms  ✨ 新增 — 4倍于其他源上限

Tests

Packaging


[1.4.7] — 2026-05-09

CertSpotter 支持 API key(对齐 OTX 设计 — 免费注册即可解锁高 quota)

Features

配置示例

# 在 ~/.zshrc / ~/.bashrc 中加:
export SPYEYES_OTX_API_KEY="..."           # AlienVault OTX 免费 key
export SPYEYES_CERTSPOTTER_API_KEY="..."   # SSLMate CertSpotter 免费 key
export SPYEYES_PHONE_API_KEY="numverify:..." # 可选实时 HLR 电话运营商

实测

example.org 4 源:certspotter(with key) 1.5s 返 36 hosts;OTX(with key) 26s 返 23 hosts。

Packaging


[1.4.6] — 2026-05-09

HTML 报告交互性 + 可读性升级(用户反馈)

Features

Tests

Packaging


[1.4.5] — 2026-05-09

🐛 HTML 报告大屏太窄 + 长主机名变阶梯式(用户反馈)

Bug Fix

用户截图显示 baidu.com 报告中 abot.pos.baidu.com(19 字符)被压成 3 行阶梯, 2K 大屏左右大量留白浪费。

修复:

测试

Packaging


[1.4.4] — 2026-05-09

🚨 紧急修复 + 报告美化补完 — Editorial Investigation Brief 全套

🎨 Visual — XMind 重做层级展开

用户反馈:之前 XMind 节点信息密度过高(host + 4 IP + status 一行),且 <title>: 看着像 HTML 残留 bug。重做 subdomain 分支:

收起时简洁,展开看详细 — 真正利用 XMind 思维导图层级特性。

🎨 Visual — Graph 改浅色

用户反馈不要深色背景。Graph 改回 Editorial 风浅色 theme:

🎨 Visual — PDF 内页美化

用户反馈”不能只加封面页就够了,第二页内容也要美化”。重做:

🎨 PDF 页脚字符间距 + 去封面冗余链接

(详细见同次发布的早期 commit 段)

🐛 Bug Fixes — 子域名查询(P0,功能性故障)

用户报告”子域名查询都是 0”,systematic-debugging 实测 4 个被动源真实状态:

现状 处理
ThreatCrowd 域名 www.threatcrowd.org无 DNS 记录(网站死了) 移除
AlienVault OTX 匿名访问被限速 429 Anonymous limited(API 政策变了) SPYEYES_OTX_API_KEY env var 支持
HackerTarget 匿名免费 quota 用完后返 API count exceeded 不可控(用户侧)
crt.sh .do / 其它特殊 TLD 需要 25-30s 才返回,SUBDOMAIN_SOURCE_TIMEOUT=15s 不够 timeout 加大到 45s

修复:

实测 example.org (.do TLD) 从 0 个 → 60+ 个子域

🐛 Bug Fixes — PDF(P1,视觉问题)

用户截图显示:

  1. 页脚 SPYEYES▲ OSINT 调查工具 字符间距过紧
    • 根因:之前用 font_name(STSong-Light 中文字体,Latin advance 偏窄)
    • 修复:页脚 canvas 改用 Helvetica,纯英文 brand SpyEyes · OSINT Toolkit(中英版统一)
    • bonus:页脚加 query 作为 running header 居中
  2. 封面页冗余项目链接 spyeyes ▲ github.com/Akxan/SpyEyes(用户嫌弃)
    • 修复:删除
  3. PDF 生成失败 'Canvas' object has no attribute 'setCharSpace'
    • 根因:setCharSpace 是 reportlab textObject 方法不是 Canvas 直接方法
    • 修复:删除该调用(Helvetica 间距已自然)

🧪 Tests

📦 Packaging


[1.4.4 早期 — 已合并到上方] — 2026-05-09

🐛 Bug Fixes — 子域名查询(P0,功能性故障)

用户报告”子域名查询都是 0”,systematic-debugging 实测 4 个被动源真实状态:

现状 处理
ThreatCrowd 域名 www.threatcrowd.org无 DNS 记录(网站死了) 移除
AlienVault OTX 匿名访问被限速 429 Anonymous limited(API 政策变了) SPYEYES_OTX_API_KEY env var 支持
HackerTarget 匿名免费 quota 用完后返 API count exceeded 不可控(用户侧)
crt.sh .do / 其它特殊 TLD 需要 25-30s 才返回,SUBDOMAIN_SOURCE_TIMEOUT=15s 不够 timeout 加大到 45s

修复:

实测 example.org (.do TLD) 从 0 个 → 60+ 个子域

🐛 Bug Fixes — PDF(P1,视觉问题)

用户截图显示:

  1. 页脚 SPYEYES▲ OSINT 调查工具 字符间距过紧
    • 根因:之前用 font_name(STSong-Light 中文字体,Latin advance 偏窄)
    • 修复:页脚 canvas 改用 Helvetica,纯英文 brand SpyEyes · OSINT Toolkit(中英版统一)
    • bonus:页脚加 query 作为 running header 居中
  2. 封面页冗余项目链接 spyeyes ▲ github.com/Akxan/SpyEyes(用户嫌弃)
    • 修复:删除
  3. PDF 生成失败 'Canvas' object has no attribute 'setCharSpace'
    • 根因:setCharSpace 是 reportlab textObject 方法不是 Canvas 直接方法
    • 修复:删除该调用(Helvetica 间距已自然)

🧪 Tests

📦 Packaging


[1.4.3] — 2026-05-09

🐛 两个用户报告问题修复 —— 子域名 cold start 返 0 + PDF 标题与 subtitle 重叠。

🐛 Bug Fixes

🧪 Tests

📦 Packaging


[1.4.2] — 2026-05-09

🎨 PDF + XMind 美化补完 —— v1.4.1 漏的两种格式同款 Editorial Investigation Brief 调性。

🎨 PDF 大改

🎨 XMind 大改

🌍 Bilingual

🧪 Tests

📦 Packaging


[1.4.1] — 2026-05-09

🎨 报告美化 — Editorial Investigation Brief 风格(调查档案/报刊调性)。 设计哲学:专业 + 沉稳 + 技术感,让客户/老板看到觉得”是真做调查”。

🎨 Visual Redesign

📐 Design Principles

🌍 Bilingual

🧪 Tests

📦 Packaging


[1.4.0] — 2026-05-09

📧 新增域名邮箱枚举(OSINT email harvest) —— 第 9 个核心 OSINT 能力。设计哲学”全 + 准”:多源被动 + 深度爬取 + 含 alive 子域,默认开;高调动作(SMTP 验证)opt-in。

✨ Features

🔒 Security

🧪 Tests

📦 Packaging

⚠️ 使用合规提醒


[1.3.3] — 2026-05-09

🎯 子域名枚举阶段反馈 —— 消除”输入域名后卡 5-15 秒不知在做什么”的困惑。

✨ Features / UX

🐛 Bug Fixes

🧪 Tests

📦 Packaging


[1.3.2] — 2026-05-09

🛠 质量打磨 + 电话运营商 MNP 修复 + UX 优化 —— 围绕”用户实际使用”的多面修复。

🐛 Bug Fixes

✨ Features

🎨 UX/打磨

🔒 Security

🧪 Tests

📦 Packaging


[1.3.0] — 2026-05-09

🌐 新增子域名枚举(被动多源 + DNS 验证 + HTTP probe) —— 第 8 个核心 OSINT 能力。

✨ Features 新功能

🔒 Security 安全

🧪 Tests 测试

📦 Packaging 打包


[1.2.0] — 2026-05-02

🎨 8 种报告格式 + 全报告 i18n + 菜单流程优化 —— 围绕”用户输出”的全方位升级。

✨ Features 新功能

🎨 UX 改进

🔒 Security 安全

🐛 Bug Fixes 修复(独立审计发现)

🧹 Cleanup 清理

🧪 Tests 测试

📦 Packaging 打包


[1.1.0] — 2026-05-02

🚀 Maigret 融合升级 —— 平台数 +57%,新增三大功能(用户名变形 / 递归扫描 / PDF 报告)。

✨ Features 新功能

🔧 Improvements 改进

🧪 Tests 测试

📦 Packaging 打包


1.0.0 — 2026-04-30

🎉 SpyEyes 首个稳定版本发布 —— 经过完整代码审计 + 多轮回归验证。

✨ Features 核心功能

🌍 i18n 国际化

🔒 Security 安全

⚡ Performance 性能

🛠 Reliability 可靠性

🛠 Developer Experience

🎨 UX