sonar-client 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9e04adb9fca3c12bc9ae907eaa64df648c568ca6
4
+ data.tar.gz: f363d98148215460920438b61f5c0f025224bbdb
5
+ SHA512:
6
+ metadata.gz: d80394c4745caba23f50df647c3b83ecfa0dccbbc76afe1986021f7f904e14bdf1d4a3c11b86ecee306e9ddc6247ff5b70aa3d48fad4213aea3aacaf5646c44a
7
+ data.tar.gz: 40263868a73f583575e963a2573a82a39b8e3c00ce0f37ba37d5f4c7ddda6c0d67fb17c98ecdb6dda8bc68fcdf71c10915fbcfd56945545496a9a71b1ab5ed58
data/.gitignore ADDED
@@ -0,0 +1,35 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ lib/bundler/man
7
+ pkg
8
+ rdoc
9
+ spec/reports
10
+ spec/cassette
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+
15
+ # OSX
16
+ .DS_Store
17
+
18
+ # YARD artifacts
19
+ .yardoc
20
+ _yardoc
21
+ doc/
22
+
23
+ # Specific to RubyMotion
24
+ .dat*
25
+ .repl_history
26
+ build/
27
+
28
+ # for a library or gem, you might want to ignore these files since the code is
29
+ # intended to run in multiple environments; otherwise, check them in:
30
+ # Gemfile.lock
31
+ .ruby-version
32
+ .ruby-gemset
33
+
34
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
35
+ .rvmrc
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format d
data/.rubocop.yml ADDED
@@ -0,0 +1,7 @@
1
+ Style/StringLiterals:
2
+ Enabled: false
3
+ Description: 'Single quotes are preferred when string interpolation is not needed.'
4
+
5
+ Metrics/LineLength:
6
+ Enabled: true
7
+ Max: 120
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sonar-client.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,94 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ sonar-client (0.0.1)
5
+ awesome_print
6
+ faraday_middleware (~> 0.9.0)
7
+ hashie (~> 2.0.3)
8
+ multi_json
9
+ thor
10
+
11
+ GEM
12
+ remote: https://rubygems.org/
13
+ specs:
14
+ activesupport (4.2.0)
15
+ i18n (~> 0.7)
16
+ json (~> 1.7, >= 1.7.7)
17
+ minitest (~> 5.1)
18
+ thread_safe (~> 0.3, >= 0.3.4)
19
+ tzinfo (~> 1.1)
20
+ addressable (2.3.6)
21
+ api_matchers (0.5.1)
22
+ activesupport (>= 3.2.5)
23
+ nokogiri (>= 1.5.2)
24
+ rspec (>= 2.14)
25
+ awesome_print (1.6.1)
26
+ crack (0.4.2)
27
+ safe_yaml (~> 1.0.0)
28
+ diff-lcs (1.2.5)
29
+ docile (1.1.5)
30
+ faraday (0.9.1)
31
+ multipart-post (>= 1.2, < 3)
32
+ faraday_middleware (0.9.1)
33
+ faraday (>= 0.7.4, < 0.10)
34
+ hashie (2.0.5)
35
+ i18n (0.7.0)
36
+ json (1.8.2)
37
+ mini_portile (0.6.2)
38
+ minitest (5.5.1)
39
+ multi_json (1.10.1)
40
+ multipart-post (2.0.0)
41
+ nokogiri (1.6.5)
42
+ mini_portile (~> 0.6.0)
43
+ rake (10.4.2)
44
+ rspec (3.1.0)
45
+ rspec-core (~> 3.1.0)
46
+ rspec-expectations (~> 3.1.0)
47
+ rspec-mocks (~> 3.1.0)
48
+ rspec-core (3.1.7)
49
+ rspec-support (~> 3.1.0)
50
+ rspec-expectations (3.1.2)
51
+ diff-lcs (>= 1.2.0, < 2.0)
52
+ rspec-support (~> 3.1.0)
53
+ rspec-mocks (3.1.3)
54
+ rspec-support (~> 3.1.0)
55
+ rspec-support (3.1.2)
56
+ safe_yaml (1.0.4)
57
+ shoulda (3.5.0)
58
+ shoulda-context (~> 1.0, >= 1.0.1)
59
+ shoulda-matchers (>= 1.4.1, < 3.0)
60
+ shoulda-context (1.2.1)
61
+ shoulda-matchers (2.7.0)
62
+ activesupport (>= 3.0.0)
63
+ simplecov (0.9.1)
64
+ docile (~> 1.1.0)
65
+ multi_json (~> 1.0)
66
+ simplecov-html (~> 0.8.0)
67
+ simplecov-html (0.8.0)
68
+ simplecov-rcov (0.2.3)
69
+ simplecov (>= 0.4.1)
70
+ thor (0.19.1)
71
+ thread_safe (0.3.4)
72
+ tzinfo (1.2.2)
73
+ thread_safe (~> 0.1)
74
+ vcr (2.8.0)
75
+ webmock (1.8.11)
76
+ addressable (>= 2.2.7)
77
+ crack (>= 0.1.7)
78
+ yard (0.8.7.6)
79
+
80
+ PLATFORMS
81
+ ruby
82
+
83
+ DEPENDENCIES
84
+ api_matchers
85
+ bundler
86
+ rake
87
+ rspec
88
+ shoulda
89
+ simplecov
90
+ simplecov-rcov
91
+ sonar-client!
92
+ vcr (~> 2.8.0)
93
+ webmock (~> 1.8.0)
94
+ yard
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Rapid7, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, 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,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,81 @@
1
+ sonar-client
2
+ ===============
3
+
4
+ Ruby API Wrapper and CLI for [Sonar](https://sonar.labs.rapid7.com)
5
+
6
+ ## Installation
7
+
8
+ Install the gem by running
9
+
10
+ gem install sonar-client
11
+
12
+ or add this line to your application's Gemfile:
13
+
14
+ gem 'sonar-client'
15
+
16
+ And then execute:
17
+
18
+ $ bundle install
19
+
20
+ ## Gem usage
21
+
22
+ ```ruby
23
+ require 'sonar'
24
+
25
+ # If you're using Rails 3+, create an initializer
26
+ # config/initializers/sonar.rb
27
+ Sonar.configure do |config|
28
+ config.api_url = 'https://sonar.labs.rapid7.com'
29
+ config.api_version = 'v2'
30
+ config.email = 'email@example.com'
31
+ config.access_token = 'YOURTOKEN'
32
+ end
33
+
34
+ # If you're using straight ruby (no Rails),
35
+ # create a Sonar::Client Object
36
+ options = {
37
+ api_url: 'https://sonar.labs.rapid7.com',
38
+ api_version: 'v2',
39
+ access_token: 'YOURTOKEN',
40
+ email: 'email@example.com'
41
+ }
42
+ client = Sonar::Client.new(options)
43
+
44
+ # Create a Client Object expecting you have an initializer in place
45
+ # Sonar::Client Object
46
+ client = Sonar::Client.new
47
+
48
+ # Get fdns
49
+ client.search(fdns: 'rapid7.com')
50
+ # => responds with a Hashie object
51
+ ```
52
+
53
+ ## CLI dev setup
54
+
55
+ From the project root directory
56
+ ```
57
+ $ rake install
58
+ sonar-client 0.0.1 built to pkg/sonar-client-0.0.1.gem.
59
+ sonar-client (0.0.1) installed.
60
+ $ sonar
61
+ ```
62
+
63
+ On the first run, sonar will setup a sonar.rc config file in your user folder. Run `sonar config` to view the full path to your config file. Here's what your file will look like when it's first created:
64
+ ```
65
+ email: YOUR_EMAIL
66
+ access_token: SONAR_TOKEN
67
+ api_url: https://sonar.labs.rapid7.com
68
+ ```
69
+ Replace YOUR_EMAIL with the email you used to register on the [Sonar website](https://sonar.labs.rapid7.com). Replace SONAR_TOKEN with your API token found on the [Settings page](https://sonar.labs.rapid7.com/users/edit) of the Sonar website. Enclosing quotes around these two credentials are not needed.
70
+
71
+ ## CLI usage
72
+
73
+ Typing `sonar help` will list all the available commands. You can type `sonar help TASK` to get help for a specific command. If running locally from the root project directory, you may need to prefix `sonar` commands with `bundle exec`. A rdns search command might look like `bundle exec sonar search rdns .rapid7.com`.
74
+
75
+ ## Contributing
76
+
77
+ 1. Fork it
78
+ 2. Create your feature branch (`git checkout -b feature/my-new-feature`)
79
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
80
+ 4. Push to the branch (`git push origin my-new-feature`)
81
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require "rspec/core/rake_task"
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task default: :spec
data/bin/sonar ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")))
4
+ require 'bundler/setup'
5
+ require 'sonar'
6
+
7
+ begin
8
+ Sonar::CLI.start(ARGV)
9
+ rescue Interrupt
10
+ puts 'Quitting...'
11
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+
3
+ module Sonar
4
+ module Certificate
5
+ ##
6
+ # Get certificate based on sha1 id
7
+ # /api/v2/certificates/1e80c24b97c928bb1db7d4d3c05475a6a40a1186
8
+ #
9
+ # @return [Hashie::Mash] with response of certificate
10
+ def get_certificate(options = {})
11
+ response = get("/api/#{Sonar.api_version}/certificates/#{options[:sha1]}", options)
12
+ response if response
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,46 @@
1
+ # encoding: utf-8
2
+
3
+ require 'thor'
4
+ require 'sonar/cli/rcfile'
5
+ require 'awesome_print'
6
+
7
+ module Sonar
8
+ class CLI < Thor
9
+ class_option 'profile', aliases: '-P', type: :string, default: File.join(File.expand_path('~'), Sonar::RCFile::FILENAME),
10
+ desc: 'Path to Sonar RC file', banner: 'FILE'
11
+
12
+ def initialize(*)
13
+ @rcfile = Sonar::RCFile.instance.load_file
14
+ @client = Sonar::Client.new(email: @rcfile["email"], access_token: @rcfile["access_token"], api_url: @rcfile["api_url"])
15
+ super
16
+ end
17
+
18
+ desc 'profile', 'Display the current profile from sonar.rc'
19
+ def profile
20
+ ap @rcfile
21
+ end
22
+
23
+ desc 'usage', 'Display API usage for current user.'
24
+ def usage
25
+ ap @client.usage
26
+ end
27
+
28
+ desc 'search [QUERY TYPE] [QUERY TERM]', 'Search anything from Sonar.'
29
+ def search(type, term)
30
+ @query = {}
31
+ @query[type.to_sym] = term
32
+ ap @client.search(@query)
33
+ end
34
+
35
+ desc 'types', 'List all Sonar query types.'
36
+ def types
37
+ ap Search::QUERY_TYPES_MAP
38
+ end
39
+
40
+ desc 'config', 'Update Sonar config file'
41
+ def config
42
+ # TODO: add a way to set config
43
+ puts "Your config file is located at #{RCFile.instance.path}"
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,31 @@
1
+ # encoding: utf-8
2
+ require 'singleton'
3
+ require 'yaml'
4
+
5
+ module Sonar
6
+ class RCFile
7
+ include Singleton
8
+
9
+ attr_accessor :path
10
+ FILENAME = 'sonar.rc'
11
+
12
+ def initialize
13
+ @path = File.join(File.expand_path('~'), FILENAME)
14
+ @data = load_file
15
+ end
16
+
17
+ def create_file
18
+ File.open(@path, 'w') do |f|
19
+ f.puts 'email: YOUR_EMAIL'
20
+ f.puts 'access_token: SONAR_TOKEN'
21
+ f.puts 'api_url: https://sonar.labs.rapid7.com'
22
+ end
23
+ puts "Config file setup at: #{@path}"
24
+ end
25
+
26
+ def load_file
27
+ create_file unless File.exist?(@path)
28
+ YAML.load_file(@path)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,83 @@
1
+ # encoding: utf-8
2
+ require 'forwardable'
3
+ require 'sonar/request'
4
+ require 'sonar/certificate'
5
+ require 'sonar/search'
6
+ require 'sonar/user'
7
+ require 'sonar/cli/cli'
8
+
9
+ module Sonar
10
+ class Client
11
+ extend Forwardable
12
+
13
+ include Request
14
+ include Certificate
15
+ include Search
16
+ include User
17
+
18
+ attr_accessor :api_url, :api_version, :access_token, :email
19
+
20
+ ##
21
+ # Create a new Sonar::Client object
22
+ #
23
+ # @params options[Hash]
24
+ def initialize(options = {})
25
+ @api_url = options[:api_url] || Sonar.api_url || "https://sonar.labs.rapid7.com"
26
+ @api_version = options[:api_version] || Sonar.api_version || "v2"
27
+ @access_token = options[:access_token] || Sonar.access_token
28
+ @email = options[:email] || Sonar.email
29
+ end
30
+
31
+ ##
32
+ # Create a Faraday::Connection object
33
+ #
34
+ # @return [Faraday::Connection]
35
+ def connection
36
+ params = {}
37
+ @conn = Faraday.new(url: api_url, params: params, headers: default_headers, ssl: { verify: true }) do |faraday|
38
+ faraday.use FaradayMiddleware::Mashify
39
+ faraday.use FaradayMiddleware::ParseJson, content_type: /\bjson$/
40
+ faraday.use FaradayMiddleware::FollowRedirects
41
+ faraday.adapter Faraday.default_adapter
42
+ end
43
+ @conn.headers['X-Sonar-Token'] = access_token
44
+ @conn.headers['X-Sonar-Email'] = email
45
+ @conn
46
+ end
47
+
48
+ ##
49
+ # Generic GET of Sonar search Objects
50
+ def get_search_endpoint(type, params = {})
51
+ url = "/api/#{api_version}/search/#{type}"
52
+ if params[:limit]
53
+ RequestIterator.new(url, connection, params)
54
+ else
55
+ get(url, params)
56
+ end
57
+ end
58
+
59
+ ##
60
+ # Generic GET of Sonar Objects
61
+ def get_endpoint(type, params = {})
62
+ url = "/api/#{api_version}/#{type}"
63
+ get(url, params)
64
+ end
65
+
66
+ ##
67
+ # Generic POST to Sonar
68
+ def post_to_sonar(type, params = {})
69
+ url = "/api/#{api_version}/#{type}"
70
+ post(url, params)
71
+ end
72
+
73
+ private
74
+
75
+ def default_headers
76
+ {
77
+ accept: 'application/json',
78
+ content_type: 'application/json',
79
+ user_agent: "Sonar #{Sonar::VERSION} Ruby Gem"
80
+ }
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,64 @@
1
+ # encoding: utf-8
2
+ require 'multi_json'
3
+
4
+ module Sonar
5
+ module Request
6
+ def extract_params(params)
7
+ # extract something from Sonar if needed
8
+ params
9
+ end
10
+
11
+ def get(path, options = {})
12
+ request(:get, path, options)
13
+ end
14
+
15
+ def post(path, options = {})
16
+ request(:post, path, options)
17
+ end
18
+
19
+ def put(path, options = {})
20
+ request(:put, path, options)
21
+ end
22
+
23
+ def request(method, path, options)
24
+ response = connection.send(method) do |request|
25
+ options.delete(:connection)
26
+ case method
27
+ when :get
28
+ request.url(path, options)
29
+ when :post, :put
30
+ request.path = path
31
+ request.body = MultiJson.encode(options) unless options.empty?
32
+ end
33
+ end
34
+
35
+ response.body
36
+ end
37
+
38
+ class RequestIterator
39
+ include Request
40
+
41
+ attr_accessor :url, :connection, :params
42
+
43
+ def initialize(url, connection, params = {})
44
+ self.url = url
45
+ self.connection = connection
46
+ self.params = params
47
+ end
48
+
49
+ def each
50
+ more = true
51
+ records_rcvd = 0
52
+ while more && records_rcvd < params[:limit]
53
+ # TODO: refactor to not pass around the connection
54
+ params[:connection] = connection
55
+ resp = get(url, params)
56
+ params[:iterator_id] = resp.iterator_id
57
+ records_rcvd += resp['collection'].size
58
+ more = resp['more']
59
+ yield resp
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,37 @@
1
+ # encoding: utf-8
2
+
3
+ module Sonar
4
+ module Search
5
+ # Implemented search query types
6
+ QUERY_TYPES_MAP = {
7
+ 'certificate' => 'Certificate lookup',
8
+ 'certips' => 'Certificate to IPs',
9
+ 'rdns' => 'IP to Reverse DNS Lookup or DNS Lookup to IP',
10
+ 'fdns' => 'Domains to IP or IPs to Domain',
11
+ 'ipcerts' => 'IP to Certificates',
12
+ 'namecerts' => 'Domain to Certificates',
13
+ 'links_to' => 'HTTP References to Domain',
14
+ 'ports' => 'Open Ports',
15
+ 'processed' => 'Open Ports (Processed)',
16
+ 'raw' => 'Open Ports (Raw)',
17
+ 'sslcert' => 'Certificate Details',
18
+ 'whois_ip' => 'Whois (IP)'
19
+ }
20
+
21
+ ##
22
+ # Get search
23
+ #
24
+ # params take in search type as key and query as value
25
+ # {fdns: 'rapid7.com'}
26
+ #
27
+ # @return [Hashie::Mash] with response of search
28
+ def search(params = {})
29
+ type_query = params.select { |k, _v| QUERY_TYPES_MAP.keys.include?(k.to_s) }.first
30
+ fail ArgumentError, "The query type provided is invalid or not yet implemented." unless type_query
31
+ type = type_query[0].to_sym
32
+ params[:q] = type_query[1]
33
+ params = extract_params(params)
34
+ get_search_endpoint(type, params)
35
+ end
36
+ end
37
+ end
data/lib/sonar/user.rb ADDED
@@ -0,0 +1,9 @@
1
+ # encoding: utf-8
2
+
3
+ module Sonar
4
+ module User
5
+ def usage
6
+ get_search_endpoint("usage")
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module Sonar
2
+ VERSION = "0.0.1"
3
+ end
data/lib/sonar.rb ADDED
@@ -0,0 +1,28 @@
1
+ require "faraday"
2
+ require "faraday_middleware"
3
+ require "sonar/cli/cli"
4
+ require "sonar/client"
5
+ require "sonar/version"
6
+
7
+ module Sonar
8
+ class << self
9
+ attr_accessor :api_url, :api_version, :access_token, :email
10
+
11
+ ##
12
+ # Configure default
13
+ #
14
+ # @yield Sonar client object
15
+ def configure
16
+ load_defaults
17
+ yield self
18
+ true
19
+ end
20
+
21
+ private
22
+
23
+ def load_defaults
24
+ self.api_url ||= "https://sonar.labs.rapid7.com"
25
+ self.api_version ||= "v2"
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,37 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'sonar/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "sonar-client"
8
+ spec.version = Sonar::VERSION
9
+ spec.authors = ["Paul Deardorff & HD Moore"]
10
+ spec.email = ["paul_deardorff@rapid7.com", "hd_moore@rapid7.com"]
11
+ spec.description = 'API Wrapper for Sonar'
12
+ spec.summary = spec.description
13
+ spec.homepage = "https://sonar.labs.rapid7.com"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
17
+ spec.executables = ["sonar"]
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'faraday_middleware', '~> 0.9.0'
22
+ spec.add_dependency 'hashie', '~> 2.0.3'
23
+ spec.add_dependency 'multi_json'
24
+ spec.add_dependency 'thor'
25
+ spec.add_dependency 'awesome_print'
26
+
27
+ spec.add_development_dependency "bundler"
28
+ spec.add_development_dependency "rake"
29
+ spec.add_development_dependency "rspec"
30
+ spec.add_development_dependency "simplecov"
31
+ spec.add_development_dependency "simplecov-rcov"
32
+ spec.add_development_dependency "yard"
33
+ spec.add_development_dependency "vcr", '~> 2.8.0'
34
+ spec.add_development_dependency "shoulda"
35
+ spec.add_development_dependency "webmock", '~> 1.8.0'
36
+ spec.add_development_dependency "api_matchers"
37
+ end
@@ -0,0 +1,3 @@
1
+ email: email@asdfasdfasfd.com
2
+ access_token: asdfasdfasdfasdf
3
+ api_url: https://sonar-staging.labs.rapid7.com/
@@ -0,0 +1,13 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe Sonar::Search do
5
+ let(:client) { Sonar::Client.new }
6
+
7
+ context "sha1 #get_certificate" do
8
+ it "should find the Rapid7 certificate by sha1" do
9
+ res = client.get_certificate(sha1: "1e80c24b97c928bb1db7d4d3c05475a6a40a1186")
10
+ expect(res.subject.CN).to eq("www.rapid7.com")
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe Sonar::CLI do
5
+ context "with a valid profile" do
6
+ before do
7
+ Sonar::RCFile.instance.path = "#{fixtures_path}/sonar.rc"
8
+ end
9
+ it "should return the profile" do
10
+ output = capture(:stdout) { Sonar::CLI.start(['profile']) }
11
+ expect(output).to match(/email@asdfasdfasfd.com/)
12
+ end
13
+ end
14
+ context "without a config file" do
15
+ before do
16
+ Sonar::RCFile.instance.path = ""
17
+ end
18
+ xit "should create the missing config file" do
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,10 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe Sonar::Client do
5
+ let(:client) { Sonar::Client.new }
6
+
7
+ it "creates a Faraday::Connection" do
8
+ expect(client.connection).to be_kind_of Faraday::Connection
9
+ end
10
+ end
@@ -0,0 +1,189 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe Sonar::Search do
5
+ let(:client) { Sonar::Client.new }
6
+
7
+ describe "parameters" do
8
+ describe "query type" do
9
+ context "with an invalid query type" do
10
+ it "should raise an ArgumentError" do
11
+ expect { client.search(invalid: 'something.org') }.to raise_error(ArgumentError)
12
+ end
13
+ end
14
+ end
15
+
16
+ describe "exact" do
17
+ it "shouldn't match anything when #exact is true" do
18
+ resp = client.search(rdns: "1.1.", exact: true)
19
+ expect(resp["collection"].size).to eq(0)
20
+ end
21
+ it "should match when #exact is false" do
22
+ resp = client.search(rdns: "1.1.", exact: false)
23
+ expect(resp["collection"].first["address"]).to match(/^1.1./)
24
+ end
25
+ end
26
+
27
+ describe "limit" do
28
+ # The default size from APIv1/v2 is 1,000 records
29
+ context "specifying the :limit to 3000 on #search" do
30
+ let(:resp) { client.search(rdns: '.hp.com', limit: 3000) }
31
+
32
+ it "should return a RequestIterator" do
33
+ expect(resp.class).to eq(Sonar::Request::RequestIterator)
34
+ end
35
+ it "should return 3 x 1,000-record blocks" do
36
+ num_blocks = 0
37
+ resp.each do |resp_block|
38
+ expect(resp_block['collection'].size).to eq(1000)
39
+ num_blocks += 1
40
+ end
41
+ expect(num_blocks).to eq(3)
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ context "certificate" do
48
+ let(:resp) { client.search(certificate: '.hp.com') }
49
+
50
+ it "should provide certificate details" do
51
+ expect(resp).to have_key('collection')
52
+ end
53
+ end
54
+
55
+ describe "rdns" do
56
+ context "rdnsname" do
57
+ let(:resp) { client.search(rdns: '208.118.227.10.rapid7.com') }
58
+
59
+ it "returns hashie response of search" do
60
+ expect(resp.class).to eq(Hashie::Mash)
61
+ end
62
+ it "rdnsname finds 208.118.227.10 for 208.118.227.10.rapid7.com" do
63
+ expect(resp['collection'].any? { |x| x['address'] == '208.118.227.10' }).to be(true)
64
+ end
65
+ end
66
+
67
+ context "rdnsip" do
68
+ let(:resp) { client.search(rdns: '188.40.56.11') }
69
+
70
+ it "rdnsip finds static.11.56.40.188.clients.your-server.de for 188.40.56.11" do
71
+ expect(resp['collection'].any? { |x| x['name'] == 'static.11.56.40.188.clients.your-server.de' }).to be(true)
72
+ end
73
+ end
74
+
75
+ context "validation" do
76
+ let(:resp) { client.search(rdns: '188.40.56.11@#&#') }
77
+
78
+ it "should error for invalid domain query type" do
79
+ expect(resp["error"]).to eq("Invalid query")
80
+ expect(resp["errors"].first).to eq("Expected a domain but got '188.40.56.11@#&#'")
81
+ end
82
+ end
83
+ end
84
+
85
+ describe "fdns" do
86
+ context "fdnsname" do
87
+ let(:resp) { client.search(fdns: 'rapid7.com') }
88
+
89
+ it "returns hashie response of search" do
90
+ expect(resp.class).to eq(Hashie::Mash)
91
+ end
92
+ it "finds fdnsname 208.118.227.10 for rapid7.com" do
93
+ expect(resp['collection'].any? { |x| x['address'] == '208.118.227.10' }).to be(true)
94
+ end
95
+ end
96
+
97
+ context "fdnsip" do
98
+ let(:resp) { client.search(fdns: '208.118.227.10') }
99
+
100
+ it "finds fdnsip rapid7.com at 208.118.227.10" do
101
+ expect(resp['collection'].any? { |x| x['address'] == 'rapid7.com' }).to be(true)
102
+ end
103
+ end
104
+
105
+ context "validation" do
106
+ let(:resp) { client.search(fdns: '188.40.56.11@#&#') }
107
+
108
+ it "should error for invalid domain query type" do
109
+ expect(resp["error"]).to eq("Invalid query")
110
+ expect(resp["errors"].first).to eq("Expected a domain but got '188.40.56.11@#&#'")
111
+ end
112
+ end
113
+ end
114
+
115
+ context "links_to" do
116
+ let(:resp) { client.search(links_to: 'rapid7.com') }
117
+
118
+ it "should provide links_to details" do
119
+ expect(resp).to have_key('collection')
120
+ end
121
+ end
122
+
123
+ context "ipcerts" do
124
+ let(:resp) { client.search(ipcerts: '208.118.227.10') }
125
+
126
+ it "should provide ipcerts details" do
127
+ expect(resp).to have_key('collection')
128
+ end
129
+ end
130
+
131
+ context "certips" do
132
+ let(:resp) { client.search(certips: '1e80c24b97c928bb1db7d4d3c05475a6a40a1186') }
133
+
134
+ it "should provide certips details" do
135
+ expect(resp).to have_key('collection')
136
+ end
137
+ end
138
+
139
+ context "namecerts" do
140
+ let(:resp) { client.search(namecerts: '.rapid7.com') }
141
+
142
+ it "should provide namecerts details" do
143
+ expect(resp).to have_key('collection')
144
+ end
145
+ end
146
+
147
+ context "sslcert" do
148
+ let(:resp) { client.search(sslcert: '1e80c24b97c928bb1db7d4d3c05475a6a40a1186') }
149
+
150
+ it "should provide sslcert details" do
151
+ expect(resp).to have_key('collection')
152
+ end
153
+ end
154
+
155
+ context "whois_ip" do
156
+ let(:resp) { client.search(whois_ip: '208.118.227.10') }
157
+
158
+ it "should find rapid7.com" do
159
+ expect(resp['name']).to eq('TWDX-208-118-227-0-1')
160
+ end
161
+ end
162
+
163
+ # TODO: actually check response
164
+ context "raw" do
165
+ let(:resp) { client.search(raw: '208.118.227.10') }
166
+
167
+ it "should return a collection" do
168
+ expect(resp).to have_key('collection')
169
+ end
170
+ end
171
+
172
+ # TODO: actually check response
173
+ context "processed" do
174
+ let(:resp) { client.search(processed: '208.118.227.10') }
175
+
176
+ it "should return a collection" do
177
+ expect(resp).to have_key('collection')
178
+ end
179
+ end
180
+
181
+ # TODO: actually check response
182
+ context "ports" do
183
+ let(:resp) { client.search(ports: '208.118.227.10') }
184
+
185
+ it "should return a collection" do
186
+ expect(resp).to have_key('collection')
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe Sonar::User do
5
+ let(:client) { Sonar::Client.new }
6
+
7
+ context "with a valid client querying usage" do
8
+ let(:res) { client.usage }
9
+
10
+ it "should show usage information for the user" do
11
+ expect(res.user.api_token).to_not be_nil
12
+ expect(res.current_api_hits).to be >= 0
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,67 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ # Skip the configure in spec_helper so we can test defaults
5
+ describe Sonar, skip_autoconfig: true do
6
+ let(:client) { Sonar::Client.new }
7
+ before :each do
8
+ reset_sonar_config
9
+ end
10
+
11
+ context "configure defaults" do
12
+ it "uses default API URL" do
13
+ expect(client.api_url).to eq 'https://sonar.labs.rapid7.com'
14
+ end
15
+ it "uses default API VERSION" do
16
+ expect(client.api_version).to eq 'v2'
17
+ end
18
+ end
19
+
20
+ context "handles custom configuration for url and version" do
21
+ let(:new_client) do
22
+ Sonar::Client.new(
23
+ api_url: 'https://somethingnew.com',
24
+ api_version: 'v1'
25
+ )
26
+ end
27
+
28
+ it "::Client API_URL configuration" do
29
+ expect(new_client.api_url).to eq 'https://somethingnew.com'
30
+ end
31
+ it "::Client API_VERSION configuration" do
32
+ expect(new_client.api_version).to eq 'v1'
33
+ end
34
+ end
35
+
36
+ context "when using a configure block and setting api_version" do
37
+ before do
38
+ Sonar.configure do |c|
39
+ c.api_version = "v3"
40
+ end
41
+ end
42
+
43
+ it "should have set the custom api_version" do
44
+ expect(Sonar.api_version).to eq("v3")
45
+ end
46
+ it "should use the default api_url" do
47
+ expect(Sonar.api_url).to eq("https://sonar.labs.rapid7.com")
48
+ end
49
+ end
50
+
51
+ context "when making a request to the client with bad creds" do
52
+ before do
53
+ Sonar.configure do |c|
54
+ c.email = "wrong@sowrong.com"
55
+ c.access_token = "somewrongkey"
56
+ c.api_version = "v2"
57
+ c.api_url = "https://sonar-staging.labs.rapid7.com"
58
+ end
59
+ client = Sonar::Client.new
60
+ @resp = client.search(rdns: "hp.com")
61
+ end
62
+
63
+ it "should return unauthorized" do
64
+ expect(@resp["error"]).to eq("Could not authenticate")
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,60 @@
1
+ require 'sonar'
2
+ require 'active_support'
3
+ require 'vcr'
4
+ require 'simplecov'
5
+ require 'simplecov-rcov'
6
+ require 'api_matchers'
7
+
8
+ class SimpleCov::Formatter::MergedFormatter
9
+ def format(result)
10
+ SimpleCov::Formatter::HTMLFormatter.new.format(result)
11
+ SimpleCov::Formatter::RcovFormatter.new.format(result)
12
+ end
13
+ end
14
+
15
+ SimpleCov.formatter = SimpleCov::Formatter::MergedFormatter
16
+ SimpleCov.start do
17
+ add_filter '/vendor'
18
+ end
19
+
20
+ VCR.configure do |c|
21
+ c.allow_http_connections_when_no_cassette = true
22
+ c.cassette_library_dir = 'spec/cassette'
23
+ c.hook_into :webmock
24
+ c.configure_rspec_metadata!
25
+ c.default_cassette_options = { record: :new_episodes }
26
+ end
27
+
28
+ RSpec.configure do |c|
29
+ c.include APIMatchers::RSpecMatchers
30
+
31
+ c.treat_symbols_as_metadata_keys_with_true_values = true
32
+
33
+ #
34
+ # Add gem specific configuration for easy access
35
+ #
36
+ c.before(:each) do
37
+ # TODO: move to using a gem like VCR for faking HTTP requests.
38
+ # For now we'll test against the staging server using
39
+ # real creds stored in env.
40
+ Sonar.configure do |config|
41
+ unless ENV['SONAR_TOKEN'] && ENV['SONAR_EMAIL']
42
+ fail ArgumentError, "Please configure Sonar for testing by setting SONAR_TOKEN, SONAR_EMAIL,
43
+ and SONAR_API_URL in your environment."
44
+ end
45
+ config.api_url = ENV['SONAR_API_URL'] || 'http://localhost:3000'
46
+ config.api_version = 'v2'
47
+ config.access_token = ENV['SONAR_TOKEN']
48
+ config.email = ENV['SONAR_EMAIL']
49
+ end
50
+ end
51
+ end
52
+
53
+ def fixtures_path
54
+ File.expand_path('../fixtures', __FILE__)
55
+ end
56
+
57
+ def reset_sonar_config
58
+ Sonar.api_version = nil
59
+ Sonar.api_url = nil
60
+ end
metadata ADDED
@@ -0,0 +1,292 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sonar-client
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Paul Deardorff & HD Moore
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-01-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday_middleware
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.9.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.9.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: hashie
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 2.0.3
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 2.0.3
41
+ - !ruby/object:Gem::Dependency
42
+ name: multi_json
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: thor
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: awesome_print
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: bundler
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rspec
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: simplecov
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: simplecov-rcov
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: yard
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: vcr
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: 2.8.0
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: 2.8.0
181
+ - !ruby/object:Gem::Dependency
182
+ name: shoulda
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ - !ruby/object:Gem::Dependency
196
+ name: webmock
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - "~>"
200
+ - !ruby/object:Gem::Version
201
+ version: 1.8.0
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - "~>"
207
+ - !ruby/object:Gem::Version
208
+ version: 1.8.0
209
+ - !ruby/object:Gem::Dependency
210
+ name: api_matchers
211
+ requirement: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - ">="
214
+ - !ruby/object:Gem::Version
215
+ version: '0'
216
+ type: :development
217
+ prerelease: false
218
+ version_requirements: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - ">="
221
+ - !ruby/object:Gem::Version
222
+ version: '0'
223
+ description: API Wrapper for Sonar
224
+ email:
225
+ - paul_deardorff@rapid7.com
226
+ - hd_moore@rapid7.com
227
+ executables:
228
+ - sonar
229
+ extensions: []
230
+ extra_rdoc_files: []
231
+ files:
232
+ - ".gitignore"
233
+ - ".rspec"
234
+ - ".rubocop.yml"
235
+ - Gemfile
236
+ - Gemfile.lock
237
+ - LICENSE
238
+ - README.md
239
+ - Rakefile
240
+ - bin/sonar
241
+ - lib/sonar.rb
242
+ - lib/sonar/certificate.rb
243
+ - lib/sonar/cli/cli.rb
244
+ - lib/sonar/cli/rcfile.rb
245
+ - lib/sonar/client.rb
246
+ - lib/sonar/request.rb
247
+ - lib/sonar/search.rb
248
+ - lib/sonar/user.rb
249
+ - lib/sonar/version.rb
250
+ - sonar-client.gemspec
251
+ - spec/fixtures/sonar.rc
252
+ - spec/sonar/certificate_spec.rb
253
+ - spec/sonar/cli_spec.rb
254
+ - spec/sonar/client_spec.rb
255
+ - spec/sonar/search_spec.rb
256
+ - spec/sonar/user_spec.rb
257
+ - spec/sonar_spec.rb
258
+ - spec/spec_helper.rb
259
+ homepage: https://sonar.labs.rapid7.com
260
+ licenses:
261
+ - MIT
262
+ metadata: {}
263
+ post_install_message:
264
+ rdoc_options: []
265
+ require_paths:
266
+ - lib
267
+ required_ruby_version: !ruby/object:Gem::Requirement
268
+ requirements:
269
+ - - ">="
270
+ - !ruby/object:Gem::Version
271
+ version: '0'
272
+ required_rubygems_version: !ruby/object:Gem::Requirement
273
+ requirements:
274
+ - - ">="
275
+ - !ruby/object:Gem::Version
276
+ version: '0'
277
+ requirements: []
278
+ rubyforge_project:
279
+ rubygems_version: 2.4.3
280
+ signing_key:
281
+ specification_version: 4
282
+ summary: API Wrapper for Sonar
283
+ test_files:
284
+ - spec/fixtures/sonar.rc
285
+ - spec/sonar/certificate_spec.rb
286
+ - spec/sonar/cli_spec.rb
287
+ - spec/sonar/client_spec.rb
288
+ - spec/sonar/search_spec.rb
289
+ - spec/sonar/user_spec.rb
290
+ - spec/sonar_spec.rb
291
+ - spec/spec_helper.rb
292
+ has_rdoc: