The Problem

Security teams need a single tool that can enumerate assets (sub‑domains, cloud buckets, code repos, etc.) and feed the results into downstream analysis pipelines. Existing scripts are fragmented, require manual stitching, and often miss passive data sources, forcing analysts to run many ad‑hoc commands.

What This Does

bbot is a Python‑based reconnaissance framework that drives dozens of open‑source modules (e.g., DNS brute‑force, GitHub code search, SSL cert scraping) and normalises their output. The core lives under bbot/ with the CLI entry point bbot/cli.py. Scans are defined by presets (YAML files in bbot/defaults.yml and examples/) that toggle module flags such as subdomain‑enum. Results are emitted via pluggable output modules (bbot/modules/output/), currently supporting CSV, JSON, Neo4j, Kafka, Slack, etc.

Key files:

  • bbot/core/scanner/scanner.py – orchestrates the scan lifecycle (_prep, async_start, event loop).
  • bbot/modules/base.py – base class for all 400+ modules; defines setup, setup_deps, handle_event.
  • bbot/core/helpers/misc.py – a utility hub (111 functions, 97 importers) used for URL parsing, domain checks, and file handling.

How It Is Wired

Execution starts at bbot/cli.py:main (line 420). The CLI builds a BBOTConfig (bbot/core/config/models.py), resolves the selected preset, and constructs a Scanner object (bbot/scanner/scanner.py).

  1. Initialisation – main → run (via bbot/core/helpers/command.py) → Scanner.__init__.
  2. Preparation – Scanner._prep loads modules, resolves dependencies, and registers event listeners. This path touches the hub module bbot/modules/base.py (97 callers) and the misc helpers (111 callers).
  3. Execution – Scanner.async_start launches the asynchronous event loop. Core work is performed by bbot/core/helpers/web/web.py (network calls) and bbot/core/helpers/dns/dns.py (DNS resolution).
  4. Event flow – Most internal activity funnels through bbot/core/event/base.py (187 functions, 46 classes). Handlers call emit_event (41 distinct callers) which in turn invokes make_event (56 callers) and verbose (85 callers).
  5. Output – Each module emits events that bbot/modules/output/* consume; e.g., output/neo4j.py writes to a Neo4j database via the neo4j driver.

The most connected modules (bbot/core/helpers/misc.py, bbot/modules/base.py, bbot/core/event/base.py) constitute the blast radius: changes here cascade to >140 importers. A small import cycle involving bbot/core/helpers/misc.py, bbot/core/modules.py, and bbot/core/__init__.py adds maintenance friction.

External effects:

  • Filesystem writes (81 functions) – config files, scan caches, output files.
  • Network I/O (5 functions) – HTTP requests, DNS queries, external API calls.
  • Database writes (5 functions) – Neo4j, PostgreSQL, Mongo, etc.
  • Subprocess execution (run_benchmarks → subprocess.run).

How To Use It

# Clone the repo (required for dev builds)
git clone https://github.com/moses-y/bbot
cd bbot

# Install dependencies in an isolated environment
uv venv .venv          # uv is declared in pyproject.toml
source .venv/bin/activate
uv pip install -e .    # editable install pulls all modules

# Run a sub‑domain enumeration preset against example.com
bbot -t example.com -p subdomain-enum

Configuration lives in bbot/defaults.yml (global defaults) and can be overridden with a custom YAML file passed via -c. API keys for modules such as censys or github are placed under the modules: section of that file. Output modules are enabled in the same preset (output_modules: list).

Real‑World Use

A red‑team can embed bbot in a CI pipeline: after a new target is added to the asset register, a GitHub Action runs bbot -t $TARGET -p full-scan, streams results to Neo4j (output/neo4j.py), and triggers Slack alerts (output/slack.py). The event‑driven architecture ensures each module runs only when its data is needed, reducing duplicate work.

Code Health & Issues

Static analysis findings (273 total): 69 high, 204 medium, 0 low across 8 categories. Notable patterns: deep nesting (24 files, max depth 10), oversized files (>1 500 LOC in modules/base.py), broad exception catches (11 files), import‑cycle members (4 files), duplicated 6‑line blocks (≈643 occurrences).

Code‑health audit (7 items):

  • HIGH – Pin GitHub Actions to commit SHAs (.github/workflows/*).
  • HIGH – Remove continue-on-error from correctness steps (tests.yml).
  • MED – Declare least‑privilege GITHUB_TOKEN permissions (distro_tests.yml).
  • MED – Pin Docker base image by digest (Dockerfile).
  • MED – Add a dependency‑vulnerability scan to PRs (.github/workflows/*).
  • MED – Run container as non‑root (Dockerfile).
  • LOW – Set timeout-minutes on workflow jobs (benchmark.yml).

Additional hygiene notes: no lockfile (dependencies declared only in pyproject.toml), a committed test TLS certificate (testsslcert.pem), and CI is present (GitHub Actions).

The Bottom Line

bbot provides a comprehensive, extensible recon engine with a well‑structured CLI and modular output pipeline. Its codebase is functional but suffers from large, highly coupled core files and several maintainability hotspots that increase change risk. Teams that need an out‑of‑the‑box scanner and are comfortable managing Python dependencies and Docker hardening will find it valuable; otherwise, consider refactoring the hub modules before extensive customization.