CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/msf/core/module/alert.rb
Views: 11784
1
module Msf::Module::Alert
2
# This mixin provides a way for alert messages to be added to module classes
3
# and instances, retrieved from module classes and instances, and displayed
4
# from module instances. The two alert levels provided by this mixin are
5
# `:error` and `:warning`, though other levels or display methods can be
6
# added by subclasses/other mixins if desired by overriding {#alert_user}
7
# method (calling `super` as necessary), adding a proxy method like
8
# {ClassMethods#add_warning} that calls {ClassMethods#add_alert} or
9
# {#add_alert} and optionally a helper retrieval method like
10
# {ClassMethods#warnings}.
11
12
module ClassMethods
13
# Add a warning that will be provided to the user as early possible when
14
# using the module, either when they select it with the `use` command, when
15
# the module is about to start running, or when the module generates its
16
# output.
17
#
18
# @param msg [String] an optional warning message
19
# @param block [Proc] an optional block that will be executed in the
20
# context of the module instance at alert time to generate the warning
21
# message. If provided the msg parameter is ignored.
22
# @return [true, nil] whether or not the message was added to the list of
23
# warnings
24
def add_warning(msg = nil, &block)
25
add_alert(:warning, msg, &block)
26
end
27
28
# Add an error that will be provided to the user as early possible when
29
# using the module, either when they select it with the `use` command, when
30
# the module is about to start running, or when the module generates its
31
# output. Adding an error will cause {#is_usable} to return `false`.
32
#
33
# @param msg [String] an optional error message
34
# @param block [Proc] an optional block that will be executed in the
35
# context of the module instance at alert time to generate the error
36
# message. If provided the msg parameter is ignored.
37
# @return [true, nil] whether or not the message was added to the list of
38
# errors
39
def add_error(msg = nil, &block)
40
add_alert(:error, msg, &block)
41
end
42
43
# Add an info message that will be provided to the user as early possible when using
44
# this instance of a module, either when they select it with the `use`
45
# command, when the module is about to start running, or when the module
46
# generates its output.
47
#
48
# @param msg [String] an optional info message
49
# @param block [Proc] an optional block that will be executed in the
50
# context of the module instance at alert time to generate the
51
# message. If provided the msg parameter is ignored.
52
# @return [true, nil] whether or not the message was added to the list of
53
# info messages
54
def add_info(msg = nil, &block)
55
add_alert(:info, msg, &block)
56
end
57
58
# @return [Array<String, Proc>] a list of warning message strings, or
59
# blocks (see #get_alerts)
60
def warnings
61
get_alerts(:warning)
62
end
63
64
# @return [Array<String, Proc>] a list of error message strings, or
65
# blocks (see #get_alerts)
66
def errors
67
get_alerts(:error)
68
end
69
70
# @return [Array<String, Proc>] a list of info message strings, or
71
# blocks (see #get_alerts)
72
def infos
73
get_alerts(:info)
74
end
75
76
# @param level [Symbol] The alert level to return
77
# @return [Array<String, Proc>] A list of `level` alerts, either in string
78
# or block form. Blocks expect to be executed in the context of a fully
79
# initialized module instance and will return `nil` if the alert they are
80
# looking for does not apply or a string or array of strings, each
81
# representing an alert.
82
def get_alerts(level)
83
# Initialize here if needed, thanks to weird metaprogramming side-effects
84
self.alerts ||= {}
85
self.alerts[level] || []
86
end
87
88
# This method allows modules to tell the framework if they are usable
89
# on the system that they are being loaded on in a generic fashion.
90
# By default, all modules are indicated as being usable. An example of
91
# where this is useful is if the module depends on something external to
92
# ruby, such as a binary.
93
#
94
# This looks to have been abandoned at some point in the past, but it may
95
# be time to resurrect it.
96
#
97
# @return [true, false] whether or not the module has encountered any fatal
98
# errors thus far.
99
def usable?
100
errors.empty?
101
end
102
103
protected
104
105
attr_accessor :alerts
106
107
# Add a message (or block that generates messages) to a module. This
108
# message will be displayed once to the user by every instance of this
109
# module.
110
def add_alert(level, msg, &block)
111
self.alerts ||= {}
112
self.alerts[level] ||= []
113
if block
114
self.alerts[level] << block
115
true
116
elsif msg
117
self.alerts[level] << msg
118
true
119
end
120
end
121
end
122
123
# @nodoc
124
def self.included(base)
125
base.extend(ClassMethods)
126
end
127
128
# Add a warning that will be provided to the user as early possible when
129
# using this instance of a module, either when they select it with the `use`
130
# command, when the module is about to start running, or when the module
131
# generates its output.
132
#
133
# @param msg [String] an optional warning message
134
# @param block [Proc] an optional block that will be executed in the
135
# context of the module instance at alert time to generate the warning
136
# message. If provided the msg parameter is ignored.
137
# @return [true, nil] whether or not the message was added to the list of
138
# warnings
139
def add_warning(msg = nil, &block)
140
add_alert(:warning, msg, &block)
141
end
142
143
# Add a error that will be provided to the user as early possible when using
144
# this instance of a module, either when they select it with the `use`
145
# command, when the module is about to start running, or when the module
146
# generates its output. Adding an error will cause {#is_usable} to return
147
# `false`.
148
#
149
# @param msg [String] an optional error message
150
# @param block [Proc] an optional block that will be executed in the
151
# context of the module instance at alert time to generate the error
152
# message. If provided the msg parameter is ignored.
153
# @return [true, nil] whether or not the message was added to the list of
154
# errors
155
def add_error(msg = nil, &block)
156
add_alert(:error, msg, &block)
157
end
158
159
# Add an info message that will be provided to the user as early possible when using
160
# this instance of a module, either when they select it with the `use`
161
# command, when the module is about to start running, or when the module
162
# generates its output.
163
#
164
# @param msg [String] an optional info message
165
# @param block [Proc] an optional block that will be executed in the
166
# context of the module instance at alert time to generate the
167
# message. If provided the msg parameter is ignored.
168
# @return [true, nil] whether or not the message was added to the list of
169
# info messages
170
def add_info(msg = nil, &block)
171
add_alert(:info, msg, &block)
172
end
173
174
# This method allows modules to tell the framework if they are usable
175
# on the system that they are being loaded on in a generic fashion.
176
# By default, all modules are indicated as being usable. An example of
177
# where this is useful is if the module depends on something external to
178
# ruby, such as a binary.
179
#
180
# This looks to have been abandoned at some point in the past, but it may
181
# be time to resurrect it.
182
#
183
# @return [true, false] whether or not the module has encountered any fatal
184
# errors thus far.
185
def is_usable?
186
errors.empty?
187
end
188
189
# @return [Array<String>] a list of warning strings to show the user
190
def warnings
191
get_alerts(:warning)
192
end
193
194
# @return [Array<String>] a list of error strings to show the user
195
def errors
196
get_alerts(:error)
197
end
198
199
# @return [Array<String>] a list of info strings to show the user
200
def infos
201
get_alerts(:info)
202
end
203
204
# Similar to {ClassMethods#get_alerts}, but executes each registered block in
205
# the context of this module instance and returns a flattened list of strings.
206
# (see {ClassMethods#get_alerts})
207
# @param level [Symbol] The alert level to return
208
# @return [Array<String>]
209
def get_alerts(level)
210
self.alerts ||= {}
211
self.alerts[level] ||= []
212
all_alerts = self.class.get_alerts(level) + self.alerts[level]
213
all_alerts.map do |alert|
214
case alert
215
when Proc
216
self.instance_exec &alert
217
else
218
alert
219
end
220
end.flatten.compact
221
end
222
223
protected
224
225
attr_accessor :alerts, :you_have_been_warned
226
227
# Add an alert for _this instance_ of a module (see {ClassMethods#add_alert})
228
def add_alert(level, msg, &block)
229
self.alerts ||= {}
230
self.alerts[level] ||= []
231
if block
232
self.alerts[level] << block
233
true
234
elsif msg
235
self.alerts[level] << msg
236
true
237
end
238
end
239
240
# Display alerts with `print_warning` for warnings and `print_error` for
241
# errors. Alerts that have already been displayed by this module instance
242
# with this method will not be displayed again.
243
def alert_user
244
self.you_have_been_warned ||= {}
245
errors.each do |msg|
246
if msg && !self.you_have_been_warned[msg.hash]
247
without_prompt { print_error(msg, prefix: '') }
248
self.you_have_been_warned[msg.hash] = true
249
end
250
end
251
252
warnings.each do |msg|
253
if msg && !self.you_have_been_warned[msg.hash]
254
without_prompt { print_warning(msg, prefix: '') }
255
self.you_have_been_warned[msg.hash] = true
256
end
257
end
258
259
infos.each do |msg|
260
if msg && !self.you_have_been_warned[msg.hash]
261
# Make prefix an empty string to avoid adding clutter (timestamps, rhost, rport, etc.) to the output
262
without_prompt { print_status(msg, prefix: '') }
263
self.you_have_been_warned[msg.hash] = true
264
end
265
end
266
end
267
268
# Temporarily set the prompt mode to false to ensure that there are not additional lines printed
269
# A workaround for the prompting bug spotted in https://github.com/rapid7/metasploit-framework/pull/18761#issuecomment-1916645095
270
def without_prompt(&block)
271
# Some user outputs cannot have their prompting value configured, i.e. WebConsolePipe
272
return yield unless user_output.respond_to?(:prompting)
273
274
begin
275
if user_output
276
previous_prompting_value = user_output.prompting?
277
user_output.prompting(false)
278
end
279
280
yield
281
ensure
282
user_output.prompting(previous_prompting_value) if user_output
283
end
284
end
285
end
286
287