ronin 1.2.0 → 1.3.0

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.
Files changed (57) hide show
  1. data/.gitignore +11 -0
  2. data/ChangeLog.md +28 -10
  3. data/Gemfile +1 -1
  4. data/README.md +14 -41
  5. data/Rakefile +3 -0
  6. data/gemspec.yml +14 -15
  7. data/lib/bond/completions/ronin.rb +0 -5
  8. data/lib/ronin/address.rb +17 -0
  9. data/lib/ronin/author.rb +1 -1
  10. data/lib/ronin/database/database.rb +11 -3
  11. data/lib/ronin/email_address.rb +33 -0
  12. data/lib/ronin/host_name.rb +57 -6
  13. data/lib/ronin/ip_address.rb +49 -5
  14. data/lib/ronin/license.rb +1 -1
  15. data/lib/ronin/mac_address.rb +39 -1
  16. data/lib/ronin/model/has_license.rb +12 -3
  17. data/lib/ronin/model/importable.rb +65 -0
  18. data/lib/ronin/repository.rb +12 -4
  19. data/lib/ronin/spec/database.rb +1 -1
  20. data/lib/ronin/tcp_port.rb +3 -12
  21. data/lib/ronin/udp_port.rb +2 -11
  22. data/lib/ronin/ui/cli/commands/emails.rb +0 -45
  23. data/lib/ronin/ui/cli/commands/hosts.rb +4 -31
  24. data/lib/ronin/ui/cli/commands/ips.rb +4 -30
  25. data/lib/ronin/ui/cli/commands/urls.rb +5 -45
  26. data/lib/ronin/ui/cli/model_command.rb +17 -27
  27. data/lib/ronin/ui/cli/resources_command.rb +25 -1
  28. data/lib/ronin/ui/cli/script_command.rb +1 -1
  29. data/lib/ronin/ui/console/context.rb +1 -1
  30. data/lib/ronin/url.rb +43 -1
  31. data/lib/ronin/version.rb +1 -1
  32. data/ronin.gemspec +1 -1
  33. data/spec/email_address_spec.rb +20 -0
  34. data/spec/host_name_spec.rb +20 -0
  35. data/spec/ip_address_spec.rb +104 -0
  36. data/spec/mac_address_spec.rb +20 -0
  37. data/spec/url_spec.rb +24 -0
  38. metadata +118 -155
  39. data/lib/ronin/network/mixins.rb +0 -27
  40. data/lib/ronin/network/mixins/esmtp.rb +0 -165
  41. data/lib/ronin/network/mixins/http.rb +0 -723
  42. data/lib/ronin/network/mixins/imap.rb +0 -151
  43. data/lib/ronin/network/mixins/pop3.rb +0 -141
  44. data/lib/ronin/network/mixins/smtp.rb +0 -159
  45. data/lib/ronin/network/mixins/tcp.rb +0 -331
  46. data/lib/ronin/network/mixins/telnet.rb +0 -199
  47. data/lib/ronin/network/mixins/udp.rb +0 -227
  48. data/lib/ronin/spec/ui/output.rb +0 -28
  49. data/lib/ronin/ui/output.rb +0 -21
  50. data/lib/ronin/ui/output/helpers.rb +0 -248
  51. data/lib/ronin/ui/output/output.rb +0 -146
  52. data/lib/ronin/ui/output/terminal.rb +0 -21
  53. data/lib/ronin/ui/output/terminal/color.rb +0 -118
  54. data/lib/ronin/ui/output/terminal/raw.rb +0 -103
  55. data/lib/ronin/ui/shell.rb +0 -92
  56. data/spec/ip_address.rb +0 -84
  57. data/spec/ui/output_spec.rb +0 -32
@@ -65,11 +65,9 @@ module Ronin
65
65
  # @since 1.0.0
66
66
  #
67
67
  def execute
68
- if options[:import]
69
- import options[:import]
70
- elsif options[:lookup]
68
+ if options[:lookup]
71
69
  lookup options[:lookup]
72
- elsif options.list?
70
+ else
73
71
  super
74
72
  end
75
73
  end
@@ -94,30 +92,6 @@ module Ronin
94
92
  print_info "Looked up #{host}"
