c6656e4031
not strictly required, these conversions keep us up to date with latest rspec conventions and best practices which will prevent use from having to convert them when they become deprecated later
466 lines
12 KiB
Ruby
466 lines
12 KiB
Ruby
RSpec.shared_examples_for 'Msf::ModuleManager::Cache' do
|
|
let(:parent_path) do
|
|
parent_pathname.to_path
|
|
end
|
|
|
|
let(:parent_pathname) do
|
|
Metasploit::Framework.root.join('modules')
|
|
end
|
|
|
|
let(:reference_name) do
|
|
'windows/smb/ms08_067_netapi'
|
|
end
|
|
|
|
let(:type) do
|
|
'exploit'
|
|
end
|
|
|
|
let(:path) do
|
|
pathname.to_path
|
|
end
|
|
|
|
let(:pathname) do
|
|
parent_pathname.join(
|
|
'exploits',
|
|
"#{reference_name}.rb"
|
|
)
|
|
end
|
|
|
|
let(:pathname_modification_time) do
|
|
pathname.mtime
|
|
end
|
|
|
|
context '#cache_empty?' do
|
|
subject(:cache_empty?) do
|
|
module_manager.cache_empty?
|
|
end
|
|
|
|
before(:example) do
|
|
module_manager.send(:module_info_by_path=, module_info_by_path)
|
|
end
|
|
|
|
context 'with empty' do
|
|
let(:module_info_by_path) do
|
|
{}
|
|
end
|
|
|
|
it { is_expected.to be_truthy }
|
|
end
|
|
|
|
context 'without empty' do
|
|
let(:module_info_by_path) do
|
|
{
|
|
'path/to/module' => {}
|
|
}
|
|
end
|
|
|
|
it { is_expected.to be_falsey }
|
|
end
|
|
end
|
|
|
|
context '#cache_in_memory' do
|
|
def cache_in_memory
|
|
module_manager.cache_in_memory(
|
|
class_or_module,
|
|
:path => path,
|
|
:reference_name => reference_name,
|
|
:type => type
|
|
)
|
|
end
|
|
|
|
def module_info_by_path
|
|
module_manager.send(:module_info_by_path)
|
|
end
|
|
|
|
let(:class_or_module) do
|
|
double('Class<Msf::Module> or Module', :parent => namespace_module)
|
|
end
|
|
|
|
let(:namespace_module) do
|
|
double('Msf::Modules::Namespace', :parent_path => parent_path)
|
|
end
|
|
|
|
context 'with existing :path' do
|
|
it 'should update module_info_by_path' do
|
|
expect {
|
|
cache_in_memory
|
|
}.to change { module_info_by_path }
|
|
end
|
|
|
|
context 'module_info_by_path' do
|
|
subject(:module_info_by_path) do
|
|
module_manager.send(:module_info_by_path)
|
|
end
|
|
|
|
before(:example) do
|
|
cache_in_memory
|
|
end
|
|
|
|
it 'should have entry for path' do
|
|
expect(module_info_by_path[path]).to be_a Hash
|
|
end
|
|
|
|
context 'value' do
|
|
subject(:value) do
|
|
module_info_by_path[path]
|
|
end
|
|
|
|
it 'should have modification time of :path option for :modification_time' do
|
|
expect(value[:modification_time]).to eq pathname_modification_time
|
|
end
|
|
|
|
it 'should have parent path from namespace module for :parent_path' do
|
|
expect(value[:parent_path]).to eq namespace_module.parent_path
|
|
end
|
|
|
|
it 'should use :reference_name option' do
|
|
expect(value[:reference_name]).to eq reference_name
|
|
end
|
|
|
|
it 'should use :type option' do
|
|
expect(value[:type]).to eq type
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'without existing :path' do
|
|
let(:path) do
|
|
'non/existent/path'
|
|
end
|
|
|
|
it 'should not raise error' do
|
|
expect {
|
|
cache_in_memory
|
|
}.to_not raise_error
|
|
end
|
|
|
|
it 'should not update module_info_by_path' do
|
|
expect {
|
|
cache_in_memory
|
|
}.to_not change { module_info_by_path }
|
|
end
|
|
end
|
|
end
|
|
|
|
context '#load_cached_module' do
|
|
subject(:load_cached_module) do
|
|
module_manager.load_cached_module(type, reference_name)
|
|
end
|
|
|
|
before(:example) do
|
|
module_manager.send(:module_info_by_path=, module_info_by_path)
|
|
end
|
|
|
|
context 'with module info in cache' do
|
|
include_context 'Metasploit::Framework::Spec::Constants cleaner'
|
|
|
|
let(:module_info_by_path) do
|
|
{
|
|
'path/to/module' => {
|
|
:parent_path => parent_path,
|
|
:reference_name => reference_name,
|
|
:type => type
|
|
}
|
|
}
|
|
end
|
|
|
|
it 'should enumerate loaders until if it find the one where loadable?(parent_path) is true' do
|
|
module_manager.send(:loaders).each do |loader|
|
|
expect(loader).to receive(:loadable?).with(parent_path).and_call_original
|
|
end
|
|
|
|
load_cached_module
|
|
end
|
|
|
|
it 'should force load using #load_module on the loader' do
|
|
expect_any_instance_of(Msf::Modules::Loader::Directory).to receive(
|
|
:load_module
|
|
).with(
|
|
parent_path,
|
|
type,
|
|
reference_name,
|
|
:force => true
|
|
).and_call_original
|
|
|
|
load_cached_module
|
|
end
|
|
|
|
context 'return from load_module' do
|
|
before(:example) do
|
|
module_manager.send(:loaders).each do |loader|
|
|
expect(loader).to receive(:load_module).and_return(module_loaded)
|
|
end
|
|
end
|
|
|
|
context 'with false' do
|
|
let(:module_loaded) do
|
|
false
|
|
end
|
|
|
|
it { is_expected.to be_falsey }
|
|
end
|
|
|
|
context 'with true' do
|
|
let(:module_loaded) do
|
|
true
|
|
end
|
|
|
|
it { is_expected.to be_truthy }
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'without module info in cache' do
|
|
let(:module_info_by_path) do
|
|
{}
|
|
end
|
|
|
|
it { is_expected.to be_falsey }
|
|
end
|
|
end
|
|
|
|
context '#refresh_cache_from_module_files' do
|
|
before(:example) do
|
|
allow(module_manager).to receive(:framework_migrated?).and_return(framework_migrated?)
|
|
end
|
|
|
|
context 'with framework migrated' do
|
|
let(:framework_migrated?) do
|
|
true
|
|
end
|
|
|
|
context 'with module argument' do
|
|
def refresh_cache_from_module_files
|
|
module_manager.refresh_cache_from_module_files(module_class_or_instance)
|
|
end
|
|
|
|
let(:module_class_or_instance) do
|
|
Class.new(Msf::Module)
|
|
end
|
|
|
|
it 'should update database and then update in-memory cache from the database for the given module_class_or_instance' do
|
|
expect(framework.db).to receive(:update_module_details).with(module_class_or_instance).ordered
|
|
expect(module_manager).to receive(:refresh_cache_from_database).ordered
|
|
|
|
refresh_cache_from_module_files
|
|
end
|
|
end
|
|
|
|
context 'without module argument' do
|
|
def refresh_cache_from_module_files
|
|
module_manager.refresh_cache_from_module_files
|
|
end
|
|
|
|
it 'should update database and then update in-memory cache from the database for all modules' do
|
|
expect(framework.db).to receive(:update_all_module_details).ordered
|
|
expect(module_manager).to receive(:refresh_cache_from_database)
|
|
|
|
refresh_cache_from_module_files
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'without framework migrated' do
|
|
def refresh_cache_from_module_files
|
|
module_manager.refresh_cache_from_module_files
|
|
end
|
|
|
|
let(:framework_migrated?) do
|
|
false
|
|
end
|
|
|
|
it 'should not call Msf::DBManager#update_module_details' do
|
|
expect(framework.db).not_to receive(:update_module_details)
|
|
|
|
refresh_cache_from_module_files
|
|
end
|
|
|
|
it 'should not call Msf::DBManager#update_all_module_details' do
|
|
expect(framework.db).not_to receive(:update_all_module_details)
|
|
|
|
refresh_cache_from_module_files
|
|
end
|
|
|
|
it 'should not call #refresh_cache_from_database' do
|
|
expect(module_manager).not_to receive(:refresh_cache_from_database)
|
|
|
|
refresh_cache_from_module_files
|
|
end
|
|
end
|
|
end
|
|
|
|
context '#refresh_cache_from_database' do
|
|
def refresh_cache_from_database
|
|
module_manager.refresh_cache_from_database
|
|
end
|
|
|
|
it 'should call #module_info_by_path_from_database!' do
|
|
expect(module_manager).to receive(:module_info_by_path_from_database!)
|
|
|
|
refresh_cache_from_database
|
|
end
|
|
end
|
|
|
|
context '#framework_migrated?' do
|
|
subject(:framework_migrated?) do
|
|
module_manager.send(:framework_migrated?)
|
|
end
|
|
|
|
context 'with framework database' do
|
|
before(:example) do
|
|
expect(framework.db).to receive(:migrated).and_return(migrated)
|
|
end
|
|
|
|
context 'with migrated' do
|
|
let(:migrated) do
|
|
true
|
|
end
|
|
|
|
it { is_expected.to be_truthy }
|
|
end
|
|
|
|
context 'without migrated' do
|
|
let(:migrated) do
|
|
false
|
|
end
|
|
|
|
it { is_expected.to be_falsey }
|
|
end
|
|
end
|
|
|
|
context 'without framework database' do
|
|
before(:example) do
|
|
expect(framework).to receive(:db).and_return(nil)
|
|
end
|
|
|
|
it { is_expected.to be_falsey }
|
|
end
|
|
end
|
|
|
|
context '#module_info_by_path' do
|
|
it 'should have protected method module_info_by_path' do
|
|
expect(subject.respond_to?(:module_info_by_path, true)).to be_truthy
|
|
end
|
|
end
|
|
|
|
context '#module_info_by_path=' do
|
|
it 'should have protected method module_info_by_path=' do
|
|
expect(subject.respond_to?(:module_info_by_path=, true)).to be_truthy
|
|
end
|
|
end
|
|
|
|
context '#module_info_by_path_from_database!' do
|
|
def module_info_by_path
|
|
module_manager.send(:module_info_by_path)
|
|
end
|
|
|
|
def module_info_by_path_from_database!
|
|
module_manager.send(:module_info_by_path_from_database!)
|
|
end
|
|
|
|
before(:example) do
|
|
allow(module_manager).to receive(:framework_migrated?).and_return(framework_migrated?)
|
|
end
|
|
|
|
context 'with framework migrated' do
|
|
let(:framework_migrated?) do
|
|
true
|
|
end
|
|
|
|
it 'should use ActiveRecord::Batches#find_each to enumerate Mdm::Module::Details in batches' do
|
|
expect(Mdm::Module::Detail).to receive(:find_each)
|
|
|
|
module_info_by_path_from_database!
|
|
end
|
|
|
|
context 'with database cache' do
|
|
#
|
|
# Let!s (let + before(:each))
|
|
#
|
|
|
|
let!(:mdm_module_detail) do
|
|
FactoryGirl.create(:mdm_module_detail,
|
|
:file => path,
|
|
:mtype => type,
|
|
:mtime => pathname.mtime,
|
|
:refname => reference_name
|
|
)
|
|
end
|
|
|
|
it 'should create cache entry for path' do
|
|
module_info_by_path_from_database!
|
|
|
|
expect(module_info_by_path).to have_key(path)
|
|
end
|
|
|
|
it 'should use Msf::Modules::Loader::Base.typed_path to derive parent_path' do
|
|
expect(Msf::Modules::Loader::Base).to receive(:typed_path).with(type, reference_name).at_least(:once).and_call_original
|
|
|
|
module_info_by_path_from_database!
|
|
end
|
|
|
|
context 'cache entry' do
|
|
subject(:cache_entry) do
|
|
module_info_by_path[path]
|
|
end
|
|
|
|
before(:example) do
|
|
module_info_by_path_from_database!
|
|
end
|
|
|
|
it { expect(subject[:modification_time]).to be_within(1.second).of(pathname_modification_time) }
|
|
it { expect(subject[:parent_path]).to eq(parent_path) }
|
|
it { expect(subject[:reference_name]).to eq(reference_name) }
|
|
it { expect(subject[:type]).to eq(type) }
|
|
end
|
|
|
|
context 'typed module set' do
|
|
let(:typed_module_set) do
|
|
module_manager.module_set(type)
|
|
end
|
|
|
|
context 'with reference_name' do
|
|
before(:example) do
|
|
typed_module_set[reference_name] = double('Msf::Module')
|
|
end
|
|
|
|
it 'should not change reference_name value' do
|
|
expect {
|
|
module_info_by_path_from_database!
|
|
}.to_not change {
|
|
typed_module_set[reference_name]
|
|
}
|
|
end
|
|
end
|
|
|
|
context 'without reference_name' do
|
|
it 'should set reference_name value to Msf::SymbolicModule' do
|
|
module_info_by_path_from_database!
|
|
|
|
# have to use fetch because [] will trigger de-symbolization and
|
|
# instantiation.
|
|
expect(typed_module_set.fetch(reference_name)).to eq Msf::SymbolicModule
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'without framework migrated' do
|
|
let(:framework_migrated?) do
|
|
false
|
|
end
|
|
|
|
it 'should reset #module_info_by_path' do
|
|
# pre-fill module_info_by_path so change can be detected
|
|
module_manager.send(:module_info_by_path=, double('In-memory Cache'))
|
|
|
|
module_info_by_path_from_database!
|
|
|
|
expect(module_info_by_path).to be_empty
|
|
end
|
|
end
|
|
end
|
|
end
|