bettercap 1.1.3 → 1.1.4

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTING.md +42 -0
  3. data/Gemfile +2 -0
  4. data/Gemfile.lock +25 -0
  5. data/README.md +1 -1
  6. data/Rakefile +7 -0
  7. data/TODO.md +2 -2
  8. data/bettercap.gemspec +2 -0
  9. data/bin/bettercap +48 -43
  10. data/lib/bettercap/context.rb +125 -20
  11. data/lib/bettercap/factories/firewall_factory.rb +4 -0
  12. data/lib/bettercap/factories/parser_factory.rb +2 -0
  13. data/lib/bettercap/firewalls/linux.rb +16 -10
  14. data/lib/bettercap/firewalls/osx.rb +19 -8
  15. data/lib/bettercap/logger.rb +2 -0
  16. data/lib/bettercap/network.rb +10 -4
  17. data/lib/bettercap/proxy/certstore.rb +68 -0
  18. data/lib/bettercap/proxy/proxy.rb +87 -43
  19. data/lib/bettercap/proxy/request.rb +22 -4
  20. data/lib/bettercap/proxy/response.rb +15 -0
  21. data/lib/bettercap/sniffer/sniffer.rb +22 -24
  22. data/lib/bettercap/spoofers/arp.rb +38 -6
  23. data/lib/bettercap/target.rb +1 -1
  24. data/lib/bettercap/version.rb +1 -1
  25. data/lib/bettercap.rb +1 -0
  26. data/test/factories/firewall_factory_test.rb +54 -0
  27. data/test/factories/parser_factory_test.rb +36 -0
  28. data/test/factories/spoofer_factory_test.rb +15 -0
  29. data/test/firewalls/linux_firewall_test.rb +72 -0
  30. data/test/firewalls/osx_firewall_test.rb +72 -0
  31. data/test/helpers/mock_shell.rb +17 -0
  32. data/test/logger_test.rb +12 -0
  33. data/test/network_test.rb +14 -0
  34. data/test/pcap/ftp.pcap +0 -0
  35. data/test/pcap/http.pcap +0 -0
  36. data/test/pcap/packets.pcap +0 -0
  37. data/test/proxy/response_test.rb +56 -0
  38. data/test/shell_test.rb +15 -0
  39. data/test/sniffer/parsers/base_parser_test.rb +20 -0
  40. data/test/sniffer/parsers/ftp_parser_test.rb +27 -0
  41. data/test/sniffer/parsers/url_parser_test.rb +25 -0
  42. data/test/target_test.rb +24 -0
  43. data/test/test_helper.rb +47 -0
  44. data/test_https_proxy.rb +29 -0
  45. metadata +40 -2
