Class: NexposeTicketing::TicketRepository

Inherits:
Object
  • Object
show all
Defined in:
lib/nexpose_ticketing/ticket_repository.rb

Overview

Repository class that creates and returns generated reports.

Constant Summary collapse

API_VERSION =
'1.2.0'

Instance Method Summary collapse

Constructor Details

#initialize(options = nil) ⇒ TicketRepository

Returns a new instance of TicketRepository.



14
15
16
17
18
19
20
21
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 14

def initialize(options = nil)
  @timeout = options[:timeout]

  # Gets the suffix of the query method signatures based on the mode
  @method_suffix = options[:query_suffix]

  define_query_methods
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &block) ⇒ Object



839
840
841
842
843
844
845
846
847
848
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 839

def method_missing(name, *args, &block)
  full_method_name = "#{name}#{@method_suffix}"

  unless Queries.respond_to? full_method_name
    fail %Q{Query request "#{full_method_name}" not understood}
  end

  log_message %Q{Creating query request "#{full_method_name}".}
  request_query(full_method_name, args[0], args[1])
end

Instance Method Details

#all_site_detailsObject

Returns an array of all sites in the users environment.

  • Returns :

    • An array of Nexpose::SiteSummary objects.



614
615
616
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 614

def all_site_details
  @nsc.sites
end

#create_solution_hash(options, nexpose_item) ⇒ Object



81
82
83
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 81

def create_solution_hash(options, nexpose_item)
  self.send("create_solution_hash#{@method_suffix}", options, nexpose_item)
end

#create_solution_hash_by_ip(options, nexpose_item) ⇒ Object



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 85

def create_solution_hash_by_ip(options, nexpose_item)
  report = all_new_vulns(options, nexpose_item)

  info = {}
  CSV.foreach(report) do |row|
    asset_id, vulnerability_id, first_discov,
        most_recently_discov, solution_ids = row

    next if asset_id == 'asset_id'
    unless info.key? asset_id.to_s
      info[asset_id.to_s] = {}
    end

    info[asset_id.to_s][vulnerability_id.to_s] = {
        first_discovered: first_discov,
        most_recently: most_recently_discov,
        solution_ids: solution_ids }
  end
  info
end

#create_solution_hash_by_vuln_id(options, nexpose_item) ⇒ Object



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 106

def create_solution_hash_by_vuln_id(options, nexpose_item)
  report = all_new_vulns(options, nexpose_item)

  info = {}
  CSV.foreach(report) do |row|
    vulnerability_id, solution_ids, references = row

    next if vulnerability_id == 'vulnerability_id'
    unless info.key? vulnerability_id.to_i
      info[vulnerability_id.to_i] = {}
    end

    info[vulnerability_id.to_i] = { solution_ids: solution_ids,
                                    refs: references }
  end
  info
end

#createVulnerabilityFilter(options = {}) ⇒ Object

Parses user-configured vulnerability filter categories and returns aforementioned categories in a format used by the Nexpose::AdhocReportConfig class.

  • Args :

    • options - A Hash with site(s), reported_scan_id and severity level.

  • Returns :

    • Returns String @vulnerability_categories



726
727
728
729
730
731
732
733
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 726

def createVulnerabilityFilter(options = {})
  if options[:vulnerabilityCategories].nil? || options[:vulnerabilityCategories].empty?
    return nil
  end

  filter = options[:vulnerabilityCategories].strip.split(',')
  filter.map { |category| "include:#{category}" }.join(',')
end

#define_query_methodsObject



23
24
25
26
27
28
29
30
31
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 23

def define_query_methods
  methods = Queries.methods.grep Regexp.new (@method_suffix+'$')

  methods.each do |m|
    define_singleton_method m do |options, override=nil|
      request_query(m, options, override)
    end
  end
end

#delta_scan_by_ip(options) ⇒ Object



245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 245

