controls 1.0.0.beta.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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