metasploit-runner 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- Yjk2ODJjODk3YmE1YTU3OWJjMDE0MDg1NTQ0ZjAyNTkyNTBiYzVlNQ==
5
- data.tar.gz: !binary |-
6
- YWViNThkOGU2MThjYjk4OGM4NTI2NDJjM2MxODBiY2QzODU0ZDFlYg==
2
+ SHA1:
3
+ metadata.gz: 993f0fd305f9c4237449fd4da5d305e13ed4f7eb
4
+ data.tar.gz: 17d5ad62828b67045402d81f779de24addd65d45
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- NjYxZTFjNzNjODE3MmZiYzkxNmJlNTFmMDlhYjhmMzAzM2ZhZmYwYzIzNzRj
10
- ZjIzOTJmMGYwZGIwNGNhZTM5OTkwYzBhNjg1NzZiODNjYTA2NTk1MDA2YTJl
11
- NDY2NjJiODlhYTExOGQyNWUyMGZmMTY3NzhiZWUzZGE5NGM2ZGI=
12
- data.tar.gz: !binary |-
13
- YTViYjk1ODJiMzExOGNhOThiMjc0ZTBhNjJmMTJkYWQyNjg0ZDEyNzdiMGVk
14
- NzEzMzQ4M2Q4MTI0ZTViNGFhYzdmNjI4NzJmYWRkOWY0ZjI3OGI1MWRjNDYw
15
- YjExZDUyYjc3OTRhNGVjYjc1Yjg0NjAxMjBmZjJkY2FmOTJmNTU=
6
+ metadata.gz: 594805a578c9a446f1afe68f39ca830cdbecfa6ee88e195f80a5175efcd4fe747a2d2c94b25862357607609b581b94ab7c71c653dd293fc73e7447b78a398a57
7
+ data.tar.gz: 59735fe882b949673c733f84a8890e46618b947b2257d05acc10b7994e90c6cbe6ba0f4df3dfa9907112c2d53222ade2ecd7c29a2f28523eab98e3ff63fb0af4
@@ -1,3 +1,3 @@
1
1
  module MetasploitPenTestScript
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -1,56 +1,32 @@
1
1
  require 'msfrpc-client'
2
2
  require 'metasploit/constants'
3
+ require 'metasploit/exploit_run_description'
3
4
 
4
5
  module Metasploit
5
6
  module Exploit
6
7
  def Exploit.start(connection_url, port, uri, use_ssl, token, workspace_name, nexpose_console_name, device_ip_to_scan)
7
- are_parameters_valid(connection_url, token, workspace_name, device_ip_to_scan)
8
+ run_details = ExploitRunDescription.new connection_url, port, uri, use_ssl, token, workspace_name, nexpose_console_name, device_ip_to_scan
9
+ run_details.verify
8
10
 
9
- rpc_client = get_new_metasploit_rpc_connection(connection_url, port, uri, use_ssl, token)
11
+ rpc_client = get_new_metasploit_rpc_connection(run_details)
10
12
 
11
- create_workspace(rpc_client, workspace_name)
13
+ create_workspace(rpc_client, run_details.workspace_name)
12
14
 
13
- do_nexpose_import(nexpose_console_name, rpc_client, workspace_name)
15
+ do_nexpose_import(rpc_client, run_details)
14
16
 
15
- do_metasploit_scan(device_ip_to_scan, rpc_client, workspace_name)
17
+ do_metasploit_scan(rpc_client, run_details)
16
18
  end
17
19
 
18
20
  private