def delta_scan_by_ip(options)
  current_vuln_instances = {}
  current_vuln_ids  = {}
  ignored_assets = []

  assets = if options[:tag_run]
             [Nexpose::Asset.load(@nsc, options[:nexpose_item])]
           else
             get_site_asset_list(options[:nexpose_item])
           end

  assets.each do |asset|
    risk_score = if options[:tag_run]
                   asset.assessment.risk_score
                 else
                   asset.risk_score
                 end
    if risk_score < (options[:riskScore] || 0)
      ignored_assets << asset.id
      next
    end

    vulns = get_vuln_instances(asset.id, options[:severity])
    current_vuln_instances[asset.id.to_s] = { asset: asset, vulns: vulns }
    current_vuln_ids[asset.id.to_s] = vulns.map { |v| v.console_id }
  end

  state = {}
  previous_state_file = get_previous_state(options,
                                           'last_scan_state_by_ip')

  CSV.foreach(previous_state_file, headers: true) do |row|
    asset_id = row['asset_id'].to_s
    vuln_id = row['vulnerability_id'].to_i

    next if ignored_assets.include? asset_id

    if current_vuln_ids.key? asset_id
      unless state.key? asset_id.to_s
        state[asset_id] =
            { asset: current_vuln_instances[asset_id][:asset], new: [],
              same: [], old: [] }
      end

      success = current_vuln_ids[asset_id].delete(vuln_id)
      if success.nil?
        state[asset_id][:old] << vuln_id
      else
        vulns = current_vuln_instances[asset_id][:vulns]
        state[asset_id][:same] << vulns.find { |v| v.console_id == vuln_id }
      end
    else
      unless state.key? asset_id
        state[asset_id] = { old_ticket: '', new: [], same: [], old: [] }
      end

      state[asset_id][:old] << vuln_id
    end
  end

  current_vuln_ids.each_key do |asset_id|
    asset_id = asset_id.to_s
    unless state.key? asset_id
      state[asset_id] =
          { asset: current_vuln_instances[asset_id][:asset], new: [],
            same: [], old: [] }
    end

    current_vuln_ids[asset_id].each do |vuln_id|
      vulns = current_vuln_instances[asset_id][:vulns]
      state[asset_id][:new] << vulns.find { |v| v.console_id == vuln_id }
    end
  end

  state.each_key do |asset_id|
    if state[asset_id][:new].size == 0 && state[asset_id][:same].size == 0
      state[asset_id][:old_ticket] = ''
    end
  end

  state
end

#delta_scan_by_vuln_id(options) ⇒ Object



328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 328

def delta_scan_by_vuln_id(options)
  current_vuln_instances = {}
  current_vuln_ids = {}
  ignored_assets = []
  assets = get_site_asset_list(options[:nexpose_item])

  assets.each do |asset|
    if asset.risk_score < (options[:riskScore] || 0)
      ignored_assets << asset.id
      next
    end
    vulns = get_vuln_instances(asset.id.to_s, options[:severity])

    vulns.each do |vuln|
      console_id = vuln.console_id
      unless current_vuln_instances.key? console_id
        current_vuln_instances[console_id] = { vuln: vuln.id,
                                               assets: {} }
        current_vuln_ids[console_id] = []
      end

      current_vuln_instances[console_id][:assets][asset.id] = asset
      current_vuln_ids[console_id] << asset.id
    end
  end

  state = {}
  previous_state_file = get_previous_state(options,
                                           'last_scan_state_by_vuln_id')

  CSV.foreach(previous_state_file, headers: true) do |row|
    vuln_id = row['vulnerability_id'].to_i
    asset_ids = row['asset_ids'][1..-2].split(',').map(&:to_i)

    if current_vuln_ids.key? vuln_id
      unless state.key? vuln_id
        state[vuln_id] = { vuln: current_vuln_instances[vuln_id][:vuln],
                           new: [], same: [], old: [] }
      end

      asset_ids.each do |asset_id|            
        next if ignored_assets.include? asset_id

        success = current_vuln_ids[vuln_id].delete(asset_id)

        if success.nil?
          asset_info = { id: asset_id, ip: get_asset_ip(asset_id) }
          state[vuln_id][:old] << asset_info
        else
          asset = current_vuln_instances[vuln_id][:assets][asset_id]
          state[vuln_id][:same] << asset
        end
      end
    else
      unless state.key? vuln_id
        state[vuln_id] = { vuln: vuln_id, old_ticket: 1, new: [],
                           same: [], old: [] }
      end

      state[vuln_id][:old] << asset_ids
    end
  end

  current_vuln_ids.each_key do |vuln_id|
    unless state.key? vuln_id
      state[vuln_id] =
          { vuln: current_vuln_instances[vuln_id][:vuln], new: [],
            same: [], old: [] }
    end

    current_vuln_ids[vuln_id].each do |asset_id|
      asset = current_vuln_instances[vuln_id][:assets][asset_id]
      state[vuln_id][:new] << asset
    end
  end

  state.each_key do |vuln_id|
    if state[vuln_id][:new].size == 0 && state[vuln_id][:same].size == 0
      state[vuln_id][:old_ticket] = 1
    end
  end

  state
