Module: Nexpose::NexposeAPI

Includes:
XMLUtils
Included in:
Connection
Defined in:
lib/nexpose/misc.rb,
lib/nexpose/role.rb,
lib/nexpose/scan.rb,
lib/nexpose/silo.rb,
lib/nexpose/site.rb,
lib/nexpose/vuln.rb,
lib/nexpose/vuln.rb,
lib/nexpose/group.rb,
lib/nexpose/manage.rb,
lib/nexpose/report.rb,
lib/nexpose/ticket.rb,
lib/nexpose/scan_engine.rb

Instance Method Summary collapse

Methods included from XMLUtils

#make_xml, #parse_xml

Instance Method Details

#asset_group_delete(id) ⇒ Boolean Also known as: delete_asset_group

Delete an asset group and all associated data.

Parameters:

  • id (Fixnum)

    Asset group ID to delete.

Returns:

  • (Boolean)

    Whether group deletion succeeded.



11
12
13
14
# File 'lib/nexpose/group.rb', line 11

def asset_group_delete(id)
  r = execute(make_xml('AssetGroupDeleteRequest', {'group-id' => id}))
  r.success
end

#asset_groupsArray[AssetGroupSummary] Also known as: asset_groups_listing, groups

Retrieve a list of all asset groups the user is authorized to view or manage.

Returns:



23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/nexpose/group.rb', line 23

def asset_groups
  r = execute(make_xml('AssetGroupListingRequest'))

  res = []
  if r.success
    r.res.elements.each('AssetGroupListingResponse/AssetGroupSummary') do |group|
      res << AssetGroupSummary.new(group.attributes['id'].to_i,
                                   group.attributes['name'].to_s,
                                   group.attributes['description'].to_s,
                                   group.attributes['riskscore'].to_f)
    end
  end
  res
end

#console_command(cmd_string) ⇒ Object

Execute an arbitrary console command that is supplied as text via the supplied parameter. Console commands are documented in the administrator’s guide. If you use a command that is not listed in the administrator’s guide, the application will return the XMLResponse.



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/nexpose/manage.rb', line 10

def console_command(cmd_string)
  xml = make_xml('ConsoleCommandRequest', {})
  cmd = REXML::Element.new('Command')
  cmd.text = cmd_string
  xml << cmd

  r = execute(xml)
  if r.success
    r.res.elements.each('//Output') do |out|
      return out.text.to_s
    end
  else
    false
  end
end

#create_multi_tenant_user(user_config, silo_configs) ⇒ Object


Creates a multi-tenant user

user_config - A map of the user data.

REQUIRED PARAMS user-id, authsrcid, user-name, full-name, enabled, superuser

OPTIONAL PARAMS email, password

silo_configs - An array of maps of silo specific data

REQUIRED PARAMS silo-id, role-name, all-groups, all-sites, default-silo

allowed_groups/allowed_sites - An array of ids




31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/nexpose/silo.rb', line 31

def create_multi_tenant_user(user_config, silo_configs)
  xml = make_xml('MultiTenantUserCreateRequest')
  mtu_config_xml = make_xml('MultiTenantUserConfig', user_config, '', false)

  # Add the silo access
  silo_xml = make_xml('SiloAccesses', {}, '', false)
  silo_configs.each do |silo_config|
    silo_config_xml = make_xml('SiloAccess', {}, '', false)
    silo_config.keys.each do |k|
      if k == 'allowed_sites'
        allowed_sites_xml = make_xml('AllowedSites', {}, '', false)
        silo_config['allowed_sites'].each do |allowed_site|
          allowed_sites_xml.add_element(make_xml('AllowedSite', {'id' => allowed_site}, '', false))
        end
        silo_config_xml.add_element(allowed_sites_xml)
      elsif k == 'allowed_groups'
        allowed_groups_xml = make_xml('AllowedGroups', {}, '', false)
        silo_config['allowed_groups'].each do |allowed_group|
          allowed_groups_xml.add_element(make_xml('AllowedGroup', {'id' => allowed_group}, '', false))
        end
        silo_config_xml.add_element(allowed_groups_xml)
      else
        silo_config_xml.attributes[k] = silo_config[k]
      end
    end
    silo_xml.add_element(silo_config_xml)
  end
  mtu_config_xml.add_element(silo_xml)
  xml.add_element(mtu_config_xml)
  r = execute(xml, '1.2')
  r.success
end

#create_silo(silo_config) ⇒ Object


Creates a silo

silo_config - A map of the silo creation data.

REQUIRED PARAMS id, name, silo-profile-id, max-assets, max-hosted-assets, max-users

OPTIONAL PARAMS description




238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
# File 'lib/nexpose/silo.rb', line 238

