Skip to content

feat: HackerOne scope ingestion into a ScopeGuard#19

Merged
TrueNix merged 1 commit into
mainfrom
feature/hackerone-scope
Jul 12, 2026
Merged

feat: HackerOne scope ingestion into a ScopeGuard#19
TrueNix merged 1 commit into
mainfrom
feature/hackerone-scope

Conversation

@TrueNix

@TrueNix TrueNix commented Jul 12, 2026

Copy link
Copy Markdown
Owner

What

Pulls a HackerOne program's structured scopes and builds a ScopeGuard from them, so a program's authoritative in-scope assets flow straight into the scanner — and its audit trail (#17) — instead of a hand-typed allowlist.

from bugbounty_ctf import SecurityScanner
from bugbounty_ctf.audit_log import AuditLog
from bugbounty_ctf.hackerone import HackerOneClient

guard = HackerOneClient().scope_guard("example-program")
scanner = SecurityScanner("https://app.example.com/", scope=guard, audit_log=AuditLog())
# every request is now checked — and recorded — against the program's real scope

API

  • HackerOneClient.structured_scopes(handle) — reads /v1/hackers/programs/{handle}/structured_scopes with HTTP Basic auth (H1_USERNAME / H1_API_TOKEN), following pagination.
  • HackerOneClient.scope_guard(handle, *, bounty_only=False, allow_subdomains=True) — maps URL / WILDCARD / DOMAIN assets to a ScopeGuard; skips submission-ineligible assets and non-host asset types (mobile apps, CIDRs, source repos).

Safety

Fail-closed on every edge: missing credentials, auth rejection (401/403), unknown program (404), non-200, invalid JSON, and malformed/path-injecting handles all raise a typed HackerOneError. The HTTP layer is an injectable typed Protocol (same idiom as brain.py), so tests run against a mocked API with no network and no real token.

This is the loop closer

It ties the two prior borrows together: H1 scope → ScopeGuardSecurityScannerAuditLog. Scope is no longer hand-maintained; it comes from the authoritative source, and every request is checked and logged against it.

Tests

tests/test_hackerone.py (injected fake session): scope parsing + auth passthrough, in-scope allow / out-of-scope deny / ineligible filtered / non-host skipped, bounty_only, pagination, missing-credentials, HTTP errors (401/403/404/500), and invalid-handle rejection. Verified locally against the real client with a stubbed transport.

I did not add an __init__ export (to avoid colliding with #17's __init__ change) — import via from bugbounty_ctf.hackerone import HackerOneClient. Can add the export once #17 merges.

Live-auth step for you

Tests mock the API. To confirm the real path: create an API token at https://hackerone.com/settings/api_token/edit, export H1_USERNAME=... H1_API_TOKEN=..., then HackerOneClient().scope_guard("<a-program-you're-invited-to>") and eyeball the allowlist.

⚠️ pytest/ruff/mypy not run locally (no pip); logic, line-length (≤100), and syntax verified directly — relying on CI for the gate.

🤖 Generated with Claude Code

Adds a HackerOne client that pulls a program's structured scopes and builds a
ScopeGuard from the host assets eligible for submission, so a program's
authoritative scope flows straight into the scanner (and its audit trail)
instead of a hand-typed allowlist.

- HackerOneClient.structured_scopes(handle): reads
  /v1/hackers/programs/{handle}/structured_scopes with HTTP Basic auth
  (H1_USERNAME / H1_API_TOKEN), following pagination.
- HackerOneClient.scope_guard(handle, *, bounty_only, allow_subdomains): maps
  URL / WILDCARD / DOMAIN assets to a ScopeGuard; skips submission-ineligible
  assets and non-host asset types (mobile apps, CIDRs, ...).
- Fail-closed on missing credentials, auth rejection, 404, non-200, bad JSON,
  and invalid handles. HTTP layer is injectable (typed Protocol) so tests run
  against a mocked API with no network.

Pattern adapted from shuvonsec/claude-bug-bounty (MIT) mcp/hackerone-mcp, moved
from the public GraphQL endpoint to the authenticated REST scopes API and shaped
to feed this project's ScopeGuard + AuditLog.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@TrueNix
TrueNix merged commit 233348c into main Jul 12, 2026
5 checks passed
@TrueNix
TrueNix deleted the feature/hackerone-scope branch July 12, 2026 12:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant