nexpose 0.2.8 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e8303bf73cfbd73693c68670f4dd448eccc8c2a0
4
- data.tar.gz: b28294ca9658ae322e759a851ad13fa35d2bf06e
3
+ metadata.gz: 096649e7275b4dd061fab3b7331e93f66fdec709
4
+ data.tar.gz: 483e8f15738e9b4297a702ee07c78b9f75381f6a
5
5
  SHA512:
6
- metadata.gz: b58c671197ce229fb7518f2cfd7ee02940931bee4c9e5a832ae54737600331f2e831ea085b4ec2b9c5d90380e9f0ca37b3534dc0c9c6ee0802b3397c96ef549a
7
- data.tar.gz: 7423eb92c736ef27cd5273bdc079dbf9806845663c98d7bd79197d79660ebab8187d80110b145ac70c385644caa33b88bbff5211e0f8ab4deda787d02e372c41
6
+ metadata.gz: 368763434ec2b93e957cbf596b36eeeddad7728170234dee6eaa72155e30f11d3826bc7503e22952005f2f10b6bc9cce0ad39769355cd1aeff59082d107db335
7
+ data.tar.gz: a02679981f17023624c99887fd2c9677678c6e1148a302c0aa7e0b1ce17c292bad83810c6a56da2e4614853fd009702a740f5d87fb41eb935a18359592a233ad
data/lib/nexpose.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  #
4
4
  =begin
5
5
 
6
- Copyright (C) 2009-2012, Rapid7 LLC
6
+ Copyright (C) 2009-2013, Rapid7 LLC
7
7
  All rights reserved.
8
8
 
9
9
  Redistribution and use in source and binary forms, with or without modification,
@@ -54,25 +54,34 @@ require 'net/http'
54
54
  require 'uri'
55
55
  require 'rex/mime'
56
56
  require 'ipaddr'
57
+ require 'json'
57
58
  require 'nexpose/error'
58
59
  require 'nexpose/util'
59
- require 'nexpose/user'
60
+ require 'nexpose/alert'
61
+ require 'nexpose/ajax'
60
62
  require 'nexpose/api_request'
63
+ require 'nexpose/common'
64
+ require 'nexpose/creds'
65
+ require 'nexpose/data_table'
66
+ require 'nexpose/device'
67
+ require 'nexpose/engine'
68
+ require 'nexpose/filter'
69
+ require 'nexpose/group'
70
+ require 'nexpose/dag'
61
71
  require 'nexpose/manage'
62
- require 'nexpose/misc'
72
+ require 'nexpose/pool'
63
73
  require 'nexpose/report'
74
+ require 'nexpose/report_template'
75
+ require 'nexpose/role'
64
76
  require 'nexpose/scan'
65
- require 'nexpose/scan_engine'
77
+ require 'nexpose/scan_template'
66
78
  require 'nexpose/silo'
67
79
  require 'nexpose/site'
68
80
  require 'nexpose/ticket'
81
+ require 'nexpose/user'
69
82
  require 'nexpose/vuln'
70
- require 'nexpose/creds'
83
+ require 'nexpose/vuln_exception'
71
84
  require 'nexpose/connection'
72
- require 'nexpose/role'
73
- require 'nexpose/common'
74
- require 'nexpose/group'
75
- require 'nexpose/alert'
76
85
 
77
86
  module Nexpose
78
87
 
