NexposeRunner 0.0.19b → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: faa4204e94afada27ead08f1bf95f248b6a978ff
4
- data.tar.gz: b35f5ca648674d15eca67eecc61f464d712a8fd4
3
+ metadata.gz: 1753280c807b7cbc9e9c3d2ce7926f544e455608
4
+ data.tar.gz: a4c4be6de7157c5a746469d0c9f878369e468328
5
5
  SHA512:
6
- metadata.gz: 39179a02b8a68f6cec50ed602954a68deef7413e69eebc96700af88265880f1985d66d2ecb6a5a86e97237f8aa262dcaa192164d9ee527c6103a9079f8f31ca9
7
- data.tar.gz: a54dde614f2af5d7c963635223a0e7fb771b170ca908113ad384cd67ab1a65a61abc3a14e9944b1cd401f8f944a427fad9a6ff0277cbb3811a36ca1b14ce6bd0
6
+ metadata.gz: 58d0eb323fd62798dd5fdfb5fd5e614f3524e6323f7d07e2a95104889b857cc1f0a15a18d184dd0db703eea92b776518b6f245afe53d0739e1152b5b64a95cbb
7
+ data.tar.gz: 2bd74be1ef45c64a473110a1c3b623675745d55a2e3577e97e540953c891e03475b88d34c06d3c6986ec1824c00e9ad19560874dd9b710390bd13feb280b57f0
data/README.md CHANGED
File without changes
data/bin/scan CHANGED
File without changes
@@ -1,4 +1,3 @@
1
1
  module NexposeRunner
2
- VERSION = '0.0.19b'
2
+ VERSION = '1.0.0'
3
3
  end
4
-
@@ -14,6 +14,7 @@ module CONSTANTS
14
14
  VULNERABILITY_DETAIL_REPORT_NAME = 'nexpose-vulnerability-detail-report.csv'
15
15
  SOFTWARE_REPORT_NAME = 'nexpose-software-report.csv'
16
16
  POLICY_REPORT_NAME = 'nexpose-policy-report.csv'
17
+ MAX_RETRY_COUNT = 5
17
18
 
18
19
  AUDIT_REPORT_FILE_NAME = 'nexpose-audit-report.html'
19
20
  AUDIT_REPORT_NAME = 'audit-report'
@@ -8,7 +8,7 @@ require 'nexpose-runner/scan_run_description'
8
8
 
9
9
  module NexposeRunner
10
10
  module Scan
11
-
11
+
12
12
  def self.allow_vulnerabilities?(vulnerabilities, run_details)
13
13
  vuln_array = []
14
14
  exceptions_array = get_exceptions(run_details)
@@ -33,8 +33,13 @@ module NexposeRunner
33
33
  end
34
34
 
35
35
  def self.get_exceptions(run_details)
36
- uri = URI("#{run_details.exceptions_list_url}")
37
- ex = Net::HTTP.get(uri).split("\n")
36
+ path = "#{run_details.exceptions_list_url}"
37
+ uri = URI(path)
38
+ if path.include? "http:"
39
+ ex = Net::HTTP.get(uri).split("\n")
40
+ elsif (File.file?(path))
41
+ ex = File.read(path).split("\n")
42
+ end
38
43
  ex
39
44
  end
40
45
 
@@ -58,7 +63,7 @@ module NexposeRunner
58
63
  puts "Scan complete for #{run_details.site_name}, Generating Vulnerability Report"
59
64
  vulnerabilities = generate_report(CONSTANTS::VULNERABILITY_REPORT_QUERY, site.id, nsc)
60
65
  generate_csv(vulnerabilities, CONSTANTS::VULNERABILITY_REPORT_NAME)
61
-
66
+
62
67
  puts "Scan complete for #{run_details.site_name}, Generating Vulnerability Detail Report"
63
68
  vuln_details = generate_report(CONSTANTS:: VULNERABILITY_DETAIL_REPORT_QUERY, site.id, nsc)
