╔═══╗───────╔═══╗─────╔╗
║╔═╗║───────║╔══╝─────║║
║║─║╠══╦═╦══╣╚══╦╦═╗╔═╝╠══╦═╗
║╚═╝║╔╗║╔╣╔╗║╔══╬╣╔╗╣╔╗║║═╣╔╝
║╔═╗║╚╝║║║╔╗║║──║║║║║╚╝║║═╣║
╚╝─╚╩═╗╠╝╚╝╚╩╝──╚╩╝╚╩══╩══╩╝
────╔═╝║
────╚══╝
A tool for graphing web requests. AgraFinder ingests HTTP traffic (uploaded JSON captures or live from Burp Suite), stores it in Neo4j, and renders the relationships between URLs, requests, responses, and authentication flows as an interactive graph in the browser.
- Request graphing — visualize how URLs reference and pull in one another
- Search — full-text search across URLs, headers, and request/response bodies
- Auth analysis — capture and graph an authentication flow with a guided browser session
- Tech fingerprinting — detect technologies and versions from captured responses
- Burp integration — poll a Burp Suite extension API for live traffic
- Stored & custom queries — run saved searches or raw Cypher against the graph
- Docker and Docker Compose
- An X server on the host (Linux) for the auth-analysis browser window
1. Create a .env file in the project root:
NEO4J_PASSWORD=ChangeMe123!
SECRET_KEY=change-this-secretNEO4J_PASSWORD must be 8 or more characters — Neo4j 5 refuses to start
with anything shorter. Optional keys: BURP_API_KEY, BURP_API_PORT,
PROXY_PORT, SEARCH_RESULT_LIMIT, DISPLAY.
2. Allow the container to open a browser on your screen (GNU/Linux host):
xhost +local:docker3. Build and run:
docker compose up --buildThe first build takes a few minutes (it installs Firefox and Python deps).
docker compose upOnce the containers are healthy:
- AgraFinder — http://localhost
- Neo4j Browser — http://localhost:7474 (user
neo4j, yourNEO4J_PASSWORD)
To get data in, either upload a JSON capture from the UI or enable the Burp API toggle to pull live traffic. Then search, click nodes to drill into request/response detail, and use the graph controls to filter resources.
The auth-analysis feature drives a real Firefox inside the container to capture an authentication flow. Two display modes are supported.
Default — virtual display (works everywhere, no host setup): Firefox runs on an in-container Xvfb display. The flow is captured normally, but the browser window is not visible — fine for simple or scripted logins, not for ones where you need to type credentials by hand. Nothing to configure; this is the default.
Interactive — host display (window appears on your screen):
Set these in your .env:
USE_HOST_DISPLAY=1
DISPLAY=:0Set DISPLAY to your host's actual value (echo $DISPLAY — often :0 or
:1). Then, on the host, before docker compose up:
xhost +SI:localuser:rootThe container runs as root, so +SI:localuser:root is required —
xhost +local:docker alone may not authorize it.
- Firefox's sandbox is disabled in the container on purpose: Docker's default seccomp profile blocks the user-namespace syscall the sandbox needs, and it is redundant inside an already-isolated container.
shm_size: 2gbis set indocker-compose.yml— Firefox crashes on startup with Docker's 64MB shared-memory default.- If Firefox fails to launch, check
uploads/geckodriver.loganddocker compose logs appfor the specific reason.
macOS has no native X server, so the default Xvfb mode is the only one that works out of the box — and it is the recommended path on Mac.
If you want the interactive window on macOS, install an X server and route display over TCP (X11 sockets are not shared the way they are on Linux):
brew install --cask xquartz
# open XQuartz, then Preferences -> Security -> enable "Allow connections
# from network clients", then log out and back in
xhost + 127.0.0.1In .env set USE_HOST_DISPLAY=1 and DISPLAY=host.docker.internal:0.
This is finicky; if it does not cooperate, stick with the default Xvfb mode.
The most robust cross-platform option for a visible, clickable browser is to
stream the container's display into the web UI (noVNC) — not yet wired up,
but the intended long-term approach.
Two containers, defined in docker-compose.yml:
| Service | Image / build | Purpose |
|---|---|---|
neo4j |
neo4j:5.13-community |
Graph database (APOC enabled) |
app |
local Dockerfile |
Flask app served by gunicorn |
The app runs under gunicorn with a single gevent worker. A single worker is intentional: AgraFinder keeps per-process state (the Burp polling thread, Selenium browser handles, the auth-flow capture, the SSE queue), and that state must stay consistent across requests.
The Python code is organized into packages: api/ (routes, Burp polling),
database/ (Neo4j and SQLite layers), auth/ (browser capture), searches/
(metadata, tech fingerprints), graphenen/ (graph builders), utils/, and
the browser front-end under all_the_web_stuff/.
The UI accepts raw Cypher for advanced graph exploration.
- Data persists in Docker volumes (
neo4j-data,neo4j-logs). To start fresh:docker compose down -v. - Uploaded files are written to
./uploadson the host. - The Burp API and any host-side proxy are reached via
host.docker.internal, so they work from inside the container.