19
- def self.are_parameters_valid(connection_url, token, workspace_name, device_ip_to_scan)
20
- raise StandardError, CONSTANTS::REQUIRED_WORKSPACE_MESSAGE if workspace_name.nil? || workspace_name.empty?
21
- raise StandardError, CONSTANTS::REQUIRED_TOKEN_MESSAGE if token.nil? || token.empty?
22
- raise StandardError, CONSTANTS::REQUIRED_CONNECTION_URL_MESSAGE if connection_url.nil? || connection_url.empty?
23
- raise StandardError, CONSTANTS::REQUIRED_DEVICE_IP_TO_SCAN_MESSAGE if device_ip_to_scan.nil? || device_ip_to_scan.empty?
24
- end
25
-
26
- def self.get_metasploit_options(connection_url, port, uri, use_ssl, token)
27
- if port.nil? || port.empty?
28
- puts CONSTANTS::USING_DEFAULT_PORT_MESSAGE
29
- port = CONSTANTS::DEFAULT_PORT
30
- end
31
-
32
- if uri.nil? || uri.empty?
33
- puts CONSTANTS::USING_DEFAULT_URI_MESSAGE
34
- uri = CONSTANTS::DEFAULT_URI
35
- end
36
-
37
- if use_ssl != false
38
- puts CONSTANTS::USING_DEFAULT_SSL_MESSAGE
39
- use_ssl = CONSTANTS::DEFAULT_SSL
40
- end
41
-
42
- {:host => connection_url, :port => port, :token => token, :uri => uri, :ssl => use_ssl}
43
- end
44
-
45
- def self.get_new_metasploit_rpc_connection(connection_url, port, uri, use_ssl, token)
46
- client = Msf::RPC::Client.new(get_metasploit_options(connection_url, port, uri, use_ssl, token))
21
+ def self.get_new_metasploit_rpc_connection(run_details)
22
+ client = Msf::RPC::Client.new(run_details.get_options)
47
23
  puts CONSTANTS::SUCCESSFUL_CONNECTION_MESSAGE
48
24
 
49
25
  client
50
26
  end
51
27
 
52
- def self.do_nexpose_import(nexpose_console_name, rpc_client, workspace_name)
53
- import = rpc_client.call('pro.start_import', {'workspace' => workspace_name, 'DS_NEXPOSE_CONSOLE' => nexpose_console_name, 'DS_NEXPOSE_SITE' => workspace_name})
28
+ def self.do_nexpose_import(rpc_client, run_details)
29
+ import = rpc_client.call('pro.start_import', {'workspace' => run_details.workspace_name, 'DS_NEXPOSE_CONSOLE' => run_details.nexpose_console_name, 'DS_NEXPOSE_SITE' => run_details.workspace_name})
54
30
 
55
31
  wait_for_task_to_stop_running(rpc_client, CONSTANTS::IMPORTING_DATA_MESSAGE, import['task_id'])
56
32
  end
@@ -59,18 +35,8 @@ module Metasploit
59
35
  rpc_client.call('pro.workspace_add', {'name' => workspace_name})
60
36
  end
61
37
 
62
- def self.do_metasploit_scan(device_ip_to_scan, rpc_client, workspace_name)
63
- scan = rpc_client.call('pro.start_webscan', {'workspace' => workspace_name, 'DS_URLS' => "http://#{device_ip_to_scan}"})
64
-
65
- wait_for_task_to_stop_running(rpc_client, CONSTANTS::SCANNING_MESSAGE, scan['task_id'])
66
- end
67
-
68
- def self.create_workspace(rpc_client, workspace_name)
69
- rpc_client.call('pro.workspace_add', {'name' => workspace_name})
70
- end
71
-
72
- def self.do_metasploit_scan(device_ip_to_scan, rpc_client, workspace_name)
73
- scan = rpc_client.call('pro.start_webscan', {'workspace' => workspace_name, 'DS_URLS' => "http://#{device_ip_to_scan}"})
38
+ def self.do_metasploit_scan(rpc_client, run_details)
39
+ scan = rpc_client.call('pro.start_webscan', {'workspace' => run_details.workspace_name, 'DS_URLS' => run_details.device_ip_to_scan})
74
40
 
75
41
  wait_for_task_to_stop_running(rpc_client, CONSTANTS::SCANNING_MESSAGE, scan['task_id'])
76
42
  end
