NexposeRunner 0.0.5 → 0.0.7
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 +4 -4
- data/.gitignore +2 -0
- data/README.md +2 -0
- data/config/scan.yml.example +7 -0
- data/lib/NexposeRunner/version.rb +1 -1
- data/lib/nexpose-runner/constants.rb +2 -1
- data/lib/nexpose-runner/scan.rb +12 -2
- data/lib/nexpose-runner/scan_run_description.rb +6 -0
- data/spec/scan_config_spec.rb +57 -0
- data/spec/scan_spec.rb +33 -10
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9a8816db77936253046c6e2858d14c794eaa3291
|
4
|
+
data.tar.gz: 21121f0ae8c931b390e9ddd239da015a008e7825
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bfcf436db720bcab7f61126360b0b19056957b1a49a9bb12bed94db7a40c260491da61c46bf6d6e0a754c69f4ac42150a54aab6ad67be8902bda2a45e882f077
|
7
|
+
data.tar.gz: 0b5d1dafd6fbe50022a8b0ac8d07a6a774331663d393bb5484678274917beb595b7a37902ff5f7c9bcb4dcf2fcbdb58b3f9aacb5993702430cccc2e36a8edd4b
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -33,6 +33,8 @@ EXAMPLE:
|
|
33
33
|
|
34
34
|
$ scan "http://test.connection" "rapid7" "password" "3780" "my_cool_software_build-28" "10.5.0.15" "full-audit-widget-corp"
|
35
35
|
|
36
|
+
It is possible to use a YAML file to drive the configuration of this module. An example configuration file is provided in config/scan.yml.example. Simply copy it to config/scan.yml and modify it to work with your environment.
|
37
|
+
|
36
38
|
## Contributing
|
37
39
|
|
38
40
|
1. Fork it ( https://github.com/[my-github-username]/nexpose-scan/fork )
|
@@ -10,6 +10,7 @@ module CONSTANTS
|
|
10
10
|
VULNERABILITY_REPORT_NAME = 'nexpose-vulnerability-report.csv'
|
11
11
|
SOFTWARE_REPORT_NAME = 'nexpose-software-report.csv'
|
12
12
|
POLICY_REPORT_NAME = 'nexpose-policy-report.csv'
|
13
|
+
AUDIT_REPORT_NAME = 'nexpose-audit-report.html'
|
13
14
|
|
14
15
|
VULNERABILITY_REPORT_QUERY = 'SELECT DISTINCT
|
15
16
|
ip_address,
|
@@ -61,4 +62,4 @@ module CONSTANTS
|
|
61
62
|
LEFT JOIN dim_policy_rule dpr on dpr.policy_id = fapr.policy_id and fapr.rule_id = dpr.rule_id
|
62
63
|
LEFT JOIN dim_asset da on da.asset_id = fapr.asset_id
|
63
64
|
ORDER BY da.ip_address'
|
64
|
-
end
|
65
|
+
end
|
data/lib/nexpose-runner/scan.rb
CHANGED
@@ -35,6 +35,9 @@ module NexposeRunner
|
|
35
35
|
policies = generate_report(CONSTANTS::POLICY_REPORT_QUERY, site.id, nsc)
|
36
36
|
generate_csv(policies, CONSTANTS::POLICY_REPORT_NAME)
|
37
37
|
|
38
|
+
puts "Scan complete for #{run_details.site_name}, Generating Audit Report"
|
39
|
+
generate_audit_report(site.id, nsc, CONSTANTS::AUDIT_REPORT_NAME)
|
40
|
+
|
38
41
|
[vulnerbilities, software, policies]
|
39
42
|
end
|
40
43
|
|
@@ -51,8 +54,9 @@ module NexposeRunner
|
|
51
54
|
|
52
55
|
begin
|
53
56
|
sleep(3)
|
54
|
-
|
55
|
-
|
57
|
+
stats = nsc.scan_statistics(scan.id)
|
58
|
+
status = stats.status
|
59
|
+
puts "Current #{run_details.site_name} scan status: #{status.to_s} -- PENDING: #{stats.tasks.pending.to_s} ACTIVE: #{stats.tasks.active.to_s} COMPLETED #{stats.tasks.completed.to_s}"
|
56
60
|
end while status == Nexpose::Scan::Status::RUNNING
|
57
61
|
end
|
58
62
|
|
@@ -83,6 +87,12 @@ module NexposeRunner
|
|
83
87
|
CSV.parse(report_output.chomp, {:headers => :first_row})
|
84
88
|
end
|
85
89
|
|
90
|
+
def self.generate_audit_report(site, nsc, name)
|
91
|
+
adhoc = Nexpose::AdhocReportConfig.new('audit-report', 'html', site)
|
92
|
+
data = adhoc.generate(nsc)
|
93
|
+
File.open(name, 'w') { |file| file.write(data) }
|
94
|
+
end
|
95
|
+
|
86
96
|
def self.generate_csv(csv_output, name)
|
87
97
|
CSV.open(name, 'w') do |csv_file|
|
88
98
|
csv_file << csv_output.headers
|
@@ -1,9 +1,15 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
1
3
|
class ScanRunDescription
|
2
4
|
attr_accessor :connection_url, :username, :password, :port, :site_name, :ip_addresses, :scan_template
|
3
5
|
@@port_value = ''
|
4
6
|
@@ip_addresses = ''
|
5
7
|
|
6
8
|
def initialize(options)
|
9
|
+
if File.file?('config/scan.yml')
|
10
|
+
options = YAML.load_file('config/scan.yml')
|
11
|
+
end
|
12
|
+
|
7
13
|
self.connection_url = options['connection_url']
|
8
14
|
self.username = options['username']
|
9
15
|
self.password = options['password']
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'nexpose-runner/scan_run_description'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
file_path = 'config/scan.yml'
|
5
|
+
describe 'scan_user_config_file_tests' do
|
6
|
+
if File.file?('config/scan.yml')
|
7
|
+
File.rename('config/scan.yml','config/scan.yml.bak')
|
8
|
+
end
|
9
|
+
|
10
|
+
describe 'start' do
|
11
|
+
before(:each) do
|
12
|
+
config_file = File.new(file_path, 'w')
|
13
|
+
config_file.puts("connection_url: 'mydomain.wat'")
|
14
|
+
config_file.puts("ip_addresses: ''")
|
15
|
+
config_file.close
|
16
|
+
@scan_run_description = ScanRunDescription.new({})
|
17
|
+
end
|
18
|
+
|
19
|
+
after(:each) do
|
20
|
+
File.delete(file_path) if File.exist?(file_path)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should get configuration from the config/scan.yaml when provided' do
|
24
|
+
expect(@scan_run_description.connection_url).to eq('mydomain.wat')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
if File.file?('config/scan.yml.bak')
|
29
|
+
File.rename('config/scan.yml.bak', 'config/scan.yml')
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
describe 'scan_default_config_tests' do
|
35
|
+
if File.file?('config/scan.yml')
|
36
|
+
File.rename('config/scan.yml', 'config/scan.yml.bak')
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'start' do
|
40
|
+
before(:each) do
|
41
|
+
@options = {
|
42
|
+
'connection_url' => 'foo.bar',
|
43
|
+
'ip_addresses' => ''
|
44
|
+
}
|
45
|
+
@scan_run_description = ScanRunDescription.new(@options)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should get configuration from the command line options' do
|
49
|
+
expect(@scan_run_description.connection_url).to eq('foo.bar')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
if File.file?('config/scan.yml.bak')
|
54
|
+
File.rename('config/scan.yml.bak', 'config/scan.yml')
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
data/spec/scan_spec.rb
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
require 'nexpose-runner/scan'
|
2
2
|
require 'nexpose-runner/constants'
|
3
|
+
require 'ostruct'
|
3
4
|
|
4
5
|
describe 'nexpose-runner' do
|
6
|
+
|
7
|
+
if File.file?('config/exploit.yml')
|
8
|
+
File.rename('config/exploit.yml', 'config/exploit.yml.bak')
|
9
|
+
end
|
10
|
+
|
5
11
|
before(:each) do
|
6
12
|
allow(NexposeRunner::Scan).to receive(:sleep)
|
7
13
|
end
|
@@ -39,10 +45,12 @@ describe 'nexpose-runner' do
|
|
39
45
|
|
40
46
|
|
41
47
|
@mock_scan = get_mock_scan
|
48
|
+
@mock_scan_summary = get_mock_scan_summary
|
42
49
|
@mock_nexpose_client = get_mock_nexpose_client
|
43
50
|
@mock_nexpose_site = get_mock_nexpose_site
|
44
51
|
@mock_report = get_mock_report
|
45
52
|
|
53
|
+
|
46
54
|
@options = {
|
47
55
|
'connection_url' => @expected_connection,
|
48
56
|
'username' => @expected_username,
|
@@ -163,20 +171,18 @@ describe 'nexpose-runner' do
|
|
163
171
|
|
164
172
|
describe 'wait for the Nexpose Scan to complete' do
|
165
173
|
it 'should call to check the status of the scan' do
|
166
|
-
expect(@mock_nexpose_client).to receive(:
|
174
|
+
expect(@mock_nexpose_client).to receive(:scan_statistics).with(@mock_scan_id)
|
167
175
|
|
168
176
|
NexposeRunner::Scan.start(@options)
|
169
177
|
end
|
170
178
|
|
171
179
|
it 'should call to check the status until it is not running' do
|
172
|
-
expect(@
|
173
|
-
.with(@mock_scan_id)
|
180
|
+
expect(@mock_scan_summary).to receive(:status)
|
174
181
|
.and_return(Nexpose::Scan::Status::RUNNING)
|
175
182
|
.exactly(3).times
|
176
183
|
.ordered
|
177
184
|
|
178
|
-
expect(@
|
179
|
-
.with(@mock_scan_id)
|
185
|
+
expect(@mock_scan_summary).to receive(:status)
|
180
186
|
.and_return(Nexpose::Scan::Status::FINISHED)
|
181
187
|
.once
|
182
188
|
.ordered
|
@@ -185,14 +191,12 @@ describe 'nexpose-runner' do
|
|
185
191
|
end
|
186
192
|
|
187
193
|
it 'should sleep for 3 seconds if the status is still running' do
|
188
|
-
expect(@
|
189
|
-
.with(@mock_scan_id)
|
194
|
+
expect(@mock_scan_summary).to receive(:status)
|
190
195
|
.and_return(Nexpose::Scan::Status::RUNNING)
|
191
196
|
.exactly(3).times
|
192
197
|
.ordered
|
193
198
|
|
194
|
-
expect(@
|
195
|
-
.with(@mock_scan_id)
|
199
|
+
expect(@mock_scan_summary).to receive(:status)
|
196
200
|
.and_return(Nexpose::Scan::Status::FINISHED)
|
197
201
|
.once
|
198
202
|
.ordered
|
@@ -227,6 +231,10 @@ describe 'nexpose-runner' do
|
|
227
231
|
}.to raise_error(StandardError, CONSTANTS::VULNERABILITY_FOUND_MESSAGE)
|
228
232
|
end
|
229
233
|
end
|
234
|
+
|
235
|
+
if File.file?('config/exploit.yml.bak')
|
236
|
+
File.rename('config/exploit.yml.bak', 'config/exploit.yml')
|
237
|
+
end
|
230
238
|
end
|
231
239
|
|
232
240
|
def expect_report_to_be_called_with(report_name, report_query, report_response)
|
@@ -249,8 +257,9 @@ def get_mock_nexpose_client
|
|
249
257
|
|
250
258
|
allow(mock_nexpose_client).to receive(:call).with(any_args).and_return({})
|
251
259
|
|
252
|
-
allow(mock_nexpose_client).to receive(:
|
260
|
+
allow(mock_nexpose_client).to receive(:scan_statistics)
|
253
261
|
.with(@mock_scan_id)
|
262
|
+
.and_return(@mock_scan_summary)
|
254
263
|
|
255
264
|
allow(mock_nexpose_client).to receive(:login)
|
256
265
|
.and_return(true)
|
@@ -261,6 +270,20 @@ def get_mock_nexpose_client
|
|
261
270
|
mock_nexpose_client
|
262
271
|
end
|
263
272
|
|
273
|
+
def get_mock_scan_summary
|
274
|
+
mock_scan_summary = double(Nexpose::ScanSummary)
|
275
|
+
|
276
|
+
tasks = OpenStruct.new(:completed => 1, :pending => 1)
|
277
|
+
allow(mock_scan_summary).to receive(:tasks).and_return(tasks)
|
278
|
+
|
279
|
+
allow(mock_scan_summary).to receive(:status).and_return(
|
280
|
+
Nexpose::Scan::Status::RUNNING,
|
281
|
+
Nexpose::Scan::Status::RUNNING,
|
282
|
+
Nexpose::Scan::Status::RUNNING,
|
283
|
+
Nexpose::Scan::Status::FINISHED)
|
284
|
+
mock_scan_summary
|
285
|
+
end
|
286
|
+
|
264
287
|
def get_mock_nexpose_site
|
265
288
|
mock_nexpose_site = double(Nexpose::Site)
|
266
289
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: NexposeRunner
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nathan Gibson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-10-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nexpose
|
@@ -83,11 +83,13 @@ files:
|
|
83
83
|
- README.md
|
84
84
|
- Rakefile
|
85
85
|
- bin/scan
|
86
|
+
- config/scan.yml.example
|
86
87
|
- lib/NexposeRunner.rb
|
87
88
|
- lib/NexposeRunner/version.rb
|
88
89
|
- lib/nexpose-runner/constants.rb
|
89
90
|
- lib/nexpose-runner/scan.rb
|
90
91
|
- lib/nexpose-runner/scan_run_description.rb
|
92
|
+
- spec/scan_config_spec.rb
|
91
93
|
- spec/scan_spec.rb
|
92
94
|
- spec/spec_helper.rb
|
93
95
|
homepage: ''
|
@@ -110,7 +112,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
110
112
|
version: '0'
|
111
113
|
requirements: []
|
112
114
|
rubyforge_project:
|
113
|
-
rubygems_version: 2.
|
115
|
+
rubygems_version: 2.4.8
|
114
116
|
signing_key:
|
115
117
|
specification_version: 4
|
116
118
|
summary: This is a gem that provides the ability to create a new site, add an IP to
|
@@ -118,5 +120,6 @@ summary: This is a gem that provides the ability to create a new site, add an IP
|
|
118
120
|
and finally produce a reports for vulnerabilities, installed software, and policy
|
119
121
|
compliance.
|
120
122
|
test_files:
|
123
|
+
- spec/scan_config_spec.rb
|
121
124
|
- spec/scan_spec.rb
|
122
125
|
- spec/spec_helper.rb
|