nexpose 0.6.5 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c5104a1f9ff2537b542608d4c5c8d42418bb42ca
4
- data.tar.gz: 6c5315d6fde97465c5d1120d15990562b518c91d
3
+ metadata.gz: 28266093c49d6743e5a662298252c67779d54659
4
+ data.tar.gz: e1525369d7ee0b2052c87c0b3d14f763085f5276
5
5
  SHA512:
6
- metadata.gz: bfe59dabbed124dbb3f0ab2e40b37bfe1a37252fc7b68c479dc1c0ebad8da7726a40074e20ca6b4cd99b1767a23fab33ac293cfac31f95a59067e10abb404a35
7
- data.tar.gz: 32b85bfd2c1f0f281a2d02302d8381639af4a728dbd6c3b193b5dc633117edc2470767bbc50c4c09b3ae739bbdd8a714f650a3f1c1f3b4ebdd57def0445b34dd
6
+ metadata.gz: baa5a71852ad9259b8086ad9e0a6983cedd152f6da5efbbd3278070e4b633c4c81abd1eeb414aacb33a9010705df40ba30a46e30d85e103028a796f0c3883e10
7
+ data.tar.gz: 481d97ce05e9b5976a3f679dcc502d6f071db593e28dab6fd4be00ccfc365680405c2eda62cb5bdb897059e1de1e78e830043e05b52089cf8303efc684b51c79
data/COPYING ADDED
@@ -0,0 +1,33 @@
1
+ Copyright (C) 2014, Rapid7, Inc.
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification,
5
+ are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice,
8
+ this list of conditions and the following disclaimer.
9
+
10
+ * Redistributions in binary form must reproduce the above copyright notice,
11
+ this list of conditions and the following disclaimer in the documentation
12
+ and/or other materials provided with the distribution.
13
+
14
+ * Neither the name of Rapid7, Inc. nor the names of its contributors
15
+ may be used to endorse or promote products derived from this software
16
+ without specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
22
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
+
29
+ ================================================================================
30
+
31
+ The nexpose-client gem is provided under the 3-clause BSD license above.
32
+
33
+ The copyright on this package is held by Rapid7, Inc.
data/README.markdown CHANGED
@@ -1,4 +1,4 @@
1
- # Nexpose
1
+ # Nexpose-Client
2
2
 
3
3
  This is the official gem package for the Ruby Nexpose API.
4
4
 
@@ -19,6 +19,10 @@ Our coding standards include:
19
19
  * Unless otherwise noted, code should adhere to the Ruby Style Guide: https://github.com/bbatsov/ruby-style-guide
20
20
  * Use YARDoc comment style to improve the API documentation of the gem.
21
21
 
22
+ ## License
23
+
24
+ The nexpose-client gem is provided under the 3-Clause BSD License. See [COPYING](COPYING) for details.
25
+
22
26
  ## Credits
23
27
 
24
- Rapid7 LLC
28
+ Rapid7, Inc.
data/lib/nexpose.rb CHANGED
@@ -66,11 +66,14 @@ require 'nexpose/creds'
66
66
  require 'nexpose/shared_cred'
67
67
  require 'nexpose/data_table'
68
68
  require 'nexpose/device'
69
+ require 'nexpose/discovery'
70
+ require 'nexpose/discovery/filter'
69
71
  require 'nexpose/engine'
70
72
  require 'nexpose/filter'
71
73
  require 'nexpose/group'
72
74
  require 'nexpose/dag'
73
75
  require 'nexpose/manage'
76
+ require 'nexpose/multi_tenant_user'
74
77
  require 'nexpose/pool'
75
78
  require 'nexpose/report'
76
79
  require 'nexpose/report_template'
@@ -78,7 +81,10 @@ require 'nexpose/role'
78
81
  require 'nexpose/scan'
79
82
  require 'nexpose/scan_template'
80
83
  require 'nexpose/silo'
84
+ require 'nexpose/silo_profile'
81
85
  require 'nexpose/site'
