nexpose 0.0.9 → 0.0.91

Sign up to get free protection for your applications and to get access to all the features.
data/lib/nexpose.rb CHANGED
@@ -53,3244 +53,22 @@ require 'net/https'
53
53
  require 'net/http'
54
54
  require 'uri'
55
55
  require 'rex/mime'
56
+ require 'nexpose/error'
57
+ require 'nexpose/util'
58
+ require 'nexpose/api_request'
59
+ require 'nexpose/misc'
60
+ require 'nexpose/report'
61
+ require 'nexpose/scan'
62
+ require 'nexpose/scan_engine'
63
+ require 'nexpose/silo'
64
+ require 'nexpose/site'
65
+ require 'nexpose/ticket'
66
+ require 'nexpose/vuln'
67
+ require 'nexpose/creds'
68
+ require 'nexpose/connection'
56
69
 
57
70
  module Nexpose
58
71
 
59
- module Sanitize
60
- def replace_entities(str)
61
- ret = str.dup
62
- ret.gsub!(/&/, "&")
63
- ret.gsub!(/'/, "'")
64
- ret.gsub!(/"/, """)
65
- ret.gsub!(/</, "&lt;")
66
- ret.gsub!(/>/, "&gt;")
67
- ret
68
- end
69
- end
70
-
71
- class APIError < ::RuntimeError
72
- attr_accessor :req, :reason
73
- def initialize(req, reason = '')
74
- self.req = req
75
- self.reason = reason
76
- end
77
- def to_s
78
- "NexposeAPI: #{self.reason}"
79
- end
80
- end
81
-
82
- class AuthenticationFailed < APIError
83
- def initialize(req)
84
- self.req = req
85
- self.reason = "Login Failed"
86
- end
87
- end
88
-
89
- module XMLUtils
90
- def parse_xml(xml)
91
- ::REXML::Document.new(xml.to_s)
92
- end
93
- end
94
-
95
- class APIRequest
96
- include XMLUtils
97
-
98
- attr_reader :http
99
- attr_reader :uri
100
- attr_reader :headers
101
- attr_reader :retry_count
102
- attr_reader :time_out
103
- attr_reader :pause
104
-
105
- attr_reader :req
106
- attr_reader :res
107
- attr_reader :sid
108
- attr_reader :success
109
-
110
- attr_reader :error
111
- attr_reader :trace
112
-
113
- attr_reader :raw_response
114
- attr_reader :raw_response_data
115
-
116
- def initialize(req, url, api_version='1.1')
117
- @url = url
118
- @req = req
119
- @api_version = api_version
120
- @url = @url.sub('API_VERSION', @api_version)
121
- prepare_http_client
122
- end
123
-
124
- def prepare_http_client
125
- @retry_count = 0
126
- @retry_count_max = 10
127
- @time_out = 30
128
- @pause = 2
129
- @uri = URI.parse(@url)
130
- @http = Net::HTTP.new(@uri.host, @uri.port)
131
- @http.use_ssl = true
132
- #
133
- # XXX: This is obviously a security issue, however, we handle this at the client level by forcing
134
- # a confirmation when the nexpose host is not localhost. In a perfect world, we would present
135
- # the server signature before accepting it, but this requires either a direct callback inside
136
- # of this module back to whatever UI, or opens a race condition between accept and attempt.
137
- #
138
- @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
139
- @headers = {'Content-Type' => 'text/xml'}
140
- @success = false
141
- end
142
-
143
- def execute
144
- @conn_tries = 0
145
-
146
- begin
147
- prepare_http_client
148
- @raw_response = @http.post(@uri.path, @req, @headers)
149
- @raw_response_data = @raw_response.read_body
150
- @res = parse_xml(@raw_response_data)
151
-
152
- if(not @res.root)
153
- @error = "NeXpose service returned invalid XML"
154
- return @sid
155
- end
156
-
157
- @sid = attributes['session-id']
158
-
159
- if(attributes['success'] and attributes['success'].to_i == 1)
160
- @success = true
161
- elsif @api_version =~ /1.2/ and @res and (@res.get_elements '//Exception').count < 1
162
- @success = true
163
- else
164
- @success = false
165
- @res.elements.each('//Failure/Exception') do |s|
166
- s.elements.each('message') do |m|
167
- @error = m.text
168
- end
169
- s.elements.each('stacktrace') do |m|
170
- @trace = m.text
171
- end
172
- end
173
- end
174
- # This is a hack to handle corner cases where a heavily loaded NeXpose instance
175
- # drops our HTTP connection before processing. We try 5 times to establish a
176
- # connection in these situations. The actual exception occurs in the Ruby
177
- # http library, which is why we use such generic error classes.
178
- rescue ::ArgumentError, ::NoMethodError
179
- if @conn_tries < 5
180
- @conn_tries += 1
181
- retry
182
- end
183
- rescue ::Timeout::Error
184
- if @conn_tries < 5
185
- @conn_tries += 1
186
- retry
187
- end
188
- @error = "NeXpose host did not respond"
189
- rescue ::Errno::EHOSTUNREACH,::Errno::ENETDOWN,::Errno::ENETUNREACH,::Errno::ENETRESET,::Errno::EHOSTDOWN,::Errno::EACCES,::Errno::EINVAL,::Errno::EADDRNOTAVAIL
190
- @error = "NeXpose host is unreachable"
191
- # Handle console-level interrupts
192
- rescue ::Interrupt
193
- @error = "received a user interrupt"
194
- rescue ::Errno::ECONNRESET,::Errno::ECONNREFUSED,::Errno::ENOTCONN,::Errno::ECONNABORTED
195
- @error = "NeXpose service is not available"
196
- rescue ::REXML::ParseException
197
- @error = "NeXpose has not been properly licensed"
198
- end
199
-
200
- if ! (@success or @error)
201
- @error = "NeXpose service returned an unrecognized response: #{@raw_response_data.inspect}"
202
- end
203
-
204
- @sid
205
- end
206
-
207
- def attributes(*args)
208
- return if not @res.root
209
- @res.root.attributes(*args)
210
- end
211
-
212
- def self.execute(url, req, api_version='1.1')
213
- obj = self.new(req, url, api_version)
214
- obj.execute
215
- if(not obj.success)
216
- raise APIError.new(obj, "Action failed: #{obj.error}")
217
- end
218
- obj
219
- end
220
-
221
- end
222
-
223
- module NexposeAPI
224
-
225
- def make_xml(name, opts={}, data='', append_session_id=true)
226
- xml = REXML::Element.new(name)
227
- if(@session_id and append_session_id)
228
- xml.attributes['session-id'] = @session_id
229
- end
230
-
231
- opts.keys.each do |k|
232
- xml.attributes[k] = "#{opts[k]}"
233
- end
234
-
235
- xml.text = data
236
-
237
- xml
238
- end
239
-
240
- def scan_stop(param)
241
- r = execute(make_xml('ScanStopRequest', { 'scan-id' => param }))
242
- r.success
243
- end
244
-
245
- def scan_status(param)
246
- r = execute(make_xml('ScanStatusRequest', { 'scan-id' => param }))
247
- r.success ? r.attributes['status'] : nil
248
- end
249
-
250
- def scan_activity
251
- r = execute(make_xml('ScanActivityRequest', { }))
252
- if(r.success)
253
- res = []
254
- r.res.elements.each("//ScanSummary") do |scan|
255
- res << {
256
- :scan_id => scan.attributes['scan-id'].to_i,
257
- :site_id => scan.attributes['site-id'].to_i,
258
- :engine_id => scan.attributes['engine-id'].to_i,
259
- :status => scan.attributes['status'].to_s,
260
- :start_time => Date.parse(scan.attributes['startTime'].to_s).to_time
261
- }
262
- end
263
- return res
264
- else
265
- return false
266
- end
267
- end
268
-
269
- def scan_statistics(param)
270
- r = execute(make_xml('ScanStatisticsRequest', {'scan-id' => param }))
271
- if(r.success)
272
- res = {}
273
- r.res.elements.each("//ScanSummary/nodes") do |node|
274
- res[:nodes] = {}
275
- node.attributes.keys.each do |k|
276
- res[:nodes][k] = node.attributes[k].to_i
277
- end
278
- end
279
- r.res.elements.each("//ScanSummary/tasks") do |task|
280
- res[:task] = {}
281
- task.attributes.keys.each do |k|
282
- res[:task][k] = task.attributes[k].to_i
283
- end
284
- end
285
- r.res.elements.each("//ScanSummary/vulnerabilities") do |vuln|
286
- res[:vulns] ||= {}
287
- k = vuln.attributes['status'] + (vuln.attributes['severity'] ? ("-" + vuln.attributes['severity']) : '')
288
- res[:vulns][k] = vuln.attributes['count'].to_i
289
- end
290
- r.res.elements.each("//ScanSummary") do |summ|
291
- res[:summary] = {}
292
- summ.attributes.keys.each do |k|
293
- res[:summary][k] = summ.attributes[k]
294
- if (res[:summary][k] =~ /^\d+$/)
295
- res[:summary][k] = res[:summary][k].to_i
296
- end
297
- end
298
- end
299
- r.res.elements.each("//ScanSummary/message") do |message|
300
- res[:message] = message.text
301
- end
302
- return res
303
- else
304
- return false
305
- end
306
- end
307
-
308
- def report_generate(param)
309
- r = execute(make_xml('ReportGenerateRequest', { 'report-id' => param }))
310
- r.success
311
- end
312
-
313
- def report_last(param)
314
- r = execute(make_xml('ReportHistoryRequest', { 'reportcfg-id' => param }))
315
- res = nil
316
- if(r.success)
317
- stk = []
318
- r.res.elements.each("//ReportSummary") do |rep|
319
- stk << [ rep.attributes['id'].to_i, rep.attributes['report-URI'] ]
320
- end
321
- if (stk.length > 0)
322
- stk.sort!{|a,b| b[0] <=> a[0]}
323
- res = stk[0][1]
324
- end
325
- end
326
- res
327
- end
328
-
329
- def report_history(param)
330
- execute(make_xml('ReportHistoryRequest', { 'reportcfg-id' => param }))
331
- end
332
-
333
- def report_config_delete(param)
334
- r = execute(make_xml('ReportDeleteRequest', { 'reportcfg-id' => param }))
335
- r.success
336
- end
337
-
338
- def report_delete(param)
339
- r = execute(make_xml('ReportDeleteRequest', { 'report-id' => param }))
340
- r.success
341
- end
342
-
343
- def device_delete(param)
344
- r = execute(make_xml('DeviceDeleteRequest', { 'site-id' => param }))
345
- r.success
346
- end
347
-
348
- def asset_group_delete(connection, id, debug = false)
349
- r = execute(make_xml('AssetGroupDeleteRequest', { 'group-id' => param }))
350
- r.success
351
- end
352
-
353
- #
354
- # Create a NeXpose ticket
355
- #
356
- # ticket_info: A hash of the data to be used to create a ticket in NeXpose:
357
- # :name => The name of the ticket (Required)
358
- # :device_id => The NeXpose device ID for the device being ticketed (Required)
359
- # :assigned_to => The NeXpose user to whom this ticket is assigned (Required)
360
- # :priority => "low,moderate,normal,high,critical" (Required)
361
- #
362
- # :vulnerabilities => An array of NeXpose vuln IDs. This is NOT the same as vuln ID. (Required)
363
- # :comments => An array of comments to accompany this ticket
364
- #
365
- # @return The ticket ID if the ticket creation was successful, {@code false} otherwise
366
- #
367
- def create_ticket ticket_info
368
- ticket_name = ticket_info[:name]
369
- unless ticket_name
370
- raise ArgumentError.new 'Ticket name is required'
371
- end
372
-
373
- device_id = ticket_info[:device_id]
374
- unless device_id
375
- raise ArgumentError.new 'Device ID is required'
376
- end
377
-
378
- assigned_to = ticket_info[:assigned_to]
379
- unless assigned_to
380
- raise ArgumentError.new 'Assignee name is required'
381
- end
382
-
383
- priority = ticket_info[:priority]
384
- unless priority
385
- raise ArgumentError.new 'Ticket priority is required'
386
- end
387
-
388
- vulnerabilities = ticket_info[:vulnerabilities]
389
- if not vulnerabilities or vulnerabilities.count < 1
390
- raise ArgumentError.new 'Vulnerabilities are required'
391
- end
392
-
393
- comments = ticket_info[:comments]
394
- base_xml = make_xml 'TicketCreateRequest'
395
-
396
- required_attributes = {
397
- 'name' => ticket_name,
398
- 'priority' => priority,
399
- 'device-id' => device_id,
400
- 'assigned-to' => assigned_to
401
- }
402
-
403
- create_request_xml = REXML::Element.new 'TicketCreate'
404
- create_request_xml.add_attributes required_attributes
405
-
406
- # Add vulnerabilities
407
- vulnerabilities_xml = REXML::Element.new 'Vulnerabilities'
408
- vulnerabilities.each do |vuln_id|
409
- vulnerabilities_xml.add_element 'Vulnerability', {'id' => vuln_id}
410
- end
411
- create_request_xml.add_element vulnerabilities_xml
412
-
413
- # Add comments
414
- if comments and comments.count > 0
415
- comments_xml = REXML::Element.new 'Comments'
416
- comments.each do |comment|
417
- comment_xml = REXML::Element.new 'Comment'
418
- comment_xml.add_text comment
419
- comments_xml.add_element comment_xml
420
- end
421
-
422
- create_request_xml.add_element comments_xml
423
- end
424
-
425
- base_xml.add_element create_request_xml
426
- r = execute base_xml, '1.2'
427
- if r.success
428
- r.res.elements.each('TicketCreateResponse') do |group|
429
- return group.attributes['id'].to_i
430
- end
431
- else
432
- false
433
- end
434
- end
435
-
436
- #
437
- # Deletes a NeXpose ticket.
438
- #
439
- # ticket_ids: An array of ticket IDs to be deleted.
440
- #
441
- # @returns {@code true} iff the call was successfull. {@code false} otherwise.
442
- #
443
- def delete_ticket ticket_ids
444
- if not ticket_ids or ticket_ids.count < 1
445
- raise ArgumentError.new 'The tickets to delete should not be null or empty'
446
- end
447
-
448
- base_xml = make_xml 'TicketDeleteRequest'
449
- ticket_ids.each do |id|
450
- base_xml.add_element 'Ticket', {'id' => id}
451
- end
452
-
453
- (execute base_xml, '1.2').success
454
- end
455
-
456
- #-------------------------------------------------------------------------
457
- # Returns all asset group information
458
- #-------------------------------------------------------------------------
459
- def asset_groups_listing()
460
- r = execute(make_xml('AssetGroupListingRequest'))
461
-
462
- if r.success
463
- res = []
464
- r.res.elements.each('//AssetGroupSummary') do |group|
465
- res << {
466
- :asset_group_id => group.attributes['id'].to_i,
467
- :name => group.attributes['name'].to_s,
468
- :description => group.attributes['description'].to_s,
469
- :risk_score => group.attributes['riskscore'].to_f,
470
- }
471
- end
472
- res
473
- else
474
- false
475
- end
476
- end
477
-
478
- #-------------------------------------------------------------------------
479
- # Returns an asset group configuration information for a specific group ID
480
- #-------------------------------------------------------------------------
481
- def asset_group_config(group_id)
482
- r = execute(make_xml('AssetGroupConfigRequest', {'group-id' => group_id}))
483
-
484
- if r.success
485
- res = []
486
- r.res.elements.each('//Devices/device') do |device_info|
487
- res << {
488
- :device_id => device_info.attributes['id'].to_i,
489
- :site_id => device_info.attributes['site-id'].to_i,
490
- :address => device_info.attributes['address'].to_s,
491
- :riskfactor => device_info.attributes['riskfactor'].to_f,
492
- }
493
- end
494
- res
495
- else
496
- false
497
- end
498
- end
499
-
500
- #-----------------------------------------------------------------------
501
- # Starts device specific site scanning.
502
- #
503
- # devices - An Array of device IDs
504
- # hosts - An Array of Hashes [o]=>{:range=>"to,from"} [1]=>{:host=>host}
505
- #-----------------------------------------------------------------------
506
- def site_device_scan_start(site_id, devices, hosts)
507
-
508
- if hosts == nil and devices == nil
509
- raise ArgumentError.new("Both the device and host list is nil")
510
- end
511
-
512
- xml = make_xml('SiteDevicesScanRequest', {'site-id' => site_id})
513
-
514
- if devices != nil
515
- inner_xml = REXML::Element.new 'Devices'
516
- for device_id in devices
517
- inner_xml.add_element 'device', {'id' => "#{device_id}"}
518
- end
519
- xml.add_element inner_xml
520
- end
521
-
522
- if hosts != nil
523
- inner_xml = REXML::Element.new 'Hosts'
524
- hosts.each_index do |x|
525
- if hosts[x].key? :range
526
- to = hosts[x][:range].split(',')[0]
527
- from = hosts[x][:range].split(',')[1]
528
- inner_xml.add_element 'range', {'to' => "#{to}", 'from' => "#{from}"}
529
- end
530
- if hosts[x].key? :host
531
- host_element = REXML::Element.new 'host'
532
- host_element.text = "#{hosts[x][:host]}"
533
- inner_xml.add_element host_element
534
- end
535
- end
536
- xml.add_element inner_xml
537
- end
538
-
539
- r = execute xml
540
- if r.success
541
- r.res.elements.each('//Scan') do |scan_info|
542
- return {
543
- :scan_id => scan_info.attributes['scan-id'].to_i,
544
- :engine_id => scan_info.attributes['engine-id'].to_i
545
- }
546
- end
547
- else
548
- false
549
- end
550
- end
551
-
552
- #
553
- # Lists all the users for the NSC along with the user details.
554
- #
555
- def list_users
556
- r = execute(make_xml('UserListingRequest'))
557
- if r.success
558
- res = []
559
- r.res.elements.each('//UserSummary') do |user_summary|
560
- res << {
561
- :auth_source => user_summary.attributes['authSource'],
562
- :auth_module => user_summary.attributes['authModule'],
563
- :user_name => user_summary.attributes['userName'],
564
- :full_name => user_summary.attributes['fullName'],
565
- :email => user_summary.attributes['email'],
566
- :is_admin => user_summary.attributes['isAdmin'].to_s.chomp.eql?('1'),
567
- :is_disabled => user_summary.attributes['disabled'].to_s.chomp.eql?('1'),
568
- :site_count => user_summary.attributes['siteCount'],
569
- :group_count => user_summary.attributes['groupCount']
570
- }
571
- end
572
- res
573
- else
574
- false
575
- end
576
- end
577
-
578
- def site_delete(param)
579
- r = execute(make_xml('SiteDeleteRequest', { 'site-id' => param }))
580
- r.success
581
- end
582
-
583
- def site_listing
584
- r = execute(make_xml('SiteListingRequest', { }))
585
-
586
- if(r.success)
587
- res = []
588
- r.res.elements.each("//SiteSummary") do |site|
589
- res << {
590
- :site_id => site.attributes['id'].to_i,
591
- :name => site.attributes['name'].to_s,
592
- :risk_factor => site.attributes['riskfactor'].to_f,
593
- :risk_score => site.attributes['riskscore'].to_f,
594
- }
595
- end
596
- return res
597
- else
598
- return false
599
- end
600
- end
601
-
602
- #-----------------------------------------------------------------------
603
- # TODO: Needs to be expanded to included details
604
- #-----------------------------------------------------------------------
605
- def site_scan_history(site_id)
606
- r = execute(make_xml('SiteScanHistoryRequest', {'site-id' => site_id.to_s}))
607
-
608
- if (r.success)
609
- res = []
610
- r.res.elements.each("//ScanSummary") do |site_scan_history|
611
- res << {
612
- :site_id => site_scan_history.attributes['site-id'].to_i,
613
- :scan_id => site_scan_history.attributes['scan-id'].to_i,
614
- :engine_id => site_scan_history.attributes['engine-id'].to_i,
615
- :start_time => site_scan_history.attributes['startTime'].to_s,
616
- :end_time => site_scan_history.attributes['endTime'].to_s
617
- }
618
- end
619
- res
620
- else
621
- false
622
- end
623
- end
624
-
625
- ###################
626
- # VULN EXCEPTIONS #
627
- ###################
628
-
629
- #-----------------------------------------------------------------------
630
- # Returns an array of vulnerability exceptions and their associated
631
- # attributes.
632
- #
633
- # @param status - (optional) The status of the vulnerability exception:
634
- # "Under Review", "Approved", "Rejected"
635
- #-----------------------------------------------------------------------
636
- def vuln_listing status=nil
637
- option = {}
638
-
639
- if status && !status.empty?
640
- if status =~ /Under Review|Approved|Rejected/
641
- option['status'] = status
642
- else
643
- raise ArgumentError.new 'The vulnerability status passed in is invalid!'
644
- end
645
- end
646
-
647
- xml = make_xml('VulnerabilityExceptionListingRequest', option)
648
- r = execute xml, '1.2'
649
-
650
- if r.success
651
- res = []
652
- r.res.elements.each("//VulnerabilityException") do |ve|
653
- submitter_comment = ve.elements['submitter-comment']
654
- reviewer_comment = ve.elements['reviewer-comment']
655
- res << {
656
- :vuln_id => ve.attributes['vuln-id'],
657
- :exception_id => ve.attributes['exception-id'],
658
- :submitter => ve.attributes['submitter'],
659
- :reviewer => ve.attributes['reviewer'],
660
- :status => ve.attributes['status'],
661
- :reason => ve.attributes['reason'],
662
- :scope => ve.attributes['scope'],
663
- :device_id => ve.attributes['device-id'],
664
- :port_no => ve.attributes['port-no'],
665
- :expiration_date => ve.attributes['expiration-date'],
666
- :vuln_key => ve.attributes['vuln-key'],
667
- :submitter_comment => submitter_comment.nil? ? '' : submitter_comment.text,
668
- :reviewer_comment => reviewer_comment.nil? ? '' : reviewer_comment.text
669
- }
670
- end
671
- res
672
- else
673
- false
674
- end
675
- end
676
-
677
- #-------------------------------------------------------------------------------------------------------------------
678
- # Creates a vulnerability exception.
679
- #
680
- # @param input - data used to create the vulnerability exception:
681
- # :vuln_id - The Nexpose vulnerability ID.
682
- # :reason - The reason for the exception
683
- # values - "False Positive", "Compensating Control", "Acceptable Use", "Acceptable Risk", "Other"
684
- # :scope - The scope type (NOTE: The case is important)
685
- # values - "All Instances", "All Instances on a Specific Asset", "Specific Instance of a specific Asset"
686
- # :comment - A user comment
687
- # :device-id - Used for specific instances related to "All Instances on a Specific Asset" AND "Specific Instance of Specific Asset"
688
- # :port - All assets on this port related to "Specific Instance of a specific Asset"
689
- # :vuln-key - The vulnerability key related to the "Specific Instance of a specific Asset"
690
- #
691
- # @returns exception-id - The Id associated with this create request
692
- #-------------------------------------------------------------------------------------------------------------------
693
- def vuln_exception_create input
694
- options = {}
695
-
696
- if input.nil?
697
- raise ArgumentError.new 'The input element cannot be null'
698
- end
699
-
700
- vuln_id = input[:vuln_id]
701
- if !vuln_id
702
- raise ArgumentError.new 'The vulnerability ID is required'
703
- end
704
- options['vuln-id'] = vuln_id
705
-
706
- reason = input[:reason]
707
- if reason.nil? || reason.empty?
708
- raise ArgumentError.new 'The reason is required'
709
- end
710
-
711
- unless reason =~ /False Positive|Compensating Control|Acceptable Use|Acceptable Risk|Other/
712
- raise ArgumentError.new 'The reason type is invalid'
713
- end
714
- options['reason'] = reason
715
-
716
- scope = input[:scope]
717
- if scope.nil? || scope.empty?
718
- raise ArgumentError.new 'The scope is required'
719
- end
720
-
721
- # For scope case matters.
722
- unless scope =~ /All Instances|All Instances on a Specific Asset|Specific Instance of Specific Asset/
723
- raise ArgumentError.new 'The scope type is invalid'
724
- end
725
-
726
- if scope =~ /All Instances on a Specific Asset|Specific Instance of Specific Asset/
727
- device_id = input[:device_id]
728
- vuln_key = input[:vuln_key]
729
- port = input[:port]
730
- if device_id
731
- options['device-id'] = device_id
732
- end
733
-
734
- if (scope =~ /All Instances on a Specific Asset/ && (vuln_key || port))
735
- raise ArgumentError.new "Vulnerability key or port cannot be used with the scope specified"
736
- end
737
-
738
- if vuln_key
739
- options['vuln-key'] = vuln_key
740
- end
741
-
742
- if port
743
- options['port-no'] = port
744
- end
745
- end
746
- options['scope'] = scope
747
-
748
- xml = make_xml('VulnerabilityExceptionCreateRequest', options)
749
-
750
- comment = input[:comment]
751
- if comment && !comment.empty?
752
- comment_xml = make_xml('comment', {}, comment, false)
753
- xml.add_element comment_xml
754
- else
755
- raise ArgumentError.new 'The comment cannot be empty'
756
- end
757
-
758
- r = execute xml, '1.2'
759
- if r.success
760
- r.res.elements.each("//VulnerabilityExceptionCreateResponse") do |vecr|
761
- return vecr.attributes['exception-id']
762
- end
763
- else
764
- false
765
- end
766
- end
767
-
768
- #-------------------------------------------------------------------------------------------------------------------
769
- # Resubmit a vulnerability exception.
770
- #
771
- # @param input - data used to create the vulnerability exception:
772
- # :vuln_id - The Nexpose vulnerability ID. (required)
773
- # :reason - The reason for the exception (optional)
774
- # values - "False Positive", "Compensating Control", "Acceptable Use", "Acceptable Risk", "Other"
775
- # :comment - A user comment (required)
776
- #-------------------------------------------------------------------------------------------------------------------
777
- def vuln_exception_resubmit input
778
- options = {}
779
-
780
- if input.nil?
781
- raise ArgumentError.new 'The input element cannot be null'
782
- end
783
-
784
- exception_id = input[:exception_id]
785
- if !exception_id
786
- raise ArgumentError.new 'The exception ID is required'
787
- end
788
- options['exception-id'] = exception_id
789
-
790
- reason = input[:reason]
791
- if !reason.nil? && !reason.empty?
792
- unless reason =~ /False Positive|Compensating Control|Acceptable Use|Acceptable Risk|Other/
793
- raise ArgumentError.new 'The reason type is invalid'
794
- end
795
- options['reason'] = reason
796
-
797
- end
798
-
799
- xml = make_xml('VulnerabilityExceptionResubmitRequest', options)
800
-
801
- comment = input[:comment]
802
- if comment && !comment.empty?
803
- comment_xml = make_xml('comment', {}, comment, false)
804
- xml.add_element comment_xml
805
- end
806
-
807
- r = execute xml, '1.2'
808
- r.success
809
- end
810
-
811
- #-------------------------------------------------------------------------------------------------------------------
812
- # Allows a previously submitted exception that has not been approved to be withdrawn.
813
- #
814
- # @param exception_id - The exception id returned after the vuln exception was submitted for creation.
815
- #-------------------------------------------------------------------------------------------------------------------
816
- def vuln_exception_recall exception_id
817
- xml = make_xml('VulnerabilityExceptionRecallRequest', {'exception-id' => exception_id})
818
- r = execute xml, '1.2'
819
- r.success
820
- end
821
-
822
-
823
- #-------------------------------------------------------------------------------------------------------------------
824
- # Allows a submitted vulnerability exception to be approved.
825
- #
826
- # @param input:
827
- # :exception_id - The exception id returned after the vuln exception was submitted for creation.
828
- # :comment - An optional comment
829
- #-------------------------------------------------------------------------------------------------------------------
830
- def vuln_exception_approve input
831
- exception_id = input[:exception_id]
832
- if !exception_id
833
- raise ArgumentError.new 'Exception Id is required'
834
- end
835
-
836
- xml = make_xml('VulnerabilityExceptionApproveRequest', {'exception-id' => exception_id})
837
- comment = input[:comment]
838
- if comment && !comment.empty?
839
- comment_xml = make_xml('comment', {}, comment, false)
840
- xml.add_element comment_xml
841
- end
842
-
843
- r = execute xml, '1.2'
844
- r.success
845
- end
846
-
847
- #-------------------------------------------------------------------------------------------------------------------
848
- # Rejects a submitted vulnerability exception to be approved.
849
- #
850
- # @param input:
851
- # :exception_id - The exception id returned after the vuln exception was submitted for creation.
852
- # :comment - An optional comment
853
- #-------------------------------------------------------------------------------------------------------------------
854
- def vuln_exception_reject input
855
- exception_id = input[:exception_id]
856
- if !exception_id
857
- raise ArgumentError.new 'Exception Id is required'
858
- end
859
-
860
- xml = make_xml('VulnerabilityExceptionRejectRequest', {'exception-id' => exception_id})
861
- comment = input[:comment]
862
- if comment && !comment.empty?
863
- comment_xml = make_xml('comment', {}, comment, false)
864
- xml.add_element comment_xml
865
- end
866
-
867
- r = execute xml, '1.2'
868
- r.success
869
- end
870
-
871
- #-------------------------------------------------------------------------------------------------------------------
872
- # Updates a vulnerability exception comment.
873
- #
874
- # @param input:
875
- # :exception_id - The exception id returned after the vuln exception was submitted for creation.
876
- # :submitter_comment - The submitter comment
877
- # :reviewer_comment - The reviewer comment
878
- #-------------------------------------------------------------------------------------------------------------------
879
- def vuln_exception_update_comment input
880
- exception_id = input[:exception_id]
881
- if !exception_id
882
- raise ArgumentError.new 'Exception Id is required'
883
- end
884
-
885
- xml = make_xml('VulnerabilityExceptionUpdateCommentRequest', {'exception-id' => exception_id})
886
- submitter_comment = input[:submitter_comment]
887
- if submitter_comment && !submitter_comment.empty?
888
- comment_xml = make_xml('submitter-comment', {}, submitter_comment, false)
889
- xml.add_element comment_xml
890
- end
891
-
892
- reviewer_comment = input[:reviewer_comment]
893
- if reviewer_comment && !reviewer_comment.empty?
894
- comment_xml = make_xml('reviewer-comment', {}, reviewer_comment, false)
895
- xml.add_element comment_xml
896
- end
897
-
898
- r = execute xml, '1.2'
899
- r.success
900
- end
901
-
902
- #-------------------------------------------------------------------------------------------------------------------
903
- # Update the expiration date for a vulnerability exception.
904
- #
905
- # @param input
906
- # :exception_id - The exception id returned after the vulnerability exception was submitted for creation.
907
- # :expiration_date - The new expiration date format: YYYY-MM-DD
908
- #-------------------------------------------------------------------------------------------------------------------
909
- def vuln_exception_update_expiration_date input
910
- exception_id = input[:exception_id]
911
- if !exception_id
912
- raise ArgumentError.new 'Exception Id is required'
913
- end
914
-
915
- expiration_date = input[:expiration_date]
916
- if expiration_date && !expiration_date.empty? && expiration_date =~ /\A\d{4}-(\d{2})-(\d{2})\z/
917
- if $1.to_i > 12
918
- raise ArgumentError.new 'The expiration date month value is invalid'
919
- end
920
-
921
- if $2.to_i > 31
922
- raise ArgumentError.new 'The expiration date day value is invalid'
923
- end
924
- else
925
- raise ArgumentError.new 'Expiration date is invalid'
926
- end
927
-
928
- options = {}
929
- options['exception-id'] = exception_id
930
- options['expiration-date'] = expiration_date
931
- xml = make_xml('VulnerabilityExceptionUpdateExpirationDateRequest', options)
932
- r = execute xml, '1.2'
933
- r.success
934
- end
935
-
936
- #-------------------------------------------------------------------------------------------------------------------
937
- # Deletes a submitted vulnerability exception to be approved.
938
- #
939
- # @param exception_id - The exception id returned after the vuln exception was submitted for creation.
940
- #-------------------------------------------------------------------------------------------------------------------
941
- def vuln_exception_delete exception_id
942
- if !exception_id
943
- raise ArgumentError.new 'Exception Id is required'
944
- end
945
-
946
- xml = make_xml('VulnerabilityExceptionDeleteRequest', {'exception-id' => exception_id})
947
- r = execute xml, '1.2'
948
- r.success
949
- end
950
-
951
- ###################
952
- # SILO MANAGEMENT #
953
- ###################
954
-
955
- #########################
956
- # MULTI-TENANT USER OPS #
957
- #########################
958
-
959
- #-------------------------------------------------------------------------
960
- # Creates a multi-tenant user
961
- #
962
- # user_config - A map of the user data.
963
- #
964
- # REQUIRED PARAMS
965
- # user-id, authsrcid, user-name, full-name, enabled, superuser
966
- #
967
- # OPTIONAL PARAMS
968
- # email, password
969
- #
970
- # silo_configs - An array of maps of silo specific data
971
- #
972
- # REQUIRED PARAMS
973
- # silo-id, role-name, all-groups, all-sites, default-silo
974
- #
975
- # allowed_groups/allowed_sites - An array of ids
976
- #-------------------------------------------------------------------------
977
- def create_multi_tenant_user(user_config, silo_configs)
978
- xml = make_xml('MultiTenantUserCreateRequest')
979
- mtu_config_xml = make_xml('MultiTenantUserConfig', user_config, '', false)
980
-
981
- # Add the silo access
982
- silo_xml = make_xml('SiloAccesses', {}, '', false)
983
- silo_config_xml = make_xml('SiloAccess', {}, '', false)
984
- silo_configs.keys.each do |k|
985
- if k.eql? 'allowed_sites'
986
- allowed_sites_xml = make_xml('AllowedSites', {}, '', false)
987
- silo_configs['allowed_sites'].each do |allowed_site|
988
- allowed_sites_xml.add_element make_xml('AllowedSite', {'id' => allowed_site}, '', false)
989
- end
990
- silo_config_xml.add_element allowed_sites_xml
991
- elsif k.eql? 'allowed_groups'
992
- allowed_groups_xml = make_xml('AllowedGroups', {}, '', false)
993
- silo_configs['allowed_groups'].each do |allowed_group|
994
- allowed_groups_xml.add_element make_xml('AllowedGroup', {'id' => allowed_group}, '', false)
995
- end
996
- silo_config_xml.add_element allowed_groups_xml
997
- else
998
- silo_config_xml.attributes[k] = silo_configs[k]
999
- end
1000
- end
1001
- silo_xml.add_element silo_config_xml
1002
- mtu_config_xml.add_element silo_xml
1003
- xml.add_element mtu_config_xml
1004
- r = execute xml, '1.2'
1005
- r.success
1006
- end
1007
-
1008
-
1009
- #-------------------------------------------------------------------------
1010
- # Lists all the multi-tenant users and their attributes.
1011
- #-------------------------------------------------------------------------
1012
- def list_mtu
1013
- xml = make_xml('MultiTenantUserListingRequest')
1014
- r = execute xml, '1.2'
1015
-
1016
- if r.success
1017
- res = []
1018
- r.res.elements.each("//MultiTenantUserSummary") do |mtu|
1019
- res << {
1020
- :id => mtu.attributes['id'],
1021
- :full_name => mtu.attributes['full-name'],
1022
- :user_name => mtu.attributes['user-name'],
1023
- :email => mtu.attributes['email'],
1024
- :super_user => mtu.attributes['superuser'],
1025
- :enabled => mtu.attributes['enabled'],
1026
- :auth_module => mtu.attributes['auth-module'],
1027
- :silo_count => mtu.attributes['silo-count'],
1028
- :locked => mtu.attributes['locked']
1029
- }
1030
- end
1031
- res
1032
- else
1033
- false
1034
- end
1035
- end
1036
-
1037
- #-------------------------------------------------------------------------
1038
- # Delete a multi-tenant user
1039
- #-------------------------------------------------------------------------
1040
- def delete_mtu user_name, user_id
1041
- using_user_name = (user_name and not user_name.empty?)
1042
- xml = make_xml('MultiTenantUserDeleteRequest', (using_user_name ? {'user-name' => user_name} : {'user-id' => user_id}))
1043
- r = execute xml, '1.2'
1044
- r.success
1045
- end
1046
-
1047
- ####################
1048
- # SILO PROFILE OPS #
1049
- ####################
1050
-
1051
- #-------------------------------------------------------------------------
1052
- # Creates a silo profile
1053
- #
1054
- # silo_config - A map of the silo data.
1055
- #
1056
- # REQUIRED PARAMS
1057
- # id, name, all‐licensed-modules, all‐global-engines, all-global-report-templates, all‐global-scan‐templates
1058
- #
1059
- # OPTIONAL PARAMS
1060
- # description
1061
- #
1062
- # permissions - A map of an array of maps of silo specific data
1063
- #
1064
- # REQUIRED PARAMS
1065
- # silo-id, role-name, all-groups, all-sites, default-silo
1066
- #
1067
- # allowed_groups/allowed_sites - An array of ids
1068
- #-------------------------------------------------------------------------
1069
- def create_silo_profile silo_profile_config, permissions
1070
- xml = make_xml 'SiloProfileCreateRequest'
1071
- spc_xml = make_xml('SiloProfileConfig', silo_profile_config, '', false)
1072
-
1073
- # Add the permissions
1074
- if permissions['global_report_templates']
1075
- grt_xml = make_xml('GlobalReportTemplates', {}, '', false)
1076
- permissions['global_report_templates'].each do |name|
1077
- grt_xml.add_element make_xml('GlobalReportTemplate', {'name' => name}, '', false)
1078
- end
1079
- spc_xml.add_element grt_xml
1080
- end
1081
-
1082
- if permissions['global_scan_engines']
1083
- gse_xml = make_xml('GlobalScanEngines', {}, '', false)
1084
- permissions['global_scan_engines'].each do |name|
1085
- gse_xml.add_element make_xml('GlobalScanEngine', {'name' => name}, '', false)
1086
- end
1087
- spc_xml.add_element gse_xml
1088
- end
1089
-
1090
- if permissions['global_scan_templates']
1091
- gst_xml = make_xml('GlobalScanTemplates', {}, '', false)
1092
- permissions['global_scan_templates'].each do |name|
1093
- gst_xml.add_element make_xml('GlobalScanTemplate', {'name' => name}, '', false)
1094
- end
1095
- spc_xml.add_element gst_xml
1096
- end
1097
-
1098
- if permissions['licensed_modules']
1099
- lm_xml = make_xml('LicensedModules', {}, '', false)
1100
- permissions['licensed_modules'].each do |name|
1101
- lm_xml.add_element make_xml('LicensedModule', {'name' => name}, '', false)
1102
- end
1103
- spc_xml.add_element lm_xml
1104
- end
1105
-
1106
- if permissions['restricted_report_formats']
1107
- rrf_xml = make_xml('RestrictedReportFormats', {}, '', false)
1108
- permissions['restricted_report_formats'].each do |name|
1109
- rrf_xml.add_element make_xml('RestrictedReportFormat', {'name' => name}, '', false)
1110
- end
1111
- spc_xml.add_element rrf_xml
1112
- end
1113
-
1114
- if permissions['restricted_report_sections']
1115
- rrs_xml = make_xml('RestrictedReportSections', {}, '', false)
1116
- permissions['restricted_report_sections'].each do |name|
1117
- rrs_xml.add_element make_xml('RestrictedReportSection', {'name' => name}, '', false)
1118
- end
1119
- spc_xml.add_element rrs_xml
1120
- end
1121
-
1122
- xml.add_element spc_xml
1123
- r = execute xml, '1.2'
1124
- r.success
1125
- end
1126
-
1127
- #-------------------------------------------------------------------------
1128
- # Lists all the silo profiles and their attributes.
1129
- #-------------------------------------------------------------------------
1130
- def list_silo_profiles
1131
- xml = make_xml('SiloProfileListingRequest')
1132
- r = execute xml, '1.2'
1133
-
1134
- if r.success
1135
- res = []
1136
- r.res.elements.each("//SiloProfileSummary") do |silo_profile|
1137
- res << {
1138
- :id => silo_profile.attributes['id'],
1139
- :name => silo_profile.attributes['name'],
1140
- :description => silo_profile.attributes['description'],
1141
- :global_report_template_count => silo_profile.attributes['global-report-template-count'],
1142
- :global_scan_engine_count => silo_profile.attributes['global-scan-engine-count'],
1143
- :global_scan_template_count => silo_profile.attributes['global-scan-template-count'],
1144
- :licensed_module_count => silo_profile.attributes['licensed-module-count'],
1145
- :restricted_report_section_count => silo_profile.attributes['restricted-report-section-count'],
1146
- :all_licensed_modules => silo_profile.attributes['all-licensed-modules'],
1147
- :all_global_engines => silo_profile.attributes['all-global-engines'],
1148
- :all_global_report_templates => silo_profile.attributes['all-global-report-templates'],
1149
- :all_global_scan_templates => silo_profile.attributes['all-global-scan-templates']
1150
- }
1151
- end
1152
- res
1153
- else
1154
- false
1155
- end
1156
- end
1157
-
1158
- #-------------------------------------------------------------------------
1159
- # Delete a silo profile
1160
- #-------------------------------------------------------------------------
1161
- def delete_silo_profile name, id
1162
- using_name = (name and not name.empty?)
1163
- xml = make_xml('SiloProfileDeleteRequest', (using_name ? {'name' => name} : {'silo-profile-id' => id}))
1164
- r = execute xml, '1.2'
1165
- r.success
1166
- end
1167
-
1168
- ####################
1169
- # SILO OPS #
1170
- ####################
1171
-
1172
- #-------------------------------------------------------------------------
1173
- # Creates a silo
1174
- #
1175
- # silo_config - A map of the silo creation data.
1176
- #
1177
- # REQUIRED PARAMS
1178
- # id, name, silo-profile-id, max-assets, max-hosted-assets, max-users
1179
- #
1180
- # OPTIONAL PARAMS
1181
- # description
1182
- #-------------------------------------------------------------------------
1183
- def create_silo silo_config
1184
- xml = make_xml 'SiloCreateRequest'
1185
- silo_config_xml = make_xml 'SiloConfig', {}, '', false
1186
-
1187
- # Add the attributes
1188
- silo_config.keys.each do |key|
1189
- if not 'merchant'.eql? key and not 'organization'.eql? key
1190
- silo_config_xml.attributes[key] = silo_config[key]
1191
- end
1192
- end
1193
-
1194
- # Add Organization info
1195
- if silo_config['organization']
1196
- org_xml = make_xml 'Organization', {}, '', false
1197
- silo_config['organization'].keys.each do |key|
1198
- if not 'address'.eql? key
1199
- org_xml.attributes[key] = silo_config['organization'][key]
1200
- end
1201
- end
1202
-
1203
- address_xml = make_xml 'Address', silo_config['organization']['address'], '', false
1204
- org_xml.add_element address_xml
1205
- silo_config_xml.add_element org_xml
1206
- end
1207
-
1208
- # Add Merchant info
1209
- if silo_config['merchant']
1210
- merchant_xml = make_xml 'Merchant', {}, '', false
1211
-
1212
- silo_config['merchant'].keys.each do |key|
1213
- if not 'dba'.eql? key and not 'other_industries'.eql? key and not 'qsa'.eql? key and not 'address'.eql? key
1214
- merchant_xml.attributes[key] = silo_config['merchant'][key]
1215
- end
1216
- end
1217
-
1218
- # Add the merchant address
1219
- merchant_address_xml = make_xml 'Address', silo_config['merchant']['address'], '', false
1220
- merchant_xml.add_element merchant_address_xml
1221
-
1222
- #Now add the complex data types
1223
- if silo_config['merchant']['dba']
1224
- dba_xml = make_xml 'DBAs', {}, '', false
1225
- silo_config['merchant']['dba'].each do |name|
1226
- dba_xml.add_element make_xml('DBA', {'name' => name}, '', false)
1227
- end
1228
- merchant_xml.add_element dba_xml
1229
- end
1230
-
1231
- if silo_config['merchant']['other_industries']
1232
- ois_xml = make_xml 'OtherIndustries', {}, '', false
1233
- silo_config['merchant']['other_industries'].each do |name|
1234
- ois_xml.add_element make_xml('Industry', {'name' => name}, '', false)
1235
- end
1236
- merchant_xml.add_element ois_xml
1237
- end
1238
-
1239
- if silo_config['merchant']['qsa']
1240
- qsa_xml = make_xml 'QSA', {}, '', false
1241
- silo_config['merchant']['qsa'].keys.each do |key|
1242
- if not 'address'.eql? key
1243
- qsa_xml.attributes[key] = silo_config['merchant']['qsa'][key]
1244
- end
1245
- end
1246
-
1247
- # Add the address for this QSA
1248
- address_xml = make_xml 'Address', silo_config['merchant']['qsa']['address'], '', false
1249
-
1250
- qsa_xml.add_element address_xml
1251
- merchant_xml.add_element qsa_xml
1252
- end
1253
- silo_config_xml.add_element merchant_xml
1254
- end
1255
-
1256
- xml.add_element silo_config_xml
1257
- r = execute xml, '1.2'
1258
- r.success
1259
- end
1260
-
1261
- #-------------------------------------------------------------------------
1262
- # Lists all the silos and their attributes.
1263
- #-------------------------------------------------------------------------
1264
- def list_silos
1265
- xml = make_xml('SiloListingRequest')
1266
- r = execute xml, '1.2'
1267
-
1268
- if r.success
1269
- res = []
1270
- r.res.elements.each("//SiloSummary") do |silo_profile|
1271
- res << {
1272
- :id => silo_profile.attributes['id'],
1273
- :name => silo_profile.attributes['name'],
1274
- :description => silo_profile.attributes['description']
1275
- }
1276
- end
1277
- res
1278
- else
1279
- false
1280
- end
1281
- end
1282
-
1283
- #-------------------------------------------------------------------------
1284
- # Delete a silo
1285
- #-------------------------------------------------------------------------
1286
- def delete_silo name, id
1287
- using_name = (name and not name.empty?)
1288
- xml = make_xml('SiloDeleteRequest', (using_name ? {'silo-name' => name} : {'silo-id' => id}))
1289
- r = execute xml, '1.2'
1290
- r.success
1291
- end
1292
-
1293
- def site_device_listing(site_id)
1294
- r = execute(make_xml('SiteDeviceListingRequest', { 'site-id' => site_id.to_s }))
1295
-
1296
- if(r.success)
1297
- res = []
1298
- r.res.elements.each("//device") do |device|
1299
- res << {
1300
- :device_id => device.attributes['id'].to_i,
1301
- :address => device.attributes['address'].to_s,
1302
- :risk_factor => device.attributes['risk_factor'].to_f,
1303
- :risk_score => device.attributes['risk_score'].to_f,
1304
- }
1305
- end
1306
- return res
1307
- else
1308
- return false
1309
- end
1310
- end
1311
-
1312
- def report_template_listing
1313
- r = execute(make_xml('ReportTemplateListingRequest', { }))
1314
-
1315
- if(r.success)
1316
- res = []
1317
- r.res.elements.each("//ReportTemplateSummary") do |template|
1318
- desc = ''
1319
- template.elements.each("//description") do |ent|
1320
- desc = ent.text
1321
- end
1322
-
1323
- res << {
1324
- :template_id => template.attributes['id'].to_s,
1325
- :name => template.attributes['name'].to_s,
1326
- :description => desc.to_s
1327
- }
1328
- end
1329
- return res
1330
- else
1331
- return false
1332
- end
1333
- end
1334
-
1335
-
1336
- def console_command(cmd_string)
1337
- xml = make_xml('ConsoleCommandRequest', { })
1338
- cmd = REXML::Element.new('Command')
1339
- cmd.text = cmd_string
1340
- xml << cmd
1341
-
1342
- r = execute(xml)
1343
-
1344
- if(r.success)
1345
- res = ""
1346
- r.res.elements.each("//Output") do |out|
1347
- res << out.text.to_s
1348
- end
1349
-
1350
- return res
1351
- else
1352
- return false
1353
- end
1354
- end
1355
-
1356
- def system_information
1357
- r = execute(make_xml('SystemInformationRequest', { }))
1358
-
1359
- if(r.success)
1360
- res = {}
1361
- r.res.elements.each("//Statistic") do |stat|
1362
- res[ stat.attributes['name'].to_s ] = stat.text.to_s
1363
- end
1364
-
1365
- return res
1366
- else
1367
- return false
1368
- end
1369
- end
1370
-
1371
- end
1372
-
1373
- # === Description
1374
- # Object that represents a connection to a NeXpose Security Console.
1375
- #
1376
- # === Examples
1377
- # # Create a new Nexpose Connection on the default port
1378
- # nsc = Connection.new("10.1.40.10","nxadmin","password")
1379
- #
1380
- # # Login to NSC and Establish a Session ID
1381
- # nsc.login()
1382
- #
1383
- # # Check Session ID
1384
- # if (nsc.session_id)
1385
- # puts "Login Successful"
1386
- # else
1387
- # puts "Login Failure"
1388
- # end
1389
- #
1390
- # # //Logout
1391
- # logout_success = nsc.logout()
1392
- # if (! logout_success)
1393
- # puts "Logout Failure" + "<p>" + nsc.error_msg.to_s
1394
- # end
1395
- #
1396
- class Connection
1397
- include XMLUtils
1398
- include NexposeAPI
1399
-
1400
- # true if an error condition exists; false otherwise
1401
- attr_reader :error
1402
- # Error message string
1403
- attr_reader :error_msg
1404
- # The last XML request sent by this object
1405
- attr_reader :request_xml
1406
- # The last XML response received by this object
1407
- attr_reader :response_xml
1408
- # Session ID of this connection
1409
- attr_reader :session_id
1410
- # The hostname or IP Address of the NSC
1411
- attr_reader :host
1412
- # The port of the NSC (default is 3780)
1413
- attr_reader :port
1414
- # The username used to login to the NSC
1415
- attr_reader :username
1416
- # The password used to login to the NSC
1417
- attr_reader :password
1418
- # The URL for communication
1419
- attr_reader :url
1420
-
1421
- # Constructor for Connection
1422
- def initialize(ip, user, pass, port = 3780, silo_id = nil)
1423
- @host = ip
1424
- @port = port
1425
- @username = user
1426
- @password = pass
1427
- @silo_id = silo_id
1428
- @session_id = nil
1429
- @error = false
1430
- @url = "https://#{@host}:#{@port}/api/API_VERSION/xml"
1431
- end
1432
-
1433
- # Establish a new connection and Session ID
1434
- def login
1435
- begin
1436
- login_hash = { 'sync-id' => 0, 'password' => @password, 'user-id' => @username }
1437
- unless @silo_id.nil?
1438
- login_hash['silo-id'] = @silo_id
1439
- end
1440
- r = execute(make_xml('LoginRequest', login_hash))
1441
- rescue APIError
1442
- raise AuthenticationFailed.new(r)
1443
- end
1444
- if(r.success)
1445
- @session_id = r.sid
1446
- return true
1447
- end
1448
- end
1449
-
1450
- # Logout of the current connection
1451
- def logout
1452
- r = execute(make_xml('LogoutRequest', {'sync-id' => 0}))
1453
- if(r.success)
1454
- return true
1455
- end
1456
- raise APIError.new(r, 'Logout failed')
1457
- end
1458
-
1459
- # Execute an API request
1460
- def execute(xml, version = '1.1')
1461
- @api_version = version
1462
- APIRequest.execute(@url,xml.to_s, @api_version)
1463
- end
1464
-
1465
- # Download a specific URL
1466
- def download(url)
1467
- uri = URI.parse(url)
1468
- http = Net::HTTP.new(@host, @port)
1469
- http.use_ssl = true
1470
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE # XXX: security issue
1471
- headers = {'Cookie' => "nexposeCCSessionID=#{@session_id}"}
1472
- resp, data = http.get(uri.path, headers)
1473
- data
1474
- end
1475
- end
1476
-
1477
- # === Description
1478
- # Object that represents a listing of all of the sites available on an NSC.
1479
- #
1480
- # === Example
1481
- # # Create a new Nexpose Connection on the default port and Login
1482
- # nsc = Connection.new("10.1.40.10","nxadmin","password")
1483
- # nsc->login();
1484
- #
1485
- # # Get Site Listing
1486
- # sitelisting = SiteListing.new(nsc)
1487
- #
1488
- # # Enumerate through all of the SiteSummaries
1489
- # sitelisting.sites.each do |sitesummary|
1490
- # # Do some operation on each site
1491
- # end
1492
- #
1493
- class SiteListing
1494
- # true if an error condition exists; false otherwise
1495
- attr_reader :error
1496
- # Error message string
1497
- attr_reader :error_msg
1498
- # The last XML request sent by this object
1499
- attr_reader :request_xml
1500
- # The last XML response received by this object
1501
- attr_reader :response_xml
1502
- # The NSC Connection associated with this object
1503
- attr_reader :connection
1504
- # Array containing SiteSummary objects for each site in the connection
1505
- attr_reader :sites
1506
- # The number of sites
1507
- attr_reader :site_count
1508
-
1509
- # Constructor
1510
- # SiteListing (connection)
1511
- def initialize(connection)
1512
- @sites = []
1513
-
1514
- @connection = connection
1515
-
1516
- r = @connection.execute('<SiteListingRequest session-id="' + @connection.session_id.to_s + '"/>')
1517
-
1518
- if (r.success)
1519
- parse(r.res)
1520
- else
1521
- raise APIError.new(r, "Failed to get site listing")
1522
- end
1523
- end
1524
-
1525
- def parse(r)
1526
- r.elements.each('SiteListingResponse/SiteSummary') do |s|
1527
- site_summary = SiteSummary.new(
1528
- s.attributes['id'].to_s,
1529
- s.attributes['name'].to_s,
1530
- s.attributes['description'].to_s,
1531
- s.attributes['riskfactor'].to_s
1532
- )
1533
- @sites.push(site_summary)
1534
- end
1535
- @site_count = @sites.length
1536
- end
1537
- end
1538
-
1539
- # === Description
1540
- # Object that represents the summary of a NeXpose Site.
1541
- #
1542
- class SiteSummary
1543
- # The Site ID
1544
- attr_reader :id
1545
- # The Site Name
1546
- attr_reader :site_name
1547
- # A Description of the Site
1548
- attr_reader :description
1549
- # User assigned risk multiplier
1550
- attr_reader :riskfactor
1551
-
1552
- # Constructor
1553
- # SiteSummary(id, site_name, description, riskfactor = 1)
1554
- def initialize(id, site_name, description, riskfactor = 1)
1555
- @id = id
1556
- @site_name = site_name
1557
- @description = description
1558
- @riskfactor = riskfactor
1559
- end
1560
-
1561
- def _set_id(id)
1562
- @id = id
1563
- end
1564
- end
1565
-
1566
- # === Description
1567
- # Object that represents a single IP address or an inclusive range of IP addresses. If to is nil then the from field will be used to specify a single IP Address only.
1568
- #
1569
- class IPRange
1570
- # Start of Range *Required
1571
- attr_reader :from;
1572
- # End of Range *Optional (If Null then IPRange is a single IP Address)
1573
- attr_reader :to;
1574
-
1575
- def initialize(from, to = nil)
1576
- @from = from
1577
- @to = to
1578
- end
1579
-
1580
- include Sanitize
1581
- def to_xml
1582
- if (to and not to.empty?)
1583
- return %Q{<range from="#{from}" to="#{to}"/>}
1584
- else
1585
- return %Q{<range from="#{from}"/>}
1586
- end
1587
- end
1588
- end
1589
-
1590
- # === Description
1591
- # Object that represents a hostname to be added to a site.
1592
- class HostName
1593
-
1594
- # The hostname
1595
- attr_reader :hostname
1596
-
1597
- def initialize(hostname)
1598
- @hostname = hostname
1599
- end
1600
-
1601
- include Sanitize
1602
- def to_xml
1603
- "<hostname>#{replace_entities(hostname)}</hostname>"
1604
- end
1605
- end
1606
-
1607
- # === Description
1608
- # Object that represents the configuration of a Site. This object is automatically created when a new Site object is instantiated.
1609
- #
1610
- class SiteConfig
1611
- # true if an error condition exists; false otherwise
1612
- attr_reader :error
1613
- # Error message string
1614
- attr_reader :error_msg
1615
- # The last XML request sent by this object
1616
- attr_reader :request_xml
1617
- # The last XML response received by this object
1618
- attr_reader :response_xml
1619
- # The NSC Connection associated with this object
1620
- attr_reader :connection
1621
- # The Site ID
1622
- attr_reader :site_id
1623
- # The Site Name
1624
- attr_reader :site_name
1625
- # A Description of the Site
1626
- attr_reader :description
1627
- # User assigned risk multiplier
1628
- attr_reader :riskfactor
1629
- # Array containing ((IPRange|HostName)*)
1630
- attr_reader :hosts
1631
- # Array containing (AdminCredentials*)
1632
- attr_reader :credentials
1633
- # Array containing ((SmtpAlera|SnmpAlert|SyslogAlert)*)
1634
- attr_reader :alerts
1635
- # ScanConfig object which holds Schedule and ScanTrigger Objects
1636
- attr_reader :scanConfig
1637
-
1638
- def initialize()
1639
- @xml_tag_stack = Array.new()
1640
- @hosts = Array.new()
1641
- @credentials = Array.new()
1642
- @alerts = Array.new()
1643
- @error = false
1644
- end
1645
-
1646
- # Adds a new host to the hosts array
1647
- def addHost(host)
1648
- @hosts.push(host)
1649
- end
1650
-
1651
- # Adds a new alert to the alerts array
1652
- def addAlert(alert)
1653
- @alerts.push(alert)
1654
- end
1655
-
1656
- # Adds a new set of credentials to the credentials array
1657
- def addCredentials(credential)
1658
- @credentials.push(credential)
1659
- end
1660
-
1661
- # TODO
1662
- def getSiteConfig(connection,site_id)
1663
- @connection = connection
1664
- @site_id = site_id
1665
-
1666
- r = APIRequest.execute(@connection.url,'<SiteConfigRequest session-id="' + @connection.session_id + '" site-id="' + @site_id + '"/>')
1667
- parse(r.res)
1668
- end
1669
-
1670
- def _set_site_id(site_id)
1671
- @site_id = site_id
1672
- end
1673
-
1674
- def _set_site_name(site_name)
1675
- @site_name = site_name
1676
- end
1677
-
1678
- def _set_description(description)
1679
- @description = description
1680
- end
1681
-
1682
- def _set_riskfactor(riskfactor)
1683
- @riskfactor = riskfactor
1684
- end
1685
-
1686
- def _set_scanConfig(scanConfig)
1687
- @scanConfig = scanConfig
1688
- end
1689
-
1690
- def _set_connection(connection)
1691
- @connection = connection
1692
- end
1693
- =begin
1694
- <SiteConfigResponse success='1'>
1695
- <Site name='Site1' id='243' description='' riskfactor='1.0'>
1696
- <Hosts>
1697
- <range from='127.0.0.1'/>
1698
- </Hosts>
1699
- <Credentials>
1700
- </Credentials>
1701
- <Alerting>
1702
- </Alerting>
1703
- <ScanConfig configID='243' name='Full audit' configVersion='3' engineID='2' templateID='full-audit'>
1704
- <Schedules>
1705
- </Schedules>
1706
- <ScanTriggers>
1707
- </ScanTriggers>
1708
- </ScanConfig>
1709
- </Site>
1710
-
1711
- =end
1712
-
1713
- def parse(response)
1714
- response.elements.each('SiteConfigResponse/Site') do |s|
1715
- @site_id = s.attributes['id']
1716
- @site_name = s.attributes['name']
1717
- @description = s.attributes['description']
1718
- @riskfactor = s.attributes['riskfactor']
1719
- s.elements.each('Hosts/range') do |r|
1720
- @hosts.push(IPRange.new(r.attributes['from'],r.attributes['to']))
1721
- end
1722
- s.elements.each('ScanConfig') do |c|
1723
- @scanConfig = ScanConfig.new(c.attributes['configID'],
1724
- c.attributes['name'],
1725
- c.attributes['configVersion'],
1726
- c.attributes['templateID'])
1727
- s.elements.each('Schedule') do |schedule|
1728
- schedule = new Schedule(schedule.attributes["type"], schedule.attributes["interval"], schedule.attributes["start"], schedule.attributes["enabled"])
1729
- @scanConfig.addSchedule(schedule)
1730
- end
1731
- end
1732
-
1733
- s.elements.each('Alerting/Alert') do |a|
1734
-
1735
- a.elements.each('smtpAlert') do |smtp|
1736
- smtp_alert = SmtpAlert.new(a.attributes["name"], smtp.attributes["sender"], smtp.attributes["limitText"], a.attributes["enabled"])
1737
-
1738
- smtp.elements.each('recipient') do |recipient|
1739
- smtp_alert.addRecipient(recipient.text)
1740
- end
1741
- @alerts.push(smtp_alert)
1742
- end
1743
-
1744
- a.elements.each('snmpAlert') do |snmp|
1745
- snmp_alert = SnmpAlert.new(a.attributes["name"], snmp.attributes["community"], snmp.attributes["server"], a.attributes["enabled"])
1746
- @alerts.push(snmp_alert)
1747
- end
1748
- a.elements.each('syslogAlert') do |syslog|
1749
- syslog_alert = SyslogAlert.new(a.attributes["name"], syslog.attributes["server"], a.attributes["enabled"])
1750
- @alerts.push(syslog_alert)
1751
- end
1752
-
1753
- a.elements.each('vulnFilter') do |vulnFilter|
1754
-
1755
- #vulnfilter = new VulnFilter.new(a.attributes["typemask"], a.attributes["severityThreshold"], $attrs["MAXALERTS"])
1756
- # Pop off the top alert on the stack
1757
- #$alert = @alerts.pop()
1758
- # Add the new recipient string to the Alert Object
1759
- #$alert.setVulnFilter($vulnfilter)
1760
- # Push the alert back on to the alert stack
1761
- #array_push($this->alerts, $alert)
1762
- end
1763
-
1764
- a.elements.each('scanFilter') do |scanFilter|
1765
- #<scanFilter scanStop='0' scanFailed='0' scanStart='1'/>
1766
- #scanfilter = ScanFilter.new(scanFilter.attributes['scanStop'],scanFilter.attributes['scanFailed'],scanFilter.attributes['scanStart'])
1767
- #alert = @alerts.pop()
1768
- #alert.setScanFilter(scanfilter)
1769
- #@alerts.push(alert)
1770
- end
1771
- end
1772
- end
1773
- end
1774
- end
1775
-
1776
- # === Description
1777
- # Object that represents the scan history of a site.
1778
- #
1779
- class SiteScanHistory
1780
- # true if an error condition exists; false otherwise
1781
- attr_reader :error
1782
- # Error message string
1783
- attr_reader :error_msg
1784
- # The last XML request sent by this object
1785
- attr_reader :request_xml
1786
- # The last XML response received by this object
1787
- attr_reader :response_xml
1788
- # The NSC Connection associated with this object
1789
- attr_reader :connection
1790
- # The Site ID
1791
- attr_reader :site_id
1792
- # //Array containing (ScanSummary*)
1793
- attr_reader :scan_summaries
1794
-
1795
- def initialize(connection, id)
1796
- @site_id = id
1797
- @error = false
1798
- @connection = connection
1799
- @scan_summaries = Array.new()
1800
-
1801
- r = @connection.execute('<SiteScanHistoryRequest' + ' session-id="' + @connection.session_id + '" site-id="' + @site_id + '"/>')
1802
- status = r.success
1803
- end
1804
- end
1805
-
1806
- # === Description
1807
- # Object that represents a listing of devices for a site or the entire NSC. Note that only devices which are accessible to the account used to create the connection object will be returned. This object is created and populated automatically with the instantiation of a new Site object.
1808
- #
1809
- class SiteDeviceListing
1810
-
1811
- # true if an error condition exists; false otherwise
1812
- attr_reader :error
1813
- # Error message string
1814
- attr_reader :error_msg
1815
- # The last XML request sent by this object
1816
- attr_reader :request_xml
1817
- # The last XML response received by this object
1818
- attr_reader :response_xml
1819
- # The NSC Connection associated with this object
1820
- attr_reader :connection
1821
- # The Site ID. 0 if all sites are specified.
1822
- attr_reader :site_id
1823
- # //Array of (Device)*
1824
- attr_reader :devices
1825
-
1826
- def initialize(connection, site_id = 0)
1827
-
1828
- @site_id = site_id
1829
- @error = false
1830
- @connection = connection
1831
- @devices = Array.new()
1832
-
1833
- r = nil
1834
- if (@site_id)
1835
- r = @connection.execute('<SiteDeviceListingRequest session-id="' + connection.session_id + '" site-id="' + @site_id + '"/>')
1836
- else
1837
- r = @connection.execute('<SiteDeviceListingRequest session-id="' + connection.session_id + '"/>')
1838
- end
1839
-
1840
- if(r.success)
1841
- response.elements.each('SiteDeviceListingResponse/SiteDevices/device') do |d|
1842
- @devices.push(Device.new(d.attributes['id'],@site_id,d.attributes["address"],d.attributes["riskfactor"],d.attributes['riskscore']))
1843
- end
1844
- end
1845
- end
1846
- end
1847
-
1848
- # === Description
1849
- # Object that represents a site, including the site configuration, scan history, and device listing.
1850
- #
1851
- # === Example
1852
- # # Create a new Nexpose Connection on the default port and Login
1853
- # nsc = Connection.new("10.1.40.10","nxadmin","password")
1854
- # nsc.login()
1855
- #
1856
- # # Get an Existing Site
1857
- # site_existing = Site.new(nsc,184)
1858
- #
1859
- # # Create a New Site, add some hosts, and save it to the NSC
1860
- # site = Site.new(nsc)
1861
- # site.setSiteConfig("New Site", "New Site Created in the API")
1862
- #
1863
- # # Add the hosts
1864
- # site.site_config.addHost(HostName.new("localhost"))
1865
- # site.site_config.addHost(IPRange.new("192.168.7.1","192.168.7.255"))
1866
- # site.site_config.addHost(IPRange.new("10.1.20.30"))
1867
- #
1868
- # status = site.saveSite()
1869
- #
1870
- class Site
1871
- # true if an error condition exists; false otherwise
1872
- attr_reader :error
1873
- # Error message string
1874
- attr_reader :error_msg
1875
- # The last XML request sent by this object
1876
- attr_reader :request_xml
1877
- # The last XML response received by this object
1878
- attr_reader :response_xml
1879
- # The NSC Connection associated with this object
1880
- attr_reader :connection
1881
- # The Site ID
1882
- # site_id = -1 means create a new site. The NSC will assign a new site_id on SiteSave.
1883
- attr_reader :site_id
1884
- # A summary overview of this site
1885
- # SiteSummary Object
1886
- attr_reader :site_summary
1887
- # The configuration of this site
1888
- # SiteConfig Object
1889
- attr_reader :site_config
1890
- # The device listing for this site
1891
- # SiteDeviceListing Object
1892
- attr_reader :site_device_listing
1893
- # The scan history of this site
1894
- # SiteScanHistory Object
1895
- attr_reader :site_scan_history
1896
-
1897
- def initialize(connection, site_id = -1)
1898
- @error = false
1899
- @connection = connection
1900
- @site_id = site_id
1901
-
1902
- # If site_id > 0 then get SiteConfig
1903
- if (@site_id.to_i > 0)
1904
- # Create new SiteConfig object
1905
- @site_config = SiteConfig.new()
1906
- # Populate SiteConfig Obect with Data from the NSC
1907
- @site_config.getSiteConfig(@connection,@site_id)
1908
- @site_summary = SiteSummary.new(@site_id, @site_config.site_name, @site_config.description, @site_config.riskfactor)
1909
- @site_scan_history = SiteScanHistory.new(@connection,@site_id)
1910
- @site_device_listing = SiteDeviceListing.new(@connection,@site_id)
1911
-
1912
- else
1913
- # Just in case user enters a number > -1 or = 0
1914
- @site_id = -1
1915
-
1916
- @site_config = SiteConfig.new()
1917
- setSiteConfig("New Site " + rand(999999999999).to_s,"")
1918
- @site_summary = nil
1919
-
1920
- end
1921
-
1922
- end
1923
-
1924
- # Creates a new site summary
1925
- def setSiteSummary(site_name, description, riskfactor = 1)
1926
- @site_summary = SiteSummary.new(-1,site_name,description,riskfactor)
1927
-
1928
- end
1929
-
1930
- # Creates a new site configuration
1931
- def setSiteConfig(site_name, description, riskfactor = 1)
1932
- setSiteSummary(site_name,description,riskfactor)
1933
- @site_config = SiteConfig.new()
1934
- @site_config._set_site_id(-1)
1935
- @site_config._set_site_name(site_name)
1936
- @site_config._set_description(description)
1937
- @site_config._set_riskfactor(riskfactor)
1938
- @site_config._set_scanConfig(ScanConfig.new(-1,"tmp","full-audit"))
1939
- @site_config._set_connection(@connection)
1940
-
1941
- end
1942
-
1943
- # Initiates a scan of this site. If successful returns scan_id and engine_id in an associative array. Returns false if scan is unsuccessful.
1944
- def scanSite()
1945
- r = @connection.execute('<SiteScanRequest session-id="' + "#{@connection.session_id}" + '" site-id="' + "#{@site_id}" + '"/>')
1946
- if(r.success)
1947
- res = {}
1948
- r.res.elements.each('//Scan/') do |s|
1949
- res[:scan_id] = s.attributes['scan-id']
1950
- res[:engine_id] = s.attributes['engine-id']
1951
- end
1952
- return res
1953
- else
1954
- return false
1955
- end
1956
- end
1957
-
1958
- # Saves this site in the NSC
1959
- def saveSite()
1960
- r = @connection.execute('<SiteSaveRequest session-id="' + @connection.session_id + '">' + getSiteXML() + ' </SiteSaveRequest>')
1961
- if (r.success)
1962
- @site_id = r.attributes['site-id']
1963
- @site_config._set_site_id(@site_id)
1964
- @site_config.scanConfig._set_configID(@site_id)
1965
- @site_config.scanConfig._set_name(@site_id)
1966
- return true
1967
- else
1968
- return false
1969
- end
1970
- end
1971
-
1972
- def deleteSite()
1973
- r = @connection.execute('<SiteDeleteRequest session-id="' + @connection.session_id.to_s + '" site-id="' + @site_id + '"/>')
1974
- r.success
1975
- end
1976
-
1977
-
1978
- def printSite()
1979
- puts "Site ID: " + @site_summary.id
1980
- puts "Site Name: " + @site_summary.site_name
1981
- puts "Site Description: " + @site_summary.description
1982
- puts "Site Risk Factor: " + @site_summary.riskfactor
1983
- end
1984
-
1985
- def getSiteXML()
1986
-
1987
- xml = '<Site id="' + "#{@site_config.site_id}" + '" name="' + "#{@site_config.site_name}" + '" description="' + "#{@site_config.description}" + '" riskfactor="' + "#{@site_config.riskfactor}" + '">'
1988
-
1989
- xml << ' <Hosts>'
1990
- @site_config.hosts.each do |h|
1991
- xml << h.to_xml if h.respond_to? :to_xml
1992
- end
1993
- xml << '</Hosts>'
1994
-
1995
- xml << '<Credentials>'
1996
- @site_config.credentials.each do |c|
1997
- xml << c.to_xml if c.respond_to? :to_xml
1998
- end
1999
- xml << ' </Credentials>'
2000
-
2001
- xml << ' <Alerting>'
2002
- @site_config.alerts.each do |a|
2003
- xml << a.to_xml if a.respond_to? :to_xml
2004
- end
2005
- xml << ' </Alerting>'
2006
-
2007
- xml << ' <ScanConfig configID="' + "#{@site_config.scanConfig.configID}" + '" name="' + "#{@site_config.scanConfig.name}" + '" templateID="' + "#{@site_config.scanConfig.templateID}" + '" configVersion="' + "#{@site_config.scanConfig.configVersion}" + '">'
2008
-
2009
- xml << ' <Schedules>'
2010
- @site_config.scanConfig.schedules.each do |s|
2011
- xml << ' <Schedule enabled="' + s.enabled + '" type="' + s.type + '" interval="' + s.interval + '" start="' + s.start + '"/>'
2012
- end
2013
- xml << ' </Schedules>'
2014
-
2015
- xml << ' <ScanTriggers>'
2016
- @site_config.scanConfig.scanTriggers.each do |s|
2017
-
2018
- if (s.class.to_s == "Nexpose::AutoUpdate")
2019
- xml << ' <autoUpdate enabled="' + s.enabled + '" incremental="' + s.incremental + '"/>'
2020
- end
2021
- end
2022
-
2023
- xml << ' </ScanTriggers>'
2024
-
2025
- xml << ' </ScanConfig>'
2026
-
2027
- xml << ' </Site>'
2028
-
2029
- return xml
2030
- end
2031
- end
2032
-
2033
- # === Description
2034
- # Object that represents administrative credentials to be used during a scan. When retrived from an existing site configuration the credentials will be returned as a security blob and can only be passed back as is during a Site Save operation. This object can only be used to create a new set of credentials.
2035
- #
2036
- class AdminCredentials
2037
- # Security blob for an existing set of credentials
2038
- attr_reader :securityblob
2039
- # Designates if this object contains user defined credentials or a security blob
2040
- attr_reader :isblob
2041
- # The service for these credentials. Can be All.
2042
- attr_reader :service
2043
- # The host for these credentials. Can be Any.
2044
- attr_reader :host
2045
- # The port on which to use these credentials.
2046
- attr_reader :port
2047
- # The user id or username
2048
- attr_reader :userid
2049
- # The password
2050
- attr_reader :password
2051
- # The realm for these credentials
2052
- attr_reader :realm
2053
-
2054
-
2055
- def initialize(isblob = false)
2056
- @isblob = isblob
2057
- end
2058
-
2059
- # Sets the credentials information for this object.
2060
- def setCredentials(service, host, port, userid, password, realm)
2061
- @isblob = false
2062
- @securityblob = nil
2063
- @service = service
2064
- @host = host
2065
- @port = port
2066
- @userid = userid
2067
- @password = password
2068
- @realm = realm
2069
- end
2070
-
2071
- # TODO: add description
2072
- def setService(service)
2073
- @service = service
2074
- end
2075
-
2076
- def setHost(host)
2077
- @host = host
2078
- end
2079
-
2080
- # TODO: add description
2081
- def setBlob(securityblob)
2082
- @isblob = true
2083
- @securityblob = securityblob
2084
- end
2085
-
2086
- include Sanitize
2087
- def to_xml
2088
- xml = ''
2089
- xml << '<adminCredentials'
2090
- xml << %Q{ service="#{replace_entities(service)}"} if (service)
2091
- xml << %Q{ userid="#{replace_entities(userid)}"} if (userid)
2092
- xml << %Q{ password="#{replace_entities(password)}"} if (password)
2093
- xml << %Q{ realm="#{replace_entities(realm)}"} if (realm)
2094
- xml << %Q{ host="#{replace_entities(host)}"} if (host)
2095
- xml << %Q{ port="#{replace_entities(port)}"} if (port)
2096
- xml << '>'
2097
- xml << replace_entities(securityblob) if (isblob)
2098
- xml << '</adminCredentials>'
2099
-
2100
- xml
2101
- end
2102
- end
2103
-
2104
-
2105
- # === Description
2106
- # Object that represents an SMTP (Email) Alert.
2107
- #
2108
- class SmtpAlert
2109
- # A unique name for this alert
2110
- attr_reader :name
2111
- # If this alert is enabled or not
2112
- attr_reader :enabled
2113
- # The email address of the sender
2114
- attr_reader :sender
2115
- # Limit the text for mobile devices
2116
- attr_reader :limitText
2117
- # Array containing Strings of email addresses
2118
- # Array of strings with the email addresses of the intended recipients
2119
- attr_reader :recipients
2120
- # The vulnerability filter to trigger the alert
2121
- attr_reader :vulnFilter
2122
- # The alert type
2123
- attr_reader :type
2124
-
2125
- def initialize(name, sender, limitText, enabled = 1)
2126
- @type = :smtp
2127
- @name = name
2128
- @sender = sender
2129
- @enabled = enabled
2130
- @limitText = limitText
2131
- @recipients = Array.new()
2132
- # Sets default vuln filter - All Events
2133
- setVulnFilter(VulnFilter.new("50790400",1))
2134
- end
2135
-
2136
- # Adds a new Recipient to the recipients array
2137
- def addRecipient(recipient)
2138
- @recipients.push(recipient)
2139
- end
2140
-
2141
- # Sets the Vulnerability Filter for this alert.
2142
- def setVulnFilter(vulnFilter)
2143
- @vulnFilter = vulnFilter
2144
- end
2145
-
2146
- include Sanitize
2147
- def to_xml
2148
- xml = "<smtpAlert"
2149
- xml << %Q{ name="#{replace_entities(name)}"}
2150
- xml << %Q{ enabled="#{replace_entities(enabled)}"}
2151
- xml << %Q{ sender="#{replace_entities(sender)}"}
2152
- xml << %Q{ limitText="#{replace_entities(limitText)}">}
2153
- recipients.each do |recpt|
2154
- xml << "<recipient>#{replace_entities(recpt)}</recipient>"
2155
- end
2156
- xml << vulnFilter.to_xml
2157
- xml << "</smtpAlert>"
2158
- xml
2159
- end
2160
- end
2161
-
2162
- # === Description
2163
- # Object that represents an SNMP Alert.
2164
- #
2165
- class SnmpAlert
2166
-
2167
- # A unique name for this alert
2168
- attr_reader :name
2169
- # If this alert is enabled or not
2170
- attr_reader :enabled
2171
- # The community string
2172
- attr_reader :community
2173
- # The SNMP server to sent this alert
2174
- attr_reader :server
2175
- # The vulnerability filter to trigger the alert
2176
- attr_reader :vulnFilter
2177
- # The alert type
2178
- attr_reader :type
2179
-
2180
- def initialize(name, community, server, enabled = 1)
2181
- @type = :snmp
2182
- @name = name
2183
- @community = community
2184
- @server = server
2185
- @enabled = enabled
2186
- # Sets default vuln filter - All Events
2187
- setVulnFilter(VulnFilter.new("50790400",1))
2188
- end
2189
-
2190
- # Sets the Vulnerability Filter for this alert.
2191
- def setVulnFilter(vulnFilter)
2192
- @vulnFilter = vulnFilter
2193
- end
2194
-
2195
- include Sanitize
2196
- def to_xml
2197
- xml = "<snmpAlert"
2198
- xml << %Q{ name="#{replace_entities(name)}"}
2199
- xml << %Q{ enabled="#{replace_entities(enabled)}"}
2200
- xml << %Q{ community="#{replace_entities(community)}"}
2201
- xml << %Q{ server="#{replace_entities(server)}">}
2202
- xml << vulnFilter.to_xml
2203
- xml << "</snmpAlert>"
2204
- xml
2205
- end
2206
-
2207
- end
2208
-
2209
- # === Description
2210
- # Object that represents a Syslog Alert.
2211
- #
2212
- class SyslogAlert
2213
-
2214
- # A unique name for this alert
2215
- attr_reader :name
2216
- # If this alert is enabled or not
2217
- attr_reader :enabled
2218
- # The Syslog server to sent this alert
2219
- attr_reader :server
2220
- # The vulnerability filter to trigger the alert
2221
- attr_reader :vulnFilter
2222
- # The alert type
2223
- attr_reader :type
2224
-
2225
- def initialize(name, server, enabled = 1)
2226
- @type = :syslog
2227
- @name = name
2228
- @server = server
2229
- @enabled = enabled
2230
- # Sets default vuln filter - All Events
2231
- setVulnFilter(VulnFilter.new("50790400",1))
2232
-
2233
- end
2234
-
2235
- # Sets the Vulnerability Filter for this alert.
2236
- def setVulnFilter(vulnFilter)
2237
- @vulnFilter = vulnFilter
2238
- end
2239
-
2240
- include Sanitize
2241
- def to_xml
2242
- xml = "<syslogAlert"
2243
- xml << %Q{ name="#{replace_entities(name)}"}
2244
- xml << %Q{ enabled="#{replace_entities(enabled)}"}
2245
- xml << %Q{ server="#{replace_entities(server)}">}
2246
- xml << vulnFilter.to_xml
2247
- xml << "</syslogAlert>"
2248
- xml
2249
- end
2250
-
2251
- end
2252
-
2253
- # TODO: review
2254
- # <scanFilter scanStop='0' scanFailed='0' scanStart='1'/>
2255
- # === Description
2256
- #
2257
- class ScanFilter
2258
-
2259
- attr_reader :scanStop
2260
- attr_reader :scanFailed
2261
- attr_reader :scanStart
2262
-
2263
- def initialize(scanstop, scanFailed, scanStart)
2264
-
2265
- @scanStop = scanStop
2266
- @scanFailed = scanFailed
2267
- @scanStart = scanStart
2268
-
2269
- end
2270
-
2271
- end
2272
-
2273
- # TODO: review
2274
- # === Description
2275
- #
2276
- class VulnFilter
2277
-
2278
- attr_reader :typeMask
2279
- attr_reader :maxAlerts
2280
- attr_reader :severityThreshold
2281
-
2282
- def initialize(typeMask, severityThreshold, maxAlerts = -1)
2283
- @typeMask = typeMask
2284
- @maxAlerts = maxAlerts
2285
- @severityThreshold = severityThreshold
2286
- end
2287
-
2288
- include Sanitize
2289
- def to_xml
2290
- xml = "<vulnFilter "
2291
- xml << %Q{ typeMask="#{replace_entities(typeMask)}"}
2292
- xml << %Q{ maxAlerts="#{replace_entities(maxAlerts)}"}
2293
- xml << %Q{ severityThreshold="#{replace_entities(severityThreshold)}"}
2294
- xml << "/>"
2295
-
2296
- xml
2297
- end
2298
-
2299
- end
2300
-
2301
- # TODO add engineID
2302
- # === Description
2303
- # Object that represents the scanning configuration for a Site.
2304
- #
2305
- class ScanConfig
2306
- # A unique ID for this scan configuration
2307
- attr_reader :configID
2308
- # The name of the scan template
2309
- attr_reader :name
2310
- # The ID of the scan template used full-audit, exhaustive-audit, web-audit, dos-audit, internet-audit, network-audit
2311
- attr_reader :templateID
2312
- # The configuration version (default is 2)
2313
- attr_reader :configVersion
2314
- # Array of (Schedule)*
2315
- attr_reader :schedules
2316
- # Array of (ScanTrigger)*
2317
- attr_reader :scanTriggers
2318
-
2319
- def initialize(configID, name, templateID, configVersion = 2)
2320
-
2321
- @configID = configID
2322
- @name = name
2323
- @templateID = templateID
2324
- @configVersion = configVersion
2325
- @schedules = Array.new()
2326
- @scanTriggers = Array.new()
2327
-
2328
- end
2329
-
2330
- # Adds a new Schedule for this ScanConfig
2331
- def addSchedule(schedule)
2332
- @schedules.push(schedule)
2333
- end
2334
-
2335
- # Adds a new ScanTrigger to the scanTriggers array
2336
- def addScanTrigger(scanTrigger)
2337
- @scanTriggers.push(scanTrigger)
2338
- end
2339
-
2340
- def _set_configID(configID)
2341
- @configID = configID
2342
- end
2343
-
2344
- def _set_name(name)
2345
- @name = name
2346
- end
2347
-
2348
- end
2349
-
2350
- # === Description
2351
- # Object that holds a scan schedule
2352
- #
2353
- class Schedule
2354
- # Type of Schedule (daily|hourly|monthly|weekly)
2355
- attr_reader :type
2356
- # The schedule interval
2357
- attr_reader :interval
2358
- # The date and time to start the first scan
2359
- attr_reader :start
2360
- # Enable or disable this schedule
2361
- attr_reader :enabled
2362
- # The date and time to disable to schedule. If null then the schedule will run forever.
2363
- attr_reader :notValidAfter
2364
- # Scan on the same date each time
2365
- attr_reader :byDate
2366
-
2367
- def initialize(type, interval, start, enabled = 1)
2368
-
2369
- @type = type
2370
- @interval = interval
2371
- @start = start
2372
- @enabled = enabled
2373
-
2374
- end
2375
-
2376
-
2377
-
2378
- end
2379
-
2380
- # === Description
2381
- # Object that holds an event that triggers the start of a scan.
2382
- #
2383
- class ScanTrigger
2384
- # Type of Trigger (AutoUpdate)
2385
- attr_reader :type
2386
- # Enable or disable this scan trigger
2387
- attr_reader :enabled
2388
- # Sets the trigger to start an incremental scan or a full scan
2389
- attr_reader :incremental
2390
-
2391
- def initialize(type, incremental, enabled = 1)
2392
-
2393
- @type = type
2394
- @incremental = incremental
2395
- @enabled = enabled
2396
-
2397
- end
2398
-
2399
- end
2400
-
2401
- # === Description
2402
- # Object that represents a single device in an NSC.
2403
- #
2404
- class Device
2405
-
2406
- # A unique device ID (assigned by the NSC)
2407
- attr_reader :id
2408
- # The site ID of this devices site
2409
- attr_reader :site_id
2410
- # IP Address or Hostname of this device
2411
- attr_reader :address
2412
- # User assigned risk multiplier
2413
- attr_reader :riskfactor
2414
- # NeXpose risk score
2415
- attr_reader :riskscore
2416
-
2417
- def initialize(id, site_id, address, riskfactor=1, riskscore=0)
2418
- @id = id
2419
- @site_id = site_id
2420
- @address = address
2421
- @riskfactor = riskfactor
2422
- @riskscore = riskscore
2423
-
2424
- end
2425
-
2426
- end
2427
-
2428
-
2429
- # === Description
2430
- # Object that represents a summary of a scan.
2431
- #
2432
- class ScanSummary
2433
- # The Scan ID of the Scan
2434
- attr_reader :scan_id
2435
- # The Engine ID used to perform the scan
2436
- attr_reader :engine_id
2437
- # TODO: add description
2438
- attr_reader :name
2439
- # The scan start time
2440
- attr_reader :startTime
2441
- # The scan finish time
2442
- attr_reader :endTime
2443
- # The scan status (running|finished|stopped|error| dispatched|paused|aborted|uknown)
2444
- attr_reader :status
2445
- # The number of pending tasks
2446
- attr_reader :tasks_pending
2447
- # The number of active tasks
2448
- attr_reader :tasks_active
2449
- # The number of completed tasks
2450
- attr_reader :tasks_completed
2451
- # The number of "live" nodes
2452
- attr_reader :nodes_live
2453
- # The number of "dead" nodes
2454
- attr_reader :nodes_dead
2455
- # The number of filtered nodes
2456
- attr_reader :nodes_filtered
2457
- # The number of unresolved nodes
2458
- attr_reader :nodes_unresolved
2459
- # The number of "other" nodes
2460
- attr_reader :nodes_other
2461
- # Confirmed vulnerabilities found (indexed by severity)
2462
- # Associative array, indexed by severity
2463
- attr_reader :vuln_exploit
2464
- # Unconfirmed vulnerabilities found (indexed by severity)
2465
- # Associative array, indexed by severity
2466
- attr_reader :vuln_version
2467
- # Not vulnerable checks run (confirmed)
2468
- attr_reader :not_vuln_exploit
2469
- # Not vulnerable checks run (unconfirmed)
2470
- attr_reader :not_vuln_version
2471
- # Vulnerability check errors
2472
- attr_reader :vuln_error
2473
- # Vulnerability checks disabled
2474
- attr_reader :vuln_disabled
2475
- # Vulnerability checks other
2476
- attr_reader :vuln_other
2477
-
2478
- # Constructor
2479
- # ScanSummary(can_id, $engine_id, $name, tartTime, $endTime, tatus)
2480
- def initialize(scan_id, engine_id, name, startTime, endTime, status)
2481
-
2482
- @scan_id = scan_id
2483
- @engine_id = engine_id
2484
- @name = name
2485
- @startTime = startTime
2486
- @endTime = endTime
2487
- @status = status
2488
-
2489
- end
2490
-
2491
- end
2492
-
2493
- # TODO
2494
- # === Description
2495
- # Object that represents the overview statistics for a particular scan.
2496
- #
2497
- # === Examples
2498
- #
2499
- # # Create a new Nexpose Connection on the default port and Login
2500
- # nsc = Connection.new("10.1.40.10","nxadmin","password")
2501
- # nsc.login()
2502
- #
2503
- # # Get a Site (Site ID = 12) from the NSC
2504
- # site = new Site(nsc,12)
2505
- #
2506
- # # Start a Scan of this site and pause for 1 minute
2507
- # scan1 = site.scanSite()
2508
- # sleep(60)
2509
- #
2510
- # # Get the Scan Statistics for this scan
2511
- # scanStatistics = new ScanStatistics(nsc,scan1["scan_id"])
2512
- #
2513
- # # Print out number of confirmed vulnerabilities with a 10 severity
2514
- # puts scanStatistics.scansummary.vuln_exploit[10]
2515
- #
2516
- # # Print out the number of pending tasks left in the scan
2517
- # puts scanStatistics.scan_summary.tasks_pending
2518
- #
2519
- class ScanStatistics
2520
- # true if an error condition exists; false otherwise
2521
- attr_reader :error
2522
- # Error message string
2523
- attr_reader :error_msg
2524
- # The last XML request sent by this object
2525
- attr_reader :request_xml
2526
- # The last XML response received by this object
2527
- attr_reader :reseponse_xml
2528
- # The Scan ID
2529
- attr_reader :scan_id
2530
- # The ScanSummary of the scan
2531
- attr_reader :scan_summary
2532
- # The NSC Connection associated with this object
2533
- attr_reader :connection
2534
-
2535
- # Vulnerability checks other
2536
- attr_reader :vuln_other
2537
- def initialize(connection, scan_id)
2538
- @error = false
2539
- @connection = connection
2540
- @scan_id = scan_id
2541
- end
2542
- end
2543
-
2544
- # ==== Description
2545
- # Object that represents a listing of all of the scan engines available on to an NSC.
2546
- #
2547
- class EngineListing
2548
- # true if an error condition exists; false otherwise
2549
- attr_reader :error
2550
- # Error message string
2551
- attr_reader :error_msg
2552
- # The last XML request sent by this object
2553
- attr_reader :request_xml
2554
- # The last XML response received by this object
2555
- attr_reader :response_xml
2556
- # The NSC Connection associated with this object
2557
- attr_reader :connection
2558
- # Array containing (EngineSummary*)
2559
- attr_reader :engines
2560
- # The number of scan engines
2561
- attr_reader :engine_count
2562
-
2563
- # Constructor
2564
- # EngineListing (connection)
2565
- def initialize(connection)
2566
- @connection = connection
2567
- @engines = []
2568
- @engine_count = 0
2569
- @error = false
2570
- r = @connection.execute('<EngineListingRequest session-id="' + @connection.session_id + '"/>', '1.2')
2571
-
2572
- if (r.success)
2573
- r.res.elements.each('EngineListingResponse/EngineSummary') do |v|
2574
- @engines.push(EngineSummary.new(v.attributes['id'], v.attributes['name'], v.attributes['address'],
2575
- v.attributes['port'], v.attributes['status']))
2576
- end
2577
- else
2578
- @error = true
2579
- @error_msg = 'EngineListingRequest Parse Error'
2580
- end
2581
- @engine_count = @engines.length
2582
- end
2583
- end
2584
-
2585
- # ==== Description
2586
- # Object that represents the summary of a scan engine.
2587
- #
2588
- # ==== Examples
2589
- #
2590
- # # Create a new Nexpose Connection on the default port and Login
2591
- # nsc = Connection.new("10.1.40.10","nxadmin","password")
2592
- # nsc.login()
2593
- #
2594
- # # Get the engine listing for the connection
2595
- # enginelisting = EngineListing.new(nsc)
2596
- #
2597
- # # Print out the status of the first scan engine
2598
- # puts enginelisting.engines[0].status
2599
- #
2600
- class EngineSummary
2601
- # A unique ID that identifies this scan engine
2602
- attr_reader :id
2603
- # The name of this scan engine
2604
- attr_reader :name
2605
- # The hostname or IP address of the engine
2606
- attr_reader :address
2607
- # The port there the engine is listening
2608
- attr_reader :port
2609
- # The engine status (active|pending-auth| incompatible|not-responding|unknown)
2610
- attr_reader :status
2611
-
2612
- # Constructor
2613
- # EngineSummary(id, name, address, port, status)
2614
- def initialize(id, name, address, port, status)
2615
- @id = id
2616
- @name = name
2617
- @address = address
2618
- @port = port
2619
- @status = status
2620
- end
2621
-
2622
- end
2623
-
2624
-
2625
- # TODO
2626
- class EngineActivity
2627
- # true if an error condition exists; false otherwise
2628
- attr_reader :error
2629
- # Error message string
2630
- attr_reader :error_msg
2631
- # The last XML request sent by this object
2632
- attr_reader :request_xml
2633
- # The last XML response received by this object
2634
- attr_reader :response_xml
2635
- # The NSC Connection associated with this object
2636
- attr_reader :connection
2637
- # The Engine ID
2638
- attr_reader :engine_id
2639
- # Array containing (ScanSummary*)
2640
- attr_reader :scan_summaries
2641
-
2642
-
2643
- end
2644
-
2645
- # === Description
2646
- # Object that represents a listing of all of the vulnerabilities in the vulnerability database
2647
- #
2648
- class VulnerabilityListing
2649
-
2650
- # true if an error condition exists; false otherwise
2651
- attr_reader :error
2652
- # Error message string
2653
- attr_reader :error_msg
2654
- # The last XML request sent by this object
2655
- attr_reader :request_xml
2656
- # The last XML response received by this object
2657
- attr_reader :response_xml
2658
- # The NSC Connection associated with this object
2659
- attr_reader :connection
2660
- # Array containing (VulnerabilitySummary*)
2661
- attr_reader :vulnerability_summaries
2662
- # The number of vulnerability definitions
2663
- attr_reader :vulnerability_count
2664
-
2665
- # Constructor
2666
- # VulnerabilityListing(connection)
2667
- def initialize(connection)
2668
- @error = false
2669
- @vulnerability_summaries = []
2670
- @connection = connection
2671
-
2672
- r = @connection.execute('<VulnerabilityListingRequest session-id="' + @connection.session_id + '"/>')
2673
-
2674
- if (r.success)
2675
- r.res.elements.each('VulnerabilityListingResponse/VulnerabilitySummary') do |v|
2676
- @vulnerability_summaries.push(VulnerabilitySummary.new(v.attributes['id'],v.attributes["title"],v.attributes["severity"]))
2677
- end
2678
- else
2679
- @error = true
2680
- @error_msg = 'VulnerabilitySummaryRequest Parse Error'
2681
- end
2682
- @vulnerability_count = @vulnerability_summaries.length
2683
- end
2684
- end
2685
-
2686
- # === Description
2687
- # Object that represents the summary of an entry in the vulnerability database
2688
- #
2689
- class VulnerabilitySummary
2690
-
2691
- # The unique ID string for this vulnerability
2692
- attr_reader :id
2693
- # The title of this vulnerability
2694
- attr_reader :title
2695
- # The severity of this vulnerability (1 – 10)
2696
- attr_reader :severity
2697
-
2698
- # Constructor
2699
- # VulnerabilitySummary(id, title, severity)
2700
- def initialize(id, title, severity)
2701
- @id = id
2702
- @title = title
2703
- @severity = severity
2704
-
2705
- end
2706
-
2707
- end
2708
-
2709
- # === Description
2710
- #
2711
- class Reference
2712
-
2713
- attr_reader :source
2714
- attr_reader :reference
2715
-
2716
- def initialize(source, reference)
2717
- @source = source
2718
- @reference = reference
2719
- end
2720
- end
2721
-
2722
- # === Description
2723
- # Object that represents the details for an entry in the vulnerability database
2724
- #
2725
- class VulnerabilityDetail
2726
- # true if an error condition exists; false otherwise
2727
- attr_reader :error
2728
- # Error message string
2729
- attr_reader :error_msg
2730
- # The last XML request sent by this object
2731
- attr_reader :request_xml
2732
- # The last XML response received by this object
2733
- attr_reader :response_xml
2734
- # The NSC Connection associated with this object
2735
- attr_reader :connection
2736
- # The unique ID string for this vulnerability
2737
- attr_reader :id
2738
- # The title of this vulnerability
2739
- attr_reader :title
2740
- # The severity of this vulnerability (1 – 10)
2741
- attr_reader :severity
2742
- # The pciSeverity of this vulnerability
2743
- attr_reader :pciSeverity
2744
- # The CVSS score of this vulnerability
2745
- attr_reader :cvssScore
2746
- # The CVSS vector of this vulnerability
2747
- attr_reader :cvssVector
2748
- # The date this vulnerability was published
2749
- attr_reader :published
2750
- # The date this vulnerability was added to NeXpose
2751
- attr_reader :added
2752
- # The last date this vulnerability was modified
2753
- attr_reader :modified
2754
- # The HTML Description of this vulnerability
2755
- attr_reader :description
2756
- # External References for this vulnerability
2757
- # Array containing (Reference)
2758
- attr_reader :references
2759
- # The HTML Solution for this vulnerability
2760
- attr_reader :solution
2761
-
2762
- # Constructor
2763
- # VulnerabilityListing(connection,id)
2764
- def initialize(connection, id)
2765
-
2766
- @error = false
2767
- @connection = connection
2768
- @id = id
2769
- @references = []
2770
-
2771
- r = @connection.execute('<VulnerabilityDetailsRequest session-id="' + @connection.session_id + '" vuln-id="' + @id + '"/>')
2772
-
2773
- if (r.success)
2774
- r.res.elements.each('VulnerabilityDetailsResponse/Vulnerability') do |v|
2775
- @id = v.attributes['id']
2776
- @title = v.attributes["title"]
2777
- @severity = v.attributes["severity"]
2778
- @pciSeverity = v.attributes['pciSeverity']
2779
- @cvssScore = v.attributes['cvssScore']
2780
- @cvssVector = v.attributes['cvssVector']
2781
- @published = v.attributes['published']
2782
- @added = v.attributes['added']
2783
- @modified = v.attributes['modified']
2784
-
2785
- v.elements.each('description') do |d|
2786
- @description = d.to_s.gsub(/\<\/?description\>/i, '')
2787
- end
2788
-
2789
- v.elements.each('solution') do |s|
2790
- @solution = s.to_s.gsub(/\<\/?solution\>/i, '')
2791
- end
2792
-
2793
- v.elements.each('references/reference') do |r|
2794
- @references.push(Reference.new(r.attributes['source'],r.text))
2795
- end
2796
- end
2797
- else
2798
- @error = true
2799
- @error_msg = 'VulnerabilitySummaryRequest Parse Error'
2800
- end
2801
-
2802
- end
2803
- end
2804
-
2805
- # === Description
2806
- # Object that represents the summary of a Report Configuration.
2807
- #
2808
- class ReportConfigSummary
2809
- # The Report Configuration ID
2810
- attr_reader :id
2811
- # A unique name for the Report
2812
- attr_reader :name
2813
- # The report format
2814
- attr_reader :format
2815
- # The date of the last report generation
2816
- attr_reader :last_generated_on
2817
- # Relative URI of the last generated report
2818
- attr_reader :last_generated_uri
2819
-
2820
- # Constructor
2821
- # ReportConfigSummary(id, name, format, last_generated_on, last_generated_uri)
2822
- def initialize(id, name, format, last_generated_on, last_generated_uri)
2823
-
2824
- @id = id
2825
- @name = name
2826
- @format = format
2827
- @last_generated_on = last_generated_on
2828
- @last_generated_uri = last_generated_uri
2829
-
2830
- end
2831
-
2832
- end
2833
-
2834
- # === Description
2835
- # Object that represents the schedule on which to automatically generate new reports.
2836
- class ReportHistory
2837
-
2838
- # true if an error condition exists; false otherwise
2839
- attr_reader :error
2840
- # Error message string
2841
- attr_reader :error_msg
2842
- # The last XML request sent by this object
2843
- attr_reader :request_xml
2844
- # The last XML response received by this object
2845
- attr_reader :response_xml
2846
- # The NSC Connection associated with this object
2847
- attr_reader :connection
2848
- # The report definition (report config) ID
2849
- # Report definition ID
2850
- attr_reader :config_id
2851
- # Array (ReportSummary*)
2852
- attr_reader :report_summaries
2853
-
2854
-
2855
- def initialize(connection, config_id)
2856
-
2857
- @error = false
2858
- @connection = connection
2859
- @config_id = config_id
2860
- @report_summaries = []
2861
-
2862
- reportHistory_request = APIRequest.new('<ReportHistoryRequest session-id="' + "#{connection.session_id}" + '" reportcfg-id="' + "#{@config_id}" + '"/>',@connection.geturl())
2863
- reportHistory_request.execute()
2864
- @response_xml = reportHistory_request.response_xml
2865
- @request_xml = reportHistory_request.request_xml
2866
-
2867
- end
2868
-
2869
- def xml_parse(response)
2870
- response = REXML::Document.new(response.to_s)
2871
- status = response.root.attributes['success']
2872
- if (status == '1')
2873
- response.elements.each('ReportHistoryResponse/ReportSummary') do |r|
2874
- @report_summaries.push(ReportSummary.new(r.attributes["id"], r.attributes["cfg-id"], r.attributes["status"], r.attributes["generated-on"],r.attributes['report-uri']))
2875
- end
2876
- else
2877
- @error = true
2878
- @error_msg = 'Error ReportHistoryReponse'
2879
- end
2880
- end
2881
-
2882
- end
2883
-
2884
- # === Description
2885
- # Object that represents the summary of a single report.
2886
- class ReportSummary
2887
-
2888
- # The Report ID
2889
- attr_reader :id
2890
- # The Report Configuration ID
2891
- attr_reader :cfg_id
2892
- # The status of this report
2893
- # available | generating | failed
2894
- attr_reader :status
2895
- # The date on which this report was generated
2896
- attr_reader :generated_on
2897
- # The relative URI of the report
2898
- attr_reader :report_uri
2899
-
2900
- def initialize(id, cfg_id, status, generated_on, report_uri)
2901
-
2902
- @id = id
2903
- @cfg_id = cfg_id
2904
- @status = status
2905
- @generated_on = generated_on
2906
- @report_uri = report_uri
2907
-
2908
- end
2909
-
2910
- end
2911
-
2912
- # === Description
2913
- #
2914
- class ReportAdHoc
2915
- include XMLUtils
2916
-
2917
- attr_reader :error
2918
- attr_reader :error_msg
2919
- attr_reader :connection
2920
- # Report Template ID strong e.g. full-audit
2921
- attr_reader :template_id
2922
- # pdf|html|xml|text|csv|raw-xml
2923
- attr_reader :format
2924
- # Array of (ReportFilter)*
2925
- attr_reader :filters
2926
- attr_reader :request_xml
2927
- attr_reader :response_xml
2928
- attr_reader :report_decoded
2929
-
2930
-
2931
- def initialize(connection, template_id = 'full-audit', format = 'raw-xml')
2932
-
2933
- @error = false
2934
- @connection = connection
2935
- @filters = Array.new()
2936
- @template_id = template_id
2937
- @format = format
2938
-
2939
- end
2940
-
2941
- def addFilter(filter_type, id)
2942
-
2943
- # filter_type can be site|group|device|scan
2944
- # id is the ID number. For scan, you can use 'last' for the most recently run scan
2945
- filter = ReportFilter.new(filter_type, id)
2946
- filters.push(filter)
2947
-
2948
- end
2949
-
2950
- def generate()
2951
- request_xml = '<ReportAdhocGenerateRequest session-id="' + @connection.session_id + '">'
2952
- request_xml += '<AdhocReportConfig template-id="' + @template_id + '" format="' + @format + '">'
2953
- request_xml += '<Filters>'
2954
- @filters.each do |f|
2955
- request_xml += '<filter type="' + f.type + '" id="'+ f.id.to_s + '"/>'
2956
- end
2957
- request_xml += '</Filters>'
2958
- request_xml += '</AdhocReportConfig>'
2959
- request_xml += '</ReportAdhocGenerateRequest>'
2960
-
2961
- ad_hoc_request = APIRequest.new(request_xml, @connection.url)
2962
- ad_hoc_request.execute()
2963
-
2964
- content_type_response = ad_hoc_request.raw_response.header['Content-Type']
2965
- if content_type_response =~ /multipart\/mixed;\s*boundary=([^\s]+)/
2966
- # NeXpose sends an incorrect boundary format which breaks parsing
2967
- # Eg: boundary=XXX; charset=XXX
2968
- # Fix by removing everything from the last semi-colon onward
2969
- last_semi_colon_index = content_type_response.index(/;/, content_type_response.index(/boundary/))
2970
- content_type_response = content_type_response[0, last_semi_colon_index]
2971
-
2972
- data = "Content-Type: " + content_type_response + "\r\n\r\n" + ad_hoc_request.raw_response_data
2973
- doc = Rex::MIME::Message.new data
2974
- doc.parts.each do |part|
2975
- if /.*base64.*/ =~ part.header.to_s
2976
- return parse_xml(part.content.unpack("m*")[0])
2977
- end
2978
- end
2979
- end
2980
- end
2981
-
2982
- end
2983
-
2984
- # === Description
2985
- # Object that represents the configuration of a report definition.
2986
- #
2987
- class ReportConfig
2988
-
2989
- # true if an error condition exists; false otherwise
2990
- attr_reader :error
2991
- # Error message string
2992
- attr_reader :error_msg
2993
- # The last XML request sent by this object
2994
- attr_reader :request_xml
2995
- # The last XML response received by this object
2996
- attr_reader :response_xml
2997
- # The NSC Connection associated with this object
2998
- attr_reader :connection
2999
- # The ID for this report definition
3000
- attr_reader :config_id
3001
- # A unique name for this report definition
3002
- attr_reader :name
3003
- # The template ID used for this report definition
3004
- attr_reader :template_id
3005
- # html, db, txt, xml, raw-xml, csv, pdf
3006
- attr_reader :format
3007
- # XXX new
3008
- attr_reader :timezone
3009
- # XXX new
3010
- attr_reader :owner
3011
- # Array of (ReportFilter)* - The Sites, Asset Groups, or Devices to run the report against
3012
- attr_reader :filters
3013
- # Automatically generate a new report at the conclusion of a scan
3014
- # 1 or 0
3015
- attr_reader :generate_after_scan
3016
- # Schedule to generate reports
3017
- # ReportSchedule Object
3018
- attr_reader :schedule
3019
- # Store the reports on the server
3020
- # 1 or 0
3021
- attr_reader :storeOnServer
3022
- # Location to store the report on the server
3023
- attr_reader :store_location
3024
- # Form to send the report via email
3025
- # "file", "zip", "url", or NULL (don’t send email)
3026
- attr_reader :email_As
3027
- # Send the Email to all Authorized Users
3028
- # boolean - Send the Email to all Authorized Users
3029
- attr_reader :email_to_all
3030
- # Array containing the email addresses of the recipients
3031
- attr_reader :email_recipients
3032
- # IP Address or Hostname of SMTP Relay Server
3033
- attr_reader :smtp_relay_server
3034
- # Sets the FROM field of the Email
3035
- attr_reader :sender
3036
- # TODO
3037
- attr_reader :db_export
3038
- # TODO
3039
- attr_reader :csv_export
3040
- # TODO
3041
- attr_reader :xml_export
3042
-
3043
-
3044
- def initialize(connection, config_id = -1)
3045
-
3046
- @error = false
3047
- @connection = connection
3048
- @config_id = config_id
3049
- @xml_tag_stack = Array.new()
3050
- @filters = Array.new()
3051
- @email_recipients = Array.new()
3052
- @name = "New Report " + rand(999999999).to_s
3053
-
3054
- r = @connection.execute('<ReportConfigRequest session-id="' + @connection.session_id.to_s + '" reportcfg-id="' + @config_id.to_s + '"/>')
3055
- if (r.success)
3056
- r.res.elements.each('ReportConfigResponse/ReportConfig') do |r|
3057
- @name = r.attributes['name']
3058
- @format = r.attributes['format']
3059
- @timezone = r.attributes['timezone']
3060
- @id = r.attributes['id']
3061
- @template_id = r.attributes['template-id']
3062
- @owner = r.attributes['owner']
3063
- end
3064
- else
3065
- @error = true
3066
- @error_msg = 'Error ReportHistoryReponse'
3067
- end
3068
- end
3069
-
3070
- # === Description
3071
- # Generate a new report on this report definition. Returns the new report ID.
3072
- def generateReport(debug = false)
3073
- return generateReport(@connection, @config_id, debug)
3074
- end
3075
-
3076
- # === Description
3077
- # Save the report definition to the NSC.
3078
- # Returns the config-id.
3079
- def saveReport()
3080
- r = @connection.execute('<ReportSaveRequest session-id="' + @connection.session_id.to_s + '">' + getXML().to_s + ' </ReportSaveRequest>')
3081
- if(r.success)
3082
- @config_id = r.attributes['reportcfg-id']
3083
- return true
3084
- end
3085
- return false
3086
- end
3087
-
3088
- # === Description
3089
- # Adds a new filter to the report config
3090
- def addFilter(filter_type, id)
3091
- filter = ReportFilter.new(filter_type,id)
3092
- @filters.push(filter)
3093
- end
3094
-
3095
- # === Description
3096
- # Adds a new email recipient
3097
- def addEmailRecipient(recipient)
3098
- @email_recipients.push(recipient)
3099
- end
3100
-
3101
- # === Description
3102
- # Sets the schedule for this report config
3103
- def setSchedule(schedule)
3104
- @schedule = schedule
3105
- end
3106
-
3107
- def getXML()
3108
-
3109
- xml = '<ReportConfig id="' + @config_id.to_s + '" name="' + @name.to_s + '" template-id="' + @template_id.to_s + '" format="' + @format.to_s + '">'
3110
-
3111
- xml += ' <Filters>'
3112
-
3113
- @filters.each do |f|
3114
- xml += ' <' + f.type.to_s + ' id="' + f.id.to_s + '"/>'
3115
- end
3116
-
3117
- xml += ' </Filters>'
3118
-
3119
- xml += ' <Generate after-scan="' + @generate_after_scan.to_s + '">'
3120
-
3121
- if (@schedule)
3122
- xml += ' <Schedule type="' + @schedule.type.to_s + '" interval="' + @schedule.interval.to_s + '" start="' + @schedule.start.to_s + '"/>'
3123
- end
3124
-
3125
- xml += ' </Generate>'
3126
-
3127
- xml += ' <Delivery>'
3128
-
3129
- xml += ' <Storage storeOnServer="' + @storeOnServer.to_s + '">'
3130
-
3131
- if (@store_location and @store_location.length > 0)
3132
- xml += ' <location>' + @store_location.to_s + '</location>'
3133
- end
3134
-
3135
- xml += ' </Storage>'
3136
-
3137
-
3138
- xml += ' </Delivery>'
3139
-
3140
- xml += ' </ReportConfig>'
3141
-
3142
- return xml
3143
- end
3144
-
3145
- def set_name(name)
3146
- @name = name
3147
- end
3148
-
3149
- def set_template_id(template_id)
3150
- @template_id = template_id
3151
- end
3152
-
3153
- def set_format(format)
3154
- @format = format
3155
- end
3156
-
3157
- def set_email_As(email_As)
3158
- @email_As = email_As
3159
- end
3160
-
3161
- def set_storeOnServer(storeOnServer)
3162
- @storeOnServer = storeOnServer
3163
- end
3164
-
3165
- def set_smtp_relay_server(smtp_relay_server)
3166
- @smtp_relay_server = smtp_relay_server
3167
- end
3168
-
3169
- def set_sender(sender)
3170
- @sender = sender
3171
- end
3172
-
3173
- def set_generate_after_scan(generate_after_scan)
3174
- @generate_after_scan = generate_after_scan
3175
- end
3176
- end
3177
-
3178
- # === Description
3179
- # Object that represents a report filter which determines which sites, asset
3180
- # groups, and/or devices that a report is run against. gtypes are
3181
- # "SiteFilter", "AssetGroupFilter", "DeviceFilter", or "ScanFilter". gid is
3182
- # the site-id, assetgroup-id, or devce-id. ScanFilter, if used, specifies
3183
- # a specifies a specific scan to use as the data source for the report. The gid
3184
- # can be a specific scan-id or "first" for the first run scan, or “last” for
3185
- # the last run scan.
3186
- #
3187
- class ReportFilter
3188
-
3189
- attr_reader :type
3190
- attr_reader :id
3191
-
3192
- def initialize(type, id)
3193
-
3194
- @type = type
3195
- @id = id
3196
-
3197
- end
3198
-
3199
- end
3200
-
3201
- # === Description
3202
- # Object that represents the schedule on which to automatically generate new reports.
3203
- #
3204
- class ReportSchedule
3205
-
3206
- # The type of schedule
3207
- # (daily, hourly, monthly, weekly)
3208
- attr_reader :type
3209
- # The frequency with which to run the scan
3210
- attr_reader :interval
3211
- # The earliest date to generate the report
3212
- attr_reader :start
3213
-
3214
- def initialize(type, interval, start)
3215
-
3216
- @type = type
3217
- @interval = interval
3218
- @start = start
3219
-
3220
- end
3221
-
3222
-
3223
- end
3224
-
3225
- class ReportTemplateListing
3226
-
3227
- attr_reader :error_msg
3228
- attr_reader :error
3229
- attr_reader :request_xml
3230
- attr_reader :response_xml
3231
- attr_reader :connection
3232
- attr_reader :xml_tag_stack
3233
- attr_reader :report_template_summaries#; //Array (ReportTemplateSummary*)
3234
-
3235
-
3236
- def ReportTemplateListing(connection)
3237
-
3238
- @error = nil
3239
- @connection = connection
3240
- @report_template_summaries = Array.new()
3241
-
3242
- r = @connection.execute('<ReportTemplateListingRequest session-id="' + connection.session_id.to_s + '"/>')
3243
- if (r.success)
3244
- r.res.elements.each('ReportTemplateListingResponse/ReportTemplateSummary') do |r|
3245
- @report_template_summaries.push(ReportTemplateSumary.new(r.attributes['id'],r.attributes['name']))
3246
- end
3247
- else
3248
- @error = true
3249
- @error_msg = 'ReportTemplateListingRequest Parse Error'
3250
- end
3251
-
3252
- end
3253
-
3254
- end
3255
-
3256
-
3257
- class ReportTemplateSummary
3258
-
3259
- attr_reader :id
3260
- attr_reader :name
3261
- attr_reader :description
3262
-
3263
- def ReportTemplateSummary(id, name, description)
3264
-
3265
- @id = id
3266
- @name = name
3267
- @description = description
3268
-
3269
- end
3270
-
3271
- end
3272
-
3273
-
3274
- class ReportSection
3275
-
3276
- attr_reader :name
3277
- attr_reader :properties
3278
-
3279
- def ReportSection(name)
3280
-
3281
- @properties = Array.new()
3282
- @name = name
3283
- end
3284
-
3285
-
3286
- def addProperty(name, value)
3287
-
3288
- @properties[name.to_s] = value
3289
- end
3290
-
3291
- end
3292
-
3293
-
3294
72
  # TODO add
3295
73
  def self.site_device_scan(connection, site_id, device_array, host_array, debug = false)
3296
74
 
@@ -3348,5 +126,4 @@ end
3348
126
  end
3349
127
  end
3350
128
 
3351
-
3352
129
  end