end

#generate_config(query_name, options, nexpose_items) ⇒ Object



800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 800

def generate_config(query_name, options, nexpose_items)
  report_config =  @report_helper.generate_sql_report_config()
  nexpose_item = options[:nexpose_item]
  reported_scan_id = options[:scan_id]

  # If it's a non-initial run, we need the last scan ID
  unless options[:initial_run]
    fail 'Nexpose item cannot be null or empty' if nexpose_item.nil? || reported_scan_id.nil?
  end

  report_config.add_filter('version', '2.0.0')
  report_config.add_filter('query', Queries.send(query_name, options))

  id_type = if options[:report_type].to_s == 'initial'
              'tag'
            elsif options[:tag_run]
              'device'
            else
              'site'
            end

  if nexpose_items != nil && !nexpose_items.empty?
    nexpose_items.each { |id| report_config.add_filter(id_type, id) }
  else
    item = options[:tag_run] ? options[:tag] : nexpose_item
    report_config.add_filter(id_type, item)
  end

  report_config.add_filter('vuln-severity', options[:severity] || 0)

  vuln_filter_cats = createVulnerabilityFilter(options)

  unless vuln_filter_cats.nil? || vuln_filter_cats.empty?
    report_config.add_filter('vuln-categories', vuln_filter_cats)
  end

  @report_helper.save_generate_cleanup_report_config(report_config)
end

#generate_delta_csv(options, close_tickets) ⇒ Object

Method converts retrieved information from Nexpose, sorted by the current state, into a CSV to be parsed into tickets for sending to a third party service

Params:

  • options: The options to use to generate information

  • close_tickets: Whether the user has ticket closures enabled

Returns: A CSV containing all necessary information to create tickets



423
424
425
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 423

def generate_delta_csv(options, close_tickets)
  self.send("generate_delta_csv#{@method_suffix}", options, close_tickets)
end

#generate_delta_csv_by_ip(options, close_tickets) ⇒ Object



427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 427

def generate_delta_csv_by_ip(options, close_tickets)
  nexpose_item = options[:nexpose_item]

  state = generate_delta_scan_data(options)
  info_hash = create_solution_hash(options, nexpose_item)

  delta_scan_file = Tempfile.new("delta_vulnerable_items_#{nexpose_item}")
  delta_scan_file.binmode

  CSV.open(delta_scan_file, 'wb') do |csv|
    csv << ['asset_id', 'vulnerability_id', 'first_discovered',
            'most_recently_discovered', 'ip_address', 'riskscore',
            'vuln_nexpose_id', 'cvss_score', 'solution_ids', 'comparison']

    state.each do |asset_id, vulns|
      asset = vulns[:asset]
      asset_id = asset.id.to_s

      if options[:tag_run]
        address = asset.ip
        risk_score = asset.assessment.risk_score
      else
        address = asset.address
        risk_score = asset.risk_score
      end

      if vulns.key? :new
        vulns[:new].each do |vuln|
          info = info_hash[asset_id][vuln.console_id.to_s]
          csv << [asset_id, vuln.console_id, info[:first_discovered],
                  info[:most_recently], address, risk_score,
                  vuln.id, vuln.cvss_score, info[:solution_ids], 'New']
        end
      end

      if vulns.key? :same
        vulns[:same].each do |vuln|
          info = info_hash[asset_id][vuln.console_id.to_s]
          csv << [asset_id, vuln.console_id, info[:first_discovered],
                  info[:most_recently], address, risk_score,
                  vuln.id, vuln.cvss_score, info[:solution_ids], 'Same']
        end
      end
    end
  end

  delta_scan_file.flush
  unless close_tickets
    return { new_csv: delta_scan_file }
  end

  old_vulns_file = Tempfile.new("delta_vuln_old_items_#{nexpose_item}")
  old_vulns_file.binmode

  old_vulns_mode = options[:old_vulns_mode]
  CSV.open(old_vulns_file, 'wb') do |csv|
    csv << %w(asset_id vulnerability_id ip_address)

    if old_vulns_mode == 'old'
      key = old_vulns_mode.intern
      old_vulns = state.select { |asset_id, vulns| vulns[key].size > 0 }
      old_vulns.each do |asset_id, vulns|
        asset = old_vulns[asset_id][:asset]
        vulns[:old].each do |vuln|
          csv << [asset_id, vuln, asset.address]
        end
      end
    else
      key = old_vulns_mode.intern
      old_vulns = state.select{ |asset_id, vulns| vulns.key? key }
      old_vulns.each do |asset_id, vulns|
        ip = if vulns.key? :asset
               vulns[:asset].address
             else
               get_asset_ip(asset_id)
             end
        csv << [asset_id, '', ip]
      end
    end
  end

  old_vulns_file.flush
  { new_csv: delta_scan_file, old_csv: old_vulns_file }
