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
@@ -0,0 +1,39 @@
|
|
1
|
+
module NexposeTicketing
|
2
|
+
class TicketMetrics
|
3
|
+
attr_accessor :ticket_counts
|
4
|
+
|
5
|
+
#Create the specific metric collecting methods
|
6
|
+
@@ticket_counts = {}
|
7
|
+
[:created, :updated, :closed].each do |action|
|
8
|
+
@@ticket_counts[action] = 0
|
9
|
+
define_method(action) do |increment=nil|
|
10
|
+
@@ticket_counts[action] += increment || 1
|
11
|
+
end
|
12
|
+
|
13
|
+
define_method("get_#{action}") do
|
14
|
+
@@ticket_counts[action]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
@start_time = nil
|
20
|
+
@log = NexposeTicketing::NxLogger.instance
|
21
|
+
end
|
22
|
+
|
23
|
+
def start
|
24
|
+
return if @start_time != nil
|
25
|
+
@start_time = Time.now
|
26
|
+
end
|
27
|
+
|
28
|
+
def finish
|
29
|
+
return if @start_time == nil
|
30
|
+
@time_taken = Time.at(Time.now - @start_time).utc.strftime("%H:%M:%S")
|
31
|
+
@start_time = nil
|
32
|
+
|
33
|
+
@log.log_message("Ticket processing took #{@time_taken} to complete.")
|
34
|
+
@@ticket_counts.keys.each do |action|
|
35
|
+
@log.log_message("Metrics: #{@@ticket_counts[action]} tickets were #{action}.")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -12,10 +12,24 @@ module NexposeTicketing
|
|
12
12
|
|
13
13
|
def initialize(options = nil)
|
14
14
|
@timeout = options[:timeout]
|
15
|
+
|
16
|
+
# Gets the suffix of the query method signatures based on the mode
|
17
|
+
@method_suffix = options[:query_suffix]
|
18
|
+
|
19
|
+
define_query_methods
|
15
20
|
end
|
16
21
|
|
17
|
-
def
|
22
|
+
def define_query_methods
|
23
|
+
methods = Queries.methods.grep Regexp.new (@method_suffix+'$')
|
18
24
|
|
25
|
+
methods.each do |m|
|
26
|
+
define_singleton_method m do |options, override=nil|
|
27
|
+
request_query(m, options, override)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def nexpose_login(nexpose_data)
|
19
33
|
@nsc = Nexpose::Connection.new(nexpose_data[:nxconsole], nexpose_data[:nxuser], nexpose_data[:nxpasswd])
|
20
34
|
@nsc.login
|
21
35
|
@log = NexposeTicketing::NxLogger.instance
|
@@ -67,8 +81,9 @@ module NexposeTicketing
|
|
67
81
|
#
|
68
82
|
def load_last_scans(options = {}, report_config = Nexpose::AdhocReportConfig.new(nil, 'sql'))
|
69
83
|
report_config.add_filter('version', '1.2.0')
|
70
|
-
sites = Array(options[:sites])
|
71
|
-
tags = Array(options[:tags])
|
84
|
+
sites = Array(options[:sites]).map!(&:to_s)
|
85
|
+
tags = Array(options[:tags]).map!(&:to_s)
|
86
|
+
|
72
87
|
if(options[:tag_run])
|
73
88
|
report_config.add_filter('query', Queries.last_tag_scans)
|
74
89
|
tags.each do |tag|
|
@@ -84,7 +99,7 @@ module NexposeTicketing
|
|
84
99
|
#We only care about sites we are monitoring.
|
85
100
|
trimmed_csv = []
|
86
101
|
if(options[:tag_run])
|
87
|
-
trimmed_csv << 'tag_id,
|
102
|
+
trimmed_csv << 'tag_id,last_scan_fingerprint'
|
88
103
|
current_tag_id = nil
|
89
104
|
tag_finger_print = ''
|
90
105
|
csv_output.each do |row|
|
@@ -94,7 +109,7 @@ module NexposeTicketing
|
|
94
109
|
current_tag_id = row[0].to_i
|
95
110
|
else
|
96
111
|
#New tag ID, finish off the old fingerprint and start on the new one
|
97
|
-
trimmed_csv << CSV::Row.new('tag_id,
|
112
|
+
trimmed_csv << CSV::Row.new('tag_id,last_scan_fingerprint'.split(','), "#{current_tag_id},#{Digest::MD5::hexdigest(tag_finger_print)}".split(','))
|
98
113
|
tag_finger_print.clear
|
99
114
|
current_tag_id = row[0].to_i
|
100
115
|
end
|
@@ -107,7 +122,7 @@ module NexposeTicketing
|
|
107
122
|
end
|
108
123
|
end
|
109
124
|
unless tag_finger_print.empty?
|
110
|
-
trimmed_csv << CSV::Row.new('tag_id,
|
125
|
+
trimmed_csv << CSV::Row.new('tag_id,last_scan_fingerprint'.split(','), "#{current_tag_id},#{Digest::MD5::hexdigest(tag_finger_print)}".split(','))
|
111
126
|
end
|
112
127
|
else
|
113
128
|
trimmed_csv << report_output.lines.first
|
@@ -117,6 +132,7 @@ module NexposeTicketing
|
|
117
132
|
end
|
118
133
|
end
|
119
134
|
end
|
135
|
+
|
120
136
|
trimmed_csv
|
121
137
|
end
|
122
138
|
|
@@ -130,18 +146,17 @@ module NexposeTicketing
|
|
130
146
|
# - Returns String @vulnerability_categories
|
131
147
|
#
|
132
148
|
def createVulnerabilityFilter(options = {})
|
133
|
-
|
134
|
-
|
135
|
-
if not options[:vulnerabilityCategories].nil? and not options[:vulnerabilityCategories].empty?
|
136
|
-
@vulnerability_categories = options[:vulnerabilityCategories].strip.split(',').map {|category| "include:#{category}"}.join(',')
|
137
|
-
end
|
149
|
+
if options[:vulnerabilityCategories].nil? || options[:vulnerabilityCategories].empty?
|
150
|
+
return nil
|
138
151
|
end
|
139
|
-
|
152
|
+
|
153
|
+
filter = options[:vulnerabilityCategories].strip.split(',')
|
154
|
+
filter.map { |category| "include:#{category}" }.join(',')
|
140
155
|
end
|
141
156
|
|
142
|
-
def read_tag_asset_list(
|
157
|
+
def read_tag_asset_list(csv_file_name)
|
143
158
|
file_identifier_histories = Hash.new(-1)
|
144
|
-
CSV.foreach(
|
159
|
+
CSV.foreach(csv_file_name, headers: true) do |row|
|
145
160
|
file_identifier_histories[row[0]] = row[1]
|
146
161
|
end
|
147
162
|
file_identifier_histories
|
@@ -150,10 +165,8 @@ module NexposeTicketing
|
|
150
165
|
def generate_tag_asset_list(options = {}, report_config = Nexpose::AdhocReportConfig.new(nil, 'sql'))
|
151
166
|
report_config.add_filter('version', '1.2.0')
|
152
167
|
tags = Array(options[:tags])
|
153
|
-
|
154
|
-
tags.each
|
155
|
-
report_config.add_filter('tag', tag)
|
156
|
-
end
|
168
|
+
report_config.add_filter('query', Queries.last_tag_scans)
|
169
|
+
tags.each { |tag| report_config.add_filter('tag', tag) }
|
157
170
|
|
158
171
|
report_output = report_config.generate(@nsc, @timeout)
|
159
172
|
csv_output = CSV.parse(report_output.chomp, headers: :first_row)
|
@@ -170,6 +183,9 @@ module NexposeTicketing
|
|
170
183
|
save_to_file(options[:csv_file], trimmed_csv)
|
171
184
|
current_tag_id = row[0].to_i
|
172
185
|
trimmed_csv = []
|
186
|
+
|
187
|
+
# TODO: test this change
|
188
|
+
trimmed_csv << 'asset_id, last_scan_id'
|
173
189
|
end
|
174
190
|
end
|
175
191
|
|
@@ -186,14 +202,14 @@ module NexposeTicketing
|
|
186
202
|
# - +csv_file_name+ - CSV File name.
|
187
203
|
#
|
188
204
|
def save_to_file(csv_file_name, trimmed_csv, saved_file = nil)
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
unless File.directory?(dir)
|
193
|
-
FileUtils.mkdir_p(dir)
|
194
|
-
end
|
195
|
-
File.open(csv_file_name, 'w') { |file| file.puts(trimmed_csv) }
|
205
|
+
unless saved_file.nil?
|
206
|
+
saved_file.open(csv_file_name, 'w') { |file| file.puts(trimmed_csv) }
|
207
|
+
return
|
196
208
|
end
|
209
|
+
|
210
|
+
dir = File.dirname(csv_file_name)
|
211
|
+
FileUtils.mkdir_p(dir) unless File.directory?(dir)
|
212
|
+
File.open(csv_file_name, 'w') { |file| file.puts(trimmed_csv) }
|
197
213
|
end
|
198
214
|
|
199
215
|
# Gets the last scan information from nexpose sans the CSV headers.
|
@@ -210,212 +226,55 @@ module NexposeTicketing
|
|
210
226
|
nexpose_ids
|
211
227
|
end
|
212
228
|
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
# - +nexpose_item_override+ - Override for user-configured tag/site options
|
218
|
-
#
|
219
|
-
# * *Returns* :
|
220
|
-
# - Returns CSV |asset_id| |ip_address| |current_scan| |vulnerability_id| |solution_id| |nexpose_id|
|
221
|
-
# |url| |summary| |fix|
|
222
|
-
#
|
223
|
-
def all_vulns(options = {}, nexpose_item_override = nil)
|
224
|
-
report_config = @report_helper.generate_sql_report_config()
|
225
|
-
severity = options[:severity].nil? ? 0 : options[:severity]
|
226
|
-
report_config.add_filter('version', '1.2.0')
|
227
|
-
if options[:ticket_mode] == 'V'
|
228
|
-
report_config.add_filter('query', Queries.all_new_vulns_by_vuln_id(options))
|
229
|
-
else
|
230
|
-
report_config.add_filter('query', Queries.all_new_vulns(options))
|
231
|
-
end
|
232
|
-
|
233
|
-
if nexpose_item_override.nil?
|
234
|
-
if(options[:tag_run])
|
235
|
-
nexpose_items = Array(options[:tags])
|
236
|
-
else
|
237
|
-
nexpose_items = Array(options[:sites])
|
238
|
-
end
|
239
|
-
else
|
240
|
-
nexpose_items = Array(nexpose_item_override)
|
241
|
-
end
|
242
|
-
|
243
|
-
if options[:tag_run]
|
244
|
-
unless nexpose_items.nil? || nexpose_items.empty?
|
245
|
-
nexpose_items.each do |tag_id|
|
246
|
-
report_config.add_filter('tag', tag_id)
|
247
|
-
end
|
248
|
-
end
|
249
|
-
else
|
250
|
-
unless nexpose_items.nil? || nexpose_items.empty?
|
251
|
-
nexpose_items.each do |site_id|
|
252
|
-
report_config.add_filter('site', site_id)
|
253
|
-
end
|
254
|
-
end
|
255
|
-
end
|
256
|
-
|
257
|
-
report_config.add_filter('vuln-severity', severity)
|
258
|
-
|
259
|
-
vuln_filter_cats = createVulnerabilityFilter(options)
|
260
|
-
|
261
|
-
if not vuln_filter_cats.nil? and not vuln_filter_cats.empty?
|
262
|
-
report_config.add_filter('vuln-categories', vuln_filter_cats)
|
263
|
-
end
|
264
|
-
|
265
|
-
@report_helper.save_generate_cleanup_report_config(report_config)
|
229
|
+
def request_query(query_name, options = {}, nexpose_items = nil)
|
230
|
+
items = Array(nexpose_items || options["#{options[:scan_mode]}s".intern])
|
231
|
+
|
232
|
+
report_config = generate_config(query_name, options, items)
|
266
233
|
end
|
267
234
|
|
268
|
-
|
269
|
-
#
|
270
|
-
# * *Args* :
|
271
|
-
# - +options+ - A Hash with user configuration information.
|
272
|
-
#
|
273
|
-
# * *Returns* :
|
274
|
-
# - Returns CSV |asset_id| |ip_address| |current_scan| |vulnerability_id| |solution_id| |nexpose_id|
|
275
|
-
# |url| |summary| |fix|
|
276
|
-
#
|
277
|
-
def new_vulns(options = {})
|
235
|
+
def generate_config(query_name, options, nexpose_items)
|
278
236
|
report_config = @report_helper.generate_sql_report_config()
|
279
237
|
nexpose_item = options[:nexpose_item]
|
280
238
|
reported_scan_id = options[:scan_id]
|
281
|
-
fail 'Nexpose item cannot be null or empty' if nexpose_item.nil? || reported_scan_id.nil?
|
282
|
-
severity = options[:severity].nil? ? 0 : options[:severity]
|
283
|
-
report_config.add_filter('version', '1.2.0')
|
284
|
-
if options[:ticket_mode] == 'V'
|
285
|
-
report_config.add_filter('query', Queries.new_vulns_by_vuln_id_since_scan(options))
|
286
|
-
else
|
287
|
-
report_config.add_filter('query', Queries.new_vulns_since_scan(options))
|
288
|
-
end
|
289
239
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
report_config.add_filter('site', nexpose_item)
|
240
|
+
# If it's a non-initial run, we need the last scan ID
|
241
|
+
unless options[:initial_run]
|
242
|
+
fail 'Nexpose item cannot be null or empty' if nexpose_item.nil? || reported_scan_id.nil?
|
294
243
|
end
|
295
244
|
|
296
|
-
report_config.add_filter('vuln-severity', severity)
|
297
|
-
|
298
|
-
vuln_filter_cats = createVulnerabilityFilter(options)
|
299
|
-
|
300
|
-
if not vuln_filter_cats.nil? and not vuln_filter_cats.empty?
|
301
|
-
report_config.add_filter('vuln-categories', vuln_filter_cats)
|
302
|
-
end
|
303
|
-
|
304
|
-
@report_helper.save_generate_cleanup_report_config(report_config)
|
305
|
-
end
|
306
|
-
|
307
|
-
# Gets the old vulns from base scan reported_scan_id and the newest / latest scan from a site.
|
308
|
-
#
|
309
|
-
# * *Args* :
|
310
|
-
# - +options+ - A Hash with user configuration information.
|
311
|
-
#
|
312
|
-
# * *Returns* :
|
313
|
-
# - Returns CSV |asset_id| |ip_address| |current_scan| |vulnerability_id| |solution_id| |nexpose_id|
|
314
|
-
# |url| |summary| |fix|
|
315
|
-
#
|
316
|
-
def old_vulns(options = {})
|
317
|
-
report_config = @report_helper.generate_sql_report_config()
|
318
|
-
nexpose_item = options[:nexpose_item]
|
319
|
-
reported_scan_id = options[:scan_id]
|
320
|
-
fail 'Nexpose item cannot be null or empty' if nexpose_item.nil? || reported_scan_id.nil?
|
321
|
-
severity = options[:severity].nil? ? 0 : options[:severity]
|
322
245
|
report_config.add_filter('version', '1.2.0')
|
323
|
-
report_config.add_filter('query', Queries.
|
324
|
-
|
325
|
-
if(options[:tag_run])
|
326
|
-
report_config.add_filter('tag', options[:tag])
|
327
|
-
else
|
328
|
-
report_config.add_filter('site', nexpose_item)
|
329
|
-
end
|
330
|
-
|
331
|
-
report_config.add_filter('vuln-severity', severity)
|
332
|
-
|
333
|
-
vuln_filter_cats = createVulnerabilityFilter(options)
|
246
|
+
report_config.add_filter('query', Queries.send(query_name, options))
|
334
247
|
|
335
|
-
|
336
|
-
report_config.add_filter('vuln-categories', vuln_filter_cats)
|
337
|
-
end
|
338
|
-
|
339
|
-
@report_helper.save_generate_cleanup_report_config(report_config)
|
340
|
-
end
|
341
|
-
|
342
|
-
# Gets information on possible tickets to close based on only having old vulns/IPs and no new/same ones.
|
343
|
-
# Based on IP address (for 'I' mode) or vuln ID ('V' mode).
|
344
|
-
#
|
345
|
-
# * *Args* :
|
346
|
-
# - +options+ - A Hash with user configuration information.
|
347
|
-
#
|
348
|
-
# * *Returns* :
|
349
|
-
# - Returns CSV |asset_id| |ip_address| |current_scan| |vulnerability_id| |comparison|
|
350
|
-
#
|
351
|
-
def tickets_to_close(options = {})
|
352
|
-
report_config = @report_helper.generate_sql_report_config()
|
353
|
-
nexpose_item = options[:nexpose_item]
|
354
|
-
reported_scan_id = options[:scan_id]
|
355
|
-
fail 'Nexpose item cannot be null or empty' if nexpose_item.nil? || reported_scan_id.nil?
|
356
|
-
severity = options[:severity].nil? ? 0 : options[:severity]
|
357
|
-
report_config.add_filter('version', '1.2.0')
|
358
|
-
if options[:ticket_mode] == 'V'
|
359
|
-
report_config.add_filter('query', Queries.old_tickets_by_vuln_id(options))
|
360
|
-
else
|
361
|
-
report_config.add_filter('query', Queries.old_tickets_by_ip(options))
|
362
|
-
end
|
248
|
+
id_type = options[:tag_run] ? 'tag' : 'site'
|
363
249
|
|
364
|
-
if
|
365
|
-
report_config.add_filter(
|
250
|
+
if nexpose_items != nil && !nexpose_items.empty?
|
251
|
+
nexpose_items.each { |id| report_config.add_filter(id_type, id) }
|
366
252
|
else
|
367
|
-
|
253
|
+
item = options[:tag_run] ? options[:tag] : nexpose_item
|
254
|
+
report_config.add_filter(id_type, item)
|
368
255
|
end
|
369
256
|
|
370
|
-
report_config.add_filter('vuln-severity', severity)
|
257
|
+
report_config.add_filter('vuln-severity', options[:severity] || 0)
|
371
258
|
|
372
259
|
vuln_filter_cats = createVulnerabilityFilter(options)
|
373
260
|
|
374
|
-
|
261
|
+
unless vuln_filter_cats.nil? || vuln_filter_cats.empty?
|
375
262
|
report_config.add_filter('vuln-categories', vuln_filter_cats)
|
376
263
|
end
|
377
264
|
|
378
265
|
@report_helper.save_generate_cleanup_report_config(report_config)
|
379
266
|
end
|
380
267
|
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
# - +options+ - A Hash with user configuration information.
|
387
|
-
#
|
388
|
-
# * *Returns* :
|
389
|
-
# - Returns CSV |asset_id| |ip_address| |current_scan| |vulnerability_id| |solution_id| |nexpose_id|
|
390
|
-
# |url| |summary| |fix| |comparison|
|
391
|
-
#
|
392
|
-
def all_vulns_since(options = {})
|
393
|
-
report_config = @report_helper.generate_sql_report_config()
|
394
|
-
nexpose_item = options[:nexpose_item]
|
395
|
-
reported_scan_id = options[:scan_id]
|
396
|
-
fail 'Nexpose item cannot be null or empty' if nexpose_item.nil? || reported_scan_id.nil?
|
397
|
-
severity = options[:severity].nil? ? 0 : options[:severity]
|
398
|
-
report_config.add_filter('version', '1.2.0')
|
399
|
-
if options[:ticket_mode] == 'V'
|
400
|
-
report_config.add_filter('query', Queries.all_vulns_by_vuln_id_since_scan(options))
|
401
|
-
else
|
402
|
-
report_config.add_filter('query', Queries.all_vulns_since_scan(options))
|
403
|
-
end
|
404
|
-
|
405
|
-
if(options[:tag_run])
|
406
|
-
report_config.add_filter('tag', options[:tag])
|
407
|
-
else
|
408
|
-
report_config.add_filter('site', nexpose_item)
|
409
|
-
end
|
410
|
-
report_config.add_filter('vuln-severity', severity)
|
411
|
-
|
412
|
-
vuln_filter_cats = createVulnerabilityFilter(options)
|
413
|
-
|
414
|
-
if not vuln_filter_cats.nil? and not vuln_filter_cats.empty?
|
415
|
-
report_config.add_filter('vuln-categories', vuln_filter_cats)
|
268
|
+
def method_missing(name, *args, &block)
|
269
|
+
full_method_name = "#{name}#{@method_suffix}"
|
270
|
+
|
271
|
+
unless Queries.respond_to? full_method_name
|
272
|
+
fail %Q{Query request "#{full_method_name}" not understood}
|
416
273
|
end
|
417
274
|
|
418
|
-
@
|
275
|
+
@log.log_message %Q{Creating query request "#{full_method_name}".}
|
276
|
+
request_query(full_method_name, args[0], args[1])
|
419
277
|
end
|
278
|
+
|
420
279
|
end
|
421
280
|
end
|