@@ -0,0 +1,68 @@
1
+ =begin
2
+
3
+ BETTERCAP
4
+
5
+ Author : Simone 'evilsocket' Margaritelli
6
+ Email : evilsocket@gmail.com
7
+ Blog : http://www.evilsocket.net/
8
+
9
+ This project is released under the GPL 3 license.
10
+
11
+ =end
12
+ require 'bettercap/logger'
13
+ require 'openssl'
14
+
15
+ module Proxy
16
+ class CertStore
17
+ @@selfsigned = {}
18
+ @@frompems = {}
19
+
20
+ def self.from_file( filename )
21
+ if !@@frompems.has_key? filename
22
+ Logger.info "Loading self signed HTTPS certificate from '#{filename}' ..."
23
+
24
+ pem = File.read filename
25
+
26
+ @@frompems[filename] = { :cert => OpenSSL::X509::Certificate.new(pem), :key => OpenSSL::PKey::RSA.new(pem) }
27
+ end
28
+
29
+ @@frompems[filename]
30
+ end
31
+
32
+ # TODO: Put a better default subject here!
33
+ def self.get_selfsigned( subject = '/C=BE/O=Test/OU=Test/CN=Test' )
34
+ if !@@selfsigned.has_key? subject
35
+ Logger.info "Generating self signed HTTPS certificate for subject '#{subject}' ..."
36
+
37
+ key = OpenSSL::PKey::RSA.new(2048)
38
+ public_key = key.public_key
39
+
40
+ cert = OpenSSL::X509::Certificate.new
41
+ cert.subject = cert.issuer = OpenSSL::X509::Name.parse(subject)
42
+ cert.not_before = Time.now
43
+ cert.not_after = Time.now + 365 * 24 * 60 * 60
44
+ cert.public_key = public_key
45
+ cert.serial = 0x0
46
+ cert.version = 2
47
+
48
+ ef = OpenSSL::X509::ExtensionFactory.new
49
+ ef.subject_certificate = cert
50
+ ef.issuer_certificate = cert
51
+ cert.extensions = [
52
+ ef.create_extension("basicConstraints","CA:TRUE", true),
53
+ ef.create_extension("subjectKeyIdentifier", "hash"),
54
+ ef.create_extension("keyUsage", "cRLSign,keyCertSign", true),
55
+ ]
56
+ cert.add_extension ef.create_extension("authorityKeyIdentifier",
57
+ "keyid:always,issuer:always")
58
+
59
+ cert.sign key, OpenSSL::Digest::SHA256.new
60
+
61
+ @@selfsigned[subject] = { :cert => cert, :key => key }
62
+ end
63
+
64
+ @@selfsigned[subject]
65
+ end
66
+ end
67
+
68
+ end
@@ -19,10 +19,14 @@ require 'bettercap/network'
19
19
  module Proxy
20
20
 
21
21
  class Proxy
22
- def initialize address, port, &processor
22
+ def initialize( address, port, is_https, processor )
23
23
  @socket = nil
24
24
  @address = address
25
25
  @port = port
26
+ @is_https = is_https
27
+ @type = is_https ? 'HTTPS' : 'HTTP'
28
+ @sslserver = nil
29
+ @sslcontext = nil
26
30
  @main_thread = nil
27
31
  @running = false
28
32
  @processor = processor
@@ -31,7 +35,7 @@ class Proxy
31
35
  begin
32
36
  @local_ips = Socket.ip_address_list.collect { |x| x.ip_address }
33
37
  rescue
34
- Logget.warn 'Could not get local ips using Socket module, using Network.get_local_ips method.'
38
+ Logger.warn 'Could not get local ips using Socket module, using Network.get_local_ips method.'
35
39
 
36
40
  @local_ips = Network.get_local_ips
37
41
  end
@@ -40,16 +44,27 @@ class Proxy
40
44
  def start
41
45
  begin
42
46
  @socket = TCPServer.new( @address, @port )
47
+
48
+ if @is_https
49
+ cert = Context.get.certificate
50
+
51
+ @sslcontext = OpenSSL::SSL::SSLContext.new
52
+ @sslcontext.cert = cert[:cert]
53
+ @sslcontext.key = cert[:key]
54
+
55
+ @sslserver = OpenSSL::SSL::SSLServer.new( @socket, @sslcontext )
56
+ end
57
+
43
58
  @main_thread = Thread.new &method(:server_thread)
44
59
  rescue Exception => e
45
- Logger.error "Error starting proxy: #{e.inspect}"
60
+ Logger.error "Error starting #{@type} proxy: #{e.inspect}"
46
61
  @socket.close unless @socket.nil?
47
62
  end
48
63
  end
49
64
 
50
65
  def stop
51
66
  begin
52
- Logger.info 'Stopping proxy ...'
67
+ Logger.info "Stopping #{@type} proxy ..."
53
68
 
54
69
  if @socket and @running
55
70
  @running = false
@@ -61,25 +76,37 @@ class Proxy
61
76
 
62
77
  private
63
78
 
79
+ def async_accept
80
+ if @is_https
81
+ begin
82
+ Thread.new @sslserver.accept, &method(:client_thread)
83
+ rescue Exception => e
84
+ Logger.warn "Error while accepting #{@type} connection: #{e.inspect}"
85
+ end
86
+ else
87
+ Thread.new @socket.accept, &method(:client_thread)
88
+ end
89
+ end
90
+
64
91
  def server_thread
