Files
metasploit-gs/spec/support/shared/examples/msf/module_manager/cache.rb
T
David Maloney c6656e4031 example_group and hook_scope conversions
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
2015-12-31 16:56:13 -06:00

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