def create_silo silo_config
  xml = make_xml 'SiloCreateRequest'
  silo_config_xml = make_xml 'SiloConfig', {}, '', false

  # Add the attributes
  silo_config.keys.each do |key|
    if not 'merchant'.eql? key and not 'organization'.eql? key
      silo_config_xml.attributes[key] = silo_config[key]
    end
  end

  # Add Organization info
  if silo_config['organization']
    org_xml = make_xml 'Organization', {}, '', false
    silo_config['organization'].keys.each do |key|
      if not 'address'.eql? key
        org_xml.attributes[key] = silo_config['organization'][key]
      end
    end

    address_xml = make_xml 'Address', silo_config['organization']['address'], '', false
    org_xml.add_element address_xml
    silo_config_xml.add_element org_xml
  end

  # Add Merchant info
  if silo_config['merchant']
    merchant_xml = make_xml 'Merchant', {}, '', false

    silo_config['merchant'].keys.each do |key|
      if not 'dba'.eql? key and not 'other_industries'.eql? key and not 'qsa'.eql? key and not 'address'.eql? key
        merchant_xml.attributes[key] = silo_config['merchant'][key]
      end
    end

    # Add the merchant address
    merchant_address_xml = make_xml 'Address', silo_config['merchant']['address'], '', false
    merchant_xml.add_element merchant_address_xml

    #Now add the complex data types
    if silo_config['merchant']['dba']
      dba_xml = make_xml 'DBAs', {}, '', false
      silo_config['merchant']['dba'].each do |name|
        dba_xml.add_element make_xml('DBA', {'name' => name}, '', false)
      end
      merchant_xml.add_element dba_xml
    end

    if silo_config['merchant']['other_industries']
      ois_xml = make_xml 'OtherIndustries', {}, '', false
      silo_config['merchant']['other_industries'].each do |name|
        ois_xml.add_element make_xml('Industry', {'name' => name}, '', false)
      end
      merchant_xml.add_element ois_xml
    end

    if silo_config['merchant']['qsa']
      qsa_xml = make_xml 'QSA', {}, '', false
      silo_config['merchant']['qsa'].keys.each do |key|
        if not 'address'.eql? key
          qsa_xml.attributes[key] = silo_config['merchant']['qsa'][key]
        end
      end

      # Add the address for this QSA
      address_xml = make_xml 'Address', silo_config['merchant']['qsa']['address'], '', false

      qsa_xml.add_element address_xml
      merchant_xml.add_element qsa_xml
    end
    silo_config_xml.add_element merchant_xml
  end

  xml.add_element silo_config_xml
  r = execute xml, '1.2'
  r.success
end

#create_silo_profile(silo_profile_config, permissions) ⇒ Object


Creates a silo profile

silo_config - A map of the silo data.

REQUIRED PARAMS id, name, all‐licensed-modules, all‐global-engines, all-global-report-templates, all‐global-scan‐templates

OPTIONAL PARAMS description

permissions - A map of an array of maps of silo specific data

REQUIRED PARAMS silo-id, role-name, all-groups, all-sites, default-silo

allowed_groups/allowed_sites - An array of ids




124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/nexpose/silo.rb', line 124

def create_silo_profile silo_profile_config, permissions
  xml = make_xml 'SiloProfileCreateRequest'
  spc_xml = make_xml('SiloProfileConfig', silo_profile_config, '', false)

  # Add the permissions
  if permissions['global_report_templates']
    grt_xml = make_xml('GlobalReportTemplates', {}, '', false)
    permissions['global_report_templates'].each do |name|
      grt_xml.add_element make_xml('GlobalReportTemplate', {'name' => name}, '', false)
    end
    spc_xml.add_element grt_xml
  end

  if permissions['global_scan_engines']
    gse_xml = make_xml('GlobalScanEngines', {}, '', false)
    permissions['global_scan_engines'].each do |name|
      gse_xml.add_element make_xml('GlobalScanEngine', {'name' => name}, '', false)
    end
    spc_xml.add_element gse_xml
  end

  if permissions['global_scan_templates']
    gst_xml = make_xml('GlobalScanTemplates', {}, '', false)
    permissions['global_scan_templates'].each do |name|
      gst_xml.add_element make_xml('GlobalScanTemplate', {'name' => name}, '', false)
    end
    spc_xml.add_element gst_xml
  end

  if permissions['licensed_modules']
    lm_xml = make_xml('LicensedModules', {}, '', false)
    permissions['licensed_modules'].each do |name|
      lm_xml.add_element make_xml('LicensedModule', {'name' => name}, '', false)
    end
    spc_xml.add_element lm_xml
  end

  if permissions['restricted_report_formats']
    rrf_xml = make_xml('RestrictedReportFormats', {}, '', false)
    permissions['restricted_report_formats'].each do |name|
      rrf_xml.add_element make_xml('RestrictedReportFormat', {'name' => name}, '', false)
    end
    spc_xml.add_element rrf_xml
  end

  if permissions['restricted_report_sections']
    rrs_xml = make_xml('RestrictedReportSections', {}, '', false)
    permissions['restricted_report_sections'].each do |name|
      rrs_xml.add_element make_xml('RestrictedReportSection', {'name' => name}, '', false)
    end
    spc_xml.add_element rrs_xml
  end

  xml.add_element spc_xml
  r = execute xml, '1.2'
  r.success
end

#create_ticket(ticket_info) ⇒ Object

Create a Nexpose ticket

ticket_info: A hash of the data to be used to create a ticket in Nexpose: :name => The name of the ticket (Required) :device_id => The Nexpose device ID for the device being ticketed (Required) :assigned_to => The Nexpose user to whom this ticket is assigned (Required) :priority => “low,moderate,normal,high,critical” (Required)

:vulnerabilities => An array of Nexpose vuln IDs. This is NOT the same as vuln ID. (Required) :comments => An array of comments to accompany this ticket

Returns:

  • The ticket ID if the ticket creation was successful, false otherwise



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/nexpose/ticket.rb', line 84

def create_ticket(ticket_info)
  ticket_name = ticket_info[:name]
  unless ticket_name
    raise ArgumentError.new 'Ticket name is required'
  end

  device_id = ticket_info[:device_id]
  unless device_id
    raise ArgumentError.new 'Device ID is required'
  end

  assigned_to = ticket_info[:assigned_to]
  unless assigned_to
    raise ArgumentError.new 'Assignee name is required'
  end

  priority = ticket_info[:priority]
  unless priority
    raise ArgumentError.new 'Ticket priority is required'
  end

  vulnerabilities = ticket_info[:vulnerabilities]
  if not vulnerabilities or vulnerabilities.count < 1
    raise ArgumentError.new 'Vulnerabilities are required'
  end

  comments = ticket_info[:comments]
  base_xml = make_xml 'TicketCreateRequest'

  required_attributes = {
      'name' => ticket_name,
      'priority' => priority,
      'device-id' => device_id,
      'assigned-to' => assigned_to
  }

  create_request_xml = REXML::Element.new 'TicketCreate'
  create_request_xml.add_attributes required_attributes

  # Add vulnerabilities
  vulnerabilities_xml = REXML::Element.new 'Vulnerabilities'
  vulnerabilities.each do |vuln_id|
    vulnerabilities_xml.add_element 'Vulnerability', {'id' => vuln_id}
  end
  create_request_xml.add_element vulnerabilities_xml

  # Add comments
  if comments and comments.count > 0
    comments_xml = REXML::Element.new 'Comments'
    comments.each do |comment|
      comment_xml = REXML::Element.new 'Comment'
      comment_xml.add_text comment
      comments_xml.add_element comment_xml
    end

    create_request_xml.add_element comments_xml
  end

  base_xml.add_element create_request_xml
  r = execute base_xml, '1.2'
  if r.success
    r.res.elements.each('TicketCreateResponse') do |group|
      return group.attributes['id'].to_i
    end
  else
    false
  end