95
93
  end
96
94
 
97
- #
98
- # Extracts and saves IP Addresses from a file.
99
- #
100
- # @param [String] path
101
- # The path of the file.
102
- #
103
- # @since 1.0.0
104
- #
105
- def import(path)
106
- File.open(options[:import]) do |file|
107
- file.each_line do |line|
108
- IPAddr.extract(line) do |match|
109
- ip = IPAddress.new(:address => match)
110
-
111
- if ip.save
112
- print_info "Imported #{ip}"
113
- else
114
- print_error "Unable to import #{match.dump}."
115
- end
116
- end
117
- end
118
- end
119
- end
120
-
121
95
  #
122
96
  # Prints an IP Address.
123
97
  #
@@ -133,11 +107,11 @@ module Ronin
133
107
 
134
108
  indent do
135
109
  if (org = ip.organization)
136
- print_hash('Organization' => org)
110
+ print_hash 'Organization' => org
137
111
  end
138
112
 
139
113
  if (last_scanned_at = ip.last_scanned_at)
140
- print_hash('Last Scanned' => last_scanned_at)
114
+ print_hash 'Last Scanned' => last_scanned_at
141
115
  end
142
116
 
143
117
  unless ip.mac_addresses.empty?
@@ -62,46 +62,8 @@ module Ronin
62
62
  :aliases => '-i',
63
63
  :banner => 'FILE'
64
64
 
65
- #
66
- # Queries the {Ronin::URL} model.
67
- #
68
- # @since 1.0.0
69
- #
70
- def execute
71
- if options[:import]
72
- import options[:import]
73
- elsif options.list?
74
- super
75
- end
76
- end
77
-
78
65
  protected
79
66
 
80
- #
81
- # Imports URLs from a file.
82
- #
83
- # @param [String] path
84
- # The path to the file.
85
- #
86
- # @since 1.0.0
87
- #
88
- def import(path)
89
- File.open(path) do |file|
90
- file.each_line do |line|
91
- line.strip!
92
- next if line.empty?
93
-
94
- url = URL.parse(line)
95
-
96
- if url.save
97
- print_info "Imported #{url}"
98
- else
99
- print_error "Could not import #{line.dump}."
100
- end
101
- end
102
- end
103
- end
104
-
105
67
  #
106
68
  # Prints a URL.
107
69
  #
@@ -116,13 +78,11 @@ module Ronin
116
78
  print_title url
117
79
 
118
80
  indent do
119
- print_hash(
120
- 'Host' => url.host_name,
121
- 'Port' => url.port.number,
122
- 'Path' => url.path,
123
- 'Fragment' => url.fragment,
124
- 'Last Scanned' => url.last_scanned_at
125
- )
81
+ print_hash 'Host' => url.host_name,
82
+ 'Port' => url.port.number,
83
+ 'Path' => url.path,
84
+ 'Fragment' => url.fragment,
85
+ 'Last Scanned' => url.last_scanned_at
126
86
 
127
87
  unless url.query_params.empty?
128
88
  params = {}
@@ -31,22 +31,6 @@ module Ronin
31
31
  class_option :database, :type => :string,
32
32
  :aliases => '-D'
33
33
 
34
- #
35
- # The model to query.
36
- #
37
- # @return [DataMapper::Resource]
38
- # The model that will be queried.
39
- #
40
- # @since 1.1.0
41
- #
42
- # @api semipublic
43
- #
44
- def self.query_model
45
- @query_model ||= if self.superclass < ModelCommand
46
- self.superclass.query_model
47
- end
48
- end
49
-
50
34
  #
51
35
  # The query options for the command.
52
36
  #
@@ -88,9 +72,9 @@ module Ronin
88
72
  protected
89
73
 
90
74
  #
91
- # Sets the model to query.
75
+ # Sets or gets the model to query.
92
76
  #
93
- # @param [DataMapper::Resource] model
77
+ # @param [DataMapper::Resource, nil] model
94
78
  # The model class.
95
79
  #
96
80
  # @return [DataMapper::Resource]
@@ -100,8 +84,14 @@ module Ronin
100
84
  #
101
85
  # @api semipublic
102
86
  #
