Land #5156 - module ranking properly handles nil

bug/bundler_fix
Trevor Rosen 2015-04-21 14:40:01 -05:00
commit 8f5d222e53
No known key found for this signature in database
GPG Key ID: 255ADB7A642D3928
2 changed files with 240 additions and 11 deletions

View File

@ -315,21 +315,40 @@ class Msf::ModuleSet < Hash
# @return [Array<Array<String, Class>>] Array of arrays where the inner array is a pair of the module reference name
# and the module class.
def rank_modules
self.mod_ranked = self.sort { |a, b|
a_name, a_mod = a
b_name, b_mod = b
# Dynamically loads the module if needed
a_mod = create(a_name) if a_mod == Msf::SymbolicModule
b_mod = create(b_name) if b_mod == Msf::SymbolicModule
# Extract the ranking between the two modules
a_rank = a_mod.const_defined?('Rank') ? a_mod.const_get('Rank') : Msf::NormalRanking
b_rank = b_mod.const_defined?('Rank') ? b_mod.const_get('Rank') : Msf::NormalRanking
self.mod_ranked = self.sort { |a_pair, b_pair|
a_rank = module_rank(*a_pair)
b_rank = module_rank(*b_pair)
# Compare their relevant rankings. Since we want highest to lowest,
# we compare b_rank to a_rank in terms of higher/lower precedence
b_rank <=> a_rank
}
end
# Retrieves the rank from a loaded, not-yet-loaded, or unloadable Metasploit Module.
#
# @param reference_name [String] The reference name of the Metasploit Module
# @param metasploit_module_class [Class<Msf::Module>, Msf::SymbolicModule] The loaded `Class` for the Metasploit
# Module, or {Msf::SymbolicModule} if the Metasploit Module is not loaded yet.
# @return [Integer] an `Msf::*Ranking`. `Msf::ManualRanking` if `metasploit_module_class` is `nil` or
# {Msf::SymbolicModule} and it could not be loaded by {#create}. Otherwise, the `Rank` constant of the
# `metasploit_module_class` or {Msf::NormalRanking} if `metasploit_module_class` does not define `Rank`.
def module_rank(reference_name, metasploit_module_class)
if metasploit_module_class.nil?
Msf::ManualRanking
elsif metasploit_module_class == Msf::SymbolicModule
# TODO don't create an instance just to get the Class.
created_metasploit_module_instance = create(reference_name)
if created_metasploit_module_instance.nil?
module_rank(reference_name, nil)
else
module_rank(reference_name, created_metasploit_module_instance.class)
end
elsif metasploit_module_class.const_defined? :Rank
metasploit_module_class.const_get :Rank
else
Msf::NormalRanking
end
end
end

View File

@ -0,0 +1,210 @@
require 'spec_helper'
RSpec.describe Msf::ModuleSet do
subject(:module_set) {
described_class.new(module_type)
}
let(:module_type) {
FactoryGirl.generate :mdm_module_detail_mtype
}
context '#rank_modules' do
subject(:rank_modules) {
module_set.send(:rank_modules)
}
context 'with Msf::SymbolicModule' do
before(:each) do
module_set['a'] = Msf::SymbolicModule
module_set['b'] = Msf::SymbolicModule
module_set['c'] = Msf::SymbolicModule
end
context 'create' do
#
# lets
#
let(:b_class) {
Class.new
}
let(:c_class) {
Class.new
}
context 'returns nil' do
before(:each) do
hide_const('A::Rank')
allow(module_set).to receive(:create).with('a').and_return(nil)
stub_const('B', b_class)
stub_const('B::Rank', Msf::LowRanking)
allow(module_set).to receive(:create).with('b').and_return(b_class.new)
stub_const('C', c_class)
stub_const('C::Rank', Msf::AverageRanking)
allow(module_set).to receive(:create).with('c').and_return(c_class.new)
end
specify {
expect {
rank_modules
}.not_to raise_error
}
it 'is ranked as Manual' do
expect(rank_modules).to eq(
[
['c', Msf::SymbolicModule],
['b', Msf::SymbolicModule],
['a', Msf::SymbolicModule]
]
)
end
end
context 'does not return nil' do
#
# lets
#
let(:a_class) {
Class.new
}
#
# Callbacks
#
before(:each) do
allow(module_set).to receive(:create).with('a').and_return(a_class.new)
allow(module_set).to receive(:create).with('b').and_return(b_class.new)
allow(module_set).to receive(:create).with('c').and_return(c_class.new)
end
context 'with Rank' do
before(:each) do
stub_const('A', a_class)
stub_const('A::Rank', Msf::LowRanking)
stub_const('B', b_class)
stub_const('B::Rank', Msf::AverageRanking)
stub_const('C', c_class)
stub_const('C::Rank', Msf::GoodRanking)
end
it 'is ranked using Rank' do
expect(rank_modules).to eq(
[
['c', Msf::SymbolicModule],
['b', Msf::SymbolicModule],
['a', Msf::SymbolicModule]
]
)
end
end
context 'without Rank' do
before(:each) do
stub_const('A', a_class)
hide_const('A::Rank')
stub_const('B', b_class)
stub_const('B::Rank', Msf::AverageRanking)
stub_const('C', c_class)
stub_const('C::Rank', Msf::GoodRanking)
end
it 'is ranked as Normal' do
expect(rank_modules).to eq(
[
['c', Msf::SymbolicModule],
['a', Msf::SymbolicModule],
['b', Msf::SymbolicModule]
]
)
end
end
end
end
end
context 'without Msf::SymbolicModule' do
#
# lets
#
let(:a_class) {
Class.new
}
let(:b_class) {
Class.new
}
let(:c_class) {
Class.new
}
#
# Callbacks
#
before(:each) do
module_set['a'] = a_class
module_set['b'] = b_class
module_set['c'] = c_class
end
context 'with Rank' do
before(:each) do
stub_const('A', a_class)
stub_const('A::Rank', Msf::LowRanking)
stub_const('B', b_class)
stub_const('B::Rank', Msf::AverageRanking)
stub_const('C', c_class)
stub_const('C::Rank', Msf::GoodRanking)
end
it 'is ranked using Rank' do
expect(rank_modules).to eq(
[
['c', c_class],
['b', b_class],
['a', a_class]
]
)
end
end
context 'without Rank' do
before(:each) do
stub_const('A', a_class)
hide_const('A::Rank')
stub_const('B', b_class)
stub_const('B::Rank', Msf::AverageRanking)
stub_const('C', c_class)
stub_const('C::Rank', Msf::GoodRanking)
end
it 'is ranked as Normal' do
expect(rank_modules).to eq(
[
['c', c_class],
['a', a_class],
['b', b_class]
]
)
end
end
end
end
end