64
69
  generate_csv(vuln_details, CONSTANTS::VULNERABILITY_DETAIL_REPORT_NAME)
@@ -73,7 +78,7 @@ module NexposeRunner
73
78
 
74
79
  puts "Scan complete for #{run_details.site_name}, Generating Audit Report"
75
80
  generate_template_report(nsc, site.id, CONSTANTS::AUDIT_REPORT_FILE_NAME, CONSTANTS::AUDIT_REPORT_NAME, CONSTANTS::AUDIT_REPORT_FORMAT)
76
-
81
+
77
82
  puts "Scan complete for #{run_details.site_name}, Generating Xml Report"
78
83
  generate_template_report(nsc, site.id, CONSTANTS::XML_REPORT_FILE_NAME, CONSTANTS::XML_REPORT_NAME, CONSTANTS::XML_REPORT_FORMAT)
79
84
 
@@ -83,7 +88,7 @@ module NexposeRunner
83
88
  def self.verify_run(vulnerabilities, run_details)
84
89
 
85
90
  if run_details.exceptions_list_url.to_s.empty? and vulnerabilities.count > 0
86
- raise StandardError, CONSTANTS::VULNERABILITY_FOUND_MESSAGE
91
+ raise StandardError, CONSTANTS::VULNERABILITY_FOUND_MESSAGE
87
92
 
88
93
  elsif vulnerabilities.count == 0
89
94
  puts "No vulnerabilities found!"
@@ -98,13 +103,24 @@ module NexposeRunner
98
103
 
99
104
  puts "Starting scan for #{run_details.site_name} using the #{run_details.scan_template_id} scan template"
100
105
  scan = site.scan nsc
101
-
106
+ retry_count = 0
102
107
  begin
103
108
  sleep(3)
104
- stats = nsc.scan_statistics(scan.id)
105
- status = stats.status
109
+ begin
110
+ stats = nsc.scan_statistics(scan.id)
111
+ rescue
112
+ if retry_count == CONSTANTS::MAX_RETRY_COUNT
113
+ raise
114
+ end
115
+ puts "Status Check failed, incrementing retry count to #{retry_count}"
116
+ retry_count = retry_count + 1
117
+ next
118
+ end
119
+ status = stats.status
106
120
  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}"
121
+ retry_count = 0
107
122
  end while status == Nexpose::Scan::Status::RUNNING
123
+
108
124
  end
109
125
 
110
126
  def self.create_site(run_details, nsc)
@@ -118,7 +134,7 @@ module NexposeRunner
118
134
  end
119
135
  site.save nsc
120
136
  puts "Created site #{run_details.site_name} successfully with the following host(s) #{run_details.ip_addresses.join(', ')}"
121
-
137
+
122
138
  site
123
139
  end
124
140
 
@@ -0,0 +1,2 @@
1
+ Database Open Access
2
+ MySQL Obsolete Version
@@ -31,8 +31,8 @@ describe 'nexpose-runner' do
31
31
  @mock_vuln_report = 'ip_address,title,date_published,severity,summary,fix
32
32
  10.5.0.15,Database Open Access,2010-01-01,Severe,Restrict database access,<p><p>Configure the database server to only allow access to trusted systems. For example, the PCI DSS standard requires you to place the database in an internal network zone, segregated from the DMZ </p></p>
33
33
  10.5.0.15.180,MySQL Obsolete Version,2007-07-25,Critical,Upgrade to the latest version of Oracle MySQL,<p>Download and apply the upgrade from: <a href=http://dev.mysql.com/downloads/mysql>http://dev.mysql.com/downloads/mysql</a></p>'.chomp
34
- @mock_exceptions = "Database Open Access\nMySQL Obsolete Version"
35
-
34
+ @mock_exceptions = "Database Open Access\nMySQL Obsolete Version"
35
+
36
36
  @mock_vuln_detail_report = 'stuff'.chomp
37
37
 
38
38
  @mock_software_report = 'name,ip_address,host_name,description,description,vendor,name,version