103
- def self.model(model)
104
- @query_model = model
87
+ def self.model(model=nil)
88
+ if model
89
+ @model = model
90
+ else
91
+ @model ||= if superclass < ModelCommand
92
+ superclass.model
93
+ end
94
+ end
105
95
  end
106
96
 
107
97
  #
@@ -119,7 +109,7 @@ module Ronin
119
109
  #
120
110
  def self.query_option(name,options={})
121
111
  query_options << name
122
- class_option(name,options)
112
+ class_option name, options
123
113
  end
124
114
 
125
115
  #
@@ -154,11 +144,11 @@ module Ronin
154
144
  # @api semipublic
155
145
  #
156
146
  def query
157
- unless self.class.query_model
147
+ unless self.class.model
158
148
  raise("query model not defined for #{self.class}")
159
149
  end
160
150
 
161
- query = self.class.query_model.all
151
+ query = self.class.model.all
162
152
 
163
153
  self.class.each_query_option do |name|
164
154
  value = options[name]
@@ -166,10 +156,10 @@ module Ronin
166
156
  # skip unset options
167
157
  next if value.nil?
168
158
 
169
- if query_model.properties.named?(name)
159
+ if model.properties.named?(name)
170
160
  query = query.all(name => value)
171
- elsif query_model.respond_to?(name)
172
- query_method = query_model.method(name)
161
+ elsif model.respond_to?(name)
162
+ query_method = model.method(name)
173
163
 
174
164
  query = case query_method.arity
175
165
  when 0
@@ -180,7 +170,7 @@ module Ronin
180
170
  query.send(name,*value)
181
171
  end
182
172
  else
183
- raise("unknown query method or property #{name} for #{query_model}")
173
+ raise("unknown query method or property #{name} for #{model}")
184
174
  end
185
175
  end
186
176
 
@@ -18,6 +18,7 @@
18
18
  #
19
19
 
20
20
  require 'ronin/ui/cli/model_command'
21
+ require 'ronin/model/importable'
21
22
 
22
23
  module Ronin
23
24
  module UI
@@ -40,11 +41,34 @@ module Ronin
40
41
  # @api semipublic
41
42
  #
42
43
  def execute
43
- print_resources(query)
44
+ if options[:import]
45
+ self.class.model.import(options[:import]) do |resource|
46
+ print_info "Imported #{resource}"
47
+ end
48
+ else
49
+ print_resources(query)
50
+ end
44
51
  end
45
52
 
46
53
  protected
47
54
 
55
+ #
56
+ # Sets the model used by the command.
57
+ #
58
+ # @see ModelCommand.model
59
+ #
60
+ # @since 1.3.0
61
+ #
62
+ def self.model(model=nil)
63
+ if (model && model < Model::Importable)
64
+ class_option :import, :type => :string,
65
+ :aliases => '-i',
66
+ :banner => 'FILE'
67
+ end
68
+
69
+ return super(model)
70
+ end
71
+
48
72
  #
49
73
  # Default method which will print every queried resource.
50
74
  #
@@ -103,7 +103,7 @@ module Ronin
103
103
  #
104
104
  def load_script
105
105
  if options[:file]
106
- self.class.query_model.load_from(options[:file])
106
+ self.class.model.load_from(options[:file])
107
107
  else
108
108
  query.load_first
109
109
  end
@@ -60,7 +60,7 @@ module Ronin
60
60
  # Populates the instance variables.
61
61
  #
62
62
  # @param [Hash] variables
63
- # The variable naems and values.
63
+ # The variable names and values.
64
64
  #
65
65
  def instance_variables=(variables)
66
66
  variables.each do |name,value|
data/lib/ronin/url.rb CHANGED
@@ -18,6 +18,7 @@
18
18
  #
19
19
 
20
20
  require 'ronin/model'
21
+ require 'ronin/model/importable'
21
22
  require 'ronin/url_scheme'
22
23
  require 'ronin/url_query_param'
23
24
  require 'ronin/host_name'
@@ -25,6 +26,7 @@ require 'ronin/tcp_port'
25
26
  require 'ronin/web_credential'
26
27
 
27
28
  require 'dm-timestamps'
29
+ require 'uri/generic'
28
30
  require 'uri/http'
