nexpose 0.2.8 → 0.5.0
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 +18 -9
- data/lib/nexpose/ajax.rb +127 -0
- data/lib/nexpose/alert.rb +29 -36
- data/lib/nexpose/common.rb +13 -12
- data/lib/nexpose/connection.rb +18 -13
- data/lib/nexpose/creds.rb +16 -55
- data/lib/nexpose/dag.rb +73 -0
- data/lib/nexpose/data_table.rb +134 -0
- data/lib/nexpose/device.rb +111 -0
- data/lib/nexpose/engine.rb +194 -0
- data/lib/nexpose/filter.rb +341 -0
- data/lib/nexpose/group.rb +33 -37
- data/lib/nexpose/manage.rb +4 -0
- data/lib/nexpose/pool.rb +142 -0
- data/lib/nexpose/report.rb +72 -278
- data/lib/nexpose/report_template.rb +249 -0
- data/lib/nexpose/scan.rb +196 -54
- data/lib/nexpose/scan_template.rb +103 -0
- data/lib/nexpose/site.rb +91 -237
- data/lib/nexpose/ticket.rb +173 -119
- data/lib/nexpose/user.rb +11 -3
- data/lib/nexpose/vuln.rb +81 -338
- data/lib/nexpose/vuln_exception.rb +368 -0
- metadata +12 -4
- data/lib/nexpose/misc.rb +0 -35
- data/lib/nexpose/scan_engine.rb +0 -325
@@ -0,0 +1,194 @@
|
|
1
|
+
module Nexpose
|
2
|
+
module NexposeAPI
|
3
|
+
include XMLUtils
|
4
|
+
|
5
|
+
# Removes a scan engine from the list of available engines.
|
6
|
+
#
|
7
|
+
# @param [Fixnum] engine_id Unique ID of an existing engine to remove.
|
8
|
+
# @param [String] scope Whether the engine is global or silo scoped.
|
9
|
+
# @return [Boolean] true if engine successfully deleted.
|
10
|
+
#
|
11
|
+
def delete_engine(engine_id, scope = 'silo')
|
12
|
+
xml = make_xml('EngineDeleteRequest',
|
13
|
+
{'engine-id' => engine_id, 'scope' => scope})
|
14
|
+
response = execute(xml, '1.2')
|
15
|
+
response.success
|
16
|
+
end
|
17
|
+
|
18
|
+
# Provide a list of current scan activities for a specific Scan Engine.
|
19
|
+
#
|
20
|
+
# @return [Array[ScanSummary]] Array of ScanSummary objects associated with
|
21
|
+
# each active scan on the engine.
|
22
|
+
#
|
23
|
+
def engine_activity(engine_id)
|
24
|
+
xml = make_xml('EngineActivityRequest', {'engine-id' => engine_id})
|
25
|
+
r = execute(xml)
|
26
|
+
arr = []
|
27
|
+
if r.success
|
28
|
+
r.res.elements.each('//ScanSummary') do |scan_event|
|
29
|
+
arr << ScanSummary.parse(scan_event)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
arr
|
33
|
+
end
|
34
|
+
|
35
|
+
# Retrieve a list of all Scan Engines managed by the Security Console.
|
36
|
+
#
|
37
|
+
# @return [Array[EngineSummary]] Array of EngineSummary objects associated
|
38
|
+
# with each engine associated with this security console.
|
39
|
+
#
|
40
|
+
def list_engines
|
41
|
+
response = execute(make_xml('EngineListingRequest'))
|
42
|
+
arr = []
|
43
|
+
if response.success
|
44
|
+
response.res.elements.each('//EngineSummary') do |engine|
|
45
|
+
arr << EngineSummary.new(engine.attributes['id'].to_i,
|
46
|
+
engine.attributes['name'],
|
47
|
+
engine.attributes['address'],
|
48
|
+
engine.attributes['port'].to_i,
|
49
|
+
engine.attributes['status'])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
arr
|
53
|
+
end
|
54
|
+
|
55
|
+
alias_method :engines, :list_engines
|
56
|
+
end
|
57
|
+
|
58
|
+
# Object representing the current details of a scan engine attached to the
|
59
|
+
# security console.
|
60
|
+
#
|
61
|
+
class EngineSummary
|
62
|
+
|
63
|
+
# A unique ID that identifies this scan engine.
|
64
|
+
attr_reader :id
|
65
|
+
# The name of this scan engine.
|
66
|
+
attr_reader :name
|
67
|
+
# The hostname or IP address of the engine.
|
68
|
+
attr_reader :address
|
69
|
+
# The port there the engine is listening.
|
70
|
+
attr_reader :port
|
71
|
+
# The engine status. One of: active, pending-auth, incompatible,
|
72
|
+
# not-responding, unknown
|
73
|
+
attr_reader :status
|
74
|
+
# A parameter that specifies whether the engine has a global
|
75
|
+
# or silo-specific scope.
|
76
|
+
attr_reader :scope
|
77
|
+
|
78
|
+
def initialize(id, name, address, port, status, scope = 'silo')
|
79
|
+
@id = id
|
80
|
+
@name = name
|
81
|
+
@address = address
|
82
|
+
@port = port
|
83
|
+
@status = status
|
84
|
+
@scope = scope
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Engine connnection to a Nexpose console.
|
89
|
+
#
|
90
|
+
class Engine
|
91
|
+
|
92
|
+
# Unique numeric identifier for the scan engine, assigned by the console
|
93
|
+
# in the order of creation.
|
94
|
+
attr_accessor :id
|
95
|
+
# The IP address or DNS name of a scan engine.
|
96
|
+
attr_accessor :address
|
97
|
+
# A name assigned to the scan engine by the security console.
|
98
|
+
attr_accessor :name
|
99
|
+
# The port on which the engine listens for requests from the security
|
100
|
+
# console.
|
101
|
+
attr_accessor :port
|
102
|
+
# Whether the engine has a global or silo-specific scope.
|
103
|
+
attr_accessor :scope
|
104
|
+
# Relative priority of a scan engine.
|
105
|
+
# One of: very-low, low, normal, high, very-high
|
106
|
+
attr_accessor :priority
|
107
|
+
|
108
|
+
# Sites to which the scan engine is assigned.
|
109
|
+
attr_accessor :sites
|
110
|
+
|
111
|
+
def initialize(address, name = nil, port = 40814)
|
112
|
+
@id = -1
|
113
|
+
@address = address
|
114
|
+
@name = name
|
115
|
+
@name ||= address
|
116
|
+
@port = port
|
117
|
+
@scope = 'silo'
|
118
|
+
@sites = []
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.load(connection, id)
|
122
|
+
xml = '<EngineConfigRequest session-id="' + connection.session_id + '"'
|
123
|
+
xml << %( engine-id="#{id}")
|
124
|
+
xml << ' />'
|
125
|
+
r = connection.execute(xml, '1.2')
|
126
|
+
|
127
|
+
if r.success
|
128
|
+
r.res.elements.each('EngineConfigResponse/EngineConfig') do |config|
|
129
|
+
engine = Engine.new(config.attributes['address'],
|
130
|
+
config.attributes['name'],
|
131
|
+
config.attributes['port'])
|
132
|
+
engine.id = config.attributes['id']
|
133
|
+
engine.scope = config.attributes['scope'] if config.attributes['scope']
|
134
|
+
engine.priority = config.attributes['priority'] if config.attributes['priority']
|
135
|
+
config.elements.each('Site') do |site|
|
136
|
+
engine.sites << SiteSummary.new(site.attributes['id'], site.attributes['name'])
|
137
|
+
end
|
138
|
+
return engine
|
139
|
+
end
|
140
|
+
end
|
141
|
+
nil
|
142
|
+
end
|
143
|
+
|
144
|
+
# Assign a site to this scan engine.
|
145
|
+
#
|
146
|
+
# @param [Fixnum] site_id Unique numerical ID of the site.
|
147
|
+
#
|
148
|
+
def add_site(site_id)
|
149
|
+
sites << SiteSummary.new(site_id, nil)
|
150
|
+
end
|
151
|
+
|
152
|
+
def to_xml
|
153
|
+
xml = '<EngineConfig'
|
154
|
+
xml << %( id="#{id}")
|
155
|
+
xml << %( address="#{address}")
|
156
|
+
xml << %( name="#{name}")
|
157
|
+
xml << %( port="#{port}")
|
158
|
+
xml << %( scope="#{scope}") if scope
|
159
|
+
xml << %( priority="#{priority}") if priority
|
160
|
+
xml << '>'
|
161
|
+
sites.each do |site|
|
162
|
+
xml << %(<Site id="#{site.id}" />)
|
163
|
+
end
|
164
|
+
xml << '</EngineConfig>'
|
165
|
+
xml
|
166
|
+
end
|
167
|
+
|
168
|
+
# Save this engine configuration to the security console.
|
169
|
+
#
|
170
|
+
# @param [Connection] connection Connection to console where site exists.
|
171
|
+
# @return [Fixnum] ID assigned to the scan engine.
|
172
|
+
#
|
173
|
+
def save(connection)
|
174
|
+
xml = '<EngineSaveRequest session-id="' + connection.session_id + '">'
|
175
|
+
xml << to_xml
|
176
|
+
xml << '</EngineSaveRequest>'
|
177
|
+
|
178
|
+
r = connection.execute(xml, '1.2')
|
179
|
+
if r.success
|
180
|
+
r.res.elements.each('EngineSaveResponse/EngineConfig') do |v|
|
181
|
+
return @id = v.attributes['id']
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# Delete this scan engine configuration from the security console.
|
187
|
+
#
|
188
|
+
# @param [Connection] connection Connection to console where site exists.
|
189
|
+
#
|
190
|
+
def delete(connection)
|
191
|
+
connection.delete_engine(@id, @scope)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
@@ -0,0 +1,341 @@
|
|
1
|
+
module Nexpose
|
2
|
+
|
3
|
+
module NexposeAPI
|
4
|
+
|
5
|
+
# Perform an asset filter search that will locate assets matching the
|
6
|
+
# provided conditions.
|
7
|
+
#
|
8
|
+
# For example, the following call will return assets with Java installed:
|
9
|
+
# nsc.filter(Search::Field::SOFTWARE, Search::Operator::CONTAINS, 'java')
|
10
|
+
#
|
11
|
+
# @param [String] field Constant from Search::Field
|
12
|
+
# @param [String] operator Constant from Search::Operator
|
13
|
+
# @param [String] value Search term or constant from Search::Value
|
14
|
+
# @return [Array[Asset]] List of matching assets.
|
15
|
+
#
|
16
|
+
def filter(field, operator, value = '')
|
17
|
+
criterion = Criterion.new(field, operator, value)
|
18
|
+
criteria = Criteria.new(criterion)
|
19
|
+
search(criteria)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Perform a search that will match the criteria provided.
|
23
|
+
#
|
24
|
+
# For example, the following call will return assets with Java and .NET:
|
25
|
+
# java_criterion = Criterion.new(Search::Field::SOFTWARE,
|
26
|
+
# Search::Operator::CONTAINS,
|
27
|
+
# 'java')
|
28
|
+
# dot_net_criterion = Criterion.new(Search::Field::SOFTWARE,
|
29
|
+
# Search::Operator::CONTAINS,
|
30
|
+
# '.net')
|
31
|
+
# criteria = Criteria.new([java_criterion, dot_net_criterion])
|
32
|
+
# results = nsc.search(criteria)
|
33
|
+
#
|
34
|
+
# @param [Criteria] criteria Criteria search object.
|
35
|
+
# @return [Array[Asset]] List of matching assets.
|
36
|
+
#
|
37
|
+
def search(criteria)
|
38
|
+
results = DataTable._get_json_table(self,
|
39
|
+
'/data/asset/filterAssets',
|
40
|
+
criteria._to_payload)
|
41
|
+
results.map { |a| Asset.new(a) }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Constans for performing Asset Filter searches and generating Dynamic Asset
|
46
|
+
# Groups.
|
47
|
+
#
|
48
|
+
module Search
|
49
|
+
module_function
|
50
|
+
|
51
|
+
# Search constants
|
52
|
+
|
53
|
+
# Only these values are accepted for a field value.
|
54
|
+
#
|
55
|
+
module Field
|
56
|
+
|
57
|
+
# Search for an Asset by name.
|
58
|
+
# Valid Operators: IS, IS_NOT, STARTS_WITH, ENDS_WITH, CONTAINS, NOT_CONTAINS
|
59
|
+
ASSET = 'ASSET'
|
60
|
+
|
61
|
+
# Valid Operators: IS, IS_NOT
|
62
|
+
# Valid Values (See Value::AccessComplexity): LOW, MEDIUM, HIGH
|
63
|
+
CVSS_ACCESS_COMPLEXITY = 'CVSS_ACCESS_COMPLEXITY'
|
64
|
+
|
65
|
+
# Valid Operators: IS, IS_NOT
|
66
|
+
# Valid Values (See Value::AccessVector): LOCAL, ADJACENT, NETWORK
|
67
|
+
CVSS_ACCESS_VECTOR = 'CVSS_ACCESS_VECTOR'
|
68
|
+
|
69
|
+
# Valid Operators: IS, IS_NOT
|
70
|
+
# Valid Values (See Value::AuthenticationRequired): NONE, SINGLE, MULTIPLE
|
71
|
+
CVSS_AUTHENTICATION_REQUIRED = 'CVSS_AUTHENTICATION_REQUIRED'
|
72
|
+
|
73
|
+
# Valid Operators: IS, IS_NOT
|
74
|
+
# Valid Values (See Value::CVSSImpact): NONE, PARTIAL, COMPLETE
|
75
|
+
CVSS_AVAILABILITY_IMPACT = 'CVSS_AVAILABILITY_IMPACT'
|
76
|
+
|
77
|
+
# Valid Operators: IS, IS_NOT
|
78
|
+
# Valid Values (See Value::CVSSImpact): NONE, PARTIAL, COMPLETE
|
79
|
+
CVSS_CONFIDENTIALITY_IMPACT = 'CVSS_CONFIDENTIALITY_IMPACT'
|
80
|
+
|
81
|
+
# Valid Operators: IS, IS_NOT
|
82
|
+
# Valid Values (See Value::CVSSImpact): NONE, PARTIAL, COMPLETE
|
83
|
+
CVSS_INTEGRITY_IMPACT = 'CVSS_INTEGRITY_IMPACT'
|
84
|
+
|
85
|
+
# Valid Operators: IS, IS_NOT, IN_RANGE, GREATER_THAN, LESS_THAN
|
86
|
+
# Valid Values: Floats from 0.0 to 10.0
|
87
|
+
CVSS_SCORE = 'CVSS_SCORE'
|
88
|
+
|
89
|
+
# Valid Operators: IN, NOT_IN
|
90
|
+
# Valid Values (See Value::HostType): UNKNOWN, VIRTUAL, HYPERVISOR, BARE_METAL
|
91
|
+
HOST_TYPE = 'HOST_TYPE'
|
92
|
+
|
93
|
+
# Valid Operators: IN, NOT_IN
|
94
|
+
# Valid Values (See Value::IPType): IPv4, IPv6
|
95
|
+
IP_ADDRESS_TYPE = 'IP_ADDRESS_TYPE'
|
96
|
+
|
97
|
+
# Valid Operators: IN
|
98
|
+
# Valid Values (See Value::IPType): IPv4, IPv6
|
99
|
+
IP_ALT_ADDRESS_TYPE = 'IP_ALT_ADDRESS_TYPE'
|
100
|
+
|
101
|
+
# Valid Operators: IN, NOT_IN
|
102
|
+
IP_RANGE = 'IP_RANGE'
|
103
|
+
|
104
|
+
# Valid Operators: CONTAINS, NOT_CONTAINS, IS_EMPTY, IS_NOT_EMPTY
|
105
|
+
OS = 'OS'
|
106
|
+
|
107
|
+
# Valid Operators: IS
|
108
|
+
# Valid Values (See Value::PCICompliance): PASS, FAIL
|
109
|
+
PCI_COMPLIANCE_STATUS = 'PCI_COMPLIANCE_STATUS'
|
110
|
+
|
111
|
+
# Valid Operators: IS, IS_NOT, IN_RANGE, GREATER_THAN, LESS_THAN
|
112
|
+
RISK_SCORE = 'RISK_SCORE'
|
113
|
+
|
114
|
+
# Search based on the last scan date of an asset.
|
115
|
+
# Valid Operators: ON_OR_BEFORE, ON_OR_AFTER, BETWEEN, EARLIER_THAN,
|
116
|
+
# WITHIN_THE_LAST
|
117
|
+
# Valid Values: Use Value::ScanDate::FORMAT for date arguments.
|
118
|
+
# Use FixNum for day arguments.
|
119
|
+
SCAN_DATE = 'SCAN_DATE'
|
120
|
+
|
121
|
+
# Valid Operators: CONTAINS, NOT_CONTAINS
|
122
|
+
SERVICE = 'SERVICE'
|
123
|
+
|
124
|
+
# Search based on the Site ID of an asset.
|
125
|
+
# (Note that underlying search used Site ID, despite 'site name' value.)
|
126
|
+
# Valid Operators: IN, NOT_IN
|
127
|
+
# Valid Values: FixNum Site ID of the site.
|
128
|
+
SITE_ID = 'SITE_NAME'
|
129
|
+
|
130
|
+
# Valid Operators: CONTAINS, NOT_CONTAINS
|
131
|
+
SOFTWARE = 'SOFTWARE'
|
132
|
+
|
133
|
+
# Search against vulnerability titles that an asset contains.
|
134
|
+
# Valid Operators: CONTAINS, NOT_CONTAINS
|
135
|
+
VULNERABILITY = 'VULNERABILITY'
|
136
|
+
|
137
|
+
# Valid Operators: INCLUDE, DO_NOT_INCLUDE
|
138
|
+
# Valid Values (See Value::VulnerabilityExposure): MALWARE, METASPLOIT, DATABASE
|
139
|
+
VULNERABILITY_EXPOSURES = 'VULNERABILITY_EXPOSURES'
|
140
|
+
end
|
141
|
+
|
142
|
+
# List of acceptable operators. Not all fields accept all operators.
|
143
|
+
#
|
144
|
+
module Operator
|
145
|
+
CONTAINS = 'CONTAINS'
|
146
|
+
NOT_CONTAINS = 'NOT_CONTAINS'
|
147
|
+
IS = 'IS'
|
148
|
+
IS_NOT = 'IS_NOT'
|
149
|
+
IN = 'IN'
|
150
|
+
NOT_IN = 'NOT_IN'
|
151
|
+
IN_RANGE = 'IN_RANGE'
|
152
|
+
STARTS_WITH = 'STARTS_WITH'
|
153
|
+
ENDS_WITH = 'ENDS_WITH'
|
154
|
+
ON_OR_BEFORE = 'ON_OR_BEFORE'
|
155
|
+
ON_OR_AFTER = 'ON_OR_AFTER'
|
156
|
+
WITHIN_THE_LAST = 'WITHIN_THE_LAST'
|
157
|
+
GREATER_THAN = 'GREATER_THAN'
|
158
|
+
LESS_THAN = 'LESS_THAN'
|
159
|
+
IS_EMPTY = 'IS_EMPTY'
|
160
|
+
IS_NOT_EMPTY = 'IS_NOT_EMPTY'
|
161
|
+
INCLUDE = 'INCLUDE'
|
162
|
+
DO_NOT_INCLUDE = 'DO_NOT_INCLUDE'
|
163
|
+
end
|
164
|
+
|
165
|
+
# Specialized values used by certain search fields
|
166
|
+
#
|
167
|
+
module Value
|
168
|
+
|
169
|
+
module AccessComplexity
|
170
|
+
LOW = 'L'
|
171
|
+
MEDIUM = 'M'
|
172
|
+
HIGH = 'H'
|
173
|
+
end
|
174
|
+
|
175
|
+
module AccessVector
|
176
|
+
LOCAL = 'L'
|
177
|
+
ADJACENT = 'A'
|
178
|
+
NETWORK = 'N'
|
179
|
+
end
|
180
|
+
|
181
|
+
module AuthenticationRequired
|
182
|
+
NONE = 'N'
|
183
|
+
SINGLE = 'S'
|
184
|
+
MULTIPLE = 'M'
|
185
|
+
end
|
186
|
+
|
187
|
+
module CVSSImpact
|
188
|
+
NONE = 'N'
|
189
|
+
PARTIAL = 'P'
|
190
|
+
COMPLETE = 'C'
|
191
|
+
end
|
192
|
+
|
193
|
+
module HostType
|
194
|
+
UNKNOWN = '0'
|
195
|
+
VIRTUAL = '1'
|
196
|
+
HYPERVISOR = '2'
|
197
|
+
BARE_METAL = '3'
|
198
|
+
end
|
199
|
+
|
200
|
+
module IPType
|
201
|
+
IPv4 = '0'
|
202
|
+
IPv6 = '1'
|
203
|
+
end
|
204
|
+
|
205
|
+
module PCICompliance
|
206
|
+
PASS = '1'
|
207
|
+
FAIL = '0'
|
208
|
+
end
|
209
|
+
|
210
|
+
module ScanDate
|
211
|
+
# Pass this format to #strftime() to get expected format for requests.
|
212
|
+
FORMAT = '%m/%d/%Y'
|
213
|
+
end
|
214
|
+
|
215
|
+
module VulnerabilityExposure
|
216
|
+
MALWARE = 'type:"malware_type", name:"malwarekit"'
|
217
|
+
# TODO: A problem in Nexpose causes these values to not be constant.
|
218
|
+
METASPLOIT = 'type:"exploit_source_type", name:"2"'
|
219
|
+
DATABASE = 'type:"exploit_source_type", name:"1"'
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
# Individual search criterion.
|
225
|
+
#
|
226
|
+
class Criterion
|
227
|
+
|
228
|
+
# Search field. One of Nexpose::Search::Field
|
229
|
+
# @see Nexpose::Search::Field for any restrictions on the other attibutes.
|
230
|
+
attr_accessor :field
|
231
|
+
# Search operator. One of Nexpose::Search::Operator
|
232
|
+
attr_accessor :operator
|
233
|
+
# Search value. A search string or one of Nexpose::Search::Value
|
234
|
+
attr_accessor :value
|
235
|
+
|
236
|
+
def initialize(field, operator, value = '')
|
237
|
+
@field, @operator = field.upcase, operator.upcase
|
238
|
+
if value.kind_of? Array
|
239
|
+
@value = value.map { |v| v.to_s }
|
240
|
+
else
|
241
|
+
@value = value.to_s
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
# Convert this object into the map format expected by Nexpose.
|
246
|
+
#
|
247
|
+
def to_map
|
248
|
+
{ 'metadata' => { 'fieldName' => field },
|
249
|
+
'operator' => operator,
|
250
|
+
'values' => value.kind_of?(Array) ? value : [value] }
|
251
|
+
end
|
252
|
+
|
253
|
+
def self.parse(json)
|
254
|
+
Criterion.new(json['metadata']['fieldName'],
|
255
|
+
json['operator'],
|
256
|
+
json['values'])
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
# Join search criteria for an asset filter search or dynamic asset group.
|
261
|
+
#
|
262
|
+
class Criteria
|
263
|
+
|
264
|
+
# Whether to match any or all filters. One of 'OR' or 'AND'.
|
265
|
+
attr_accessor :match
|
266
|
+
# Array of criteria to match against.
|
267
|
+
attr_accessor :criteria
|
268
|
+
|
269
|
+
def initialize(criteria = [], match = 'AND')
|
270
|
+
if criteria.kind_of?(Array)
|
271
|
+
@criteria = criteria
|
272
|
+
else
|
273
|
+
@criteria = [criteria]
|
274
|
+
end
|
275
|
+
@match = match.upcase
|
276
|
+
end
|
277
|
+
|
278
|
+
def to_map
|
279
|
+
{ 'operator' => @match,
|
280
|
+
'criteria' => @criteria.map { |c| c.to_map } }
|
281
|
+
end
|
282
|
+
|
283
|
+
# Convert this object into the format expected by Nexpose.
|
284
|
+
#
|
285
|
+
def to_json
|
286
|
+
JSON.generate(to_map)
|
287
|
+
end
|
288
|
+
|
289
|
+
# Generate the payload needed for a POST request for Asset Filter.
|
290
|
+
#
|
291
|
+
def _to_payload
|
292
|
+
{ 'dir' => -1,
|
293
|
+
'results' => -1,
|
294
|
+
'sort' => 'assetIP',
|
295
|
+
'startIndex' => -1,
|
296
|
+
'table-id' => 'assetfilter',
|
297
|
+
'searchCriteria' => to_json }
|
298
|
+
end
|
299
|
+
|
300
|
+
def self.parse(json)
|
301
|
+
ret = Criteria.new([], json['operator'])
|
302
|
+
json['criteria'].each do |c|
|
303
|
+
ret.criteria << Criterion.parse(c)
|
304
|
+
end
|
305
|
+
ret
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
# Asset data as returned by an Asset Filter search.
|
310
|
+
#
|
311
|
+
class Asset
|
312
|
+
|
313
|
+
# Unique identifier of this asset. Also known as device ID.
|
314
|
+
attr_reader :id
|
315
|
+
|
316
|
+
attr_reader :ip
|
317
|
+
attr_reader :name
|
318
|
+
attr_reader :os
|
319
|
+
|
320
|
+
attr_reader :exploit_count
|
321
|
+
attr_reader :malware_count
|
322
|
+
attr_reader :vuln_count
|
323
|
+
attr_reader :risk_score
|
324
|
+
|
325
|
+
attr_reader :site_id
|
326
|
+
attr_reader :last_scan
|
327
|
+
|
328
|
+
def initialize(json)
|
329
|
+
@id = json['assetID']['ID'].to_i
|
330
|
+
@ip = json['assetIP']
|
331
|
+
@name = json['assetName']
|
332
|
+
@os = json['assetOSName']
|
333
|
+
@exploit_count = json['exploitCount'].to_i
|
334
|
+
@malware_count = json['malwareCount'].to_i
|
335
|
+
@vuln_count = json['vulnCount'].to_i
|
336
|
+
@risk_score = json['riskScore'].to_f
|
337
|
+
@site_id = json['siteID']
|
338
|
+
@last_scan = Time.at(json['lastScanDate'] / 1000)
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|