@@ -0,0 +1,127 @@
1
+ # encoding: utf-8
2
+
3
+ module Nexpose
4
+
5
+ # Accessor to the Nexpose AJAX API.
6
+ # These core methods should allow direct access to underlying controllers
7
+ # in order to test functionality that is not currently exposed
8
+ # through the XML API.
9
+ #
10
+ module AJAX
11
+ module_function
12
+
13
+ # GET call to a Nexpose controller.
14
+ #
15
+ # @param [Connection] nsc API connection to a Nexpose console.
16
+ # @param [String] uri Controller address relative to https://host:port
17
+ # @param [String] content_type Content type to use when issuing the GET.
18
+ # @return [String|REXML::Document|Hash] The response from the call.
19
+ #
20
+ def get(nsc, uri, content_type = 'text/xml; charset=UTF-8')
21
+ get = Net::HTTP::Get.new(uri)
22
+ get.set_content_type(content_type)
23
+ _request(nsc, get)
24
+ end
25
+
26
+ # PUT call to a Nexpose controller.
27
+ #
28
+ # @param [Connection] nsc API connection to a Nexpose console.
29
+ # @param [String] uri Controller address relative to https://host:port
30
+ # @param [String|REXML::Document] payload XML document required by the call.
31
+ # @param [String] content_type Content type to use when issuing the PUT.
32
+ # @return [String] The response from the call.
33
+ #
34
+ def put(nsc, uri, payload = nil, content_type = 'text/xml; charset=UTF-8')
35
+ put = Net::HTTP::Put.new(uri)
36
+ put.set_content_type(content_type)
37
+ put.body = payload.to_s if payload
38
+ _request(nsc, put)
39
+ end
40
+
41
+ # POST call to a Nexpose controller.
42
+ #
43
+ # @param [Connection] nsc API connection to a Nexpose console.
44
+ # @param [String] uri Controller address relative to https://host:port
45
+ # @param [String|REXML::Document] payload XML document required by the call.
46
+ # @param [String] content_type Content type to use when issuing the POST.
47
+ # @return [String|REXML::Document|Hash] The response from the call.
48
+ #
49
+ def post(nsc, uri, payload = nil, content_type = 'text/xml')
50
+ post = Net::HTTP::Post.new(uri)
51
+ post.set_content_type(content_type)
52
+ post.body = payload.to_s if payload
53
+ _request(nsc, post)
54
+ end
55
+
56
+ # POST call to a Nexpose controller that uses a form-post model.
57
+ # This is here to support legacy use of POST in old controllers.
58
+ #
59
+ # @param [Connection] nsc API connection to a Nexpose console.
60
+ # @param [String] uri Controller address relative to https://host:port
61
+ # @param [Hash] parameters Hash of attributes that need to be sent
62
+ # to the controller.
63
+ # @param [String] content_type Content type to use when issuing the POST.
64
+ # @return [Hash] The parsed JSON response from the call.
65
+ #
66
+ def form_post(nsc, uri, parameters, content_type = 'application/x-www-form-urlencoded; charset=UTF-8')
67
+ post = Net::HTTP::Post.new(uri)
68
+ post.set_content_type(content_type)
69
+ post.set_form_data(parameters)
70
+ _request(nsc, post)
71
+ end
72
+
73
+ # DELETE call to a Nexpose controller.
74
+ #
75
+ # @param [Connection] nsc API connection to a Nexpose console.
76
+ # @param [String] uri Controller address relative to https://host:port
77
+ # @param [String] content_type Content type to use when issuing the DELETE.
78
+ def delete(nsc, uri, content_type = 'text/xml')
79
+ delete = Net::HTTP::Delete.new(uri)
80
+ delete.set_content_type(content_type)
81
+ _request(nsc, delete)
82
+ end
83
+
84
+ # Append the query parameters to given URI.
85
+ #
86
+ # @param [String] uri Controller address relative to https://host:port
87
+ # @param [Hash] parameters Hash of attributes that need to be sent
88
+ # to the controller.
89
+ # @return [Hash] The parametrized URI.
90
+
91
+ def parametrize_uri(uri, parameters)
92
+ uri = uri.concat(('?').concat(parameters.map { |k, v| "#{k}=#{CGI.escape(v[0].to_s)}" }.join('&'))) if parameters
93
+ end
94
+
95
+ ###
96
+ # Internal helper methods
97
+
98
+ # Use the Nexpose::Connection to establish a correct HTTPS object.
99
+ def _https(nsc)
100
+ http = Net::HTTP.new(nsc.host, nsc.port)
101
+ http.use_ssl = true
102
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
103
+ http
104
+ end
105
+
106
+ # Attach necessary header fields.
107
+ def _headers(nsc, request)
108
+ request.add_field('nexposeCCSessionID', nsc.session_id)
109
+ request.add_field('Cookie', "nexposeCCSessionID=#{nsc.session_id}")
110
+ end
111
+
112
+ def _request(nsc, request)
113
+ http = _https(nsc)
114
+ _headers(nsc, request)
115
+
116
+ # Return response body if request is successful. Brittle.
117
+ response = http.request(request)
118
+ case response
119
+ when Net::HTTPOK
120
+ response.body
121
+ else
122
+ req_type = request.class.name.split('::').last
123
+ raise Nexpose::APIError.new(response, "#{req_type} request to #{request.path} failed. #{request.body}")
124
+ end
125
+ end
126
+ end
127
+ end
data/lib/nexpose/alert.rb CHANGED
@@ -7,21 +7,16 @@ module Nexpose
7
7
 
