sonar-client 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 46f4c95ec260a3eb4b212c2f85c4bee5893dc461
4
- data.tar.gz: 4a574ae9f388f0db79f28e80e9dc69b9c97de3eb
3
+ metadata.gz: 4dc46841e0ddf4f091d927804c15b77b5ad95d33
4
+ data.tar.gz: 50cfb040770c7b69ef437e09edc48adf1d08fa81
5
5
  SHA512:
6
- metadata.gz: 8056aa52de648957853d67ad2ebcdb8168e4ee586dc8dfe0bdab2e6ef9d638f6caac2ae142c4eceffd9c598f0e17e402179bfad8a71df5d700b94dbce477b852
7
- data.tar.gz: 66db84c021545e129f001839cb8de8e56b221175bbbb202df443d8f35e92ee5dd344c34dbb5ec034599abededc92f805a9d4cdea2b979d9c28a3a180aca684c0
6
+ metadata.gz: 27c4336fe464f3f988df579531b2795318a12b08055b8fa049cd32104a3a7d1de577480fcc3c96c93cb1739d223ff24f28ee7e7599b052393d9e98996424cffb
7
+ data.tar.gz: 4f237e0a682fdaf8e90a9c8f63c630a4e4da90e0566550f0f1ff5659edcb99fc10be33c50f8a112d7c03ecde8ccb93230cfc93e8e0053c04e4cd5438ddeb2ca5
@@ -2,14 +2,17 @@ language: ruby
2
2
  sudo: false
3
3
  cache: bundler
4
4
  rvm:
5
- - 2.1.5
6
- - jruby
5
+ - 2.2.2
6
+ - jruby-9.1.5.0
7
7
  before_install:
8
8
  - "echo 'gem: --no-ri --no-rdoc' > ~/.gemrc"
9
9
  - rake --version
10
10
  before_script:
11
11
  - bundle exec rake --version
12
12
  script: bundle exec rspec
13
+ notifications:
14
+ email:
15
+ - r7_labs@rapid7.com
13
16
  #
14
17
  # Environment variables should be set in Travis repository settings per
15
18
  # https://docs.travis-ci.com/user/environment-variables/#Defining-Variables-in-Repository-Settings
@@ -2,11 +2,15 @@
2
2
 
3
3
  require 'thor'
4
4
  require 'sonar/cli/rcfile'
5
+ require 'sonar/search'
5
6
  require 'awesome_print'
7
+ require 'table_print'
8
+
9
+ include Sonar::Search
6
10
 
7
11
  module Sonar
8
12
  class CLI < Thor
9
- class_option 'format', type: :string, desc: 'Flat JSON, JSON lines, or pretty printed [flat/lines/pretty]'
13
+ class_option 'format', type: :string, desc: 'Flat JSON (include empty collections), JSON lines of collection data (default), or pretty printed [flat/lines/pretty]'
10
14
 
11
15
  def initialize(*)
12
16
  @config = Sonar::RCFile.instance.load_file
@@ -24,34 +28,34 @@ module Sonar
24
28
  ap @client.usage
25
29
  end
26
30
 
27
- desc 'search [QUERY TYPE] [QUERY TERM]', 'Search anything from Sonars'
31
+ desc 'search [QUERY TYPE] [QUERY TERM]', 'Search any query type from Sonar or specify \'all\' as QUERY TYPE to search them all.'
28
32
  method_option 'record_limit', type: :numeric, aliases: '-n', desc: 'Maximum number of records to fetch'
29
33
  method_option 'exact', type: :boolean, aliases: '-e', desc: 'Search for the query string exactly, do not include partial string matches'
30
-
31
34
  def search(type, term)
32
- @query = {}
33
- @query[type.to_sym] = term
34
- @query[:limit] = options['record_limit']
35
- @query[:exact] = options['exact']
36
- resp = @client.search(@query)
37
-
38
- errors = 0
39
- if resp.is_a?(Sonar::Request::RequestIterator)
40
- resp.each do |data|
41
- errors += 1 if data.key?('errors') || data.key?('error')
42
- print_json(cleanup_data(data), options['format'])
35
+ types = [type]
36
+
37
+ if type == 'all'
38
+ if term =~ IS_IP
39
+ types = ip_search_type_names
40
+ else
41
+ types = domain_search_type_names
43
42
  end
