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.
@@ -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 nexpose_login(nexpose_data)
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, last_scan_fingerprint'
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, last_scan_fingerprint'.split(','), "#{current_tag_id},#{Digest::MD5::hexdigest(tag_finger_print)}".split(','))
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, last_scan_fingerprint'.split(','), "#{current_tag_id},#{Digest::MD5::hexdigest(tag_finger_print)}".split(','))
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
- @vulnerability_categories = nil
134
- if options.has_key?(:vulnerabilityCategories)
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
- @vulnerability_categories
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(cvs_file_name)
157
+ def read_tag_asset_list(csv_file_name)
143
158
  file_identifier_histories = Hash.new(-1)
144
- CSV.foreach(cvs_file_name, headers: true) do |row|
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
- report_config.add_filter('query', Queries.last_tag_scans)
154
- tags.each do |tag|
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
- saved_file.open(csv_file_name, 'w') { |file| file.puts(trimmed_csv) } unless saved_file.nil?
190
- if saved_file.nil?
191
- dir = File.dirname(csv_file_name)
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
- # Gets all the vulnerabilities for a new site or fresh install.
214
- #
215
- # * *Args* :
216
- # - +options+ - A Hash with user configuration information.
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
- # Gets the new vulns from base scan reported_scan_id and the newest / latest scan from a site.
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
- if(options[:tag_run])
291
- report_config.add_filter('tag', options[:tag])
292
- else
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.old_vulns_since_scan(options))
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
- if not vuln_filter_cats.nil? and not vuln_filter_cats.empty?
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(options[:tag_run])
365
- report_config.add_filter('tag', options[:tag])
250
+ if nexpose_items != nil && !nexpose_items.empty?
251
+ nexpose_items.each { |id| report_config.add_filter(id_type, id) }
366
252
  else
367
- report_config.add_filter('site', nexpose_item)
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
- if not vuln_filter_cats.nil? and not vuln_filter_cats.empty?
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
- # Gets all vulns from base scan reported_scan_id and the newest / latest scan from a site. This is
383
- # used for IP-based issue updating. Includes the baseline comparision value ('Old','New', or 'Same').
384
- #
385
- # * *Args* :
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
- @report_helper.save_generate_cleanup_report_config(report_config)
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