controls 1.0.0.beta.2

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fadb777b4fbaf3fdc6b52f08d20b285c8f1904fb
4
+ data.tar.gz: c7589578857e422ebc60be2dcd6ef9b9e42bcb6e
5
+ SHA512:
6
+ metadata.gz: ee1c3c4c8a44dd97310f9616752ff416204055d21cf40984605be06534f9fdf853cca8a8374f606322372b408005dd06bbc4c71288cfece0e8667e06a6204b3d
7
+ data.tar.gz: 15212139246d997a70435bb8d7fd5c8cde611b4bab1c7b778a9971fd899dfb8cac37f1ee69637e9b5c07d2713a98d6eeec7d5b3ad52eba8a2b0f6c03ca087035
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1 @@
1
+ - LICENSE.md
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in controls.gemspec
4
+ gemspec
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 Erran Carey
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,78 @@
1
+ # ![controls insight](https://raw.github.com/ipwnstuff/controls.rb/master/docs/images/controlsinsight.png "controlsinsight") client gem
2
+ The **controls**insight (controls) gem interfaces with [Rapid7's **controls**insight API](http://docs.controlsinsight.apiary.io).
3
+
4
+ ## Installation
5
+ Add this line to your application's Gemfile:
6
+
7
+ gem 'controls'
8
+
9
+ And then execute:
10
+
11
+ $ bundle
12
+
13
+ Or install it yourself as:
14
+
15
+ $ gem install controls
16
+
17
+ ## Documentation
18
+ * [Apiary API documentation](http://docs.controlsinsight.apiary.io)
19
+ * [YARD documentation for the Ruby client](http://www.rubydoc.info/github/ipwnstuff/controls.rb)
20
+
21
+ ## Basic Resources
22
+ ### Authentication
23
+ ```ruby
24
+ Controls.web_endpoint = 'https://nexpose.local:3780/insight/controls'
25
+ Controls.api_endpoint = "#{Controls.web_endpoint}/api/1.0"
26
+
27
+ # If your endpoint uses a self-signed cert. turn off SSL cert. verification
28
+ Controls.verify_ssl = false
29
+
30
+ Controls.login :user => 'admin', :password => 'password'
31
+
32
+ Controls.client.methods
33
+ ```
34
+
35
+ ### Assessments
36
+ ```ruby
37
+ # Retrieve all the assessments that have been ran
38
+ Controls.assessments
39
+ # => TODO: Add example output
40
+
41
+ # Only retrieve a single assessment
42
+ Controls.assessments(1)
43
+ # => TODO: Add example output
44
+ ```
45
+
46
+
47
+ ### Assets
48
+ ```ruby
49
+ # Retrieve a list of all the assets that Controls has access to
50
+ Controls.assets
51
+ # => TODO: Add example output
52
+
53
+ # Only retrieve a single assessment
54
+ Controls.assets('your-asset-uuid-here')
55
+ # => TODO: Add example output
56
+ ```
57
+
58
+ ### Guidance
59
+ ```ruby
60
+ # Only retrieve a single guidance by name
61
+ Controls.guidance('your-guidance-name-here')
62
+ # => TODO: Add example output
63
+ ```
64
+
65
+ ### Threats
66
+ ```ruby
67
+ # Retrieve a list of all the threats
68
+ Controls.threats
69
+ # => TODO: Add example output
70
+
71
+ # Only retrieve a single threat
72
+ Controls.threats('threat-name-here')
73
+ # => TODO: Add example output
74
+ ```
75
+
76
+
77
+ ## License
78
+ This project was created by [Erran Carey (@ipwnstuff)](http://ipwnstuff.github.io) and licensed under [the MIT License](LICENSE.md).
@@ -0,0 +1,3 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ task :default => :install
@@ -0,0 +1,30 @@
1
+ # encoding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'controls/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'controls'
8
+ spec.version = Controls::VERSION
9
+ spec.authors = ['Erran Carey']
10
+ spec.email = %w['me@errancarey.com']
11
+ spec.description = %q{This gem interfaces to Rapid7's **controls**insight API.}
12
+ spec.summary = %q{This gem interfaces to Rapid7's **controls**insight API.}
13
+ spec.homepage = ''
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = %w[lib]
20
+
21
+ spec.add_dependency 'activesupport'
22
+ spec.add_dependency 'faraday'
23
+ spec.add_dependency 'nokogiri'
24
+
25
+ spec.add_development_dependency 'bundler', '~> 1.3'
26
+ spec.add_development_dependency 'netrc'
27
+ spec.add_development_dependency 'rake'
28
+ spec.add_development_dependency 'vcr'
29
+ spec.add_development_dependency 'yard'
30
+ end
@@ -0,0 +1,42 @@
1
+ require 'controls/client'
2
+ require 'controls/default'
3
+
4
+ # A Ruby client for the **controls**insight API
5
+ module Controls
6
+ class << self
7
+ include Controls::Configurable
8
+
9
+ # A {Client} object that includes {Configurable}
10
+ #
11
+ # @return [Client] the current {Client} object or a newly initialized
12
+ # {Client} object
13
+ def client
14
+ unless defined?(@client) && @client.same_options?(options)
15
+ @client = Controls::Client.new(options)
16
+ end
17
+
18
+ @client
19
+ end
20
+
21
+ # @yield [client]
22
+ def configure
23
+ yield client
24
+ end
25
+
26
+ def respond_to_missing?(method_name, include_private = false)
27
+ client.respond_to?(method_name, include_private)
28
+ end
29
+
30
+ private
31
+
32
+ def method_missing(method_name, *args, &block)
33
+ if client.respond_to?(method_name)
34
+ client.send(method_name, *args, &block)
35
+ else
36
+ super
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ Controls.setup
@@ -0,0 +1,46 @@
1
+ module Controls
2
+ # A module that holds authentication methods for {Controls::Client}
3
+ module Authentication
4
+ # @return [Boolean] whether the user is authenticated using Basic Auth
5
+ def basic_authenticated?
6
+ @username && @password
7
+ end
8
+
9
+ # @note the following aliases should become real methods once new methods
10
+ # of authentication become available such as token/OAuth authentication
11
+ alias_method :authenticated?, :basic_authenticated?
12
+ alias_method :user_authenticated?, :basic_authenticated?
13
+
14
+ # @note this method should be updated if new methods for authentication
15
+ # become available.
16
+ def login(username, password)
17
+ middleware.basic_auth(username, password)
18
+ end
19
+
20
+ # @return [Boolean] whether the netrc file was successfully loaded, otherwise
21
+ # returns false and prints an error to STDERR
22
+ def login_from_netrc
23
+ return false unless netrc?
24
+
25
+ require 'netrc'
26
+ host = URI.parse(api_endpoint).host
27
+ creds = Netrc.read(File.expand_path(netrc_file))[host]
28
+
29
+ if creds
30
+ self.username = creds.shift
31
+ self.password = creds.shift
32
+
33
+ middleware.basic_auth(username, password)
34
+ else
35
+ warn "No credentials found for '#{host}' in '#{netrc_file}'."
36
+
37
+ false
38
+ end
39
+ rescue LoadError
40
+ warn 'You must install the netrc gem to login via netrc.',
41
+ 'Retry after running `gem install netrc`.'
42
+
43
+ false
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,151 @@
1
+ require 'faraday'
2
+ require 'json'
3
+ require 'nokogiri'
4
+ require 'controls/authentication'
5
+ require 'controls/configurable'
6
+ require 'controls/client/assessments'
7
+ require 'controls/client/assets'
8
+ require 'controls/client/guidance'
9
+ require 'controls/client/security_controls'
10
+ require 'controls/client/threats'
11
+ require 'controls/response'
12
+
13
+ module Controls
14
+ # A class that handles interactions with the **controls**insight API
15
+ class Client
16
+ include Controls::Authentication
17
+ include Controls::Configurable
18
+ include Controls::Client::Assessments
19
+ include Controls::Client::Assets
20
+ include Controls::Client::Guidance
21
+ include Controls::Client::SecurityControls
22
+ include Controls::Client::Threats
23
+
24
+ SSL_WARNING = ["The API endpoint used a self-signed or invalid SSL certificate.",
25
+ "To allow this connection temporarily use `Controls.verify_ssl = false`.",
26
+ "See the Controls.rb wiki on GitHub for more information on SSL verification."]
27
+
28
+ # Creates a new {Controls::Client} object
29
+ #
30
+ # @param [Hash] options the options to use when adding keys from
31
+ # {Controls::Configurable}
32
+ def initialize(options = {})
33
+ Controls::Configurable.keys.each do |key|
34
+ value = options[key].nil? ? Controls.instance_variable_get(:"@#{key}") : options[key]
35
+ instance_variable_set(:"@#{key}", value)
36
+ end
37
+
38
+ if options[:verify_ssl].nil?
39
+ middleware.ssl[:verify] = if ENV['CONTROLS_VERIFY_SSL'].nil?
40
+ true
41
+ else
42
+ !(ENV['CONTROLS_VERIFY_SSL'] =~ /false/)
43
+ end
44
+ else
45
+ middleware.ssl[:verify] = !!options[:verify_ssl]
46
+ end
47
+
48
+ login_from_netrc unless authenticated?
49
+
50
+ if basic_authenticated?
51
+ middleware.basic_auth(@username, @password)
52
+ end
53
+ end
54
+
55
+ def verify_ssl
56
+ middleware.ssl[:verify].nil? || !!middleware.ssl[:verify]
57
+ end
58
+
59
+ def verify_ssl=(verify)
60
+ middleware.ssl[:verify] = !!verify
61
+ end
62
+
63
+ # Censors the password from the output of {#inspect}
64
+ #
65
+ # @return [String] the censored data
66
+ def inspect
67
+ raw = super
68
+ raw.sub!(/(@password=")#{@password}(")/, "\\1*********\\2") if @password
69
+
70
+ raw
71
+ end
72
+
73
+ # A wrapper for GET requests
74
+ #
75
+ # @return [Array,Hash] an array or hash of parsed JSON data
76
+ def get(path, params = {}, headers = {})
77
+ headers = connection_options[:headers].merge(headers)
78
+ url = URI.escape(File.join(api_endpoint, path))
79
+ resp = middleware.get(url, params, headers)
80
+
81
+ Response.generate_ruby(resp.body)
82
+ rescue Faraday::Error::ConnectionFailed => e
83
+ if e.message =~ /^SSL_connect/
84
+ warn(*SSL_WARNING)
85
+ else
86
+ raise e
87
+ end
88
+ end
89
+
90
+
91
+ # A wrapper for GET requests
92
+ #
93
+ # @return [Array,Hash] an array or hash of parsed JSON data
94
+ def web_get(path, params = {}, headers = {})
95
+ headers = connection_options[:headers].merge(headers)
96
+ url = URI.escape(File.join(web_endpoint, path))
97
+ resp = middleware.get(url, params, headers)
98
+
99
+ # Response.parse(resp.body)
100
+ Response.generate_ruby(resp.body)
101
+ rescue Faraday::Error::ConnectionFailed => e
102
+ if e.message =~ /^SSL_connect/
103
+ warn(*SSL_WARNING)
104
+ else
105
+ raise e
106
+ end
107
+ end
108
+
109
+ def api_methods
110
+ mods = Controls::Client.included_modules.map do |mod|
111
+ if mod.to_s =~ /^Controls::Client::/
112
+ mod
113
+ end
114
+ end
115
+
116
+ mods.compact.map { |mod| mod.instance_methods(false) }.flatten.sort
117
+ end
118
+
119
+ def references(version = '1.0')
120
+ version = '1.0' unless version =~ /\d.\d/
121
+
122
+ web_get "/api/#{version}"
123
+
124
+ @references = Hash[Response.generate_ruby(resp.body).sort]
125
+ rescue Faraday::Error::ConnectionFailed => e
126
+ if e.message =~ /^SSL_connect/
127
+ warn(*SSL_WARNING)
128
+ else
129
+ raise e
130
+ end
131
+ end
132
+
133
+ def same_options?(opts)
134
+ opts.hash.eql? options.hash
135
+ end
136
+
137
+ %w[assessment asset configuration
138
+ guidance security_control threat
139
+ threat_vector].each do |predicate_method|
140
+ pluralized_method = predicate_method.eql?('guidance') ? 'guidance' : "#{predicate_method}s"
141
+
142
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
143
+ def #{predicate_method}?(*args) # def assessment?(*args)
144
+ send(:#{pluralized_method}, *args) # assessments(*args)
145
+ rescue Controls::NotFound => e # rescue Controls::NotFound => e
146
+ false # false
147
+ end # end
148
+ RUBY
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,17 @@
1
+ module Controls
2
+ class Client
3
+ # A module to encapsulate API methods related to assessments
4
+ # @since API v1.0
5
+ # @version v1.0.0
6
+ module Assessments
7
+ # @return [Array<Hash>] an array of assessment hashes
8
+ def assessments(assessment_id = nil)
9
+ if assessment_id
10
+ get "/assessments/#{assessment_id}"
11
+ else
12
+ get '/assessments'
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,64 @@
1
+ module Controls
2
+ class Client
3
+ # A module to encapsulate API methods related to assets
4
+ # @since API v1.0
5
+ # @version v1.0.0
6
+ # TODO: Update docs
7
+ module Assets
8
+ # @note since the uuid is an optional param it has been added to the
9
+ # params options hash
10
+ # @raise [Controls::NotFound] if the uuid didn't match any assets
11
+ # @return [Hash] a hash representing the matching asset
12
+ def assets(params = {})
13
+ if params.is_a? Hash
14
+ uuid = params.delete(:uuid)
15
+ else
16
+ uuid = params
17
+ params = {}
18
+ end
19
+
20
+ if uuid
21
+ get "/assets/#{uuid}", params
22
+ else
23
+ get '/assets', params
24
+ end
25
+ end
26
+
27
+ # @param [String] guidance the guidance name to search by
28
+ # @return [Array<Hash>] an array of hashes that represent assets
29
+ def applicable_assets(guidance, params = {})
30
+ get "/guidance/#{guidance}/applicable_assets", params
31
+ end
32
+ alias_method :assets_by_guidance, :applicable_assets
33
+
34
+ # @param [String] configuration the name of the configuration to search by
35
+ # @return [Array<Hash>] an array of hashes that represent assets
36
+ def misconfigured_assets(configuration, params = {})
37
+ get "/configurations/#{configuration}/uncovered_assets", params
38
+ end
39
+ alias_method :assets_by_configuration, :misconfigured_assets
40
+
41
+ # @param [String] threat the threat name to search by
42
+ # @return [Array<Hash>] an array of hashes that represent assets
43
+ def threat_assets(threat, params = {})
44
+ get "/threats/#{threat}/assets", params
45
+ end
46
+ alias_method :assets_by_threat, :threat_assets
47
+
48
+ # @param [String] security_control the name of the security control to
49
+ # search by
50
+ # @return [Array<Hash>] an array of hashes that represent assets
51
+ def uncovered_assets(security_control, params = {})
52
+ get "/security_controls/#{security_control}/uncovered_assets", params
53
+ end
54
+ alias_method :assets_by_security_control, :uncovered_assets
55
+
56
+ # @param [String] threat_vector the threat vectory to search by
57
+ # @return [Array<Hash>] an array of hashes that represent assets
58
+ def undefended_assets(threat_vector, params = {})
59
+ get "/threat_vectors/#{threat_vector}/undefended_assets", params
60
+ end
61
+ alias_method :assets_by_threat_vector, :undefended_assets
62
+ end
63
+ end
64
+ end