86
+ require 'nexpose/tag'
87
+ require 'nexpose/tag/criteria'
82
88
  require 'nexpose/ticket'
83
89
  require 'nexpose/user'
84
90
  require 'nexpose/vuln'
data/lib/nexpose/ajax.rb CHANGED
@@ -10,6 +10,12 @@ module Nexpose
10
10
  module AJAX
11
11
  module_function
12
12
 
13
+ module CONTENT_TYPE
14
+ XML = 'text/xml; charset=UTF-8'
15
+ JSON = 'application/json; charset-utf-8'
16
+ FORM = 'application/x-www-form-urlencoded; charset=UTF-8'
17
+ end
18
+
13
19
  # GET call to a Nexpose controller.
14
20
  #
15
21
  # @param [Connection] nsc API connection to a Nexpose console.
@@ -17,7 +23,7 @@ module Nexpose
17
23
  # @param [String] content_type Content type to use when issuing the GET.
18
24
  # @return [String|REXML::Document|Hash] The response from the call.
19
25
  #
20
- def get(nsc, uri, content_type = 'text/xml; charset=UTF-8')
26
+ def get(nsc, uri, content_type = CONTENT_TYPE::XML)
21
27
  get = Net::HTTP::Get.new(uri)
22
28
  get.set_content_type(content_type)
23
29
  _request(nsc, get)
@@ -31,7 +37,7 @@ module Nexpose
31
37
  # @param [String] content_type Content type to use when issuing the PUT.
32
38
  # @return [String] The response from the call.
33
39
  #
34
- def put(nsc, uri, payload = nil, content_type = 'text/xml; charset=UTF-8')
40
+ def put(nsc, uri, payload = nil, content_type = CONTENT_TYPE::XML)
35
41
  put = Net::HTTP::Put.new(uri)
36
42
  put.set_content_type(content_type)
37
43
  put.body = payload.to_s if payload
@@ -46,13 +52,28 @@ module Nexpose
46
52
  # @param [String] content_type Content type to use when issuing the POST.
47
53
  # @return [String|REXML::Document|Hash] The response from the call.
48
54
  #
49
- def post(nsc, uri, payload = nil, content_type = 'text/xml')
55
+ def post(nsc, uri, payload = nil, content_type = CONTENT_TYPE::XML)
50
56
  post = Net::HTTP::Post.new(uri)
51
57
  post.set_content_type(content_type)
52
58
  post.body = payload.to_s if payload
53
59
  _request(nsc, post)
54
60
  end
55
61
 
62
+ # PATCH call to a Nexpose controller.
63
+ #
64
+ # @param [Connection] nsc API connection to a Nexpose console.
65
+ # @param [String] uri Controller address relative to https://host:port
66
+ # @param [String|REXML::Document] payload XML document required by the call.
67
+ # @param [String] content_type Content type to use when issuing the PATCH.
68
+ # @return [String] The response from the call.
69
+ #
70
+ def patch(nsc, uri, payload = nil, content_type = CONTENT_TYPE::XML)
71
+ patch = Net::HTTP::Patch.new(uri)
72
+ patch.set_content_type(content_type)
73
+ patch.body = payload.to_s if payload
74
+ _request(nsc, patch)
75
+ end
76
+
56
77
  # POST call to a Nexpose controller that uses a form-post model.
57
78
  # This is here to support legacy use of POST in old controllers.
58
79
  #
@@ -63,7 +84,7 @@ module Nexpose
63
84
  # @param [String] content_type Content type to use when issuing the POST.
64
85
  # @return [Hash] The parsed JSON response from the call.
65
86
  #
66
- def form_post(nsc, uri, parameters, content_type = 'application/x-www-form-urlencoded; charset=UTF-8')
87
+ def form_post(nsc, uri, parameters, content_type = CONTENT_TYPE::FORM)
67
88
  post = Net::HTTP::Post.new(uri)
68
89
  post.set_content_type(content_type)