@@ -0,0 +1,70 @@
1
+ class ExploitRunDescription
2
+ attr_accessor :connection_url, :port, :uri, :use_ssl, :token, :workspace_name, :nexpose_console_name, :device_ip_to_scan
3
+ @@port_value = ''
4
+ @@uri_value = ''
5
+ @@use_ssl_value = ''
6
+ @@device_ip_to_scan_value = ''
7
+
8
+ def initialize(connection_url, port, uri, use_ssl, token, workspace_name, nexpose_console_name, device_ip_to_scan)
9
+ self.connection_url = connection_url
10
+ @@port_value = port
11
+ @@uri_value = uri
12
+ @@use_ssl_value = use_ssl
13
+ self.token = token
14
+ self.workspace_name = workspace_name
15
+ self.nexpose_console_name = nexpose_console_name
16
+ @@device_ip_to_scan_value = device_ip_to_scan
17
+ end
18
+
19
+ def verify
20
+ raise StandardError, CONSTANTS::REQUIRED_TOKEN_MESSAGE if token.nil? || token.empty?
21
+ raise StandardError, CONSTANTS::REQUIRED_CONNECTION_URL_MESSAGE if connection_url.nil? || connection_url.empty?
22
+ raise StandardError, CONSTANTS::REQUIRED_DEVICE_IP_TO_SCAN_MESSAGE if @@device_ip_to_scan_value.nil? || @@device_ip_to_scan_value.empty?
23
+ raise StandardError, CONSTANTS::REQUIRED_WORKSPACE_MESSAGE if workspace_name.nil? || workspace_name.empty?
24
+ end
25
+
26
+ def get_options
27
+ {:host => self.connection_url,
28
+ :port => self.port,
29
+ :token => self.token,
30
+ :uri => self.uri,
31
+ :ssl => self.use_ssl}
32
+ end
33
+
34
+ def device_ip_to_scan=(value)
35
+ @@device_ip_to_scan_value = value
36
+ end
37
+
38
+ def device_ip_to_scan
39
+ "http://#{@@device_ip_to_scan_value}"
40
+ end
41
+
42
+ def port=(value)
43
+ @@port_value = value
44
+ end
45
+
46
+ def port
47
+ get_value(@@port_value, CONSTANTS::DEFAULT_PORT)
48
+ end
49
+
50
+ def uri=(value)
51
+ @@uri_value = value
52
+ end
53
+
54
+ def uri
55
+ get_value(@@uri_value, CONSTANTS::DEFAULT_URI)
56
+ end
57
+
58
+ def use_ssl=(value)
59
+ @@use_ssl_value = value
60
+ end
61
+
62
+ def use_ssl
63
+ (@@use_ssl_value != false) ? true : false
64
+ end
65
+
66
+ def get_value(value_to_check, default)
67
+ (value_to_check.nil? || value_to_check.empty?) ? default : value_to_check
68
+ end
69
+
70
+ end
@@ -0,0 +1,135 @@
1
+ require 'metasploit/exploit_run_description'
2
+
3
+ describe 'exploit_run_description' do
4
+ describe 'start' do
5
+ before(:each) do
6
+ @expected_connection = 'http://test.connection'
7
+ @expected_token = 'testtoken'
8
+ @expected_port = '3791'
9
+ @expected_uri = '/api/1.1'
10
+ @expected_ssl = false
11
+ @expected_workspace_name = 'workspacename'
12
+ @expected_nexpose_console_name = 'nexpose_console_name'
13
+ @expected_webscan_task_id = '12'
14
+ @expected_import_task_id = '1'
15
+ @mock_rpc_client = get_mock_rpc_client
16
+ @mock_device_ip_to_scan = '127.0.0.1'
17
+ @mock_device_url_to_scan = "http://#{@mock_device_ip_to_scan}"
18
+
19
+ @exploit_run_description = ExploitRunDescription.new @expected_connection,
20
+ @expected_port,
21
+ @expected_uri,
22
+ @expected_ssl,
23
+ @expected_token,
24
+ @expected_workspace_name,
25
+ @expected_nexpose_console_name,
26
+ @mock_device_ip_to_scan
27
+ end
28
+
29
+ it 'should accept all of the needed parameters and persist them' do
30
+ expect(@exploit_run_description.connection_url).to eq(@expected_connection)
31
+ expect(@exploit_run_description.port).to eq(@expected_port)
32
+ expect(@exploit_run_description.uri).to eq(@expected_uri)
33
+ expect(@exploit_run_description.use_ssl).to eq(@expected_ssl)
34
+ expect(@exploit_run_description.token).to eq(@expected_token)
35
+ expect(@exploit_run_description.workspace_name).to eq(@expected_workspace_name)
36
+ expect(@exploit_run_description.nexpose_console_name).to eq(@expected_nexpose_console_name)
37
+ expect(@exploit_run_description.device_ip_to_scan).to eq(@mock_device_url_to_scan)
38
+ end
39
+
40
+ it 'should use 3790 as default if port is empty string' do
41
+ @exploit_run_description.port = ''
42
+ expect(@exploit_run_description.port).to eq(CONSTANTS::DEFAULT_PORT)
43
+ end
44
+
45
+ it 'should use 3790 as default if port is nil' do
46
+ @exploit_run_description.port = nil
47
+ expect(@exploit_run_description.port).to eq(CONSTANTS::DEFAULT_PORT)
48
+ end
49
+
50
+ it 'should use /api/1.0 as default if empty string' do
51
+ @exploit_run_description.uri = ''
52
+ expect(@exploit_run_description.uri).to eq(CONSTANTS::DEFAULT_URI)
53
+ end
54
+
55
+ it 'should use /api/1.0 as default if nil' do
56
+ @exploit_run_description.uri = nil
57
+ expect(@exploit_run_description.uri).to eq(CONSTANTS::DEFAULT_URI)
58
+ end
59
+
60
+ it 'should use ssl true as default if empty string is passed' do
61
+ @exploit_run_description.use_ssl = ''
62
+ expect(@exploit_run_description.use_ssl).to eq(CONSTANTS::DEFAULT_SSL)
63
+ end
64
+
65
+ it 'should use ssl true as default if random string is passed' do
66
+ @exploit_run_description.use_ssl = 'nathan is god'
67
+ expect(@exploit_run_description.use_ssl).to eq(CONSTANTS::DEFAULT_SSL)
68
+ end
69
+
70
+ it 'should use ssl true as default if nil is passed' do
71
+ @exploit_run_description.use_ssl = nil
72
+ expect(@exploit_run_description.use_ssl).to eq(CONSTANTS::DEFAULT_SSL)
73
+ end
74
+
75
+ it 'should use ssl true as default if true is passed' do
76
+ @exploit_run_description.use_ssl = true
77
+ expect(@exploit_run_description.use_ssl).to eq(CONSTANTS::DEFAULT_SSL)
78
+ end
79
+
80
+ it 'should return false if false is passed' do
81
+ @exploit_run_description.use_ssl = false
82
+ expect(@exploit_run_description.use_ssl).to eq(false)
83
+ end
84
+
85
+ describe 'get metasploit options' do
86
+ it 'should return the correct options' do
87
+ expect(@exploit_run_description.get_options).to eq({:host => @expected_connection,
88
+ :port => @expected_port,
89
+ :token => @expected_token,
90
+ :uri => @expected_uri,
91
+ :ssl => @expected_ssl})
92
+ end
93
+
94
+ it 'should be using the correct methods to fill the options' do
95
+ @exploit_run_description.port = ''
96
+ expect(@exploit_run_description.get_options).to eq({:host => @expected_connection,
97
+ :port => CONSTANTS::DEFAULT_PORT,
98
+ :token => @expected_token,
99
+ :uri => @expected_uri,
100
+ :ssl => @expected_ssl})
101
+ end
102
+ end
103
+
104
+ describe 'verify' do
105
+ it 'should throw an error if no token' do
106
+ @exploit_run_description.token = ''
107
+ expect { @exploit_run_description.verify }.to raise_error(StandardError, CONSTANTS::REQUIRED_TOKEN_MESSAGE)
108
+ @exploit_run_description.token = nil
109
+ expect { @exploit_run_description.verify }.to raise_error(StandardError, CONSTANTS::REQUIRED_TOKEN_MESSAGE)
110
+ end
111
+
112
+ it 'should throw an error if no connection url' do
113
+ @exploit_run_description.connection_url = ''
114
+ expect { @exploit_run_description.verify }.to raise_error(StandardError, CONSTANTS::REQUIRED_CONNECTION_URL_MESSAGE)
115
+ @exploit_run_description.connection_url = nil
116
+ expect { @exploit_run_description.verify }.to raise_error(StandardError, CONSTANTS::REQUIRED_CONNECTION_URL_MESSAGE)
117
+ end
118
+
119
+
120
+ it 'should throw an error if no device ip' do
121
+ @exploit_run_description.device_ip_to_scan = ''
122
+ expect { @exploit_run_description.verify }.to raise_error(StandardError, CONSTANTS::REQUIRED_DEVICE_IP_TO_SCAN_MESSAGE)
123
+ @exploit_run_description.device_ip_to_scan = nil
124
+ expect { @exploit_run_description.verify }.to raise_error(StandardError, CONSTANTS::REQUIRED_DEVICE_IP_TO_SCAN_MESSAGE)
125
+ end
126
+
127
+ it 'should throw an error if workspace' do
128
+ @exploit_run_description.workspace_name = ''
129
+ expect { @exploit_run_description.verify }.to raise_error(StandardError, CONSTANTS::REQUIRED_WORKSPACE_MESSAGE)
130
+ @exploit_run_description.workspace_name = nil
131
+ expect { @exploit_run_description.verify }.to raise_error(StandardError, CONSTANTS::REQUIRED_WORKSPACE_MESSAGE)
132
+ end
133
+ end
134
+ end
135
+ end
data/spec/exploit_spec.rb CHANGED
@@ -56,76 +56,6 @@ describe 'exploit' do
56
56
 
