# ffuf-automation — Complete Documentation

> **Professional ffuf Web Directory Discovery Automation Tool**
> 

---

## Table of Contents

1. [Legal Warning](#1-legal-warning)
2. [What This Tool Does](#2-what-this-tool-does)
3. [How It Works (Architecture)](#3-how-it-works-architecture)
4. [Requirements & Installation](#4-requirements--installation)
   - [Native Python Setup](#native-python-setup)
   - [Docker Setup](#docker-setup)
5. [Configuration Files](#5-configuration-files)
6. [CLI Reference — All Arguments](#6-cli-reference--all-arguments)
7. [Running Scans](#7-running-scans)
   - [Quick Start](#quick-start)
   - [Full Example](#full-example)
   - [Docker (run.sh)](#docker-runsh)
   - [Docker (manual)](#docker-manual)
   - [docker-compose](#docker-compose)
8. [Scan Modes](#8-scan-modes)
   - [Standard Scan](#standard-scan)
   - [Force Recursion Mode](#force-recursion-mode)
   - [Scheme Selection](#scheme-selection)
9. [Live Keyboard Controls](#9-live-keyboard-controls)
10. [Output Structure](#10-output-structure)
11. [Reports](#11-reports)
    - [HTML Report](#html-report)
    - [CSV Report](#csv-report)
    - [JSON Report](#json-report)
12. [Interesting Path Detection](#12-interesting-path-detection)
13. [Scope Enforcement](#13-scope-enforcement)
14. [Advanced Usage & Tips](#14-advanced-usage--tips)
15. [Troubleshooting](#15-troubleshooting)

---

## 1. Legal Warning

**This tool is for authorized security testing only.**

You must only scan domains you **own** or have **explicit written authorization** to test.
Unauthorized scanning is illegal under the CFAA (US), the Computer Misuse Act (UK), and
equivalent laws worldwide. The author bears zero liability for misuse.

Every scan begins with a live confirmation prompt listing the domains to be scanned.
You must type `yes` exactly to proceed.

---

## 2. What This Tool Does

`ffuf_automation.py` is a wrapper around [ffuf](https://github.com/ffuf/ffuf) — a popular
open-source web fuzzer — that adds:

- **Multi-domain batch scanning** from a plain text file
- **Pause / resume / graceful stop** with keyboard controls during a live scan
- **Force recursion mode** — digs into sub-paths even when ffuf returns 404, using
  Python-level orchestration rather than ffuf's built-in recursion
- **Structured reporting** in HTML, CSV, and JSON
- **Interesting path detection** — automatically flags `/admin`, `/.env`, `/api`, etc.
- **Scope enforcement** — an optional whitelist so domains outside scope are never scanned
- **Partial report generation** — if you cancel mid-scan, results collected so far are
  always saved and unscanned domains are marked as pending
- **Docker-first deployment** — a single `./run.sh` builds the image (Kali Linux + ffuf +
  SecLists pre-installed) and runs the scan

---

## 3. How It Works (Architecture)

```
                 ┌─────────────────────────────────────────────────────────┐
                 │                  ffuf_automation.py                     │
                 │                                                         │
  domains.txt ──►│  load_domains()          normalize URLs, deduplicate   │
  scope.txt   ──►│  load_scope()            optional domain whitelist      │
                 │  confirm_scan()          interactive yes/no gate        │
                 │                                                         │
                 │  ┌────────────────────────────────────────────────┐    │
                 │  │  Per-domain loop                               │    │
                 │  │                                                │    │
                 │  │  build_ffuf_command()  assemble ffuf flags     │    │
                 │  │  run_ffuf()            launch subprocess       │    │
                 │  │    └─ PauseController  PTY + keyboard thread   │    │
                 │  │  parse_ffuf_results()  read JSON output        │    │
                 │  │  find_interesting()    flag sensitive paths    │    │
                 │  └────────────────────────────────────────────────┘    │
                 │                                                         │
                 │  write_json_report()                                    │
                 │  write_csv_report()                                     │
                 │  write_html_report()    Jinja2 template                 │
                 └─────────────────────────────────────────────────────────┘
```

**PTY magic:** `run_ffuf()` opens a pseudo-terminal so ffuf thinks it has a real TTY.
This makes ffuf print its live progress line (`req/sec`, `Errors`). A background thread
reads that line, parses the stats, and displays them. A second background thread listens
for keypresses (`p`/`r`/`q`) without blocking the main scan.

**Force recursion:** When `--force-recursion` is set, `run_force_recursive_scan()` takes
over. It runs ffuf at each depth level independently, treating each discovered directory
as a new base URL for the next depth. This bypasses ffuf's own recursion limiter and
works even when paths return 404 (useful for blind enumeration).

---

## 4. Requirements & Installation

### Native Python Setup

**System requirements:**
- Python 3.9 or later
- `ffuf` v2 installed and in `$PATH`
- `jinja2` Python package (optional but strongly recommended — needed for full HTML report)

**Install ffuf:**

```bash
# Kali Linux / Debian / Ubuntu
sudo apt install ffuf

# macOS (Homebrew)
brew install ffuf

# Any platform via Go
go install github.com/ffuf/ffuf/v2@latest

# Verify
ffuf -V
```

**Clone and install Python dependencies:**

```bash
git clone https://github.com/your-repo/ffuf-automation.git
cd ffuf-automation
pip install -r requirements.txt
chmod +x ffuf_automation.py
```

`requirements.txt` contents:
| Package | Purpose |
|---|---|
| `jinja2>=3.1.0` | Full dark-themed HTML report rendering (strongly recommended) |

The tool runs without any pip dependencies — if Jinja2 is missing, a bare-bones HTML
fallback is generated instead.

---

### Docker Setup

Docker is the easiest way to run this tool. The image is based on Kali Linux and includes
`ffuf`, `python3`, `jinja2`, and SecLists wordlists pre-installed.

**Build the image (once):**

```bash
docker build -t ffuf-automation:latest .
```

**Rebuild from scratch:**

```bash
docker build -t ffuf-automation:latest . --no-cache
```

The [Dockerfile](Dockerfile) does the following:
1. Starts from `kalilinux/kali-rolling:latest`
2. `apt install ffuf python3 python3-jinja2 seclists`
3. Copies `ffuf_automation.py` and `requirements.txt` into `/app`
4. Creates `/app/results/` output subdirectories
5. Symlinks `/usr/share/seclists` → `/app/wordlists` for convenience
6. Sets entrypoint to `python3 ffuf_automation.py`

---

## 5. Configuration Files

All files live in the project root. Only `domains.txt` and a wordlist are required.

### `domains.txt` — Target Domains

One domain per line. Lines starting with `#` are comments.

```
# My authorized targets
example.com
https://staging.example.com
http://192.168.1.100
```

Rules:
- `https://` is prepended automatically if no scheme is present
- Trailing slashes are stripped
- Duplicate URLs (after normalization) are skipped
- Blank lines and comments are ignored

---

### `scope.txt` — Scope Whitelist (optional)

Used with `--scope scope.txt`. Only domains listed here will be scanned — any domain
in `domains.txt` that is NOT in `scope.txt` is silently skipped with a warning.

```
example.com
https://staging.example.com
```

Useful for bug bounty programs where you need a hard boundary around what is in-scope.

---

### `extensions.txt` — Extensions File (optional)

Used with `--extensions-file extensions.txt`. One extension per line.

```
.php
.txt
.bak
.json
```

Equivalent to passing `--extensions .php,.txt,.bak,.json` on the command line.

---

### `interesting.txt` — Custom Interesting Paths (optional)

Used with `--interesting-file interesting.txt`. Overrides the built-in list of paths that
get flagged as "interesting" in the report.

```
/admin
/api
/.env
/.git
/backup
```

See [Section 12](#12-interesting-path-detection) for the full built-in list.

---

### `force-paths.txt` — Force Recursion Paths (optional)

Used with `--force-recursion --force-recursion-paths force-paths.txt`. Lists the specific
sub-paths to recurse into at depth 1. Without this file, every word in the wordlist is
used as a sub-path candidate.

```
api
admin
v1
v2
```

---

### `.env` / `.env.example` — Docker-Compose Defaults

Copy `.env.example` to `.env` and edit to set defaults for `docker-compose`:

```env
RESULTS_DIR=./results
DOMAINS_FILE=./domains.txt
WORDLIST=/app/wordlists/Discovery/Web-Content/common.txt
```

---

## 6. CLI Reference — All Arguments

```
python3 ffuf_automation.py [OPTIONS]
```

| Argument | Type | Default | Description |
|---|---|---|---|
| `--domains` | path | **required** | Path to the domains file (one domain per line) |
| `--wordlist` | path | **required** | Path to the ffuf wordlist |
| `--output` | path | `results` | Directory where all output is written |
| `--depth` | int | `1` | ffuf recursion depth (`0` disables recursion) |
| `--force-recursion` | flag | off | Enable Python-level force recursion (see Section 8) |
| `--force-recursion-paths` | path | — | File of paths to force-recurse into at depth 1 |
| `--extensions` | string | — | Comma-separated extensions: `.php,.txt,.bak` |
| `--extensions-file` | path | — | File with extensions, one per line |
| `--match-codes` | string | `200,204,301,302,403,401` | HTTP codes to include in results |
| `--filter-codes` | string | `404` | HTTP codes to exclude from results |
| `--filter-words` | string | — | Filter responses by word count, e.g. `10,20` |
| `--filter-sizes` | string | — | Filter responses by byte size, e.g. `1234,5678` |
| `--filter-lines` | string | — | Filter responses by line count, e.g. `0` |
| `--threads` | int | `40` | Concurrent ffuf threads per domain |
| `--timeout` | int | `10` | Per-request timeout in seconds |
| `--rate` | int | `0` | Max requests per second (`0` = unlimited) |
| `--delay` | float | `0` | Seconds to wait between domains |
| `--max-results` | int | `0` | Max results to keep per domain (`0` = unlimited) |
| `--scope` | path | — | Scope whitelist file; domains outside it are skipped |
| `--interesting-file` | path | — | Custom interesting path patterns file |
| `--scheme` | choice | `https` | Protocol: `http`, `https`, or `both` |
| `--version` | flag | — | Print version and exit |

### Filter notes

All `--filter-*` and `--match-codes` arguments are passed directly to ffuf flags:
- `--match-codes` → `-mc`
- `--filter-codes` → `-fc`
- `--filter-words` → `-fw`
- `--filter-sizes` → `-fs`
- `--filter-lines` → `-fl`

To filter by multiple values, pass them comma-separated: `--filter-sizes 1234,5678`.

---

## 7. Running Scans

### Quick Start

```bash
python3 ffuf_automation.py \
  --domains domains.txt \
  --wordlist /usr/share/wordlists/dirb/common.txt
```

---

### Full Example

```bash
python3 ffuf_automation.py \
  --domains domains.txt \
  --wordlist /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt \
  --depth 2 \
  --extensions .php,.txt,.bak,.json,.asp,.aspx,.js \
  --match-codes 200,204,301,302,401,403 \
  --filter-codes 404 \
  --filter-words 10,20 \
  --filter-sizes 1234,5678 \
  --filter-lines 0 \
  --threads 50 \
  --timeout 10 \
  --rate 100 \
  --delay 2 \
  --max-results 500 \
  --scope scope.txt \
  --output results
```

---

### Docker (run.sh)

`run.sh` is the recommended way to run with Docker. It:
1. Checks Docker is installed
2. Builds the image if it doesn't exist yet
3. Auto-mounts `extensions.txt`, `interesting.txt`, and any file passed to
   `--force-recursion-paths`, `--extensions-file`, or `--interesting-file`
4. Runs the container with `domains.txt`, `wordlists/`, and `results/` mounted

```bash
# Make executable (once)
chmod +x run.sh

# Basic scan
./run.sh \
  --domains domains.txt \
  --wordlist /app/wordlists/Discovery/Web-Content/common.txt \
  --output results

# Force recursion with paths file
./run.sh \
  --domains domains.txt \
  --wordlist /app/wordlists/Discovery/Web-Content/common.txt \
  --force-recursion \
  --force-recursion-paths force-paths.txt \
  --output results
```

---

### Docker (manual)

```bash
# Basic
docker run --rm -it \
  -v $(pwd)/domains.txt:/app/domains.txt:ro \
  -v $(pwd)/results:/app/results \
  ffuf-automation:latest \
  --domains domains.txt \
  --wordlist /app/wordlists/Discovery/Web-Content/common.txt \
  --output results

# With scope + custom wordlist from host folder
docker run --rm -it \
  -v $(pwd)/domains.txt:/app/domains.txt:ro \
  -v $(pwd)/scope.txt:/app/scope.txt:ro \
  -v $(pwd)/results:/app/results \
  -v $(pwd)/wordlists:/app/custom-wordlists:ro \
  ffuf-automation:latest \
  --domains domains.txt \
  --scope scope.txt \
  --wordlist /app/custom-wordlists/my-list.txt \
  --output results

# Enter container shell (for debugging)
docker run --rm -it \
  -v $(pwd)/domains.txt:/app/domains.txt:ro \
  -v $(pwd)/results:/app/results \
  --entrypoint /bin/bash \
  ffuf-automation:latest
```

**Built-in wordlists inside the Docker image:**

| Path | Size | Best For |
|---|---|---|
| `/app/wordlists/Discovery/Web-Content/common.txt` | ~4k | Quick test |
| `/app/wordlists/Discovery/Web-Content/directory-list-2.3-small.txt` | ~87k | Standard scan |
| `/app/wordlists/Discovery/Web-Content/directory-list-2.3-medium.txt` | ~220k | Deep scan |
| `/app/wordlists/Discovery/Web-Content/raft-medium-directories.txt` | ~30k | Broad coverage |

---

### docker-compose

```bash
# Basic
docker compose run ffuf-auto \
  --domains domains.txt \
  --wordlist /app/wordlists/Discovery/Web-Content/common.txt \
  --output results

# Full options
docker compose run ffuf-auto \
  --domains domains.txt \
  --wordlist /app/wordlists/Discovery/Web-Content/directory-list-2.3-small.txt \
  --depth 2 \
  --extensions .php,.txt,.bak \
  --threads 50 --rate 100 --delay 2 \
  --output results
```

To mount additional files (scope, custom wordlists), edit `docker-compose.yml` and add
volume entries under the `volumes:` key.

---

## 8. Scan Modes

### Standard Scan

The default mode. ffuf's built-in recursion is used when `--depth > 0`.

The ffuf command generated internally looks like:

```bash
ffuf \
  -w /path/to/wordlist.txt:FUZZ \
  -u https://example.com/FUZZ \
  -recursion \
  -recursion-depth 2 \
  -e .php,.txt,.bak \
  -mc 200,204,301,302,401,403 \
  -fc 404 \
  -t 50 \
  -timeout 10 \
  -noninteractive \
  -of json \
  -o results/raw/example_com.json
```

Each domain gets its own raw JSON file in `results/raw/`. If a raw file already exists
(non-empty) for a domain, it is skipped (deduplication across runs).

---

### Force Recursion Mode

Activated with `--force-recursion`. This replaces ffuf's built-in recursion with
Python-level orchestration that recurses into paths **regardless of the HTTP response
code** — including 404. Useful when:

- The server returns 404 for valid directories
- You want to enumerate inside a path that only returned 403

**How depth works in force recursion:**

| Depth | What is scanned |
|---|---|
| 0 | `domain/FUZZ` — full wordlist at root only |
| 1 | `domain/FUZZ` then `domain/<forcepath>/FUZZ` for each path in `--force-recursion-paths` |
| 2+ | Same as depth 1, plus `domain/<path>/<word>/FUZZ` at each deeper level |

**With `--force-recursion-paths`:**
```bash
python3 ffuf_automation.py \
  --domains domains.txt \
  --wordlist common.txt \
  --force-recursion \
  --force-recursion-paths force-paths.txt \
  --depth 2 \
  --output results
```
At depth 1, only the paths listed in `force-paths.txt` are used as sub-path candidates
(e.g. `api`, `admin`). At depth 2+, the full wordlist is used.

**Without `--force-recursion-paths`:**
Every word in the wordlist is a sub-path candidate at every depth. This can generate
very large numbers of sub-scans — use carefully with large wordlists.

---

### Scheme Selection

`--scheme` controls the protocol prepended to domains that have no scheme in `domains.txt`:

| Value | Effect |
|---|---|
| `https` (default) | All domains use `https://` |
| `http` | All domains use `http://` |
| `both` | Each domain generates two targets: `http://` and `https://` |

Example — scan both protocols:

```bash
python3 ffuf_automation.py \
  --domains domains.txt \
  --wordlist wordlist.txt \
  --scheme both \
  --output results
```

---

## 9. Live Keyboard Controls

While a scan is running, keyboard controls are active in the terminal:

| Key | Action |
|---|---|
| `p` | **Pause** — sends `SIGSTOP` to the ffuf process |
| `r` | **Resume** — sends `SIGCONT` to continue a paused scan |
| `q` or `Ctrl+C` | **Stop & save** — interrupts ffuf gracefully, waits up to 15 seconds for it to write its JSON output, then generates a partial report |

A live stats line shows `req/sec` and `Errors` while scanning:

```
  ↳  req/sec: 245     │  errors: 0
```

When paused, a panel is shown:

```
  ┌──────────────────────────────────────┐
  │  ⏸  SCAN PAUSED                       │
  │  [r] resume  │  [q] stop & save report│
  └──────────────────────────────────────┘
```

**Partial report behavior:** Cancelling mid-scan (via `q` or `Ctrl+C`) always produces a
complete report. Domains that completed have their results included normally. The domain
that was being scanned when you cancelled includes whatever ffuf had written. Domains that
had not started yet are marked **NOT SCANNED** in the HTML report.

---

## 10. Output Structure

```
results/
├── raw/
│   ├── example_com.json               ← raw ffuf JSON output per domain
│   ├── staging_example_com.json
│   └── 104_248_248_151_api_admin.json ← sub-path files in force-recursion mode
├── reports/
│   ├── report_20250522_143000.html    ← full styled HTML report
│   ├── report_20250522_143000.csv     ← flat CSV export
│   └── report_20250522_143000.json    ← machine-readable JSON report
└── logs/
    └── scan_20250522_143000.log       ← full DEBUG log for this run
```

**Raw files** are named using the domain URL converted to a filesystem-safe string
(special characters replaced with `_`). In force recursion mode, each sub-path scan
gets its own raw file.

**Report filenames** use the timestamp of when reports were written. If the scan was
cancelled, `_partial` is appended: `report_20250522_143000_partial.html`.

**Logs** are written at DEBUG level (more verbose than the console INFO output).

---

## 11. Reports

### HTML Report

A dark-themed, fully self-contained HTML file viewable in any browser.

```bash
open results/reports/report_*.html      # macOS
xdg-open results/reports/report_*.html # Linux
```

**Sections:**

| Section | Contents |
|---|---|
| Scan Summary | Stats cards: domains scanned, paths found, interesting findings, failed domains, recursion depth, threads |
| Scan Configuration | Wordlist, extensions, match/filter codes, threads, timeout, depth |
| Domains Index | Table of contents with per-domain path count and interesting count badges |
| Per-Domain Results | Table of all discovered URLs with status code, size, words, lines, redirect; switchable to **Tree View** |
| Interesting Findings | Sub-table per domain for flagged paths with the matching pattern shown |
| Failed Domains | Domains where ffuf exited with an error |

**Status code colors:**

| Code | Color |
|---|---|
| 200 OK | Green |
| 301 / 302 Redirect | Blue |
| 401 Unauthorized | Yellow |
| 403 Forbidden | Orange |
| 500 Server Error | Red |
| Other | Gray |

**Tree View vs Table View:** Each domain section has a toggle button to switch between
a flat table and an interactive tree view that mirrors the directory hierarchy of found
paths.

> **Note:** The HTML report requires Jinja2. Without it, a plain fallback is generated.
> Install with: `pip install jinja2`

---

### CSV Report

A flat file importable into Excel, Google Sheets, or any data tool.

**Columns:**

| Column | Description |
|---|---|
| `domain` | The target domain |
| `url` | Full URL discovered |
| `path` | URL path only (e.g. `/api/v1`) |
| `status` | HTTP status code |
| `length` | Response size in bytes |
| `words` | Word count in response |
| `lines` | Line count in response |
| `redirect` | Location header value (if redirect) |
| `interesting` | `YES` if path matched an interesting pattern, else empty |

```bash
cat results/reports/report_*.csv
```

---

### JSON Report

Full machine-readable structured data. Useful for piping into other tools or scripts.

**Top-level structure:**

```json
{
  "scan_date": "2025-05-22 14:30:00",
  "tool": "ffuf-automation v1.0.0",
  "scan_config": {
    "wordlist": "/path/to/wordlist.txt",
    "extensions": ".php,.txt",
    "match_codes": "200,204,301,302,403,401",
    "filter_codes": "404",
    "threads": 40,
    "timeout": 10,
    "depth": 2,
    "scheme": "https"
  },
  "domains": [
    {
      "domain": "https://example.com",
      "failed": false,
      "results": [
        {
          "url": "https://example.com/admin",
          "path": "/admin",
          "status": 403,
          "length": 1234,
          "words": 45,
          "lines": 12,
          "redirect": "",
          "input": "admin"
        }
      ],
      "interesting": [...],
      "command": "ffuf -w ...",
      "path_tree": {...}
    }
  ],
  "failed_domains": [],
  "total_paths": 42,
  "total_interesting": 5
}
```

Pretty-print a raw ffuf output file:

```bash
cat results/raw/example_com.json | python3 -m json.tool
```

---

## 12. Interesting Path Detection

After each domain scan, `find_interesting()` checks every discovered path against a list
of known-sensitive patterns. Matching paths are highlighted in the HTML report, tagged
`YES` in the CSV, and collected under `interesting` in the JSON.

**Built-in patterns** (checked as substrings of the path, case-insensitive):

```
/api        /admin      /login      /dashboard  /backup
/config     /core       /uploads    /dev        /test
/.env       /.git       /wp-admin   /phpmyadmin /swagger
/graphql    /internal   /debug      /secret
```

**Custom patterns** — supply your own list with `--interesting-file`:

```bash
python3 ffuf_automation.py \
  --domains domains.txt \
  --wordlist wordlist.txt \
  --interesting-file interesting.txt \
  --output results
```

See [interesting.txt](interesting.txt) for a comprehensive example covering auth,
API endpoints, sensitive files, dev/debug paths, CMS paths, and common leak paths.

---

## 13. Scope Enforcement

Passing `--scope scope.txt` enables a whitelist mode. The tool reads `scope.txt` (same
format as `domains.txt`) and skips any domain from `--domains` that is not in the scope
file. Skipped domains are logged as warnings and never scanned.

```bash
python3 ffuf_automation.py \
  --domains domains.txt \
  --wordlist wordlist.txt \
  --scope scope.txt \
  --output results
```

If all domains are out of scope, the tool exits immediately with an error.

This is useful during bug bounty testing where your `domains.txt` might contain a large
list but only some are in-scope for the current program.

---

## 14. Advanced Usage & Tips

### Recommended starting settings

```bash
python3 ffuf_automation.py \
  --domains domains.txt \
  --wordlist /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt \
  --depth 1 \
  --threads 40 \
  --timeout 10 \
  --match-codes 200,204,301,302,401,403 \
  --filter-codes 404 \
  --output results
```

Start with `depth 1` and `threads 40`. Increase only after confirming the target can
handle the load.

---

### Filtering noise

If the server returns the same-sized error page for every path:

```bash
# Find the size of the 404 page first, then filter it
--filter-sizes 4321

# Or filter by word count if all 404s have the same word count
--filter-words 12
```

---

### Rate-limiting a sensitive target

```bash
--threads 10 --rate 20 --delay 5
```

- `--rate 20` caps ffuf at 20 requests/second
- `--delay 5` adds a 5-second pause between scanning each domain

---

### Scanning both HTTP and HTTPS

```bash
--scheme both
```

Generates two scan targets per domain entry in `domains.txt`.

---

### Force recursion into a specific API path

When you know `/api` exists but want to enumerate sub-paths regardless of status codes:

```bash
# force-paths.txt:
# api

python3 ffuf_automation.py \
  --domains domains.txt \
  --wordlist wordlist.txt \
  --force-recursion \
  --force-recursion-paths force-paths.txt \
  --depth 2 \
  --output results
```

This scans:
- `domain/FUZZ` (root, depth 0)
- `domain/api/FUZZ` (forced path, depth 1)
- `domain/api/<word>/FUZZ` for each word in the wordlist (depth 2)

---

### Re-running without re-scanning completed domains

Raw JSON files act as a cache. If `results/raw/example_com.json` exists and is non-empty,
that domain is skipped on the next run. To force a re-scan, delete the raw file:

```bash
rm results/raw/example_com.json
```

Or wipe all raw files to restart everything:

```bash
rm results/raw/*.json
```

---

### Viewing results quickly

```bash
# Open the HTML report (macOS)
open results/reports/report_*.html

# List all raw files
ls results/raw/

# Search CSV for interesting findings
grep YES results/reports/report_*.csv

# Pretty-print raw ffuf JSON
python3 -m json.tool results/raw/example_com.json | less
```

---

## 15. Troubleshooting

### `[ERROR] ffuf is not installed or not in PATH`

```bash
which ffuf    # should return a path
ffuf -V       # should print a version
```

If missing, install: `sudo apt install ffuf` or `go install github.com/ffuf/ffuf/v2@latest`

---

### HTML report has no styling / shows "Install Jinja2"

```bash
pip install jinja2
```

---

### Scan completes but `results/raw/` files are empty

ffuf may have exited before writing output. Check the log:

```bash
cat results/logs/scan_*.log | grep -E "FAIL|ERROR|WARN"
```

Common causes:
- The domain is unreachable (network/firewall)
- ffuf timed out (`--timeout` too low for a slow server)
- The wordlist path is wrong

---

### All results are filtered out (0 paths found)

Your `--filter-sizes` or `--filter-words` may be accidentally matching valid responses.
Start without any filter flags and add them back one at a time.

Also check `--match-codes` — if the server is returning `200` but you only match `403`,
results will be empty.

---

### Confirmation prompt never appears / hangs

If running non-interactively (CI, cron), the `confirm_scan()` prompt blocks. You can
bypass it by piping `yes`:

```bash
echo "yes" | python3 ffuf_automation.py --domains domains.txt --wordlist wl.txt
```

---

### Docker: `permission denied` on results directory

```bash
chmod 777 results/
```

Or run the container as the host user:

```bash
docker run --rm -it -u $(id -u):$(id -g) ...
```

---

### Docker: container exits immediately

Check that `domains.txt` is mounted and non-empty:

```bash
docker run --rm \
  -v $(pwd)/domains.txt:/app/domains.txt:ro \
  ffuf-automation:latest --help
```

---


