nexpose 0.0.98 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,898 +1,826 @@
1
- module Nexpose
2
- module NexposeAPI
3
- include XMLUtils
4
-
5
- #
6
- #
7
- #
8
- def site_device_listing(site_id)
9
- r = execute(make_xml('SiteDeviceListingRequest', {'site-id' => site_id.to_s}))
10
-
11
- if (r.success)
12
- res = []
13
- r.res.elements.each("//device") do |device|
14
- res << {
15
- :device_id => device.attributes['id'].to_i,
16
- :address => device.attributes['address'].to_s,
17
- :risk_factor => device.attributes['risk_factor'].to_f,
18
- :risk_score => device.attributes['risk_score'].to_f,
19
- }
20
- end
21
- res
22
- else
23
- false
24
- end
25
- end
26
-
27
- #
28
- #
29
- #
30
- def site_delete(param)
31
- r = execute(make_xml('SiteDeleteRequest', {'site-id' => param}))
32
- r.success
33
- end
34
-
35
- #
36
- #
37
- #
38
- def site_listing
39
- r = execute(make_xml('SiteListingRequest', {}))
40
-
41
- if (r.success)
42
- res = []
43
- r.res.elements.each("//SiteSummary") do |site|
44
- res << {
45
- :site_id => site.attributes['id'].to_i,
46
- :name => site.attributes['name'].to_s,
47
- :risk_factor => site.attributes['riskfactor'].to_f,
48
- :risk_score => site.attributes['riskscore'].to_f,
49
- }
50
- end
51
- res
52
- else
53
- false
54
- end
55
- end
56
-
57
- #-----------------------------------------------------------------------
58
- # TODO: Needs to be expanded to included details
59
- #-----------------------------------------------------------------------
60
- def site_scan_history(site_id)
61
- r = execute(make_xml('SiteScanHistoryRequest', {'site-id' => site_id.to_s}))
62
-
63
- if (r.success)
64
- res = []
65
- r.res.elements.each("//ScanSummary") do |site_scan_history|
66
- res << {
67
- :site_id => site_scan_history.attributes['site-id'].to_i,
68
- :scan_id => site_scan_history.attributes['scan-id'].to_i,
69
- :engine_id => site_scan_history.attributes['engine-id'].to_i,
70
- :start_time => site_scan_history.attributes['startTime'].to_s,
71
- :end_time => site_scan_history.attributes['endTime'].to_s
72
- }
73
- end
74
- res
75
- else
76
- false
77
- end
78
- end
79
-
80
- #-----------------------------------------------------------------------
81
- # Starts device specific site scanning.
82
- #
83
- # devices - An Array of device IDs
84
- # hosts - An Array of Hashes [o]=>{:range=>"to,from"} [1]=>{:host=>host}
85
- #-----------------------------------------------------------------------
86
- def site_device_scan_start(site_id, devices, hosts)
87
-
88
- if hosts == nil and devices == nil
89
- raise ArgumentError.new("Both the device and host list is nil")
90
- end
91
-
92
- xml = make_xml('SiteDevicesScanRequest', {'site-id' => site_id})
93
-
94
- if devices != nil
95
- inner_xml = REXML::Element.new 'Devices'
96
- for device_id in devices
97
- inner_xml.add_element 'device', {'id' => "#{device_id}"}
98
- end
99
- xml.add_element inner_xml
100
- end
101
-
102
- if hosts != nil
103
- inner_xml = REXML::Element.new 'Hosts'
104
- hosts.each_index do |x|
105
- if hosts[x].key? :range
106
- to = hosts[x][:range].split(',')[0]
107
- from = hosts[x][:range].split(',')[1]
108
- inner_xml.add_element 'range', {'to' => "#{to}", 'from' => "#{from}"}
109
- end
110
- if hosts[x].key? :host
111
- host_element = REXML::Element.new 'host'
112
- host_element.text = "#{hosts[x][:host]}"
113
- inner_xml.add_element host_element
114
- end
115
- end
116
- xml.add_element inner_xml
117
- end
118
-
119
- r = execute xml
120
- if r.success
121
- r.res.elements.each('//Scan') do |scan_info|
122
- return {
123
- :scan_id => scan_info.attributes['scan-id'].to_i,
124
- :engine_id => scan_info.attributes['engine-id'].to_i
125
- }
126
- end
127
- else
128
- false
129
- end
130
- end
131
- end
132
-
133
- #-------------------------------------------------------------------------------------------------------------------
134
- # === Description
135
- # Object that represents a site, including the site configuration, scan history, and device listing.
136
- #
137
- # === Example
138
- # # Create a new Nexpose Connection on the default port and Login
139
- # nsc = Connection.new("10.1.40.10","nxadmin","password")
140
- # nsc.login()
141
- #
142
- # # Get an Existing Site
143
- # site_existing = Site.new(nsc,184)
144
- #
145
- # # Create a New Site, add some hosts, and save it to the NSC
146
- # site = Site.new(nsc)
147
- # site.setSiteConfig("New Site", "New Site Created in the API")
148
- #
149
- # # Add the hosts
150
- # site.site_config.addHost(HostName.new("localhost"))
151
- # site.site_config.addHost(IPRange.new("192.168.7.1","192.168.7.255"))
152
- # site.site_config.addHost(IPRange.new("10.1.20.30"))
153
- #
154
- # status = site.saveSite()
155
- #-------------------------------------------------------------------------------------------------------------------
156
- class Site
157
- # true if an error condition exists; false otherwise
158
- attr_reader :error
159
- # Error message string
160
- attr_reader :error_msg
161
- # The last XML request sent by this object
162
- attr_reader :request_xml
163
- # The last XML response received by this object
164
- attr_reader :response_xml
165
- # The NSC Connection associated with this object
166
- attr_reader :connection
167
- # The Site ID
168
- # site_id = -1 means create a new site. The NSC will assign a new site_id on SiteSave.
169
- attr_reader :site_id
170
- # A summary overview of this site
171
- # SiteSummary Object
172
- attr_reader :site_summary
173
- # The configuration of this site
174
- # SiteConfig Object
175
- attr_reader :site_config
176
- # The device listing for this site
177
- # SiteDeviceListing Object
178
- attr_reader :site_device_listing
179
- # The scan history of this site
180
- # SiteScanHistory Object
181
- attr_reader :site_scan_history
182
-
183
- def initialize(connection, site_id = -1)
184
- @error = false
185
- @connection = connection
186
- @site_id = site_id
187
-
188
- # If site_id > 0 then get SiteConfig
189
- if (@site_id.to_i > 0)
190
- # Create new SiteConfig object
191
- @site_config = SiteConfig.new()
192
- # Populate SiteConfig Obect with Data from the NSC
193
- @site_config.getSiteConfig(@connection, @site_id)
194
- @site_summary = SiteSummary.new(@site_id, @site_config.site_name, @site_config.description, @site_config.riskfactor)
195
- @site_scan_history = SiteScanHistory.new(@connection, @site_id)
196
- @site_device_listing = SiteDeviceListing.new(@connection, @site_id)
197
-
198
- else
199
- # Just in case user enters a number > -1 or = 0
200
- @site_id = -1
201
-
202
- @site_config = SiteConfig.new()
203
- setSiteConfig("New Site " + rand(999999999999).to_s, "")
204
- @site_summary = nil
205
-
206
- end
207
-
208
- end
209
-
210
- # Creates a new site summary
211
- def setSiteSummary(site_name, description, riskfactor = 1)
212
- @site_summary = SiteSummary.new(-1, site_name, description, riskfactor)
213
-
214
- end
215
-
216
- # Creates a new site configuration
217
- def setSiteConfig(site_name, description, riskfactor = 1)
218
- setSiteSummary(site_name, description, riskfactor)
219
- @site_config = SiteConfig.new()
220
- @site_config._set_site_id(-1)
221
- @site_config._set_site_name(site_name)
222
- @site_config._set_description(description)
223
- @site_config._set_riskfactor(riskfactor)
224
- @site_config._set_scanConfig(ScanConfig.new(-1, "tmp", "full-audit"))
225
- @site_config._set_connection(@connection)
226
-
227
- end
228
-
229
- # Initiates a scan of this site. If successful returns scan_id and engine_id in an associative array. Returns false if scan is unsuccessful.
230
- def scanSite()
231
- r = @connection.execute('<SiteScanRequest session-id="' + "#{@connection.session_id}" + '" site-id="' + "#{@site_id}" + '"/>')
232
- if (r.success)
233
- res = {}
234
- r.res.elements.each('//Scan/') do |s|
235
- res[:scan_id] = s.attributes['scan-id']
236
- res[:engine_id] = s.attributes['engine-id']
237
- end
238
- return res
239
- else
240
- return false
241
- end
242
- end
243
-
244
- # Saves this site in the NSC
245
- def saveSite()
246
- r = @connection.execute('<SiteSaveRequest session-id="' + @connection.session_id + '">' + getSiteXML() + ' </SiteSaveRequest>')
247
- if (r.success)
248
- @site_id = r.attributes['site-id']
249
- @site_config._set_site_id(@site_id)
250
- @site_config.scanConfig._set_configID(@site_id)
251
- @site_config.scanConfig._set_name(@site_id)
252
- return true
253
- else
254
- return false
255
- end
256
- end
257
-
258
- def deleteSite()
259
- r = @connection.execute('<SiteDeleteRequest session-id="' + @connection.session_id.to_s + '" site-id="' + @site_id + '"/>')
260
- r.success
261
- end
262
-
263
-
264
- def printSite()
265
- puts "Site ID: " + @site_summary.id
266
- puts "Site Name: " + @site_summary.site_name
267
- puts "Site Description: " + @site_summary.description
268
- puts "Site Risk Factor: " + @site_summary.riskfactor
269
- end
270
-
271
- def getSiteXML()
272
-
273
- xml = '<Site id="' + "#{@site_config.site_id}" + '" name="' + "#{@site_config.site_name}" + '" description="' + "#{@site_config.description}" + '" riskfactor="' + "#{@site_config.riskfactor}" + '">'
274
-
275
- xml << ' <Hosts>'
276
- @site_config.hosts.each do |h|
277
- xml << h.to_xml if h.respond_to? :to_xml
278
- end
279
- xml << '</Hosts>'
280
-
281
- xml << '<Credentials>'
282
- @site_config.credentials.each do |c|
283
- xml << c.to_xml if c.respond_to? :to_xml
284
- end
285
- xml << ' </Credentials>'
286
-
287
- xml << ' <Alerting>'
288
- @site_config.alerts.each do |a|
289
- xml << a.to_xml if a.respond_to? :to_xml
290
- end
291
- xml << ' </Alerting>'
292
-
293
- xml << ' <ScanConfig configID="' + "#{@site_config.scanConfig.configID}" + '" name="' + "#{@site_config.scanConfig.name}" + '" templateID="' + "#{@site_config.scanConfig.templateID}" + '" configVersion="' + "#{@site_config.scanConfig.configVersion}" + '">'
294
-
295
- xml << ' <Schedules>'
296
- @site_config.scanConfig.schedules.each do |s|
297
- xml << ' <Schedule enabled="' + s.enabled + '" type="' + s.type + '" interval="' + s.interval + '" start="' + s.start + '"/>'
298
- end
299
- xml << ' </Schedules>'
300
-
301
- xml << ' <ScanTriggers>'
302
- @site_config.scanConfig.scanTriggers.each do |s|
303
-
304
- if (s.class.to_s == "Nexpose::AutoUpdate")
305
- xml << ' <autoUpdate enabled="' + s.enabled + '" incremental="' + s.incremental + '"/>'
306
- end
307
- end
308
-
309
- xml << ' </ScanTriggers>'
310
-
311
- xml << ' </ScanConfig>'
312
-
313
- xml << ' </Site>'
314
-
315
- xml
316
- end
317
- end
318
-
319
- # === Description
320
- # Object that represents a listing of all of the sites available on an NSC.
321
- #
322
- # === Example
323
- # # Create a new Nexpose Connection on the default port and Login
324
- # nsc = Connection.new("10.1.40.10","nxadmin","password")
325
- # nsc->login();
326
- #
327
- # # Get Site Listing
328
- # sitelisting = SiteListing.new(nsc)
329
- #
330
- # # Enumerate through all of the SiteSummaries
331
- # sitelisting.sites.each do |sitesummary|
332
- # # Do some operation on each site
333
- # end
334
- #
335
- class SiteListing
336
- # true if an error condition exists; false otherwise
337
- attr_reader :error
338
- # Error message string
339
- attr_reader :error_msg
340
- # The last XML request sent by this object
341
- attr_reader :request_xml
342
- # The last XML response received by this object
343
- attr_reader :response_xml
344
- # The NSC Connection associated with this object
345
- attr_reader :connection
346
- # Array containing SiteSummary objects for each site in the connection
347
- attr_reader :sites
348
- # The number of sites
349
- attr_reader :site_count
350
-
351
- # Constructor
352
- # SiteListing (connection)
353
- def initialize(connection)
354
- @sites = []
355
-
356
- @connection = connection
357
-
358
- r = @connection.execute('<SiteListingRequest session-id="' + @connection.session_id.to_s + '"/>')
359
-
360
- if (r.success)
361
- parse(r.res)
362
- else
363
- raise APIError.new(r, "Failed to get site listing")
364
- end
365
- end
366
-
367
- def parse(r)
368
- r.elements.each('SiteListingResponse/SiteSummary') do |s|
369
- site_summary = SiteSummary.new(
370
- s.attributes['id'].to_s,
371
- s.attributes['name'].to_s,
372
- s.attributes['description'].to_s,
373
- s.attributes['riskfactor'].to_s
374
- )
375
- @sites.push(site_summary)
376
- end
377
- @site_count = @sites.length
378
- end
379
- end
380
-
381
- # === Description
382
- # Object that represents the summary of a NeXpose Site.
383
- #
384
- class SiteSummary
385
- # The Site ID
386
- attr_reader :id
387
- # The Site Name
388
- attr_reader :site_name
389
- # A Description of the Site
390
- attr_reader :description
391
- # User assigned risk multiplier
392
- attr_reader :riskfactor
393
-
394
- # Constructor
395
- # SiteSummary(id, site_name, description, riskfactor = 1)
396
- def initialize(id, site_name, description, riskfactor = 1)
397
- @id = id
398
- @site_name = site_name
399
- @description = description
400
- @riskfactor = riskfactor
401
- end
402
-
403
- def _set_id(id)
404
- @id = id
405
- end
406
- end
407
-
408
- # === Description
409
- # Object that represents the configuration of a Site. This object is automatically created when a new Site object is instantiated.
410
- #
411
- class SiteConfig
412
- # true if an error condition exists; false otherwise
413
- attr_reader :error
414
- # Error message string
415
- attr_reader :error_msg
416
- # The last XML request sent by this object
417
- attr_reader :request_xml
418
- # The last XML response received by this object
419
- attr_reader :response_xml
420
- # The NSC Connection associated with this object
421
- attr_reader :connection
422
- # The Site ID
423
- attr_reader :site_id
424
- # The Site Name
425
- attr_reader :site_name
426
- # A Description of the Site
427
- attr_reader :description
428
- # User assigned risk multiplier
429
- attr_reader :riskfactor
430
- # Array containing ((IPRange|HostName)*)
431
- attr_reader :hosts
432
- # Array containing (AdminCredentials*)
433
- attr_reader :credentials
434
- # Array containing ((SmtpAlera|SnmpAlert|SyslogAlert)*)
435
- attr_reader :alerts
436
- # ScanConfig object which holds Schedule and ScanTrigger Objects
437
- attr_reader :scanConfig
438
-
439
- def initialize()
440
- @xml_tag_stack = []
441
- @hosts = []
442
- @credentials = []
443
- @alerts = []
444
- @error = false
445
- end
446
-
447
- # Adds a new host to the hosts array
448
- def addHost(host)
449
- @hosts.push(host)
450
- end
451
-
452
- # Adds a new alert to the alerts array
453
- def addAlert(alert)
454
- @alerts.push(alert)
455
- end
456
-
457
- # Adds a new set of credentials to the credentials array
458
- def addCredentials(credential)
459
- @credentials.push(credential)
460
- end
461
-
462
- # TODO
463
- def getSiteConfig(connection, site_id)
464
- @connection = connection
465
- @site_id = site_id
466
-
467
- r = APIRequest.execute(@connection.url, '<SiteConfigRequest session-id="' + @connection.session_id + '" site-id="' + "#{@site_id}" + '"/>')
468
- parse(r.res)
469
- end
470
-
471
- def _set_site_id(site_id)
472
- @site_id = site_id
473
- end
474
-
475
- def _set_site_name(site_name)
476
- @site_name = site_name
477
- end
478
-
479
- def _set_description(description)
480
- @description = description
481
- end
482
-
483
- def _set_riskfactor(riskfactor)
484
- @riskfactor = riskfactor
485
- end
486
-
487
- def _set_scanConfig(scanConfig)
488
- @scanConfig = scanConfig
489
- end
490
-
491
- def _set_connection(connection)
492
- @connection = connection
493
- end
494
-
495
-
496
- def parse(response)
497
- response.elements.each('SiteConfigResponse/Site') do |s|
498
- @site_id = s.attributes['id']
499
- @site_name = s.attributes['name']
500
- @description = s.attributes['description']
501
- @riskfactor = s.attributes['riskfactor']
502
- s.elements.each('Hosts/range') do |r|
503
- @hosts.push(IPRange.new(r.attributes['from'], r.attributes['to']))
504
- end
505
- s.elements.each('ScanConfig') do |c|
506
- @scanConfig = ScanConfig.new(c.attributes['configID'],
507
- c.attributes['name'],
508
- c.attributes['templateID'],
509
- c.attributes['configVersion'])
510
- s.elements.each('Schedule') do |schedule|
511
- schedule = new Schedule(schedule.attributes["type"], schedule.attributes["interval"], schedule.attributes["start"], schedule.attributes["enabled"])
512
- @scanConfig.addSchedule(schedule)
513
- end
514
- end
515
-
516
- s.elements.each('Alerting/Alert') do |a|
517
-
518
- a.elements.each('smtpAlert') do |smtp|
519
- smtp_alert = SmtpAlert.new(a.attributes["name"], smtp.attributes["sender"], smtp.attributes["limitText"], a.attributes["enabled"])
520
-
521
- smtp.elements.each('recipient') do |recipient|
522
- smtp_alert.addRecipient(recipient.text)
523
- end
524
- @alerts.push(smtp_alert)
525
- end
526
-
527
- a.elements.each('snmpAlert') do |snmp|
528
- snmp_alert = SnmpAlert.new(a.attributes["name"], snmp.attributes["community"], snmp.attributes["server"], a.attributes["enabled"])
529
- @alerts.push(snmp_alert)
530
- end
531
- a.elements.each('syslogAlert') do |syslog|
532
- syslog_alert = SyslogAlert.new(a.attributes["name"], syslog.attributes["server"], a.attributes["enabled"])
533
- @alerts.push(syslog_alert)
534
- end
535
-
536
- a.elements.each('vulnFilter') do |vulnFilter|
537
-
538
- #vulnfilter = new VulnFilter.new(a.attributes["typemask"], a.attributes["severityThreshold"], $attrs["MAXALERTS"])
539
- # Pop off the top alert on the stack
540
- #$alert = @alerts.pop()
541
- # Add the new recipient string to the Alert Object
542
- #$alert.setVulnFilter($vulnfilter)
543
- # Push the alert back on to the alert stack
544
- #array_push($this->alerts, $alert)
545
- end
546
-
547
- a.elements.each('scanFilter') do |scanFilter|
548
- #<scanFilter scanStop='0' scanFailed='0' scanStart='1'/>
549
- #scanfilter = ScanFilter.new(scanFilter.attributes['scanStop'],scanFilter.attributes['scanFailed'],scanFilter.attributes['scanStart'])
550
- #alert = @alerts.pop()
551
- #alert.setScanFilter(scanfilter)
552
- #@alerts.push(alert)
553
- end
554
- end
555
- end
556
- end
557
- end
558
-
559
- # === Description
560
- # Object that represents the scan history of a site.
561
- #
562
- class SiteScanHistory
563
- # true if an error condition exists; false otherwise
564
- attr_reader :error
565
- # Error message string
566
- attr_reader :error_msg
567
- # The last XML request sent by this object
568
- attr_reader :request_xml
569
- # The last response received by this object
570
- attr_reader :response
571
- # The NSC Connection associated with this object
572
- attr_reader :connection
573
- # The Site ID
574
- attr_reader :site_id
575
- # //Array containing (ScanSummary*)
576
- attr_reader :scan_summaries
577
-
578
- def initialize(connection, id)
579
- @site_id = id
580
- @error = false
581
- @connection = connection
582
- @scan_summaries = []
583
-
584
- @request_xml = '<SiteScanHistoryRequest' + ' session-id="' + @connection.session_id + '" site-id="' + "#{@site_id}" + '"/>'
585
- r = @connection.execute(@request_xml)
586
- @response = r
587
-
588
- if r and r.success
589
- r.res.elements.each('//ScanSummary') do |summary|
590
- scan_id=summary.attributes['scan-id'].to_i
591
- engine_id=summary.attributes['engine-id'].to_i
592
- name=summary.attributes['name'].to_s
593
- start_time=summary.attributes['startTime'].to_s
594
- end_time=summary.attributes['endTime'].to_s
595
- status=summary.attributes['status'].to_s
596
- scan_summary = ScanSummary.new(scan_id, engine_id, name, start_time, end_time, status)
597
- scan_summaries << scan_summary
598
- end
599
- end
600
- end
601
- end
602
-
603
- # === Description
604
- # Object that represents a listing of devices for a site or the entire NSC. Note that only devices which are accessible to the account used to create the connection object will be returned. This object is created and populated automatically with the instantiation of a new Site object.
605
- #
606
- class SiteDeviceListing
607
-
608
- # true if an error condition exists; false otherwise
609
- attr_reader :error
610
- # Error message string
611
- attr_reader :error_msg
612
- # The last XML request sent by this object
613
- attr_reader :request_xml
614
- # The last XML response received by this object
615
- attr_reader :response_xml
616
- # The NSC Connection associated with this object
617
- attr_reader :connection
618
- # The Site ID. 0 if all sites are specified.
619
- attr_reader :site_id
620
- # //Array of (Device)*
621
- attr_reader :devices
622
-
623
- def initialize(connection, site_id = 0)
624
-
625
- @site_id = site_id
626
- @error = false
627
- @connection = connection
628
- @devices = []
629
-
630
- r = nil
631
- if (@site_id)
632
- r = @connection.execute('<SiteDeviceListingRequest session-id="' + connection.session_id + '" site-id="' + "#{@site_id}" + '"/>')
633
- if r.success
634
- r.res.elements.each('SiteDeviceListingResponse/SiteDevices/device') do |d|
635
- @devices.push(Device.new(d.attributes['id'], @site_id, d.attributes["address"], d.attributes["riskfactor"], d.attributes["riskscore"]))
636
- end
637
- end
638
- else
639
- r = @connection.execute('<SiteDeviceListingRequest session-id="' + connection.session_id + '"/>')
640
- if r.success
641
- r.res.elements.each('SiteDeviceListingResponse/SiteDevices') do |rr|
642
- @sid = rr.attribute("site-id")
643
- rr.elements.each('device') do |d|
644
- @devices.push(Device.new(d.attributes['id'], @sid, d.attributes["address"], d.attributes['riskfactor'], d.attributes['riskscore']))
645
- end
646
- end
647
- end
648
- end
649
- end
650
- end
651
-
652
- # === Description
653
- # Object that represents a single device in an NSC.
654
- #
655
- class Device
656
-
657
- # A unique device ID (assigned by the NSC)
658
- attr_reader :id
659
- # The site ID of this devices site
660
- attr_reader :site_id
661
- # IP Address or Hostname of this device
662
- attr_reader :address
663
- # User assigned risk multiplier
664
- attr_reader :riskfactor
665
- # NeXpose risk score
666
- attr_reader :riskscore
667
-
668
- def initialize(id, site_id, address, riskfactor=1, riskscore=0)
669
- @id = id
670
- @site_id = site_id
671
- @address = address
672
- @riskfactor = riskfactor
673
- @riskscore = riskscore
674
-
675
- end
676
- end
677
-
678
- # === Description
679
- # Object that holds a scan schedule
680
- #
681
- class Schedule
682
- # Type of Schedule (daily|hourly|monthly|weekly)
683
- attr_reader :type
684
- # The schedule interval
685
- attr_reader :interval
686
- # The date and time to start the first scan
687
- attr_reader :start
688
- # Enable or disable this schedule
689
- attr_reader :enabled
690
- # The date and time to disable to schedule. If null then the schedule will run forever.
691
- attr_reader :notValidAfter
692
- # Scan on the same date each time
693
- attr_reader :byDate
694
-
695
- def initialize(type, interval, start, enabled = 1)
696
-
697
- @type = type
698
- @interval = interval
699
- @start = start
700
- @enabled = enabled
701
-
702
- end
703
- end
704
-
705
- # === Description
706
- # Object that represents a Syslog Alert.
707
- #
708
- class SyslogAlert
709
-
710
- # A unique name for this alert
711
- attr_reader :name
712
- # If this alert is enabled or not
713
- attr_reader :enabled
714
- # The Syslog server to sent this alert
715
- attr_reader :server
716
- # The vulnerability filter to trigger the alert
717
- attr_reader :vulnFilter
718
- # The alert type
719
- attr_reader :type
720
-
721
- def initialize(name, server, enabled = 1)
722
- @type = :syslog
723
- @name = name
724
- @server = server
725
- @enabled = enabled
726
- # Sets default vuln filter - All Events
727
- setVulnFilter(VulnFilter.new("50790400", 1))
728
-
729
- end
730
-
731
- # Sets the Vulnerability Filter for this alert.
732
- def setVulnFilter(vulnFilter)
733
- @vulnFilter = vulnFilter
734
- end
735
-
736
- include Sanitize
737
-
738
- def to_xml
739
- xml = "<syslogAlert"
740
- xml << %Q{ name="#{replace_entities(name)}"}
741
- xml << %Q{ enabled="#{replace_entities(enabled)}"}
742
- xml << %Q{ server="#{replace_entities(server)}">}
743
- xml << vulnFilter.to_xml
744
- xml << "</syslogAlert>"
745
- xml
746
- end
747
-
748
- end
749
-
750
- # === Description
751
- # Object that represents an SNMP Alert.
752
- #
753
- class SnmpAlert
754
- include Sanitize
755
-
756
- # A unique name for this alert
757
- attr_reader :name
758
- # If this alert is enabled or not
759
- attr_reader :enabled
760
- # The community string
761
- attr_reader :community
762
- # The SNMP server to sent this alert
763
- attr_reader :server
764
- # The vulnerability filter to trigger the alert
765
- attr_reader :vulnFilter
766
- # The alert type
767
- attr_reader :type
768
-
769
- def initialize(name, community, server, enabled = 1)
770
- @type = :snmp
771
- @name = name
772
- @community = community
773
- @server = server
774
- @enabled = enabled
775
- # Sets default vuln filter - All Events
776
- setVulnFilter(VulnFilter.new("50790400", 1))
777
- end
778
-
779
- # Sets the Vulnerability Filter for this alert.
780
- def setVulnFilter(vulnFilter)
781
- @vulnFilter = vulnFilter
782
- end
783
-
784
- def to_xml
785
- xml = "<snmpAlert"
786
- xml << %Q{ name="#{replace_entities(name)}"}
787
- xml << %Q{ enabled="#{replace_entities(enabled)}"}
788
- xml << %Q{ community="#{replace_entities(community)}"}
789
- xml << %Q{ server="#{replace_entities(server)}">}
790
- xml << vulnFilter.to_xml
791
- xml << "</snmpAlert>"
792
- xml
793
- end
794
-
795
- end
796
-
797
- # === Description
798
- # Object that represents an SMTP (Email) Alert.
799
- #
800
- class SmtpAlert
801
- # A unique name for this alert
802
- attr_reader :name
803
- # If this alert is enabled or not
804
- attr_reader :enabled
805
- # The email address of the sender
806
- attr_reader :sender
807
- # Limit the text for mobile devices
808
- attr_reader :limitText
809
- # Array containing Strings of email addresses
810
- # Array of strings with the email addresses of the intended recipients
811
- attr_reader :recipients
812
- # The vulnerability filter to trigger the alert
813
- attr_reader :vulnFilter
814
- # The alert type
815
- attr_reader :type
816
-
817
- def initialize(name, sender, limitText, enabled = 1)
818
- @type = :smtp
819
- @name = name
820
- @sender = sender
821
- @enabled = enabled
822
- @limitText = limitText
823
- @recipients = []
824
- # Sets default vuln filter - All Events
825
- setVulnFilter(VulnFilter.new("50790400", 1))
826
- end
827
-
828
- # Adds a new Recipient to the recipients array
829
- def addRecipient(recipient)
830
- @recipients.push(recipient)
831
- end
832
-
833
- # Sets the Vulnerability Filter for this alert.
834
- def setVulnFilter(vulnFilter)
835
- @vulnFilter = vulnFilter
836
- end
837
-
838
- include Sanitize
839
-
840
- def to_xml
841
- xml = "<smtpAlert"
842
- xml << %Q{ name="#{replace_entities(name)}"}
843
- xml << %Q{ enabled="#{replace_entities(enabled)}"}
844
- xml << %Q{ sender="#{replace_entities(sender)}"}
845
- xml << %Q{ limitText="#{replace_entities(limitText)}">}
846
- recipients.each do |recpt|
847
- xml << "<recipient>#{replace_entities(recpt)}</recipient>"
848
- end
849
- xml << vulnFilter.to_xml
850
- xml << "</smtpAlert>"
851
- xml
852
- end
853
- end
854
-
855
- # === Description
856
- # Object that represents a hostname to be added to a site.
857
- class HostName
858
-
859
- # The hostname
860
- attr_reader :hostname
861
-
862
- def initialize(hostname)
863
- @hostname = hostname
864
- end
865
-
866
- include Sanitize
867
-
868
- def to_xml
869
- "<host>#{replace_entities(hostname)}</host>"
870
- end
871
- end
872
-
873
- # === Description
874
- # Object that represents a single IP address or an inclusive range of IP addresses.
875
- # If to is nil then the from field will be used to specify a single IP Address only.
876
- #
877
- class IPRange
878
- # Start of Range *Required
879
- attr_reader :from
880
- # End of Range *Optional (If Null then IPRange is a single IP Address)
881
- attr_reader :to
882
-
883
- def initialize(from, to = nil)
884
- @from = from
885
- @to = to
886
- end
887
-
888
- include Sanitize
889
-
890
- def to_xml
891
- if (to and not to.empty?)
892
- return %Q{<range from="#{from}" to="#{to}"/>}
893
- else
894
- return %Q{<range from="#{from}"/>}
895
- end
896
- end
897
- end
898
- end
1
+ module Nexpose
2
+ module NexposeAPI
3
+ include XMLUtils
4
+
5
+ #
6
+ #
7
+ #
8
+ def site_device_listing(site_id)
9
+ r = execute(make_xml('SiteDeviceListingRequest', {'site-id' => site_id.to_s}))
10
+
11
+ if (r.success)
12
+ res = []
13
+ r.res.elements.each("//device") do |device|
14
+ res << {
15
+ :device_id => device.attributes['id'].to_i,
16
+ # TODO Covert to using?
17
+ # :address => IPAddr.new(device.attributes['address']),
18
+ :address => device.attributes['address'].to_s,
19
+ :risk_factor => device.attributes['riskfactor'].to_f,
20
+ :risk_score => device.attributes['riskscore'].to_f,
21
+ }
22
+ end
23
+ res
24
+ else
25
+ false
26
+ end
27
+ end
28
+
29
+ #
30
+ #
31
+ #
32
+ def site_delete(param)
33
+ r = execute(make_xml('SiteDeleteRequest', {'site-id' => param}))
34
+ r.success
35
+ end
36
+
37
+ #
38
+ #
39
+ # TODO Should just return empty array if doesn't work?
40
+ def site_listing
41
+ r = execute(make_xml('SiteListingRequest', {}))
42
+
43
+ if (r.success)
44
+ res = []
45
+ r.res.elements.each("//SiteSummary") do |site|
46
+ res << {
47
+ :site_id => site.attributes['id'].to_i,
48
+ :name => site.attributes['name'].to_s,
49
+ :risk_factor => site.attributes['riskfactor'].to_f,
50
+ :risk_score => site.attributes['riskscore'].to_f,
51
+ }
52
+ end
53
+ res
54
+ else
55
+ false
56
+ end
57
+ end
58
+
59
+ #-----------------------------------------------------------------------
60
+ # TODO: Needs to be expanded to included details
61
+ # Also confusing. Name clashes with field on Site
62
+ #-----------------------------------------------------------------------
63
+ def site_scan_history(site_id)
64
+ r = execute(make_xml('SiteScanHistoryRequest', {'site-id' => site_id.to_s}))
65
+
66
+ if (r.success)
67
+ res = []
68
+ r.res.elements.each("//ScanSummary") do |site_scan_history|
69
+ res << {
70
+ :site_id => site_scan_history.attributes['site-id'].to_i,
71
+ :scan_id => site_scan_history.attributes['scan-id'].to_i,
72
+ :engine_id => site_scan_history.attributes['engine-id'].to_i,
73
+ :start_time => site_scan_history.attributes['startTime'].to_s,
74
+ :end_time => site_scan_history.attributes['endTime'].to_s
75
+ }
76
+ end
77
+ res
78
+ else
79
+ false
80
+ end
81
+ end
82
+
83
+ #-----------------------------------------------------------------------
84
+ # Starts device specific site scanning.
85
+ #
86
+ # devices - An Array of device IDs
87
+ # hosts - An Array of Hashes [o]=>{:range=>"to,from"} [1]=>{:host=>host}
88
+ #-----------------------------------------------------------------------
89
+ def site_device_scan_start(site_id, devices, hosts)
90
+
91
+ if hosts == nil and devices == nil
92
+ raise ArgumentError.new("Both the device and host list is nil")
93
+ end
94
+
95
+ xml = make_xml('SiteDevicesScanRequest', {'site-id' => site_id})
96
+
97
+ if devices != nil
98
+ inner_xml = REXML::Element.new 'Devices'
99
+ for device_id in devices
100
+ inner_xml.add_element 'device', {'id' => "#{device_id}"}
101
+ end
102
+ xml.add_element inner_xml
103
+ end
104
+
105
+ if hosts
106
+ inner_xml = REXML::Element.new 'Hosts'
107
+ hosts.each_index do |x|
108
+ if hosts[x].key? :range
109
+ to = hosts[x][:range].split(',')[0]
110
+ from = hosts[x][:range].split(',')[1]
111
+ inner_xml.add_element 'range', {'to' => "#{to}", 'from' => "#{from}"}
112
+ end
113
+ if hosts[x].key? :host
114
+ host_element = REXML::Element.new 'host'
115
+ host_element.text = "#{hosts[x][:host]}"
116
+ inner_xml.add_element host_element
117
+ end
118
+ end
119
+ xml.add_element inner_xml
120
+ end
121
+
122
+ r = execute xml
123
+ if r.success
124
+ r.res.elements.each('//Scan') do |scan_info|
125
+ return {
126
+ :scan_id => scan_info.attributes['scan-id'].to_i,
127
+ :engine_id => scan_info.attributes['engine-id'].to_i
128
+ }
129
+ end
130
+ else
131
+ false
132
+ end
133
+ end
134
+ end
135
+
136
+ # Configuration object representing a Nexpose site.
137
+ #
138
+ # For a basic walk-through, see {https://github.com/rapid7/nexpose-client/wiki/Using-Sites}
139
+ class Site
140
+
141
+ # The site ID. An ID of -1 is used to designate a site that has not been
142
+ # saved to a Nexpose console.
143
+ attr_accessor :id
144
+
145
+ # Unique name of the site. Required.
146
+ attr_accessor :name
147
+
148
+ # Description of the site.
149
+ attr_accessor :description
150
+
151
+ # [Array] Collection of assets. May be IPv4, IPv6, or DNS names.
152
+ # @see HostName
153
+ # @see IPRange
154
+ attr_accessor :assets
155
+
156
+ # Scan template to use when starting a scan job. Default: full-audit
157
+ attr_accessor :scan_template
158
+
159
+ # Friendly name of scan template to use when starting a scan job.
160
+ # Value is populated when a site is saved or loaded from a console.
161
+ attr_accessor :scan_template_name
162
+
163
+ # Scan Engine to use. Will use the default engine if nil or -1.
164
+ attr_accessor :engine
165
+
166
+ # [Array] Schedule starting dates and times for scans, and set their frequency.
167
+ attr_accessor :schedules
168
+
169
+ # The risk factor associated with this site. Default: 1.0
170
+ attr_accessor :risk_factor
171
+
172
+ # [Array] Collection of credentials associated with this site.
173
+ attr_accessor :credentials
174
+
175
+ # [Array] Collection of real-time alerts.
176
+ # @see SMTPAlert
177
+ # @see SNMPAlert
178
+ # @see SyslogAlert
179
+ attr_accessor :alerts
180
+
181
+ # Configuration version. Default: 3
182
+ attr_accessor :config_version
183
+
184
+ # Whether or not this site is dynamic.
185
+ # Dynamic sites are created through Asset Discovery Connections.
186
+ # Modifying their behavior through the API is not recommended.
187
+ attr_accessor :is_dynamic
188
+
189
+ # Site constructor. Both arguments are optional.
190
+ #
191
+ # @param [String] name Unique name of the site.
192
+ # @param [String] scan_template ID of the scan template to use.
193
+ def initialize(name = nil, scan_template = 'full-audit')
194
+ @name = name;
195
+ @scan_template = scan_template
196
+
197
+ @id = -1
198
+ @risk_factor = 1.0
199
+ @config_version = 3
200
+ @is_dynamic = false
201
+ @assets = []
202
+ @schedules = []
203
+ @credentials = []
204
+ @alerts = []
205
+ end
206
+
207
+ # Returns true when the site is dynamic.
208
+ def dynamic?
209
+ is_dynamic
210
+ end
211
+
212
+ # Load an existing configuration from a Nexpose instance.
213
+ #
214
+ # @param [Connection] connection Connection to console where scan will be launched.
215
+ # @param [Fixnum] id Site ID of an existing site.
216
+ # @return [Site] Site configuration loaded from a Nexpose console.
217
+ def self.load(connection, id)
218
+ r = APIRequest.execute(connection.url, %Q(<SiteConfigRequest session-id="#{connection.session_id}" site-id="#{id}"/>))
219
+ parse(r.res)
220
+ end
221
+
222
+ # Copy an existing configuration from a Nexpose instance.
223
+ #
224
+ # @param [Connection] connection Connection to console where scan will be launched.
225
+ # @param [Fixnum] id Site ID of an existing site.
226
+ # @return [Site] Site configuration loaded from a Nexpose console.
227
+ def self.copy(connection, id)
228
+ site = self.load(connection, id)
229
+ site.id = -1
230
+ site.name = "#{site.name} Copy"
231
+ site
232
+ end
233
+
234
+ # Saves this site to a Nexpose console.
235
+ #
236
+ # @param [Connection] connection Connection to console where this site will be saved.
237
+ # @return [Fixnum] Site ID assigned to this configuration, if successful.
238
+ def save(connection)
239
+ r = connection.execute('<SiteSaveRequest session-id="' + connection.session_id + '">' + to_xml + ' </SiteSaveRequest>')
240
+ if (r.success)
241
+ @id = r.attributes['site-id']
242
+ return @id
243
+ end
244
+ end
245
+
246
+ # Delete this site from a Nexpose console.
247
+ #
248
+ # @param [Connection] connection Connection to console where this site will be saved.
249
+ # @return [Boolean] Whether or not the site was successfully deleted.
250
+ def delete(connection)
251
+ r = connection.execute(%Q{<SiteDeleteRequest session-id="#{connection.session_id}" site-id="#@id"/>})
252
+ r.success
253
+ end
254
+
255
+ # Scan this site.
256
+ #
257
+ # @param [Connection] connection Connection to console where scan will be launched.
258
+ # @param [String] sync_id Optional syncronization token.
259
+ # @return [Fixnum, Fixnum] Scan ID and engine ID where the scan was launched.
260
+ def scan(connection, sync_id = nil)
261
+ xml = REXML::Element.new('SiteScanRequest')
262
+ xml.add_attributes({'session-id' => connection.session_id,
263
+ 'site-id' => id,
264
+ 'sync-id' => sync_id})
265
+
266
+ response = connection.execute(xml)
267
+ if response.success
268
+ response.res.elements.each('/SiteScanResponse/Scan/') do |scan|
269
+ return [scan.attributes['scan-id'].to_i, scan.attributes['engine-id'].to_i]
270
+ end
271
+ end
272
+ end
273
+
274
+ # Generate an XML representation of this site configuration
275
+ # @return [String] XML valid for submission as part of other requests.
276
+ def to_xml
277
+ xml = %Q(<Site id='#{id}' name='#{name}' description='#{description}' riskfactor='#{risk_factor}'>)
278
+
279
+ xml << '<Hosts>'
280
+ xml << assets.reduce('') { |acc, host| acc << host.to_xml }
281
+ xml << '</Hosts>'
282
+
283
+ unless credentials.empty?
284
+ xml << '<Credentials>'
285
+ credentials.each do |c|
286
+ xml << c.to_xml if c.respond_to? :to_xml
287
+ end
288
+ xml << '</Credentials>'
289
+ end
290
+
291
+ unless alerts.empty?
292
+ xml << '<Alerting>'
293
+ alerts.each do |a|
294
+ xml << a.to_xml if a.respond_to? :to_xml
295
+ end
296
+ xml << '</Alerting>'
297
+ end
298
+
299
+ xml << %Q(<ScanConfig configID="#{@id}" name="#{@scan_template_name || @scan_template}" templateID="#{@scan_template}" configVersion="#{@config_version || 3}" engineID="#{@engine}">)
300
+
301
+ xml << '<Schedules>'
302
+ @schedules.each do |sched|
303
+ xml << %Q{<Schedule enabled="#{sched.enabled ? 1 : 0}" type="#{sched.type}" interval="#{sched.interval}" start="#{sched.start}" />}
304
+ end
305
+ xml << '</Schedules>'
306
+ xml << '</ScanConfig>'
307
+ xml << '</Site>'
308
+ end
309
+
310
+ # Parse a response from a Nexpose console into a valid Site object.
311
+ #
312
+ # @param [REXML::Document] rexml XML document to parse.
313
+ # @return [Site] Site object represented by the XML.
314
+ # ## TODO What is returned on failure?
315
+ def self.parse(rexml)
316
+ rexml.elements.each('SiteConfigResponse/Site') do |s|
317
+ site = Site.new(s.attributes['name'])
318
+ site.id = s.attributes['id'].to_i
319
+ site.description = s.attributes['description']
320
+ site.risk_factor = s.attributes['riskfactor'] || 1.0
321
+ site.is_dynamic = true if s.attributes['isDynamic'] == '1'
322
+
323
+ s.elements.each('Hosts/range') do |r|
324
+ site.assets << IPRange.new(r.attributes['from'], r.attributes['to'])
325
+ end
326
+ s.elements.each('Hosts/host') do |host|
327
+ site.assets << HostName.new(host.text)
328
+ end
329
+
330
+ s.elements.each('ScanConfig') do |scan_config|
331
+ site.scan_template_name = scan_config.attributes['name']
332
+ site.scan_template = scan_config.attributes['templateID']
333
+ site.config_version = scan_config.attributes['configVersion'].to_i
334
+ site.engine = scan_config.attributes['engineID'].to_i
335
+ scan_config.elements.each('Schedules/Schedule') do |sched|
336
+ schedule = Schedule.new(sched.attributes['type'],
337
+ sched.attributes['interval'],
338
+ sched.attributes['start'],
339
+ sched.attributes['enabled'])
340
+ site.schedules << schedule
341
+ end
342
+ end
343
+
344
+ s.elements.each('Credentials') do |cred|
345
+ # TODO
346
+ end
347
+
348
+ s.elements.each('Alerting/Alert') do |a|
349
+ a.elements.each('smtpAlert') do |smtp|
350
+ smtp_alert = SMTPAlert.new(a.attributes['name'], smtp.attributes['sender'], smtp.attributes['limitText'], a.attributes['enabled'])
351
+
352
+ smtp.elements.each('recipient') do |recipient|
353
+ smtp_alert.addRecipient(recipient.text)
354
+ end
355
+ site.alerts << smtp_alert
356
+ end
357
+
358
+ a.elements.each('snmpAlert') do |snmp|
359
+ snmp_alert = SNMPAlert.new(a.attributes['name'], snmp.attributes['community'], snmp.attributes['server'], a.attributes['enabled'])
360
+ site.alerts << snmp_alert
361
+ end
362
+
363
+ a.elements.each('syslogAlert') do |syslog|
364
+ syslog_alert = SyslogAlert.new(a.attributes['name'], syslog.attributes['server'], a.attributes['enabled'])
365
+ site.alerts << syslog_alert
366
+ end
367
+
368
+ a.elements.each('vulnFilter') do |vulnFilter|
369
+
370
+ #vulnfilter = new VulnFilter.new(a.attributes["typemask"], a.attributes["severityThreshold"], $attrs["MAXALERTS"])
371
+ # Pop off the top alert on the stack
372
+ #$alert = @alerts.pop()
373
+ # Add the new recipient string to the Alert Object
374
+ #$alert.setVulnFilter($vulnfilter)
375
+ # Push the alert back on to the alert stack
376
+ #array_push($this->alerts, $alert)
377
+ end
378
+
379
+ a.elements.each('scanFilter') do |scanFilter|
380
+ #<scanFilter scanStop='0' scanFailed='0' scanStart='1'/>
381
+ #scanfilter = ScanFilter.new(scanFilter.attributes['scanStop'],scanFilter.attributes['scanFailed'],scanFilter.attributes['scanStart'])
382
+ #alert = @alerts.pop()
383
+ #alert.setScanFilter(scanfilter)
384
+ #@alerts.push(alert)
385
+ end
386
+ end
387
+
388
+ return site
389
+ end
390
+ nil
391
+ end
392
+ end
393
+
394
+ # === Description
395
+ # Object that represents a listing of all of the sites available on an NSC.
396
+ #
397
+ # === Example
398
+ # # Create a new Nexpose Connection on the default port and Login
399
+ # nsc = Connection.new("10.1.40.10","nxadmin","password")
400
+ # nsc->login;
401
+ #
402
+ # # Get Site Listing
403
+ # sitelisting = SiteListing.new(nsc)
404
+ #
405
+ # # Enumerate through all of the SiteSummaries
406
+ # sitelisting.sites.each do |sitesummary|
407
+ # # Do some operation on each site
408
+ # end
409
+ #
410
+ class SiteListing
411
+ # true if an error condition exists; false otherwise
412
+ attr_reader :error
413
+ # Error message string
414
+ attr_reader :error_msg
415
+ # The last XML request sent by this object
416
+ attr_reader :request_xml
417
+ # The last XML response received by this object
418
+ attr_reader :response_xml
419
+ # The NSC Connection associated with this object
420
+ attr_reader :connection
421
+ # Array containing SiteSummary objects for each site in the connection
422
+ attr_reader :sites
423
+ # The number of sites
424
+ attr_reader :site_count
425
+
426
+ # Constructor
427
+ # SiteListing (connection)
428
+ def initialize(connection)
429
+ @sites = []
430
+
431
+ @connection = connection
432
+
433
+ r = @connection.execute('<SiteListingRequest session-id="' + @connection.session_id.to_s + '"/>')
434
+
435
+ if (r.success)
436
+ parse(r.res)
437
+ else
438
+ raise APIError.new(r, "Failed to get site listing")
439
+ end
440
+ end
441
+
442
+ def parse(r)
443
+ r.elements.each('SiteListingResponse/SiteSummary') do |s|
444
+ site_summary = SiteSummary.new(
445
+ s.attributes['id'].to_s,
446
+ s.attributes['name'].to_s,
447
+ s.attributes['description'].to_s,
448
+ s.attributes['riskfactor'].to_s
449
+ )
450
+ @sites.push(site_summary)
451
+ end
452
+ @site_count = @sites.length
453
+ end
454
+ end
455
+
456
+ # === Description
457
+ # Object that represents the summary of a Nexpose Site.
458
+ #
459
+ class SiteSummary
460
+ # The Site ID
461
+ attr_reader :id
462
+ # The Site Name
463
+ attr_reader :site_name
464
+ # A Description of the Site
465
+ attr_reader :description
466
+ # User assigned risk multiplier
467
+ attr_reader :riskfactor
468
+
469
+ # Constructor
470
+ # SiteSummary(id, site_name, description, riskfactor = 1)
471
+ def initialize(id, site_name, description, riskfactor = 1.0)
472
+ @id = id
473
+ @site_name = site_name
474
+ @description = description
475
+ @riskfactor = riskfactor
476
+ end
477
+
478
+ def _set_id(id)
479
+ @id = id
480
+ end
481
+ end
482
+
483
+ # === Description
484
+ # Object that represents the scan history of a site.
485
+ #
486
+ class SiteScanHistory
487
+ # true if an error condition exists; false otherwise
488
+ attr_reader :error
489
+ # Error message string
490
+ attr_reader :error_msg
491
+ # The last XML request sent by this object
492
+ attr_reader :request_xml
493
+ # The last response received by this object
494
+ attr_reader :response
495
+ # The NSC Connection associated with this object
496
+ attr_reader :connection
497
+ # The Site ID
498
+ attr_reader :site_id
499
+ # //Array containing (ScanSummary*)
500
+ attr_reader :scan_summaries
501
+
502
+ def initialize(connection, id)
503
+ @site_id = id
504
+ @error = false
505
+ @connection = connection
506
+ @scan_summaries = []
507
+
508
+ @request_xml = '<SiteScanHistoryRequest' + ' session-id="' + @connection.session_id + '" site-id="' + "#{@site_id}" + '"/>'
509
+ r = @connection.execute(@request_xml)
510
+ @response = r
511
+
512
+ if r and r.success
513
+ r.res.elements.each('//ScanSummary') do |summary|
514
+ scan_id=summary.attributes['scan-id'].to_i
515
+ engine_id=summary.attributes['engine-id'].to_i
516
+ name=summary.attributes['name'].to_s
517
+ start_time=summary.attributes['startTime'].to_s
518
+ end_time=summary.attributes['endTime'].to_s
519
+ status=summary.attributes['status'].to_s
520
+ scan_summary = ScanSummary.new(scan_id, engine_id, name, start_time, end_time, status)
521
+ scan_summaries << scan_summary
522
+ end
523
+ end
524
+ end
525
+ end
526
+
527
+ # === Description
528
+ # Object that represents a listing of devices for a site or the entire NSC. Note that only devices which are accessible to the account used to create the connection object will be returned. This object is created and populated automatically with the instantiation of a new Site object.
529
+ #
530
+ class SiteDeviceListing
531
+
532
+ # true if an error condition exists; false otherwise
533
+ attr_reader :error
534
+ # Error message string
535
+ attr_reader :error_msg
536
+ # The last XML request sent by this object
537
+ attr_reader :request_xml
538
+ # The last XML response received by this object
539
+ attr_reader :response_xml
540
+ # The NSC Connection associated with this object
541
+ attr_reader :connection
542
+ # The Site ID. 0 if all sites are specified.
543
+ attr_reader :site_id
544
+ # //Array of (Device)*
545
+ attr_reader :devices
546
+
547
+ def initialize(connection, site_id = 0)
548
+
549
+ @site_id = site_id
550
+ @error = false
551
+ @connection = connection
552
+ @devices = []
553
+
554
+ r = nil
555
+ if (@site_id)
556
+ r = @connection.execute('<SiteDeviceListingRequest session-id="' + connection.session_id + '" site-id="' + "#{@site_id}" + '"/>')
557
+ if r.success
558
+ r.res.elements.each('SiteDeviceListingResponse/SiteDevices/device') do |d|
559
+ @devices.push(Device.new(d.attributes['id'], @site_id, d.attributes["address"], d.attributes["riskfactor"], d.attributes["riskscore"]))
560
+ end
561
+ end
562
+ else
563
+ r = @connection.execute('<SiteDeviceListingRequest session-id="' + connection.session_id + '"/>')
564
+ if r.success
565
+ r.res.elements.each('SiteDeviceListingResponse/SiteDevices') do |rr|
566
+ @sid = rr.attribute("site-id")
567
+ rr.elements.each('device') do |d|
568
+ @devices.push(Device.new(d.attributes['id'], @sid, d.attributes["address"], d.attributes['riskfactor'], d.attributes['riskscore']))
569
+ end
570
+ end
571
+ end
572
+ end
573
+ end
574
+ end
575
+
576
+ # === Description
577
+ # Object that represents a single device in an NSC.
578
+ #
579
+ class Device
580
+
581
+ # A unique device ID (assigned by the NSC)
582
+ attr_reader :id
583
+ # The site ID of this devices site
584
+ attr_reader :site_id
585
+ # IP Address or Hostname of this device
586
+ attr_reader :address
587
+ # User assigned risk multiplier
588
+ attr_reader :riskfactor
589
+ # Nexpose risk score
590
+ attr_reader :riskscore
591
+
592
+ def initialize(id, site_id, address, riskfactor=1, riskscore=0)
593
+ @id = id
594
+ @site_id = site_id
595
+ @address = address
596
+ @riskfactor = riskfactor
597
+ @riskscore = riskscore
598
+
599
+ end
600
+ end
601
+
602
+ # === Description
603
+ # Object that represents a Syslog Alert.
604
+ #
605
+ class SyslogAlert
606
+
607
+ # A unique name for this alert
608
+ attr_reader :name
609
+ # If this alert is enabled or not
610
+ attr_reader :enabled
611
+ # The Syslog server to sent this alert
612
+ attr_reader :server
613
+ # The vulnerability filter to trigger the alert
614
+ attr_reader :vulnFilter
615
+ # The alert type
616
+ attr_reader :type
617
+
618
+ def initialize(name, server, enabled = 1)
619
+ @type = :syslog
620
+ @name = name
621
+ @server = server
622
+ @enabled = enabled
623
+ # Sets default vuln filter - All Events
624
+ setVulnFilter(VulnFilter.new("50790400", 1))
625
+
626
+ end
627
+
628
+ # Sets the Vulnerability Filter for this alert.
629
+ def setVulnFilter(vulnFilter)
630
+ @vulnFilter = vulnFilter
631
+ end
632
+
633
+ include Sanitize
634
+
635
+ def to_xml
636
+ xml = "<syslogAlert"
637
+ xml << %Q{ name="#{replace_entities(name)}"}
638
+ xml << %Q{ enabled="#{replace_entities(enabled)}"}
639
+ xml << %Q{ server="#{replace_entities(server)}">}
640
+ xml << vulnFilter.to_xml
641
+ xml << "</syslogAlert>"
642
+ xml
643
+ end
644
+
645
+ end
646
+
647
+ # === Description
648
+ # Object that represents an SNMP Alert.
649
+ #
650
+ class SNMPAlert
651
+ include Sanitize
652
+
653
+ # A unique name for this alert
654
+ attr_reader :name
655
+ # If this alert is enabled or not
656
+ attr_reader :enabled
657
+ # The community string
658
+ attr_reader :community
659
+ # The SNMP server to sent this alert
660
+ attr_reader :server
661
+ # The vulnerability filter to trigger the alert
662
+ attr_reader :vulnFilter
663
+ # The alert type
664
+ attr_reader :type
665
+
666
+ def initialize(name, community, server, enabled = 1)
667
+ @type = :snmp
668
+ @name = name
669
+ @community = community
670
+ @server = server
671
+ @enabled = enabled
672
+ # Sets default vuln filter - All Events
673
+ setVulnFilter(VulnFilter.new("50790400", 1))
674
+ end
675
+
676
+ # Sets the Vulnerability Filter for this alert.
677
+ def setVulnFilter(vulnFilter)
678
+ @vulnFilter = vulnFilter
679
+ end
680
+
681
+ def to_xml
682
+ xml = "<snmpAlert"
683
+ xml << %Q{ name="#{replace_entities(name)}"}
684
+ xml << %Q{ enabled="#{replace_entities(enabled)}"}
685
+ xml << %Q{ community="#{replace_entities(community)}"}
686
+ xml << %Q{ server="#{replace_entities(server)}">}
687
+ xml << vulnFilter.to_xml
688
+ xml << "</snmpAlert>"
689
+ xml
690
+ end
691
+
692
+ end
693
+
694
+ # === Description
695
+ # Object that represents an SMTP (Email) Alert.
696
+ #
697
+ class SMTPAlert
698
+ # A unique name for this alert
699
+ attr_reader :name
700
+ # If this alert is enabled or not
701
+ attr_reader :enabled
702
+ # The email address of the sender
703
+ attr_reader :sender
704
+ # Limit the text for mobile devices
705
+ attr_reader :limitText
706
+ # Array containing Strings of email addresses
707
+ # Array of strings with the email addresses of the intended recipients
708
+ attr_reader :recipients
709
+ # The vulnerability filter to trigger the alert
710
+ attr_reader :vulnFilter
711
+ # The alert type
712
+ attr_reader :type
713
+
714
+ def initialize(name, sender, limitText, enabled = 1)
715
+ @type = :smtp
716
+ @name = name
717
+ @sender = sender
718
+ @enabled = enabled
719
+ @limitText = limitText
720
+ @recipients = []
721
+ # Sets default vuln filter - All Events
722
+ setVulnFilter(VulnFilter.new("50790400", 1))
723
+ end
724
+
725
+ # Adds a new Recipient to the recipients array
726
+ def addRecipient(recipient)
727
+ @recipients.push(recipient)
728
+ end
729
+
730
+ # Sets the Vulnerability Filter for this alert.
731
+ def setVulnFilter(vulnFilter)
732
+ @vulnFilter = vulnFilter
733
+ end
734
+
735
+ include Sanitize
736
+
737
+ def to_xml
738
+ xml = "<smtpAlert"
739
+ xml << %Q{ name="#{replace_entities(name)}"}
740
+ xml << %Q{ enabled="#{replace_entities(enabled)}"}
741
+ xml << %Q{ sender="#{replace_entities(sender)}"}
742
+ xml << %Q{ limitText="#{replace_entities(limitText)}">}
743
+ recipients.each do |recpt|
744
+ xml << "<recipient>#{replace_entities(recpt)}</recipient>"
745
+ end
746
+ xml << vulnFilter.to_xml
747
+ xml << "</smtpAlert>"
748
+ xml
749
+ end
750
+ end
751
+
752
+ # === Description
753
+ # Object that represents a hostname to be added to a site.
754
+ class HostName
755
+ # Named host (usually DNS or Netbios name).
756
+ attr_accessor :host
757
+
758
+ def initialize(hostname)
759
+ @host = hostname
760
+ end
761
+
762
+ include Comparable
763
+
764
+ def <=>(other)
765
+ to_xml <=> other.to_xml
766
+ end
767
+
768
+ def eql?(other)
769
+ to_xml == other.to_xml
770
+ end
771
+
772
+ def hash
773
+ to_xml.hash
774
+ end
775
+
776
+ def to_xml_elem
777
+ xml = REXML::Element.new('host')
778
+ xml.text = @host
779
+ xml
780
+ end
781
+
782
+ def to_xml
783
+ to_xml_elem.to_s
784
+ end
785
+ end
786
+
787
+ # === Description
788
+ # Object that represents a single IP address or an inclusive range of IP addresses.
789
+ # If to is nil then the from field will be used to specify a single IP Address only.
790
+ class IPRange
791
+
792
+ # Start of range *Required
793
+ attr_accessor :from
794
+ # End of range *Optional (If nil then IPRange is a single IP Address)
795
+ attr_accessor :to
796
+
797
+ def initialize(from, to = nil)
798
+ @from = IPAddr.new(from)
799
+ @to = IPAddr.new(to) if to
800
+ end
801
+
802
+ include Comparable
803
+
804
+ def <=>(other)
805
+ to_xml <=> other.to_xml
806
+ end
807
+
808
+ def eql?(other)
809
+ to_xml == other.to_xml
810
+ end
811
+
812
+ def hash
813
+ to_xml.hash
814
+ end
815
+
816
+ def to_xml_elem
817
+ xml = REXML::Element.new('range')
818
+ xml.add_attributes({'from' => @from, 'to' => @to})
819
+ xml
820
+ end
821
+
822
+ def to_xml
823
+ to_xml_elem.to_s
824
+ end
825
+ end
826
+ end