29
31
  require 'uri/https'
30
32
  require 'uri/ftp'
@@ -37,6 +39,7 @@ module Ronin
37
39
  class URL
38
40
 
39
41
  include Model
42
+ include Model::Importable
40
43
  include DataMapper::Timestamps
41
44
 
42
45
  # Mapping of URL Schemes and URI classes
@@ -76,6 +79,45 @@ module Ronin
76
79
  # Defines the created_at timestamp
77
80
  timestamps :created_at
78
81
 
82
+ #
83
+ # Extracts URLs from the given text.
84
+ #
85
+ # @param [String] text
86
+ # The text to parse.
87
+ #
88
+ # @yield [url]
89
+ # The given block will be passed each extracted URL.
90
+ #
91
+ # @yieldparam [URL] url
92
+ # An extracted URL from the text.
93
+ #
94
+ # @return [Array<URL>]
95
+ # If no block is given, an Array of the extracted URLs is returned.
96
+ #
97
+ # @see http://rubydoc.info/stdlib/uri/URI#extract-class_method
98
+ # @see URL.parse
99
+ #
100
+ # @since 1.3.0
101
+ #
102
+ # @api public
103
+ #
104
+ def self.extract(text)
105
+ return enum_for(:extract,text).to_a unless block_given?
106
+
107
+ ::URI.extract(text) do |uri|
108
+ uri = begin
109
+ ::URI.parse(uri)
110
+ rescue URI::InvalidURIError
111
+ # URI.extract can parse URIs that URI.parse cannot handle
112
+ next
113
+ end
114
+
115
+ yield from(uri)
116
+ end
117
+
118
+ return nil
119
+ end
120
+
79
121
  #
80
122
  # Searches for all URLs using HTTP.
81
123
  #
@@ -403,7 +445,7 @@ module Ronin
403
445
  #
404
446
  def to_uri
405
447
  # map the URL scheme to a URI class
406
- url_class = (SCHEMES[self.scheme.name] || ::URI::Generic)
448
+ url_class = SCHEMES.fetch(self.scheme.name,::URI::Generic)
407
449
 
408
450
  host = if self.host_name
409
451
  self.host_name.address
data/lib/ronin/version.rb CHANGED
@@ -19,5 +19,5 @@
19
19
 
20
20
  module Ronin
21
21
  # Ronin version
22
- VERSION = '1.2.0'
22
+ VERSION = '1.3.0'
23
23
  end
data/ronin.gemspec CHANGED
@@ -99,7 +99,7 @@ Gem::Specification.new do |gemspec|
99
99
  end
100
100
 
101
101
  if gemspec.respond_to?(:required_rubygems_version=)
102
- gemspec.required_rubygems_version = metadata['required_ruby_version']
102
+ gemspec.required_rubygems_version = metadata['required_rubygems_version']
103
103
  end
104
104
 
105
105
  parse_versions = lambda { |versions|
@@ -13,6 +13,26 @@ describe EmailAddress do
13
13
  )
14
14
  }
15
15
 
16
+ describe "extract" do
17
+ subject { described_class }
18
+
19
+ let(:email1) { subject.parse('foo@bar.com') }
20
+ let(:email2) { subject.parse('foo!bar@baz.com') }
21
+ let(:text) { "To: #{email1}, #{email2}." }
22
+
23
+ it "should extract multiple email addresses from text" do
24
+ subject.extract(text).should == [email1, email2]
25
+ end
26
+
27
+ it "should yield the extracted email addresses if a block is given" do
28
+ emails = []
29
+
30
+ subject.extract(text) { |email| emails << email }
31
+
32
+ emails.should == [email1, email2]
33
+ end
34
+ end
35
+
16
36
  describe "parse" do
17
37
  it "should parse email addresses" do
18
38
  email_address = EmailAddress.parse(email)
@@ -17,6 +17,26 @@ describe HostName do
17
17
  subject.name.should == subject.address
18
18
  end
19
19
 
