"""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")))