bbconnectapi / app.py
wuhp's picture
Deploy bbcollect 8fcea0a244395b5057e019e22276481549cb1a4b
fc0b9f5 verified
Raw History Blame Contribute Delete
6.23 kB
"""Gradio UI and named JSON API endpoints for bbcollect."""
import os
os.environ.setdefault("GRADIO_ANALYTICS_ENABLED", "False")
import gradio as gr
from collector import PLATFORMS, collector
from api import create_api
def collect(platforms, search, bounty_only, limit, offset):
"""Stable five-input API; always include live rewards, scopes and rules."""
return collector.collect(platforms, search, bounty_only, limit, offset, True)
def collect_options(platforms, search, bounty_only, limit, offset, include_details=True):
"""Collect programs with an explicit full-details/summary toggle."""
return collector.collect(platforms, search, bounty_only, limit, offset, include_details)
def scope(platform, handle):
"""Fetch scopes, rules and rewards for a program (alias of /details)."""
return collector.scope(platform, handle.strip())
def details(platform, handle):
"""Get live public rewards, scopes, exclusions and full program rules."""
return collector.details(platform, handle.strip())
def sources():
"""Describe supported sources and cache behavior without crawling upstreams."""
return {"platforms": list(PLATFORMS), "hackerone": "Public live GraphQL, dataset fallback",
"other_platforms": "Directory: public dataset; details: live platform public program documents",
"collect_default": "Five inputs; full details, scopes, rules and reward tables",
"collect_options": "Six inputs; include_details=False for summaries",
"cache_seconds": 900, "failure_cache_seconds": 60,
"scope_fields": "Platform-native fields; in_scope and out_of_scope kept separate"}
with gr.Blocks(title="bbconnectapi") as demo:
gr.Markdown("# bbconnectapi\nPublic bug bounty programs and scope. **No API key or login required.** "
"[Interactive REST docs](/api/docs) · [OpenAPI schema](/api/openapi.json) · "
"[GitHub guide](https://github.com/Wuhpondiscord/bbcollect)\n\n"
"HackerOne uses live public data with an explicit dataset fallback. "
"All four platforms have live program details: reward tables, scopes and rules. "
"Directory feeds use public snapshots outside HackerOne. Results are cached for 15 minutes.")
with gr.Tab("Programs"):
platforms = gr.CheckboxGroup(list(PLATFORMS), value=["hackerone"], label="Platforms")
search = gr.Textbox(label="Search name, handle, URL or website", max_lines=1)
bounty_only = gr.Checkbox(label="Only programs known to offer bounties", value=False)
include_details = gr.Checkbox(label="Include scopes, rules and live reward tables (slower)", value=True)
with gr.Row():
limit = gr.Slider(1, 100, value=10, step=1, label="Page size")
offset = gr.Number(value=0, precision=0, minimum=0, maximum=100000, label="Offset")
collect_button = gr.Button("Collect programs", variant="primary")
programs_json = gr.JSON(label="Programs and source status")
collect_button.click(collect_options, [platforms, search, bounty_only, limit, offset, include_details],
programs_json, api_name="collect_options", concurrency_limit=2)
# Preserve the original wire format: Gradio validates component counts
# before Python defaults apply, so /collect must have exactly five inputs.
legacy_collect = gr.Button(visible=False)
legacy_collect.click(collect, [platforms, search, bounty_only, limit, offset],
programs_json, api_name="collect", concurrency_limit=2)
with gr.Tab("Program details and scope"):
platform = gr.Dropdown(list(PLATFORMS), value="hackerone", label="Platform")
handle = gr.Textbox(value="security", label="Program handle (from collect results)")
scope_button = gr.Button("Get rewards, scopes and rules")
scope_json = gr.JSON(label="Rewards, targets, rules and source status")
scope_button.click(scope, [platform, handle], scope_json, api_name="scope", concurrency_limit=2)
details_button = gr.Button("Get full details via /details")
details_button.click(details, [platform, handle], scope_json, api_name="details", concurrency_limit=2)
with gr.Tab("API"):
gr.Markdown('''### REST API — direct JSON, no key
Base URL: `https://wuhp-bbconnectapi.hf.space/api/v1`
```python
import requests
base = "https://wuhp-bbconnectapi.hf.space/api/v1"
page = requests.get(f"{base}/programs", params={
"platforms": "hackerone,bugcrowd", "search": "", "limit": 10
}, timeout=120).json()
detail = requests.get(f"{base}/programs/hackerone/security", timeout=120).json()
```
Summaries are the REST default. Add `include_details=true` for full policies and scopes.
Use `/programs/{platform}/{handle}/assets` for normalized assets and exclusions,
`POST /programs/batch` for up to 20 detail lookups, and `/stats`, `/sources`, `/health` for metadata.
[Try requests in the interactive docs](/api/docs) · [Full API reference on GitHub](https://github.com/Wuhpondiscord/bbcollect/blob/main/docs/API.md)
### Existing Gradio API
Use **View API** in the footer for generated client examples. `/collect` keeps its five-input contract.
```python
from gradio_client import Client
client = Client("wuhp/bbconnectapi")
result = client.predict(
platforms=["hackerone", "bugcrowd"], search="", bounty_only=False,
limit=100, offset=0, api_name="/collect")
scope = client.predict(platform="hackerone", handle="security", api_name="/scope")
```
Scope is a snapshot: check the linked program's current rules and exclusions before testing.''')
source_button = gr.Button("Source information")
source_json = gr.JSON(label="Supported sources")
source_button.click(sources, [], source_json, api_name="sources")
demo.queue(max_size=32, default_concurrency_limit=2)
# Register REST routes before the root Gradio mount. Existing Gradio URLs stay unchanged.
app = gr.mount_gradio_app(create_api(), demo, path="/", show_error=False, ssr_mode=False)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=int(os.getenv("PORT", "7860")))