lib/hackerone/client.rb in hackerone-client-0.1.1 vs lib/hackerone/client.rb in hackerone-client-0.2.0
- old
+ new
@@ -10,10 +10,21 @@
DEFAULT_LOW_RANGE = 1...999
DEFAULT_MEDIUM_RANGE = 1000...2499
DEFAULT_HIGH_RANGE = 2500...4999
DEFAULT_CRITICAL_RANGE = 5000...100_000_000
+ STATES = %w(
+ new
+ triaged
+ needs-more-info
+ resolved
+ not-applicable
+ informative
+ duplicate
+ spam
+ ).map(&:to_sym)
+
class << self
ATTRS = [:low_range, :medium_range, :high_range, :critical_range].freeze
attr_accessor :program
attr_reader *ATTRS
@@ -59,30 +70,110 @@
data.map do |report|
Report.new(report)
end
end
+ ## Idempotent: add the issue reference and put the report into the "triage" state.
+ #
+ # id: the ID of the report
+ # state: value for the reference (e.g. issue number or relative path to cross-repo issue)
+ #
+ # returns an HackerOne::Client::Report object or raises an error if
+ # no report is found.
+ def triage(id, reference)
+ add_report_reference(id, reference)
+ state_change(id, :triaged)
+ end
+
+ ## Idempotent: Add a report reference to a project
+ #
+ # id: the ID of the report
+ # state: value for the reference (e.g. issue number or relative path to cross-repo issue)
+ #
+ # returns an HackerOne::Client::Report object or raises an error if
+ # no report is found.
+ def add_report_reference(id, reference)
+ body = {
+ data: {
+ type: "issue-tracker-reference-id",
+ attributes: {
+ reference: reference
+ }
+ }
+ }
+
+ post("reports/#{id}/issue_tracker_reference_id", body)
+ end
+
+ ## Idempotent: change the state of a report. See STATES for valid values.
+ #
+ # id: the ID of the report
+ # state: the state in which the report is to be put in
+ #
+ # returns an HackerOne::Client::Report object or raises an error if
+ # no report is found.
+ def state_change(id, state)
+ raise ArgumentError, "state (#{state}) must be one of #{STATES}" unless STATES.include?(state)
+
+ body = {
+ data: {
+ type: "state-change",
+ attributes: {
+ message: "This is has been triaged internally.",
+ state: state
+ }
+ }
+ }
+ post("reports/#{id}/state_changes", body)
+ end
+
## 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.
def report(id)
+ get("reports/#{id}")
+ end
+
+ private
+ 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
+
+ parse_response(response)
+ end
+
+ def get(endpoint, params = nil)
+ response = with_retry do
self.class.hackerone_api_connection.get do |req|
- req.url "reports/#{id}"
+ req.headers['Content-Type'] = 'application/json'
+ req.params = params || {}
+ req.url endpoint
end
end
- if response.success?
+ parse_response(response)
+ end
+
+ def parse_response(response)
+ 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 Runtime, "API called failed, probobly their fault: #{response.body}"
+ elsif response.success?
Report.new(JSON.parse(response.body, :symbolize_names => true)[:data])
else
- raise ArgumentError, "Could not retrieve HackerOne report ##{id}: #{response.body}"
+ raise RuntimeError, "Not sure what to do here: #{response.body}"
end
end
- private
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