65
- Logger.info "Proxy started on #{@address}:#{@port} ...\n"
92
+ Logger.info "#{@type} Proxy started on #{@address}:#{@port} ...\n"
66
93
 
67
94
  @running = true
68
95
 
69
96
  begin
70
97
  while @running do
71
- Thread.new @socket.accept, &method(:client_thread)
98
+ async_accept
72
99
  end
73
100
  rescue Exception => e
74
101
  if @running
75
- Logger.warn "Error while accepting connection: #{e.inspect}"
102
+ Logger.error "Error while accepting #{@type} connection: #{e.inspect}"
76
103
  end
77
104
  ensure
78
105
  @socket.close unless @socket.nil?
79
106
  end
80
107
  end
81
108
 
82
- def binary_streaming from, to, opts = {}
109
+ def binary_streaming( from, to, opts = {} )
83
110
 
84
111
  total_size = 0
85
112
 
@@ -125,8 +152,8 @@ class Proxy
125
152
  end
126
153
  end
127
154
 
128
- def html_streaming request, response, from, to
129
- buff = ""
155
+ def html_streaming( request, response, from, to )
156
+ buff = ''
130
157
  loop do
131
158
  from.read 1024, buff
132
159
 
@@ -141,12 +168,12 @@ class Proxy
141
168
  to.write response.to_s
142
169
  end
143
170
 
144
- def log_stream client, request, response
171
+ def log_stream( client, request, response )
145
172
  client_s = "[#{client}]"
146
173
  verb_s = request.verb
147
- request_s = "http://#{request.host}#{request.url}"
174
+ request_s = "#{@is_https ? 'https' : 'http'}://#{request.host}#{request.url}"
148
175
  response_s = "( #{response.content_type} )"
149
- request_s = request_s.slice(0..50) + "..." unless request_s.length <= 50
176
+ request_s = request_s.slice(0..50) + '...' unless request_s.length <= 50
150
177
 
151
178
  verb_s = verb_s.light_blue
152
179
 
@@ -165,36 +192,56 @@ class Proxy
165
192
  Logger.write "#{client_s} #{verb_s} #{request_s} #{response_s}"
166
193
  end
167
194
 
168
- def is_self_request? request
195
+ def is_self_request?(request)
169
196
  @local_ips.include? IPSocket.getaddress(request.host)
170
197
  end
171
198
 
172
- def rickroll_lamer client
199
+ def rickroll_lamer(client)
173
200
  client.write "HTTP/1.1 302 Found\n"
174
201
  client.write "Location: https://www.youtube.com/watch?v=dQw4w9WgXcQ\n\n"
175
202
  end
176
203
 
177
- def client_thread client
178
- client_port, client_ip = Socket.unpack_sockaddr_in(client.getpeername)
179
- Logger.debug "New connection from #{client_ip}:#{client_port}"
204
+ def create_upstream_connection( request )
205
+ if @is_https
206
+ sock = TCPSocket.new( request.host, request.port )
180
207
 
181
- server = nil
182
- request = Request.new
208
+ ctx = OpenSSL::SSL::SSLContext.new
183
209
 
184
- begin
185
- # read the first line
186
- request << client.readline
187
-
188
- loop do
189
- line = client.readline
190
- request << line
210
+ # do we need this? :P ctx.set_params(verify_mode: OpenSSL::SSL::VERIFY_PEER)
191
211
 
192
- if line.chomp == ""
193
- break
194
- end
212
+ socket = OpenSSL::SSL::SSLSocket.new(sock, ctx).tap do |socket|
213
+ socket.sync_close = true
214
+ socket.connect
195
215
  end
196
216
 