end

#generate_delta_csv_by_vuln_id(options, close_tickets) ⇒ Object



512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 512

def generate_delta_csv_by_vuln_id(options, close_tickets)
  nexpose_item = options[:nexpose_item]

  state = generate_delta_scan_data(options)
  info = create_solution_hash(options, nexpose_item)

  delta_scan_file = Tempfile.new("delta_vulnerable_items_#{nexpose_item}")
  delta_scan_file.binmode

  old_tickets = []
  CSV.open(delta_scan_file, 'wb') do |csv|
    csv << ['vulnerability_id', 'vuln_nexpose_id', 'title', 'cvss_score',
            'assets', 'description', 'solution_ids', 'references',
            'comparison']

    state.each do |vuln_id, data|
      if data.has_key? :old_ticket
        old_tickets << vuln_id
        next
      end

      vuln = data[:vuln]
      vuln_def = Nexpose::VulnerabilityDefinition.load(@nsc, vuln)

      if data[:new].count > 0
        assets = []
        data[:new].each do |asset|
          assets << "#{asset.id}|#{asset.address}|#{asset.risk_score}"
        end
        csv << [vuln_id, vuln, vuln_def.title, vuln_def.cvss_score,
                assets.join('~'), vuln_def.description,
                info[vuln_id][:solution_ids], info[vuln_id][:refs], 'New']

        if data[:same].count > 0
          assets = []
          data[:same].each do |asset|
            assets << "#{asset.id}|#{asset.address}|#{asset.risk_score}"
          end

          csv << [vuln_id, vuln, vuln_def.title, vuln_def.cvss_score,
                  assets.join('~'), '', '', '', 'Same']
        end
      elsif data[:same].count > 0
        assets = []
        data[:same].each do |asset|
          assets << "#{asset.id}|#{asset.address}|#{asset.risk_score}"
        end
        csv << [vuln_id, vuln, vuln_def.title, vuln_def.cvss_score,
                assets.join('~'), vuln_def.description,
                info[vuln_id][:solution_ids], info[vuln_id][:refs], 'Same']
      end

      if data[:old].count > 0
        assets = []
        data[:old].each do |asset|
          assets << "#{asset[:id]}|#{asset[:ip]}|"
        end

        csv << [vuln_id, vuln, vuln_def.title, vuln_def.cvss_score,
                assets.join('~'), '', '', '', 'Old']
      end
    end
  end
  delta_scan_file.flush

  unless close_tickets
    return { new_csv: delta_scan_file }
  end

  old_vulns_file = Tempfile.new("delta_vuln_old_items_#{nexpose_item}")
  old_vulns_file.binmode

  CSV.open(old_vulns_file, 'wb') do |csv|
    csv << %w(vulnerability_id)
    old_tickets.each { |id| csv << [id] }
  end

  old_vulns_file.flush
  { new_csv: delta_scan_file, old_csv: old_vulns_file }
end

#generate_delta_scan_data(options) ⇒ Object

Method retrieves current state of a site from Nexpose, before creating a diff against the state the last time the integration was run.

Params:

  • Options: The options to use to generate information

Returns: The current state of vulnerabilities for assets in a site.



241
242
243
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 241

def generate_delta_scan_data(options)
  self.send("delta_scan#{@method_suffix}", options)
end

#generate_initial_scan_data(options, nexpose_item) ⇒ Object

Method retrieves current state of a site / tag from Nexpose for use in a Ticketing Integration

Params:

  • Options: The options to use to generate information

  • Nexpose_Item: The ID of the Site / Tag to generate data for

Returns: CSV containing vulnerability information



132
133
134
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 132