8
8
  # Name for this alert.
9
9
  attr_accessor :name
10
-
11
10
  # Whether or not this alert is currently active.
12
11
  attr_accessor :enabled
13
-
14
12
  # Send at most this many alerts per scan.
15
13
  attr_accessor :max_alerts
16
-
17
14
  # Send alerts based upon scan status.
18
15
  attr_accessor :scan_filter
19
-
20
16
  # Send alerts based upon vulnerability finding status.
21
17
  attr_accessor :vuln_filter
22
-
23
18
  # Alert type and its configuration. One of SMTPAlert, SyslogAlert, SNMPAlert
24
- attr_accessor :alert
19
+ attr_accessor :type
25
20
 
26
21
  def initialize(name, enabled = 1, max_alerts = -1)
27
22
  @name, @enabled, @max_alerts = name, enabled, max_alerts
@@ -29,13 +24,13 @@ module Nexpose
29
24
 
30
25
  def to_xml
31
26
  xml = '<Alert'
32
- xml << %Q( name="#{@name}")
33
- xml << %Q( enabled="#{@enabled}")
34
- xml << %Q( maxAlerts="#{@max_alerts}")
27
+ xml << %( name="#{@name}")
28
+ xml << %( enabled="#{@enabled}")
29
+ xml << %( maxAlerts="#{@max_alerts}")
35
30
  xml << '>'
36
31
  xml << scan_filter.to_xml
37
32
  xml << vuln_filter.to_xml
38
- xml << alert.to_xml
33
+ xml << type.to_xml
39
34
  xml << '</Alert>'
40
35
  end
41
36
 
@@ -43,7 +38,7 @@ module Nexpose
43
38
  #
44
39
  # @param [REXML::Document] rexml XML document to parse.
45
40
  # @return [Alert] Alert object represented by the XML.
46
- #
41
+ #
47
42
  def self.parse(rexml)
48
43
  name = rexml.attributes['name']
49
44
  rexml.elements.each("//Alert[@name='#{name}']") do |xml|
@@ -53,11 +48,11 @@ module Nexpose
53
48
  alert.scan_filter = ScanFilter.parse(REXML::XPath.first(xml, "//Alert[@name='#{name}']/scanFilter"))
54
49
  alert.vuln_filter = VulnFilter.parse(REXML::XPath.first(xml, "//Alert[@name='#{name}']/vulnFilter"))
55
50
  if (type = REXML::XPath.first(xml, "//Alert[@name='#{name}']/smtpAlert"))
56
- alert.alert = SMTPAlert.parse(type)
51
+ alert.type = SMTPAlert.parse(type)
57
52
  elsif (type = REXML::XPath.first(xml, "//Alert[@name='#{name}']/syslogAlert"))
58
- alert.alert = SyslogAlert.parse(type)
53
+ alert.type = SyslogAlert.parse(type)
59
54
  elsif (type = REXML::XPath.first(xml, "//Alert[@name='#{name}']/snmpAlert"))
60
- alert.alert = SNMPAlert.parse(type)
55
+ alert.type = SNMPAlert.parse(type)
61
56
  end
62
57
  return alert
63
58
  end
@@ -78,11 +73,11 @@ module Nexpose
78
73
 
79
74
  def to_xml
80
75
  xml = '<scanFilter'
81
- xml << %Q( scanStart="#{@start}")
82
- xml << %Q( scanStop="#{@stop}")
83
- xml << %Q( scanFailed="#{@fail}")
84
- xml << %Q( scanResumed="#{@resume}")
85
- xml << %Q( scanPaused="#{@pause}")
76
+ xml << %( scanStart="#{@start}")
77
+ xml << %( scanStop="#{@stop}")
78
+ xml << %( scanFailed="#{@fail}")
79
+ xml << %( scanResumed="#{@resume}")
80
+ xml << %( scanPaused="#{@pause}")
86
81
  xml << '/>'
87
82
  end
88
83
 
@@ -99,6 +94,7 @@ module Nexpose
99
94
  # Set values to 1 to enable and 0 to disable.
100
95
  #
101
96
  class VulnFilter
97
+
102
98
  # Only alert on vulnerability findings with a severity level greater than this level.
103
99
  # Range is 0 to 10.
104
100
  # Values in the UI correspond as follows:
@@ -116,10 +112,10 @@ module Nexpose
116
112
 
117
113
  def to_xml
118
114
  xml = '<vulnFilter'
