From a0a9c2140be71573f7462caef3b0d9c636be1c1e Mon Sep 17 00:00:00 2001 From: Luke Imhoff Date: Tue, 28 Oct 2014 11:21:35 -0500 Subject: [PATCH] Log leaked constants and fail rake spec on leak MSP-11130 Instead of printing the leaked constants to stderr, log them to `log/leaked-constants.log`. In task action for spec, read `log/leaked-constants.log`. If it exists, print each leaked constants (and it appropriate it's module full name) and then exit with 1. If the file does not exist, do nothing. --- Rakefile | 1 + lib/metasploit/framework/spec/constants.rb | 56 ++++++++++++++++++++-- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/Rakefile b/Rakefile index 232a7351b2..3f7b1076dd 100755 --- a/Rakefile +++ b/Rakefile @@ -9,3 +9,4 @@ require 'metasploit/framework/require' Metasploit::Framework::Require.optionally_active_record_railtie Metasploit::Framework::Application.load_tasks +Metasploit::Framework::Spec::Constants.define_task diff --git a/lib/metasploit/framework/spec/constants.rb b/lib/metasploit/framework/spec/constants.rb index 834b5d0831..3261812b85 100644 --- a/lib/metasploit/framework/spec/constants.rb +++ b/lib/metasploit/framework/spec/constants.rb @@ -3,6 +3,10 @@ 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 + # Regex parsing loaded module constants + LOADED_MODULE_CHILD_CONSTANT_REGEXP = /^Mod(?[0-9a-f]+)$/ + # Path to log holding leaked constants from last spec run. + LOG_PATHNAME = Pathname.new('log/leaked-constants.log') # The parent namespace constant that can have children added when loading modules. PARENT_CONSTANT = Msf::Modules @@ -11,9 +15,20 @@ module Metasploit::Framework::Spec::Constants unless @configured RSpec.configure do |config| config.after(:suite) do - ::Metasploit::Framework::Spec::Constants.each { |child_name| - $stderr.puts "#{child_name} not removed from #{PARENT_CONSTANT}" - } + count = 0 + + LOG_PATHNAME.open('w') do |f| + count = ::Metasploit::Framework::Spec::Constants.each { |child_name| + f.puts child_name + } + end + + if count > 0 + $stderr.puts "#{count} #{'constant'.pluralize(count)} leaked under #{PARENT_CONSTANT}. " \ + "See #{LOG_PATHNAME} for details." + else + LOG_PATHNAME.delete + end end end @@ -21,6 +36,41 @@ module Metasploit::Framework::Spec::Constants end end + # Adds action to `spec` task so that `rake spec` fails if `log/leaked-constants.log` exists after printing out the + # leaked constants. + # + # @return [void] + def self.define_task + Rake::Task.define_task(:spec) do + if LOG_PATHNAME.exist? + $stderr.puts "Leaked constants detected under #{PARENT_CONSTANT}:" + + LOG_PATHNAME.open do |f| + f.each_line do |line| + constant = line.strip + decoded = '' + + match = LOADED_MODULE_CHILD_CONSTANT_REGEXP.match(constant) + + 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 + decoded = " # #{potential_full_name}" + end + end + + $stderr.puts " #{constant}#{decoded}" + end + end + + exit 1 + end + end + end + # Yields each constant under {PARENT_CONSTANT}. # # @yield [child_name]