end

#delete_engine(engine_id) ⇒ Object

Removes a scan engine from the list of available engines.



6
7
8
9
# File 'lib/nexpose/scan_engine.rb', line 6

def delete_engine(engine_id)
  xml = make_xml('EngineDeleteRequest', {'engine-id' => engine_id})
  execute(xml, '1.2')
end

#delete_mtu(user_name, user_id) ⇒ Object


Delete a multi-tenant user




95
96
97
98
99
100
# File 'lib/nexpose/silo.rb', line 95

def delete_mtu user_name, user_id
  using_user_name = (user_name and not user_name.empty?)
  xml = make_xml('MultiTenantUserDeleteRequest', (using_user_name ? {'user-name' => user_name} : {'user-id' => user_id}))
  r = execute xml, '1.2'
  r.success
end

#delete_report(report_id) ⇒ Object

Delete a previously generated report.



50
51
52
53
# File 'lib/nexpose/report.rb', line 50

def delete_report(report_id)
  xml = make_xml('ReportDeleteRequest', {'report-id' => report_id})
  execute(xml).success
end

#delete_report_config(report_config_id) ⇒ Object

Delete a previously generated report definition. Also deletes any reports generated from that configuration.



44
45
46
47
# File 'lib/nexpose/report.rb', line 44

def delete_report_config(report_config_id)
  xml = make_xml('ReportDeleteRequest', {'reportcfg-id' => report_config_id})
  execute(xml).success
end

#delete_silo(name, id) ⇒ Object


Delete a silo




341
342
343
344
345
346
# File 'lib/nexpose/silo.rb', line 341

def delete_silo name, id
  using_name = (name and not name.empty?)
  xml = make_xml('SiloDeleteRequest', (using_name ? {'silo-name' => name} : {'silo-id' => id}))
  r = execute xml, '1.2'
  r.success
end

#delete_silo_profile(name, id) ⇒ Object


Delete a silo profile




216
217
218
219
220
221
# File 'lib/nexpose/silo.rb', line 216

def delete_silo_profile name, id
  using_name = (name and not name.empty?)
  xml = make_xml('SiloProfileDeleteRequest', (using_name ? {'name' => name} : {'silo-profile-id' => id}))
  r = execute xml, '1.2'
  r.success
end

#delete_ticket(ticket) ⇒ Boolean

Deletes a Nexpose ticket.

Parameters:

  • ticket (Fixnum)

    Unique ID of the ticket to delete.

Returns:

  • (Boolean)

    Whether or not the ticket deletion succeeded.



50
51
52
# File 'lib/nexpose/ticket.rb', line 50

def delete_ticket(ticket)
  delete_tickets([ticket])
end

#delete_tickets(tickets) ⇒ Boolean Also known as: ticket_delete

Deletes a Nexpose ticket.

Parameters:

  • tickets (Array[Fixnum])

    Array of unique IDs of tickets to delete.

Returns:

  • (Boolean)

    Whether or not the ticket deletions succeeded.



59
60
61
62
63
64
65
66
# File 'lib/nexpose/ticket.rb', line 59

def delete_tickets(tickets)
  xml = make_xml('TicketDeleteRequest')
  tickets.each do |id|
    xml.add_element('Ticket', {'id' => id})
  end

  (execute xml, '1.2').success
end

#device_delete(param) ⇒ Object



5
6
7
8
# File 'lib/nexpose/misc.rb', line 5

def device_delete(param)
  r = execute(make_xml('DeviceDeleteRequest', {'device-id' => param}))
  r.success
end

#engine_activity(engine_id) ⇒ Array[ScanSummary]

Provide a list of current scan activities for a specific Scan Engine.

Returns:

  • (Array[ScanSummary])

    Array of ScanSummary objects associated with each active scan on the engine.



16
17
18
19
20
21
22
23
24
25
26
# File 'lib/nexpose/scan_engine.rb', line 16

def engine_activity(engine_id)
  xml = make_xml('EngineActivityRequest', {'engine-id' => engine_id})
  r = execute(xml)
  arr = []
  if r.success
    r.res.elements.each('//ScanSummary') do |scan_event|
      arr << ScanSummary.parse(scan_event)
    end
  end
  arr
end

#generate_report(report_id, wait = false) ⇒ Object

Generate a new report using the specified report definition.



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/nexpose/report.rb', line 6

def generate_report(report_id, wait = false)
  xml = make_xml('ReportGenerateRequest', {'report-id' => report_id})
  response = execute(xml)
  if response.success
    response.res.elements.each('//ReportSummary') do |summary|
      summary = ReportSummary.parse(summary)
      # If not waiting or the report is finished, return now.
      return summary unless wait and summary.status == 'Started'
    end
  end
  so_far = 0
  while wait
    summary = last_report(report_id)
    return summary unless summary.status == 'Started'
    sleep 5
    so_far += 5
    if so_far % 60 == 0
      puts "Still waiting. Current status: #{summary.status}"
    end
  end
  nil
end

#get_report_config(report_config_id) ⇒ Object

Retrieve the configuration for a report definition.



88
89
90
91
# File 'lib/nexpose/report.rb', line 88

def get_report_config(report_config_id)
  xml = make_xml('ReportConfigRequest', {'reportcfg-id' => report_config_id})
  ReportConfig.parse(execute(xml))