197
- raise "Couldn't extract host from the request." unless request.host
217
+ socket
218
+ else
219
+ TCPSocket.new( request.host, request.port )
220
+ end
221
+ end
222
+
223
+ def get_client_details( client )
224
+ if !@is_https
225
+ client_port, client_ip = Socket.unpack_sockaddr_in(client.getpeername)
226
+ else
227
+ _, client_port, _, client_ip = client.peeraddr
228
+ end
229
+
230
+ [ client_ip, client_port ]
231
+ end
232
+
233
+ def client_thread( client )
234
+ client_ip, client_port = get_client_details client
235
+
236
+ Logger.debug "New #{@type} connection from #{client_ip}:#{client_port}"
237
+
238
+ server = nil
239
+ request = Request.new @is_https ? 443 : 80
240
+
241
+ begin
242
+ Logger.debug 'Reading request ...'
243
+
244
+ request.read client
198
245
 
199
246
  # someone is having fun with us =)
200
247
  if is_self_request? request
@@ -203,9 +250,15 @@ class Proxy
203
250
 
204
251
  rickroll_lamer client
205
252
 
253
+ elsif request.verb == 'CONNECT'
254
+
255
+ Logger.error "You're using bettercap as a normal HTTP(S) proxy, it wasn't designed to handle CONNECT requests:\n\n#{request.to_s}"
256
+
206
257
  else
207
258
 
208
- server = TCPSocket.new( request.host, request.port )
259
+ Logger.debug 'Creating upstream connection ...'
260
+
261
+ server = create_upstream_connection request
209
262
 
210
263
  server.write request.to_s
211
264
 
@@ -218,16 +271,7 @@ class Proxy
218
271
 
219
272
  Logger.debug 'Reading response ...'
220
273
 
221
- response = Response.new
222
-
223
- # read all response headers
224
- loop do
225
- line = server.readline
226
-
227
- response << line
228
-
229
- break unless not response.headers_done
230
- end
274
+ response = Response.from_socket server
231
275
 
232
276
  if response.textual?
233
277
  log_stream client_ip, request, response
@@ -243,7 +287,7 @@ class Proxy
243
287
  binary_streaming server, client, response: response
244
288
  end
245
289
 
246
- Logger.debug "#{client_ip}:#{client_port} served."
290
+ Logger.debug "#{@type} client served."
247
291
  end
248
292
 
249
293
  rescue Exception => e
@@ -15,18 +15,36 @@ module Proxy
15
15
  class Request
16
16
  attr_reader :lines, :verb, :url, :host, :port, :content_length
17
17
 
18
- def initialize
18
+ def initialize( default_port = 80 )
19
19
  @lines = []
20
20
  @verb = nil
21
21
  @url = nil
22
22
  @host = nil
23
- @port = 80
23
+ @port = default_port
24
24
  @content_length = 0
25
25
  end
26
26
 
27
+ def read(sock)
28
+ # read the first line
29
+ self << sock.readline
30
+
31
+ loop do
32
+ line = sock.readline
33
+ self << line
34
+
35
+ if line.chomp == ''
36
+ break
37
+ end
38
+ end
39
+
40
+ raise "Couldn't extract host from the request." unless @host
41
+ end
42
+
27
43
  def <<(line)
28
44
  line = line.chomp
29
45
 
46
+ Logger.debug " REQUEST LINE: '#{line}'"
47
+
30
48
  # is this the first line '<VERB> <URI> HTTP/<VERSION>' ?
31
49
  if @url.nil? and line =~ /^(\w+)\s+(\S+)\s+HTTP\/[\d\.]+\s*$/
32
50
  @verb = $1
@@ -35,13 +53,13 @@ class Request
35
53
  # fix url
36
54
  if @url.include? '://'
37
55
  uri = URI::parse @url
38
- @url = "#{uri.path}" + ( uri.query ? "?#{uri.query}" : "" )
56
+ @url = "#{uri.path}" + ( uri.query ? "?#{uri.query}" : '' )
39
57
  end
40
58
 
41
59
  line = "#{@verb} #{url} HTTP/1.0"
42
60
 
43
61
  # get the host header value
44
- elsif line =~ /^Host: (.*)$/
62
+ elsif line =~ /^Host:\s*(.*)$/
45
63
  @host = $1
