Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/lib/msf/core/module/alert.rb
Views: 11784
module Msf::Module::Alert1# This mixin provides a way for alert messages to be added to module classes2# and instances, retrieved from module classes and instances, and displayed3# from module instances. The two alert levels provided by this mixin are4# `:error` and `:warning`, though other levels or display methods can be5# added by subclasses/other mixins if desired by overriding {#alert_user}6# method (calling `super` as necessary), adding a proxy method like7# {ClassMethods#add_warning} that calls {ClassMethods#add_alert} or8# {#add_alert} and optionally a helper retrieval method like9# {ClassMethods#warnings}.1011module ClassMethods12# Add a warning that will be provided to the user as early possible when13# using the module, either when they select it with the `use` command, when14# the module is about to start running, or when the module generates its15# output.16#17# @param msg [String] an optional warning message18# @param block [Proc] an optional block that will be executed in the19# context of the module instance at alert time to generate the warning20# message. If provided the msg parameter is ignored.21# @return [true, nil] whether or not the message was added to the list of22# warnings23def add_warning(msg = nil, &block)24add_alert(:warning, msg, &block)25end2627# Add an error that will be provided to the user as early possible when28# using the module, either when they select it with the `use` command, when29# the module is about to start running, or when the module generates its30# output. Adding an error will cause {#is_usable} to return `false`.31#32# @param msg [String] an optional error message33# @param block [Proc] an optional block that will be executed in the34# context of the module instance at alert time to generate the error35# message. If provided the msg parameter is ignored.36# @return [true, nil] whether or not the message was added to the list of37# errors38def add_error(msg = nil, &block)39add_alert(:error, msg, &block)40end4142# Add an info message that will be provided to the user as early possible when using43# this instance of a module, either when they select it with the `use`44# command, when the module is about to start running, or when the module45# generates its output.46#47# @param msg [String] an optional info message48# @param block [Proc] an optional block that will be executed in the49# context of the module instance at alert time to generate the50# message. If provided the msg parameter is ignored.51# @return [true, nil] whether or not the message was added to the list of52# info messages53def add_info(msg = nil, &block)54add_alert(:info, msg, &block)55end5657# @return [Array<String, Proc>] a list of warning message strings, or58# blocks (see #get_alerts)59def warnings60get_alerts(:warning)61end6263# @return [Array<String, Proc>] a list of error message strings, or64# blocks (see #get_alerts)65def errors66get_alerts(:error)67end6869# @return [Array<String, Proc>] a list of info message strings, or70# blocks (see #get_alerts)71def infos72get_alerts(:info)73end7475# @param level [Symbol] The alert level to return76# @return [Array<String, Proc>] A list of `level` alerts, either in string77# or block form. Blocks expect to be executed in the context of a fully78# initialized module instance and will return `nil` if the alert they are79# looking for does not apply or a string or array of strings, each80# representing an alert.81def get_alerts(level)82# Initialize here if needed, thanks to weird metaprogramming side-effects83self.alerts ||= {}84self.alerts[level] || []85end8687# This method allows modules to tell the framework if they are usable88# on the system that they are being loaded on in a generic fashion.89# By default, all modules are indicated as being usable. An example of90# where this is useful is if the module depends on something external to91# ruby, such as a binary.92#93# This looks to have been abandoned at some point in the past, but it may94# be time to resurrect it.95#96# @return [true, false] whether or not the module has encountered any fatal97# errors thus far.98def usable?99errors.empty?100end101102protected103104attr_accessor :alerts105106# Add a message (or block that generates messages) to a module. This107# message will be displayed once to the user by every instance of this108# module.109def add_alert(level, msg, &block)110self.alerts ||= {}111self.alerts[level] ||= []112if block113self.alerts[level] << block114true115elsif msg116self.alerts[level] << msg117true118end119end120end121122# @nodoc123def self.included(base)124base.extend(ClassMethods)125end126127# Add a warning that will be provided to the user as early possible when128# using this instance of a module, either when they select it with the `use`129# command, when the module is about to start running, or when the module130# generates its output.131#132# @param msg [String] an optional warning message133# @param block [Proc] an optional block that will be executed in the134# context of the module instance at alert time to generate the warning135# message. If provided the msg parameter is ignored.136# @return [true, nil] whether or not the message was added to the list of137# warnings138def add_warning(msg = nil, &block)139add_alert(:warning, msg, &block)140end141142# Add a error that will be provided to the user as early possible when using143# this instance of a module, either when they select it with the `use`144# command, when the module is about to start running, or when the module145# generates its output. Adding an error will cause {#is_usable} to return146# `false`.147#148# @param msg [String] an optional error message149# @param block [Proc] an optional block that will be executed in the150# context of the module instance at alert time to generate the error151# message. If provided the msg parameter is ignored.152# @return [true, nil] whether or not the message was added to the list of153# errors154def add_error(msg = nil, &block)155add_alert(:error, msg, &block)156end157158# Add an info message that will be provided to the user as early possible when using159# this instance of a module, either when they select it with the `use`160# command, when the module is about to start running, or when the module161# generates its output.162#163# @param msg [String] an optional info message164# @param block [Proc] an optional block that will be executed in the165# context of the module instance at alert time to generate the166# message. If provided the msg parameter is ignored.167# @return [true, nil] whether or not the message was added to the list of168# info messages169def add_info(msg = nil, &block)170add_alert(:info, msg, &block)171end172173# This method allows modules to tell the framework if they are usable174# on the system that they are being loaded on in a generic fashion.175# By default, all modules are indicated as being usable. An example of176# where this is useful is if the module depends on something external to177# ruby, such as a binary.178#179# This looks to have been abandoned at some point in the past, but it may180# be time to resurrect it.181#182# @return [true, false] whether or not the module has encountered any fatal183# errors thus far.184def is_usable?185errors.empty?186end187188# @return [Array<String>] a list of warning strings to show the user189def warnings190get_alerts(:warning)191end192193# @return [Array<String>] a list of error strings to show the user194def errors195get_alerts(:error)196end197198# @return [Array<String>] a list of info strings to show the user199def infos200get_alerts(:info)201end202203# Similar to {ClassMethods#get_alerts}, but executes each registered block in204# the context of this module instance and returns a flattened list of strings.205# (see {ClassMethods#get_alerts})206# @param level [Symbol] The alert level to return207# @return [Array<String>]208def get_alerts(level)209self.alerts ||= {}210self.alerts[level] ||= []211all_alerts = self.class.get_alerts(level) + self.alerts[level]212all_alerts.map do |alert|213case alert214when Proc215self.instance_exec &alert216else217alert218end219end.flatten.compact220end221222protected223224attr_accessor :alerts, :you_have_been_warned225226# Add an alert for _this instance_ of a module (see {ClassMethods#add_alert})227def add_alert(level, msg, &block)228self.alerts ||= {}229self.alerts[level] ||= []230if block231self.alerts[level] << block232true233elsif msg234self.alerts[level] << msg235true236end237end238239# Display alerts with `print_warning` for warnings and `print_error` for240# errors. Alerts that have already been displayed by this module instance241# with this method will not be displayed again.242def alert_user243self.you_have_been_warned ||= {}244errors.each do |msg|245if msg && !self.you_have_been_warned[msg.hash]246without_prompt { print_error(msg, prefix: '') }247self.you_have_been_warned[msg.hash] = true248end249end250251warnings.each do |msg|252if msg && !self.you_have_been_warned[msg.hash]253without_prompt { print_warning(msg, prefix: '') }254self.you_have_been_warned[msg.hash] = true255end256end257258infos.each do |msg|259if msg && !self.you_have_been_warned[msg.hash]260# Make prefix an empty string to avoid adding clutter (timestamps, rhost, rport, etc.) to the output261without_prompt { print_status(msg, prefix: '') }262self.you_have_been_warned[msg.hash] = true263end264end265end266267# Temporarily set the prompt mode to false to ensure that there are not additional lines printed268# A workaround for the prompting bug spotted in https://github.com/rapid7/metasploit-framework/pull/18761#issuecomment-1916645095269def without_prompt(&block)270# Some user outputs cannot have their prompting value configured, i.e. WebConsolePipe271return yield unless user_output.respond_to?(:prompting)272273begin274if user_output275previous_prompting_value = user_output.prompting?276user_output.prompting(false)277end278279yield280ensure281user_output.prompting(previous_prompting_value) if user_output282end283end284end285286287