nexpose 0.8.8 → 0.8.9
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/lib/nexpose.rb +1 -0
- data/lib/nexpose/global_settings.rb +117 -0
- data/lib/nexpose/settings.xml +14 -0
- data/lib/nexpose/site.rb +2 -11
- data/lib/nexpose/util.rb +57 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6698a2821faefa7c1822f324cc1bfd355869ad7f
|
4
|
+
data.tar.gz: 3a14efb354cbc7036cf829f60088ceeaa0f7a80b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 219848942e56f658d1a3f43e8411ded2c12e693d5f258106196ddca5121c3434be50c17dffd45fe61504d5d76050bd77c123ea3d844e5f07a65656a353b03ea2
|
7
|
+
data.tar.gz: 89ae18299c87ef71a2805038ef3f1e3c7ab7a53cf87e0c66f799dbaa83e024f7714dc4198854d509156646472aea80d344f215c2de7b4fcfa6d71535ca87ef97
|
data/lib/nexpose.rb
CHANGED
@@ -0,0 +1,117 @@
|
|
1
|
+
module Nexpose
|
2
|
+
# Object used to manage the global settings of a Nexpose console.
|
3
|
+
#
|
4
|
+
class GlobalSettings
|
5
|
+
# IP addresses and/or host names that will be excluded from scanning across
|
6
|
+
# all sites.
|
7
|
+
attr_accessor :asset_exclusions
|
8
|
+
|
9
|
+
# Whether control scanning in enabled. A feature tied to ControlsInsight
|
10
|
+
# integration.
|
11
|
+
attr_accessor :control_scanning
|
12
|
+
|
13
|
+
# XML document representing the entire configuration.
|
14
|
+
attr_reader :xml
|
15
|
+
|
16
|
+
# Private constructor. See #load method for retrieving a settings object.
|
17
|
+
#
|
18
|
+
def initialize(xml)
|
19
|
+
@xml = xml
|
20
|
+
|
21
|
+
@asset_exclusions = HostOrIP.parse(xml)
|
22
|
+
@control_scanning = _get_control_scanning(xml)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns true if controls scanning is enabled.
|
26
|
+
def control_scanning?
|
27
|
+
control_scanning
|
28
|
+
end
|
29
|
+
|
30
|
+
# Save any updates to this settings object to the Nexpose console.
|
31
|
+
#
|
32
|
+
# @param [Connection] nsc Connection to a Nexpose console.
|
33
|
+
# @return [Boolean] Whether saving was successful.
|
34
|
+
#
|
35
|
+
def save(nsc)
|
36
|
+
# load method can return XML missing this required attribute.
|
37
|
+
unless REXML::XPath.first(xml, '//*[@recalculation_duration]')
|
38
|
+
risk_model = REXML::XPath.first(xml, '//riskModel')
|
39
|
+
risk_model.add_attribute('recalculation_duration', 'do_not_recalculate')
|
40
|
+
end
|
41
|
+
|
42
|
+
_replace_exclusions(xml, asset_exclusions)
|
43
|
+
_set_control_scanning(xml, control_scanning)
|
44
|
+
|
45
|
+
response = AJAX.post(nsc, '/data/admin/global-settings', xml)
|
46
|
+
XMLUtils.success? response
|
47
|
+
end
|
48
|
+
|
49
|
+
# Add an asset exclusion setting.
|
50
|
+
#
|
51
|
+
# @param [IPRange|HostName|String] host_or_ip Host or IP (range) to exclude
|
52
|
+
# from scanning by the Nexpose console.
|
53
|
+
#
|
54
|
+
def add_exclusion(host_or_ip)
|
55
|
+
asset = host_or_ip
|
56
|
+
unless host_or_ip.respond_to?(:host) || host_or_ip.respond_to?(:from)
|
57
|
+
asset = HostOrIP.convert(host_or_ip)
|
58
|
+
end
|
59
|
+
@asset_exclusions << asset
|
60
|
+
end
|
61
|
+
|
62
|
+
# Remove an asset exclusion setting.
|
63
|
+
# If you need to remove a range of IPs, be sure to explicitly supply an
|
64
|
+
# IPRange object to the method.
|
65
|
+
#
|
66
|
+
# @param [IPRange|HostName|String] host_or_ip Host or IP (range) to remove
|
67
|
+
# from the exclusion list.
|
68
|
+
#
|
69
|
+
def remove_exclusion(host_or_ip)
|
70
|
+
asset = host_or_ip
|
71
|
+
unless host_or_ip.respond_to?(:host) || host_or_ip.respond_to?(:from)
|
72
|
+
# Attept to convert String to appropriate object.
|
73
|
+
asset = HostOrIP.convert(host_or_ip)
|
74
|
+
end
|
75
|
+
@asset_exclusions = asset_exclusions.reject { |a| a.eql? asset }
|
76
|
+
end
|
77
|
+
|
78
|
+
# Load the global settings from a Nexpose console.
|
79
|
+
#
|
80
|
+
# @param [Connection] nsc Connection to a Nexpose console.
|
81
|
+
# @return [GlobalSettings] Settings object for the console.
|
82
|
+
#
|
83
|
+
def self.load(nsc)
|
84
|
+
response = AJAX.get(nsc, '/data/admin/global-settings')
|
85
|
+
new(REXML::Document.new(response))
|
86
|
+
end
|
87
|
+
|
88
|
+
# Internal method for updating exclusions before saving.
|
89
|
+
def _replace_exclusions(xml, exclusions)
|
90
|
+
xml.elements.delete('//ExcludedHosts')
|
91
|
+
elem = xml.root.add_element('ExcludedHosts')
|
92
|
+
exclusions.each do |exclusion|
|
93
|
+
elem.add_element(exclusion.as_xml)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Internal method for parsing XML for whether control scanning in enabled.
|
98
|
+
def _get_control_scanning(xml)
|
99
|
+
enabled = false
|
100
|
+
if elem = REXML::XPath.first(xml, '//enableControlsScan[@enabled]')
|
101
|
+
enabled = elem.attribute('enabled').value.to_i == 1
|
102
|
+
end
|
103
|
+
enabled
|
104
|
+
end
|
105
|
+
|
106
|
+
# Internal method for updating control scanning before saving.
|
107
|
+
def _set_control_scanning(xml, enabled)
|
108
|
+
if elem = REXML::XPath.first(xml, '//enableControlsScan')
|
109
|
+
elem.attributes['enabled'] = enabled ? '1' : '0'
|
110
|
+
else
|
111
|
+
elem = REXML::Element.new('ControlsScan', xml.root)
|
112
|
+
elem.add_element('enableControlsScan',
|
113
|
+
'enabled' => enabled ? '1' : '0')
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<GlobalSettings>
|
2
|
+
<riskModel id="real_risk" recalculation_duration="do_not_recalculate"></riskModel>
|
3
|
+
<RiskConfig includeRiskModifiers="false">
|
4
|
+
<RiskModifiers veryHigh="2" high="1.5" medium="1" low="0.75" veryLow="0.5"></RiskModifiers>
|
5
|
+
</RiskConfig>
|
6
|
+
<ControlsScan>
|
7
|
+
<enableControlsScan enabled="0"></enableControlsScan>
|
8
|
+
</ControlsScan>
|
9
|
+
<ExcludedHosts>
|
10
|
+
<range from="10.4.18.219"></range>
|
11
|
+
<range from="10.4.25.117"></range>
|
12
|
+
</ExcludedHosts>
|
13
|
+
</GlobalSettings>
|
14
|
+
|
data/lib/nexpose/site.rb
CHANGED
@@ -217,17 +217,8 @@ module Nexpose
|
|
217
217
|
# @param [String] asset Identifier of an asset, either IP or host name.
|
218
218
|
#
|
219
219
|
def add_asset(asset)
|
220
|
-
|
221
|
-
|
222
|
-
ip = IPAddr.new(asset)
|
223
|
-
add_ip(asset)
|
224
|
-
rescue ArgumentError => e
|
225
|
-
if e.message == 'invalid address'
|
226
|
-
add_host(asset)
|
227
|
-
else
|
228
|
-
raise "Unable to parse asset: '#{asset}'. #{e.message}"
|
229
|
-
end
|
230
|
-
end
|
220
|
+
obj = HostOrIP.convert(asset)
|
221
|
+
@assets << obj
|
231
222
|
end
|
232
223
|
|
233
224
|
# Remove an asset to this site, resolving whether an IP or hostname is
|
data/lib/nexpose/util.rb
CHANGED
@@ -34,5 +34,62 @@ module Nexpose
|
|
34
34
|
|
35
35
|
xml
|
36
36
|
end
|
37
|
+
|
38
|
+
# Check a typical Nexpose XML response for success.
|
39
|
+
# Typically, the root element has a 'success' attribute, and its value is
|
40
|
+
# '1' if the call succeeded.
|
41
|
+
#
|
42
|
+
def self.success?(xml_string)
|
43
|
+
xml = ::REXML::Document.new(xml_string.to_s)
|
44
|
+
success = ::REXML::XPath.first(xml, '//@success')
|
45
|
+
!success.nil? && success.value.to_i == 1
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Function module for dealing with String to HostName|IPRange conversions.
|
50
|
+
#
|
51
|
+
module HostOrIP
|
52
|
+
module_function
|
53
|
+
|
54
|
+
# Convert a host or IP address to the corresponding HostName or IPRange
|
55
|
+
# class.
|
56
|
+
#
|
57
|
+
# If the String cannot be converted, it will raise an error.
|
58
|
+
#
|
59
|
+
# @param [String] asset String representation of an IP or host name.
|
60
|
+
# @return [IPRange|HostName] Valid class, if it can be converted.
|
61
|
+
#
|
62
|
+
def convert(asset)
|
63
|
+
begin
|
64
|
+
# Use IPAddr construtor validation to see if it's an IP.
|
65
|
+
IPAddr.new(asset)
|
66
|
+
IPRange.new(asset)
|
67
|
+
rescue ArgumentError => e
|
68
|
+
if e.message == 'invalid address'
|
69
|
+
HostName.new(asset)
|
70
|
+
else
|
71
|
+
raise "Unable to parse asset: '#{asset}'. #{e.message}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Parse a REXML::Document or REXML::Element for any hosts listed and convert
|
77
|
+
# them to HostName and IPRange objects.
|
78
|
+
#
|
79
|
+
# @param [REXML::Document|REXML::Element] xml REXML class potentially
|
80
|
+
# containing host references.
|
81
|
+
# @return [Array[HostName|IPRange]] Collection of parsed hosts.
|
82
|
+
#
|
83
|
+
def parse(xml)
|
84
|
+
coll = []
|
85
|
+
xml.elements.each('//range') do |elem|
|
86
|
+
to = elem.attribute('to').nil? ? nil : elem.attribute('to').value
|
87
|
+
coll << IPRange.new(elem.attribute('from').value, to)
|
88
|
+
end
|
89
|
+
xml.elements.each('//host') do |elem|
|
90
|
+
coll << HostName.new(elem.text)
|
91
|
+
end
|
92
|
+
coll
|
93
|
+
end
|
37
94
|
end
|
38
95
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nexpose
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- HD Moore
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2014-10-
|
14
|
+
date: 2014-10-20 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: rex
|
@@ -85,6 +85,7 @@ files:
|
|
85
85
|
- lib/nexpose/engine.rb
|
86
86
|
- lib/nexpose/error.rb
|
87
87
|
- lib/nexpose/filter.rb
|
88
|
+
- lib/nexpose/global_settings.rb
|
88
89
|
- lib/nexpose/group.rb
|
89
90
|
- lib/nexpose/maint.rb
|
90
91
|
- lib/nexpose/manage.rb
|
@@ -95,6 +96,7 @@ files:
|
|
95
96
|
- lib/nexpose/role.rb
|
96
97
|
- lib/nexpose/scan.rb
|
97
98
|
- lib/nexpose/scan_template.rb
|
99
|
+
- lib/nexpose/settings.xml
|
98
100
|
- lib/nexpose/shared_cred.rb
|
99
101
|
- lib/nexpose/silo.rb
|
100
102
|
- lib/nexpose/silo_profile.rb
|