end

#get_report_template(template_id) ⇒ Object

Retrieve the configuration for a report template.



69
70
71
72
# File 'lib/nexpose/report.rb', line 69

def get_report_template(template_id)
  xml = make_xml('ReportTemplateConfigRequest', {'template-id' => template_id})
  ReportTemplate.parse(execute(xml))
end

#last_report(report_config_id) ⇒ Object

Get the details of the last report generated with the specified report id.



37
38
39
40
# File 'lib/nexpose/report.rb', line 37

def last_report(report_config_id)
  history = report_history(report_config_id)
  history.sort { |a, b| b.generated_on <=> a.generated_on }.first
end

#last_scan(site_id) ⇒ Object

Retrieve the scan summary statistics for the latest completed scan on a site.

Method will not return data on an active scan.

Parameters:

  • site_id (FixNum)

    Site ID to find latest scan for.



92
93
94
# File 'lib/nexpose/site.rb', line 92

def last_scan(site_id)
  site_scan_history(site_id).select { |scan| scan.end_time }.max_by { |scan| scan.end_time }
end

#list_enginesArray[EngineSummary] Also known as: engines

Retrieve a list of all Scan Engines managed by the Security Console.

Returns:

  • (Array[EngineSummary])

    Array of EngineSummary objects associated with each engine associated with this security console.



33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/nexpose/scan_engine.rb', line 33

def list_engines
  response = execute(make_xml('EngineListingRequest'))
  arr = []
  if response.success
    response.res.elements.each('//EngineSummary') do |engine|
      arr << EngineSummary.new(engine.attributes['id'].to_i,
                               engine.attributes['name'],
                               engine.attributes['address'],
                               engine.attributes['port'].to_i,
                               engine.attributes['status'])
    end
  end
  arr
end

#list_mtuObject


Lists all the multi-tenant users and their attributes.




67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/nexpose/silo.rb', line 67

def list_mtu
  xml = make_xml('MultiTenantUserListingRequest')
  r = execute xml, '1.2'

  if r.success
    res = []
    r.res.elements.each("//MultiTenantUserSummary") do |mtu|
      res << {
        :id => mtu.attributes['id'],
        :full_name => mtu.attributes['full-name'],
        :user_name => mtu.attributes['user-name'],
        :email => mtu.attributes['email'],
        :super_user => mtu.attributes['superuser'],
        :enabled => mtu.attributes['enabled'],
        :auth_module => mtu.attributes['auth-module'],
        :silo_count => mtu.attributes['silo-count'],
        :locked => mtu.attributes['locked']
      }
    end
    res
  else
    false
  end
end

#list_silo_profilesObject


Lists all the silo profiles and their attributes.




185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/nexpose/silo.rb', line 185

def list_silo_profiles
  xml = make_xml('SiloProfileListingRequest')
  r = execute xml, '1.2'

  if r.success
    res = []
    r.res.elements.each("//SiloProfileSummary") do |silo_profile|
      res << {
        :id => silo_profile.attributes['id'],
        :name => silo_profile.attributes['name'],
        :description => silo_profile.attributes['description'],
        :global_report_template_count => silo_profile.attributes['global-report-template-count'],
        :global_scan_engine_count => silo_profile.attributes['global-scan-engine-count'],
        :global_scan_template_count => silo_profile.attributes['global-scan-template-count'],
        :licensed_module_count => silo_profile.attributes['licensed-module-count'],
        :restricted_report_section_count => silo_profile.attributes['restricted-report-section-count'],
        :all_licensed_modules => silo_profile.attributes['all-licensed-modules'],
        :all_global_engines => silo_profile.attributes['all-global-engines'],
        :all_global_report_templates => silo_profile.attributes['all-global-report-templates'],
        :all_global_scan_templates => silo_profile.attributes['all-global-scan-templates']
      }
    end
    res
  else
    false
  end
end

#list_silosObject


Lists all the silos and their attributes.




319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
# File 'lib/nexpose/silo.rb', line 319

def list_silos
  xml = make_xml('SiloListingRequest')
  r = execute xml, '1.2'

  if r.success
    res = []
    r.res.elements.each("//SiloSummary") do |silo_profile|
      res << {
        :id => silo_profile.attributes['id'],
        :name => silo_profile.attributes['name'],
        :description => silo_profile.attributes['description']
      }
    end
    res
  else
    false
  end
end

#list_usersObject

Lists all the users for the NSC along with the user details.



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/nexpose/misc.rb', line 12

def list_users
  r = execute(make_xml('UserListingRequest'))
  if r.success
    res = []
    r.res.elements.each('//UserSummary') do |user_summary|
      res << {
        :auth_source => user_summary.attributes['authSource'],
        :auth_module => user_summary.attributes['authModule'],
        :user_name => user_summary.attributes['userName'],
        :full_name => user_summary.attributes['fullName'],
        :email => user_summary.attributes['email'],
        :is_admin => user_summary.attributes['isAdmin'].to_s.chomp.eql?('1'),
        :is_disabled => user_summary.attributes['disabled'].to_s.chomp.eql?('1'),
        :site_count => user_summary.attributes['siteCount'],
        :group_count => user_summary.attributes['groupCount']
      }
    end
    res
  else
    false
  end
end

#report_history(report_config_id) ⇒ Object

Provide a history of all reports generated with the specified report definition.



31
32
33
34
# File 'lib/nexpose/report.rb', line 31

def report_history(report_config_id)
  xml = make_xml('ReportHistoryRequest', {'reportcfg-id' => report_config_id})
  ReportSummary.parse_all(execute(xml))
end

#report_listingObject

Provide a listing of all report definitions the user can access on the Security Console.



76
77
78
79
80
81
82
83
84
85
# File 'lib/nexpose/report.rb', line 76