@@ -89,40 +89,40 @@ describe 'nexpose-runner' do
89
89
  it 'should throw an error if no connection url is passed' do
90
90
  options = @options.clone
91
91
  options['connection_url'] = nil
92
- expect {
93
- NexposeRunner::Scan.start(options)
92
+ expect {
93
+ NexposeRunner::Scan.start(options)
94
94
  }.to raise_error(StandardError, 'OOPS! Looks like you forgot to give me the URL/IP address to your Nexpose Server')
95
95
  end
96
96
 
97
97
  it 'should throw an error if no username is passed' do
98
98
  options = @options.clone
99
99
  options['username'] = nil
100
- expect {
101
- NexposeRunner::Scan.start(options)
100
+ expect {
101
+ NexposeRunner::Scan.start(options)
102
102
  }.to raise_error(StandardError, 'OOPS! Looks like you forgot to give me a username to login to Nexpose with')
103
103
  end
104
104
 
105
105
  it 'should throw an error if no password is passed' do
106
106
  options = @options.clone
107
107
  options['password'] = nil
108
- expect {
109
- NexposeRunner::Scan.start(options)
108
+ expect {
109
+ NexposeRunner::Scan.start(options)
110
110
  }.to raise_error(StandardError, 'OOPS! Looks like you forgot to give me a password to login to Nexpose with')
111
111
  end
112
112
 
113
113
  it 'should throw an error if no site name is passed' do
114
114
  options = @options.clone
115
115
  options['site_name'] = nil
116
- expect {
117
- NexposeRunner::Scan.start(options)
116
+ expect {
117
+ NexposeRunner::Scan.start(options)
118
118
  }.to raise_error(StandardError, 'OOPS! Looks like you forgot to give me a Nexpose Site Name')
119
119
  end
120
120
 
121
121
  it 'should throw an error if no ip address is passed' do
122
122
  options = @options.clone
123
123
  options['ip_addresses'] = '';
124
- expect {
125
- NexposeRunner::Scan.start(options)
124
+ expect {
125
+ NexposeRunner::Scan.start(options)
126
126
  }.to raise_error(StandardError, 'OOPS! Looks like you forgot to give me an IP Address to scan')
127
127
  end
128
128
 
@@ -136,9 +136,9 @@ describe 'nexpose-runner' do
136
136
 
137
137
  it 'should use 3780 as default if port is empty string' do
138
138
  expect(Nexpose::Connection).to receive(:new)
139
- .with(@options['connection_url'],
140
- @options['username'],
141
- @options['password'],
139
+ .with(@options['connection_url'],
140
+ @options['username'],
141
+ @options['password'],
142
142
  '3780')
143
143
  .and_return(@mock_nexpose_client)
144
144
 
@@ -181,37 +181,37 @@ describe 'nexpose-runner' do
181
181
  describe 'wait for the Nexpose Scan to complete' do
182
182
  it 'should call to check the status of the scan' do
183
183
  expect(@mock_nexpose_client).to receive(:scan_statistics).with(@mock_scan_id)
184
-
184
+
185
185
  NexposeRunner::Scan.start(@options)
186
186
  end
187
-
187
+
188
188
  it 'should call to check the status until it is not running' do
189
189
  expect(@mock_scan_summary).to receive(:status)
190
190
  .and_return(Nexpose::Scan::Status::RUNNING)
191
191
  .exactly(3).times
192
192
  .ordered
193
-
193
+
194
194
  expect(@mock_scan_summary).to receive(:status)
195
195
  .and_return(Nexpose::Scan::Status::FINISHED)
196
196
  .once
197
197
  .ordered
198
-
198
+
199
199
  NexposeRunner::Scan.start(@options)
200
200
  end
201
-
201
+
202
202
  it 'should sleep for 3 seconds if the status is still running' do
203
203
  expect(@mock_scan_summary).to receive(:status)
204
204
  .and_return(Nexpose::Scan::Status::RUNNING)
205
205
  .exactly(3).times
206
206
  .ordered
207
-
207
+
208
208
  expect(@mock_scan_summary).to receive(:status)
209
209
  .and_return(Nexpose::Scan::Status::FINISHED)
210
210
  .once
211
211
  .ordered
212
212
 
213
213
  expect(NexposeRunner::Scan).to receive(:sleep).with(3).exactly(4).times
214
-
214
+
215
215
  NexposeRunner::Scan.start(@options)
216
216
  end
217
217
  end
@@ -230,16 +230,16 @@ describe 'nexpose-runner' do
230
230
  expect(Nexpose::AdhocReportConfig).to receive(:new)
231
231
  .with(CONSTANTS::AUDIT_REPORT_NAME, CONSTANTS::AUDIT_REPORT_FORMAT, @mock_site_id)
232
232
  .and_return(@mock_report)
233
-
233
+
234
234
  expect(Nexpose::AdhocReportConfig).to receive(:new)
235
235
  .with(CONSTANTS::XML_REPORT_NAME, CONSTANTS::XML_REPORT_FORMAT, @mock_site_id)
236
236
  .and_return(@mock_report)
237
237
 
238
238
  expect_template_report_to_be_called_with(CONSTANTS::AUDIT_REPORT_FILE_NAME)
239
239
  expect_template_report_to_be_called_with(CONSTANTS::XML_REPORT_FILE_NAME)
240
-
241
- expect {
242
- NexposeRunner::Scan.start(@options)
240
+
241
+ expect {
242
+ NexposeRunner::Scan.start(@options)
243
243
  }.to raise_error(StandardError, CONSTANTS::VULNERABILITY_FOUND_MESSAGE)
244
244
  end
245
245
  end
@@ -247,19 +247,30 @@ describe 'nexpose-runner' do
247
247
  it 'should throw exception if vulnerability exists' do
248
248
  expect_report_to_be_called_with(CONSTANTS::VULNERABILITY_REPORT_NAME, CONSTANTS::VULNERABILITY_REPORT_QUERY, @mock_vuln_report)
249
249
 
250
- expect {
251
- NexposeRunner::Scan.start(@options)
250
+ expect {
251
+ NexposeRunner::Scan.start(@options)
252
252
  }.to raise_error(StandardError, CONSTANTS::VULNERABILITY_FOUND_MESSAGE)
253
253
  end
254
254
 
255
255
  it 'should not throw exception if exceptions exist for all vulnerabilities' do
256
256
  expect_report_to_be_called_with(CONSTANTS::VULNERABILITY_REPORT_NAME, CONSTANTS::VULNERABILITY_REPORT_QUERY, @mock_vuln_report)
257
-
257
+
258
258
  options = @options.clone
259
259
  options['exceptions_list_url'] = 'http://google.com'
260
-
260
+
261
261
  expect_exceptions_to_be_called_with(options['exceptions_list_url'])
262
-
262
+
263
+ NexposeRunner::Scan.start(options)
264
+ end
265
+
266
+ it 'should not throw exception if exceptions exist from a file for all vulnerabilities' do
267
+ expect_report_to_be_called_with(CONSTANTS::VULNERABILITY_REPORT_NAME, CONSTANTS::VULNERABILITY_REPORT_QUERY, @mock_vuln_report)
268
+
269
+ options = @options.clone
270
+ options['exceptions_list_url'] = 'spec/data/test_exclist.txt'
271
+
272
+ expect_exceptions_to_be_called_with_file(options['exceptions_list_url'])
273
+
263
274
  NexposeRunner::Scan.start(options)
264
275
  end
265
276
  end
@@ -275,6 +286,10 @@ def expect_exceptions_to_be_called_with(exceptions_list_url)
275
286
  .with(uri).and_return(@mock_exceptions)
276
287
  end
277
288
 
289
+ def expect_exceptions_to_be_called_with_file(exceptions_list_url)
290
+ expect(File.read(exceptions_list_url)).to eq "Database Open Access\nMySQL Obsolete Version\n"
291
+ end
292
+
278
293
  def expect_report_to_be_called_with(report_name, report_query, report_response)
279
294
  expect(@mock_report).to receive(:add_filter)
280
295
  .with('version', '1.3.0')
@@ -311,29 +326,29 @@ def get_mock_nexpose_client
311
326
 
312
327
  allow(Nexpose::Connection).to receive(:new)
313
328
  .and_return(mock_nexpose_client)
314
-
329
+
315
330
  allow(mock_nexpose_client).to receive(:make_xml)
316
331
  .with(any_args)
317
332
  .and_return(xml)
318
-
333
+
319
334
  allow(mock_nexpose_client).to receive(:make_xml)
320
335
  .with(any_args)
321
336
  .and_return(xml)
322
-
337
+
323
338
  allow(mock_nexpose_client).to receive(:filter)
324
339
  .with(any_args)
325
- .and_return({})
326
-
340
+ .and_return({})
341
+
327
342
  allow(mock_nexpose_client).to receive(:execute)
328
343
  .with(any_args)
329
344
  .and_return(mock_api_request)
330
-
345
+
331
346
  allow(mock_api_request).to receive(:success)
332
347
  .and_return(false) #this is just to shut up the underlying api.
333
-
348
+
334
349
  allow(mock_api_request).to receive(:attributes)
335
- .and_return(xml)
336
-
350
+ .and_return(xml)
351
+
337
352
  mock_nexpose_client
338
353
  end
339
354
 
@@ -345,9 +360,9 @@ def get_mock_scan_summary
345
360
  allow(mock_scan_summary).to receive(:tasks).and_return(tasks)
346
361
 
347
362
  allow(mock_scan_summary).to receive(:status).and_return(
348
- Nexpose::Scan::Status::RUNNING,
349
363
  Nexpose::Scan::Status::RUNNING,
350
- Nexpose::Scan::Status::RUNNING,
364
+ Nexpose::Scan::Status::RUNNING,
365
+ Nexpose::Scan::Status::RUNNING,
351
366
  Nexpose::Scan::Status::FINISHED)
352
367
  mock_scan_summary
353
368
  end
@@ -411,4 +426,3 @@ def get_mock_scan
411
426
  allow(mock_scan).to receive(:id).and_return(@mock_scan_id)
412
427
  mock_scan
413
428
  end
414
-
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.19b
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan Gibson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-10-31 00:00:00.000000000 Z
11
+ date: 2017-12-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nexpose
@@ -91,6 +91,7 @@ files:
91
91
  - lib/nexpose-runner/constants.rb
92
92
  - lib/nexpose-runner/scan.rb
93
93
  - lib/nexpose-runner/scan_run_description.rb
94
+ - spec/data/test_exclist.txt
94
95
  - spec/scan_config_spec.rb
95
96
  - spec/scan_spec.rb
96
97
  - spec/spec_helper.rb
@@ -109,12 +110,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
109
110
  version: '0'
110
111
  required_rubygems_version: !ruby/object:Gem::Requirement
111
112
  requirements:
112
- - - ">"
113
+ - - ">="
113
114
  - !ruby/object:Gem::Version
114
- version: 1.3.1
115
+ version: '0'
115
116
  requirements: []
116
117
  rubyforge_project:
117
- rubygems_version: 2.5.2
118
+ rubygems_version: 2.4.5.2
118
119
  signing_key:
119
120
  specification_version: 4
120
121
  summary: This is a gem that provides the ability to create a new site, add an IP to
@@ -122,6 +123,7 @@ summary: This is a gem that provides the ability to create a new site, add an IP
122
123
  and finally produce a reports for vulnerabilities, installed software, and policy
123
124
  compliance.
124
125
  test_files:
126
+ - spec/data/test_exclist.txt
125
127
  - spec/scan_config_spec.rb
126
128
  - spec/scan_spec.rb
127
129
  - spec/spec_helper.rb