NexposeRunner 0.0.11 → 0.0.15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +1 -2
- data/Exclusions.txt +3 -0
- data/README.md +5 -6
- data/lib/NexposeRunner/version.rb +1 -1
- data/lib/nexpose-runner/command_line_arg_parser.rb +6 -0
- data/lib/nexpose-runner/constants.rb +1 -0
- data/lib/nexpose-runner/scan.rb +49 -9
- data/lib/nexpose-runner/scan_run_description.rb +12 -1
- data/spec/scan_spec.rb +19 -2
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5928523f9cb26228648a7bd116e26bee432b2992
|
4
|
+
data.tar.gz: 427bc4fd6579b8ca6c60eaa698cf014f955f5fd9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cc462110f837f49d1620371315ec578cbcac2e9c6fbf3d6f9bb3dabcbfe2571e1f0b3b8b8cdb5d32e3ff178974a87c0f6a8598fd3e4f1f51a5e978a0df23e89c
|
7
|
+
data.tar.gz: 7f88846124ca3500a757b50759cb4de40bf0a02c9349794f97246ca02ceb401f2e91a18733e5c9889787582d80856f2cc8dcd7449dcb9931e9005db2b6671371
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/Exclusions.txt
ADDED
data/README.md
CHANGED
@@ -6,7 +6,7 @@ This gem will make a nexpose server connection, create a new site, initiate a sc
|
|
6
6
|
|
7
7
|
Basically this gem allows you to attach Nexpose to your Continuous Delivery/Continuous Integration pipeline. Though it can be used for other purposes.
|
8
8
|
|
9
|
-
At the end of the scan it will generate 3 csv reports and save them in the directory where the script was executed from. It will also raise an exception if a
|
9
|
+
At the end of the scan it will generate 3 csv reports and save them in the directory where the script was executed from. It will also raise an exception if a vulnerability is detected. This is used to break the Continuous Delivery/Continuous integration build. You may add an exception list URL to prevent breaking the build.
|
10
10
|
|
11
11
|
## Installation
|
12
12
|
|
@@ -24,15 +24,14 @@ Or install it yourself as:
|
|
24
24
|
|
25
25
|
## Usage
|
26
26
|
|
27
|
-
This gem allows you to specify the Nexpose Server URL, Nexpose Username, Nexpose Password, Nexpose Server Port (optional, defaults to 3780), Site Name, Target IP Address, Scan Template, and Engine Number (optional).
|
27
|
+
This gem allows you to specify the Nexpose Server URL, Exceptions URL (optional), Nexpose Username, Nexpose Password, Nexpose Server Port (optional, defaults to 3780), Site Name, Target IP Address, Scan Template, and Engine Number (optional).
|
28
28
|
|
29
|
+
*NOTE:* If you use the "exceptions_list_url" parameter, please ensure you have proper authentication in place.
|
29
30
|
|
30
|
-
$ scan "connection_url" "username" "password" "port" "site_name" "ip_address" "scan_template" "engine_number"
|
31
|
-
|
32
31
|
EXAMPLE:
|
33
32
|
|
34
|
-
$ scan
|
35
|
-
|
33
|
+
$ scan --connection test.com --exceptions_list_url raw.github.com/exceptions.txt --username username1 --password password1 --port 443 --site-name myfirstsite --ip-addresses 192.168.1.10 --scan-template full-audit --Engine 2
|
34
|
+
|
36
35
|
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
37
|
## Contributing
|
@@ -4,6 +4,7 @@ class CommandLineArgumentParser
|
|
4
4
|
def self.parse(args)
|
5
5
|
options = {}
|
6
6
|
options['connection_url'] = ''
|
7
|
+
options['exceptions_list_url'] = ''
|
7
8
|
options['username'] = ''
|
8
9
|
options['password'] = ''
|
9
10
|
options['port'] = 0
|
@@ -22,6 +23,9 @@ class CommandLineArgumentParser
|
|
22
23
|
options['connection_url'] = url
|
23
24
|
end
|
24
25
|
|
26
|
+
opts.on('--exceptions_list_url eURL', 'Vulnerability list URL') do |exceptions_list_url|
|
27
|
+
options['exceptions_list_url'] = exceptions_list_url
|
28
|
+
end
|
25
29
|
opts.on('--username USERNAME', 'Nexpose Login Username') do |username|
|
26
30
|
options['username'] = username
|
27
31
|
end
|
@@ -49,6 +53,8 @@ class CommandLineArgumentParser
|
|
49
53
|
opts.on('--engine ENGINE', 'Nexpose scan engine to use') do |engine|
|
50
54
|
options['engine'] = engine
|
51
55
|
end
|
56
|
+
|
57
|
+
|
52
58
|
|
53
59
|
end
|
54
60
|
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module CONSTANTS
|
2
2
|
REQUIRED_CONNECTION_URL_MESSAGE = 'OOPS! Looks like you forgot to give me the URL/IP address to your Nexpose Server'
|
3
|
+
DEFAULT_EXCEPTIONS_URL = ''
|
3
4
|
REQUIRED_USERNAME_MESSAGE = 'OOPS! Looks like you forgot to give me a username to login to Nexpose with'
|
4
5
|
REQUIRED_PASSWORD_MESSAGE = 'OOPS! Looks like you forgot to give me a password to login to Nexpose with'
|
5
6
|
REQUIRED_SITE_NAME_MESSAGE = 'OOPS! Looks like you forgot to give me a Nexpose Site Name'
|
data/lib/nexpose-runner/scan.rb
CHANGED
@@ -1,11 +1,43 @@
|
|
1
|
-
require '
|
1
|
+
require 'net/http'
|
2
2
|
require 'csv'
|
3
|
+
require 'nexpose'
|
4
|
+
require 'open-uri'
|
3
5
|
require 'json'
|
4
6
|
require 'nexpose-runner/constants'
|
5
7
|
require 'nexpose-runner/scan_run_description'
|
6
8
|
|
7
9
|
module NexposeRunner
|
8
10
|
module Scan
|
11
|
+
|
12
|
+
def self.allow_vulnerabilities?(vulnerabilities, run_details)
|
13
|
+
vuln_array = []
|
14
|
+
exceptions_array = get_exceptions(run_details)
|
15
|
+
titles = vulnerabilities.map{ |v| v[1] }[1..-1]
|
16
|
+
for vuln in titles
|
17
|
+
if !exceptions_array.include?(vuln)
|
18
|
+
puts "#{vuln} not found in Exceptions list"
|
19
|
+
vuln_array << [vuln]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
if vuln_array.count > 0
|
24
|
+
File.open('No_Exceptions_Found.txt', 'w+') do |f|
|
25
|
+
vuln_array.each { |element| f.puts(element) }
|
26
|
+
return false
|
27
|
+
end
|
28
|
+
|
29
|
+
else
|
30
|
+
puts "All exceptions passed!"
|
31
|
+
return true
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.get_exceptions(run_details)
|
36
|
+
uri = URI("#{run_details.exceptions_list_url}")
|
37
|
+
ex = Net::HTTP.get(uri).split("\n")
|
38
|
+
ex
|
39
|
+
end
|
40
|
+
|
9
41
|
def Scan.start(options)
|
10
42
|
|
11
43
|
run_details = ScanRunDescription.new(options)
|
@@ -19,13 +51,13 @@ module NexposeRunner
|
|
19
51
|
|
20
52
|
reports = generate_reports(nsc, site, run_details)
|
21
53
|
|
22
|
-
verify_run(reports[0])
|
54
|
+
verify_run(reports[0], run_details)
|
23
55
|
end
|
24
56
|
|
25
57
|
def self.generate_reports(nsc, site, run_details)
|
26
58
|
puts "Scan complete for #{run_details.site_name}, Generating Vulnerability Report"
|
27
|
-
|
28
|
-
generate_csv(
|
59
|
+
vulnerabilities = generate_report(CONSTANTS::VULNERABILITY_REPORT_QUERY, site.id, nsc)
|
60
|
+
generate_csv(vulnerabilities, CONSTANTS::VULNERABILITY_REPORT_NAME)
|
29
61
|
|
30
62
|
puts "Scan complete for #{run_details.site_name}, Generating Vulnerability Detail Report"
|
31
63
|
vuln_details = generate_report(CONSTANTS:: VULNERABILITY_DETAIL_REPORT_QUERY, site.id, nsc)
|
@@ -45,13 +77,21 @@ module NexposeRunner
|
|
45
77
|
puts "Scan complete for #{run_details.site_name}, Generating Xml Report"
|
46
78
|
generate_template_report(nsc, site.id, CONSTANTS::XML_REPORT_FILE_NAME, CONSTANTS::XML_REPORT_NAME, CONSTANTS::XML_REPORT_FORMAT)
|
47
79
|
|
48
|
-
[
|
80
|
+
[vulnerabilities, software, policies]
|
49
81
|
end
|
50
82
|
|
51
|
-
def self.verify_run(vulnerabilities)
|
83
|
+
def self.verify_run(vulnerabilities, run_details)
|
84
|
+
|
85
|
+
if run_details.exceptions_list_url.to_s.empty? and vulnerabilities.count > 0
|
86
|
+
raise StandardError, CONSTANTS::VULNERABILITY_FOUND_MESSAGE
|
52
87
|
|
53
|
-
|
88
|
+
elsif vulnerabilities.count == 0
|
89
|
+
puts "No vulnerabilities found!"
|
90
|
+
return true
|
54
91
|
|
92
|
+
elsif allow_vulnerabilities?(vulnerabilities, run_details) == false
|
93
|
+
raise StandardError, CONSTANTS::VULNERABILITY_FOUND_MESSAGE
|
94
|
+
end
|
55
95
|
end
|
56
96
|
|
57
97
|
def self.start_scan(nsc, site, run_details)
|
@@ -62,7 +102,7 @@ module NexposeRunner
|
|
62
102
|
begin
|
63
103
|
sleep(3)
|
64
104
|
stats = nsc.scan_statistics(scan.id)
|
65
|
-
|
105
|
+
status = stats.status
|
66
106
|
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}"
|
67
107
|
end while status == Nexpose::Scan::Status::RUNNING
|
68
108
|
end
|
@@ -85,7 +125,7 @@ module NexposeRunner
|
|
85
125
|
def self.get_new_nexpose_connection(run_details)
|
86
126
|
nsc = Nexpose::Connection.new run_details.connection_url, run_details.username, run_details.password, run_details.port
|
87
127
|
nsc.login
|
88
|
-
puts 'Successfully logged into the Nexpose Server'
|
128
|
+
puts 'Successfully logged into the Nexpose Server!'
|
89
129
|
nsc
|
90
130
|
end
|
91
131
|
|
@@ -2,9 +2,10 @@ require 'yaml'
|
|
2
2
|
require 'nexpose-runner/command_line_arg_parser'
|
3
3
|
|
4
4
|
class ScanRunDescription
|
5
|
-
attr_accessor :connection_url, :username, :password, :port, :site_name, :ip_addresses, :scan_template, :engine
|
5
|
+
attr_accessor :connection_url, :exceptions_list_url, :username, :password, :port, :site_name, :ip_addresses, :scan_template, :engine
|
6
6
|
@@port_value = ''
|
7
7
|
@@ip_addresses = []
|
8
|
+
exceptions_list_url_value = ''
|
8
9
|
|
9
10
|
def initialize(options)
|
10
11
|
if File.file?('config/scan.yml')
|
@@ -14,6 +15,7 @@ class ScanRunDescription
|
|
14
15
|
end
|
15
16
|
|
16
17
|
self.connection_url = options['connection_url']
|
18
|
+
@@exceptions_list_url_value = options['exceptions_list_url']
|
17
19
|
self.username = options['username']
|
18
20
|
self.password = options['password']
|
19
21
|
@@port_value = options['port']
|
@@ -30,6 +32,7 @@ class ScanRunDescription
|
|
30
32
|
raise StandardError, CONSTANTS::REQUIRED_SITE_NAME_MESSAGE if site_name.nil? || site_name.empty?
|
31
33
|
raise StandardError, CONSTANTS::REQUIRED_IP_ADDRESS_MESSAGE if ip_addresses.length == 0
|
32
34
|
raise StandardError, CONSTANTS::REQUIRED_SCAN_TEMPLATE_MESSAGE if scan_template.nil? || scan_template.empty?
|
35
|
+
|
33
36
|
end
|
34
37
|
|
35
38
|
def port=(value)
|
@@ -40,6 +43,14 @@ class ScanRunDescription
|
|
40
43
|
get_value(@@port_value, CONSTANTS::DEFAULT_PORT)
|
41
44
|
end
|
42
45
|
|
46
|
+
def exceptions_list_url=(value)
|
47
|
+
@@exceptions_list_url_value = value
|
48
|
+
end
|
49
|
+
|
50
|
+
def exceptions_list_url
|
51
|
+
get_value(@@exceptions_list_url_value, CONSTANTS::DEFAULT_EXCEPTIONS_URL)
|
52
|
+
end
|
53
|
+
|
43
54
|
def ip_addresses=(value)
|
44
55
|
@@ip_addresses = value.split(',') unless value.nil?
|
45
56
|
end
|
data/spec/scan_spec.rb
CHANGED
@@ -29,7 +29,8 @@ describe 'nexpose-runner' do
|
|
29
29
|
@mock_vuln_report = 'ip_address,title,date_published,severity,summary,fix
|
30
30
|
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>
|
31
31
|
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
|
32
|
-
|
32
|
+
@mock_exceptions = "Database Open Access\nMySQL Obsolete Version"
|
33
|
+
|
33
34
|
@mock_vuln_detail_report = 'stuff'.chomp
|
34
35
|
|
35
36
|
@mock_software_report = 'name,ip_address,host_name,description,description,vendor,name,version
|
@@ -62,7 +63,6 @@ describe 'nexpose-runner' do
|
|
62
63
|
'site_name' => @expected_site_name,
|
63
64
|
'ip_addresses' => @expected_ips,
|
64
65
|
'scan_template' => @expected_scan_template,
|
65
|
-
'exception_file' => @expected_exception_file
|
66
66
|
}
|
67
67
|
|
68
68
|
end
|
@@ -246,6 +246,17 @@ describe 'nexpose-runner' do
|
|
246
246
|
NexposeRunner::Scan.start(@options)
|
247
247
|
}.to raise_error(StandardError, CONSTANTS::VULNERABILITY_FOUND_MESSAGE)
|
248
248
|
end
|
249
|
+
|
250
|
+
it 'should not throw exception if exceptions exist for all vulnerabilities' do
|
251
|
+
expect_report_to_be_called_with(CONSTANTS::VULNERABILITY_REPORT_NAME, CONSTANTS::VULNERABILITY_REPORT_QUERY, @mock_vuln_report)
|
252
|
+
|
253
|
+
options = @options.clone
|
254
|
+
options['exceptions_list_url'] = 'http://google.com'
|
255
|
+
|
256
|
+
expect_exceptions_to_be_called_with(options['exceptions_list_url'])
|
257
|
+
|
258
|
+
NexposeRunner::Scan.start(options)
|
259
|
+
end
|
249
260
|
end
|
250
261
|
|
251
262
|
if File.file?('config/exploit.yml.bak')
|
@@ -253,6 +264,12 @@ describe 'nexpose-runner' do
|
|
253
264
|
end
|
254
265
|
end
|
255
266
|
|
267
|
+
def expect_exceptions_to_be_called_with(exceptions_list_url)
|
268
|
+
uri = URI(exceptions_list_url)
|
269
|
+
expect(Net::HTTP).to receive(:get)
|
270
|
+
.with(uri).and_return(@mock_exceptions)
|
271
|
+
end
|
272
|
+
|
256
273
|
def expect_report_to_be_called_with(report_name, report_query, report_response)
|
257
274
|
expect(@mock_report).to receive(:add_filter)
|
258
275
|
.with('version', '1.3.0')
|
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.15
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nathan Gibson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-10-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nexpose
|
@@ -77,6 +77,7 @@ files:
|
|
77
77
|
- ".gitignore"
|
78
78
|
- ".rspec"
|
79
79
|
- ".travis.yml"
|
80
|
+
- Exclusions.txt
|
80
81
|
- Gemfile
|
81
82
|
- LICENSE.txt
|
82
83
|
- NexposeRunner.gemspec
|
@@ -113,7 +114,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
113
114
|
version: '0'
|
114
115
|
requirements: []
|
115
116
|
rubyforge_project:
|
116
|
-
rubygems_version: 2.
|
117
|
+
rubygems_version: 2.5.2
|
117
118
|
signing_key:
|
118
119
|
specification_version: 4
|
119
120
|
summary: This is a gem that provides the ability to create a new site, add an IP to
|