def report_listing
  r = execute(make_xml('ReportListingRequest', {}))
  reports = []
  if r.success
    r.res.elements.each('//ReportConfigSummary') do |report|
      reports << ReportConfigSummary.parse(report)
    end
  end
  reports
end

#report_template_listingObject

Provide a list of all report templates the user can access on the Security Console.



57
58
59
60
61
62
63
64
65
66
# File 'lib/nexpose/report.rb', line 57

def report_template_listing
  r = execute(make_xml('ReportTemplateListingRequest', {}))
  templates = []
  if r.success
    r.res.elements.each('//ReportTemplateSummary') do |template|
      templates << ReportTemplateSummary.parse(template)
    end
  end
  templates
end

#restartObject

Restart the application.

There is no response to a RestartRequest. When the application shuts down as part of the restart process, it terminates any active connections. Therefore, the application cannot issue a response when it restarts.



55
56
57
# File 'lib/nexpose/manage.rb', line 55

def restart
  execute(make_xml('RestartRequest', {})).success
end

#role_delete(role, scope = Scope::SILO) ⇒ Object Also known as: delete_role



66
67
68
69
70
71
72
# File 'lib/nexpose/role.rb', line 66

def role_delete(role, scope = Scope::SILO)
  xml = %Q(<RoleDeleteRequest session-id="#{@session_id}">)
  xml << %Q(<Role name="#{role}" scope="#{scope}"/>)
  xml << '</RoleDeleteRequest>' 
  response = execute(xml, '1.2')
  response.success
end

#role_listingObject Also known as: roles

Returns a summary list of all roles.



52
53
54
55
56
57
58
59
60
61
62
# File 'lib/nexpose/role.rb', line 52

def role_listing
  xml = make_xml('RoleListingRequest')
  r = execute(xml, '1.2')
  roles = []
  if r.success
    r.res.elements.each('RoleListingResponse/RoleSummary') do |summary|
      roles << RoleSummary::parse(summary)
    end
  end
  roles
end

#scan_activityArray[ScanSummary]

Retrieve a list of current scan activities across all Scan Engines managed by Nexpose.

Returns:

  • (Array[ScanSummary])

    Array of ScanSummary objects associated with each active scan on the engines.



57
58
59
60
61
62
63
64
65
66
# File 'lib/nexpose/scan.rb', line 57

def scan_activity
  r = execute(make_xml('ScanActivityRequest'))
  res = []
  if r.success
    r.res.elements.each('//ScanSummary') do |scan|
      res << ScanSummary.parse(scan)
    end
  end
  res
end

#scan_pause(scan_id) ⇒ Object


Pauses a scan.


Parameters:

  • scan_id

    The scan ID.



46
47
48
49
# File 'lib/nexpose/scan.rb', line 46

def scan_pause(scan_id)
  r = execute(make_xml('ScanPauseRequest',{ 'scan-id' => scan_id}))
  r.success ? r.attributes['success'] : nil
end

#scan_resume(scan_id) ⇒ Object


Resumes a scan.


Parameters:

  • scan_id

    The scan ID.



34
35
36
37
# File 'lib/nexpose/scan.rb', line 34

def scan_resume(scan_id)
  r = execute(make_xml('ScanResumeRequest', {'scan-id' => scan_id}))
  r.success ? r.attributes['success'] : nil
end

#scan_statistics(scan_id) ⇒ ScanSummary

Get scan statistics, including node and vulnerability breakdowns.

Returns:

  • (ScanSummary)

    ScanSummary object providing statistics for the scan.



72
73
74
75
76
77
78
79
# File 'lib/nexpose/scan.rb', line 72

def scan_statistics(scan_id)
  r = execute(make_xml('ScanStatisticsRequest', {'scan-id' => scan_id}))
  if r.success
    ScanSummary.parse(r.res.elements['//ScanSummary'])
  else
    false
  end
end

#scan_status(param) ⇒ Object



23
24
25
26
# File 'lib/nexpose/scan.rb', line 23

def scan_status(param)
  r = execute(make_xml('ScanStatusRequest', {'scan-id' => param}))
  r.success ? r.attributes['status'] : nil
end

#scan_stop(scan_id, wait_sec = 0) ⇒ Object

Stop a running or paused scan.

Parameters:

  • scan_id (Fixnum)

    ID of the scan to stop.

  • wait_sec (Fixnum) (defaults to: 0)

    Number of seconds to wait for status to be updated. Default: 0



9
10
11
12
13
14
15
16
17
18
19
20
21
# File 'lib/nexpose/scan.rb', line 9

def scan_stop(scan_id, wait_sec = 0)
  r = execute(make_xml('ScanStopRequest', {'scan-id' => scan_id}))
  if r.success
    so_far = 0
    while so_far < wait_sec
      status = scan_status(scan_id)
      return status if status == 'stopped'
      sleep 5
      so_far += 5
    end
  end
  r.success
end

#send_log(key_id, protocol, transport) ⇒ Object

– TODO This is not yet implemented correctly.

Output diagnostic information into log files, zip the files, and encrypt the archive with a PGP public key that is provided as a parameter for the API call. Then, either e-mail this archive to an address that is specified as an API parameter, or upload the archive using HTTP or HTTPS to a URL that is specified as an API parameter.

If you do not specify a key, the SendLogRequest uses a default key.

++

Parameters:

  • protocol

    should be one of: smtp, http, https.



72
73
74
75
76
77
78
79
80
# File 'lib/nexpose/manage.rb', line 72

def send_log(key_id, protocol, transport)
  xml = make_xml('ConsoleCommandRequest', {'keyid' => key_id})
  tpt = REXML::Element.new('Transport')
  tpt.add_attribute('protocol', protocol)
  tpt.text = transport
  xml << tpt

  # execute(xml)
end

#site_delete(param) ⇒ Object

Delete the specified site and all associated scan data.

Returns:

  • Whether or not the delete request succeeded.



41
42
43
44
# File 'lib/nexpose/site.rb', line 41

def site_delete(param)
  r = execute(make_xml('SiteDeleteRequest', {'site-id' => param}))
  r.success
