nexpose 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
data/lib/nexpose/scan.rb CHANGED
@@ -120,31 +120,27 @@ module Nexpose
120
120
  # @return [ScanSummary] Scan summary represented by the XML.
121
121
  #
122
122
  def self.parse(rexml)
123
- tasks = Tasks.parse(rexml)
124
- nodes = Nodes.parse(rexml)
125
- vulns = Vulnerabilities.parse(rexml)
126
- msg = nil
127
- rexml.elements.each("//ScanSummary/message") do |message|
128
- msg = message.text
129
- end
123
+ tasks = Tasks.parse(rexml.elements['tasks'])
124
+ nodes = Nodes.parse(rexml.elements['nodes'])
125
+ vulns = Vulnerabilities.parse(rexml.attributes['scan-id'], rexml)
126
+ msg = rexml.elements['message'] ? rexml.elements['message'].text : nil
130
127
 
131
- rexml.elements.each("//ScanSummary") do |scan|
132
- start_time = DateTime.parse(scan.attributes['startTime'].to_s).to_time
133
- # End time is often not present, since reporting on running scans.
134
- if scan.attributes['endTime']
135
- end_time = DateTime.parse(scan.attributes['endTime'].to_s).to_time
136
- end
137
- return ScanSummary.new(scan.attributes['scan-id'].to_i,
138
- scan.attributes['site-id'].to_i,
139
- scan.attributes['engine-id'].to_i,
140
- scan.attributes['status'],
141
- start_time,
142
- end_time,
143
- msg,
144
- tasks,
145
- nodes,
146
- vulns)
128
+ start_time = DateTime.parse(rexml.attributes['startTime'].to_s).to_time
129
+ # End time is often not present, since reporting on running scans.
130
+ end_time = nil
131
+ if rexml.attributes['endTime']
132
+ end_time = DateTime.parse(rexml.attributes['endTime'].to_s).to_time
147
133
  end
134
+ return ScanSummary.new(rexml.attributes['scan-id'].to_i,
135
+ rexml.attributes['site-id'].to_i,
136
+ rexml.attributes['engine-id'].to_i,
137
+ rexml.attributes['status'],
138
+ start_time,
139
+ end_time,
140
+ msg,
141
+ tasks,
142
+ nodes,
143
+ vulns)
148
144
  end
149
145
 
150
146
  # Value class to tracking task counts.
@@ -162,11 +158,9 @@ module Nexpose
162
158
  # @return [Tasks] Task summary represented by the XML.
163
159
  #
164
160
  def self.parse(rexml)
165
- rexml.elements.each("//ScanSummary/tasks") do |task|
166
- return Tasks.new(task.attributes['pending'],
167
- task.attributes['active'],
168
- task.attributes['completed'])
169
- end
161
+ return Tasks.new(rexml.attributes['pending'].to_i,
162
+ rexml.attributes['active'].to_i,
163
+ rexml.attributes['completed'].to_i)
170
164
  end
171
165
  end
172
166
 
@@ -185,13 +179,11 @@ module Nexpose
185
179
  # @return [Nodes] Node summary represented by the XML.
186
180
  #
187
181
  def self.parse(rexml)
188
- rexml.elements.each("//ScanSummary/nodes") do |node|
189
- return Nodes.new(node.attributes['live'],
190
- node.attributes['dead'],
191
- node.attributes['filtered'],
192
- node.attributes['unresolved'],
193
- node.attributes['other'])
194
- end
182
+ return Nodes.new(rexml.attributes['live'].to_i,
183
+ rexml.attributes['dead'].to_i,
184
+ rexml.attributes['filtered'].to_i,
185
+ rexml.attributes['unresolved'].to_i,
186
+ rexml.attributes['other'].to_i)
195
187
  end
196
188
  end
197
189
 
@@ -215,12 +207,13 @@ module Nexpose
215
207
 
216
208
  # Parse REXML to Vulnerabilities object.
217
209
  #
