c8e4745d6d
MSP-11130 Fail `rake spec` if `Metasploit::Framework::Spec::Constants::Each.configure!`'s `before(:each)` does not clean all leaked constants and if should be removed so that it does not interefore with future specs.
99 lines
3.0 KiB
Ruby
99 lines
3.0 KiB
Ruby
require 'msf/core/modules'
|
|
|
|
# Monitor constants created by module loading to ensure that the loads in one example don't interfere with the
|
|
# assertions in another example.
|
|
module Metasploit::Framework::Spec::Constants
|
|
extend ActiveSupport::Autoload
|
|
|
|
autoload :Each
|
|
autoload :Suite
|
|
|
|
#
|
|
# CONSTANTS
|
|
#
|
|
|
|
# Regex parsing loaded module constants
|
|
LOADED_MODULE_CHILD_CONSTANT_REGEXP = /^Mod(?<unpacked_full_name>[0-9a-f]+)$/
|
|
# The parent namespace child_constant_name that can have children added when loading modules.
|
|
PARENT_CONSTANT = Msf::Modules
|
|
# Constant names under {PARENT_CONSTANT} that can persist between specs because they are part of the loader library
|
|
# and not dynamically loaded code
|
|
PERSISTENT_CHILD_CONSTANT_NAMES = %w{
|
|
Error
|
|
Loader
|
|
MetasploitClassCompatibilityError
|
|
Namespace
|
|
VersionCompatibilityError
|
|
}.map(&:to_sym)
|
|
|
|
# Cleans child constants from {PARENT_CONSTANT}.
|
|
#
|
|
# @return [true] if there were leaked constants that were cleaned.
|
|
# @return [false] if there were no leaked constants.
|
|
# @see each
|
|
def self.clean
|
|
count = each do |child_name|
|
|
PARENT_CONSTANT.send(:remove_const, child_name)
|
|
end
|
|
|
|
count != 0
|
|
end
|
|
|
|
# Adds actions to `spec` task so that `rake spec` fails if any of the following:
|
|
#
|
|
# # `log/leaked-constants.log` exists after printing out the leaked constants.
|
|
# # {Each.configured!} is unnecessary in `spec/spec_helper.rb` and should be removed.
|
|
#
|
|
# @return [void]
|
|
def self.define_task
|
|
Suite.define_task
|
|
# After Suite as Suite will kill for leaks before Each say it cleaned no leaks in case there are leaks in an
|
|
# `after(:all)` that {Each} won't catch in its `after(:each)` checks.
|
|
Each.define_task
|
|
end
|
|
|
|
# Yields each child_constant_name under {PARENT_CONSTANT}.
|
|
#
|
|
# @yield [child_name]
|
|
# @yieldparam child_name [Symbol] name of child_constant_name relative to {PARENT_CONSTANT}.
|
|
# @yieldreturn [void]
|
|
# @return [Integer] count
|
|
def self.each
|
|
inherit = false
|
|
count = 0
|
|
|
|
child_constant_names = PARENT_CONSTANT.constants(inherit)
|
|
|
|
child_constant_names.each do |child_constant_name|
|
|
unless PERSISTENT_CHILD_CONSTANT_NAMES.include? child_constant_name
|
|
count += 1
|
|
yield child_constant_name
|
|
end
|
|
end
|
|
|
|
count
|
|
end
|
|
|
|
# The module full name for `child_constant_name`
|
|
#
|
|
# @param child_constant_name [String] the name of a child constant_name under {PARENT_CONSTANT}.
|
|
# @return [String] full module name used to load `child_constant_name`.
|
|
# @return [nil] if `child_constant_name` does not correspond to a loaded module.
|
|
def self.full_name(child_constant_name)
|
|
full_name = nil
|
|
|
|
match = LOADED_MODULE_CHILD_CONSTANT_REGEXP.match(child_constant_name)
|
|
|
|
if match
|
|
potential_full_name = [match[:unpacked_full_name]].pack('H*')
|
|
|
|
module_type, _reference_name = potential_full_name.split('/', 2)
|
|
|
|
if Msf::MODULE_TYPES.include? module_type
|
|
full_name = potential_full_name
|
|
end
|
|
end
|
|
|
|
full_name
|
|
end
|
|
end |