end

#site_device_listing(site_id = nil) ⇒ Array[Device] Also known as: assets, devices, list_devices

Retrieve a list of all of the assets in a site.

If no site-id is specified, then return all of the assets for the Nexpose console, grouped by site-id.

Parameters:

  • site_id (FixNum) (defaults to: nil)

    Site ID to request device listing for. Optional.

Returns:

  • (Array[Device])

    Array of devices associated with the site, or all devices on the console if no site is provided.



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/nexpose/site.rb', line 14

def site_device_listing(site_id = nil)
  r = execute(make_xml('SiteDeviceListingRequest', {'site-id' => site_id}))

  devices = []
  if r.success
    r.res.elements.each('SiteDeviceListingResponse/SiteDevices') do |site|
      site_id = site.attributes['site-id'].to_i
      site.elements.each('device') do |device|
        devices << Device.new(device.attributes['id'].to_i,
                          device.attributes['address'],
                          site_id,
                          device.attributes['riskfactor'].to_f,
                          device.attributes['riskscore'].to_f)
      end
    end
  end
  devices
end

#site_device_scan_start(site_id, devices, hosts = nil) ⇒ Object Also known as: site_device_scan, adhoc_device_scan


Starts device specific site scanning.

devices - An Array of device IDs hosts - An Array of Hashes [o]=>:range=>“from,to” [1]=>:host=>host




102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/nexpose/site.rb', line 102

def site_device_scan_start(site_id, devices, hosts = nil)

  if hosts == nil and devices == nil
    raise ArgumentError.new('Both the device and host list is nil.')
  end

  xml = make_xml('SiteDevicesScanRequest', {'site-id' => site_id})

  if devices != nil
    inner_xml = REXML::Element.new 'Devices'
    for device_id in devices
      inner_xml.add_element 'device', {'id' => "#{device_id}"}
    end
    xml.add_element inner_xml
  end

  if hosts
    inner_xml = REXML::Element.new 'Hosts'
    hosts.each_index do |x|
      if hosts[x].key? :range
        from, to = hosts[x][:range].split(',')
        if to
          inner_xml.add_element 'range', {'to' => to, 'from' => from}
        else
          inner_xml.add_element 'range', {'from' => from}
        end
      end
      if hosts[x].key? :host
        host_element = REXML::Element.new 'host'
        host_element.text = "#{hosts[x][:host]}"
        inner_xml.add_element host_element
      end
    end
    xml.add_element inner_xml
  end

  r = execute xml
  if r.success
    r.res.elements.each('//Scan') do |scan_info|
      return {
        :scan_id => scan_info.attributes['scan-id'].to_i,
        :engine_id => scan_info.attributes['engine-id'].to_i
      }
    end
  else
    false
  end
end

#site_listingArray[SiteSummary] Also known as: list_sites, sites

Retrieve a list of all sites the user is authorized to view or manage.

Returns:

  • (Array[SiteSummary])

    Array of SiteSummary objects.



50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/nexpose/site.rb', line 50

def site_listing
  r = execute(make_xml('SiteListingRequest'))
  arr = []
  if (r.success)
    r.res.elements.each("SiteListingResponse/SiteSummary") do |site|
      arr << SiteSummary.new(site.attributes['id'].to_i,
                             site.attributes['name'],
                             site.attributes['description'],
                             site.attributes['riskfactor'].to_f,
                             site.attributes['riskscore'].to_f)
    end
  end
  arr
end

#site_scan_history(site_id) ⇒ Array[ScanSummary]

Retrieve a list of all previous scans of the site.

Parameters:

  • site_id (FixNum)

    Site ID to request scan history for.

Returns:

  • (Array[ScanSummary])

    Array of ScanSummary objects representing each scan run to date on the site provided.



74
75
76
77
78
79
80
81
82
83
# File 'lib/nexpose/site.rb', line 74

def site_scan_history(site_id)
  r = execute(make_xml('SiteScanHistoryRequest', {'site-id' => site_id}))
  scans = []
  if r.success
    r.res.elements.each('SiteScanHistoryResponse/ScanSummary') do |scan_event|
      scans << ScanSummary.parse(scan_event)
    end
  end
  scans
end

#start_updateObject

Induce the application to retrieve required updates and restart if necessary.



45
46
47
# File 'lib/nexpose/manage.rb', line 45

def start_update
  execute(make_xml('StartUpdateRequest', {})).success
end

#system_informationObject

Obtain system data, such as total RAM, free RAM, total disk space, free disk space, CPU speed, number of CPU cores, and other vital information.



29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/nexpose/manage.rb', line 29

def system_information
  r = execute(make_xml('SystemInformationRequest', {}))

  if r.success
    res = {}
    r.res.elements.each('//Statistic') do |stat|
      res[stat.attributes['name'].to_s] = stat.text.to_s
    end
    res
  else
    false
  end
end

#ticket_listingObject Also known as: tickets



31
32
33
34
35
36
37
38
39
40
41
# File 'lib/nexpose/ticket.rb', line 31

def ticket_listing
  xml = make_xml('TicketListingRequest')
  r = execute(xml, '1.2')
  tickets = []
  if r.success
    r.res.elements.each('TicketListingResponse/TicketSummary') do |summary|
      tickets << TicketSummary::parse(summary)
    end
  end
  tickets
end

#vuln_details(vuln_id) ⇒ VulnerabilityDetail

Retrieve details for a vulnerability.

Parameters:

  • vuln_id (String)

    Nexpose vulnerability ID, such as ‘windows-duqu-cve-2011-3402’.

Returns:



38
39
40
41
42
43
44
45
46
# File 'lib/nexpose/vuln.rb', line 38

def vuln_details(vuln_id)
  xml = make_xml('VulnerabilityDetailsRequest', {'vuln-id' => vuln_id})
  response = execute(xml, '1.2')
  if response.success
    response.res.elements.each('VulnerabilityDetailsResponse/Vulnerability') do |vuln|
      return VulnerabilityDetail::parse(vuln)
    end
  end