57
57
  Metasploit::Exploit.start(@expected_connection, '', @expected_uri, @expected_ssl, @expected_token, @expected_workspace_name, '', @mock_device_ip_to_scan)
58
58
  end
59
-
60
- it 'should use 3790 as default if port is nil' do
61
- expected_options = get_default_options_and_override({:port => '3790'})
62
-
63
- expect(Msf::RPC::Client).to receive(:new)
64
- .with(expected_options)
65
- .and_return(@mock_rpc_client)
66
-
67
- Metasploit::Exploit.start(@expected_connection, nil, @expected_uri, @expected_ssl, @expected_token, @expected_workspace_name, '', @mock_device_ip_to_scan)
68
- end
69
-
70
- it 'should use /api/1.0 as default if no uri is passed' do
71
- expected_options = get_default_options_and_override({:uri => '/api/1.0'})
72
-
73
- expect(Msf::RPC::Client).to receive(:new)
74
- .with(expected_options)
75
- .and_return(@mock_rpc_client)
76
-
77
- Metasploit::Exploit.start(@expected_connection, @expected_port, '', @expected_ssl, @expected_token, @expected_workspace_name, '', @mock_device_ip_to_scan)
78
- end
79
-
80
- it 'should use /api/1.0 as default if no uri is passed' do
81
- expected_options = get_default_options_and_override({:uri => '/api/1.0'})
82
-
83
- expect(Msf::RPC::Client).to receive(:new)
84
- .with(expected_options)
85
- .and_return(@mock_rpc_client)
86
-
87
- Metasploit::Exploit.start(@expected_connection, @expected_port, nil, @expected_ssl, @expected_token, @expected_workspace_name, '', @mock_device_ip_to_scan)
88
- end
89
-
90
- it 'should use ssl true as default if empty string is passed' do
91
- expected_options = get_default_options_and_override({:ssl => true})
92
-
93
- expect(Msf::RPC::Client).to receive(:new)
94
- .with(expected_options)
95
- .and_return(@mock_rpc_client)
96
-
97
- Metasploit::Exploit.start(@expected_connection, @expected_port, @expected_uri, '', @expected_token, @expected_workspace_name, '', @mock_device_ip_to_scan)
98
- end
99
-
100
- it 'should use ssl true as default if random string is passed' do
101
- expected_options = get_default_options_and_override({:ssl => true})
102
-
103
- expect(Msf::RPC::Client).to receive(:new)
104
- .with(expected_options)
105
- .and_return(@mock_rpc_client)
106
-
107
- Metasploit::Exploit.start(@expected_connection, @expected_port, @expected_uri, 'sadf', @expected_token, @expected_workspace_name, '', @mock_device_ip_to_scan)
108
- end
109
-
110
- it 'should use ssl true as default if nil is passed' do
111
- expected_options = get_default_options_and_override({:ssl => true})
112
-
113
- expect(Msf::RPC::Client).to receive(:new)
114
- .with(expected_options)
115
- .and_return(@mock_rpc_client)
116
-
117
- Metasploit::Exploit.start(@expected_connection, @expected_port, @expected_uri, nil, @expected_token, @expected_workspace_name, '', @mock_device_ip_to_scan)
118
- end
119
-
120
- it 'should use ssl true as default if true is passed' do
121
- expected_options = get_default_options_and_override({:ssl => true})
122
-
123
- expect(Msf::RPC::Client).to receive(:new)
124
- .with(expected_options)
125
- .and_return(@mock_rpc_client)
126
-
127
- Metasploit::Exploit.start(@expected_connection, @expected_port, @expected_uri, true, @expected_token, @expected_workspace_name, '', @mock_device_ip_to_scan)
128
- end
129
59
  end
