nexpose_ticketing 0.8.1 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/lib/nexpose_ticketing/config/ticket_service.config +5 -2
- data/lib/nexpose_ticketing/helpers/jira_helper.rb +20 -20
- data/lib/nexpose_ticketing/helpers/remedy_helper.rb +34 -34
- data/lib/nexpose_ticketing/helpers/servicedesk_helper.rb +15 -15
- data/lib/nexpose_ticketing/helpers/servicenow_helper.rb +19 -19
- data/lib/nexpose_ticketing/queries.rb +40 -15
- data/lib/nexpose_ticketing/ticket_repository.rb +208 -141
- data/lib/nexpose_ticketing/ticket_service.rb +123 -56
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6665f45b4fb44b5e107eb13ae98ea53e768e3a3c
|
4
|
+
data.tar.gz: 49e4aefdadb17710a8a927bee0bc858da29a1559
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c36501adcb968939ad7af6cf3e3a599f9d3bb999c3d4300119f4e15cbcd00e9dba144cfc502b62087f66bd33e7dd179e4a48f3a42cdacc07ca06e6b2e7e160b5
|
7
|
+
data.tar.gz: 5ff1131739ab1d3ec3649ded018143fa2688aab89635fda74b6c07d2fdeaf686751cb92a5201b0e886ed1cd3cd497005b57ba5ef66b75ad4c1db52c422fd7ed4
|
data/Gemfile.lock
CHANGED
@@ -11,8 +11,10 @@
|
|
11
11
|
- '1'
|
12
12
|
# Minimum floor severity to report on. Number between 0 and 10.
|
13
13
|
:severity: 8
|
14
|
-
# (M) Name of the report historical file saved in disk.
|
14
|
+
# (M) Name of the report historical file for sites saved in disk.
|
15
15
|
:file_name: last_scan_data.csv
|
16
|
+
# (M) Name of the report historical file for tags saved in disk.
|
17
|
+
:tag_file_name: tag_last_scan_data.csv
|
16
18
|
# (M) Defines the ticket creation mode:
|
17
19
|
# 'D' Default IP *-* Vulnerability
|
18
20
|
# 'I' IP address -* Vulnerability
|
@@ -25,8 +27,9 @@
|
|
25
27
|
# (M) For 'I' & 'V' mode. Set to 'Y' for tickets that have been fixed to be closed after update, set to 'N' for ticket to be left
|
26
28
|
# open for a user to manually close or change the status.
|
27
29
|
:close_old_tickets_on_update: Y
|
28
|
-
#
|
30
|
+
# Filters the reports to specific tags one per line, leave empty for no tags. Note tags cannot be used in conjunction with sites (tags get priority).
|
29
31
|
:tags:
|
32
|
+
#- '1'
|
30
33
|
# A comma separated list of vulnerability categories to include
|
31
34
|
:vulnerabilityCategories:
|
32
35
|
# The asset minimum risk score to open tickets for
|
@@ -22,28 +22,28 @@ class JiraHelper
|
|
22
22
|
# Generates the NXID. The NXID is a unique identifier used to find and update and/or close tickets.
|
23
23
|
#
|
24
24
|
# * *Args* :
|
25
|
-
# - +
|
25
|
+
# - +nexpose_identifier_id+ - Site/TAG ID the tickets are being generated for. Required for all ticketing modes
|
26
26
|
# - +row+ - Row from the generated Nexpose CSV report. Required for default ('D') mode.
|
27
27
|
# - +current_ip+ - The IP address of that this ticket is for. Required for IP mode ('I') mode.
|
28
28
|
#
|
29
29
|
# * *Returns* :
|
30
30
|
# - NXID string.
|
31
31
|
#
|
32
|
-
def generate_nxid(
|
33
|
-
fail '
|
32
|
+
def generate_nxid(nexpose_identifier_id, row=nil, current_ip=nil)
|
33
|
+
fail 'Nexpose Identifier ID is required to generate the NXID.' if nexpose_identifier_id.empty?
|
34
34
|
case @options[:ticket_mode]
|
35
35
|
# 'D' Default mode: IP *-* Vulnerability
|
36
36
|
when 'D'
|
37
37
|
fail 'Row is required to generate the NXID in \'D\' mode.' if row.nil? || row.empty?
|
38
|
-
@nxid = "#{
|
38
|
+
@nxid = "#{nexpose_identifier_id}#{row['asset_id']}#{row['vulnerability_id']}#{row['solution_id']}"
|
39
39
|
# 'I' IP address mode: IP address -* Vulnerability
|
40
40
|
when 'I'
|
41
41
|
fail 'Current IP is required to generate the NXID in \'I\' mode.' if current_ip.nil? || current_ip.empty?
|
42
|
-
@nxid = "#{
|
42
|
+
@nxid = "#{nexpose_identifier_id}#{current_ip.tr('.','')}"
|
43
43
|
# 'V' mode net yet implemented.
|
44
44
|
# 'V' Vulnerability mode: Vulnerability -* IP address
|
45
45
|
# when 'V'
|
46
|
-
# @NXID = "#{
|
46
|
+
# @NXID = "#{nexpose_identifier_id}#{row['current_asset_id']}#{row['current_vuln_id']}"
|
47
47
|
else
|
48
48
|
fail 'Could not close tickets - do not understand the ticketing mode!'
|
49
49
|
end
|
@@ -150,22 +150,22 @@ class JiraHelper
|
|
150
150
|
|
151
151
|
# Prepares tickets from the CSV.
|
152
152
|
# TODO Implement V Version.
|
153
|
-
def prepare_create_tickets(vulnerability_list,
|
153
|
+
def prepare_create_tickets(vulnerability_list, nexpose_identifier_id)
|
154
154
|
@ticket = Hash.new(-1)
|
155
155
|
case @options[:ticket_mode]
|
156
156
|
# 'D' Default IP *-* Vulnerability
|
157
157
|
when 'D'
|
158
|
-
prepare_tickets_default(vulnerability_list,
|
158
|
+
prepare_tickets_default(vulnerability_list, nexpose_identifier_id)
|
159
159
|
# 'I' IP address -* Vulnerability
|
160
160
|
when 'I'
|
161
|
-
prepare_tickets_by_ip(vulnerability_list,
|
161
|
+
prepare_tickets_by_ip(vulnerability_list, nexpose_identifier_id)
|
162
162
|
else
|
163
163
|
fail 'Unsupported ticketing mode selected.'
|
164
164
|
end
|
165
165
|
end
|
166
166
|
|
167
167
|
# Prepares and creates tickets in default mode.
|
168
|
-
def prepare_tickets_default(vulnerability_list,
|
168
|
+
def prepare_tickets_default(vulnerability_list, nexpose_identifier_id)
|
169
169
|
@log.log_message('Preparing tickets for default mode.')
|
170
170
|
tickets = []
|
171
171
|
CSV.parse(vulnerability_list.chomp, headers: :first_row) do |row|
|
@@ -176,7 +176,7 @@ class JiraHelper
|
|
176
176
|
'project' => {
|
177
177
|
'key' => "#{@jira_data[:project]}" },
|
178
178
|
'summary' => "#{row['ip_address']} => #{summary}",
|
179
|
-
'description' => "CVSS Score: #{row['cvss_score']} \n\n #{row['fix']} \n\n #{row['url']} \n\n\n NXID: #{generate_nxid(
|
179
|
+
'description' => "CVSS Score: #{row['cvss_score']} \n\n #{row['fix']} \n\n #{row['url']} \n\n\n NXID: #{generate_nxid(nexpose_identifier_id, row)}",
|
180
180
|
'issuetype' => {
|
181
181
|
'name' => 'Task' }
|
182
182
|
}
|
@@ -190,12 +190,12 @@ class JiraHelper
|
|
190
190
|
# per IP i.e. any vulnerabilities for a single IP in one ticket
|
191
191
|
#
|
192
192
|
# - +vulnerability_list+ - CSV of vulnerabilities within Nexpose.
|
193
|
-
# - +
|
193
|
+
# - +nexpose_identifier_id+ - Site/TAG ID the vulnerability list was generate from.
|
194
194
|
#
|
195
195
|
# * *Returns* :
|
196
196
|
# - List of JSON-formatted tickets for updating within Jira.
|
197
197
|
#
|
198
|
-
def prepare_tickets_by_ip(vulnerability_list,
|
198
|
+
def prepare_tickets_by_ip(vulnerability_list, nexpose_identifier_id)
|
199
199
|
@log.log_message('Preparing tickets for IP mode.')
|
200
200
|
tickets = []
|
201
201
|
current_ip = -1
|
@@ -229,13 +229,13 @@ class JiraHelper
|
|
229
229
|
\n\n"
|
230
230
|
end
|
231
231
|
unless current_ip == row['ip_address']
|
232
|
-
@ticket['fields']['description'] += "\n\n\n NXID: #{generate_nxid(
|
232
|
+
@ticket['fields']['description'] += "\n\n\n NXID: #{generate_nxid(nexpose_identifier_id, row, current_ip)}"
|
233
233
|
tickets.push(@ticket.to_json)
|
234
234
|
current_ip = -1
|
235
235
|
redo
|
236
236
|
end
|
237
237
|
end
|
238
|
-
@ticket['fields']['description'] += "\n\n\n NXID: #{generate_nxid(
|
238
|
+
@ticket['fields']['description'] += "\n\n\n NXID: #{generate_nxid(nexpose_identifier_id, nil, current_ip)}"
|
239
239
|
tickets.push(@ticket.to_json) unless @ticket.nil?
|
240
240
|
tickets
|
241
241
|
end
|
@@ -304,12 +304,12 @@ class JiraHelper
|
|
304
304
|
# * *Returns* :
|
305
305
|
# - List of Jira ticket Keys to be closed.
|
306
306
|
#
|
307
|
-
def prepare_close_tickets(vulnerability_list,
|
307
|
+
def prepare_close_tickets(vulnerability_list, nexpose_identifier_id)
|
308
308
|
@log.log_message('Preparing tickets to close.')
|
309
309
|
@nxid = nil
|
310
310
|
tickets = []
|
311
311
|
CSV.parse(vulnerability_list.chomp, headers: :first_row) do |row|
|
312
|
-
@nxid = generate_nxid(
|
312
|
+
@nxid = generate_nxid(nexpose_identifier_id, nil, row['ip_address'])
|
313
313
|
# Query Jira for the ticket by unique id (generated NXID)
|
314
314
|
queried_key = get_jira_key("jql=project=#{@jira_data[:project]} AND description ~ \"NXID: #{@nxid}\" AND (status != #{@jira_data[:close_step_name]})&fields=key")
|
315
315
|
if queried_key.nil? || queried_key.empty?
|
@@ -365,16 +365,16 @@ class JiraHelper
|
|
365
365
|
# per IP i.e. any vulnerabilities for a single IP in one ticket
|
366
366
|
#
|
367
367
|
# - +vulnerability_list+ - CSV of vulnerabilities within Nexpose.
|
368
|
-
# - +
|
368
|
+
# - +nexpose_identifier_id+ - Site/TAG ID the vulnerability list was generate from.
|
369
369
|
#
|
370
370
|
# * *Returns* :
|
371
371
|
# - List of JSON-formated tickets for updating within Jira.
|
372
372
|
#
|
373
|
-
def prepare_update_tickets(vulnerability_list,
|
373
|
+
def prepare_update_tickets(vulnerability_list, nexpose_identifier_id)
|
374
374
|
fail 'Ticket updates are only supported in IP-address mode.' if @options[:ticket_mode] != 'I'
|
375
375
|
@log.log_message('Preparing tickets to update.')
|
376
376
|
#Jira uses the ticket key to push updates. Since new IPs won't have a Jira key, generate new tickets for all of the IPs found.
|
377
|
-
updated_tickets = prepare_tickets_by_ip(vulnerability_list,
|
377
|
+
updated_tickets = prepare_tickets_by_ip(vulnerability_list, nexpose_identifier_id)
|
378
378
|
tickets_to_send = []
|
379
379
|
|
380
380
|
#Find the keys that exist (IPs that have tickets already)
|
@@ -162,18 +162,18 @@ class RemedyHelper
|
|
162
162
|
# * *Returns* :
|
163
163
|
# - List of savon-formated (hash) tickets for creating within Remedy.
|
164
164
|
#
|
165
|
-
def prepare_create_tickets(vulnerability_list,
|
165
|
+
def prepare_create_tickets(vulnerability_list, nexpose_identifier_id)
|
166
166
|
@ticket = Hash.new(-1)
|
167
167
|
case @options[:ticket_mode]
|
168
168
|
# 'D' Default mode: IP *-* Vulnerability
|
169
169
|
when 'D'
|
170
|
-
prepare_create_tickets_default(vulnerability_list,
|
170
|
+
prepare_create_tickets_default(vulnerability_list, nexpose_identifier_id)
|
171
171
|
# 'I' IP address mode: IP address -* Vulnerability
|
172
172
|
when 'I'
|
173
|
-
prepare_create_tickets_by_ip(vulnerability_list,
|
173
|
+
prepare_create_tickets_by_ip(vulnerability_list, nexpose_identifier_id)
|
174
174
|
# 'V' Vulnerability mode: Vulnerability -* IP address
|
175
175
|
when 'V'
|
176
|
-
prepare_create_tickets_by_vulnerability(vulnerability_list,
|
176
|
+
prepare_create_tickets_by_vulnerability(vulnerability_list, nexpose_identifier_id)
|
177
177
|
else
|
178
178
|
fail 'No ticketing mode selected.'
|
179
179
|
end
|
@@ -188,15 +188,15 @@ class RemedyHelper
|
|
188
188
|
# * *Returns* :
|
189
189
|
# - List of savon-formated (hash) tickets for creating within Remedy.
|
190
190
|
#
|
191
|
-
def prepare_update_tickets(vulnerability_list,
|
191
|
+
def prepare_update_tickets(vulnerability_list, nexpose_identifier_id)
|
192
192
|
@ticket = Hash.new(-1)
|
193
193
|
case @options[:ticket_mode]
|
194
194
|
# 'I' IP address mode: IP address -* Vulnerability
|
195
195
|
when 'I'
|
196
|
-
prepare_update_tickets_by_ip(vulnerability_list,
|
196
|
+
prepare_update_tickets_by_ip(vulnerability_list, nexpose_identifier_id)
|
197
197
|
# 'V' Vulnerability mode: Vulnerability -* IP address
|
198
198
|
when 'V'
|
199
|
-
prepare_update_tickets_by_vulnerability(vulnerability_list,
|
199
|
+
prepare_update_tickets_by_vulnerability(vulnerability_list, nexpose_identifier_id)
|
200
200
|
else
|
201
201
|
fail 'No ticketing mode selected.'
|
202
202
|
end
|
@@ -213,7 +213,7 @@ class RemedyHelper
|
|
213
213
|
# * *Returns* :
|
214
214
|
# - List of savon-formated (hash) tickets for creating within Remedy.
|
215
215
|
#
|
216
|
-
def prepare_create_tickets_default(vulnerability_list,
|
216
|
+
def prepare_create_tickets_default(vulnerability_list, nexpose_identifier_id)
|
217
217
|
@log.log_message("Preparing tickets by default method.")
|
218
218
|
tickets = []
|
219
219
|
CSV.parse(vulnerability_list.chomp, headers: :first_row) do |row|
|
@@ -229,7 +229,7 @@ class RemedyHelper
|
|
229
229
|
'Action' => 'CREATE',
|
230
230
|
'Summary' => "#{row['ip_address']} => #{row['summary']}",
|
231
231
|
'Notes' => "Summary: #{row['summary']} \n\nFix: #{row['fix']} \n\nURL: #{row['url']}
|
232
|
-
\n\nNXID: #{
|
232
|
+
\n\nNXID: #{nexpose_identifier_id}#{row['asset_id']}#{row['vulnerability_id']}#{row['solution_id']}",
|
233
233
|
'Urgency' => '1-Critical'
|
234
234
|
}
|
235
235
|
tickets.push(ticket)
|
@@ -248,14 +248,14 @@ class RemedyHelper
|
|
248
248
|
# * *Returns* :
|
249
249
|
# - List of savon-formated (hash) tickets for creating within Remedy.
|
250
250
|
#
|
251
|
-
def prepare_create_tickets_by_ip(vulnerability_list,
|
251
|
+
def prepare_create_tickets_by_ip(vulnerability_list, nexpose_identifier_id)
|
252
252
|
@log.log_message('Preparing tickets by IP address.')
|
253
253
|
tickets = []
|
254
254
|
current_ip = -1
|
255
255
|
CSV.parse(vulnerability_list.chomp, headers: :first_row) do |row|
|
256
256
|
if current_ip == -1
|
257
257
|
current_ip = row['ip_address']
|
258
|
-
@log.log_message("Creating ticket with IP address: #{row['ip_address']}, Asset ID: #{row['asset_id']} and
|
258
|
+
@log.log_message("Creating ticket with IP address: #{row['ip_address']}, Asset ID: #{row['asset_id']} and Nexpose Identifier ID: #{nexpose_identifier_id}")
|
259
259
|
@ticket = {
|
260
260
|
'First_Name' => "#{@remedy_data[:first_name]}",
|
261
261
|
'Impact' => '1-Extensive/Widespread',
|
@@ -279,14 +279,14 @@ class RemedyHelper
|
|
279
279
|
end
|
280
280
|
unless current_ip == row['ip_address']
|
281
281
|
# NXID in the work_notes is the unique identifier used to query incidents to update them.
|
282
|
-
@ticket['Notes'] += "\n\nNXID: #{
|
282
|
+
@ticket['Notes'] += "\n\nNXID: #{nexpose_identifier_id}#{current_ip}"
|
283
283
|
tickets.push(@ticket)
|
284
284
|
current_ip = -1
|
285
285
|
redo
|
286
286
|
end
|
287
287
|
end
|
288
288
|
# NXID in the work_notes is the unique identifier used to query incidents to update them.
|
289
|
-
@ticket['Notes'] += "\n\nNXID: #{
|
289
|
+
@ticket['Notes'] += "\n\nNXID: #{nexpose_identifier_id}#{current_ip}"
|
290
290
|
tickets.push(@ticket) unless @ticket.nil?
|
291
291
|
tickets
|
292
292
|
end
|
@@ -303,7 +303,7 @@ class RemedyHelper
|
|
303
303
|
# * *Returns* :
|
304
304
|
# - List of savon-formated (hash) tickets for creating within Remedy.
|
305
305
|
#
|
306
|
-
def prepare_create_tickets_by_vulnerability(vulnerability_list,
|
306
|
+
def prepare_create_tickets_by_vulnerability(vulnerability_list, nexpose_identifier_id)
|
307
307
|
@log.log_message("Preparing tickets by vulnerability.")
|
308
308
|
tickets = []
|
309
309
|
current_vuln_id = -1
|
@@ -317,7 +317,7 @@ class RemedyHelper
|
|
317
317
|
current_vuln_id = row['vulnerability_id']
|
318
318
|
current_solution_id = row['solution_id']
|
319
319
|
current_asset_id = row['asset_id']
|
320
|
-
@log.log_message("Creating ticket with vulnerability id: #{row['vulnerability_id']}, Asset ID: #{row['asset_id']} and
|
320
|
+
@log.log_message("Creating ticket with vulnerability id: #{row['vulnerability_id']}, Asset ID: #{row['asset_id']} and Nexpose Identifier ID: #{nexpose_identifier_id}")
|
321
321
|
summary = "Vulnerability: #{row['title']}"
|
322
322
|
|
323
323
|
#Remedy has a summary field max size of 100 so truncate any summaries that are larger than that and place the full summary in the notes.
|
@@ -373,7 +373,7 @@ class RemedyHelper
|
|
373
373
|
end
|
374
374
|
unless current_vuln_id == row['vulnerability_id']
|
375
375
|
# NXID in the work_notes is the unique identifier used to query incidents to update them.
|
376
|
-
@ticket['Notes'] += "\n\nNXID: #{
|
376
|
+
@ticket['Notes'] += "\n\nNXID: #{nexpose_identifier_id}#{current_asset_id}#{current_vuln_id}"
|
377
377
|
current_vuln_id = -1
|
378
378
|
current_solution_id = -1
|
379
379
|
current_asset_id = -1
|
@@ -383,7 +383,7 @@ class RemedyHelper
|
|
383
383
|
end
|
384
384
|
end
|
385
385
|
# NXID in the work_notes is the unique identifier used to query incidents to update them.
|
386
|
-
@ticket['Notes'] += "\n\nNXID: #{
|
386
|
+
@ticket['Notes'] += "\n\nNXID: #{nexpose_identifier_id}#{current_asset_id}#{current_vuln_id}"
|
387
387
|
@ticket = format_notes_by_vulnerability(@ticket, full_summary)
|
388
388
|
tickets.push(@ticket) unless @ticket.nil?
|
389
389
|
tickets
|
@@ -413,7 +413,7 @@ class RemedyHelper
|
|
413
413
|
# * *Returns* :
|
414
414
|
# - List of savon-formated (hash) tickets for updating within Remedy.
|
415
415
|
#
|
416
|
-
def prepare_update_tickets_by_ip(vulnerability_list,
|
416
|
+
def prepare_update_tickets_by_ip(vulnerability_list, nexpose_identifier_id)
|
417
417
|
fail 'Ticket updates are only supported in IP-address mode.' if @options[:ticket_mode] != 'I'
|
418
418
|
@ticket = Hash.new(-1)
|
419
419
|
|
@@ -427,11 +427,11 @@ class RemedyHelper
|
|
427
427
|
ticket_status = row['comparison']
|
428
428
|
|
429
429
|
# Query Remedy for the incident by unique id (generated NXID)
|
430
|
-
queried_incident = query_for_ticket("NXID: #{
|
430
|
+
queried_incident = query_for_ticket("NXID: #{nexpose_identifier_id}#{row['ip_address']}")
|
431
431
|
if queried_incident.nil? || queried_incident.empty?
|
432
|
-
@log.log_message("No incident found for NXID: #{
|
432
|
+
@log.log_message("No incident found for NXID: #{nexpose_identifier_id}#{row['asset_id']}#{row['vulnerability_id']}#{row['solution_id']}")
|
433
433
|
else
|
434
|
-
@log.log_message("Creating ticket update with IP address: #{row['ip_address']} for
|
434
|
+
@log.log_message("Creating ticket update with IP address: #{row['ip_address']} for Nexpose Identifier with ID: #{nexpose_identifier_id}")
|
435
435
|
@log.log_message("Ticket status #{ticket_status}")
|
436
436
|
# Remedy incident updates require populating all fields.
|
437
437
|
@ticket = extract_queried_incident(queried_incident, "++ #{row['comparison']} Vulnerabilities ++++++++++++++++++++++++++\n")
|
@@ -455,14 +455,14 @@ class RemedyHelper
|
|
455
455
|
end
|
456
456
|
unless current_ip == row['ip_address']
|
457
457
|
# NXID in the work_notes is the unique identifier used to query incidents to update them.
|
458
|
-
@ticket['Notes'] += "\n\nNXID: #{
|
458
|
+
@ticket['Notes'] += "\n\nNXID: #{nexpose_identifier_id}#{current_ip}"
|
459
459
|
tickets.push(@ticket)
|
460
460
|
current_ip = -1
|
461
461
|
redo
|
462
462
|
end
|
463
463
|
end
|
464
464
|
# NXID in the work_notes is the unique identifier used to query incidents to update them.
|
465
|
-
@ticket['Notes'] += "\n\nNXID: #{
|
465
|
+
@ticket['Notes'] += "\n\nNXID: #{nexpose_identifier_id}#{current_ip}" unless @ticket.empty?
|
466
466
|
tickets.push(@ticket) unless @ticket.nil? || @ticket.empty?
|
467
467
|
tickets
|
468
468
|
end
|
@@ -477,7 +477,7 @@ class RemedyHelper
|
|
477
477
|
# * *Returns* :
|
478
478
|
# - List of savon-formated (hash) tickets for updating within Remedy.
|
479
479
|
#
|
480
|
-
def prepare_update_tickets_by_vulnerability(vulnerability_list,
|
480
|
+
def prepare_update_tickets_by_vulnerability(vulnerability_list, nexpose_identifier_id)
|
481
481
|
fail 'Ticket updates are only supported in IP-address mode.' if @options[:ticket_mode] != 'V'
|
482
482
|
@ticket = Hash.new(-1)
|
483
483
|
|
@@ -497,12 +497,12 @@ class RemedyHelper
|
|
497
497
|
current_solution_id = -1
|
498
498
|
|
499
499
|
# Query Remedy for the incident by unique id (generated NXID)
|
500
|
-
queried_incident = query_for_ticket("NXID: #{
|
500
|
+
queried_incident = query_for_ticket("NXID: #{nexpose_identifier_id}#{row['asset_id']}#{row['vulnerability_id']}")
|
501
501
|
if queried_incident.nil? || queried_incident.empty?
|
502
|
-
@log.log_message("No incident found for NXID: #{
|
502
|
+
@log.log_message("No incident found for NXID: #{nexpose_identifier_id}#{row['asset_id']}#{row['vulnerability_id']}#{row['solution_id']}. Creating...")
|
503
503
|
new_ticket_csv = vulnerability_list.split("\n").first
|
504
504
|
new_ticket_csv += "\n#{row.to_s}"
|
505
|
-
new_ticket = prepare_create_tickets_by_vulnerability(new_ticket_csv,
|
505
|
+
new_ticket = prepare_create_tickets_by_vulnerability(new_ticket_csv, nexpose_identifier_id)
|
506
506
|
@log.log_message('Created ticket. Sending to Remedy...')
|
507
507
|
create_tickets(new_ticket)
|
508
508
|
@log.log_message('Ticket sent. Performing update for ticket...')
|
@@ -510,7 +510,7 @@ class RemedyHelper
|
|
510
510
|
current_vuln_id = -1
|
511
511
|
redo
|
512
512
|
else
|
513
|
-
@log.log_message("Creating ticket update for vulnerability with ID: #{row['vulnerability_id']}, Asset ID: #{row['asset_id']} and
|
513
|
+
@log.log_message("Creating ticket update for vulnerability with ID: #{row['vulnerability_id']}, Asset ID: #{row['asset_id']} and Nexpose Identifier ID: #{nexpose_identifier_id}. Ticket status #{ticket_status}.")
|
514
514
|
# Remedy incident updates require populating all fields.
|
515
515
|
@ticket = extract_queried_incident(queried_incident, "++ #{row['comparison']} Assets ++++++++++++++++++++++\n")
|
516
516
|
end
|
@@ -548,7 +548,7 @@ class RemedyHelper
|
|
548
548
|
unless current_vuln_id == row['vulnerability_id']
|
549
549
|
# NXID in the work_notes is the unique identifier used to query incidents to update them.
|
550
550
|
@ticket['Notes'] += "\n\n" + current_solutions_text
|
551
|
-
@ticket['Notes'] += "\n\nNXID: #{
|
551
|
+
@ticket['Notes'] += "\n\nNXID: #{nexpose_identifier_id}#{current_asset_id}#{current_vuln_id}"
|
552
552
|
tickets.push(@ticket)
|
553
553
|
current_vuln_id = -1
|
554
554
|
current_solution_id = -1
|
@@ -558,7 +558,7 @@ class RemedyHelper
|
|
558
558
|
end
|
559
559
|
# NXID in the work_notes is the unique identifier used to query incidents to update them.
|
560
560
|
@ticket['Notes'] += current_solutions_text
|
561
|
-
@ticket['Notes'] += "\n\nNXID: #{
|
561
|
+
@ticket['Notes'] += "\n\nNXID: #{nexpose_identifier_id}#{current_asset_id}#{current_vuln_id}" unless @ticket.empty?
|
562
562
|
tickets.push(@ticket) unless @ticket.nil? || @ticket.empty?
|
563
563
|
tickets
|
564
564
|
end
|
@@ -692,7 +692,7 @@ class RemedyHelper
|
|
692
692
|
# * *Returns* :
|
693
693
|
# - List of savon-formated (hash) tickets for closing within Remedy.
|
694
694
|
#
|
695
|
-
def prepare_close_tickets(vulnerability_list,
|
695
|
+
def prepare_close_tickets(vulnerability_list, nexpose_identifier_id)
|
696
696
|
fail 'Ticket closures are only supported in default mode.' if @options[:ticket_mode] == 'I'
|
697
697
|
@log.log_message('Preparing ticket closures by default method.')
|
698
698
|
@nxid = nil
|
@@ -701,13 +701,13 @@ class RemedyHelper
|
|
701
701
|
case @options[:ticket_mode]
|
702
702
|
# 'D' Default mode: IP *-* Vulnerability
|
703
703
|
when 'D'
|
704
|
-
@nxid = "#{
|
704
|
+
@nxid = "#{nexpose_identifier_id}#{row['asset_id']}#{row['vulnerability_id']}#{row['solution_id']}"
|
705
705
|
# 'I' IP address mode: IP address -* Vulnerability
|
706
706
|
when 'I'
|
707
|
-
@nxid = "#{
|
707
|
+
@nxid = "#{nexpose_identifier_id}#{row['current_ip']}"
|
708
708
|
# 'V' Vulnerability mode: Vulnerability -* IP address
|
709
709
|
when 'V'
|
710
|
-
@nxid = "#{
|
710
|
+
@nxid = "#{nexpose_identifier_id}#{row['current_asset_id']}#{row['current_vuln_id']}"
|
711
711
|
else
|
712
712
|
fail 'Could not close tickets - do not understand the ticketing mode!'
|
713
713
|
end
|
@@ -52,15 +52,15 @@ class ServiceDeskHelper
|
|
52
52
|
db.close()
|
53
53
|
end
|
54
54
|
|
55
|
-
def prepare_create_tickets(vulnerability_list,
|
55
|
+
def prepare_create_tickets(vulnerability_list, nexpose_identifier_id)
|
56
56
|
@log.log_message('Preparing ticket requests...')
|
57
57
|
case @options[:ticket_mode]
|
58
58
|
# 'D' Default mode: IP *-* Vulnerability
|
59
59
|
when 'D'
|
60
|
-
tickets = create_tickets_by_default(vulnerability_list,
|
60
|
+
tickets = create_tickets_by_default(vulnerability_list, nexpose_identifier_id)
|
61
61
|
# 'I' IP address mode: IP address -* Vulnerability
|
62
62
|
when 'I'
|
63
|
-
tickets = create_tickets_by_ip(vulnerability_list,
|
63
|
+
tickets = create_tickets_by_ip(vulnerability_list, nexpose_identifier_id)
|
64
64
|
else
|
65
65
|
fail 'No ticketing mode selected.'
|
66
66
|
end
|
@@ -143,27 +143,27 @@ class ServiceDeskHelper
|
|
143
143
|
end
|
144
144
|
|
145
145
|
## Given a bunch of vulnerabilities that passed the filters, make tickets for each one
|
146
|
-
def create_tickets_by_default(vulnerability_list,
|
146
|
+
def create_tickets_by_default(vulnerability_list, nexpose_identifier_id)
|
147
147
|
@log.log_message('Preparing tickets by vulnerability...')
|
148
148
|
tickets = []
|
149
149
|
CSV.parse( vulnerability_list.chomp, headers: :first_row ) do |vuln|
|
150
150
|
subject = "#{vuln['ip_address']}: #{vuln['summary']}"
|
151
151
|
description = "Host: #{ip_address}\nSummary: #{vuln['summary']}\nFix: #{vuln['fix']}\nURL: #{vuln['url']}"
|
152
152
|
|
153
|
-
tickets << { :action => :create, :nxid => "#{
|
153
|
+
tickets << { :action => :create, :nxid => "#{nexpose_identifier_id}#{vuln['asset_id']}#{vuln['vulnerability_id']}#{vuln['solution_id']}",
|
154
154
|
:description => create_ticket_request( subject, description ) }
|
155
155
|
end
|
156
156
|
return tickets
|
157
157
|
end
|
158
158
|
|
159
159
|
|
160
|
-
def create_tickets_by_ip(vulnerability_list,
|
160
|
+
def create_tickets_by_ip(vulnerability_list, nexpose_identifier_id)
|
161
161
|
@log.log_message('Preparing tickets by ip')
|
162
162
|
tickets = []
|
163
163
|
hostVulns = {}
|
164
164
|
CSV.parse( vulnerability_list.chomp, headers: :first_row ) do |vuln|
|
165
|
-
hostVulns["#{
|
166
|
-
hostVulns["#{
|
165
|
+
hostVulns["#{nexpose_identifier_id}#{vuln['ip_address']}"] = { :ip => vuln['ip_address'], :description => "" } if not hostVulns.has_key?("#{nexpose_identifier_id}#{vuln['ip_address']}")
|
166
|
+
hostVulns["#{nexpose_identifier_id}#{vuln['ip_address']}"][:description] += "Summary: #{vuln['summary']}\nFix: #{vuln['fix']}\nURL: #{vuln['url']}\n\n"
|
167
167
|
end
|
168
168
|
|
169
169
|
hostVulns.each do |nxid, vulnInfo|
|
@@ -260,15 +260,15 @@ class ServiceDeskHelper
|
|
260
260
|
end
|
261
261
|
|
262
262
|
|
263
|
-
def prepare_update_tickets(vulnerability_list,
|
263
|
+
def prepare_update_tickets(vulnerability_list, nexpose_identifier_id)
|
264
264
|
fail 'Ticket updates are only supported in IP-address mode.' if @options[:ticket_mode] != 'I'
|
265
265
|
|
266
266
|
@log.log_message('Preparing ticket updates by IP address.')
|
267
267
|
tickets = []
|
268
268
|
hostVulns={}
|
269
269
|
CSV.parse( vulnerability_list.chomp, headers: :first_row ) do |vuln|
|
270
|
-
hostVulns["#{
|
271
|
-
hostVulns["#{
|
270
|
+
hostVulns["#{nexpose_identifier_id}#{vuln['ip_address']}"] = { :ip => vuln['ip_address'], :description => "" } if not hostVulns.has_key?(vuln['asset_id'])
|
271
|
+
hostVulns["#{nexpose_identifier_id}#{vuln['ip_address']}"][:description] += "Summary: #{vuln['summary']}\nFix: #{vuln['fix']}\nURL: #{vuln['url']}\n\n"
|
272
272
|
end
|
273
273
|
|
274
274
|
hostVulns.each do |nxid, vulnInfo|
|
@@ -308,7 +308,7 @@ class ServiceDeskHelper
|
|
308
308
|
# * *Returns* :
|
309
309
|
# - List of savon-formated (hash) tickets for closing within ServiceDesk.
|
310
310
|
#
|
311
|
-
def prepare_close_tickets(vulnerability_list,
|
311
|
+
def prepare_close_tickets(vulnerability_list, nexpose_identifier_id)
|
312
312
|
fail 'Ticket closures are only supported in default mode.' if @options[:ticket_mode] == 'I'
|
313
313
|
@log.log_message('Preparing ticket closures by default method.')
|
314
314
|
@nxid = nil
|
@@ -317,13 +317,13 @@ class ServiceDeskHelper
|
|
317
317
|
case @options[:ticket_mode]
|
318
318
|
# 'D' Default mode: IP *-* Vulnerability
|
319
319
|
when 'D'
|
320
|
-
@nxid = "#{
|
320
|
+
@nxid = "#{nexpose_identifier_id}#{row['asset_id']}#{row['vulnerability_id']}#{row['solution_id']}"
|
321
321
|
# 'I' IP address mode: IP address -* Vulnerability
|
322
322
|
when 'I'
|
323
|
-
@nxid = "#{
|
323
|
+
@nxid = "#{nexpose_identifier_id}#{row['current_ip']}"
|
324
324
|
# 'V' Vulnerability mode: Vulnerability -* IP address
|
325
325
|
# when 'V'
|
326
|
-
# @NXID = "#{
|
326
|
+
# @NXID = "#{nexpose_identifier_id}#{row['current_asset_id']}#{row['current_vuln_id']}"
|
327
327
|
else
|
328
328
|
fail 'Could not close tickets - do not understand the ticketing mode!'
|
329
329
|
end
|