metasploit-runner 0.0.1 → 0.0.2

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.
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