class HackerOne::Client::Report
Constants
- RESOLVED_STATES
- SEVERITY_RATINGS
- STATES
- STATES_REQUIRING_STATE_CHANGE_MESSAGE
Public Class Methods
# File lib/hackerone/client/report.rb, line 46 def add_on_state_change_hook(proc) on_state_change_hooks << proc end
# File lib/hackerone/client/report.rb, line 50 def clear_on_state_change_hooks @on_state_change_hooks = [] end
# File lib/hackerone/client/report.rb, line 59 def initialize(report) @report = report end
# File lib/hackerone/client/report.rb, line 54 def on_state_change_hooks @on_state_change_hooks ||= [] end
Public Instance Methods
# File lib/hackerone/client/report.rb, line 152 def activities if ships = relationships.fetch(:activities, {}).fetch(:data, []) ships.map do |activity_data| Activities.build(activity_data) end end end
Add a comment to a report. By default, internal comments will be added.
id: the ID of the report message: the content of the comment that will be created internal: “team only” comment (true, default) or “all participants”
# File lib/hackerone/client/report.rb, line 294 def add_comment(message, internal: true) fail ArgumentError, "message is required" if message.blank? body = { type: "activity-comment", attributes: { message: message, internal: internal } } response_json = make_post_request("reports/#{id}/activities", request_body: body) HackerOne::Client::Activities.build(response_json) 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.
# File lib/hackerone/client/report.rb, line 264 def add_report_reference(reference) body = { type: "issue-tracker-reference-id", attributes: { reference: reference } } response_json = make_post_request("reports/#{id}/issue_tracker_reference_id", request_body: body) @report = response_json[:relationships][:report][:data] self end
# File lib/hackerone/client/report.rb, line 327 def assign_to_group(name) group = program.find_group(name) _assign_to(group.id, :group) end
# File lib/hackerone/client/report.rb, line 322 def assign_to_user(name) member = program.find_member(name) _assign_to(member.user.id, :user) end
# File lib/hackerone/client/report.rb, line 98 def assignee if assignee_relationship = relationships[:assignee] HackerOne::Client::User.new(assignee_relationship[:data]) else nil end end
# File lib/hackerone/client/report.rb, line 146 def attachments @attachments ||= relationships.fetch(:attachments, {}) .fetch(:data, []) .map { |attachment| HackerOne::Client::Attachment.new(attachment) } end
# File lib/hackerone/client/report.rb, line 164 def award_bounty(message:, amount:, bonus_amount: nil) request_body = { message: message, amount: amount, bonus_amount: bonus_amount } response_body = make_post_request( "reports/#{id}/bounties", request_body: request_body ) Bounty.new(response_body) end
# File lib/hackerone/client/report.rb, line 178 def award_swag(message:) request_body = { message: message } response_body = make_post_request( "reports/#{id}/swags", request_body: request_body ) Swag.new(response_body, program) end
# File lib/hackerone/client/report.rb, line 137 def classification_label weakness.to_owasp end
# File lib/hackerone/client/report.rb, line 71 def created_at attributes[:created_at] end
# File lib/hackerone/client/report.rb, line 63 def id @report[:id] end
# File lib/hackerone/client/report.rb, line 79 def issue_tracker_reference_id attributes[:issue_tracker_reference_id] end
# File lib/hackerone/client/report.rb, line 75 def issue_tracker_reference_url attributes[:issue_tracker_reference_url] end
# File lib/hackerone/client/report.rb, line 309 def lock! unless RESOLVED_STATES.include? self.state.to_sym raise ArgumentError, "Report must be closed before locking" end body = { type: "activity-comments-closed" } response_json = make_put_request("reports/#{id}/close_comments", request_body: body) HackerOne::Client::Activities.build(response_json) end
# File lib/hackerone/client/report.rb, line 106 def payment_total payments.reduce(0) { |total, payment| total + payment_amount(payment) } end
# File lib/hackerone/client/report.rb, line 160 def program @program || Program.find(relationships[:program][:data][:attributes][:handle]) end
# File lib/hackerone/client/report.rb, line 91 def reporter relationships .fetch(:reporter, {}) .fetch(:data, {}) .fetch(:attributes, {}) end
Excludes reports where the payout amount is 0 indicating swag-only or no payout for the issue supplied
# File lib/hackerone/client/report.rb, line 116 def risk case payment_total when HackerOne::Client.low_range || DEFAULT_LOW_RANGE "low" when HackerOne::Client.medium_range || DEFAULT_MEDIUM_RANGE "medium" when HackerOne::Client.high_range || DEFAULT_HIGH_RANGE "high" when HackerOne::Client.critical_range || DEFAULT_CRITICAL_RANGE "critical" end end
# File lib/hackerone/client/report.rb, line 83 def severity attributes[:severity] end
# File lib/hackerone/client/report.rb, line 87 def state attributes[:state] 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.
# File lib/hackerone/client/report.rb, line 228 def state_change(state, message = nil, attributes = {}) raise ArgumentError, "state (#{state}) must be one of #{STATES}" unless STATES.include?(state) old_state = self.state body = { type: "state-change", attributes: { state: state } } body[:attributes] = body[:attributes].reverse_merge(attributes) if message body[:attributes][:message] = message elsif STATES_REQUIRING_STATE_CHANGE_MESSAGE.include?(state) fail ArgumentError, "State #{state} requires a message. No message was supplied." else # message is in theory optional, but a value appears to be required. body[:attributes][:message] = "" end response_json = make_post_request("reports/#{id}/state_changes", request_body: body) @report = response_json self.class.on_state_change_hooks.each do |hook| hook.call(self, old_state.to_s, state.to_s) end self end
# File lib/hackerone/client/report.rb, line 110 def structured_scope StructuredScope.new(relationships[:structured_scope].fetch(:data, {})) end
# File lib/hackerone/client/report.rb, line 207 def suggest_bounty(message:, amount:, bonus_amount: nil) request_body = { message: message, amount: amount, bonus_amount: bonus_amount } response_body = make_post_request( "reports/#{id}/bounty_suggestions", request_body: request_body ) Activities.build(response_body) end
# File lib/hackerone/client/report.rb, line 129 def summary attributes[:vulnerability_information] end
# File lib/hackerone/client/report.rb, line 67 def title attributes[:title] 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.
# File lib/hackerone/client/report.rb, line 284 def triage(reference) add_report_reference(reference) state_change(:triaged) end
# File lib/hackerone/client/report.rb, line 332 def unassign _assign_to(nil, :nobody) end
# File lib/hackerone/client/report.rb, line 190 def update_severity(rating:) raise ArgumentError, "Invalid severity rating" unless SEVERITY_RATINGS.include?(rating.to_s) request_body = { type: "severity", attributes: { rating: rating } } response_body = make_post_request( "reports/#{id}/severities", request_body: request_body ) @report[:attributes][:severity] = { rating: rating } Activities.build(response_body) end
# File lib/hackerone/client/report.rb, line 133 def weakness @weakness ||= Weakness.new(relationships.fetch(:weakness, {}).fetch(:data, {}).fetch(:attributes, {})) end
Bounty
writeups just use the key, and not the label value.
# File lib/hackerone/client/report.rb, line 142 def writeup_classification classification_label.split("-").first end
Private Instance Methods
# File lib/hackerone/client/report.rb, line 358 def _assign_to(assignee_id, assignee_type) request_body = { type: assignee_type, } request_body[:id] = assignee_id if assignee_id response = HackerOne::Client::Api.hackerone_api_connection.put do |req| req.headers["Content-Type"] = "application/json" req.url "reports/#{id}/assignee" req.body = { data: request_body }.to_json end unless response.success? fail("Unable to assign report #{id} to #{assignee_type} with id '#{assignee_id}'. Response status: #{response.status}, body: #{response.body}") end @report = parse_response response end
# File lib/hackerone/client/report.rb, line 346 def attributes @report[:attributes] end
# File lib/hackerone/client/report.rb, line 342 def payment_amount(payment) payment.bounty_amount end
# File lib/hackerone/client/report.rb, line 338 def payments activities.select { |activity| activity.is_a? Activities::BountyAwarded } end
# File lib/hackerone/client/report.rb, line 350 def relationships @report[:relationships] end
# File lib/hackerone/client/report.rb, line 354 def vulnerability_types relationships.fetch(:vulnerability_types, {}).fetch(:data, []) end