119
- xml << %Q( severityThreshold="#{@severity}")
120
- xml << %Q( confirmed="#{@confirmed}")
121
- xml << %Q( unconfirmed="#{@unconfirmed}")
122
- xml << %Q( potential="#{@potential}")
115
+ xml << %( severityThreshold="#{@severity}")
116
+ xml << %( confirmed="#{@confirmed}")
117
+ xml << %( unconfirmed="#{@unconfirmed}")
118
+ xml << %( potential="#{@potential}")
123
119
  xml << '/>'
124
120
  end
125
121
 
@@ -151,7 +147,7 @@ module Nexpose
151
147
 
152
148
  def to_xml
153
149
  xml = '<syslogAlert'
154
- xml << %Q( server="#{replace_entities(server)}">)
150
+ xml << %( server="#{replace_entities(server)}">)
155
151
  xml << '</syslogAlert>'
156
152
  end
157
153
  end
@@ -180,8 +176,8 @@ module Nexpose
180
176
 
181
177
  def to_xml
182
178
  xml = '<snmpAlert'
183
- xml << %Q( community="#{replace_entities(community)}")
184
- xml << %Q( server="#{replace_entities(server)}">)
179
+ xml << %( community="#{replace_entities(community)}")
180
+ xml << %( server="#{replace_entities(server)}">)
185
181
  xml << '</snmpAlert>'
186
182
  end
187
183
  end
@@ -191,15 +187,12 @@ module Nexpose
191
187
  #
192
188
  class SMTPAlert
193
189
 
194
- # The email address of the sender
190
+ # The e-mail address of the sender.
195
191
  attr_accessor :sender
196
-
197
- # The server to sent this alert
192
+ # The server to sent this alert.
198
193
  attr_accessor :server
199
-
200
- # Limit the text for mobile devices
194
+ # Limit the text for mobile devices.
201
195
  attr_accessor :limit_text
202
-
203
196
  # Array of strings with the e-mail addresses of the intended recipients.
204
197
  attr_accessor :recipients
205
198
 
@@ -210,7 +203,7 @@ module Nexpose
210
203
  @recipients = []
211
204
  end
212
205
 
213
- # Adds a new Recipient to the recipients array
206
+ # Adds a new recipient to the alert.
214
207
  def add_recipient(recipient)
215
208
  @recipients << recipient
216
209
  end
@@ -219,9 +212,9 @@ module Nexpose
219
212
 
220
213
  def to_xml
221
214
  xml = '<smtpAlert'
222
- xml << %Q( sender="#{replace_entities(sender)}")
223
- xml << %Q( server="#{replace_entities(server)}")
224
- xml << %Q( limitText="#{limit_text}">)
215
+ xml << %( sender="#{replace_entities(sender)}")
216
+ xml << %( server="#{replace_entities(server)}")
217
+ xml << %( limitText="#{limit_text}">)
225
218
  recipients.each do |recpt|
226
219
  xml << "<recipient>#{replace_entities(recpt)}</recipient>"
227
220
  end
@@ -27,6 +27,7 @@ module Nexpose
27
27
  # the application will send reports via e-mail to access-list members and
28
28
  # non-members.
29
29
  class Email
30
+
30
31
  # Send as file attachment or zipped file to individuals who are not members
31
32
  # of the report access list. One of: file|zip
32
33
  attr_accessor :send_as
@@ -55,17 +56,17 @@ module Nexpose
55
56
 
56
57
  def to_xml
57
58
  xml = '<Email'
58
- xml << %Q{ toAllAuthorized='#{@toAllAuthorized ? 1 : 0}'}
59
- xml << %Q{ sendToOwnerAs='#{@send_to_owner_as}'} if @send_to_owner_as
60
- xml << %Q{ sendToAclAs='#{@send_to_acl_as}'} if @send_to_acl_as
61
- xml << %Q{ sendAs='#{@send_as}'} if @send_as
59
+ xml << %( toAllAuthorized='#{@toAllAuthorized ? 1 : 0}')
60
+ xml << %( sendToOwnerAs='#{@send_to_owner_as}') if @send_to_owner_as
61
+ xml << %( sendToAclAs='#{@send_to_acl_as}') if @send_to_acl_as
62
+ xml << %( sendAs='#{@send_as}') if @send_as
62
63
  xml << '>'
