nexpose_ticketing 1.0.2 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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}.")
|