69
90
  post.set_form_data(parameters)
@@ -75,7 +96,7 @@ module Nexpose
75
96
  # @param [Connection] nsc API connection to a Nexpose console.
76
97
  # @param [String] uri Controller address relative to https://host:port
77
98
  # @param [String] content_type Content type to use when issuing the DELETE.
78
- def delete(nsc, uri, content_type = 'text/xml')
99
+ def delete(nsc, uri, content_type = CONTENT_TYPE::XML)
79
100
  delete = Net::HTTP::Delete.new(uri)
80
101
  delete.set_content_type(content_type)
81
102
  _request(nsc, delete)
@@ -89,7 +110,7 @@ module Nexpose
89
110
  # @return [Hash] The parametrized URI.
90
111
 
91
112
  def parametrize_uri(uri, parameters)
92
- uri = uri.concat(('?').concat(parameters.map { |k, v| "#{k}=#{CGI.escape(v[0].to_s)}" }.join('&'))) if parameters
113
+ uri = uri.concat(('?').concat(parameters.map { |k, v| "#{k}=#{CGI.escape(v.to_s)}" }.join('&'))) if parameters
93
114
  end
94
115
 
95
116
  ###
data/lib/nexpose/dag.rb CHANGED
@@ -31,9 +31,9 @@ module Nexpose
31
31
  # load includes admin users, but save will fail if they are included.
32
32
  admins = nsc.users.select { |u| u.is_admin }.map { |u| u.id }
33
33
  @users.reject! { |id| admins.member? id }
34
- data = JSON.parse(AJAX.form_post(nsc,
35
- '/data/assetGroup/saveAssetGroup',
36
- to_map))
34
+ params = @id ? { 'entityid' => @id, 'mode' => 'edit' } : { 'entityid' => false, 'mode' => false }
35
+ uri = AJAX.parametrize_uri('/data/assetGroup/saveAssetGroup', params)
36
+ data = JSON.parse(AJAX.post(nsc, uri, _to_entity_details, AJAX::CONTENT_TYPE::JSON))
37
37
  data['response'] == 'success.'
38
38
  end
39
39
 
@@ -53,21 +53,13 @@ module Nexpose
53
53
  dag
54
54
  end
55
55
 
56
- def to_map
56
+ def _to_entity_details
57
57
  obj = { 'searchCriteria' => @criteria.to_map,
58
58
  'name' => @name,
59
59
  'tag' => @description.nil? ? '' : @description,
60
60
  'dynamic' => true,
61
61
  'users' => @users }
62
- map = { 'entityDetails' => JSON.generate(obj) }
63
- if @id
64
- map['entityid'] = @id
65
- map['mode'] = 'edit'
66
- else
67
- map['entityid'] = false
68
- map['mode'] = false
69
- end
70
- map
62
+ JSON.generate(obj)
71
63
  end
72
64
  end
73
65
  end
@@ -28,7 +28,7 @@ module Nexpose
28
28
  # 'table-id' => 'site-assets',
29
29
  # 'siteID' => site_id })
30
30
  #
31
- def _get_json_table(console, address, parameters, page_size = 500, records = nil)
31
+ def _get_json_table(console, address, parameters = {}, page_size = 500, records = nil)
32
32
  parameters['dir'] = 'DESC'
33
33
  parameters['startIndex'] = -1
34
34
  parameters['results'] = -1
