sonar-client 0.0.1

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