end

#vuln_exception_approve(input) ⇒ Object


Allows a submitted vulnerability exception to be approved.

:exception_id - The exception id returned after the vuln exception was submitted for creation. :comment - An optional comment


Parameters:

  • input:


375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
# File 'lib/nexpose/vuln.rb', line 375

def vuln_exception_approve(input)
  exception_id = input[:exception_id]
  unless exception_id
    raise ArgumentError.new 'Exception Id is required'
  end

  xml = make_xml('VulnerabilityExceptionApproveRequest', {'exception-id' => exception_id})
  comment = input[:comment]
  if comment && !comment.empty?
    comment_xml = make_xml('comment', {}, comment, false)
    xml.add_element comment_xml
  end

  r = execute xml, '1.2'
  r.success
end

#vuln_exception_create(input) ⇒ Object


Creates a vulnerability exception.

:vuln_id - The Nexpose vulnerability ID. :reason - The reason for the exception

values - "False Positive", "Compensating Control", "Acceptable Use", "Acceptable Risk", "Other"

:scope - The scope type (NOTE: The case is important)

values - "All Instances", "All Instances on a Specific Asset", "Specific Instance of a specific Asset"

:comment - A user comment :device-id - Used for specific instances related to “All Instances on a Specific Asset” AND “Specific Instance of Specific Asset” :port - All assets on this port related to “Specific Instance of a specific Asset” :vuln-key - The vulnerability key related to the “Specific Instance of a specific Asset”


Parameters:

  • input
    • data used to create the vulnerability exception:



238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/nexpose/vuln.rb', line 238

def vuln_exception_create(input)
  options = {}

  if input.nil?
    raise ArgumentError.new 'The input element cannot be null'
  end

  vuln_id = input[:vuln_id]
  unless vuln_id
    raise ArgumentError.new 'The vulnerability ID is required'
  end
  options['vuln-id'] = vuln_id

  reason = input[:reason]
  if reason.nil? || reason.empty?
    raise ArgumentError.new 'The reason is required'
  end

  unless reason =~ /False Positive|Compensating Control|Acceptable Use|Acceptable Risk|Other/
    raise ArgumentError.new 'The reason type is invalid'
  end
  options['reason'] = reason

  scope = input[:scope]
  if scope.nil? || scope.empty?
    raise ArgumentError.new 'The scope is required'
  end

  # For scope case matters.
  unless scope =~ /All Instances|All Instances on a Specific Asset|Specific Instance of Specific Asset/
    raise ArgumentError.new 'The scope type is invalid'
  end

  if scope =~ /All Instances on a Specific Asset|Specific Instance of Specific Asset/
    device_id = input[:device_id]
    vuln_key = input[:vuln_key]
    port = input[:port]
    if device_id
      options['device-id'] = device_id
    end

    if scope =~ /All Instances on a Specific Asset/ && (vuln_key || port)
      raise ArgumentError.new 'Vulnerability key or port cannot be used with the scope specified'
    end

    if vuln_key
      options['vuln-key'] = vuln_key
    end

    if port
      options['port-no'] = port
    end
  end
  options['scope'] = scope

  xml = make_xml('VulnerabilityExceptionCreateRequest', options)

  comment = input[:comment]
  if comment && !comment.empty?
    comment_xml = make_xml('comment', {}, comment, false)
    xml.add_element comment_xml
  else
    raise ArgumentError.new 'The comment cannot be empty'
  end

  r = execute xml, '1.2'
  if r.success
    r.res.elements.each('//VulnerabilityExceptionCreateResponse') do |vecr|
      return vecr.attributes['exception-id']
    end
  else
    false
  end
end

#vuln_exception_delete(exception_id) ⇒ Object


Deletes a submitted vulnerability exception to be approved.


Parameters:

  • exception_id
    • The exception id returned after the vuln exception was submitted for creation.



486
487
488
489
490
491
492
493
494
# File 'lib/nexpose/vuln.rb', line 486

def vuln_exception_delete(exception_id)
  unless exception_id
    raise ArgumentError.new 'Exception Id is required'
  end

  xml = make_xml('VulnerabilityExceptionDeleteRequest', {'exception-id' => exception_id})
  r = execute xml, '1.2'
  r.success
end

#vuln_exception_listing(status = nil) ⇒ Object


Returns an array of vulnerability exceptions and their associated attributes.

“Under Review”, “Approved”, “Rejected”


Parameters:

  • status (defaults to: nil)
    • (optional) The status of the vulnerability exception:



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/nexpose/vuln.rb', line 181

def vuln_exception_listing(status = nil)
  option = {}

  if status && !status.empty?
    if status =~ /Under Review|Approved|Rejected/
      option['status'] = status
    else
      raise ArgumentError.new 'The vulnerability status passed in is invalid!'
    end
  end

  xml = make_xml('VulnerabilityExceptionListingRequest', option)
  r = execute xml, '1.2'

  if r.success
    res = []
    r.res.elements.each('//VulnerabilityException') do |ve|
      submitter_comment = ve.elements['submitter-comment']
      reviewer_comment = ve.elements['reviewer-comment']
      res << {
        :vuln_id => ve.attributes['vuln-id'],
        :exception_id => ve.attributes['exception-id'],
        :submitter => ve.attributes['submitter'],
        :reviewer => ve.attributes['reviewer'],
        :status => ve.attributes['status'],
        :reason => ve.attributes['reason'],
        :scope => ve.attributes['scope'],
        :device_id => ve.attributes['device-id'],
        :port_no => ve.attributes['port-no'],
        :expiration_date => ve.attributes['expiration-date'],
        :vuln_key => ve.attributes['vuln-key'],
        :submitter_comment => submitter_comment.nil? ? '' : submitter_comment.text,
        :reviewer_comment => reviewer_comment.nil? ? '' : reviewer_comment.text
      }
    end
    res
  else
    false
  end
end

#vuln_exception_recall(exception_id) ⇒ Object


Allows a previously submitted exception that has not been approved to be withdrawn.


Parameters:

  • exception_id
    • The exception id returned after the vuln exception was submitted for creation.



361
362
363
364
365
# File 'lib/nexpose/vuln.rb', line 361

def vuln_exception_recall(exception_id)
  xml = make_xml('VulnerabilityExceptionRecallRequest', {'exception-id' => exception_id})
  r = execute xml, '1.2'
  r.success
end

#vuln_exception_reject(input) ⇒ Object


Rejects a submitted vulnerability exception to be approved.

:exception_id - The exception id returned after the vuln exception was submitted for creation. :comment - An optional comment


Parameters:

  • input:


399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
# File 'lib/nexpose/vuln.rb', line 399

def vuln_exception_reject(input)
  exception_id = input[:exception_id]
  unless exception_id
    raise ArgumentError.new 'Exception Id is required'
  end

  xml = make_xml('VulnerabilityExceptionRejectRequest', {'exception-id' => exception_id})
  comment = input[:comment]
  if comment && !comment.empty?
    comment_xml = make_xml('comment', {}, comment, false)
    xml.add_element comment_xml
  end

  r = execute xml, '1.2'
  r.success
end

#vuln_exception_resubmit(input) ⇒ Object


Resubmit a vulnerability exception.

:vuln_id - The Nexpose vulnerability ID. (required) :reason - The reason for the exception (optional)

values - "False Positive", "Compensating Control", "Acceptable Use", "Acceptable Risk", "Other"

:comment - A user comment (required)


Parameters:

  • input
    • data used to create the vulnerability exception:



322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
# File 'lib/nexpose/vuln.rb', line 322

def vuln_exception_resubmit(input)
  options = {}

  if input.nil?
    raise ArgumentError.new 'The input element cannot be null'
  end

  exception_id = input[:exception_id]
  unless exception_id
    raise ArgumentError.new 'The exception ID is required'
  end
  options['exception-id'] = exception_id

  reason = input[:reason]
  if !reason.nil? && !reason.empty?
    unless reason =~ /False Positive|Compensating Control|Acceptable Use|Acceptable Risk|Other/
      raise ArgumentError.new 'The reason type is invalid'
    end
    options['reason'] = reason

  end

  xml = make_xml('VulnerabilityExceptionResubmitRequest', options)

  comment = input[:comment]
  if comment && !comment.empty?
    comment_xml = make_xml('comment', {}, comment, false)
    xml.add_element comment_xml
  end

  r = execute xml, '1.2'
  r.success
end

#vuln_exception_update_comment(input) ⇒ Object


Updates a vulnerability exception comment.

:exception_id - The exception id returned after the vuln exception was submitted for creation. :submitter_comment - The submitter comment :reviewer_comment - The reviewer comment


Parameters:

  • input:


424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
# File 'lib/nexpose/vuln.rb', line 424

def vuln_exception_update_comment(input)
  exception_id = input[:exception_id]
  unless exception_id
    raise ArgumentError.new 'Exception Id is required'
  end

  xml = make_xml('VulnerabilityExceptionUpdateCommentRequest', {'exception-id' => exception_id})
  submitter_comment = input[:submitter_comment]
  if submitter_comment && !submitter_comment.empty?
    comment_xml = make_xml('submitter-comment', {}, submitter_comment, false)
    xml.add_element comment_xml
  end

  reviewer_comment = input[:reviewer_comment]
  if reviewer_comment && !reviewer_comment.empty?
    comment_xml = make_xml('reviewer-comment', {}, reviewer_comment, false)
    xml.add_element comment_xml
  end

  r = execute xml, '1.2'
  r.success
end

#vuln_exception_update_expiration_date(input) ⇒ Object


Update the expiration date for a vulnerability exception.

:exception_id - The exception id returned after the vulnerability exception was submitted for creation. :expiration_date - The new expiration date format: YYYY-MM-DD


Parameters:

  • input


454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
# File 'lib/nexpose/vuln.rb', line 454

def vuln_exception_update_expiration_date(input)
  exception_id = input[:exception_id]
  unless exception_id
    raise ArgumentError.new 'Exception Id is required'
  end

  expiration_date = input[:expiration_date]
  if expiration_date && !expiration_date.empty? && expiration_date =~ /\A\desc{4}-(\desc{2})-(\desc{2})\z/
    if $1.to_i > 12
      raise ArgumentError.new 'The expiration date month value is invalid'
    end

  if $2.to_i > 31
    raise ArgumentError.new 'The expiration date day value is invalid'
  end
  else
    raise ArgumentError.new 'Expiration date is invalid'
  end

  options = {}
  options['exception-id'] = exception_id
  options['expiration-date'] = expiration_date
  xml = make_xml('VulnerabilityExceptionUpdateExpirationDateRequest', options)
  r = execute xml, '1.2'
  r.success
end

#vuln_listing(full = false) ⇒ Array[Vulnerability|VulnerabilitySummary] Also known as: vulns

Retrieve summary details of all vulnerabilities.

Parameters:

  • full (Boolean) (defaults to: false)

    Whether or not to gather the full summary. Without the flag, only id, title, and severity are returned. It can take twice a long to retrieve full summary information.

Returns:



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/nexpose/vuln.rb', line 12

def vuln_listing(full = false)
  xml = make_xml('VulnerabilityListingRequest')
  # TODO Add a flag to do stream parsing of the XML to improve performance.
  response = execute(xml, '1.2')
  vulns = []
  if response.success
    response.res.elements.each('VulnerabilityListingResponse/VulnerabilitySummary') do |vuln|
      if full
        vulns << VulnerabilitySummary::parse(vuln)
      else
        vulns << Vulnerability.new(vuln.attributes['id'],
                                   vuln.attributes['title'],
                                   vuln.attributes['severity'].to_i)
      end
    end
  end
  vulns
end