BountySkiller
BountySkiller is a Flask app with two bug-bounty-focused tools: a collector that pulls disclosed HackerOne hacktivity and bug-bounty writeups (plus RSS/Google sources) into local JSON, and "Hunt Buddy," a browser-driven orchestrator that takes a target profile, runs a gap analysis against a set of pluggable bug-class hunting modules (sourced from an external HUNT_TOOLS directory, e.g. SSRF), and produces ranked findings with per-module status and enforced scope.domains restrictions. It exposes a small REST API (/api/hunt/plan, /api/hunt/run, /api/fetch, etc.) for scripting the collection and hunt workflow.
What it is
BountySkiller is a self-hosted Flask app (python app.py, serves on http://127.0.0.1:5000) with two independent tools: a writeup/hacktivity collector (/) and the Hunt Buddy orchestrator (/hunt). The summary states the concept; what follows is the operational detail an engineer needs to actually run it and to trust its output.
How it works
Hunt Buddy plans before it acts. A plan table lists every bug-class module, whether it can run, and the exact profile key missing if it cannot. A skipped module is never reported as clean — absence of a finding is not evidence of safety. The engines live outside this repo, at the directory pointed to by HUNT_TOOLS (defaults to ~/Bounty/claude-bug-bounty/tools).
Scope is enforced by the engines, not the UI: scope.domains is required, and an out-of-scope request is blocked before the socket opens. The /hunt page refuses to start without a scope.
What to do
Run with the tools directory set explicitly:
HUNT_TOOLS=~/Bounty/claude-bug-bounty/tools python3 app.py
open http://127.0.0.1:5000/huntCollector API:
POST /api/hunt/plan— profile JSON in body, returns gap analysis, sends no traffic (safe dry inspection)POST /api/hunt/run— body{"profile": {...}, "only": ["ssrf"], "dry_run": false}GET /api/hunt/status,GET /api/hunt/report
Google Custom Search is optional and free-tier limited to 100 queries/day:
export GOOGLE_API_KEY=...
export GOOGLE_CSE_ID=...(or pass google_api_key / google_cse_id as query params).
Caveats
The page states several failure modes that will silently corrupt results if unknown:
- The HackerOne index field is
team_handle, notteam.handle— the latter silently returns 0 hits. - RSS sources only expose recent posts, so
months=24returns the same rows asmonths=1for them. bountyis oftennull(HackerOne only exposes disclosed amounts), andmin_bountydrops all null-bounty rows — a high threshold discards most data.- HackerOne pages are rate-limited to 0.4s apart; don't hammer.
flowchart TD
P[POST /api/hunt/plan] -->|no traffic| PLAN[Module gap analysis]
PLAN --> D{Module runnable?}
D -->|missing profile key| SKIP[Skipped — NOT reported clean]
D -->|scope.domains present| RUN[POST /api/hunt/run]
RUN --> B{Request in scope?}
B -->|out of scope| BLOCK[Blocked before socket opens]
B -->|in scope| ENGINE[HUNT_TOOLS engine executes]Related in Offensive Security