Class: HackerOne::Client::Report
- Inherits:
-
Object
- Object
- HackerOne::Client::Report
show all
- Includes:
- ResourceHelper
- Defined in:
- lib/hackerone/client/report.rb
Constant Summary
collapse
- STATES =
%w(
new
triaged
needs-more-info
resolved
not-applicable
informative
duplicate
spam
).map(&:to_sym).freeze
- STATES_REQUIRING_STATE_CHANGE_MESSAGE =
%w(
needs-more-info
informative
duplicate
).map(&:to_sym).freeze
- RESOLVED_STATES =
%w(
resolved
not-applicable
informative
duplicate
spam
).map(&:to_sym).freeze
- SEVERITY_RATINGS =
%w(
none
low
medium
high
critical
).freeze
Class Method Summary
collapse
Instance Method Summary
collapse
included, #make_get_request, #make_post_request, #make_put_request, #parse_response
Constructor Details
#initialize(report) ⇒ Report
Returns a new instance of Report.
59
60
61
|
# File 'lib/hackerone/client/report.rb', line 59
def initialize(report)
@report = report
end
|
Class Method Details
.add_on_state_change_hook(proc) ⇒ Object
46
47
48
|
# File 'lib/hackerone/client/report.rb', line 46
def add_on_state_change_hook(proc)
on_state_change_hooks << proc
end
|
.clear_on_state_change_hooks ⇒ Object
50
51
52
|
# File 'lib/hackerone/client/report.rb', line 50
def clear_on_state_change_hooks
@on_state_change_hooks = []
end
|
.on_state_change_hooks ⇒ Object
54
55
56
|
# File 'lib/hackerone/client/report.rb', line 54
def on_state_change_hooks
@on_state_change_hooks ||= []
end
|
Instance Method Details
#activities ⇒ Object
152
153
154
155
156
157
158
|
# 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”
294
295
296
297
298
299
300
301
302
303
304
305
306
307
|
# File 'lib/hackerone/client/report.rb', line 294
def (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
|
#add_report_reference(reference) ⇒ Object
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.
264
265
266
267
268
269
270
271
272
273
274
275
|
# 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
|
#assign_to_group(name) ⇒ Object
327
328
329
330
|
# File 'lib/hackerone/client/report.rb', line 327
def assign_to_group(name)
group = program.find_group(name)
_assign_to(group.id, :group)
end
|
#assign_to_user(name) ⇒ Object
322
323
324
325
|
# 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
|
#assignee ⇒ Object
98
99
100
101
102
103
104
|
# 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
|
#attachments ⇒ Object
146
147
148
149
150
|
# File 'lib/hackerone/client/report.rb', line 146
def attachments
@attachments ||= relationships.fetch(:attachments, {})
.fetch(:data, [])
.map { |attachment| HackerOne::Client::Attachment.new(attachment) }
end
|
#award_bounty(message:, amount:, bonus_amount: nil) ⇒ Object
164
165
166
167
168
169
170
171
172
173
174
175
176
|
# 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
|
#award_swag(message:) ⇒ Object
178
179
180
181
182
183
184
185
186
187
188
|
# 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
|
#classification_label ⇒ Object
137
138
139
|
# File 'lib/hackerone/client/report.rb', line 137
def classification_label
weakness.to_owasp
end
|
#created_at ⇒ Object
71
72
73
|
# File 'lib/hackerone/client/report.rb', line 71
def created_at
attributes[:created_at]
end
|
#id ⇒ Object
63
64
65
|
# File 'lib/hackerone/client/report.rb', line 63
def id
@report[:id]
end
|
#issue_tracker_reference_id ⇒ Object
79
80
81
|
# File 'lib/hackerone/client/report.rb', line 79
def issue_tracker_reference_id
attributes[:issue_tracker_reference_id]
end
|
#issue_tracker_reference_url ⇒ Object
75
76
77
|
# File 'lib/hackerone/client/report.rb', line 75
def issue_tracker_reference_url
attributes[:issue_tracker_reference_url]
end
|
#lock! ⇒ Object
309
310
311
312
313
314
315
316
317
318
319
320
|
# 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
|
#payment_total ⇒ Object
106
107
108
|
# File 'lib/hackerone/client/report.rb', line 106
def payment_total
payments.reduce(0) { |total, payment| total + payment_amount(payment) }
end
|
#program ⇒ Object
160
161
162
|
# File 'lib/hackerone/client/report.rb', line 160
def program
@program || Program.find(relationships[:program][:data][:attributes][:handle])
end
|
#reporter ⇒ Object
91
92
93
94
95
96
|
# File 'lib/hackerone/client/report.rb', line 91
def reporter
relationships
.fetch(:reporter, {})
.fetch(:data, {})
.fetch(:attributes, {})
end
|
#risk ⇒ Object
Excludes reports where the payout amount is 0 indicating swag-only or no payout for the issue supplied
#severity ⇒ Object
83
84
85
|
# File 'lib/hackerone/client/report.rb', line 83
def severity
attributes[:severity]
end
|
#state ⇒ Object
87
88
89
|
# File 'lib/hackerone/client/report.rb', line 87
def state
attributes[:state]
end
|
#state_change(state, message = nil, attributes = {}) ⇒ Object
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.
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
|
# 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
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
|
#structured_scope ⇒ Object
110
111
112
|
# File 'lib/hackerone/client/report.rb', line 110
def structured_scope
StructuredScope.new(relationships[:structured_scope].fetch(:data, {}))
end
|
#suggest_bounty(message:, amount:, bonus_amount: nil) ⇒ Object
207
208
209
210
211
212
213
214
215
216
217
218
219
|
# 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
|
#summary ⇒ Object
129
130
131
|
# File 'lib/hackerone/client/report.rb', line 129
def summary
attributes[:vulnerability_information]
end
|
#title ⇒ Object
67
68
69
|
# File 'lib/hackerone/client/report.rb', line 67
def title
attributes[:title]
end
|
#triage(reference) ⇒ Object
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.
284
285
286
287
|
# File 'lib/hackerone/client/report.rb', line 284
def triage(reference)
add_report_reference(reference)
state_change(:triaged)
end
|
#unassign ⇒ Object
332
333
334
|
# File 'lib/hackerone/client/report.rb', line 332
def unassign
_assign_to(nil, :nobody)
end
|
#update_severity(rating:) ⇒ Object
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
|
# 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
|
#weakness ⇒ Object
133
134
135
|
# File 'lib/hackerone/client/report.rb', line 133
def weakness
@weakness ||= Weakness.new(relationships.fetch(:weakness, {}).fetch(:data, {}).fetch(:attributes, {}))
end
|
#writeup_classification ⇒ Object
Bounty writeups just use the key, and not the label value.
142
143
144
|
# File 'lib/hackerone/client/report.rb', line 142
def writeup_classification
classification_label.split("-").first
end
|