20
+ describe "extract" do
21
+ subject { described_class }
22
+
23
+ let(:host1) { subject.parse('www.example.com') }
24
+ let(:host2) { subject.parse('1.2.3.4.in-addr.arpa') }
25
+ let(:text) { "Hosts: #{host1}, #{host2}." }
26
+
27
+ it "should extract multiple host-names from text" do
28
+ subject.extract(text).should == [host1, host2]
29
+ end
30
+
31
+ it "should yield the extracted host-names if a block is given" do
32
+ hosts = []
33
+
34
+ subject.extract(text) { |host| hosts << host }
35
+
36
+ hosts.should == [host1, host2]
37
+ end
38
+ end
39
+
20
40
  describe "lookup" do
21
41
  subject { HostName }
22
42
 
@@ -0,0 +1,104 @@
1
+ require 'spec_helper'
2
+ require 'ronin/ip_address'
3
+
4
+ describe IPAddress do
5
+ let(:example_domain) { 'localhost' }
6
+ let(:example_ip) { '127.0.0.1' }
7
+
8
+ subject { IPAddress.new(:address => example_ip) }
9
+
10
+ it "should require an address" do
11
+ ip_address = IPAddress.new
12
+
13
+ ip_address.should_not be_valid
14
+ end
15
+
16
+ describe "extract" do
17
+ subject { IPAddress }
18
+
19
+ let(:ip1) { subject.parse('127.0.0.1') }
20
+ let(:ip2) { subject.parse('10.1.1.1') }
21
+ let(:text) { "Hosts: #{ip1}, #{ip2}" }
22
+
23
+ it "should extract multiple IP Addresses from text" do
24
+ subject.extract(text).should == [ip1, ip2]
25
+ end
26
+
27
+ it "should yield the extracted IPs if a block is given" do
28
+ ip_addresses = []
29
+
30
+ subject.extract(text) { |ip| ip_addresses << ip }
31
+
32
+ ip_addresses.should == [ip1, ip2]
33
+ end
34
+ end
35
+
36
+ describe "lookup" do
37
+ subject { IPAddress }
38
+
39
+ let(:bad_domain) { '.bad.domain.com.' }
40
+
41
+ it "should lookup host-names to IP Addresses" do
42
+ ip_addresses = subject.lookup(example_domain)
43
+ addresses = ip_addresses.map { |ip| ip.address }
44
+
45
+ addresses.should include(example_ip)
46
+ end
47
+
48
+ it "should associate the IP addresses with the original host name" do
49
+ ip_addresses = subject.lookup(example_domain)
50
+ host_names = ip_addresses.map { |ip| ip.host_names[0].address }
51
+
52
+ host_names.should include(example_domain)
53
+ end
54
+
55
+ it "should return an empty Array for unknown domain names" do
56
+ ip_addresses = subject.lookup(bad_domain)
57
+
58
+ ip_addresses.should be_empty
59
+ end
60
+ end
61
+
62
+ describe "#lookup!" do
63
+ let(:bad_ip) { '0.0.0.0' }
64
+
65
+ it "should reverse lookup the host-name for an IP Address" do
66
+ host_names = subject.lookup!
67
+ addresses = host_names.map { |host_name| host_name.address }
68
+
69
+ addresses.should include(example_domain)
70
+ end
71
+
72
+ it "should associate the host names with the original IP address" do
73
+ host_names = subject.lookup!
74
+ ip_addresses = host_names.map do |host_name|
75
+ host_name.ip_addresses[0].address
76
+ end
77
+
78
+ ip_addresses.should include(subject)
79
+ end
80
+
81
+ it "should return an empty Array for unknown domain names" do
82
+ ip_address = IPAddress.new(:address => bad_ip)
83
+ host_names = ip_address.lookup!
84
+
85
+ host_names.should be_empty
86
+ end
87
+ end
88
+
89
+ describe "#version" do
90
+ let(:ipv4) { IPAddress.new(:address => '127.0.0.1') }
91
+ let(:ipv6) { IPAddress.new(:address => '::1') }
92
+
93
+ it "should only accept 4 or 6" do
94
+ ip_address = IPAddress.new(:address => '1.1.1.1', :version => 7)
95
+
96
+ ip_address.should_not be_valid
97
+ end
98
+
99
+ it "should default to the version of the address" do
100
+ ipv4.version.should == 4
101
+ ipv6.version.should == 6
102
+ end
103
+ end
104
+ end