def generate_initial_scan_data(options, nexpose_item)
  self.send("initial_scan#{@method_suffix}", options, nexpose_item)
end

#generate_tag_asset_list(options = {}) ⇒ Object



743
744
745
746
747
748
749
750
751
752
753
754
755
756
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 743

def generate_tag_asset_list(options = {})
  tags = Array(options[:tags]).map!(&:to_s)

  tags.each do |t|
    trimmed_csv = ['asset_id, last_scan_id']

    assets = Nexpose::Tag.load(@nsc, t).asset_ids
    assets.each do |a|
      scan = @nsc.asset_scan_history(a).max_by { |s| s.scan_id }
      trimmed_csv << "#{a},#{scan.scan_id}"
    end
    save_to_file(options[:csv_file], trimmed_csv)
  end
end

#get_asset_ip(asset_id) ⇒ Object



77
78
79
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 77

def get_asset_ip(asset_id)
  Nexpose::Asset.load(@nsc, asset_id).ip
end

#get_asset_list(item, mode) ⇒ Object



61
62
63
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 61

def get_asset_list(item, mode)
  self.send("get_#{mode}_asset_list", item)
end

#get_previous_state(options, query_name) ⇒ Object

Returns the previous state of a site



594
595
596
597
598
599
600
601
602
603
604
605
606
607
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 594

def get_previous_state(options, query_name)
  report_config =  @report_helper.generate_sql_report_config()
  report_config.add_filter('version', API_VERSION)
  report_config.add_filter('query', Queries.send(query_name, options))
  if options[:tag_run]
    report_config.add_filter('device', options[:nexpose_item].to_s)
  else
    report_config.add_filter('site', options[:nexpose_item])
  end

  report_config.add_filter('vuln-severity', options[:severity] || 0)

  @report_helper.save_generate_cleanup_report_config(report_config)
end

#get_site_asset_list(site_id) ⇒ Object



65
66
67
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 65

def get_site_asset_list(site_id)
  @nsc.assets(site_id)
end

#get_solution_dataObject



54
55
56
57
58
59
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 54

def get_solution_data
  report_config =  @report_helper.generate_sql_report_config()
  report_config.add_filter('version', API_VERSION)
  report_config.add_filter('query', Queries.all_solutions)
  @report_helper.save_generate_cleanup_report_config(report_config)
end

#get_tag_asset_list(tag_id) ⇒ Object



69
70
71
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 69

def get_tag_asset_list(tag_id)
  Nexpose::Tag.load(@nsc, tag_id).asset_ids
end

#get_vuln_instances(asset_id, severity = 0) ⇒ Object



73
74
75
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 73

def get_vuln_instances(asset_id, severity = 0)
  @nsc.list_device_vulns(asset_id).select {|vuln| vuln.severity >= severity}
end

#initial_scan_by_ip(options, nexpose_item) ⇒ Object



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 136

def initial_scan_by_ip(options, nexpose_item)
  log_message "Getting vuln info for #{options[:scan_mode]}: " \
              "#{nexpose_item}"

  query_options = options.dup
  query_options[:report_type] = 'initial' if options[:tag_run]
  info_hash = create_solution_hash_by_ip(query_options, nexpose_item)

  log_debug_message "Getting assets for #{nexpose_item}"
  assets = get_asset_list(nexpose_item, options[:scan_mode])

  initial_scan_file = Tempfile.new("vulnerable_items_#{nexpose_item}")
  initial_scan_file.binmode

  log_debug_message 'Creating CSV for helper'
  CSV.open(initial_scan_file, 'wb') do |csv|
    csv << ['asset_id', 'vulnerability_id', 'first_discovered',
            'most_recently_discovered', 'ip_address', 'riskscore',
            'vuln_nexpose_id', 'cvss_score', 'solution_ids']

    assets.each do |asset|
      if options[:tag_run]
        asset_id = asset.to_s
        asset_deets = Nexpose::Asset.load(@nsc, asset)
        address = asset_deets.ip
        risk_score = asset_deets.assessment.risk_score
      else
        asset_id = asset.id.to_s
        address = asset.address
        risk_score = asset.risk_score
      end

      next if risk_score < (options[:riskScore] || 0)

      log_debug_message "Getting vulns for asset #{address}"
      vulns = get_vuln_instances(asset_id, options[:severity])
      vulns.each do |vuln|
        info = info_hash[asset_id][vuln.console_id.to_s]
        csv << [asset_id, vuln.console_id, info[:first_discovered],
                info[:most_recently], address, risk_score,
                vuln.id, vuln.cvss_score, info[:solution_ids]]
      end
    end
  end
  initial_scan_file.flush

  initial_scan_file
end

#initial_scan_by_vuln_id(options, nexpose_item) ⇒ Object



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 185

def initial_scan_by_vuln_id(options, nexpose_item)
  log_message "Getting vuln info for #{options[:scan_mode]}: " \
              "#{nexpose_item}"
  soln_ref_hash = create_solution_hash_by_vuln_id(options, nexpose_item)

  log_debug_message "Getting assets for #{nexpose_item}"
  assets = get_asset_list(nexpose_item, options[:scan_mode])
  current_vuln_instances = {}
  assets.each do |asset|
    next if asset.risk_score < (options[:riskScore] || 0)

    vulns = get_vuln_instances(asset.id.to_s, options[:severity])

    vulns.each do |vuln|
      unless current_vuln_instances.key? vuln.console_id
        current_vuln_instances[vuln.console_id] = { vuln: vuln.id,
                                                    assets: [] }
      end

      current_vuln_instances[vuln.console_id][:assets] << asset
    end
  end

  initial_scan_file = Tempfile.new("vulnerable_items_#{nexpose_item}")
  initial_scan_file.binmode
  log_debug_message 'Creating CSV for helper'

  CSV.open(initial_scan_file, 'wb') do |csv|
    csv << ['vulnerability_id', 'vuln_nexpose_id', 'title', 'cvss_score',
            'assets', 'description', 'solution_ids', 'references']
    current_vuln_instances.each do |vuln_id, data|
      vuln_def = Nexpose::VulnerabilityDefinition.load(@nsc, data[:vuln])
      assets = []
      data[:assets].each do |asset|
        assets << "#{asset.id}|#{asset.address}|#{asset.risk_score}"
      end

      csv << [vuln_id, data[:vuln], vuln_def.title, vuln_def.cvss_score,
              assets.join('~'), vuln_def.description,
              soln_ref_hash[vuln_id][:solution_ids],
              soln_ref_hash[vuln_id][:refs]]
    end
  end
  initial_scan_file.flush

  initial_scan_file
end

#last_scans(options = {}) ⇒ Object

Gets the last scan information from nexpose sans the CSV headers.

  • Returns :

    • A hash with nexpose_ids (site ID or tag ID) => last_scan_id



779
780
781
782
783
784
785
786
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 779

def last_scans(options = {})
  nexpose_ids= Hash.new(-1)
  trimmed_csv = load_last_scans(options)
  trimmed_csv.drop(1).each  do |row|
    nexpose_ids[row[0]] = row[1]
  end
  nexpose_ids
end

#load_last_scans(options = {}) ⇒ Object

Loads the last scan info to memory.

  • Args :

    • csv_file_name - CSV File name.



649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 649

def load_last_scans(options = {})
  @nsc.

  if options[:tag_run]
    # Need a scan ID for every asset associated with that tag
    tags = Array(options[:tags]).map!(&:to_s)
    scan_details = CSV.generate do |csv|
      csv << %w(tag_id asset_id last_scan_id scan_finished)

      tags.each do |t|
        assets = Nexpose::Tag.load(@nsc, t).asset_ids
        assets.each do |a|
          latest = @nsc.asset_scan_history(a).max_by { |s| s.scan_id }
          csv << [t, a, latest.scan_id, latest.end_time]
        end
      end
    end
  else
    sites = Array(options[:sites]).map!(&:to_s)
    scan_details = CSV.generate do |csv|
      csv << %w(site_id last_scan_id finished)
      sites.each do |s|
        scan = @nsc.last_scan(s)
        csv << [s, scan.scan_id, scan.end_time]
      end
    end
  end

  csv_output = CSV.parse(scan_details.chomp,  headers: :first_row)

  #We only care about sites we are monitoring.
  trimmed_csv = []
  if(options[:tag_run])
    trimmed_csv << 'tag_id,last_scan_fingerprint'
    current_tag_id = nil
    tag_finger_print = ''
    csv_output.each  do |row|
      if (tags.include? row[0].to_s) && (row[0].to_i != current_tag_id)
        if(current_tag_id.nil?)
          #Initial run
          current_tag_id = row[0].to_i
        else
          #New tag ID, finish off the old fingerprint and start on the new one
          trimmed_csv << CSV::Row.new('tag_id,last_scan_fingerprint'.split(','), "#{current_tag_id},#{Digest::MD5::hexdigest(tag_finger_print)}".split(','))
          tag_finger_print.clear
          current_tag_id = row[0].to_i
        end
      end

      if(current_tag_id == row[0].to_i)
        #yield current_tag_id, row[1].to_s, row[2].to_s if block_given?
        tag_finger_print << row[1].to_s
        tag_finger_print << row[2].to_s
      end
    end
    unless tag_finger_print.empty?
      trimmed_csv << CSV::Row.new('tag_id,last_scan_fingerprint'.split(','), "#{current_tag_id},#{Digest::MD5::hexdigest(tag_finger_print)}".split(','))
    end
  else
    trimmed_csv << scan_details.lines.first
    csv_output.each do |row|
      trimmed_csv << row
    end
  end

  trimmed_csv