210
+ # @param [FixNum] scan_id Scan ID to collect vulnerability data for.
218
211
  # @param [REXML::Document] rexml XML document to parse.
219
212
  # @return [Vulnerabilities] Vulnerability summary represented by the XML.
220
213
  #
221
- def self.parse(rexml)
214
+ def self.parse(scan_id, rexml)
222
215
  map = {}
223
- rexml.elements.each("//ScanSummary/vulnerabilities") do |vuln|
216
+ rexml.elements.each("//ScanSummary[contains(@scan-id,'#{scan_id}')]/vulnerabilities") do |vuln|
224
217
  status = map[vuln.attributes['status']]
225
218
  if status && vuln.attributes['status'] =~ /^vuln-/
226
219
  status.add_severity(vuln.attributes['severity'].to_i, vuln.attributes['count'].to_i)
@@ -24,76 +24,48 @@ module Nexpose
24
24
  end
25
25
  arr
26
26
  end
27
- end
28
-
29
- # ==== Description
30
- # Object that represents a listing of all of the scan engines available on to an NSC.
31
- class EngineListing
32
- # true if an error condition exists; false otherwise
33
- attr_reader :error
34
- # Error message string
35
- attr_reader :error_msg
36
- # The last XML request sent by this object
37
- attr_reader :connection
38
- # Array containing (EngineSummary*)
39
- attr_reader :engines
40
- # The number of scan engines
41
- attr_reader :engine_count
42
-
43
- # Constructor
44
- # EngineListing (connection)
45
- def initialize(connection)
46
- @connection = connection
47
- @engines = []
48
- @engine_count = 0
49
- @error = false
50
- r = @connection.execute('<EngineListingRequest session-id="' + @connection.session_id + '"/>', '1.2')
51
27
 
52
- if r.success
53
- r.res.elements.each('EngineListingResponse/EngineSummary') do |v|
54
- @engines.push(EngineSummary.new(v.attributes['id'], v.attributes['name'], v.attributes['address'],
55
- v.attributes['port'], v.attributes['status']))
28
+ # Retrieve a list of all Scan Engines managed by the Security Console.
29
+ #
30
+ # @return [Array[EngineSummary]] Array of EngineSummary objects associated with
31
+ # each engine associated with this security console.
32
+ #
33
+ def list_engines
34
+ response = execute(make_xml('EngineListingRequest'))
35
+ arr = []
36
+ if response.success
37
+ response.res.elements.each("//EngineSummary") do |engine|
38
+ arr << EngineSummary.new(engine.attributes['id'].to_i,
39
+ engine.attributes['name'],
40
+ engine.attributes['address'],
41
+ engine.attributes['port'].to_i,
42
+ engine.attributes['status'])
56
43
  end
57
- else
58
- @error = true
59
- @error_msg = 'EngineListingRequest Parse Error'
60
44
  end
61
- @engine_count = @engines.length
45
+ arr
62
46
  end
47
+
48
+ alias_method :engines, :list_engines
63
49
  end
64
50
 
65
- # ==== Description
66
- # Object that represents the summary of a scan engine.
67
- #
68
- # ==== Examples
69
- #
70
- # # Create a new Nexpose Connection on the default port and Login
71
- # nsc = Connection.new("10.1.40.10","nxadmin","password")
72
- # nsc.login()
73
- #
74
- # # Get the engine listing for the connection
75
- # enginelisting = EngineListing.new(nsc)
76
- #
77
- # # Print out the status of the first scan engine
78
- # puts enginelisting.engines[0].status
51
+ # Object representing the current details of a scan engine attached to the security console.
79
52
  #
80
53
  class EngineSummary
81
- # A unique ID that identifies this scan engine
54
+
55
+ # A unique ID that identifies this scan engine.
82
56
  attr_reader :id
83
- # The name of this scan engine
57
+ # The name of this scan engine.
84
58
  attr_reader :name
85
- # The hostname or IP address of the engine
59
+ # The hostname or IP address of the engine.
86
60
  attr_reader :address
