Skip to content

Commit 8f5d222

Browse files
committed
Land rapid7#5156 - module ranking properly handles nil
2 parents edbf9b7 + 9aa0159 commit 8f5d222

File tree

2 files changed

+240
-11
lines changed

2 files changed

+240
-11
lines changed

lib/msf/core/module_set.rb

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -315,21 +315,40 @@ def each_module_list(ary, opts, &block)
315315
# @return [Array<Array<String, Class>>] Array of arrays where the inner array is a pair of the module reference name
316316
# and the module class.
317317
def rank_modules
318-
self.mod_ranked = self.sort { |a, b|
319-
a_name, a_mod = a
320-
b_name, b_mod = b
321-
322-
# Dynamically loads the module if needed
323-
a_mod = create(a_name) if a_mod == Msf::SymbolicModule
324-
b_mod = create(b_name) if b_mod == Msf::SymbolicModule
325-
326-
# Extract the ranking between the two modules
327-
a_rank = a_mod.const_defined?('Rank') ? a_mod.const_get('Rank') : Msf::NormalRanking
328-
b_rank = b_mod.const_defined?('Rank') ? b_mod.const_get('Rank') : Msf::NormalRanking
318+
self.mod_ranked = self.sort { |a_pair, b_pair|
319+
a_rank = module_rank(*a_pair)
320+
b_rank = module_rank(*b_pair)
329321

330322
# Compare their relevant rankings. Since we want highest to lowest,
331323
# we compare b_rank to a_rank in terms of higher/lower precedence
332324
b_rank <=> a_rank
333325
}
334326
end
327+
328+
# Retrieves the rank from a loaded, not-yet-loaded, or unloadable Metasploit Module.
329+
#
330+
# @param reference_name [String] The reference name of the Metasploit Module
331+
# @param metasploit_module_class [Class<Msf::Module>, Msf::SymbolicModule] The loaded `Class` for the Metasploit
332+
# Module, or {Msf::SymbolicModule} if the Metasploit Module is not loaded yet.
333+
# @return [Integer] an `Msf::*Ranking`. `Msf::ManualRanking` if `metasploit_module_class` is `nil` or
334+
# {Msf::SymbolicModule} and it could not be loaded by {#create}. Otherwise, the `Rank` constant of the
335+
# `metasploit_module_class` or {Msf::NormalRanking} if `metasploit_module_class` does not define `Rank`.
336+
def module_rank(reference_name, metasploit_module_class)
337+
if metasploit_module_class.nil?
338+
Msf::ManualRanking
339+
elsif metasploit_module_class == Msf::SymbolicModule
340+
# TODO don't create an instance just to get the Class.
341+
created_metasploit_module_instance = create(reference_name)
342+
343+
if created_metasploit_module_instance.nil?
344+
module_rank(reference_name, nil)
345+
else
346+
module_rank(reference_name, created_metasploit_module_instance.class)
347+
end
348+
elsif metasploit_module_class.const_defined? :Rank
349+
metasploit_module_class.const_get :Rank
350+
else
351+
Msf::NormalRanking
352+
end
353+
end
335354
end

spec/lib/msf/core/module_set_spec.rb

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
require 'spec_helper'
2+
3+
RSpec.describe Msf::ModuleSet do
4+
subject(:module_set) {
5+
described_class.new(module_type)
6+
}
7+
8+
let(:module_type) {
9+
FactoryGirl.generate :mdm_module_detail_mtype
10+
}
11+
12+
context '#rank_modules' do
13+
subject(:rank_modules) {
14+
module_set.send(:rank_modules)
15+
}
16+
17+
context 'with Msf::SymbolicModule' do
18+
before(:each) do
19+
module_set['a'] = Msf::SymbolicModule
20+
module_set['b'] = Msf::SymbolicModule
21+
module_set['c'] = Msf::SymbolicModule
22+
end
23+
24+
context 'create' do
25+
#
26+
# lets
27+
#
28+
29+
let(:b_class) {
30+
Class.new
31+
}
32+
33+
let(:c_class) {
34+
Class.new
35+
}
36+
37+
context 'returns nil' do
38+
before(:each) do
39+
hide_const('A::Rank')
40+
allow(module_set).to receive(:create).with('a').and_return(nil)
41+
42+
stub_const('B', b_class)
43+
stub_const('B::Rank', Msf::LowRanking)
44+
allow(module_set).to receive(:create).with('b').and_return(b_class.new)
45+
46+
stub_const('C', c_class)
47+
stub_const('C::Rank', Msf::AverageRanking)
48+
allow(module_set).to receive(:create).with('c').and_return(c_class.new)
49+
end
50+
51+
specify {
52+
expect {
53+
rank_modules
54+
}.not_to raise_error
55+
}
56+
57+
it 'is ranked as Manual' do
58+
expect(rank_modules).to eq(
59+
[
60+
['c', Msf::SymbolicModule],
61+
['b', Msf::SymbolicModule],
62+
['a', Msf::SymbolicModule]
63+
]
64+
)
65+
end
66+
end
67+
68+
context 'does not return nil' do
69+
#
70+
# lets
71+
#
72+
73+
let(:a_class) {
74+
Class.new
75+
}
76+
77+
#
78+
# Callbacks
79+
#
80+
81+
before(:each) do
82+
allow(module_set).to receive(:create).with('a').and_return(a_class.new)
83+
allow(module_set).to receive(:create).with('b').and_return(b_class.new)
84+
allow(module_set).to receive(:create).with('c').and_return(c_class.new)
85+
end
86+
87+
context 'with Rank' do
88+
before(:each) do
89+
stub_const('A', a_class)
90+
stub_const('A::Rank', Msf::LowRanking)
91+
92+
stub_const('B', b_class)
93+
stub_const('B::Rank', Msf::AverageRanking)
94+
95+
stub_const('C', c_class)
96+
stub_const('C::Rank', Msf::GoodRanking)
97+
end
98+
99+
it 'is ranked using Rank' do
100+
expect(rank_modules).to eq(
101+
[
102+
['c', Msf::SymbolicModule],
103+
['b', Msf::SymbolicModule],
104+
['a', Msf::SymbolicModule]
105+
]
106+
)
107+
end
108+
end
109+
110+
context 'without Rank' do
111+
before(:each) do
112+
stub_const('A', a_class)
113+
hide_const('A::Rank')
114+
115+
stub_const('B', b_class)
116+
stub_const('B::Rank', Msf::AverageRanking)
117+
118+
stub_const('C', c_class)
119+
stub_const('C::Rank', Msf::GoodRanking)
120+
end
121+
122+
it 'is ranked as Normal' do
123+
expect(rank_modules).to eq(
124+
[
125+
['c', Msf::SymbolicModule],
126+
['a', Msf::SymbolicModule],
127+
['b', Msf::SymbolicModule]
128+
]
129+
)
130+
end
131+
end
132+
end
133+
end
134+
end
135+
136+
context 'without Msf::SymbolicModule' do
137+
#
138+
# lets
139+
#
140+
141+
let(:a_class) {
142+
Class.new
143+
}
144+
145+
let(:b_class) {
146+
Class.new
147+
}
148+
149+
let(:c_class) {
150+
Class.new
151+
}
152+
153+
#
154+
# Callbacks
155+
#
156+
157+
before(:each) do
158+
module_set['a'] = a_class
159+
module_set['b'] = b_class
160+
module_set['c'] = c_class
161+
end
162+
163+
context 'with Rank' do
164+
before(:each) do
165+
stub_const('A', a_class)
166+
stub_const('A::Rank', Msf::LowRanking)
167+
168+
stub_const('B', b_class)
169+
stub_const('B::Rank', Msf::AverageRanking)
170+
171+
stub_const('C', c_class)
172+
stub_const('C::Rank', Msf::GoodRanking)
173+
end
174+
175+
it 'is ranked using Rank' do
176+
expect(rank_modules).to eq(
177+
[
178+
['c', c_class],
179+
['b', b_class],
180+
['a', a_class]
181+
]
182+
)
183+
end
184+
end
185+
186+
context 'without Rank' do
187+
before(:each) do
188+
stub_const('A', a_class)
189+
hide_const('A::Rank')
190+
191+
stub_const('B', b_class)
192+
stub_const('B::Rank', Msf::AverageRanking)
193+
194+
stub_const('C', c_class)
195+
stub_const('C::Rank', Msf::GoodRanking)
196+
end
197+
198+
it 'is ranked as Normal' do
199+
expect(rank_modules).to eq(
200+
[
201+
['c', c_class],
202+
['a', a_class],
203+
['b', b_class]
204+
]
205+
)
206+
end
207+
end
208+
end
209+
end
210+
end

0 commit comments

Comments
 (0)