Spaces:
Running
Running
File size: 6,234 Bytes
86b8634 fc0b9f5 86b8634 6a19827 1c74866 86b8634 1c74866 86b8634 1c74866 86b8634 1c74866 6a19827 86b8634 fc0b9f5 86b8634 1c74866 86b8634 1c74866 86b8634 1c74866 86b8634 6a19827 86b8634 1c74866 86b8634 1c74866 86b8634 1c74866 86b8634 fc0b9f5 86b8634 fc0b9f5 86b8634 fc0b9f5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | """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")))
|