87
- # The port there the engine is listening
61
+ # The port there the engine is listening.
88
62
  attr_reader :port
89
- # The engine status (active|pending-auth| incompatible|not-responding|unknown)
63
+ # The engine status. One of: active|pending-auth|incompatible|not-responding|unknown
90
64
  attr_reader :status
91
65
  # A parameter that specifies whether the engine has a global
92
66
  # or silo-specific scope.
93
67
  attr_reader :scope
94
68
 
95
- # Constructor
96
- # EngineSummary(id, name, address, port, status, scope)
97
69
  def initialize(id, name, address, port, status, scope = 'silo')
98
70
  @id = id
99
71
  @name = name
@@ -102,10 +74,6 @@ module Nexpose
102
74
  @status = status
103
75
  @scope = scope
104
76
  end
105
-
106
- def to_s
107
- "Engine: #@name [ID: #@id] #@address:#@port, Status: #@status, Scope: #@scope"
108
- end
109
77
  end
110
78
 
111
79
  #-------------------------------------------------------------------------------------------------------------------
@@ -303,10 +271,10 @@ module Nexpose
303
271
  @scope = pool.attributes['scope']
304
272
  @engines = []
305
273
  r.res.elements.each('EnginePoolDetailsResponse/EnginePool/EngineSummary') do |summary|
