nexpose 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -48,59 +48,31 @@ module Nexpose
48
48
  r.success ? r.attributes['success'] : nil
49
49
  end
50
50
 
51
+ # Retrieve a list of current scan activities across all Scan Engines managed
52
+ # by Nexpose.
53
+ #
54
+ # @return [Array[ScanSummary]] Array of ScanSummary objects associated with
55
+ # each active scan on the engines.
56
+ #
51
57
  def scan_activity
52
58
  r = execute(make_xml('ScanActivityRequest', {}))
59
+ res = []
53
60
  if (r.success)
54
- res = []
55
61
  r.res.elements.each("//ScanSummary") do |scan|
56
- res << {
57
- :scan_id => scan.attributes['scan-id'].to_i,
58
- :site_id => scan.attributes['site-id'].to_i,
59
- :engine_id => scan.attributes['engine-id'].to_i,
60
- :status => scan.attributes['status'].to_s,
61
- :start_time => Date.parse(scan.attributes['startTime'].to_s).to_time
62
- }
62
+ res << ScanSummary.parse(scan)
63
63
  end
64
- res
65
- else
66
- false
67
64
  end
65
+ res
68
66
  end
69
67
 
70
- def scan_statistics(param)
71
- r = execute(make_xml('ScanStatisticsRequest', {'scan-id' => param}))
72
- if (r.success)
73
- res = {}
74
- r.res.elements.each("//ScanSummary/nodes") do |node|
75
- res[:nodes] = {}
76
- node.attributes.keys.each do |k|
77
- res[:nodes][k] = node.attributes[k].to_i
78
- end
79
- end
80
- r.res.elements.each("//ScanSummary/tasks") do |task|
81
- res[:task] = {}
82
- task.attributes.keys.each do |k|
83
- res[:task][k] = task.attributes[k].to_i
84
- end
85
- end
86
- r.res.elements.each("//ScanSummary/vulnerabilities") do |vuln|
87
- res[:vulns] ||= {}
88
- k = vuln.attributes['status'] + (vuln.attributes['severity'] ? ("-" + vuln.attributes['severity']) : '')
89
- res[:vulns][k] = vuln.attributes['count'].to_i
90
- end
91
- r.res.elements.each("//ScanSummary") do |summ|
92
- res[:summary] = {}
93
- summ.attributes.keys.each do |k|
94
- res[:summary][k] = summ.attributes[k]
95
- if (res[:summary][k] =~ /^\d+$/)
96
- res[:summary][k] = res[:summary][k].to_i
97
- end
98
- end
99
- end
100
- r.res.elements.each("//ScanSummary/message") do |message|
101
- res[:message] = message.text
102
- end
103
- res
68
+ # Get scan statistics, including node and vulnerability breakdowns.
69
+ #
70
+ # @return [ScanSummary] ScanSummary object providing statistics for the scan.
71
+ #
72
+ def scan_statistics(scan_id)
73
+ r = execute(make_xml('ScanStatisticsRequest', {'scan-id' => scan_id}))
74
+ if r.success
75
+ ScanSummary.parse(r.res)
104
76
  else
105
77
  false
106
78
  end
@@ -111,115 +83,188 @@ module Nexpose
111
83
  # Object that represents a summary of a scan.
112
84
  #
113
85
  class ScanSummary
86
+
114
87
  # The Scan ID of the Scan
115
88
  attr_reader :scan_id
116
- # The Engine ID used to perform the scan
89
+ # The site that was scanned.
90
+ attr_reader :site_id
91
+ # The Engine ID the scan was dispatched to.
117
92
  attr_reader :engine_id
118
- # TODO: add description
119
- attr_reader :name
120
93
  # The scan start time
121
- attr_reader :startTime
94
+ attr_reader :start_time
122
95
  # The scan finish time
123
- attr_reader :endTime
124
- # The scan status (running|finished|stopped|error| dispatched|paused|aborted|uknown)
96
+ attr_reader :end_time
97
+ # The scan status.
98
+ # One of: running|finished|stopped|error|dispatched|paused|aborted|uknown
125
99
  attr_reader :status
