class HackerOne::Client::Api

Public Class Methods

new(program = nil) click to toggle source
# File lib/hackerone/client.rb, line 59
def initialize(program = nil)
  @program = program
end

Private Class Methods

hackerone_api_connection() click to toggle source
# File lib/hackerone/client.rb, line 198
def self.hackerone_api_connection
  unless ENV["HACKERONE_TOKEN_NAME"] && ENV["HACKERONE_TOKEN"]
    raise NotConfiguredError, "HACKERONE_TOKEN_NAME HACKERONE_TOKEN environment variables must be set"
  end

  @connection ||= Faraday.new(url: "https://api.hackerone.com/v1") do |faraday|
    faraday.basic_auth(ENV["HACKERONE_TOKEN_NAME"], ENV["HACKERONE_TOKEN"])
    faraday.adapter Faraday.default_adapter
  end
end
parse_response(response, extract_data: true) click to toggle source
# File lib/hackerone/client.rb, line 181
def self.parse_response(response, extract_data: true)
  if response.status.to_s.start_with?("4")
    raise ArgumentError, "API called failed, probably your fault: #{response.body}"
  elsif response.status.to_s.start_with?("5")
    raise RuntimeError, "API called failed, probably their fault: #{response.body}"
  elsif response.success?
    response_body_json = JSON.parse(response.body, symbolize_names: true)
    if extract_data && response_body_json.key?(:data)
      response_body_json[:data]
    else
      response_body_json
    end
  else
    raise RuntimeError, "Not sure what to do here: #{response.body}"
  end
end

Public Instance Methods

create_report(title:, summary:, impact:, severity_rating:, source:) click to toggle source
Public: create a new report

title: The title of the report summary: Summary of the report impact: Impact of the report severity_rating: severity of report, must be one of api.hackerone.com/reference/#severity-ratings source: where the report came from, i.e. API, Bugcrowd, etc.

returns an HackerOne::Client::Report object or raises an error if error during creation

# File lib/hackerone/client.rb, line 127
def create_report(title:, summary:, impact:, severity_rating:, source:)
  raise ArgumentError, "Program cannot be nil" unless program

  data = {
    "data": {
      "type": "report",
      "attributes": {
        "team_handle": program,
        "title": title,
        "vulnerability_information": summary,
        "impact": impact,
        "severity_rating": severity_rating,
        "source": source
      }
    }
  }
  Report.new(post("reports", data))
end
program() click to toggle source
# File lib/hackerone/client.rb, line 63
def program
  @program || HackerOne::Client.program
end
report(id) click to toggle source
Public: retrieve a report

id: the ID of a specific report

returns an HackerOne::Client::Report object or raises an error if no report is found.

# File lib/hackerone/client.rb, line 152
def report(id)
  Report.new(get("reports/#{id}"))
end
reporters() click to toggle source
# File lib/hackerone/client.rb, line 67
def reporters
  raise ArgumentError, "Program cannot be nil" unless program
  response = self.class.hackerone_api_connection.get do |req|
    req.url "programs/#{Program.find(program).id}/reporters"
  end

  data = self.class.parse_response(response)
  if data.nil?
    raise RuntimeError, "Expected data attribute in response: #{response.body}"
  end

  data.map do |reporter|
    Reporter.new(reporter)
  end
end
reports(since: 3.days.ago, before: nil, state: :new) click to toggle source
Returns all reports in a given state, optionally with a time bound

program: the HackerOne program to search on (configure globally with Hackerone::Client.program=) since (optional): a time bound, don't include reports earlier than since. Must be a DateTime object. before (optional): a time bound, don't include reports later than before. Must be a DateTime object. state (optional): state that a report is in, by default new

returns all open reports or an empty array

# File lib/hackerone/client.rb, line 91
def reports(since: 3.days.ago, before: nil, state: :new)
  raise ArgumentError, "Program cannot be nil" unless program
  raise ArgumentError, "State is invalid" unless REPORT_STATES.include?(state.to_s)

  response = self.class.hackerone_api_connection.get do |req|
    options = {
      "filter[state][]" => state,
      "filter[program][]" => program
    }
    unless since.nil?
      options["filter[created_at__gt]"] = since.iso8601
    end
    unless before.nil?
      options["filter[created_at__lt]"] = before.iso8601
    end

    req.url "reports", options
  end

  data = self.class.parse_response(response)

  data.map do |report|
    Report.new(report)
  end
end

Private Instance Methods

get(endpoint, params = nil) click to toggle source
# File lib/hackerone/client.rb, line 169
def get(endpoint, params = nil)
  response = with_retry do
    self.class.hackerone_api_connection.get do |req|
      req.headers["Content-Type"] = "application/json"
      req.params = params || {}
      req.url endpoint
    end
  end

  self.class.parse_response(response)
end
post(endpoint, body) click to toggle source
# File lib/hackerone/client.rb, line 157
def post(endpoint, body)
  response = with_retry do
    self.class.hackerone_api_connection.post do |req|
      req.headers["Content-Type"] = "application/json"
      req.body = body.to_json
      req.url endpoint
    end
  end

  self.class.parse_response(response)
end
with_retry(attempts = 3) { || ... } click to toggle source
# File lib/hackerone/client.rb, line 209
def with_retry(attempts = 3, &block)
  attempts_remaining = attempts

  begin
    yield
  rescue StandardError
    if attempts_remaining > 0
      attempts_remaining -= 1
      sleep (attempts - attempts_remaining)
      retry
    else
      raise
    end
  end
end