Module: Metasploit::Framework::Spec::Constants::Each

Defined in:
lib/metasploit/framework/spec/constants/each.rb

Overview

Note:

This should only temporarily be used in `spec/spec_helper.rb` when `Metasploit::Framework::Spec::Constants::Suite.configure!` detects a leak. Permanently having `Metasploit::Framework::Spec::Constants::Each.configure!` can lead to false positives when modules are purposely loaded in a `before(:all)` and cleaned up in a `after(:all)`.

Fails example if it leaks module loading constants.

Constant Summary collapse

LOG_PATHNAME =

CONSTANTS

Pathname.new('log/metasploit/framework/spec/constants/each.log')

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.leaks_cleanedObject

Returns the value of attribute leaks_cleaned


19
20
21
# File 'lib/metasploit/framework/spec/constants/each.rb', line 19

def leaks_cleaned
  @leaks_cleaned
end

Class Method Details

.configure!void

This method returns an undefined value.

Configures after(:each) callback for RSpe to fail example if leaked constants.


33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/metasploit/framework/spec/constants/each.rb', line 33

def self.configure!
  unless @configured
    RSpec.configure do |config|
      config.before(:each) do |example|
        leaks_cleaned = Metasploit::Framework::Spec::Constants.clean

        if leaks_cleaned
          $stderr.puts "Cleaned leaked constants before #{example..full_description}"
        end

        # clean so that leaks from earlier example aren't attributed to this example
        Metasploit::Framework::Spec::Constants::Each.leaks_cleaned ||= leaks_cleaned
      end

      config.after(:each) do |example|
        child_names = Metasploit::Framework::Spec::Constants.to_enum(:each).to_a

        if child_names.length > 0
          lines = ['Leaked constants:']

          child_names.sort.each do |child_name|
            lines << "  #{child_name}"
          end

          lines << ''
          lines << "Add `include_context 'Metasploit::Framework::Spec::Constants cleaner'` to clean up constants from #{example..full_description}"

          message = lines.join("\n")

          # use caller metadata so that Jump to Source in the Rubymine RSpec running jumps to the example instead of
          # here
          fail RuntimeError, message, example.[:caller]
        end
      end

      config.after(:suite) do
        if Metasploit::Framework::Spec::Constants::Each.leaks_cleaned?
          if LOG_PATHNAME.exist?
            LOG_PATHNAME.delete
          end
        else
          LOG_PATHNAME.open('w') { |f|
            f.puts "No leaks were cleaned by `Metasploit::Framework::Spec::Constants::Each.configured!`.  Remove " \
                   "it from `spec/spec_helper.rb` so it does not interfere with contexts that persist loaded " \
                   "modules for entire context and clean up modules in `after(:all)`"
          }
        end
      end
    end

    @configured = true
  end
end

.configured?Boolean

Whether configure! was called

Returns:

  • (Boolean)

90
91
92
# File 'lib/metasploit/framework/spec/constants/each.rb', line 90

def self.configured?
  !!@configured
end

.define_taskvoid

This method returns an undefined value.

Adds action to `spec` task so that `rake spec` fails if configured! is unnecessary in `spec/spec_helper.rb` and should be removed


98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/metasploit/framework/spec/constants/each.rb', line 98

def self.define_task
  Rake::Task.define_task('metasploit:framework:spec:constant:each:clean') do
    if LOG_PATHNAME.exist?
      LOG_PATHNAME.delete
    end
  end

  Rake::Task.define_task(spec: 'metasploit:framework:spec:constant:each:clean')

  Rake::Task.define_task(:spec) do
    if LOG_PATHNAME.exist?
      LOG_PATHNAME.open { |f|
        f.each_line do |line|
          $stderr.write line
        end
      }

      exit(1)
    end
  end
end

.leaks_cleaned?true, false

Is Metasploit::Framework::Spec::Constants::Each.configure! still necessary or should it be removed?

Returns:

  • (true)

    if configure!'s `before(:each)` cleaned up leaked constants

  • (false)

    otherwise


26
27
28
# File 'lib/metasploit/framework/spec/constants/each.rb', line 26

def self.leaks_cleaned?
  !!@leaks_cleaned
end