126
- # The number of pending tasks
127
- attr_reader :tasks_pending
128
- # The number of active tasks
129
- attr_reader :tasks_active
130
- # The number of completed tasks
131
- attr_reader :tasks_completed
132
- # The number of "live" nodes
133
- attr_reader :nodes_live
134
- # The number of "dead" nodes
135
- attr_reader :nodes_dead
136
- # The number of filtered nodes
137
- attr_reader :nodes_filtered
138
- # The number of unresolved nodes
139
- attr_reader :nodes_unresolved
140
- # The number of "other" nodes
141
- attr_reader :nodes_other
142
- # Confirmed vulnerabilities found (indexed by severity)
143
- # Associative array, indexed by severity
144
- attr_reader :vuln_exploit
145
- # Unconfirmed vulnerabilities found (indexed by severity)
146
- # Associative array, indexed by severity
147
- attr_reader :vuln_version
148
- # Not vulnerable checks run (confirmed)
149
- attr_reader :not_vuln_exploit
150
- # Not vulnerable checks run (unconfirmed)
151
- attr_reader :not_vuln_version
152
- # Vulnerability check errors
153
- attr_reader :vuln_error
154
- # Vulnerability checks disabled
155
- attr_reader :vuln_disabled
156
- # Vulnerability checks other
157
- attr_reader :vuln_other
100
+
101
+ # The reason the scan was stopped or failed, if applicable.
102
+ attr_reader :message
103
+
104
+ # Task statistics, including pending, active, and completed tasks.
105
+ attr_reader :tasks
106
+ # Node statistics, including live, dead, filtered, and unresolved.
107
+ attr_reader :nodes
108
+ # Vulnerability statistics, including statuses, severities, and counts.
109
+ attr_reader :vulnerabilities
158
110
 
159
111
  # Constructor
160
- # ScanSummary(can_id, $engine_id, $name, tartTime, $endTime, tatus)
161
- def initialize(scan_id, engine_id, name, startTime, endTime, status)
112
+ def initialize(scan_id, site_id, engine_id, status, start_time, end_time, message, tasks, nodes, vulnerabilities)
113
+ @scan_id, @site_id, @engine_id, @status, @start_time, @end_time = scan_id, site_id, engine_id, status, start_time, end_time
114
+ @message, @tasks, @nodes, @vulnerabilities = message, tasks, nodes, vulnerabilities
115
+ end
162
116
 
163
- @scan_id = scan_id
164
- @engine_id = engine_id
165
- @name = name
166
- @startTime = startTime
167
- @endTime = endTime
168
- @status = status
117
+ # Parse a response from a Nexpose console into a valid ScanSummary object.
118
+ #
119
+ # @param [REXML::Document] rexml XML document to parse.
120
+ # @return [ScanSummary] Scan summary represented by the XML.
121
+ #
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
169
130
 
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)
147
+ end
170
148
  end
171
149
 
172
- end
150
+ # Value class to tracking task counts.
151
+ #
152
+ class Tasks
153
+ attr_reader :pending, :active, :completed
173
154
 
174
- # TODO
175
- # === Description
176
- # Object that represents the overview statistics for a particular scan.
177
- #
178
- # === Examples
179
- #
180
- # # Create a new Nexpose Connection on the default port and Login
181
- # nsc = Connection.new("10.1.40.10","nxadmin","password")
182
- # nsc.login()
183
- #
184
- # # Get a Site (Site ID = 12) from the NSC
185
- # site = new Site(nsc,12)
186
- #
187
- # # Start a Scan of this site and pause for 1 minute
188
- # scan1 = site.scanSite()
189
- # sleep(60)
190
- #
191
- # # Get the Scan Statistics for this scan
192
- # scanStatistics = new ScanStatistics(nsc,scan1["scan_id"])
193
- #
194
- # # Print out number of confirmed vulnerabilities with a 10 severity
195
- # puts scanStatistics.scansummary.vuln_exploit[10]
196
- #
197
- # # Print out the number of pending tasks left in the scan
198
- # puts scanStatistics.scan_summary.tasks_pending
199
- #
200
- class ScanStatistics
201
- # true if an error condition exists; false otherwise
202
- attr_reader :error
203
- # Error message string
204
- attr_reader :error_msg
205
- # The last XML request sent by this object
206
- attr_reader :request_xml
207
- # The last XML response received by this object
208
- attr_reader :reseponse_xml
209
- # The Scan ID
210
- attr_reader :scan_id
211
- # The ScanSummary of the scan
212
- attr_reader :scan_summary
213
- # The NSC Connection associated with this object
214
- attr_reader :connection
155
+ def initialize(pending, active, completed)
156
+ @pending, @active, @completed = pending, active, completed
157
+ end
158
+
159
+ # Parse REXML to Tasks object.
160
+ #
161
+ # @param [REXML::Document] rexml XML document to parse.
162
+ # @return [Tasks] Task summary represented by the XML.
163
+ #
164
+ 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
170
+ end
171
+ end
172
+
173
+ # Value class for tracking node counts.
174
+ #
175
+ class Nodes
176
+ attr_reader :live, :dead, :filtered, :unresolved, :other
215
177
 
216
- # Vulnerability checks other
217
- attr_reader :vuln_other
178
+ def initialize(live, dead, filtered, unresolved, other)
179
+ @live, @dead, @filtered, @unresolved, @other = live, dead, filtered, unresolved, other
180
+ end
218
181
 
219
- def initialize(connection, scan_id)
220
- @error = false
221
- @connection = connection
222
- @scan_id = scan_id
182
+ # Parse REXML to Nodes object.
183
+ #
184
+ # @param [REXML::Document] rexml XML document to parse.
185
+ # @return [Nodes] Node summary represented by the XML.
186
+ #
187
+ 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
195
+ end
196
+ end
197
+
198
+ # Value class for tracking vulnerability counts.
199
+ #
200
+ class Vulnerabilities
201
+ attr_reader :vuln_exploit, :vuln_version, :vuln_potential,
202
+ :not_vuln_exploit, :not_vuln_version,
203
+ :error, :disabled, :other
204
+
205
+ def initialize(vuln_exploit, vuln_version, vuln_potential,
206
+ not_vuln_exploit, not_vuln_version,
207
+ error, disabled, other)
208
+ @vuln_exploit, @vuln_version, @vuln_potential,
209
+ @not_vuln_exploit, @not_vuln_version,
210
+ @error, @disabled, @other =
211
+ vuln_exploit, vuln_version, vuln_potential,
212
+ not_vuln_exploit, not_vuln_version,
213
+ error, disabled, other
214
+ end
215
+
216
+ # Parse REXML to Vulnerabilities object.
217
+ #
218
+ # @param [REXML::Document] rexml XML document to parse.
219
+ # @return [Vulnerabilities] Vulnerability summary represented by the XML.
220
+ #
221
+ def self.parse(rexml)
222
+ map = {}
223
+ rexml.elements.each("//ScanSummary/vulnerabilities") do |vuln|
224
+ status = map[vuln.attributes['status']]
225
+ if status && vuln.attributes['status'] =~ /^vuln-/
226
+ status.add_severity(vuln.attributes['severity'].to_i, vuln.attributes['count'].to_i)
227
+ else
228
+ map[vuln.attributes['status']] = Status.new(vuln.attributes['severity'], vuln.attributes['count'].to_i)
229
+ end
230
+ end
231
+ Vulnerabilities.new(map['vuln-exploit'],
232
+ map['vuln-version'],
233
+ map['vuln-potential'],
234
+ map['not-vuln-exploit'],
235
+ map['not-vuln-version'],
236
+ map['error'],
237
+ map['disabled'],
238
+ map['other'])
239
+ end
240
+
241
+ # Value class for tracking vulnerability status counts.
242
+ #
243
+ # Severities will only be mapped if they are provided in the response,
244
+ # which currently only happens for vuln-exploit, vuln-version,
245
+ # and vuln-potential.
246
+ #
247
+ class Status
248
+ attr_reader :severities, :count
249
+
250
+ def initialize(severity = nil, count = 0)
251
+ if severity
252
+ @severities = {}
253
+ @count = 0
254
+ add_severity(severity.to_i, count)
255
+ else
256
+ @severities = nil
257
+ @count = count
258
+ end
259
+ end
260
+
261
+ # For vuln-exploit, vuln-version, and vuln-potential,
262
+ # map the count at a severity level, but also maintain an overall count.
263
+ def add_severity(severity, count)
264
+ @count += count
265
+ @severities[severity] = count
266
+ end
267
+ end
223
268
  end
224
269
  end
225
270
 
@@ -7,6 +7,23 @@ module Nexpose
7
7
  xml = make_xml('EngineDeleteRequest', {'engine-id' => engine_id})
8
8
  execute(xml, '1.2')
9
9
  end
10
+
11
+ # Provide a list of current scan activities for a specific Scan Engine.
12
+ #
13
+ # @return [Array[ScanSummary]] Array of ScanSummary objects associated with
14
+ # each active scan on the engine.
15
+ #
16
+ def engine_activity(engine_id)
17
+ xml = make_xml('EngineActivityRequest', {'engine-id' => engine_id})
18
+ r = execute(xml)
19
+ arr = []
20
+ if r.success
21
+ r.res.elements.each("//ScanSummary") do |scan_event|
22
+ arr << ScanSummary.parse(scan_event)
23
+ end
24
+ end
25
+ arr
26
+ end
10
27
  end
11
28
 
12
29
  # ==== Description
@@ -45,10 +62,6 @@ module Nexpose
45
62
  end
46
63
  end
47
64
 
48
- # TODO
49
- class EngineActivity
50
- end
51
-
52
65
  # ==== Description
53
66
  # Object that represents the summary of a scan engine.
54
67
  #
@@ -56,28 +56,21 @@ module Nexpose
56
56
  end
57
57
  end
58
58
 
59
- #-----------------------------------------------------------------------
60
- # TODO: Needs to be expanded to included details
61
- # Also confusing. Name clashes with field on Site
62
- #-----------------------------------------------------------------------
59
+ # Retrieve a list of all previous scans of the site.
60
+ #
61
+ # @param [FixNum] site_id Site ID to request scan history for.
62
+ # @return [Array[ScanSummary]] Array of ScanSummary objects representing
63
+ # each scan run to date on the site provided.
64
+ #
63
65
  def site_scan_history(site_id)
64
- r = execute(make_xml('SiteScanHistoryRequest', {'site-id' => site_id.to_s}))
65
-
66
- if (r.success)
67
- res = []
68
- r.res.elements.each("//ScanSummary") do |site_scan_history|
69
- res << {
70
- :site_id => site_scan_history.attributes['site-id'].to_i,
71
- :scan_id => site_scan_history.attributes['scan-id'].to_i,
72
- :engine_id => site_scan_history.attributes['engine-id'].to_i,
73
- :start_time => site_scan_history.attributes['startTime'].to_s,
74
- :end_time => site_scan_history.attributes['endTime'].to_s
75
- }
66
+ r = execute(make_xml('SiteScanHistoryRequest', {'site-id' => site_id}))
67
+ res = []
68
+ if r.success
69
+ r.res.elements.each("//ScanSummary") do |scan_event|
70
+ res << ScanSummary.parse(scan_event)
76
71
  end
77
- res
78
- else
79
- false
80
72
  end
73
+ res
81
74
  end
82
75
 
83
76
  #-----------------------------------------------------------------------
@@ -480,50 +473,6 @@ module Nexpose
480
473
  end
481
474
  end
482
475
 
483
- # === Description
484
- # Object that represents the scan history of a site.
485
- #
486
- class SiteScanHistory
487
- # true if an error condition exists; false otherwise
488
- attr_reader :error
489
- # Error message string
490
- attr_reader :error_msg
491
- # The last XML request sent by this object
492
- attr_reader :request_xml
493
- # The last response received by this object
494
- attr_reader :response
495
- # The NSC Connection associated with this object
496
- attr_reader :connection
497
- # The Site ID
498
- attr_reader :site_id
499
- # //Array containing (ScanSummary*)
500
- attr_reader :scan_summaries
501
-
502
- def initialize(connection, id)
503
- @site_id = id
504
- @error = false
505
- @connection = connection
506
- @scan_summaries = []
507
-
508
- @request_xml = '<SiteScanHistoryRequest' + ' session-id="' + @connection.session_id + '" site-id="' + "#{@site_id}" + '"/>'
509
- r = @connection.execute(@request_xml)
510
- @response = r
511
-
512
- if r and r.success
513
- r.res.elements.each('//ScanSummary') do |summary|
514
- scan_id=summary.attributes['scan-id'].to_i
515
- engine_id=summary.attributes['engine-id'].to_i
516
- name=summary.attributes['name'].to_s
517
- start_time=summary.attributes['startTime'].to_s
518
- end_time=summary.attributes['endTime'].to_s
519
- status=summary.attributes['status'].to_s
520
- scan_summary = ScanSummary.new(scan_id, engine_id, name, start_time, end_time, status)
521
- scan_summaries << scan_summary
522
- end
523
- end
524
- end
525
- end
526
-
527
476
  # === Description
528
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.
529
478
  #
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.2
4
+ version: 0.1.3
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: 2012-12-27 00:00:00.000000000 Z
13
+ date: 2013-02-06 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: librex