306
- @engines.push(EngineSummary.new(summary.attributes['id'],
274
+ @engines.push(EngineSummary.new(summary.attributes['id'].to_i,
307
275
  summary.attributes['name'],
308
276
  summary.attributes['address'],
309
- summary.attributes['port'],
277
+ summary.attributes['port'].to_i,
310
278
  summary.attributes['status'],
311
279
  summary.attributes['scope']))
312
280
  end
data/lib/nexpose/site.rb CHANGED
@@ -2,60 +2,69 @@ module Nexpose
2
2
  module NexposeAPI
3
3
  include XMLUtils
4
4
 
5
+ # Retrieve a list of all of the assets in a site.
5
6
  #
7
+ # If no site-id is specified, then return all of the assets
8
+ # for the Nexpose console, grouped by site-id.
6
9
  #
10
+ # @param [FixNum] site_id Site ID to request device listing for. Optional.
11
+ # @return [Array[Device]] Array of devices associated with the site, or
12
+ # all devices on the console if no site is provided.
7
13
  #
8
- def site_device_listing(site_id)
9
- r = execute(make_xml('SiteDeviceListingRequest', {'site-id' => site_id.to_s}))
14
+ def site_device_listing(site_id = nil)
15
+ r = execute(make_xml('SiteDeviceListingRequest', {'site-id' => site_id}))
10
16
 
11
- if (r.success)
12
- res = []
13
- r.res.elements.each("//device") do |device|
14
- res << {
15
- :device_id => device.attributes['id'].to_i,
16
- # TODO Covert to using?
17
- # :address => IPAddr.new(device.attributes['address']),
18
- :address => device.attributes['address'].to_s,
19
- :risk_factor => device.attributes['riskfactor'].to_f,
20
- :risk_score => device.attributes['riskscore'].to_f,
21
- }
17
+ arr = []
18
+ if r.success
19
+ r.res.elements.each('//SiteDevices') do |site|
20
+ site_id = site.attributes['site-id'].to_i
21
+ site.elements.each("//SiteDevices[contains(@site-id,'#{site_id}')]/device") do |device|
22
+ arr << Device.new(device.attributes['id'].to_i,
23
+ device.attributes['address'],
24
+ site_id,
25
+ device.attributes['riskfactor'].to_f,
26
+ device.attributes['riskscore'].to_f)
27
+ end
22
28
  end
23
- res
24
- else
25
- false
26
29
  end
30
+ arr
27
31
  end
28
32
 
33
+ alias_method :assets, :site_device_listing
34
+ alias_method :devices, :site_device_listing
35
+ alias_method :list_devices, :site_device_listing
36
+
37
+ # Delete the specified site and all associated scan data.
29
38
  #
30
- #
39
+ # @return Whether or not the delete request succeeded.
31
40
  #
32
41
  def site_delete(param)
33
42
  r = execute(make_xml('SiteDeleteRequest', {'site-id' => param}))
34
43
  r.success
35
44
  end
36
45
 
46
+ # Retrieve a list of all sites the user is authorized to view or manage.
37
47
  #
38
- #
39
- # TODO Should just return empty array if doesn't work?
48
+ # @return [Array[SiteSummary]] Array of SiteSummary objects.
49
+ #
40
50
  def site_listing
41
- r = execute(make_xml('SiteListingRequest', {}))
42
-
51
+ r = execute(make_xml('SiteListingRequest'))
52
+ arr = []
43
53
  if (r.success)
44
- res = []
45
54
  r.res.elements.each("//SiteSummary") do |site|
46
- res << {
47
- :site_id => site.attributes['id'].to_i,
48
- :name => site.attributes['name'].to_s,
49
- :risk_factor => site.attributes['riskfactor'].to_f,
50
- :risk_score => site.attributes['riskscore'].to_f,
51
- }
55
+ arr << SiteSummary.new(site.attributes['id'].to_i,
56
+ site.attributes['name'],
57
+ site.attributes['description'],
58
+ site.attributes['riskfactor'].to_f,
59
+ site.attributes['riskscore'].to_f)
52
60
  end
53
- res
54
- else
55
- false
56
61
  end
62
+ arr
57
63
  end
58
64
 
65
+ alias_method :list_sites, :site_listing
66
+ alias_method :sites, :site_listing
67
+
59
68
  # Retrieve a list of all previous scans of the site.
60
69
  #
61
70
  # @param [FixNum] site_id Site ID to request scan history for.
@@ -73,6 +82,18 @@ module Nexpose
73
82
  res
74
83
  end
75
84
 
85
+ # Retrieve the scan summary statistics for the latest completed scan
86
+ # on a site.
87
+ #
88
+ # Method will not return data on an active scan.
89
+ #
90
+ # @param [FixNum] site_id Site ID to find latest scan for.
91
+ #
92
+ def last_scan(site_id)
93
+ site_scan_history(site_id).select { |scan| scan.end_time }
94
+ .max_by { |scan| scan.end_time }
95
+ end
96
+
76
97
  #-----------------------------------------------------------------------
77
98
  # Starts device specific site scanning.
78
99
  #
@@ -436,8 +457,8 @@ module Nexpose
436
457
  r.elements.each('SiteListingResponse/SiteSummary') do |s|
437
458
  site_summary = SiteSummary.new(
438
459
  s.attributes['id'].to_s,
439
- s.attributes['name'].to_s,
440
- s.attributes['description'].to_s,
460
+ s.attributes['name'],
461
+ s.attributes['description'],
441
462
  s.attributes['riskfactor'].to_s
442
463
  )
443
464
  @sites.push(site_summary)
@@ -450,101 +471,51 @@ module Nexpose
450
471
  # Object that represents the summary of a Nexpose Site.
451
472
  #
452
473
  class SiteSummary
453
- # The Site ID
474
+
475
+ # The Site ID.
454
476
  attr_reader :id
455
- # The Site Name
456
- attr_reader :site_name
457
- # A Description of the Site
477
+ # The Site Name.
478
+ attr_reader :name
479
+ # A Description of the Site.
458
480
  attr_reader :description
459
- # User assigned risk multiplier
460
- attr_reader :riskfactor
481
+ # User assigned risk multiplier.
482
+ attr_reader :risk_factor
483
+ # Current computed risk score for the site.
484
+ attr_reader :risk_factor
461
485
 
462
486
  # Constructor
463
- # SiteSummary(id, site_name, description, riskfactor = 1)
464
- def initialize(id, site_name, description, riskfactor = 1.0)
487
+ # SiteSummary(id, name, description, riskfactor = 1)
488
+ def initialize(id, name, description, risk_factor = 1.0, risk_score = 0.0)
465
489
  @id = id
466
- @site_name = site_name
490
+ @name = name
467
491
  @description = description
468
- @riskfactor = riskfactor
469
- end
470
-
471
- def _set_id(id)
472
- @id = id
473
- end
474
- end
475
-
476
- # === Description
477
- # 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.
478
- #
479
- class SiteDeviceListing
480
-
481
- # true if an error condition exists; false otherwise
482
- attr_reader :error
483
- # Error message string
484
- attr_reader :error_msg
485
- # The last XML request sent by this object
486
- attr_reader :request_xml
487
- # The last XML response received by this object
488
- attr_reader :response_xml
489
- # The NSC Connection associated with this object
490
- attr_reader :connection
491
- # The Site ID. 0 if all sites are specified.
492
- attr_reader :site_id
493
- # //Array of (Device)*
494
- attr_reader :devices
495
-
496
- def initialize(connection, site_id = 0)
497
-
498
- @site_id = site_id
499
- @error = false
500
- @connection = connection
501
- @devices = []
502
-
503
- r = nil
504
- if (@site_id)
505
- r = @connection.execute('<SiteDeviceListingRequest session-id="' + connection.session_id + '" site-id="' + "#{@site_id}" + '"/>')
506
- if r.success
507
- r.res.elements.each('SiteDeviceListingResponse/SiteDevices/device') do |d|
508
- @devices.push(Device.new(d.attributes['id'], @site_id, d.attributes["address"], d.attributes["riskfactor"], d.attributes["riskscore"]))
509
- end
510
- end
511
- else
512
- r = @connection.execute('<SiteDeviceListingRequest session-id="' + connection.session_id + '"/>')
513
- if r.success
514
- r.res.elements.each('SiteDeviceListingResponse/SiteDevices') do |rr|
515
- @sid = rr.attribute("site-id")
516
- rr.elements.each('device') do |d|
517
- @devices.push(Device.new(d.attributes['id'], @sid, d.attributes["address"], d.attributes['riskfactor'], d.attributes['riskscore']))
518
- end
519
- end
520
- end
521
- end
492
+ @risk_factor = risk_factor
493
+ @risk_score = risk_score
522
494
  end
523
495
  end
524
496
 
525
497
  # === Description
526
- # Object that represents a single device in an NSC.
498
+ # Object that represents a single device in a Nexpose security console.
527
499
  #
528
500
  class Device
529
501
 
530
- # A unique device ID (assigned by the NSC)
502
+ # A unique device ID (assigned automatically by the Nexpose console).
531
503
  attr_reader :id
532
- # The site ID of this devices site
533
- attr_reader :site_id
534
- # IP Address or Hostname of this device
504
+ # IP Address or Hostname of this device.
535
505
  attr_reader :address
536
- # User assigned risk multiplier
537
- attr_reader :riskfactor
538
- # Nexpose risk score
539
- attr_reader :riskscore
506
+ # User assigned risk multiplier.
507
+ attr_reader :risk_factor
508
+ # Nexpose risk score.
509
+ attr_reader :risk_score
510
+ # Site ID that this device is associated with.
511
+ attr_reader :site_id
540
512
 
541
- def initialize(id, site_id, address, riskfactor=1, riskscore=0)
513
+ def initialize(id, address, site_id, risk_factor = 1.0, risk_score = 0.0)
542
514
  @id = id
543
- @site_id = site_id
544
515
  @address = address
545
- @riskfactor = riskfactor
546
- @riskscore = riskscore
547
-
516
+ @site_id = site_id
517
+ @risk_factor = risk_factor
518
+ @risk_score = risk_score
548
519
  end
549
520
  end
550
521
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nexpose
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-02-06 00:00:00.000000000 Z
13
+ date: 2013-02-14 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: librex