130
60
 
131
61
  describe 'create workspace' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: metasploit-runner
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan Gibson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-22 00:00:00.000000000 Z
11
+ date: 2014-08-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msfrpc-client
@@ -28,28 +28,28 @@ dependencies:
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ~>
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '1.6'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ~>
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.6'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ! '>='
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ! '>='
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
@@ -74,8 +74,8 @@ executables:
74
74
  extensions: []
75
75
  extra_rdoc_files: []
76
76
  files:
77
- - .gitignore
78
- - .rspec
77
+ - ".gitignore"
78
+ - ".rspec"
79
79
  - Gemfile
80
80
  - LICENSE.txt
81
81
  - MetasploitPenTestScript.gemspec
@@ -86,6 +86,8 @@ files:
86
86
  - lib/MetasploitPenTestScript/version.rb
87
87
  - lib/metasploit/constants.rb
88
88
  - lib/metasploit/exploit.rb
89
+ - lib/metasploit/exploit_run_description.rb
90
+ - spec/exploit_run_description_spec.rb
89
91
  - spec/exploit_spec.rb
90
92
  - spec/spec_helper.rb
91
93
  homepage: ''
@@ -98,12 +100,12 @@ require_paths:
98
100
  - lib
99
101
  required_ruby_version: !ruby/object:Gem::Requirement
100
102
  requirements:
101
- - - ! '>='
103
+ - - ">="
102
104
  - !ruby/object:Gem::Version
103
105
  version: '0'
104
106
  required_rubygems_version: !ruby/object:Gem::Requirement
105
107
  requirements:
106
- - - ! '>='
108
+ - - ">="
107
109
  - !ruby/object:Gem::Version
108
110
  version: '0'
109
111
  requirements: []
@@ -113,5 +115,6 @@ signing_key:
113
115
  specification_version: 4
114
116
  summary: Script to run automated Metaspolit Penetration Tests.
115
117
  test_files:
118
+ - spec/exploit_run_description_spec.rb
116
119
  - spec/exploit_spec.rb
117
120
  - spec/spec_helper.rb