46
64
  if host =~ /([^:]*):([0-9]*)$/
47
65
  @host = $1
@@ -25,6 +25,21 @@ class Response
25
25
  @headers_done = false
26
26
  end
27
27
 
28
+ def self.from_socket(sock)
29
+ response = Response.new
30
+
31
+ # read all response headers
32
+ loop do
33
+ line = sock.readline
34
+
35
+ response << line
36
+
37
+ break unless not response.headers_done
38
+ end
39
+
40
+ response
41
+ end
42
+
28
43
  def <<(line)
29
44
  # we already parsed the heders, collect response body
30
45
  if @headers_done
@@ -28,40 +28,38 @@ class Sniffer
28
28
  setup( ctx )
29
29
 
30
30
  @@cap.stream.each do |p|
31
- append_packet p
32
- parse_packet p
31
+ begin
32
+ parsed = Packet.parse p
33
+ rescue Exception => e
34
+ parsed = nil
35
+ Logger.debug e.message
36
+ end
37
+
38
+ if not parsed.nil? and parsed.is_ip? and !skip_packet?(parsed)
39
+ append_packet p
40
+ parse_packet parsed
41
+ end
33
42
  end
34
43
  end
35
44
 
36
45
  private
37
46
 
38
- def self.parse_packet( p )
39
- begin
40
- pkt = Packet.parse p
41
- rescue Exception => e
42
- pkt = nil
43
- Logger.debug e.message
44
- end
47
+ def self.skip_packet?( pkt )
48
+ !@@ctx.options[:local] and
49
+ ( pkt.ip_saddr == @@ctx.ifconfig[:ip_saddr] or
50
+ pkt.ip_daddr == @@ctx.ifconfig[:ip_saddr] )
51
+ end
45
52
 
46
- if not pkt.nil? and pkt.is_ip?
47
- if !skip_packet? pkt
48
- @@parsers.each do |parser|
49
- begin
50
- parser.on_packet pkt
51
- rescue Exception => e
52
- Logger.warn e.message
53
- end
54
- end
53
+ def self.parse_packet( parsed )
54
+ @@parsers.each do |parser|
55
+ begin
56
+ parser.on_packet parsed
57
+ rescue Exception => e
58
+ Logger.warn e.message
55
59
  end
56
60
  end
57
61
  end
58
62
 
59
- def self.skip_packet?( pkt )
60
- !@@ctx.options[:local] and
61
- ( pkt.ip_saddr == @@ctx.ifconfig[:ip_saddr] or
62
- pkt.ip_daddr == @@ctx.ifconfig[:ip_saddr] )
63
- end
64
-
65
63
  def self.append_packet( p )
66
64
  begin
