hackerone-api 0.2.0

Unofficial, dependency-light Rust client for the HackerOne API (v1): submit reports, read your reports, hacktivity, balance, and earnings.
Documentation

hackerone-api

Unofficial, dependency-light Rust client for the HackerOne API (v1).

Blocking, no async runtime. HTTP Basic auth. An injectable transport so tests use a mock and embedders can swap the HTTP stack.

It covers both the hacker surface (submit reports, your reports, hacktivity, balance, earnings) and the customer read surface (programs, scopes, reports, weaknesses).

use hackerone_api::{Client, CreateHackerReport, SeverityRating};

fn main() -> Result<(), hackerone_api::Error> {
    let client = Client::new("api-identifier", "api-token");

    // Submit a report as a hacker (POST /v1/hackers/reports).
    let report = CreateHackerReport::new("chia_network", "Remote panic in ProofOfSpace::parse")
        .vulnerability_information("## Steps\n1. …")
        .impact("Any node on the gossip path aborts.")
        .severity(SeverityRating::High)
        .weakness_id(1337)
        .structured_scope_id(57);

    let created = client.create_report(&report)?;
    println!("filed: {:?}", created.title);
    Ok(())
}

Auth

HTTP Basic: the username is your API token identifier and the password is the token value. Create one in your HackerOne account settings. The examples read:

export HACKERONE_API_IDENTIFIER="..."
export HACKERONE_API_TOKEN="..."

Never commit a token. In AXIOM, seed it in the vault (ax-vault put persona/smurf77/hackerone_api_token hackerone-api) and inject it at call time.

Endpoints

Hacker surface (/v1/hackers/…)

Method Endpoint
create_report(&CreateHackerReport) POST /v1/hackers/reports
my_reports(&PageQuery) GET /v1/hackers/me/reports
my_report(id) GET /v1/hackers/reports/{id}
hacktivity(&HacktivityQuery) GET /v1/hackers/hacktivity
balance() GET /v1/hackers/payments/balance
earnings(&PageQuery) GET /v1/hackers/payments/earnings

Customer read surface (kept)

Method Endpoint
me() GET /v1/me
programs() GET /v1/me/programs
program(id) GET /v1/programs/{id}
structured_scopes(program, page) GET /v1/programs/{id}/structured_scopes
reports(&ReportQuery) GET /v1/reports
report(id) GET /v1/reports/{id}
add_comment(report, text) POST /v1/reports/{id}/activities
change_state(report, state, msg) POST /v1/reports/{id}/state_changes
weaknesses() GET /v1/weaknesses
next_page(&page) follows a links.next URL
get_raw(path, query) any authenticated GET

Submitting a report

Report submission is a hacker operation and must go to POST /v1/hackers/reports with a team_handle attribute — not the customer /v1/reports surface. The body this crate sends is exactly:

{
  "data": {
    "type": "report",
    "attributes": {
      "team_handle": "chia_network",
      "title": "",
      "vulnerability_information": "",
      "impact": "",
      "severity_rating": "high",
      "weakness_id": 1337,
      "structured_scope_id": 57
    }
  }
}

team_handle, title, vulnerability_information, and impact are required; severity_rating (one of none|low|medium|high|critical), weakness_id (integer), and structured_scope_id (integer) are optional and omitted when unset. The response is the created report resource.

Try it without submitting:

cargo run --example submit_report -- chia_network "Title" poc.md "Impact" high 1337 57
# add H1_API_SUBMIT=1 to actually POST

Errors

Failures are typed. A non-2xx response becomes Error::Api carrying the HTTP status and the server's parsed errors array:

if let Err(e) = client.create_report(&report) {
    if e.is_client_error() {
        eprintln!("HTTP {}: {}", e.status().unwrap_or(0), e.detail().unwrap_or(""));
        for api_error in e.api_errors() {
            eprintln!("  - {:?}: {:?}", api_error.title, api_error.detail);
        }
    }
}

Design

  • #![forbid(unsafe_code)], #![warn(missing_docs)].
  • Errors are typed: Error::{Transport, Decode, Invalid, Api}.
  • Transport is a trait (Transport); UreqTransport is the default.
  • Domain types keep the documented fields and stash unknown ones in a flattened extra map (and JSON:API ids accept string or integer).
  • Tests run entirely against a mock transport — no network.

Status

0.2.0 — hacker report submission corrected to POST /v1/hackers/reports; hacker report list/get, hacktivity, balance, and earnings added.

License

MIT. See LICENSE.

Disclaimer

This crate is unofficial and not affiliated with or endorsed by HackerOne. "HackerOne" is a trademark of its owner; the name is used only to describe what the library talks to.