theHarvester 贡献指南:从开发环境搭建、数据源扩展到安全测试与合并的全流程实践
【免费下载链接】theHarvesterE-mails, subdomains and names Harvester - OSINT项目地址: https://gitcode.com/GitHub_Trending/th/theHarvester
导读:theHarvester 是一个面向渗透测试早期阶段的 OSINT(开源情报)收集工具,围绕"邮箱、子域名、主机名"等目标开展被动与主动侦察。本文以仓库根目录的 CONTRIBUTING.md 为骨架,完整讲解贡献者如何搭建开发环境、提交聚焦变更、为 Discovery 数据源编写契约与离线测试,以及如何通过安全测试门禁、Release validation 工作流与 Pull Request 规范将改动安全地合入上游dev分支。读完本文,你将掌握 theHarvester 的贡献全链路,并能在不触发外部网络的前提下,为项目新增或修改数据源适配器。
1. 开始之前:贡献流程的三条铁律
CONTRIBUTING.md 在动手写代码前先立下三条规矩,所有贡献都应遵守:
- 先搜索,再开工:动手前先检查上游仓库的 issues 与 pull requests,确认没有重复的进行中工作;任何较大的功能、依赖变更或用户可见行为变更,都应先开一个 issue 与维护者对齐方向,避免投入被否决。
- 一次 PR 只做一个逻辑变更:无关的清理、格式化、重构应拆分为独立 PR,便于评审与回滚。
- 基于上游
dev分支建分支:所有贡献分支都应从当前上游dev分支拉出,而不是从master或自己的旧分支拉出,确保合并时冲突最小。
关于 Bug 报告,文档要求附带最小可复现用例,并明确写出预期行为与实际行为、操作系统、Python 版本,以及仅足够诊断问题的输出;提交前必须移除凭据、账户信息、私有目标数据与原始 API 响应。这条"最小化暴露"原则贯穿整个贡献流程,后续的测试与 PR 章节会反复出现。
2. 搭建开发环境:Python 3.14 + uv 同步依赖
theHarvester 要求Python 3.14,并使用 uv 中可以看到requires-python = ">=3.14",[tool.uv]段还配置了python-preference = "managed"与exclude-newer = "7 days",因此仓库的.python-version文件可让 uv 自动选择正确的解释器版本。
标准流程是先 fork 仓库,再克隆自己的 fork 并添加上游 remote:
git clone https://github.com/YOUR-GITHUB-USERNAME/theHarvester.git cd theHarvester git remote add upstream https://github.com/laramies/theHarvester.git git fetch upstream git switch -c fix/short-description upstream/dev uv sync --all-groups分支名要简短且能描述变更内容,例如fix/certspotter-pagination或feature/source-name。uv sync --all-groups会同步所有依赖组——从 pyproject.toml 可以看到,除了运行期依赖(aiohttp、aiodns、fastapi、playwright、sqlalchemy、uvloop/winloop等),还有一个dev依赖组,包含pytest、pytest-asyncio、pytest-playwright、httpx、ruff、ty等测试与质量工具;--all-groups正是为了把这些一并装上。
3. 提交一个聚焦的变更:代码风格与职责边界
写代码时需遵循以下约定:
- 复用现有基础设施:优先复用仓库中已有的配置、传输(transport)、解析与结果归一化(result-normalization)辅助组件,而不是另起炉灶。
- 测试不是硬性要求,但有更好:Bug 修复与新增行为尤其欢迎测试;若要新增测试,从最近的现有测试开始改,保持风格一致。
- 命名与类型:使用描述性名称,并在能提升可读性的地方补充类型注解(仓库整体采用 PEP 604 风格,如
int | None)。 - 不随意加依赖:除非变更确实需要,否则不要新增依赖或配置项。
- AI 辅助代码的责任归属:文档明确强调"你对提交的每一行代码负责,包括 AI 辅助生成的代码"——提交前必须通读、理解并亲自测试。
从源码结构看,质量门禁在 pyproject.toml 中有明确配置:ruff启用E/F/N/I/B/UP/FA/FAST/RUF/PT/TC/FURB/ASYNC/T20等规则集,line-length = 130,字符串统一单引号;ty负责静态类型检查([tool.ty.src] include = ["theHarvester"])。
4. 新增或修改 Discovery 数据源:目录注册 + 契约 + 离线测试
这是贡献指南中最具 theHarvester 特色的部分。一个普通的数据源(provider)只需要一条SourceSpec目录条目 + 一条SOURCE_FACTORIES条目,严禁修改 CLI 编排、持久化或输出代码——因为"目录驱动选择/帮助/活动元数据,共享 runner 负责构造、采集与完成结果输出"。这条约束配合 How-to-add-a-new-module.md 可以拼出完整落地路径。
4.1 目录条目(SourceSpec)与工厂条目
在 theHarvester/lib/source_catalog.py 中,每个数据源由不可变的SourceSpec描述:
name:公开且稳定的源标识符,大小写不敏感查找(get_source_spec()内部做了 casefold)。routes:该源能产出哪些结果类型,对应ResultRoute枚举——SUBDOMAINS、EMAILS、IPS、ASNS、PEOPLE、URLS、BREACHES。activity:活动等级ActivityClass——P0(被动,如 routeviews、shodan)、P1(DNS,如 dns-brute、dns-lookup)、P2(直接交互,如 api-scan、screenshot、takeover、vhost)。这个分级直接决定了"能否被all选择"以及"是否允许 live 冒烟"(见第 6 节)。retains_unresolved_hostnames:是否保留当前无法解析的主机名(如hackertarget、pentesttools、rapiddns为True)。
工厂条目位于 theHarvester/lib/source_runner.py 的SOURCE_FACTORIES: dict[str, SourceFactory],由共享 runner 依据目录解析出的源名构造适配器(见该文件SOURCE_FACTORIESget_source_spec(request.source).name的调用链)。目录还支撑resolve_sources()对all与能力选择器(如subdomains、emails)的展开逻辑——这正是 CLI 里-b参数能写all、all_sources或按能力筛选的底层依据。
4.2 为数据源编写聚焦测试
新增或修改数据源时,文档建议补充聚焦的离线测试:mock HTTP、DNS 与 provider 响应,测试绝不能依赖 API Key 或外部网络访问。tests/discovery/test_baidusearch.py 是官方推荐的"小而美"范本,可直接复制改编。该文件用pytest的monkeypatch替换了 Playwright 浏览器、HTTP session 与asyncio.sleep,从而离线验证了:
- 分页与翻页终止(如重复页返回
SourceExecutionReport('partial', 'repeated-page')); - 验证码/安全验证中断(
'security-verification')、HTTP 429('rate-limited')、空响应('no-response')、传输错误('transport-error'); - 浏览器传输失败后回退到直接 HTTP 的降级路径;
- 取消(
CancelledError)在清理阶段失败时依然正确传播; - 结果归一化与去重(
get_hostnames()、get_emails()断言)。
文档列出的必测用例清单值得逐条对照:
- 缺失凭据与配置;
- 非成功响应、超时、畸形或空数据;
- 分页与重试终止条件;
- 归一化、去重后的结果。
4.3 provider_contract 标记与覆盖门
每个 canonical(权威)数据源必须有且仅有一个离线契约模块,标记为pytest.mark.provider_contract("source-name")。在 tests/discovery/test_baidusearch.py 末尾可以看到pytestmark = pytest.mark.provider_contract('baidu')的写法。
该标记由目录派生出的覆盖门(coverage gate)校验,实现在 tests/test_provider_contract_coverage.py 中:当目录条目没有契约、契约指向未知源、或两个模块声明同一个源时,测试直接失败(分别对应missing provider contracts、unknown provider contracts、duplicate provider contracts三种诊断信息)。测试收集逻辑在 tests/conftest.py 的pytest_collection_modifyitems中,从每个非 live 测试的 marker 聚合出契约源集合,与SOURCE_SPECS比对。契约标记必须加在"确定性离线契约模块"上,不要在测试里维护第二份源列表——目录是唯一事实来源。
4.4 共享传输与日志红线
文档要求:尽量使用共享传输(shared transport)表示请求;若数据源需要共享传输不支持的行为,把异常限制在本地并在 PR 中解释原因。会话的生命周期有严格约束(详见 How-to-add-a-new-module.md):相关请求复用同一个AsyncFetcher.open_session()以保持连接池、Cookie 与代理身份稳定;会话只服务于一个数据源和一个授权目标;取消时必须在关闭每个 session、response、task、connector 的同时正确传播。
贯穿始终的红线是:绝不在日志中输出凭据、账户信息、私有目标数据或原始 API 响应——这条约束与 Bug 报告、PR 内容的要求完全一致。
5. 安全测试:先窄后全,默认禁网
5.1 逐级验证命令
先跑最窄的受影响测试:
uv run pytest tests/path/to/test_file.py提交前跑完整质量门禁:
uv run ruff check . uv run ruff format --check . uv run pytest uv run ty check其中ruff check做静态检查、ruff format --check校验格式、ty check做类型检查,pytest跑全量测试。从 pyproject.toml 的 pytest 配置看,addopts = "--no-header --strict-markers -m 'not harvestview_e2e'"意味着默认排除真实浏览器的 HarvestView 端到端测试,并启用严格 marker 校验。
5.2 网络守卫:默认禁网,live 测试显式开闸
这是本仓库测试体系最值得强调的安全设计。在 tests/conftest.py 的pytest_sessionstart中,测试会话会自动打桩socket模块的getaddrinfo、gethostbyaddr、gethostbyname、getnameinfo、socket.socket.connect、connect_ex、sendto等 API:除非目标地址是回环地址(localhost、127.0.0.1、IPv6 回环等),否则任何外部 socket 流量都会直接触发AssertionError。也就是说,常规测试环境根本"出不了网"。
只有同时满足两个条件才能联网:
- 测试标记为
@pytest.mark.live_network; - pytest 以
--run-live-network -m live_network两个参数同时调用(conftest.py会强制校验-m live_network,否则直接pytest.UsageError)。
此外,live 标记的测试永远不能满足 provider-contract 覆盖门——契约覆盖必须靠离线测试保证,这是硬性设计。
5.3 真实目标的红线
文档明确禁止对第三方目标进行宽泛或主动侦察;若确需 live 验证,只能使用自己拥有或明确获授权的目标,限制请求范围,且采集数据不得进入 commit、issue 或 PR。手动派发的 provider 工作流对mozilla.org只做小规模被动 CLI 崩溃冒烟(crash smoke)——它能发现打包、凭据或数据源漂移,但不是一致性测试,不应为了凑结果而反复重跑。
CI 还会额外运行数据源冒烟测试、CodeQL、依赖审查与容器检查,贡献者无需在本地复现全部 live 数据源检查。
6. Release validation 工作流:发布前的最后闸门
贡献指南要求在发布前手动派发Release validation工作流(对应 .github/workflows/provider-smoke.yml),且必须针对确切的发布分支或 tag:
gh workflow run provider-smoke.yml --ref dev -f run_live=false该工作流在干净的 GitHub 托管 runner 上组合了四类检查:Python 检查(复用 theHarvester.yml)、真实浏览器的 HarvestView 端到端检查(复用 harvestview-e2e.yml)、容器检查(复用 harvestview-container.yml)以及打包冒烟(uv build后分别用 wheel 与 sdist 运行theHarvester --help、harvestview --help)。
工作流通过workflow_dispatch暴露布尔输入run_live(默认false)。只有获得明确授权的维护者可以设置run_live=true,此时会额外增加两条受限通道:
- live-provider-tests:先断言
certspotter、otx、thc三个源在目录中均为P0(被动)活动等级,再以--run-live-network -m live_network只跑这三个源的test_api类用例; - live-cli-smoke:以矩阵方式对
certspotter、crtsh、duckduckgo、hackertarget、otx、rapiddns、urlscan、yahoo八个 P0 源,用安装后的 wheel 执行theHarvester -d mozilla.org -b <source> -l 10 -q的小规模 CLI 冒烟。
两条 live 通道都设置了SMOKE_TEST_DOMAIN: mozilla.org,且从不启用 DNS 或直接目标交互——这就是文档所说"live lane never enables DNS or direct target interaction"的落地实现。
7. 打开 Pull Request:提交清单与评审礼仪
将主题分支推送到自己的 fork,并向上游laramies/theHarvester:dev发起 PR。PR 应包含:
- 问题描述与相关 issue;
- 变更前后的行为差异;
- 实际运行的测试与检查清单;
- 仅当有助于展示结果时的脱敏输出或截图;
- 评审者需要知晓的兼容性、数据源、限流或运维风险。
工作未完成或验证未通过时使用draft PR;保持分支与上游同步,及时响应评审意见,确保必选检查通过后再请求正式评审。
8. 安全敏感问题:走私有通道,不公开细节
若发现疑似漏洞,不要在任何公开 issue 或 PR 中透露漏洞细节、凭据或敏感采集数据,应遵循 SECURITY.md 的流程:
- 优先使用仓库Security标签页的私有报告表单;若无私有表单,则开一个不包含任何漏洞细节的最小 issue,请求维护者提供私有联系方式;
- 私有报告中应提供足够复现的信息,但仅使用自己拥有或明确授权测试的系统与账户;
- 该策略覆盖 theHarvester 的代码、依赖、打包与仓库自动化;第三方数据源自身的漏洞应向对应厂商报告;
- 数据源故障、限流、数据质量与普通功能 Bug不属于安全漏洞,走常规 issue 渠道(同样需要先移除凭据与敏感内容)。
9. 小结:贡献一份可合并改动的完整路径
回顾全文,theHarvester 的贡献流程可以浓缩为一条闭环:先搜索对齐 → 基于dev分支 + uv 同步环境 → 聚焦单一逻辑变更 → 数据源改动走"目录 + 工厂 + 离线契约测试"三件套 → 用默认禁网的测试体系验证 → 必要时在授权目标上做受限 live 冒烟 → 按清单提交 PR → 安全敏感问题走私有通道。这套体系通过 source_catalog.py 的目录驱动架构、conftest.py 的 socket 网络守卫与 provider-smoke.yml 的分级验证,把"可审查、可验证、不泄露"落到了工程实处——这也正是新贡献者快速融入项目、老贡献者持续维护数据源时最值得依赖的参考坐标。
【免费下载链接】theHarvesterE-mails, subdomains and names Harvester - OSINT项目地址: https://gitcode.com/GitHub_Trending/th/theHarvester
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考