67
65
  @@pcap.array_to_file(
@@ -22,6 +22,8 @@ class ArpSpoofer < ISpoofer
22
22
  @gw_hw = nil
23
23
  @forwarding = @ctx.firewall.forwarding_enabled?
24
24
  @spoof_thread = nil
25
+ @sniff_thread = nil
26
+ @capture = nil
25
27
  @running = false
26
28
 
27
29
  Logger.debug 'ARP SPOOFER SELECTED'
@@ -35,7 +37,7 @@ class ArpSpoofer < ISpoofer
35
37
  Logger.info " Gateway : #{@ctx.gateway} ( #{@gw_hw} )"
36
38
  end
37
39
 
38
- def send_spoofed_packed( saddr, smac, daddr, dmac )
40
+ def send_spoofed_packet( saddr, smac, daddr, dmac )
39
41
  pkt = PacketFu::ARPPacket.new
40
42
  pkt.eth_saddr = smac
41
43
  pkt.eth_daddr = dmac
@@ -51,7 +53,7 @@ class ArpSpoofer < ISpoofer
51
53
  def start
52
54
  stop() unless @running == false
53
55
 
54
- Logger.info 'Starting ARP spoofer ...'
56
+ Logger.info "Starting ARP spoofer ( #{@ctx.options[:half_duplex] ? 'Half' : 'Full'} Duplex ) ..."
55
57
 
56
58
  if @forwarding == false
57
59
  Logger.debug 'Enabling packet forwarding.'
@@ -60,6 +62,36 @@ class ArpSpoofer < ISpoofer
60
62
  end
61
63
 
62
64
  @running = true
65
+ @sniff_thread = Thread.new do
66
+ Logger.info 'ARP watcher started ...'
67
+ begin
68
+ @capture = PacketFu::Capture.new(
69
+ iface: @ctx.options[:iface],
70
+ filter: 'arp',
71
+ start: true
72
+ )
73
+ rescue Exception => e
74
+ Logger.error e.message
75
+ end
76
+ @capture.stream.each do |p|
77
+ begin
78
+ pkt = PacketFu::Packet.parse p
79
+ # we're only interested in 'who-has' packets
80
+ if pkt.arp_opcode == 1 and pkt.arp_dst_mac.to_s == '00:00:00:00:00:00'
81
+ is_from_us = ( pkt.arp_src_ip.to_s == @ctx.ifconfig[:ip_saddr] )
82
+
83
+ if !is_from_us
84
+ Logger.info "[ARP] #{pkt.arp_src_ip.to_s} is asking who #{pkt.arp_dst_ip.to_s} is."
85
+
86
+ send_spoofed_packet pkt.arp_dst_ip.to_s, @ctx.ifconfig[:eth_saddr], pkt.arp_src_ip.to_s, pkt.arp_src_mac.to_s
87
+ end
88
+ end
89
+ rescue Exception => e
90
+ Logger.error e.message
91
+ end
92
+ end
93
+ end
94
+
63
95
  @spoof_thread = Thread.new do
64
96
  prev_size = @ctx.targets.size
65
97
  loop do
@@ -95,8 +127,8 @@ class ArpSpoofer < ISpoofer
95
127
  end
96
128
  end
97
129
 
98
- send_spoofed_packed @ctx.gateway, @ctx.ifconfig[:eth_saddr], target.ip, target.mac
99
- send_spoofed_packed target.ip, @ctx.ifconfig[:eth_saddr], @ctx.gateway, @gw_hw
130
+ send_spoofed_packet( @ctx.gateway, @ctx.ifconfig[:eth_saddr], target.ip, target.mac )
131
+ send_spoofed_packet( target.ip, @ctx.ifconfig[:eth_saddr], @ctx.gateway, @gw_hw ) unless @ctx.options[:half_duplex]
100
132
  end
101
133
 
102
134
  prev_size = @ctx.targets.size
@@ -121,8 +153,8 @@ class ArpSpoofer < ISpoofer
121
153
 
122
154
  @ctx.targets.each do |target|
123
155
  if !target.mac.nil?
124
- send_spoofed_packed @ctx.gateway, @gw_hw, target.ip, target.mac
125
- send_spoofed_packed target.ip, target.mac, @ctx.gateway, @gw_hw
156
+ send_spoofed_packet( @ctx.gateway, @gw_hw, target.ip, target.mac )
157
+ send_spoofed_packet( target.ip, target.mac, @ctx.gateway, @gw_hw ) unless @ctx.options[:half_duplex]
126
158
  end
127
159
  end
128
160
  sleep 1
@@ -16,7 +16,7 @@ class Target
16
16
 
17
17
  @@prefixes = nil
18
18
 
19
- def initialize( ip, mac )
19
+ def initialize( ip, mac=nil )
20
20
  @ip = ip
21
21
  @mac = mac
22
22
  @vendor = Target.lookup_vendor(mac) if not mac.nil?
@@ -10,6 +10,6 @@ This project is released under the GPL 3 license.
10
10
 
11
11
  =end
12
12
  module BetterCap
13
- VERSION = '1.1.3'
13
+ VERSION = '1.1.4'
14
14
  BANNER = File.read( File.dirname(__FILE__) + '/banner' ).gsub( '#VERSION#', "v#{VERSION}")
15
15
  end
data/lib/bettercap.rb CHANGED
@@ -36,4 +36,5 @@ require 'bettercap/proxy/request'
36
36
  require 'bettercap/proxy/response'
37
37
  require 'bettercap/proxy/proxy'
38
38
  require 'bettercap/proxy/module'
39
+ require 'bettercap/proxy/certstore'
39
40
  require 'bettercap/httpd/server'
@@ -0,0 +1,54 @@
1
+ require 'minitest/autorun'
2
+ require 'factories/firewall_factory'
3
+
4
+ class FirewallFactoryTest < MiniTest::Test
5
+ # TODO: Fix the tests for the Mac and Linux firewall initialization. Right now
6
+ # they are being created in a way which executes a shell command, causing
7
+ # tests to fail.
8
+
9
+ # def test_mac_firewall
10
+ # FirewallFactory.clear_firewall
11
+ #
12
+ # override_ruby_platform('darwin') do
13
+ # firewall = FirewallFactory.get_firewall
14
+ # assert_equal firewall.class, OSXFirewall
15
+ # end
16
+ # end
17
+
18
+ # def test_linux_firewall
19
+ # FirewallFactory.clear_firewall
20
+ #
21
+ # override_ruby_platform('linux') do
22
+ # firewall = FirewallFactory.get_firewall
23
+ # assert_equal firewall.class, LinuxFirewall
24
+ # end
25
+ # end
26
+
27
+ def test_unknown_firewall
28
+ FirewallFactory.clear_firewall
29
+
30
+ override_ruby_platform('ms') do
31
+ assert_raises BetterCap::Error do
32
+ FirewallFactory.get_firewall
33
+ end
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def override_ruby_platform(platform)
40
+ actual_platform = RUBY_PLATFORM
41
+
42
+ begin
43
+ redefine_const :RUBY_PLATFORM, platform
44
+ yield
45
+ ensure
46
+ redefine_const :RUBY_PLATFORM, actual_platform
47
+ end
48
+ end
49
+
50
+ def redefine_const(const, value)
51
+ Object.send(:remove_const, const) if Object.const_defined?(const)
52
+ Object.const_set(const, value)
53
+ end
54
+ end
@@ -0,0 +1,36 @@
1
+ require 'minitest/autorun'
2
+ require 'factories/parser_factory'
3
+
4
+ class ParserFactoryTest < MiniTest::Test
5
+ def test_getting_available_parsers
6
+ available_parsers = ParserFactory.available
7
+ assert available_parsers.include?('FTP')
8
+ end
9
+
10
+ def test_successful_cmdline_parser_name
11
+ parsers = ParserFactory.from_cmdline('ftp,https')
12
+ assert_equal parsers, ['FTP', 'HTTPS']
13
+ end
14
+
15
+ def test_failed_cmdline_parser_name
16
+ assert_raises BetterCap::Error do
17
+ ParserFactory.from_cmdline 'unknown'
18
+ end
19
+ end
20
+
21
+ def test_no_cmdline_parser_provided
22
+ assert_raises BetterCap::Error do
23
+ ParserFactory.from_cmdline nil
24
+ end
25
+ end
26
+
27
+ def test_successfully_loading_parsers
28
+ loaded = ParserFactory.load_by_names 'FTP'
29
+ assert_equal loaded.first.class, FtpParser
30
+ end
31
+
32
+ def test_unsuccessfully_loading_parsers
33
+ loaded = ParserFactory.load_by_names 'unknown'
34
+ assert_empty loaded
35
+ end
36
+ end
@@ -0,0 +1,15 @@
1
+ require 'minitest/autorun'
2
+ require 'factories/spoofer_factory'
3
+
4
+ class SpooferFactoryTest < MiniTest::Test
5
+ def test_getting_available_parsers
6
+ spoofers = SpooferFactory.available
7
+ assert spoofers.include?('ARP')
8
+ end
9
+
10
+ def test_unsuccessful_name
11
+ assert_raises BetterCap::Error do
12
+ SpooferFactory.get_by_name 'unknown'
13
+ end
14
+ end
15
+ end