nexpose 7.2.1 → 7.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -1,65 +1,118 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nexpose (5.3.0)
4
+ nexpose (7.3.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
- addressable (2.3.8)
10
- ast (2.2.0)
11
- astrolabe (1.3.1)
12
- parser (~> 2.2)
13
- codeclimate-test-reporter (0.4.7)
9
+ activesupport (6.1.1)
10
+ concurrent-ruby (~> 1.0, >= 1.0.2)
11
+ i18n (>= 1.6, < 2)
12
+ minitest (>= 5.1)
13
+ tzinfo (~> 2.0)
14
+ zeitwerk (~> 2.3)
15
+ addressable (2.7.0)
16
+ public_suffix (>= 2.0.2, < 5.0)
17
+ ast (2.4.1)
18
+ codeclimate-test-reporter (0.4.8)
14
19
  simplecov (>= 0.7.1, < 1.0.0)
15
- crack (0.4.2)
16
- safe_yaml (~> 1.0.0)
17
- diff-lcs (1.2.5)
20
+ coderay (1.1.3)
21
+ concurrent-ruby (1.1.7)
22
+ crack (0.4.5)
23
+ rexml
24
+ diff-lcs (1.4.4)
18
25
  docile (1.1.5)
19
- multi_json (1.11.2)
20
- parser (2.3.1.2)
21
- ast (~> 2.2)
22
- powerpack (0.1.1)
23
- rainbow (2.0.0)
24
- rake (12.0.0)
25
- rspec (3.3.0)
26
- rspec-core (~> 3.3.0)
27
- rspec-expectations (~> 3.3.0)
28
- rspec-mocks (~> 3.3.0)
29
- rspec-core (3.3.2)
30
- rspec-support (~> 3.3.0)
31
- rspec-expectations (3.3.1)
26
+ faraday (1.3.0)
27
+ faraday-net_http (~> 1.0)
28
+ multipart-post (>= 1.2, < 3)
29
+ ruby2_keywords
30
+ faraday-http-cache (2.2.0)
31
+ faraday (>= 0.8)
32
+ faraday-net_http (1.0.1)
33
+ github_changelog_generator (1.15.2)
34
+ activesupport
35
+ faraday-http-cache
36
+ multi_json
37
+ octokit (~> 4.6)
38
+ rainbow (>= 2.2.1)
39
+ rake (>= 10.0)
40
+ retriable (~> 3.0)
41
+ i18n (1.8.7)
42
+ concurrent-ruby (~> 1.0)
43
+ method_source (0.9.2)
44
+ minitest (5.14.3)
45
+ multi_json (1.15.0)
46
+ multipart-post (2.1.1)
47
+ octokit (4.20.0)
48
+ faraday (>= 0.9)
49
+ sawyer (~> 0.8.0, >= 0.5.3)
50
+ parallel (1.20.1)
51
+ parser (3.0.0.0)
52
+ ast (~> 2.4.1)
53
+ pry (0.9.12.6)
54
+ coderay (~> 1.0)
55
+ method_source (~> 0.8)
56
+ slop (~> 3.4)
57
+ public_suffix (4.0.6)
58
+ rainbow (3.0.0)
59
+ rake (13.0.3)
60
+ regexp_parser (2.0.3)
61
+ retriable (3.1.2)
62
+ rexml (3.2.4)
63
+ rspec (3.10.0)
64
+ rspec-core (~> 3.10.0)
65
+ rspec-expectations (~> 3.10.0)
66
+ rspec-mocks (~> 3.10.0)
67
+ rspec-core (3.10.1)
68
+ rspec-support (~> 3.10.0)
69
+ rspec-expectations (3.10.1)
32
70
  diff-lcs (>= 1.2.0, < 2.0)
33
- rspec-support (~> 3.3.0)
34
- rspec-mocks (3.3.2)
71
+ rspec-support (~> 3.10.0)
72
+ rspec-mocks (3.10.1)
35
73
  diff-lcs (>= 1.2.0, < 2.0)
36
- rspec-support (~> 3.3.0)
37
- rspec-support (3.3.0)
38
- rubocop (0.29.1)
39
- astrolabe (~> 1.3)
40
- parser (>= 2.2.0.1, < 3.0)
41
- powerpack (~> 0.1)
42
- rainbow (>= 1.99.1, < 3.0)
43
- ruby-progressbar (~> 1.4)
44
- ruby-progressbar (1.7.5)
45
- safe_yaml (1.0.4)
74
+ rspec-support (~> 3.10.0)
75
+ rspec-support (3.10.1)
76
+ rubocop (1.8.1)
77
+ parallel (~> 1.10)
78
+ parser (>= 3.0.0.0)
79
+ rainbow (>= 2.2.2, < 4.0)
80
+ regexp_parser (>= 1.8, < 3.0)
81
+ rexml
82
+ rubocop-ast (>= 1.2.0, < 2.0)
83
+ ruby-progressbar (~> 1.7)
84
+ unicode-display_width (>= 1.4.0, < 3.0)
85
+ rubocop-ast (1.4.0)
86
+ parser (>= 2.7.1.5)
87
+ ruby-progressbar (1.11.0)
88
+ ruby2_keywords (0.0.2)
89
+ sawyer (0.8.2)
90
+ addressable (>= 2.3.5)
91
+ faraday (> 0.8, < 2.0)
46
92
  simplecov (0.9.2)
47
93
  docile (~> 1.1.0)
48
94
  multi_json (~> 1.0)
49
95
  simplecov-html (~> 0.9.0)
50
96
  simplecov-html (0.9.0)
97
+ slop (3.6.0)
98
+ tzinfo (2.0.4)
99
+ concurrent-ruby (~> 1.0)
100
+ unicode-display_width (2.0.0)
51
101
  vcr (2.9.3)
52
102
  webmock (1.20.4)
53
103
  addressable (>= 2.3.6)
54
104
  crack (>= 0.3.2)
105
+ zeitwerk (2.4.2)
55
106
 
56
107
  PLATFORMS
57
108
  ruby
58
109
 
59
110
  DEPENDENCIES
60
- bundler (~> 1.3)
111
+ bundler
61
112
  codeclimate-test-reporter (~> 0.4.6)
113
+ github_changelog_generator
62
114
  nexpose!
115
+ pry (= 0.9.12.6)
63
116
  rake
64
117
  rspec (~> 3.2)
65
118
  rubocop
@@ -68,4 +121,4 @@ DEPENDENCIES
68
121
  webmock (~> 1.20.4)
69
122
 
70
123
  BUNDLED WITH
71
- 1.13.6
124
+ 1.17.3
data/README.markdown CHANGED
@@ -1,5 +1,10 @@
1
+ # DEPRECATED
2
+ The [RESTful API for the Nexpose/InsightVM Security Console](https://help.rapid7.com/insightvm/en-us/api/index.html) has rendered this library obsolete. If you require a Ruby library for that API you can use a [generated client](https://github.com/rapid7/vm-console-client-ruby). Clients for other languages can be generated from the Swagger specification. Note that generated clients are not officially supported or maintained by Rapid7.
3
+
4
+ While this project is no longer under active development, it is still maintained by Rapid7 for internal testing needs. Pull requests will continue to be reviewed and accepted, and new versions published as requested.
5
+
1
6
  # Nexpose-Client
2
- [![Gem Version](https://badge.fury.io/rb/nexpose.svg)](http://badge.fury.io/rb/nexpose) [![Build Status](https://travis-ci.org/rapid7/nexpose-client.svg?branch=master)](https://travis-ci.org/rapid7/nexpose-client) [![Test Coverage](https://codeclimate.com/github/rapid7/nexpose-client/badges/coverage.svg)](https://codeclimate.com/github/rapid7/nexpose-client) [![Inline docs](http://inch-ci.org/github/rapid7/nexpose-client.svg?branch=master)](http://inch-ci.org/github/rapid7/nexpose-client) [![Code Climate](https://codeclimate.com/github/rapid7/nexpose-client/badges/gpa.svg)](https://codeclimate.com/github/rapid7/nexpose-client)
7
+ [![No Maintenance Intended](http://unmaintained.tech/badge.svg)](http://unmaintained.tech/) [![Gem Version](https://badge.fury.io/rb/nexpose.svg)](http://badge.fury.io/rb/nexpose) [![Build Status](https://travis-ci.org/rapid7/nexpose-client.svg?branch=master)](https://travis-ci.org/rapid7/nexpose-client) [![Test Coverage](https://codeclimate.com/github/rapid7/nexpose-client/badges/coverage.svg)](https://codeclimate.com/github/rapid7/nexpose-client) [![Inline docs](http://inch-ci.org/github/rapid7/nexpose-client.svg?branch=master)](http://inch-ci.org/github/rapid7/nexpose-client) [![Code Climate](https://codeclimate.com/github/rapid7/nexpose-client/badges/gpa.svg)](https://codeclimate.com/github/rapid7/nexpose-client)
3
8
 
4
9
  This is the official gem package for the Ruby Nexpose API client library.
5
10
 
data/Rakefile CHANGED
@@ -12,4 +12,6 @@ GitHubChangelogGenerator::RakeTask.new :changelog do |config|
12
12
  if token.nil?
13
13
  warn "!!WARNING!! Missing Github Token Environment Variable. Fix before you run rake changelog. !!WARNING!!"
14
14
  end
15
+ config.user = 'rapid7'
16
+ config.project = 'nexpose-client'
15
17
  end
data/lib/eso.rb ADDED
@@ -0,0 +1,23 @@
1
+ require 'date'
2
+ require 'json'
3
+ require 'net/http'
4
+ require 'net/https'
5
+ require 'nexpose'
6
+ require 'time'
7
+ require 'uri'
8
+
9
+ require 'eso/conductor'
10
+ require 'eso/configuration/configuration'
11
+ require 'eso/configuration/configuration_manager'
12
+ require 'eso/filter'
13
+ require 'eso/integration_option'
14
+ require 'eso/integration_options_manager'
15
+ require 'eso/nexpose'
16
+ require 'eso/service'
17
+ require 'eso/step'
18
+ require 'eso/step_configuration'
19
+ require 'eso/workflow'
20
+
21
+ module Eso
22
+
23
+ end
@@ -0,0 +1,227 @@
1
+ require 'eso/service'
2
+
3
+ module Eso
4
+ class Conductor < Service
5
+
6
+ # Constructor for Conductor.
7
+ #
8
+ # @param [String] host Hostname or IP address where this conductor resides.
9
+ # @param [Integer] port The TCP port to connect to this conductor on.
10
+ # @param [Nexpose::Connection] nsc A logged-in Nexpose::Connection object with a valid session used to authenticate.
11
+ # @return [Eso::Conductor] The newly created conductor object
12
+ #
13
+ def initialize(host:, port: 3780, nsc:)
14
+ super(host: host, port: port, nsc: nsc)
15
+ @url = "https://#{@host}:#{@port}/eso/conductor-service/api/"
16
+ end
17
+
18
+ # Return all of the workflows that currently exist on this conductor.
19
+ #
20
+ # @return [Array] An array containing all of the current workflows on the conductor in Eso::Workflow object format. Returns an empty array if no workflows are present.
21
+ #
22
+ def workflows
23
+ rv = []
24
+ json_data = get(url: "#{@url}workflows/")
25
+ json_data.each do |wf|
26
+ workflow = Workflow.new(id: wf[:id], name: wf[:name])
27
+ steps = wf[:steps]
28
+ steps.each do |step|
29
+ workflow_step = Step.new(uuid: step[:uuid],
30
+ service_name: step[:serviceName],
31
+ workflow: workflow,
32
+ type_name: step[:stepConfiguration][:typeName],
33
+ previous_type_name: step[:stepConfiguration][:previousTypeName],
34
+ configuration_params: step[:stepConfiguration][:configurationParams])
35
+ workflow.steps << workflow_step
36
+ end
37
+ rv << workflow
38
+ end
39
+ rv
40
+ end
41
+
42
+ # Return the workflow histories with only the state histories for the given date range.
43
+ #
44
+ # @param [Fixnum] starttime The time in milliseconds since epoch for which you want the workflow histories
45
+ # @param [Fixnum] endtime The time in milliseconds since epoch for which you want the workflow histories
46
+ # @return [Array[Eso::Workflow::History]] An array containing all of the workflow histories from the
47
+ # Conductor, which has StateHistory objects containing startTime's within the specified time range. Only the
48
+ # StateHistories within that range are returned in the WorkflowHistory object. Returns an empty array if none are present.
49
+ def workflow_histories(starttime, endtime)
50
+ histories = []
51
+ json_data = get(url: "#{@url}workflows/history/#{starttime}/#{endtime}")
52
+ json_data.each do |wf|
53
+ # Initialize WorkflowHistory elements
54
+ workflow_steps = []
55
+ state_histories = []
56
+
57
+ # Create a new WorkflowHistory with the details we already know
58
+ workflow_history = Eso::Workflow::History.new(id: wf[:id],
59
+ name: wf[:name],
60
+ timeCreated: wf[:timeCreated],
61
+ state: wf[:state],
62
+ message: wf[:message],
63
+ steps: workflow_steps,
64
+ history: state_histories
65
+ )
66
+
67
+ # Parse the steps out of the response to be returned with the WorkflowHistory
68
+ wf[:steps].each do |step|
69
+ workflow_steps << Step.new(uuid: step[:uuid],
70
+ service_name: step[:serviceName],
71
+ workflow: workflow_history,
72
+ type_name: step[:stepConfiguration][:typeName],
73
+ previous_type_name: step[:stepConfiguration][:previousTypeName],
74
+ configuration_params: step[:stepConfiguration][:configurationParams])
75
+ end
76
+ workflow_history.steps = workflow_steps
77
+
78
+ # Parse the histories out of the response, to be returned with the WorkflowHistory. For some reason.
79
+ # this failed with named parameters. For now I returned it to positional.
80
+ wf[:history].each do |history|
81
+ state_histories << Eso::Workflow::StateHistory.new(history[:message],
82
+ history[:state],
83
+ history[:startTime])
84
+ end
85
+ workflow_history.state_histories = state_histories
86
+
87
+ # Add the Workflow History we just built to the list to be returned.
88
+ histories << workflow_history
89
+ end
90
+ histories
91
+ end
92
+
93
+ # Get the state of the specified workflow.
94
+ #
95
+ # @param [String] workflow_id The ID of the workflow to retrieve the state of.
96
+ # @return [String] The current state of the workflow.
97
+ #
98
+ def workflow_state(workflow_id:)
99
+ get(url: "#{@url}workflows/#{workflow_id}/state")
100
+ end
101
+
102
+ # Get the count of items in a state of the specified workflow.
103
+ #
104
+ # @param [Eso::Workflow::State] state The state of the workflows to retrieve the count of.
105
+ # @return [Integer] The number of workflows in the requested state.
106
+ #
107
+ def workflows_state_count(state)
108
+ get(url: "#{@url}workflows/count/#{state}")
109
+ end
110
+
111
+ # Retrieve the states for all of the workflows created on the conductor.
112
+ #
113
+ # @return [Hash] A hash containing the states of all existing workflows, keyed by workflow ID.
114
+ #
115
+ def workflow_states
116
+ wfs = workflows
117
+ states = {}
118
+ wfs.each { |wf| states[wf.id] = workflow_state(workflow_id: wf.id) }
119
+ states
120
+ end
121
+
122
+ # Create a new workflow on this conductor.
123
+ #
124
+ # @param [String] name The user-facing name the workflow will be created with.
125
+ # @param [Array] steps An array containing each of the steps that the workflow will be created with, in Eso::Step format.
126
+ # @return [Eso::Workflow] The newly created workflow object
127
+ #
128
+ def create_workflow(name:, steps:)
129
+ workflow = Workflow.new(name: name, steps: steps)
130
+
131
+ resp = post(url: "#{@url}workflows/", payload: workflow.to_json)
132
+ created_workflow = Workflow.load(self, resp[:id])
133
+
134
+ created_workflow
135
+ end
136
+
137
+ # Update an existing workflow on the conductor to have the configuration of the workflow object passed into this method.
138
+ #
139
+ # @param [Eso::Workflow] updated_workflow A workflow object that has already had all required changes made to it. This workflow must have an ID set.
140
+ #
141
+ def update_workflow(updated_workflow:)
142
+ payload = updated_workflow.to_json
143
+ put(url: "#{@url}workflows/#{updated_workflow.id}", payload: payload)
144
+ end
145
+
146
+ # Delete an existing workflow from the conductor.
147
+ #
148
+ # @param [String] workflow_id The ID of the workflow to be deleted.
149
+ #
150
+ def delete_workflow(workflow_id:)
151
+ delete(url: "#{@url}workflows/#{workflow_id}")
152
+ end
153
+
154
+ # Delete all current workflows on the conductor.
155
+ #
156
+ def delete_all_workflows
157
+ wfs = workflows
158
+ wfs.each { |wf| delete_workflow(workflow_id: wf.id) }
159
+ end
160
+
161
+ # Start the specified workflow.
162
+ #
163
+ # @param [String] workflow_id The ID of the workflow to be started.
164
+ #
165
+ def start_workflow(workflow_id:)
166
+ post(url: "#{@url}workflows/#{workflow_id}/state")
167
+ end
168
+
169
+ # Stop the specified workflow.
170
+ #
171
+ # @param [String] workflow_id The ID of the workflow to be stopped.
172
+ #
173
+ def stop_workflow(workflow_id:)
174
+ delete(url: "#{@url}workflows/#{workflow_id}/state")
175
+ end
176
+
177
+ # Start all workflows that exist on the conductor.
178
+ #
179
+ # @return [Hash] A hash containing the states of all existing workflows, keyed by workflow ID.
180
+ #
181
+ def start_all_workflows
182
+ wf_states = workflow_states
183
+
184
+ wf_states.each { |wf_id, state| start_workflow(workflow_id: wf_id) if state[:workflow_state] == "STOPPED" }
185
+ workflow_states
186
+ end
187
+
188
+ # Stop all workflows that exist on the conductor.
189
+ #
190
+ # @return [Hash] A hash containing the states of all existing workflows, keyed by workflow ID.
191
+ #
192
+ def stop_all_workflows
193
+ wf_states = workflow_states
194
+
195
+ wf_states.each { |wf_id, state| stop_workflow(workflow_id: wf_id) if state[:workflow_state] == "RUNNING" }
196
+ workflow_states
197
+ end
198
+
199
+ # Returns the translated value of the specified key for a step type (defined in Eso::StepNames).
200
+ # The translated value will be based on the language settings the user has configured.
201
+ #
202
+ # @param [String] step_type The step type to query metadata for. Valid values defined in Eso::StepNames
203
+ # @param [String] key The key value in the metadata that maps to the desired label.
204
+ # @return [String] The translated value of the key.
205
+ #
206
+ def get_translation_label(step_type, key)
207
+ json_data = get(url: "#{@url}services/nexpose/metadata/#{step_type}")
208
+
209
+ target_hash = json_data[:labels].values.find { |label_hash| label_hash.has_key?(key) }
210
+ target_hash[key] if target_hash
211
+ end
212
+
213
+ # Returns the metadata key for a specified translated string.
214
+ # The translated value needs to be in language that the user has configured.
215
+ #
216
+ # @param [String] step_type The step type to query metadata for. Valid values defined in Eso::StepNames
217
+ # @param [String] label The translated value of which you are requesting the key for.
218
+ # @return [String] The metadata key corresponding to this label.
219
+ #
220
+ def get_translation_key(step_type, label)
221
+ json_data = get(url: "#{@url}services/nexpose/metadata/#{step_type}")
222
+
223
+ target_hash = json_data[:labels].values.find { |label_hash| label_hash.values.include?(label) }
224
+ target_hash.key(label).to_s if target_hash
225
+ end
226
+ end
227
+ end
@@ -0,0 +1,124 @@
1
+ module Eso
2
+ # This class represents the Configuration that is sent to the server for new
3
+ # style Discovery Connections.
4
+ class Configuration
5
+ attr_accessor :service_name, :config_name, :config_id, :properties
6
+
7
+ def initialize(service_name:, config_name:, properties:[], config_id:)
8
+ @service_name = service_name
9
+ @config_name = config_name
10
+ @properties = properties
11
+ @config_id = config_id
12
+ end
13
+
14
+ # Convert the Configuration to a JSON string for sending to Nexpose
15
+ #
16
+ # @return [String] A JSON String representation of the Configuration
17
+ def to_json
18
+ self.to_hash.to_json
19
+ end
20
+
21
+ # Convert the Configuration to a Hash
22
+ #
23
+ # @return [Hash] A Hash representation of the Configuration
24
+ def to_hash
25
+ hash = {:configId => @config_id,
26
+ :serviceName => @service_name,
27
+ :configName => @config_name,
28
+ :configurationAttributes => {:valueClass => Eso::Values::OBJECT,
29
+ :objectType => 'service_configuration',
30
+ :properties => []}}
31
+ properties.each {|prop| hash[:configurationAttributes][:properties] << prop.to_hash}
32
+ end
33
+
34
+ # Retrieve a Configuration attribute property value given the name of the property
35
+ #
36
+ # @param [String] name The name of the property to retrieve
37
+ # @return [String] The value of the property
38
+ def property(name)
39
+ prop = properties.find{|attr| attr.property == name}
40
+ prop.value unless prop.nil?
41
+ end
42
+
43
+ # Update a Configuration attribute property value given the name of the property
44
+ #
45
+ # @param [String] name The name of the property to update
46
+ # @param [String] value The value of the property to update
47
+ # @return [String] The value of the property
48
+ def update_property(name, value)
49
+ properties.find{|attr| attr.property == name}.value = value
50
+ end
51
+
52
+ # Delete a Configuration attribute property value given the name of the property
53
+ #
54
+ # @param [String] name The name of the property to update
55
+ def delete_property(name)
56
+ properties.delete_if{|attr| attr.property == name}
57
+ end
58
+
59
+ # Load a Configuration object from a Hash
60
+ #
61
+ # @param [Hash] hash The Hash containing the Configuration object
62
+ # @return [Configuration] The Configuration object which was in the Hash
63
+ def self.load(hash)
64
+ configuration = self.new(service_name: hash[:serviceName],
65
+ config_name: hash[:configName],
66
+ config_id: hash[:configID])
67
+ hash[:configurationAttributes][:properties].each do |prop|
68
+ configuration.properties << ConfigurationAttribute.load(prop)
69
+ end
70
+ configuration
71
+ end
72
+ end
73
+
74
+ # The ConfigurationAttribute is a property of the Configuration
75
+ class ConfigurationAttribute
76
+ attr_accessor :property, :value_class, :value
77
+
78
+ def initialize(property, value_class, value)
79
+ @property = property
80
+ @value_class = value_class
81
+ @value = value
82
+ end
83
+
84
+ # Convert the ConfigurationAttribute to a JSON string for sending to Nexpose
85
+ #
86
+ # @return [String] A JSON String representation of the ConfigurationAttribute
87
+ def to_json
88
+ self.to_hash.to_json
89
+ end
90
+
91
+ # Convert the ConfigurationAttribute to a Hash
92
+ #
93
+ # @return [Hash] A Hash representation of the ConfigurationAttribute
94
+ def to_hash
95
+ prop = @property.to_sym
96
+ hash = {prop => {}}
97
+ hash[prop]['valueClass'] = @value_class
98
+ if @value_class == Eso::Values::ARRAY
99
+ items = []
100
+ @value.each{|v| items<< {'value' => v, 'valueClass' => Eso::Values::STRING}}
101
+ hash[prop]['items'] = items
102
+ else
103
+ hash[prop]['value'] = @value
104
+ end
105
+ hash
106
+ end
107
+
108
+ # Load a ConfigurationAttribute object from an Array
109
+ #
110
+ # @param [Array] array The Array containing the ConfigurationAttribute object
111
+ # @return [ConfigurationAttribute] The ConfigurationAttribute object which was in the Array
112
+ def self.load(array)
113
+ property = array.first
114
+ value_class = array.last['valueClass']
115
+ value =
116
+ if value_class == Eso::Values::ARRAY
117
+ array.last['items'].map{|item| item['value']}
118
+ else
119
+ array.last['value']
120
+ end
121
+ self.new(property, value_class, value)
122
+ end
123
+ end
124
+ end