nexpose 0.9.8 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/lib/nexpose.rb +8 -4
- data/lib/nexpose/ajax.rb +29 -4
- data/lib/nexpose/alert.rb +160 -177
- data/lib/nexpose/api.rb +18 -0
- data/lib/nexpose/common.rb +144 -10
- data/lib/nexpose/credential.rb +185 -1
- data/lib/nexpose/discovery.rb +141 -16
- data/lib/nexpose/discovery/filter.rb +26 -3
- data/lib/nexpose/engine.rb +16 -0
- data/lib/nexpose/json_serializer.rb +92 -0
- data/lib/nexpose/scan.rb +131 -23
- data/lib/nexpose/scan_template.rb +1 -1
- data/lib/nexpose/shared_secret.rb +31 -0
- data/lib/nexpose/site.rb +339 -317
- data/lib/nexpose/site_credentials.rb +178 -0
- data/lib/nexpose/tag.rb +42 -1
- data/lib/nexpose/util.rb +11 -16
- data/lib/nexpose/version.rb +1 -1
- data/lib/nexpose/wait.rb +103 -0
- data/lib/nexpose/web_credentials.rb +252 -0
- metadata +18 -8
- data/lib/nexpose/site_credential.rb +0 -323
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0443897c1940a9c3099ecc230589e7925fca9697
|
4
|
+
data.tar.gz: e31a6036c4170c0fa796b961973601bc9d5db6bb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e9b887b9f2564478be53d7b45271d1035fef4f51d19e87f5a729ce0423d1571eb0c9e145f3eba7726992a936596e7db5f6f8b2fbe174f98444ab32ef44f77566
|
7
|
+
data.tar.gz: 636f2e44bb5b141a5d3d670b4f8290009af0e1f1dc5d2aeea6b1e2c77687f99d6483f6d063fbd696e1f558ac679dc58915dd896103cc7baa8786ddba5793b3c7
|
data/Gemfile.lock
CHANGED
data/lib/nexpose.rb
CHANGED
@@ -57,25 +57,27 @@ require 'rex/mime'
|
|
57
57
|
require 'ipaddr'
|
58
58
|
require 'json'
|
59
59
|
require 'cgi'
|
60
|
+
require 'nexpose/api'
|
61
|
+
require 'nexpose/json_serializer'
|
60
62
|
require 'nexpose/error'
|
61
63
|
require 'nexpose/util'
|
62
64
|
require 'nexpose/alert'
|
63
65
|
require 'nexpose/ajax'
|
64
|
-
require 'nexpose/api'
|
65
66
|
require 'nexpose/api_request'
|
66
67
|
require 'nexpose/asset'
|
67
68
|
require 'nexpose/common'
|
68
69
|
require 'nexpose/console'
|
69
70
|
require 'nexpose/credential'
|
70
|
-
require 'nexpose/
|
71
|
+
require 'nexpose/site_credentials'
|
71
72
|
require 'nexpose/shared_credential'
|
73
|
+
require 'nexpose/web_credentials'
|
72
74
|
require 'nexpose/data_table'
|
73
75
|
require 'nexpose/device'
|
74
|
-
require 'nexpose/discovery'
|
75
|
-
require 'nexpose/discovery/filter'
|
76
76
|
require 'nexpose/engine'
|
77
77
|
require 'nexpose/external'
|
78
78
|
require 'nexpose/filter'
|
79
|
+
require 'nexpose/discovery'
|
80
|
+
require 'nexpose/discovery/filter'
|
79
81
|
require 'nexpose/global_settings'
|
80
82
|
require 'nexpose/group'
|
81
83
|
require 'nexpose/dag'
|
@@ -87,6 +89,7 @@ require 'nexpose/report_template'
|
|
87
89
|
require 'nexpose/role'
|
88
90
|
require 'nexpose/scan'
|
89
91
|
require 'nexpose/scan_template'
|
92
|
+
require 'nexpose/shared_secret'
|
90
93
|
require 'nexpose/silo'
|
91
94
|
require 'nexpose/silo_profile'
|
92
95
|
require 'nexpose/site'
|
@@ -100,6 +103,7 @@ require 'nexpose/vuln_exception'
|
|
100
103
|
require 'nexpose/connection'
|
101
104
|
require 'nexpose/maint'
|
102
105
|
require 'nexpose/version'
|
106
|
+
require 'nexpose/wait'
|
103
107
|
|
104
108
|
module Nexpose
|
105
109
|
|
data/lib/nexpose/ajax.rb
CHANGED
@@ -8,6 +8,9 @@ module Nexpose
|
|
8
8
|
module AJAX
|
9
9
|
module_function
|
10
10
|
|
11
|
+
API_PATTERN = %r{/api/(?<version>[\d\.]+)}
|
12
|
+
private_constant :API_PATTERN
|
13
|
+
|
11
14
|
# Content type strings acceptect by Nexpose.
|
12
15
|
#
|
13
16
|
module CONTENT_TYPE
|
@@ -157,15 +160,37 @@ module Nexpose
|
|
157
160
|
if response.header['location'] =~ /login/
|
158
161
|
raise Nexpose::AuthenticationFailed.new(response)
|
159
162
|
else
|
160
|
-
|
161
|
-
raise Nexpose::APIError.new(response, "#{req_type} request to #{request.path} failed. #{request.body}", response.code)
|
163
|
+
raise get_api_error(request, response)
|
162
164
|
end
|
163
165
|
else
|
164
|
-
|
165
|
-
raise Nexpose::APIError.new(response, "#{req_type} request to #{request.path} failed. #{request.body}", response.code)
|
166
|
+
raise get_api_error(request, response)
|
166
167
|
end
|
167
168
|
end
|
168
169
|
|
170
|
+
def get_api_error(request, response)
|
171
|
+
req_type = request.class.name.split('::').last.upcase
|
172
|
+
error_message = get_error_message(request, response)
|
173
|
+
Nexpose::APIError.new(response, "#{req_type} request to #{request.path} failed. #{error_message}", response.code)
|
174
|
+
end
|
175
|
+
|
176
|
+
# Get the version of the api target by request
|
177
|
+
#
|
178
|
+
# @param [HTTPRequest] request
|
179
|
+
def get_request_api_version(request)
|
180
|
+
matches = request.path.match(API_PATTERN)
|
181
|
+
matches[:version].to_f
|
182
|
+
rescue
|
183
|
+
0.0
|
184
|
+
end
|
185
|
+
|
186
|
+
# Get an error message from the response body if the request url api version
|
187
|
+
# is 2.1 or greater otherwise use the request body
|
188
|
+
def get_error_message(request, response)
|
189
|
+
version = get_request_api_version(request)
|
190
|
+
|
191
|
+
(version >= 2.1 && response.body) ? "response body: #{response.body}" : "request body: #{request.body}"
|
192
|
+
end
|
193
|
+
|
169
194
|
# Execute a block of code while presenving the preferences for any
|
170
195
|
# underlying table being accessed. Use this method when accessing data
|
171
196
|
# tables which are present in the UI to prevent existing row preferences
|
data/lib/nexpose/alert.rb
CHANGED
@@ -1,107 +1,29 @@
|
|
1
1
|
module Nexpose
|
2
2
|
|
3
|
-
# Alert parent object.
|
4
|
-
# The three alert types should be wrapped in this object to store data.
|
5
|
-
#
|
6
|
-
class Alert
|
7
|
-
|
8
|
-
# Name for this alert.
|
9
|
-
attr_accessor :name
|
10
|
-
# Whether or not this alert is currently active.
|
11
|
-
attr_accessor :enabled
|
12
|
-
# Send at most this many alerts per scan.
|
13
|
-
attr_accessor :max_alerts
|
14
|
-
# Send alerts based upon scan status.
|
15
|
-
attr_accessor :scan_filter
|
16
|
-
# Send alerts based upon vulnerability finding status.
|
17
|
-
attr_accessor :vuln_filter
|
18
|
-
# Alert type and its configuration. One of SMTPAlert, SyslogAlert, SNMPAlert
|
19
|
-
attr_accessor :type
|
20
|
-
|
21
|
-
def initialize(name, enabled = 1, max_alerts = -1)
|
22
|
-
@name, @enabled, @max_alerts = name, enabled, max_alerts
|
23
|
-
end
|
24
|
-
|
25
|
-
def as_xml
|
26
|
-
xml = REXML::Element.new('Alert')
|
27
|
-
xml.attributes['name'] = @name
|
28
|
-
xml.attributes['enabled'] = @enabled
|
29
|
-
xml.attributes['maxAlerts'] = @max_alerts
|
30
|
-
xml.add_element(scan_filter.as_xml)
|
31
|
-
xml.add_element(vuln_filter.as_xml)
|
32
|
-
xml.add_element(type.as_xml)
|
33
|
-
xml
|
34
|
-
end
|
35
|
-
|
36
|
-
def to_xml
|
37
|
-
as_xml.to_s
|
38
|
-
end
|
39
|
-
|
40
|
-
# Parse a response from a Nexpose console into a valid Alert object.
|
41
|
-
#
|
42
|
-
# @param [REXML::Document] rexml XML document to parse.
|
43
|
-
# @return [Alert] Alert object represented by the XML.
|
44
|
-
#
|
45
|
-
def self.parse(rexml)
|
46
|
-
name = rexml.attributes['name']
|
47
|
-
rexml.elements.each("//Alert[@name='#{name}']") do |xml|
|
48
|
-
alert = new(name,
|
49
|
-
xml.attributes['enabled'].to_i,
|
50
|
-
xml.attributes['maxAlerts'].to_i)
|
51
|
-
alert.scan_filter = ScanFilter.parse(REXML::XPath.first(xml, "//Alert[@name='#{name}']/scanFilter"))
|
52
|
-
alert.vuln_filter = VulnFilter.parse(REXML::XPath.first(xml, "//Alert[@name='#{name}']/vulnFilter"))
|
53
|
-
if (type = REXML::XPath.first(xml, "//Alert[@name='#{name}']/smtpAlert"))
|
54
|
-
alert.type = SMTPAlert.parse(type)
|
55
|
-
elsif (type = REXML::XPath.first(xml, "//Alert[@name='#{name}']/syslogAlert"))
|
56
|
-
alert.type = SyslogAlert.parse(type)
|
57
|
-
elsif (type = REXML::XPath.first(xml, "//Alert[@name='#{name}']/snmpAlert"))
|
58
|
-
alert.type = SNMPAlert.parse(type)
|
59
|
-
end
|
60
|
-
return alert
|
61
|
-
end
|
62
|
-
nil
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
3
|
# Scan filter for alerting.
|
67
4
|
# Set values to 1 to enable and 0 to disable.
|
68
|
-
#
|
69
5
|
class ScanFilter
|
6
|
+
include JsonSerializer
|
70
7
|
# Scan events to alert on.
|
71
8
|
attr_accessor :start, :stop, :fail, :resume, :pause
|
72
9
|
|
73
10
|
def initialize(start = 0, stop = 0, fail = 0, resume = 0, pause = 0)
|
74
|
-
@start, @stop, @fail, @resume, @pause = start, stop, fail, resume, pause
|
75
|
-
end
|
76
|
-
|
77
|
-
def as_xml
|
78
|
-
xml = REXML::Element.new('scanFilter')
|
79
|
-
xml.attributes['scanStart'] = @start
|
80
|
-
xml.attributes['scanStop'] = @stop
|
81
|
-
xml.attributes['scanFailed'] = @fail
|
82
|
-
xml.attributes['scanResumed'] = @resume
|
83
|
-
xml.attributes['scanPaused'] = @pause
|
84
|
-
xml
|
85
|
-
end
|
86
|
-
|
87
|
-
def to_xml
|
88
|
-
as_xml.to_s
|
11
|
+
@start, @stop, @fail, @resume, @pause = start.to_i, stop.to_i, fail.to_i, resume.to_i, pause.to_i
|
89
12
|
end
|
90
13
|
|
91
|
-
def self.
|
92
|
-
new(
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
14
|
+
def self.json_initializer(filter)
|
15
|
+
new(filter[:start] ? 1 : 0,
|
16
|
+
filter[:stop] ? 1 : 0,
|
17
|
+
filter[:failed] ? 1 : 0,
|
18
|
+
filter[:resume] ? 1 : 0,
|
19
|
+
filter[:pause] ? 1 : 0)
|
97
20
|
end
|
98
21
|
end
|
99
22
|
|
100
23
|
# Vulnerability filtering for alerting.
|
101
24
|
# Set values to 1 to enable and 0 to disable.
|
102
|
-
#
|
103
25
|
class VulnFilter
|
104
|
-
|
26
|
+
include JsonSerializer
|
105
27
|
# Only alert on vulnerability findings with a severity level greater than this level.
|
106
28
|
# Range is 0 to 10.
|
107
29
|
# Values in the UI correspond as follows:
|
@@ -114,138 +36,199 @@ module Nexpose
|
|
114
36
|
attr_accessor :confirmed, :unconfirmed, :potential
|
115
37
|
|
116
38
|
def initialize(severity = 1, confirmed = 1, unconfirmed = 1, potential = 1)
|
117
|
-
@severity, @confirmed
|
118
|
-
|
119
|
-
|
120
|
-
def as_xml
|
121
|
-
xml = REXML::Element.new('vulnFilter')
|
122
|
-
xml.attributes['severityThreshold'] = @severity
|
123
|
-
xml.attributes['confirmed'] = @confirmed
|
124
|
-
xml.attributes['unconfirmed'] = @unconfirmed
|
125
|
-
xml.attributes['potential'] = @potential
|
126
|
-
xml
|
127
|
-
end
|
128
|
-
|
129
|
-
def to_xml
|
130
|
-
as_xml.to_s
|
39
|
+
@severity, @confirmed = severity.to_i, confirmed.to_i
|
40
|
+
@unconfirmed, @potential = unconfirmed.to_i, potential.to_i
|
131
41
|
end
|
132
42
|
|
133
|
-
def self.
|
134
|
-
new(
|
135
|
-
|
136
|
-
|
137
|
-
|
43
|
+
def self.json_initializer(filter)
|
44
|
+
new(filter[:severity] ? 1 : 0,
|
45
|
+
filter[:unconfirmed] ? 1 : 0,
|
46
|
+
filter[:confirmed] ? 1 : 0,
|
47
|
+
filter[:potential] ? 1 : 0)
|
138
48
|
end
|
139
49
|
end
|
140
50
|
|
141
|
-
#
|
142
|
-
#
|
143
|
-
|
144
|
-
|
51
|
+
# Alert base behavior.
|
52
|
+
# The supported three alert types should have these properties and behaviors
|
53
|
+
module Alert
|
54
|
+
include JsonSerializer
|
55
|
+
extend TypedAccessor
|
145
56
|
|
146
|
-
#
|
57
|
+
# ID for this alert.
|
58
|
+
attr_accessor :id
|
59
|
+
# Name for this alert.
|
60
|
+
attr_accessor :name
|
61
|
+
# Whether or not this alert is currently active.
|
62
|
+
attr_accessor :enabled
|
63
|
+
# Send at most this many alerts per scan.
|
64
|
+
attr_accessor :max_alerts
|
65
|
+
# Alert type and its configuration. One of SMTPAlert, SyslogAlert, SNMPAlert
|
66
|
+
attr_accessor :alert_type
|
67
|
+
# Server target the alerts
|
147
68
|
attr_accessor :server
|
69
|
+
# Server port
|
70
|
+
attr_accessor :server_port
|
148
71
|
|
149
|
-
|
150
|
-
|
151
|
-
|
72
|
+
# Send alerts based upon scan status.
|
73
|
+
typed_accessor :scan_filter, ScanFilter
|
74
|
+
# Send alerts based upon vulnerability finding status.
|
75
|
+
typed_accessor :vuln_filter, VulnFilter
|
152
76
|
|
153
|
-
|
154
|
-
|
77
|
+
# load a particular site alert
|
78
|
+
def self.load(nsc, site_id, alert_id)
|
79
|
+
uri = "/api/2.1/site_configurations/#{site_id}/alerts/#{alert_id}"
|
80
|
+
resp = AJAX.get(nsc, uri, AJAX::CONTENT_TYPE::JSON)
|
81
|
+
|
82
|
+
unless resp.to_s == ''
|
83
|
+
data = JSON.parse(resp, symbolize_names: true)
|
84
|
+
json_initializer(data).deserialize(data)
|
85
|
+
end
|
155
86
|
end
|
156
87
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
xml
|
88
|
+
# load alerts from an array of hashes
|
89
|
+
def self.load_alerts(alerts)
|
90
|
+
alerts.map { |hash| json_initializer(hash).deserialize(hash) }
|
161
91
|
end
|
162
92
|
|
163
|
-
|
164
|
-
|
93
|
+
# load a list of alerts for a given site
|
94
|
+
def self.list_alerts(nsc, site_id)
|
95
|
+
uri = "/api/2.1/site_configurations/#{site_id}/alerts"
|
96
|
+
resp = AJAX.get(nsc, uri, AJAX::CONTENT_TYPE::JSON)
|
97
|
+
data = JSON.parse(resp, symbolize_names: true)
|
98
|
+
load_alerts(data) unless data.nil?
|
165
99
|
end
|
166
|
-
end
|
167
100
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
class SNMPAlert
|
101
|
+
def self.json_initializer(hash)
|
102
|
+
create(hash)
|
103
|
+
end
|
172
104
|
|
173
|
-
|
174
|
-
|
105
|
+
def to_h
|
106
|
+
to_hash(Hash.new)
|
107
|
+
end
|
175
108
|
|
176
|
-
|
177
|
-
|
109
|
+
def to_json
|
110
|
+
serialize
|
111
|
+
end
|
178
112
|
|
179
|
-
|
180
|
-
|
181
|
-
|
113
|
+
# delete an alert from the given site
|
114
|
+
def delete(nsc, site_id)
|
115
|
+
uri = "/api/2.1/site_configurations/#{site_id}/alerts/#{id}"
|
116
|
+
AJAX.delete(nsc, uri, AJAX::CONTENT_TYPE::JSON)
|
182
117
|
end
|
183
118
|
|
184
|
-
|
185
|
-
|
119
|
+
# save an alert for a given site
|
120
|
+
def save(nsc, site_id)
|
121
|
+
validate
|
122
|
+
uri = "/api/2.1/site_configurations/#{site_id}/alerts"
|
123
|
+
id = AJAX.put(nsc, uri, self.to_json, AJAX::CONTENT_TYPE::JSON)
|
124
|
+
@id = id.to_i
|
186
125
|
end
|
187
126
|
|
188
|
-
def
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
xml
|
127
|
+
def validate
|
128
|
+
raise ArgumentError.new('Name is a required attribute.') unless @name
|
129
|
+
raise ArgumentError.new('Scan filter is a required attribute.') unless @scan_filter
|
130
|
+
raise ArgumentError.new('Vuln filter is a required attribute.') unless @vuln_filter
|
193
131
|
end
|
194
132
|
|
195
|
-
|
196
|
-
|
133
|
+
private
|
134
|
+
|
135
|
+
def self.create(hash)
|
136
|
+
alert_type = hash[:alert_type]
|
137
|
+
raise 'An alert must have an alert type' if alert_type.nil?
|
138
|
+
raise 'Alert name cannot be empty.' if !hash.has_key?(:name) || hash[:name].to_s == ''
|
139
|
+
raise 'SNMP and Syslog alerts must have a server defined' if ['SNMP', 'Syslog'].include?(alert_type) && hash[:server].to_s == ''
|
140
|
+
|
141
|
+
case alert_type
|
142
|
+
when 'SMTP'
|
143
|
+
alert = SMTPAlert.new(hash[:name],
|
144
|
+
hash[:sender],
|
145
|
+
hash[:server],
|
146
|
+
hash[:recipients],
|
147
|
+
hash[:enabled],
|
148
|
+
hash[:max_alerts],
|
149
|
+
hash[:verbose])
|
150
|
+
when 'SNMP'
|
151
|
+
alert = SNMPAlert.new(hash[:name],
|
152
|
+
hash[:community],
|
153
|
+
hash[:server],
|
154
|
+
hash[:enabled],
|
155
|
+
hash[:max_alerts])
|
156
|
+
when 'Syslog'
|
157
|
+
alert = SyslogAlert.new(hash[:name],
|
158
|
+
hash[:server],
|
159
|
+
hash[:enabled],
|
160
|
+
hash[:max_alerts])
|
161
|
+
else
|
162
|
+
fail "Unknown alert type: #{alert_type}"
|
163
|
+
end
|
164
|
+
|
165
|
+
alert.scan_filter = ScanFilter.new
|
166
|
+
alert.vuln_filter = VulnFilter.new
|
167
|
+
alert
|
197
168
|
end
|
198
169
|
end
|
199
170
|
|
200
171
|
# SMTP (e-mail) Alert
|
201
|
-
# This class should only exist as an element of an Alert.
|
202
|
-
#
|
203
172
|
class SMTPAlert
|
173
|
+
include Alert
|
174
|
+
attr_accessor :recipients, :sender, :verbose
|
204
175
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
176
|
+
def initialize(name, sender, server, recipients, enabled = 1, max_alerts = -1, verbose = 0)
|
177
|
+
unless recipients.is_a?(Array) && recipients.length > 0
|
178
|
+
raise 'An SMTP alert must contain an array of recipient emails with at least 1 recipient'
|
179
|
+
end
|
180
|
+
recipients.each do |recipient|
|
181
|
+
unless recipient =~ /^.+@.+\..+$/
|
182
|
+
raise "Recipients must contain valid emails, #{recipient} has an invalid format"
|
183
|
+
end
|
184
|
+
end
|
213
185
|
|
214
|
-
|
186
|
+
@alert_type = 'SMTP'
|
187
|
+
@name = name
|
188
|
+
@enabled = enabled
|
189
|
+
@max_alerts = max_alerts
|
215
190
|
@sender = sender
|
216
191
|
@server = server
|
217
|
-
@
|
218
|
-
@recipients = []
|
192
|
+
@verbose = verbose
|
193
|
+
@recipients = recipients.nil? ? [] : recipients
|
219
194
|
end
|
220
195
|
|
221
|
-
|
222
|
-
def add_recipient(recipient)
|
196
|
+
def add_email_recipient(recipient)
|
223
197
|
@recipients << recipient
|
224
198
|
end
|
225
199
|
|
226
|
-
def
|
227
|
-
|
228
|
-
xml.attributes['sender'] = @sender
|
229
|
-
xml.attributes['server'] = @server
|
230
|
-
xml.attributes['limitText'] = @limit_text
|
231
|
-
recipients.each do |recpt|
|
232
|
-
elem = REXML::Element.new('recipient')
|
233
|
-
elem.text = recpt
|
234
|
-
xml.add_element(elem)
|
235
|
-
end
|
236
|
-
xml
|
200
|
+
def remove_email_recipient(recipient)
|
201
|
+
@recipients.delete(recipient)
|
237
202
|
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# SNMP Alert
|
206
|
+
class SNMPAlert
|
207
|
+
include Alert
|
208
|
+
attr_accessor :community
|
209
|
+
|
210
|
+
def initialize(name, community, server, enabled = 1, max_alerts = -1)
|
211
|
+
raise 'SNMP alerts must have a community defined.' if community.nil?
|
238
212
|
|
239
|
-
|
240
|
-
|
213
|
+
@alert_type = 'SNMP'
|
214
|
+
@name = name
|
215
|
+
@enabled = enabled
|
216
|
+
@max_alerts = max_alerts
|
217
|
+
@community = community
|
218
|
+
@server = server
|
241
219
|
end
|
220
|
+
end
|
242
221
|
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
222
|
+
# Syslog Alert
|
223
|
+
class SyslogAlert
|
224
|
+
include Alert
|
225
|
+
|
226
|
+
def initialize(name, server, enabled = 1, max_alerts = -1)
|
227
|
+
@alert_type = 'Syslog'
|
228
|
+
@name = name
|
229
|
+
@enabled = enabled
|
230
|
+
@max_alerts = max_alerts
|
231
|
+
@server = server
|
249
232
|
end
|
250
233
|
end
|
251
234
|
end
|