end

#log_debug_message(message) ⇒ Object



50
51
52
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 50

def log_debug_message(message)
  @log.log_debug_message(message)
end

#log_message(message) ⇒ Object

Logs a message if logging is enabled.



46
47
48
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 46

def log_message(message)
  @log.log_message(message)
end

#nexpose_login(nexpose_data) ⇒ Object



33
34
35
36
37
38
39
40
41
42
43
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 33

def (nexpose_data)
  @nsc = Nexpose::Connection.new(nexpose_data[:nxconsole],
                                 nexpose_data[:nxuser],
                                 nexpose_data[:nxpasswd])
  @nsc.
  @log = NexposeTicketing::NxLogger.instance
  @log.on_connect(nexpose_data[:nxconsole], 3780, @nsc.session_id, '{}')

  #After login, create the report helper
  @report_helper = NexposeReportHelper::ReportOps.new(@nsc, @timeout)
end

#read_last_scans(csv_file_name) ⇒ Object

Reads a nexpose identifier (tag ID, site ID etc) scan history from disk.

  • Args :

    • csv_file_name - CSV File name.

  • Returns :

    • A hash with site_ids => last_scan_id



626
627
628
629
630
631
632
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 626

def read_last_scans(csv_file_name)
  file_identifier_histories = Hash.new(-1)
  CSV.foreach(csv_file_name, headers: true) do |row|
    file_identifier_histories[row[0]] = row[1]
  end
  file_identifier_histories
end

#read_tag_asset_list(csv_file_name) ⇒ Object



735
736
737
738
739
740
741
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 735

def read_tag_asset_list(csv_file_name)
  file_identifier_histories = Hash.new(-1)
  CSV.foreach(csv_file_name, headers: true) do |row|
    file_identifier_histories[row[0]] = row[1]
  end
  file_identifier_histories
end

#request_query(query_name, options = {}, nexpose_items = nil) ⇒ Object



788
789
790
791
792
793
794
795
796
797
798
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 788

def request_query(query_name, options = {}, nexpose_items = nil)
  items = if nexpose_items
            Array(nexpose_items)
          elsif options[:nexpose_item]
            nil
          else
            options["#{options[:scan_mode]}s".intern]
          end

  report_config = generate_config(query_name, options, items)
end

#save_last_scans(csv_file_name, options = {}, saved_file = nil) ⇒ Object

Saves the last scan info to disk.

  • Args :

    • csv_file_name - CSV File name.



639
640
641
642
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 639

def save_last_scans(csv_file_name, options = {}, saved_file = nil)
  current_scan_state = load_last_scans(options)
  save_to_file(csv_file_name, current_scan_state, saved_file)
end

#save_to_file(csv_file_name, trimmed_csv, saved_file = nil) ⇒ Object

Saves CSV scan information to disk

  • Args :

    • csv_file_name - CSV File name.



763
764
765
766
767
768
769
770
771
772
# File 'lib/nexpose_ticketing/ticket_repository.rb', line 763

def save_to_file(csv_file_name, trimmed_csv, saved_file = nil)
  unless saved_file.nil?
    saved_file.open(csv_file_name, 'w') { |file| file.puts(trimmed_csv) }
    return
  end

  dir = File.dirname(csv_file_name)
  FileUtils.mkdir_p(dir) unless File.directory?(dir)
  File.open(csv_file_name, 'w') { |file| file.puts(trimmed_csv) }
end