File size: 5,743 Bytes
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# Usage recipes

No API key or login is needed. All examples call the public REST API. Start with summaries, then retrieve the details you need.

## Python: summary search and full details

```bash
pip install requests
```

```python
import requests

base = "https://wuhp-bbconnectapi.hf.space/api/v1"
session = requests.Session()

def get(path, **params):
    response = session.get(base + path, params=params, timeout=180)
    response.raise_for_status()
    result = response.json()
    if not result["ok"]:
        raise RuntimeError(result)
    return result

page = get("/programs", platforms="hackerone,bugcrowd", search="", limit=10)
print(page["total"], page["next_offset"])

for program in page["programs"][:2]:
    detail = get(f"/programs/{program['platform']}/{program['handle']}")
    print(program["name"], detail["min_bounty"], detail["max_bounty"], detail["currency"])
    print("partial:", detail["partial"], "fields:", detail["field_status"])
    print(detail["targets"], detail["rules"])
```

Details may use a fallback. A successful HTTP response is not proof that every field is present; inspect `partial`/`field_status` before accessing optional values.

## Crawl directory pages and export JSON/CSV

Run the [complete example](../examples/export_programs.py):

```bash
python examples/export_programs.py --platforms hackerone,bugcrowd,intigriti,yeswehack --output programs.json --csv programs.csv
```

The script follows `next_offset`, deduplicates IDs, validates statuses, and retries a bounded number of 429/503 responses. It exports directory summaries, not every policy document. To point it at a local instance, add `--base-url http://localhost:7860/api/v1`.

Equivalent pagination pattern:

```python
programs = {}
offset = 0
while True:
    page = get("/programs", platforms="hackerone,bugcrowd", limit=100, offset=offset)
    if page["partial"]:
        raise RuntimeError("A directory source is unavailable; inspect page['sources']")
    programs.update({p["id"]: p for p in page["programs"]})
    if page["next_offset"] is None:
        break
    offset = page["next_offset"]
```

For large collections, use summaries to discover IDs, then batch selected detail lookups. Offset pagination is not an atomic snapshot and can shift after cache expiration.

## Filter advertised rewards without mixing currencies

```python
page = get("/programs", platforms="hackerone", currency="USD",
           reward_at_least=1000, sort_by="max_bounty", descending=True, limit=10)
```

This uses directory `max_bounty`. Unknown amounts/currencies are excluded. It does not convert EUR to USD, infer minimums, or crawl all live tables before filtering.

## Get normalized assets while retaining exclusions

```python
assets = get("/programs/hackerone/security/assets", asset_type="wildcard", limit=100)
for asset in assets["in_scope"]:
    print(asset["identifier"], asset["type"], asset["instructions"])
print("All exclusions:", assets["out_of_scope"])
print("Program rules:", assets["rules"])
```

If `next_offset` is present, continue paging the included assets. All exclusions and rules remain in each response. Native target-group restrictions are in each asset's `raw` record; a normalized identifier alone is not sufficient authorization to test.

## Batch detail lookup

```python
response = session.post(base + "/programs/batch", json={"programs": [
    {"platform": "hackerone", "handle": "security"},
    {"platform": "yeswehack", "handle": "outscale"},
]}, timeout=180)
# Batch status 503 still contains individual results if all lookups failed.
if response.status_code not in (200, 503):
    response.raise_for_status()
batch = response.json()
if "programs" not in batch:
    raise RuntimeError(batch)
for detail in batch["programs"]:
    print(detail["platform"], detail["handle"], detail["ok"], detail.get("partial"))
```

## JavaScript / browser fetch

```javascript
const base = "https://wuhp-bbconnectapi.hf.space/api/v1";
const query = new URLSearchParams({
  platforms: "hackerone,bugcrowd", limit: "10", include_details: "false"
});
const response = await fetch(`${base}/programs?${query}`);
const page = await response.json();
if (!response.ok || !page.ok) throw new Error(JSON.stringify(page));
console.log(page.programs, page.next_offset);
```

CORS is enabled for public GET/POST requests. Do not add credentials, tokens, or an Authorization header. For batch requests use `method: "POST"`, `headers: {"Content-Type": "application/json"}`, and `body: JSON.stringify({programs: [...]})`.

## curl

```bash
# Is the application awake? (No upstream check.)
curl 'https://wuhp-bbconnectapi.hf.space/api/v1/health'

# Program counts and source status
curl 'https://wuhp-bbconnectapi.hf.space/api/v1/stats'

# A small page with full scopes and rules
curl 'https://wuhp-bbconnectapi.hf.space/api/v1/programs?platforms=hackerone&include_details=true&limit=5'

# Save a complete policy/scope response
curl 'https://wuhp-bbconnectapi.hf.space/api/v1/programs/yeswehack/outscale' -o outscale.json
```

In Windows PowerShell, use `curl.exe` if `curl` is an alias in your shell. For JSON POSTs, Python or a saved request-body file avoids shell-specific quote escaping.

## Existing Gradio integrations

```python
from gradio_client import Client
client = Client("wuhp/bbconnectapi")

# Original contract: exactly five positional values, always full details.
page = client.predict(["hackerone"], "", False, 10, 0, api_name="/collect")

# Six values only on collect_options; False selects summaries.
summary = client.predict(["hackerone"], "", False, 10, 0, False, api_name="/collect_options")
```

For direct REST examples, `requests` is the only client dependency. `gradio_client` is needed only for Gradio calls.