@@ -0,0 +1,191 @@
1
+ module Nexpose
2
+
3
+ class Connection
4
+
5
+ # Retrieve information about all available connections for dynamic
6
+ # discovery of assets, including whether or not connections are active.
7
+ #
8
+ def list_discovery_connections
9
+ xml = make_xml('DiscoveryConnectionListingRequest')
10
+ response = execute(xml, '1.2')
11
+ connections = []
12
+ response.res.elements.each('DiscoveryConnectionListingResponse/DiscoveryConnectionSummary') do |conn|
13
+ connections << DiscoveryConnection.parse(conn)
14
+ end
15
+ connections
16
+ end
17
+ alias_method :discovery_connections, :list_discovery_connections
18
+
19
+ # Delete an existing connection to a target used for dynamic discovery of assets.
20
+ #
21
+ # @param [Fixnum] id ID of an existing discovery connection.
22
+ #
23
+ def delete_discovery_connection(id)
24
+ xml = make_xml('DiscoveryConnectionDeleteRequest', { 'id' => id })
25
+ response = execute(xml, '1.2')
26
+ response.success
27
+ end
28
+ end
29
+
30
+ class DiscoveryConnection
31
+ include XMLUtils
32
+
33
+ module Protocol
34
+ HTTP = 'HTTP'
35
+ HTTPS = 'HTTPS'
36
+ end
37
+
38
+ # A unique identifier for this connection.
39
+ attr_accessor :id
40
+
41
+ # A unique name for this connection.
42
+ attr_accessor :name
43
+
44
+ # The IP address or fully qualified domain name of the server.
45
+ attr_accessor :address
46
+
47
+ # A user name that can be used to log into the server.
48
+ attr_accessor :user
49
+
50
+ # The password to use when connecting with the defined user.
51
+ attr_accessor :password
52
+
53
+ # The protocol used for conneting to the server. One of DiscoveryConnection::Protocol
54
+ attr_accessor :protocol
55
+
56
+ # The port used for connecting to the server. A valid port from 1 to 65535.
57
+ attr_accessor :port
58
+
59
+ # Whether or not the connection is active.
60
+ # Discovery is only possible when the connection is active.
61
+ attr_accessor :status
62
+
63
+ # Create a new discovery connection.
64
+ #
65
+ # @param [String] name Name to assign to this connection.
66
+ # @param [String] address IP or fully qualified domain name of the
67
+ # connection server.
68
+ # @param [String] user User name for credentials on this connection.
69
+ # @param [String] password Password for credentials on this connection.
70
+ #
71
+ def initialize(name, address, user, password = nil)
72
+ @name, @address, @user, @password = name, address, user, password
73
+ @id = -1
74
+ @port = 443
75
+ @protocol = Protocol::HTTPS
76
+ end
77
+
78
+ # Save this discovery connection to a Nexpose console.
79
+ #
80
+ # @param [Connection] nsc Connection to a console.
81
+ #
82
+ def save(nsc)
83
+ if @id == -1
84
+ xml = nsc.make_xml('DiscoveryConnectionCreateRequest')
85
+ else
86
+ xml = nsc.make_xml('DiscoveryConnectionUpdateRequest')
87
+ end
88
+ xml.add_element(as_xml)
89
+ response = nsc.execute(xml, '1.2')
90
+ if response.success
91
+ ret = REXML::XPath.first(response.res, 'DiscoveryConnectionCreateResponse')
92
+ @id = ret.attributes['id'].to_i unless ret.nil?
93
+ end
94
+ @id
95
+ end
96
+
97
+ # Perform dynamic discover of assets against this connection.
98
+ #
99
+ # @param [Connection] nsc Connection to a console.
100
+ # @param [Criteria] criteria Criteria search object narrowing which assets
101
+ # to filter.
102
+ # @return [Array[DiscoveredAsset]] All discovered assets matching the criteria.
103
+ #
104
+ def discover(nsc, criteria = nil)
105
+ parameters = { 'table-id' => 'assetdiscovery',
106
+ 'sort' => 'assetDiscoveryName',
107
+ 'searchCriteria' => criteria.nil? ? 'null' : criteria.to_json,
108
+ 'configID' => @id }
109
+ data = DataTable._get_json_table(nsc, '/data/discoveryAsset/discoverAssets', parameters)
110
+ data.map { |a| DiscoveredAsset.parse(a) }
111
+ end
112
+
113
+ # Initiates a connection to a target used for dynamic discovery of assets.
114
+ # As long as a connection is active, dynamic discovery is continuous.
115
+ #
116
+ # @param [Connection] nsc Connection to a console.
117
+ #
118
+ def connect(nsc)
119
+ xml = nsc.make_xml('DiscoveryConnectionConnectRequest', { 'id' => id })
120
+ response = nsc.execute(xml, '1.2')
121
+ response.success
122
+ end
123
+
124
+ # Delete this connection from the console.
125
+ #
126
+ # @param [Connection] nsc Connection to a console.
127
+ #
128
+ def delete(nsc)
129
+ nsc.delete_discovery_connection(@id)
130
+ end
131
+
132
+ def as_xml
133
+ xml = REXML::Element.new('DiscoveryConnection')
134
+ xml.add_attributes({ 'name' => @name,
135
+ 'address' => @address,
136
+ 'port' => @port,
137
+ 'protocol' => @protocol,
138
+ 'user-name' => @user,
139
+ 'password' => @password })
140
+ xml
141
+ end
142
+
143
+ def to_xml
144
+ as_xml.to_s
145
+ end
146
+
147
+ def self.parse(xml)
148
+ conn = new(xml.attributes['name'],
149
+ xml.attributes['address'],
150
+ xml.attributes['user-name'])
151
+ conn.id = xml.attributes['id'].to_i
152
+ conn.protocol = xml.attributes['protocol']
153
+ conn.port = xml.attributes['port'].to_i
154
+ conn.status = xml.attributes['connection-status']
155
+ conn
156
+ end
157
+ end
158
+
159
+ class DiscoveredAsset
160
+
161
+ attr_accessor :name
162
+ attr_accessor :ip
163
+ attr_accessor :host
164
+ attr_accessor :datacenter
165
+ attr_accessor :cluster
166
+ attr_accessor :pool
167
+ attr_accessor :os
168
+ attr_accessor :status
169
+
170
+ def initialize(&block)
171
+ instance_eval &block if block_given?
172
+ end
173
+
174
+ def on?
175
+ @status == 'On'
176
+ end
177
+
178
+ def self.parse(json)
179
+ new do |asset|
180
+ asset.ip = json['IPAddress']
181
+ asset.os = json['OSName']
182
+ asset.name = json['assetDiscoveryName']
183
+ asset.cluster = json['cluster']
184
+ asset.datacenter = json['datacenter']
185
+ asset.host = json['host']
186
+ asset.status = json['powerStatus']
187
+ asset.pool = json['resourcePool']
188
+ end
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,36 @@
1
+ module Nexpose
2
+ module Search
3
+ module Field
4
+
5
+ # Valid Operators: IS, IS_NOT, CONTAINS, NOT_CONTAINS, STARTS_WITH
6
+ CLUSTER = 'CLUSTER'
7
+
8
+ # Valid Operators: IS, IS_NOT
9
+ DATACENTER ='DATACENTER'
10
+
11
+ # Valid Operators: CONTAINS, NOT_CONTAINS
12
+ GUEST_OS_FAMILY = 'GUEST_OS_FAMILY'
13
+
14
+ # Valid Operators: IN, NOT_IN
15
+ IP_ADDRESS_RANGE = 'IP_ADDRESS'
16
+
17
+ # Valid Operators: IN, NOT_IN
18
+ # Valid Values (See Value::PowerState): ON, OFF, SUSPENDED
19
+ POWER_STATE = 'POWER_STATE'
20
+
21
+ # Valid Operators: CONTAINS, NOT_CONTAINS
22
+ RESOURCE_POOL_PATH ='RESOURCE_POOL_PATH'
23
+
24
+ # Valid Operators: IS, IS_NOT, CONTAINS, NOT_CONTAINS, STARTS_WITH
25
+ VIRTUAL_MACHINE_NAME = 'VM'
26
+ end
27
+
28
+ module Value
29
+ module PowerState
30
+ ON = 'poweredOn'
31
+ OFF = 'poweredOff'
32
+ SUSPENDED = 'suspended'
33
+ end
34
+ end
35
+ end
36
+ end