nexpose 0.2.8 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|