44
- else
45
- errors += 1 if resp.key?('errors') || resp.key?('error')
46
- print_json(cleanup_data(resp), options['format'])
47
43
  end
48
44
 
49
- raise Search::SearchError.new("Encountered #{errors} errors while searching") if errors > 0
45
+ types.each do |type|
46
+ @query = {}
47
+ @query[type.to_sym] = term
48
+ @query[:limit] = options['record_limit']
49
+ @query[:exact] = options['exact']
50
+ resp = @client.search(@query)
51
+ handle_search_response(resp)
52
+ end
50
53
  end
51
54
 
52
55
  desc 'types', 'List all Sonar query types'
53
56
  def types
54
- ap Search::QUERY_TYPES_MAP
57
+ tp.set :io, $stdout
58
+ tp QUERY_TYPES, :name, { description: { width: 100 } }, :input
55
59
  end
56
60
 
57
61
  desc 'config', 'Sonar config file location'
@@ -2,21 +2,25 @@
2
2
 
3
3
  module Sonar
4
4
  module Search
5
+
6
+ # Allow IP queries to be in the form of "1.", "1.2.", "1.2.3.", and "1.2.3.4"
7
+ IS_IP = /^(\d{1,3}\.|\d{1,3}\.\d{1,3}\.|\d{1,3}\.\d{1,3}\.\d{1,3}\.|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/
8
+
5
9
  # 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
- }
10
+ QUERY_TYPES = [
11
+ { name: 'certificate', description: 'Certificate lookup', input: 'sha' },
12
+ { name: 'certips', description: 'Certificate to IPs', input: 'sha' },
13
+ { name: 'rdns', description: 'IP to Reverse DNS Lookup or DNS Lookup to IP', input: 'ip' },
14
+ { name: 'fdns', description: 'Domains to IP or IPs to Domain', input: 'domain' },
15
+ { name: 'ipcerts', description: 'IP to Certificates', input: 'ip' },
16
+ { name: 'namecerts', description: 'Domain to Certificates', input: 'domain' },
17
+ { name: 'links_to', description: 'HTTP References to Domain', input: 'domain' },
18
+ { name: 'ports', description: 'Open Ports', input: 'ip' },
19
+ { name: 'processed', description: 'Open Ports (Processed)', input: 'ip' },
20
+ { name: 'raw', description: 'Open Ports (Raw)', input: 'ip' },
21
+ { name: 'sslcert', description: 'Certificate Details', input: 'sha' },
22
+ { name: 'all', description: 'Search all appropriate search types for an IP or domain', input: 'all' }
23
+ ]
20
24
 
21
25
  ##
22
26
  # Generic exception for errors encountered while searching
@@ -24,6 +28,41 @@ module Sonar
24
28
  class SearchError < StandardError
25
29
  end
26
30
 
31
+ def ip_search_type_names
32
+ ip_search_types.map { |type| type[:name] }
33
+ end
34
+
35
+ def domain_search_type_names
36
+ domain_search_types.map { |type| type[:name] }
37
+ end
38
+
39
+ def ip_search_types
40
+ QUERY_TYPES.select { |type| type[:input] == 'ip' }
41
+ end
42
+
43
+ def domain_search_types
44
+ QUERY_TYPES.select { |type| type[:input] == 'domain' }
45
+ end
46
+
47
+ def query_type_names
48
+ QUERY_TYPES.map { |type| type[:name] }
49
+ end
50
+
51
+ def handle_search_response(resp)
52
+ errors = 0
53
+ if resp.is_a?(Sonar::Request::RequestIterator)
54
+ resp.each do |data|
55
+ errors += 1 if data.key?('errors') || data.key?('error')
56
+ print_json(cleanup_data(data), options['format'])
57
+ end
58
+ else
59
+ errors += 1 if resp.key?('errors') || resp.key?('error')
60
+ print_json(cleanup_data(resp), options['format'])
61
+ end
62
+
63
+ raise Search::SearchError.new("Encountered #{errors} errors while searching") if errors > 0
64
+ end
65
+
27
66
  ##
28
67
  # Get search
29
68
  #
@@ -32,7 +71,7 @@ module Sonar
32
71
  #
33
72
  # @return [Hashie::Mash] with response of search
34
73
  def search(params = {})