63
- xml << %Q{<Sender>#{@sender}</Sender>} if @sender
64
- xml << %Q{<SmtpRelayServer>#{@smtp_relay_server}</SmtpRelayServer>} if @smtp_relay_server
64
+ xml << %(<Sender>#{@sender}</Sender>) if @sender
65
+ xml << %(<SmtpRelayServer>#{@smtp_relay_server}</SmtpRelayServer>) if @smtp_relay_server
65
66
  if @recipients
66
67
  xml << '<Recipients>'
67
68
  @recipients.each do |recipient|
68
- xml << %Q{<Recipient>#{recipient}</Recipient>}
69
+ xml << %(<Recipient>#{recipient}</Recipient>)
69
70
  end
70
71
  xml << '</Recipients>'
71
72
  end
@@ -121,11 +122,11 @@ module Nexpose
121
122
  end
122
123
 
123
124
  def to_xml
124
- xml = %Q{<Schedule enabled='#{@enabled ? 1 : 0}' type='#{@type}' interval='#{@interval}' start='#{@start}'}
125
- xml << %Q{ maxDuration='#@max_duration'} if @max_duration
126
- xml << %Q{ notValidAfter='#@not_valid_after'} if @not_valid_after
127
- xml << %Q{ incremental='#{@incremental ? 1 : 0}'} if @incremental
128
- xml << %Q{ repeaterType='#@repeater_type'} if @repeater_type
125
+ xml = %(<Schedule enabled='#{@enabled ? 1 : 0}' type='#{@type}' interval='#{@interval}' start='#{@start}')
126
+ xml << %( maxDuration='#{@max_duration}') if @max_duration
127
+ xml << %( notValidAfter='#{@not_valid_after}') if @not_valid_after
128
+ xml << %( incremental='#{@incremental ? 1 : 0}') if @incremental
129
+ xml << %( repeaterType='#{@repeater_type}') if @repeater_type
129
130
  xml << '/>'
130
131
  end
131
132
 
@@ -4,9 +4,12 @@ module Nexpose
4
4
  # Object that represents a connection to a Nexpose Security Console.
5
5
  #
6
6
  # === Examples
7
- # # Create a new Nexpose Connection on the default port
7
+ # # Create a new Nexpose::Connection on the default port
8
8
  # nsc = Connection.new('10.1.40.10', 'nxadmin', 'password')
9
9
  #
10
+ # # Create a new Nexpose::Connection from a URI or "URI" String
11
+ # nsc = Connection.from_uri('https://10.1.40.10:3780', 'nxadmin', 'password')
12
+ #
10
13
  # # Login to NSC and Establish a Session ID
11
14
  # nsc.login
12
15
  #
@@ -17,7 +20,7 @@ module Nexpose
17
20
  # puts 'Login Failure'
18
21
  # end
19
22
  #
20
- # # //Logout
23
+ # # Logout
21
24
  # logout_success = nsc.logout
22
25
  #
23
26
  class Connection
@@ -42,7 +45,13 @@ module Nexpose
42
45
  # The last XML response received by this object, useful for debugging.
43
46
  attr_reader :response_xml
44
47
 
45
- # Constructor for Connection
48
+ # A constructor to load a Connection object from a URI
49
+ def self.from_uri(uri, user, pass, silo_id = nil)
50
+ uri = URI.parse(uri)
51
+ new(uri.host, user, pass, uri.port, silo_id)
52
+ end
53
+
54
+ # A constructor for Connection
46
55
  def initialize(ip, user, pass, port = 3780, silo_id = nil)
47
56
  @host = ip
48
57
  @port = port
@@ -57,25 +66,21 @@ module Nexpose
57
66
  def login
58
67
  begin
59
68
  login_hash = {'sync-id' => 0, 'password' => @password, 'user-id' => @username}
60
- unless @silo_id.nil?
61
- login_hash['silo-id'] = @silo_id
62
- end
69
+ login_hash['silo-id'] = @silo_id if @silo_id
63
70
  r = execute(make_xml('LoginRequest', login_hash))
71
+ if r.success
72
+ @session_id = r.sid
73
+ true
74
+ end
64
75
  rescue APIError
65
76
  raise AuthenticationFailed.new(r)
66
77
  end
67
- if (r.success)
68
- @session_id = r.sid
69
- true
70
- end
71
78
  end
72
79
 
73
80
  # Logout of the current connection
74
81
  def logout
75
82
  r = execute(make_xml('LogoutRequest', {'sync-id' => 0}))
76
- if (r.success)
77
- return true
78
- end
83
+ return true if r.success
79
84
  raise APIError.new(r, 'Logout failed')
80
85
  end
81
86