nexpose 0.6.5 → 0.7.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: 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