nexpose_ticketing 1.0.2 → 1.2.1
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/README.md +22 -0
- data/bin/nexpose_ticketing +45 -0
- data/lib/nexpose_ticketing/config/jira.config +5 -1
- data/lib/nexpose_ticketing/config/remedy.config +4 -1
- data/lib/nexpose_ticketing/config/servicedesk.config +4 -1
- data/lib/nexpose_ticketing/config/servicenow.config +4 -1
- data/lib/nexpose_ticketing/config/ticket_service.config +13 -5
- data/lib/nexpose_ticketing/helpers/base_helper.rb +43 -0
- data/lib/nexpose_ticketing/helpers/jira_helper.rb +149 -97
- data/lib/nexpose_ticketing/helpers/remedy_helper.rb +37 -52
- data/lib/nexpose_ticketing/helpers/servicedesk_helper.rb +30 -49
- data/lib/nexpose_ticketing/helpers/servicenow_helper.rb +39 -56
- data/lib/nexpose_ticketing/modes/base_mode.rb +199 -0
- data/lib/nexpose_ticketing/modes/default_mode.rb +50 -0
- data/lib/nexpose_ticketing/modes/ip_mode.rb +52 -0
- data/lib/nexpose_ticketing/modes/vulnerability_mode.rb +50 -0
- data/lib/nexpose_ticketing/nx_logger.rb +44 -33
- data/lib/nexpose_ticketing/queries.rb +30 -27
- data/lib/nexpose_ticketing/report_helper.rb +1 -0
- data/lib/nexpose_ticketing/ticket_metrics.rb +39 -0
- data/lib/nexpose_ticketing/ticket_repository.rb +65 -206
- data/lib/nexpose_ticketing/ticket_service.rb +470 -441
- data/lib/nexpose_ticketing/version.rb +1 -1
- metadata +15 -16
- data/bin/nexpose_jira +0 -27
- data/bin/nexpose_remedy +0 -27
- data/bin/nexpose_servicedesk +0 -27
- data/bin/nexpose_servicenow +0 -27
- data/lib/nexpose_ticketing/common_helper.rb +0 -344
@@ -6,17 +6,14 @@ require 'csv'
|
|
6
6
|
require 'savon'
|
7
7
|
require 'nexpose_ticketing/nx_logger'
|
8
8
|
require 'nexpose_ticketing/version'
|
9
|
-
|
9
|
+
require_relative './base_helper'
|
10
10
|
|
11
11
|
# Serves as the Remedy interface for creating/updating issues from
|
12
12
|
# vulnelrabilities found in Nexpose.
|
13
|
-
class RemedyHelper
|
14
|
-
attr_accessor :
|
15
|
-
def initialize(
|
16
|
-
|
17
|
-
@options = options
|
18
|
-
@log = NexposeTicketing::NxLogger.instance
|
19
|
-
@common_helper = NexposeTicketing::CommonHelper.new(@options)
|
13
|
+
class RemedyHelper < BaseHelper
|
14
|
+
attr_accessor :log, :client
|
15
|
+
def initialize(service_data, options, mode)
|
16
|
+
super(service_data, options, mode)
|
20
17
|
end
|
21
18
|
|
22
19
|
# Generates a savon-based ticket object.
|
@@ -26,9 +23,9 @@ class RemedyHelper
|
|
26
23
|
#
|
27
24
|
def generate_new_ticket(extra_fields=nil)
|
28
25
|
base_ticket = {
|
29
|
-
'First_Name' => "#{@
|
26
|
+
'First_Name' => "#{@service_data[:first_name]}",
|
30
27
|
'Impact' => '1-Extensive/Widespread',
|
31
|
-
'Last_Name' => "#{@
|
28
|
+
'Last_Name' => "#{@service_data[:last_name]}",
|
32
29
|
'Reported_Source' => 'Other',
|
33
30
|
'Service_Type' => 'Infrastructure Event',
|
34
31
|
'Status' => 'New',
|
@@ -52,13 +49,13 @@ class RemedyHelper
|
|
52
49
|
Savon.client(wsdl: File.join(File.dirname(__FILE__), "../config/remedy_wsdl/#{wdsl}"),
|
53
50
|
adapter: :net_http,
|
54
51
|
ssl_verify_mode: :none,
|
55
|
-
open_timeout: @
|
56
|
-
read_timeout: @
|
57
|
-
endpoint: @
|
52
|
+
open_timeout: @service_data[:open_timeout],
|
53
|
+
read_timeout: @service_data[:read_timeout],
|
54
|
+
endpoint: @service_data[endpoint.intern],
|
58
55
|
soap_header: { 'AuthenticationInfo' =>
|
59
|
-
{ 'userName' => "#{@
|
60
|
-
'password' => "#{@
|
61
|
-
'authentication' => "#{@
|
56
|
+
{ 'userName' => "#{@service_data[:username]}",
|
57
|
+
'password' => "#{@service_data[:password]}",
|
58
|
+
'authentication' => "#{@service_data[:authentication]}"
|
62
59
|
}
|
63
60
|
})
|
64
61
|
end
|
@@ -95,6 +92,7 @@ class RemedyHelper
|
|
95
92
|
#
|
96
93
|
def create_tickets(tickets)
|
97
94
|
fail 'Ticket(s) cannot be empty' if tickets.nil? || tickets.empty?
|
95
|
+
@metrics.created tickets.count
|
98
96
|
client = get_client('HPD_IncidentInterface_Create_WS.xml', :create_soap_endpoint)
|
99
97
|
send_tickets(client, :help_desk_submit_service, tickets)
|
100
98
|
end
|
@@ -110,6 +108,7 @@ class RemedyHelper
|
|
110
108
|
@log.log_message("No tickets to update.")
|
111
109
|
return
|
112
110
|
end
|
111
|
+
@metrics.updated tickets.count - @metrics.get_created
|
113
112
|
client = get_client('HPD_IncidentInterface_WS.xml', :query_modify_soap_endpoint)
|
114
113
|
send_tickets(client, :help_desk_modify_service, tickets)
|
115
114
|
end
|
@@ -125,6 +124,7 @@ class RemedyHelper
|
|
125
124
|
@log.log_message("No tickets to close.")
|
126
125
|
return
|
127
126
|
end
|
127
|
+
@metrics.closed tickets.count
|
128
128
|
client = get_client('HPD_IncidentInterface_WS.xml', :query_modify_soap_endpoint)
|
129
129
|
send_tickets(client, :help_desk_modify_service, tickets)
|
130
130
|
end
|
@@ -164,19 +164,9 @@ class RemedyHelper
|
|
164
164
|
# - List of savon-formated (hash) tickets for creating within Remedy.
|
165
165
|
#
|
166
166
|
def prepare_create_tickets(vulnerability_list, nexpose_identifier_id)
|
167
|
+
@metrics.start
|
167
168
|
@log.log_message('Preparing ticket requests...')
|
168
|
-
|
169
|
-
# 'D' Default IP *-* Vulnerability
|
170
|
-
when 'D' then matching_fields = ['ip_address', 'vulnerability_id']
|
171
|
-
# 'I' IP address -* Vulnerability
|
172
|
-
when 'I' then matching_fields = ['ip_address']
|
173
|
-
# 'V' Vulnerability -* Assets
|
174
|
-
when 'V' then matching_fields = ['vulnerability_id']
|
175
|
-
else
|
176
|
-
fail 'Unsupported ticketing mode selected.'
|
177
|
-
end
|
178
|
-
|
179
|
-
prepare_tickets(vulnerability_list, nexpose_identifier_id, matching_fields)
|
169
|
+
prepare_tickets(vulnerability_list, nexpose_identifier_id)
|
180
170
|
end
|
181
171
|
|
182
172
|
# Prepare to update tickets from the CSV of vulnerabilities exported from Nexpose. This method determines
|
@@ -189,18 +179,8 @@ class RemedyHelper
|
|
189
179
|
# - List of savon-formated (hash) tickets for creating within Remedy.
|
190
180
|
#
|
191
181
|
def prepare_update_tickets(vulnerability_list, nexpose_identifier_id)
|
192
|
-
|
193
|
-
|
194
|
-
when 'D' then fail 'Ticket updates are not supported in Default mode.'
|
195
|
-
# 'I' IP address -* Vulnerability
|
196
|
-
when 'I' then matching_fields = ['ip_address']
|
197
|
-
# 'V' Vulnerability -* Assets
|
198
|
-
when 'V' then matching_fields = ['vulnerability_id']
|
199
|
-
else
|
200
|
-
fail 'Unsupported ticketing mode selected.'
|
201
|
-
end
|
202
|
-
|
203
|
-
prepare_tickets(vulnerability_list, nexpose_identifier_id, matching_fields)
|
182
|
+
@metrics.start
|
183
|
+
prepare_tickets(vulnerability_list, nexpose_identifier_id)
|
204
184
|
end
|
205
185
|
|
206
186
|
# Prepares a list of vulnerabilities into a list of savon-formatted tickets (incidents) for
|
@@ -212,7 +192,8 @@ class RemedyHelper
|
|
212
192
|
# * *Returns* :
|
213
193
|
# - List of savon-formated (hash) tickets for creating within Remedy.
|
214
194
|
#
|
215
|
-
def prepare_tickets(vulnerability_list, nexpose_identifier_id
|
195
|
+
def prepare_tickets(vulnerability_list, nexpose_identifier_id)
|
196
|
+
matching_fields = @mode_helper.get_matching_fields
|
216
197
|
@ticket = Hash.new(-1)
|
217
198
|
|
218
199
|
@log.log_message("Preparing tickets for #{@options[:ticket_mode]} mode.")
|
@@ -222,19 +203,21 @@ class RemedyHelper
|
|
222
203
|
CSV.parse(vulnerability_list.chomp, headers: :first_row) do |row|
|
223
204
|
if previous_row.nil?
|
224
205
|
previous_row = row.dup
|
225
|
-
description = @
|
226
|
-
@ticket = generate_new_ticket({'Summary' => "#{@
|
206
|
+
description = @mode_helper.get_description(nexpose_identifier_id, row)
|
207
|
+
@ticket = generate_new_ticket({'Summary' => "#{@mode_helper.get_title(row)}"[0...100],
|
227
208
|
'Notes' => ""})
|
228
209
|
#Skip querying for ticket if it's the initial scan
|
229
210
|
next if row['comparison'].nil?
|
230
211
|
|
231
212
|
# Query Remedy for the incident by unique id (generated NXID)
|
232
|
-
queried_incident = query_for_ticket("NXID: #{@
|
213
|
+
queried_incident = query_for_ticket("NXID: #{@mode_helper.get_nxid(nexpose_identifier_id, row)}")
|
233
214
|
if !queried_incident.nil? && queried_incident.first.is_a?(Hash)
|
234
215
|
queried_incident.select! { |t| !['Closed', 'Resolved', 'Cancelled'].include?(t[:status]) }
|
235
216
|
end
|
217
|
+
|
236
218
|
if queried_incident.nil? || queried_incident.empty?
|
237
|
-
@log.log_message("No incident found for NXID: #{@
|
219
|
+
@log.log_message("No incident found for NXID: #{@mode_helper.get_nxid(nexpose_identifier_id, row)}. Creating...")
|
220
|
+
|
238
221
|
new_ticket_csv = vulnerability_list.split("\n").first
|
239
222
|
new_ticket_csv += "\n#{row.to_s}"
|
240
223
|
|
@@ -250,28 +233,30 @@ class RemedyHelper
|
|
250
233
|
previous_row = nil
|
251
234
|
redo
|
252
235
|
else
|
253
|
-
info = @
|
236
|
+
info = @mode_helper.get_field_info(matching_fields, previous_row)
|
254
237
|
@log.log_message("Creating ticket update with #{info} for Nexpose Identifier with ID: #{nexpose_identifier_id}")
|
255
238
|
@log.log_message("Ticket status #{row['comparison']}")
|
256
239
|
# Remedy incident updates require populating all fields.
|
257
240
|
@ticket = extract_queried_incident(queried_incident, "")
|
258
241
|
end
|
259
242
|
elsif matching_fields.any? { |x| previous_row[x].nil? || previous_row[x] != row[x] }
|
260
|
-
info = @
|
243
|
+
info = @mode_helper.get_field_info(matching_fields, previous_row)
|
261
244
|
@log.log_message("Generated ticket with #{info}")
|
262
245
|
|
263
|
-
@ticket['Notes'] = @
|
246
|
+
@ticket['Notes'] = @mode_helper.print_description(description)
|
264
247
|
tickets.push(@ticket)
|
265
248
|
previous_row = nil
|
266
249
|
description = nil
|
267
250
|
redo
|
268
251
|
else
|
269
|
-
description = @
|
252
|
+
description = @mode_helper.update_description(description, row)
|
270
253
|
end
|
271
254
|
end
|
272
255
|
|
273
256
|
unless @ticket.nil? || @ticket.empty?
|
274
|
-
|
257
|
+
info = @mode_helper.get_field_info(matching_fields, previous_row)
|
258
|
+
@log.log_message("Creating ticket update with #{info} for Nexpose Identifier with ID: #{nexpose_identifier_id}")
|
259
|
+
@ticket['Notes'] = @mode_helper.print_description(description)
|
275
260
|
tickets.push(@ticket)
|
276
261
|
end
|
277
262
|
|
@@ -351,7 +336,7 @@ class RemedyHelper
|
|
351
336
|
return ticket_from_queried_incident(queried_incident, notes_header, nil)
|
352
337
|
end
|
353
338
|
|
354
|
-
fail "Multiple tickets returned for same NXID" if
|
339
|
+
fail "Multiple tickets returned for same NXID" if queried_incident.count > 1
|
355
340
|
ticket_from_queried_incident(queried_incident.first, notes_header, nil)
|
356
341
|
end
|
357
342
|
|
@@ -368,7 +353,7 @@ class RemedyHelper
|
|
368
353
|
@nxid = nil
|
369
354
|
tickets = []
|
370
355
|
CSV.parse(vulnerability_list.chomp, headers: :first_row) do |row|
|
371
|
-
@nxid = @
|
356
|
+
@nxid = @mode_helper.get_nxid(nexpose_identifier_id, row)
|
372
357
|
|
373
358
|
# Query Remedy for the incident by unique id (generated NXID)
|
374
359
|
queried_incident = query_for_ticket("NXID: #{@nxid}")
|
@@ -1,22 +1,19 @@
|
|
1
1
|
require 'net/http'
|
2
2
|
require 'nokogiri'
|
3
3
|
require 'dbm'
|
4
|
-
|
4
|
+
require_relative './base_helper'
|
5
5
|
require 'nexpose_ticketing/nx_logger'
|
6
6
|
require 'nexpose_ticketing/version'
|
7
7
|
|
8
|
-
class ServiceDeskHelper
|
9
|
-
attr_accessor :
|
8
|
+
class ServiceDeskHelper < BaseHelper
|
9
|
+
attr_accessor :log
|
10
10
|
|
11
|
-
def initialize(
|
12
|
-
|
13
|
-
@options = options
|
14
|
-
@log = NexposeTicketing::NxLogger.instance
|
11
|
+
def initialize(service_data, options, mode)
|
12
|
+
super(service_data, options, mode)
|
15
13
|
|
16
|
-
@rest_uri =
|
17
|
-
@api_key =
|
18
|
-
@ticket_db_path =
|
19
|
-
@common_helper = NexposeTicketing::CommonHelper.new(@options)
|
14
|
+
@rest_uri = service_data[:rest_uri]
|
15
|
+
@api_key = service_data[:api_key]
|
16
|
+
@ticket_db_path = service_data[:ticket_db_path]
|
20
17
|
end
|
21
18
|
|
22
19
|
|
@@ -62,26 +59,17 @@ class ServiceDeskHelper
|
|
62
59
|
|
63
60
|
def prepare_create_tickets(vulnerability_list, nexpose_identifier_id)
|
64
61
|
@log.log_message('Preparing ticket creation...')
|
65
|
-
|
66
|
-
# 'D' Default IP *-* Vulnerability
|
67
|
-
when 'D' then matching_fields = ['ip_address', 'vulnerability_id']
|
68
|
-
# 'I' IP address -* Vulnerability
|
69
|
-
when 'I' then matching_fields = ['ip_address']
|
70
|
-
# 'V' Vulnerability -* Assets
|
71
|
-
when 'V' then matching_fields = ['vulnerability_id']
|
72
|
-
else
|
73
|
-
fail 'Unsupported ticketing mode selected.'
|
74
|
-
end
|
75
|
-
|
76
|
-
tickets = prepare_tickets(vulnerability_list, nexpose_identifier_id, matching_fields)
|
62
|
+
tickets = prepare_tickets(vulnerability_list, nexpose_identifier_id)
|
77
63
|
|
78
64
|
tickets.each { |ticket| @log.log_message("Prepared ticket: #{ticket}")}
|
79
65
|
tickets
|
80
66
|
end
|
81
67
|
|
82
68
|
|
83
|
-
def prepare_tickets(vulnerability_list, nexpose_identifier_id
|
69
|
+
def prepare_tickets(vulnerability_list, nexpose_identifier_id)
|
70
|
+
@metrics.start
|
84
71
|
@log.log_message("Preparing ticket for #{@options[:ticket_mode]} mode.")
|
72
|
+
matching_fields = @mode_helper.get_matching_fields
|
85
73
|
tickets = []
|
86
74
|
host_vulns={}
|
87
75
|
previous_row = nil
|
@@ -94,39 +82,40 @@ class ServiceDeskHelper
|
|
94
82
|
initial_scan = initial_scan || row['comparison'].nil?
|
95
83
|
|
96
84
|
if previous_row.nil?
|
97
|
-
nxid = @
|
85
|
+
nxid = @mode_helper.get_nxid(nexpose_identifier_id, row)
|
98
86
|
previous_row = row.dup
|
99
|
-
description = @
|
87
|
+
description = @mode_helper.get_description(nexpose_identifier_id, row)
|
100
88
|
|
101
89
|
host_vulns[nxid] = { :ip => row['ip_address'],
|
102
90
|
:description => "",
|
103
|
-
:title => @
|
91
|
+
:title => @mode_helper.get_title(row) }
|
104
92
|
elsif matching_fields.any? { |x| previous_row[x].nil? || previous_row[x] != row[x] }
|
105
|
-
|
106
|
-
info = @common_helper.get_field_info(matching_fields, previous_row)
|
93
|
+
info = @mode_helper.get_field_info(matching_fields, previous_row)
|
107
94
|
@log.log_message("Generated ticket with #{info}")
|
108
95
|
|
109
|
-
host_vulns[nxid][:description] = @
|
96
|
+
host_vulns[nxid][:description] = @mode_helper.print_description(description)
|
110
97
|
previous_row = nil
|
111
98
|
description = nil
|
112
99
|
redo
|
113
100
|
else
|
114
|
-
description = @
|
101
|
+
description = @mode_helper.update_description(description, row)
|
115
102
|
end
|
116
103
|
end
|
117
104
|
|
118
105
|
unless host_vulns[nxid].nil? || host_vulns[nxid].empty?
|
119
|
-
host_vulns[nxid][:description] = @
|
106
|
+
host_vulns[nxid][:description] = @mode_helper.print_description(description)
|
120
107
|
end
|
121
108
|
|
122
109
|
host_vulns.each do |nxid, vuln_info|
|
123
110
|
workorderid = initial_scan ? nil : find_ticket_in_database(nxid)
|
124
111
|
if workorderid.nil? || workorderid.empty?
|
125
112
|
@log.log_message("Creating new incident for assetid #{nxid}")
|
113
|
+
@metrics.created
|
126
114
|
tickets << { :action => :create, :nxid => nxid,
|
127
115
|
:description => create_ticket_request(vuln_info[:title], vuln_info[:description]) }
|
128
116
|
else
|
129
117
|
@log.log_message("Updating incident for assetid #{nxid}")
|
118
|
+
@metrics.updated
|
130
119
|
tickets << { :action => :modify, :nxid => nxid,
|
131
120
|
:workorderid => workorderid,
|
132
121
|
:description => modify_ticket_request(vuln_info[:description]) }
|
@@ -145,7 +134,7 @@ class ServiceDeskHelper
|
|
145
134
|
xml.text 'requester'
|
146
135
|
}
|
147
136
|
xml.value {
|
148
|
-
xml.text @
|
137
|
+
xml.text @service_data[:requester]
|
149
138
|
}
|
150
139
|
}
|
151
140
|
xml.parameter {
|
@@ -153,7 +142,7 @@ class ServiceDeskHelper
|
|
153
142
|
xml.text 'Group'
|
154
143
|
}
|
155
144
|
xml.value {
|
156
|
-
xml.text @
|
145
|
+
xml.text @service_data[:group]
|
157
146
|
}
|
158
147
|
}
|
159
148
|
xml.parameter {
|
@@ -197,7 +186,7 @@ class ServiceDeskHelper
|
|
197
186
|
xml.text 'requester'
|
198
187
|
}
|
199
188
|
xml.value {
|
200
|
-
xml.text @
|
189
|
+
xml.text @service_data[:requester]
|
201
190
|
}
|
202
191
|
}
|
203
192
|
xml.parameter {
|
@@ -295,18 +284,7 @@ class ServiceDeskHelper
|
|
295
284
|
|
296
285
|
def prepare_update_tickets(vulnerability_list, nexpose_identifier_id)
|
297
286
|
@log.log_message('Preparing ticket updates...')
|
298
|
-
|
299
|
-
# 'D' Default IP *-* Vulnerability
|
300
|
-
when 'D' then fail 'Ticket updates are not supported in Default mode.'
|
301
|
-
# 'I' IP address -* Vulnerability
|
302
|
-
when 'I' then matching_fields = ['ip_address']
|
303
|
-
# 'V' Vulnerability -* Assets
|
304
|
-
when 'V' then matching_fields = ['vulnerability_id']
|
305
|
-
else
|
306
|
-
fail 'Unsupported ticketing mode selected.'
|
307
|
-
end
|
308
|
-
|
309
|
-
prepare_tickets(vulnerability_list, nexpose_identifier_id, matching_fields)
|
287
|
+
prepare_tickets(vulnerability_list, nexpose_identifier_id)
|
310
288
|
end
|
311
289
|
|
312
290
|
|
@@ -336,7 +314,7 @@ class ServiceDeskHelper
|
|
336
314
|
@nxid = nil
|
337
315
|
tickets = []
|
338
316
|
CSV.parse(vulnerability_list.chomp, headers: :first_row) do |row|
|
339
|
-
@nxid = @
|
317
|
+
@nxid = @mode_helper.get_nxid(nexpose_identifier_id, row)
|
340
318
|
|
341
319
|
workorderid = find_ticket_in_database(@nxid)
|
342
320
|
# Query ServiceDesk for the incident by unique id (generated NXID)
|
@@ -352,8 +330,11 @@ class ServiceDeskHelper
|
|
352
330
|
end
|
353
331
|
|
354
332
|
|
333
|
+
|
355
334
|
def close_tickets( tickets )
|
356
|
-
tickets.
|
335
|
+
to_close = tickets.select { |t| t[:action] == :close && !t[:workorderid].nil? }
|
336
|
+
@metrics.closed to_close.count
|
337
|
+
to_close.each { |ticket| close_ticket(ticket) }
|
357
338
|
remove_tickets_from_database(tickets)
|
358
339
|
end
|
359
340
|
end
|
@@ -5,18 +5,15 @@ require 'uri'
|
|
5
5
|
require 'csv'
|
6
6
|
require 'nexpose_ticketing/nx_logger'
|
7
7
|
require 'nexpose_ticketing/version'
|
8
|
-
|
8
|
+
require_relative './base_helper'
|
9
9
|
require 'securerandom'
|
10
10
|
|
11
11
|
# Serves as the ServiceNow interface for creating/updating issues from
|
12
12
|
# vulnelrabilities found in Nexpose.
|
13
|
-
class ServiceNowHelper
|
14
|
-
attr_accessor :
|
15
|
-
def initialize(
|
16
|
-
|
17
|
-
@options = options
|
18
|
-
@log = NexposeTicketing::NxLogger.instance
|
19
|
-
@common_helper = NexposeTicketing::CommonHelper.new(@options)
|
13
|
+
class ServiceNowHelper < BaseHelper
|
14
|
+
attr_accessor :log, :transform
|
15
|
+
def initialize(service_data, options, mode)
|
16
|
+
super(service_data, options, mode)
|
20
17
|
end
|
21
18
|
|
22
19
|
# Sends a list of tickets (in JSON format) to ServiceNow individually (each ticket in the list
|
@@ -29,7 +26,7 @@ class ServiceNowHelper
|
|
29
26
|
fail 'Ticket(s) cannot be empty' if tickets.nil? || tickets.empty?
|
30
27
|
|
31
28
|
tickets.each do |ticket|
|
32
|
-
send_ticket(ticket, @
|
29
|
+
send_ticket(ticket, @service_data[:servicenow_url], @service_data[:redirect_limit])
|
33
30
|
end
|
34
31
|
end
|
35
32
|
|
@@ -42,10 +39,10 @@ class ServiceNowHelper
|
|
42
39
|
def update_tickets(tickets)
|
43
40
|
if tickets.nil? || tickets.empty?
|
44
41
|
@log.log_message('No tickets to update.')
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
42
|
+
return
|
43
|
+
end
|
44
|
+
tickets.each do |ticket|
|
45
|
+
send_ticket(ticket, @service_data[:servicenow_url], @service_data[:redirect_limit])
|
49
46
|
end
|
50
47
|
end
|
51
48
|
|
@@ -56,12 +53,14 @@ class ServiceNowHelper
|
|
56
53
|
# - +tickets+ - List of JSON-formatted ticket closures.
|
57
54
|
#
|
58
55
|
def close_tickets(tickets)
|
56
|
+
@metrics.closed tickets.count
|
57
|
+
|
59
58
|
if tickets.nil? || tickets.empty?
|
60
59
|
@log.log_message('No tickets to close.')
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
60
|
+
return
|
61
|
+
end
|
62
|
+
tickets.each do |ticket|
|
63
|
+
send_ticket(ticket, @service_data[:servicenow_url], @service_data[:redirect_limit])
|
65
64
|
end
|
66
65
|
end
|
67
66
|
|
@@ -76,10 +75,10 @@ class ServiceNowHelper
|
|
76
75
|
|
77
76
|
#Get the address
|
78
77
|
query = "incident.do?JSONv2&sysparm_query=active=true^u_nxid=#{nxid}"
|
79
|
-
uri = URI.join(@
|
78
|
+
uri = URI.join(@service_data[:servicenow_url], '/')
|
80
79
|
full_url = URI.join(uri, "/").to_s + query
|
81
80
|
req = Net::HTTP::Get.new(full_url, headers)
|
82
|
-
req.basic_auth @
|
81
|
+
req.basic_auth @service_data[:username], @service_data[:password]
|
83
82
|
resp = Net::HTTP.new(uri.host, uri.port)
|
84
83
|
|
85
84
|
# Enable this line for debugging the https call.
|
@@ -132,12 +131,12 @@ class ServiceNowHelper
|
|
132
131
|
headers = { 'Content-Type' => 'application/json',
|
133
132
|
'Accept' => 'application/json' }
|
134
133
|
req = Net::HTTP::Post.new(url, headers)
|
135
|
-
req.basic_auth @
|
134
|
+
req.basic_auth @service_data[:username], @service_data[:password]
|
136
135
|
req.body = ticket
|
137
136
|
|
138
137
|
resp = Net::HTTP.new(uri.host, uri.port)
|
139
138
|
# Setting verbose_mode to 'Y' will debug the https call(s).
|
140
|
-
resp.set_debug_output $stderr if @
|
139
|
+
resp.set_debug_output $stderr if @service_data[:verbose_mode] == 'Y'
|
141
140
|
resp.use_ssl = true if uri.scheme == 'https'
|
142
141
|
# Currently, we do not verify SSL certificates (in case the local servicenow instance uses
|
143
142
|
# and unsigned or expired certificate)
|
@@ -162,19 +161,7 @@ class ServiceNowHelper
|
|
162
161
|
# - List of JSON-formated tickets for creating within ServiceNow.
|
163
162
|
#
|
164
163
|
def prepare_create_tickets(vulnerability_list, nexpose_identifier_id)
|
165
|
-
|
166
|
-
case @options[:ticket_mode]
|
167
|
-
# 'D' Default IP *-* Vulnerability
|
168
|
-
when 'D' then matching_fields = ['ip_address', 'vulnerability_id']
|
169
|
-
# 'I' IP address -* Vulnerability
|
170
|
-
when 'I' then matching_fields = ['ip_address']
|
171
|
-
# 'V' Vulnerability -* Assets
|
172
|
-
when 'V' then matching_fields = ['vulnerability_id']
|
173
|
-
else
|
174
|
-
fail 'Unsupported ticketing mode selected.'
|
175
|
-
end
|
176
|
-
|
177
|
-
prepare_tickets(vulnerability_list, nexpose_identifier_id, matching_fields)
|
164
|
+
prepare_tickets(vulnerability_list, nexpose_identifier_id)
|
178
165
|
end
|
179
166
|
|
180
167
|
# Prepares a list of vulnerabilities into a list of JSON-formatted tickets (incidents) for
|
@@ -186,7 +173,9 @@ class ServiceNowHelper
|
|
186
173
|
# * *Returns* :
|
187
174
|
# - List of JSON-formated tickets for creating within ServiceNow.
|
188
175
|
#
|
189
|
-
def prepare_tickets(vulnerability_list, nexpose_identifier_id
|
176
|
+
def prepare_tickets(vulnerability_list, nexpose_identifier_id)
|
177
|
+
@metrics.start
|
178
|
+
matching_fields = @mode_helper.get_matching_fields
|
190
179
|
@ticket = Hash.new(-1)
|
191
180
|
|
192
181
|
@log.log_message("Preparing tickets in #{options[:ticket_mode]} address.")
|
@@ -198,7 +187,7 @@ class ServiceNowHelper
|
|
198
187
|
CSV.parse(vulnerability_list.chomp, headers: :first_row) do |row|
|
199
188
|
if previous_row.nil?
|
200
189
|
previous_row = row.dup
|
201
|
-
nxid = @
|
190
|
+
nxid = @mode_helper.get_nxid(nexpose_identifier_id, row)
|
202
191
|
|
203
192
|
action = if row['comparison'].nil? || row['comparison'] == 'New'
|
204
193
|
'insert'
|
@@ -208,21 +197,21 @@ class ServiceNowHelper
|
|
208
197
|
|
209
198
|
@ticket = {
|
210
199
|
'sysparm_action' => action,
|
211
|
-
'caller_id' => "#{@
|
200
|
+
'caller_id' => "#{@service_data[:username]}",
|
212
201
|
'category' => 'Software',
|
213
202
|
'impact' => '1',
|
214
203
|
'urgency' => '1',
|
215
|
-
'short_description' => @
|
204
|
+
'short_description' => @mode_helper.get_title(row),
|
216
205
|
'work_notes' => "",
|
217
206
|
'u_nxid' => nxid,
|
218
207
|
'u_rpd_id' => nil
|
219
208
|
}
|
220
|
-
description = @
|
209
|
+
description = @mode_helper.get_description(nexpose_identifier_id, row)
|
221
210
|
elsif matching_fields.any? { |x| previous_row[x].nil? || previous_row[x] != row[x] }
|
222
|
-
info = @
|
211
|
+
info = @mode_helper.get_field_info(matching_fields, previous_row)
|
223
212
|
@log.log_message("Generated ticket with #{info}")
|
224
213
|
|
225
|
-
@ticket['work_notes'] = @
|
214
|
+
@ticket['work_notes'] = @mode_helper.print_description(description)
|
226
215
|
tickets.push(@ticket)
|
227
216
|
|
228
217
|
previous_row = nil
|
@@ -232,12 +221,14 @@ class ServiceNowHelper
|
|
232
221
|
unless row['comparison'].nil? || row['comparison'] == 'New'
|
233
222
|
@ticket['sysparm_action'] = 'update'
|
234
223
|
end
|
235
|
-
description = @
|
224
|
+
description = @mode_helper.update_description(description, row)
|
236
225
|
end
|
237
226
|
end
|
238
227
|
|
239
228
|
unless @ticket.nil? || @ticket.empty?
|
240
|
-
|
229
|
+
info = @mode_helper.get_field_info(matching_fields, previous_row)
|
230
|
+
@log.log_message("Generated ticket with #{info}")
|
231
|
+
@ticket['work_notes'] = @mode_helper.print_description(description) unless (@ticket.size == 0)
|
241
232
|
tickets.push(@ticket)
|
242
233
|
end
|
243
234
|
@log.log_message("Generated <#{tickets.count.to_s}> tickets.")
|
@@ -246,6 +237,9 @@ class ServiceNowHelper
|
|
246
237
|
if t['sysparm_action'] == 'update'
|
247
238
|
t['sysparm_action'] = 'insert'
|
248
239
|
t['u_rpd_id'] = get_ticket_identifier(t['u_nxid'])
|
240
|
+
@metrics.updated
|
241
|
+
else
|
242
|
+
@metrics.created
|
249
243
|
end
|
250
244
|
|
251
245
|
t['u_rpd_id'] ||= SecureRandom.uuid
|
@@ -263,18 +257,7 @@ class ServiceNowHelper
|
|
263
257
|
# - List of JSON-formated tickets for updating within ServiceNow.
|
264
258
|
#
|
265
259
|
def prepare_update_tickets(vulnerability_list, nexpose_identifier_id)
|
266
|
-
|
267
|
-
case @options[:ticket_mode]
|
268
|
-
when 'D' then fail 'Ticket updates are not supported in Default mode.'
|
269
|
-
# 'I' IP address -* Vulnerability
|
270
|
-
when 'I' then matching_fields = ['ip_address']
|
271
|
-
# 'V' Vulnerability -* Assets
|
272
|
-
when 'V' then matching_fields = ['vulnerability_id']
|
273
|
-
else
|
274
|
-
fail 'Unsupported ticketing mode selected.'
|
275
|
-
end
|
276
|
-
|
277
|
-
prepare_tickets(vulnerability_list, nexpose_identifier_id, matching_fields)
|
260
|
+
prepare_tickets(vulnerability_list, nexpose_identifier_id)
|
278
261
|
end
|
279
262
|
|
280
263
|
|
@@ -292,7 +275,7 @@ class ServiceNowHelper
|
|
292
275
|
tickets = []
|
293
276
|
@nxid = nil
|
294
277
|
CSV.parse(vulnerability_list.chomp, headers: :first_row) do |row|
|
295
|
-
@nxid = @
|
278
|
+
@nxid = @mode_helper.get_nxid(nexpose_identifier_id, row)
|
296
279
|
# 'state' 7 is the "Closed" state within ServiceNow.
|
297
280
|
ticket_id = get_ticket_identifier(@nxid)
|
298
281
|
@log.log_message("Closing ticket with NXID: #{@nxid}.")
|