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