35
- type_query = params.select { |k, _v| QUERY_TYPES_MAP.keys.include?(k.to_s) }.first
74
+ type_query = params.select { |k, _v| query_type_names.include?(k.to_s) }.first
36
75
  fail ArgumentError, "The query type provided is invalid or not yet implemented." unless type_query
37
76
  type = type_query[0].to_sym
38
77
  params[:q] = type_query[1]
@@ -1,3 +1,3 @@
1
1
  module Sonar
2
- VERSION = "0.1.3"
2
+ VERSION = "0.1.4"
3
3
  end
@@ -23,6 +23,7 @@ Gem::Specification.new do |spec|
23
23
  spec.add_dependency 'multi_json'
24
24
  spec.add_dependency 'thor'
25
25
  spec.add_dependency 'awesome_print'
26
+ spec.add_dependency 'table_print'
26
27
 
27
28
  spec.add_development_dependency "bundler"
28
29
  spec.add_development_dependency "rake"
@@ -82,6 +82,29 @@ describe Sonar::CLI do
82
82
  end
83
83
  end
84
84
  end
85
+
86
+ describe 'sonar types command' do
87
+ it 'returns all sonar search types' do
88
+ output = run_command('types')
89
+ expect(output).to match(/Certificate to IPs/)
90
+ end
91
+ end
92
+
93
+ describe 'search all command' do
94
+ before do
95
+ allow_any_instance_of(Sonar::Client).to receive(:search).and_return(
96
+ Sonar::Client.new.search(fdns: '208.118.227.20', exact: true)
97
+ )
98
+ end
99
+ it 'returns results when searching for an IP' do
100
+ output = run_command('search all 208.118.227.20')
101
+ expect(output).to match(/rapid7\.com/)
102
+ end
103
+ it 'returns results when searching for a domain' do
104
+ output = run_command('search all rapid7.com')
105
+ expect(output).to match(/208\.118\.227\.20/)
106
+ end
107
+ end
85
108
  end
86
109
 
87
110
  def run_command(args)
@@ -4,6 +4,24 @@ require 'spec_helper'
4
4
  describe Sonar::Search do
5
5
  let(:client) { Sonar::Client.new }
6
6
 
7
+ describe "#ip_search_type_names" do
8
+ it 'includes rdns' do
9
+ expect(subject.ip_search_type_names).to include('rdns')
10
+ end
11
+ it 'does not include fdns' do
12
+ expect(subject.ip_search_type_names).to_not include('fdns')
13
+ end
14
+ end
15
+
16
+ describe "#domain_search_type_names" do
17
+ it 'includes fdns' do
18
+ expect(subject.domain_search_type_names).to include('fdns')
19
+ end
20
+ it 'does not include rdns' do
21
+ expect(subject.domain_search_type_names).to_not include('rdns')
22
+ end
23
+ end
24
+
7
25
  describe "parameters" do
8
26
  describe "query type" do
9
27
  context "with an invalid query type" do
@@ -15,12 +33,12 @@ describe Sonar::Search do
15
33
 
16
34
  describe "exact" do
17
35
  it "shouldn't match anything when #exact is true" do
18
- resp = client.search(rdns: "1.1.", exact: true)
36
+ resp = client.search(fdns: ".rapid7.com", exact: true)
19
37
  expect(resp["collection"].size).to eq(0)
20
38
  end
21
39
  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./)
40
+ resp = client.search(fdns: ".rapid7.com", exact: false)
41
+ expect(resp["collection"].size).to be > 0
24
42
  end
25
43
  end
26
44
 
@@ -152,14 +170,6 @@ describe Sonar::Search do
152
170
  end
153
171
  end
154
172
 
155
- context "whois_ip" do
156
- let(:resp) { client.search(whois_ip: '208.118.227.10') }
157
-
158
- xit "should find rapid7.com" do
159
- expect(resp['name']).to eq('TWDX-208-118-227-0-1')
160
- end
161
- end
162
-
163
173
  # TODO: actually check response
164
174
  context "raw" do
165
175
  let(:resp) { client.search(raw: '208.118.227.10') }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sonar-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul Deardorff & HD Moore
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-04-22 00:00:00.000000000 Z
11
+ date: 2017-08-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday_middleware
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: table_print
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: bundler
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -296,4 +310,3 @@ test_files:
296
310
  - spec/sonar/user_spec.rb
297
311
  - spec/sonar_spec.rb
298
312
  - spec/spec_helper.rb
299
- has_rdoc: