# # Specs # require 'spec_helper' # # Project # require 'metasploit/framework/database' require 'msf/core' describe Msf::DBManager do include_context 'Msf::DBManager' subject do db_manager end it_should_behave_like 'Msf::DBManager::Migration' it_should_behave_like 'Msf::DBManager::ImportMsfXml' context '#initialize_metasploit_data_models' do def initialize_metasploit_data_models db_manager.initialize_metasploit_data_models end it 'should not add duplicate paths to ActiveRecord::Migrator.migrations_paths' do initialize_metasploit_data_models expect { initialize_metasploit_data_models }.to_not change { ActiveRecord::Migrator.migrations_paths.length } ActiveRecord::Migrator.migrations_paths.uniq.should == ActiveRecord::Migrator.migrations_paths end end context '#purge_all_module_details' do def purge_all_module_details db_manager.purge_all_module_details end let(:migrated) do false end let(:module_detail_count) do 2 end let!(:module_details) do FactoryGirl.create_list( :mdm_module_detail, module_detail_count ) end before(:each) do db_manager.stub(:migrated => migrated) end context 'with migrated' do let(:migrated) do true end let(:modules_caching) do false end before(:each) do db_manager.stub(:modules_caching => modules_caching) end context 'with modules_caching' do let(:modules_caching) do true end it 'should not destroy Mdm::Module::Details' do expect { purge_all_module_details }.to_not change(Mdm::Module::Detail, :count) end end context 'without modules_caching' do it 'should create a connection' do # in purge_all_module_details # in after(:each) ActiveRecord::Base.connection_pool.should_receive(:with_connection).twice.and_call_original purge_all_module_details end it 'should destroy all Mdm::Module::Details' do expect { purge_all_module_details }.to change(Mdm::Module::Detail, :count).by(-module_detail_count) end end end context 'without migrated' do it 'should not destroy Mdm::Module::Details' do expect { purge_all_module_details }.to_not change(Mdm::Module::Detail, :count) end end end context '#report_session' do let(:options) do {} end subject(:report_session) do db_manager.report_session(options) end context 'with active' do let(:active) do true end it 'should create connection' do # 1st time from with_established_connection # 2nd time from report_session ActiveRecord::Base.connection_pool.should_receive(:with_connection).exactly(2).times report_session end context 'with :session' do before(:each) do options[:session] = session end context 'with Msf::Session' do let(:exploit_datastore) do Msf::ModuleDataStore.new(module_instance).tap do |datastore| datastore['ParentModule'] = parent_module_fullname remote_port = rand(2 ** 16 - 1) datastore['RPORT'] = remote_port end end let(:host) do FactoryGirl.create(:mdm_host, :workspace => session_workspace) end let(:module_instance) do name = 'multi/handler' double( 'Msf::Module', :fullname => "exploit/#{name}", :framework => framework, :name => name ) end let(:options_workspace) do FactoryGirl.create(:mdm_workspace) end let(:parent_module_fullname) do "exploit/#{parent_module_name}" end let(:parent_module_name) do 'windows/smb/ms08_067_netapi' end let(:parent_path) do Metasploit::Framework.root.join('modules').to_path end let(:session) do session_class.new.tap do |session| session.exploit_datastore = exploit_datastore session.info = 'Info' session.platform = 'Platform' session.session_host = host.address session.sid = rand(100) session.type = 'Session Type' session.via_exploit = 'exploit/multi/handler' session.via_payload = 'payload/single/windows/metsvc_bind_tcp' session.workspace = session_workspace.name end end let(:session_class) do Class.new do include Msf::Session attr_accessor :datastore attr_accessor :platform attr_accessor :type attr_accessor :via_exploit attr_accessor :via_payload end end let(:session_workspace) do FactoryGirl.create(:mdm_workspace) end before(:each) do reference_name = 'multi/handler' path = File.join(parent_path, 'exploits', reference_name) # fake cache data for exploit/multi/handler so it can be loaded framework.modules.send( :module_info_by_path=, { path => { :parent_path => parent_path, :reference_name => reference_name, :type => 'exploit', } } ) FactoryGirl.create( :mdm_module_detail, :fullname => parent_module_fullname, :name => parent_module_name ) end context 'with :workspace' do before(:each) do options[:workspace] = options_workspace end it 'should not find workspace from session' do db_manager.should_not_receive(:find_workspace) report_session end end context 'without :workspace' do it 'should find workspace from session' do db_manager.should_receive(:find_workspace).with(session.workspace).and_call_original report_session end it 'should pass session.workspace to #find_or_create_host' do db_manager.should_receive(:find_or_create_host).with( hash_including( :workspace => session_workspace ) ).and_return(host) report_session end end context 'with workspace from either :workspace or session' do it 'should pass normalized host from session as :host to #find_or_create_host' do normalized_host = double('Normalized Host') db_manager.stub(:normalize_host).with(session).and_return(normalized_host) # stub report_vuln so its use of find_or_create_host and normalize_host doesn't interfere. db_manager.stub(:report_vuln) db_manager.should_receive(:find_or_create_host).with( hash_including( :host => normalized_host ) ).and_return(host) report_session end context 'with session responds to arch' do let(:arch) do FactoryGirl.generate :mdm_host_arch end before(:each) do session.stub(:arch => arch) end it 'should pass :arch to #find_or_create_host' do db_manager.should_receive(:find_or_create_host).with( hash_including( :arch => arch ) ).and_call_original report_session end end context 'without session responds to arch' do it 'should not pass :arch to #find_or_create_host' do db_manager.should_receive(:find_or_create_host).with( hash_excluding( :arch ) ).and_call_original report_session end end it 'should create an Mdm::Session' do expect { report_session }.to change(Mdm::Session, :count).by(1) end it { should be_an Mdm::Session } it 'should set session.db_record to created Mdm::Session' do mdm_session = report_session session.db_record.should == mdm_session end context 'with session.via_exploit' do it 'should create session.via_exploit module' do framework.modules.should_receive(:create).with(session.via_exploit).and_call_original report_session end it 'should create Mdm::Vuln' do expect { report_session }.to change(Mdm::Vuln, :count).by(1) end context 'created Mdm::Vuln' do let(:mdm_session) do Mdm::Session.last end let(:rport) do nil end before(:each) do Timecop.freeze session.exploit_datastore['RPORT'] = rport report_session end after(:each) do Timecop.return end subject(:vuln) do Mdm::Vuln.last end its(:host) { should == Mdm::Host.last } its(:refs) { should == [] } its(:exploited_at) { should be_within(1.second).of(Time.now.utc) } context "with session.via_exploit 'exploit/multi/handler'" do context "with session.exploit_datastore['ParentModule']" do its(:info) { should == "Exploited by #{parent_module_fullname} to create Session #{mdm_session.id}" } its(:name) { should == parent_module_name } end end context "without session.via_exploit 'exploit/multi/handler'" do let(:reference_name) do 'windows/smb/ms08_067_netapi' end before(:each) do path = File.join( parent_path, 'exploits', "#{reference_name}.rb" ) type = 'exploit' # fake cache data for ParentModule so it can be loaded framework.modules.send( :module_info_by_path=, { path => { :parent_path => parent_path, :reference_name => reference_name, :type => type, } } ) session.via_exploit = "#{type}/#{reference_name}" end its(:info) { should == "Exploited by #{session.via_exploit} to create Session #{mdm_session.id}"} its(:name) { should == reference_name } end context 'with RPORT' do let(:rport) do # use service.port instead of having service use rport so # that service is forced to exist before call to # report_service, which happens right after using rport in # outer context's before(:each) service.port end let(:service) do FactoryGirl.create( :mdm_service, :host => host ) end its(:service) { should == service } end context 'without RPORT' do its(:service) { should be_nil } end end context 'created Mdm::ExploitAttempt' do let(:rport) do nil end before(:each) do Timecop.freeze session.exploit_datastore['RPORT'] = rport report_session end after(:each) do Timecop.return end subject(:exploit_attempt) do Mdm::ExploitAttempt.last end its(:attempted_at) { should be_within(1.second).of(Time.now.utc) } # @todo https://www.pivotaltracker.com/story/show/48362615 its(:session_id) { should == Mdm::Session.last.id } its(:exploited) { should == true } # @todo https://www.pivotaltracker.com/story/show/48362615 its(:vuln_id) { should == Mdm::Vuln.last.id } context "with session.via_exploit 'exploit/multi/handler'" do context "with session.datastore['ParentModule']" do its(:module) { should == parent_module_fullname } end end context "without session.via_exploit 'exploit/multi/handler'" do before(:each) do session.via_exploit = parent_module_fullname end its(:module) { should == session.via_exploit } end end end context 'returned Mdm::Session' do before(:each) do Timecop.freeze end after(:each) do Timecop.return end subject(:mdm_session) do report_session end # # Ensure session has attributes present so its on mdm_session are # not just comparing nils. # it 'should have session.info present' do session.info.should be_present end it 'should have session.sid present' do session.sid.should be_present end it 'should have session.platform present' do session.platform.should be_present end it 'should have session.type present' do session.type.should be_present end it 'should have session.via_exploit present' do session.via_exploit.should be_present end it 'should have session.via_payload present' do session.via_exploit.should be_present end its(:datastore) { should == session.exploit_datastore.to_h } its(:desc) { should == session.info } its(:host_id) { should == Mdm::Host.last.id } its(:last_seen) { should be_within(1.second).of(Time.now.utc) } its(:local_id) { should == session.sid } its(:opened_at) { should be_within(1.second).of(Time.now.utc) } its(:platform) { should == session.platform } its(:routes) { should == [] } its(:stype) { should == session.type } its(:via_payload) { should == session.via_payload } context "with session.via_exploit 'exploit/multi/handler'" do it "should have session.via_exploit of 'exploit/multi/handler'" do session.via_exploit.should == 'exploit/multi/handler' end context "with session.exploit_datastore['ParentModule']" do it "should have session.exploit_datastore['ParentModule']" do session.exploit_datastore['ParentModule'].should_not be_nil end its(:via_exploit) { should == parent_module_fullname } end end context "without session.via_exploit 'exploit/multi/handler'" do before(:each) do reference_name = 'windows/smb/ms08_067_netapi' path = File.join( parent_path, 'exploits', "#{reference_name}.rb" ) type = 'exploit' # fake cache data for ParentModule so it can be loaded framework.modules.send( :module_info_by_path=, { path => { :parent_path => parent_path, :reference_name => reference_name, :type => type, } } ) session.via_exploit = "#{type}/#{reference_name}" end it "should not have session.via_exploit of 'exploit/multi/handler'" do session.via_exploit.should_not == 'exploit/multi/handler' end its(:via_exploit) { should == session.via_exploit } end end end end context 'without Msf::Session' do let(:session) do double('Not a Msf::Session') end it 'should raise ArgumentError' do expect { report_session }.to raise_error(ArgumentError, "Invalid :session, expected Msf::Session") end end end context 'without :session' do context 'with :host' do before(:each) do options[:host] = host end context 'with Mdm::Host' do let(:host) do FactoryGirl.create(:mdm_host) end context 'created Mdm::Session' do let(:closed_at) do nil end let(:close_reason) do 'Closed because...' end let(:description) do 'Session Description' end let(:exploit_full_name) do 'exploit/windows/smb/ms08_067_netapi' end let(:last_seen) do nil end let(:opened_at) do Time.now.utc - 5.minutes end let(:payload_full_name) do 'payload/singles/windows/metsvc_reverse_tcp' end let(:platform) do 'Host Platform' end let(:routes) do nil end let(:session_type) do 'Session Type' end before(:each) do options[:closed_at] = closed_at options[:close_reason] = close_reason options[:desc] = description options[:last_seen] = last_seen options[:opened_at] = opened_at options[:platform] = platform options[:routes] = routes options[:stype] = session_type options[:via_payload] = payload_full_name options[:via_exploit] = exploit_full_name end subject(:mdm_session) do report_session end its(:close_reason) { should == close_reason } its(:desc) { should == description } its(:host) { should == host } its(:platform) { should == platform } its(:stype) { should == session_type } its(:via_exploit) { should == exploit_full_name } its(:via_payload) { should == payload_full_name } context 'with :last_seen' do let(:last_seen) do opened_at end its(:last_seen) { should == last_seen } end context 'with :closed_at' do let(:closed_at) do opened_at + 1.minute end its(:closed_at) { should == closed_at } end context 'without :closed_at' do its(:closed_at) { should == nil } end context 'without :last_seen' do context 'with :closed_at' do let(:closed_at) do opened_at + 1.minute end its(:last_seen) { should == closed_at } end context 'without :closed_at' do its(:last_seen) { should be_nil } end end context 'with :routes' do let(:routes) do FactoryGirl.build_list( :mdm_route, 1, :session => nil ) end its(:routes) { should == routes } end context 'without :routes' do its(:routes) { should == [] } end end end context 'without Mdm::Host' do let(:host) do '192.168.0.1' end it 'should raise ArgumentError' do expect { report_session }.to raise_error(ArgumentError, "Invalid :host, expected Host object") end end end context 'without :host' do it 'should raise ArgumentError' do expect { report_session }.to raise_error(ArgumentError) end end end end context 'without active' do let(:active) do false end it { should be_nil } it 'should not create a connection' do # 1st time for with_established_connection ActiveRecord::Base.connection_pool.should_receive(:with_connection).once report_session end end end context '#remove_module_details' do def remove_module_details db_manager.remove_module_details(mtype, refname) end let(:migrated) do false end let(:mtype) do FactoryGirl.generate :mdm_module_detail_mtype end let(:refname) do FactoryGirl.generate :mdm_module_detail_refname end let!(:module_detail) do FactoryGirl.create( :mdm_module_detail ) end before(:each) do db_manager.stub(:migrated => migrated) end context 'with migrated' do let(:migrated) do true end let!(:module_detail) do FactoryGirl.create(:mdm_module_detail) end context 'with matching Mdm::Module::Detail' do let(:mtype) do module_detail.mtype end let(:refname) do module_detail.refname end it 'should destroy Mdm::Module::Detail' do expect { remove_module_details }.to change(Mdm::Module::Detail, :count).by(-1) end end context 'without matching Mdm::Module::Detail' do it 'should not destroy Mdm::Module::Detail' do expect { remove_module_details }.to_not change(Mdm::Module::Detail, :count) end end end context 'without migrated' do it 'should not destroy Mdm::Module::Detail' do expect { remove_module_details }.to_not change(Mdm::Module::Detail, :count) end end end context '#search_modules' do subject(:search_modules) do db_manager.search_modules(search_string) end let(:module_details) do search_modules.to_a end context 'with app keyword' do let(:search_string) do "app:#{app}" end before(:each) do Mdm::Module::Detail::STANCES.each do |stance| FactoryGirl.create(:mdm_module_detail, :stance => stance) end end context 'with client' do let(:app) do 'client' end it "should match Mdm::Module::Detail#stance 'passive'" do module_details.count.should > 0 module_details.all? { |module_detail| module_detail.stance == 'passive' }.should be_true end end context 'with server' do let(:app) do 'server' end it "should match Mdm::Module::Detail#stance 'aggressive'" do module_details.count.should > 0 module_details.all? { |module_detail| module_detail.stance == 'aggressive' }.should be_true end end end context 'with author keyword' do let(:search_string) do # us inspect so strings with spaces are quoted correctly "author:#{author}" end let!(:module_authors) do FactoryGirl.create_list(:mdm_module_author, 2) end let(:target_module_author) do module_authors.first end context 'with Mdm::Module::Author#email' do let(:author) do target_module_author.email end it 'should match Mdm::Module::Author#email' do module_details.count.should > 0 module_details.all? { |module_detail| module_detail.authors.any? { |module_author| module_author.email == target_module_author.email } }.should be_true end end context 'with Mdm::Module::Author#name' do let(:author) do # use inspect to quote space in name target_module_author.name.inspect end it 'should match Mdm::Module::Author#name' do module_details.count.should > 0 module_details.all? { |module_detail| module_detail.authors.any? { |module_author| module_author.name == target_module_author.name } }.should be_true end end end it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Ref#name keyword', :bid it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Ref#name keyword', :cve it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Ref#name keyword', :edb context 'with name keyword' do let(:search_string) do "name:#{name}" end let!(:existing_module_details) do FactoryGirl.create_list(:mdm_module_detail, 2) end let(:target_module_detail) do existing_module_details.first end context 'with Mdm::Module::Detail#fullname' do let(:name) do target_module_detail.fullname end it 'should match Mdm::Module::Detail#fullname' do module_details.count.should > 0 module_details.all? { |module_detail| module_detail.fullname == target_module_detail.fullname }.should be_true end end context 'with Mdm::Module::Detail#name' do let(:name) do # use inspect so spaces are inside quotes target_module_detail.name.inspect end it 'should match Mdm::Module::Detail#name' do module_details.count.should > 0 module_details.all? { |module_detail| module_detail.name == target_module_detail.name }.should be_true end end end it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Platform#name or Mdm::Module::Target#name keyword', :os it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Ref#name keyword', :osvdb it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Platform#name or Mdm::Module::Target#name keyword', :platform context 'with ref keyword' do let(:ref) do FactoryGirl.generate :mdm_module_ref_name end let(:search_string) do # use inspect to quote spaces in string "ref:#{ref.inspect}" end let!(:module_ref) do FactoryGirl.create(:mdm_module_ref) end context 'with Mdm::Module::Ref#name' do let(:ref) do module_ref.name end it 'should match Mdm::Module::Ref#name' do module_details.count.should > 0 module_details.all? { |module_detail| module_detail.refs.any? { |module_ref| module_ref.name == ref } }.should be_true end end context 'without Mdm::Module::Ref#name' do it 'should not match Mdm::Module::Ref#name' do module_details.count.should == 0 end end end context 'with type keyword' do let(:type) do FactoryGirl.generate :mdm_module_detail_mtype end let(:search_string) do "type:#{type}" end let(:target_module_detail) do all_module_details.first end let!(:all_module_details) do FactoryGirl.create_list(:mdm_module_detail, 2) end context 'with Mdm::Module::Ref#name' do let(:type) do target_module_detail.mtype end it 'should match Mdm::Module::Detail#mtype' do module_details.count.should > 0 module_details.all? { |module_detail| module_detail.mtype == type }.should be_true end end context 'without Mdm::Module::Detail#mtype' do it 'should not match Mdm::Module::Detail#mtype' do module_details.count.should == 0 end end end context 'without keyword' do context 'with Mdm::Module::Action#name' do let(:search_string) do module_action.name end let!(:module_action) do FactoryGirl.create(:mdm_module_action) end it 'should match Mdm::Module::Action#name' do module_details.count.should > 0 module_details.all? { |module_detail| module_detail.actions.any? { |module_action| module_action.name == search_string } }.should be_true end end context 'with Mdm::Module::Arch#name' do let(:search_string) do module_arch.name end let!(:module_arch) do FactoryGirl.create(:mdm_module_arch) end it 'should match Mdm::Module::Arch#name' do module_details.count.should > 0 module_details.all? { |module_detail| module_detail.archs.any? { |module_arch| module_arch.name == search_string } }.should be_true end end context 'with Mdm::Module::Author#name' do let(:search_string) do module_author.name end let!(:module_author) do FactoryGirl.create(:mdm_module_author) end it 'should match Mdm::Module::Author#name' do module_details.count.should > 0 module_details.all? { |module_detail| module_detail.authors.any? { |module_author| module_author.name == search_string } }.should be_true end end context 'with Mdm::Module::Detail' do let(:target_module_detail) do all_module_details.first end let!(:all_module_details) do FactoryGirl.create_list(:mdm_module_detail, 3) end context 'with #description' do let(:search_string) do # use inspect to quote spaces in string target_module_detail.description.inspect end it 'should match Mdm::Module::Detail#description' do module_details.count.should == 1 module_details.all? { |module_detail| module_detail.description == target_module_detail.description }.should be_true end end context 'with #fullname' do let(:search_string) do target_module_detail.fullname end it 'should match Mdm::Module::Detail#fullname' do module_details.count.should == 1 module_details.all? { |module_detail| module_detail.fullname == search_string }.should be_true end end context 'with #name' do let(:search_string) do # use inspect to quote spaces in string target_module_detail.name.inspect end it 'should match Mdm::Module::Detail#name' do module_details.count.should == 1 module_details.all? { |module_detail| module_detail.name == target_module_detail.name }.should be_true end end end context 'with Mdm::Module::Platform#name' do let(:search_string) do module_platform.name end let!(:module_platform) do FactoryGirl.create(:mdm_module_platform) end it 'should match Mdm::Module::Platform#name' do module_details.count.should > 0 module_details.all? { |module_detail| module_detail.platforms.any? { |module_platform| module_platform.name == search_string } }.should be_true end end context 'with Mdm::Module::Ref#name' do let(:search_string) do module_ref.name end let!(:module_ref) do FactoryGirl.create(:mdm_module_ref) end it 'should match Mdm::Module::Ref#name' do module_details.count.should > 0 module_details.all? { |module_detail| module_detail.refs.any? { |module_ref| module_ref.name == search_string } }.should be_true end end context 'with Mdm::Module::Target#name' do let(:search_string) do module_target.name end let!(:module_target) do FactoryGirl.create(:mdm_module_target) end it 'should match Mdm::Module::Target#name' do module_details.count.should > 0 module_details.all? { |module_detail| module_detail.targets.any? { |module_target| module_target.name == search_string } }.should be_true end end end end context '#update_all_module_details' do def update_all_module_details db_manager.update_all_module_details end let(:migrated) do false end before(:each) do db_manager.stub(:migrated => migrated) end context 'with migrated' do let(:migrated) do true end let(:modules_caching) do true end before(:each) do db_manager.stub(:modules_caching => modules_caching) end context 'with modules_caching' do it 'should not update module details' do db_manager.should_not_receive(:update_module_details) update_all_module_details end end context 'without modules_caching' do let(:modules_caching) do false end it 'should create a connection' do ActiveRecord::Base.connection_pool.should_receive(:with_connection).twice.and_call_original update_all_module_details end it 'should set framework.cache_thread to current thread and then nil around connection' do framework.should_receive(:cache_thread=).with(Thread.current).ordered ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered framework.should_receive(:cache_thread=).with(nil).ordered update_all_module_details ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered.and_call_original end it 'should set modules_cached to false and then true around connection' do db_manager.should_receive(:modules_cached=).with(false).ordered ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered db_manager.should_receive(:modules_cached=).with(true).ordered update_all_module_details ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered.and_call_original end it 'should set modules_caching to true and then false around connection' do db_manager.should_receive(:modules_caching=).with(true).ordered ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered db_manager.should_receive(:modules_caching=).with(false).ordered update_all_module_details ActiveRecord::Base.connection_pool.should_receive(:with_connection).ordered.and_call_original end context 'with Mdm::Module::Details' do let(:module_pathname) do parent_pathname.join( 'exploits', "#{reference_name}.rb" ) end let(:modification_time) do module_pathname.mtime 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!(:module_detail) do # needs to reference a real module so that it can be loaded FactoryGirl.create( :mdm_module_detail, :file => module_pathname.to_path, :mtime => modification_time, :mtype => type, :ready => ready, :refname => reference_name ) end context '#ready' do context 'false' do let(:ready) do false end it_should_behave_like 'Msf::DBManager#update_all_module_details refresh' end context 'true' do let(:ready) do true end context 'with existing Mdm::Module::Detail#file' do context 'with same Mdm::Module::Detail#mtime and File.mtime' do it 'should not update module details' do db_manager.should_not_receive(:update_module_details) update_all_module_details end end context 'without same Mdm::Module::Detail#mtime and File.mtime' do let(:modification_time) do # +1 as rand can return 0 and the time must be different for # this context. super() - (rand(1.day) + 1) end it_should_behave_like 'Msf::DBManager#update_all_module_details refresh' end end # Emulates a module being removed or renamed context 'without existing Mdm::Module::Detail#file' do # have to compute modification manually since the # `module_pathname` refers to a non-existent file and # `module_pathname.mtime` would error. let(:modification_time) do Time.now.utc - 1.day end let(:module_pathname) do parent_pathname.join('exploits', 'deleted.rb') end it 'should not update module details' do db_manager.should_not_receive(:update_module_details) update_all_module_details end end end end end end end context 'without migrated' do it 'should not update module details' do db_manager.should_not_receive(:update_module_details) update_all_module_details end end end context '#update_module_details' do def update_module_details db_manager.update_module_details(module_instance) end let(:loader) do loader = framework.modules.send(:loaders).find { |loader| loader.loadable?(parent_path) } # Override load_error so that rspec will print it instead of going to framework log def loader.load_error(module_path, error) raise error end loader end let(:migrated) do false end let(:module_instance) do # make sure the module is loaded into the module_set loaded = loader.load_module(parent_path, module_type, module_reference_name) unless loaded module_path = loader.module_path(parent_path, type, module_reference_name) fail "#{description} failed to load: #{module_path}" end module_set.create(module_reference_name) end let(:module_set) do framework.modules.module_set(module_type) end let(:module_type) do 'exploit' end let(:module_reference_name) do 'windows/smb/ms08_067_netapi' end let(:parent_path) do parent_pathname.to_path end let(:parent_pathname) do Metasploit::Framework.root.join('modules') end let(:type_directory) do 'exploits' end before(:each) do db_manager.stub(:migrated => migrated) end context 'with migrated' do let(:migrated) do true end it 'should create connection' do ActiveRecord::Base.connection_pool.should_receive(:with_connection) ActiveRecord::Base.connection_pool.should_receive(:with_connection).and_call_original update_module_details end it 'should call module_to_details_hash to get Mdm::Module::Detail attributes and association attributes' do db_manager.should_receive(:module_to_details_hash).and_call_original update_module_details end it 'should create an Mdm::Module::Detail' do expect { update_module_details }.to change(Mdm::Module::Detail, :count).by(1) end context 'module_to_details_hash' do let(:module_to_details_hash) do { :mtype => module_type, :privileged => privileged, :rank => rank, :refname => module_reference_name, :stance => stance } end let(:privileged) do FactoryGirl.generate :mdm_module_detail_privileged end let(:rank) do FactoryGirl.generate :mdm_module_detail_rank end let(:stance) do FactoryGirl.generate :mdm_module_detail_stance end before(:each) do db_manager.stub( :module_to_details_hash ).with( module_instance ).and_return( module_to_details_hash ) end context 'Mdm::Module::Detail' do subject(:module_detail) do Mdm::Module::Detail.last end before(:each) do update_module_details end its(:mtype) { should == module_type } its(:privileged) { should == privileged } its(:rank) { should == rank } its(:ready) { should == true } its(:refname) { should == module_reference_name } its(:stance) { should == stance } end context 'with :bits' do let(:bits) do [] end before(:each) do module_to_details_hash[:bits] = bits end context 'with :action' do let(:name) do FactoryGirl.generate :mdm_module_action_name end let(:bits) do super() << [ :action, { :name => name } ] end it 'should create an Mdm::Module::Action' do expect { update_module_details }.to change(Mdm::Module::Action, :count).by(1) end context 'Mdm::Module::Action' do subject(:module_action) do module_detail.actions.last end let(:module_detail) do Mdm::Module::Detail.last end before(:each) do update_module_details end its(:name) { should == name } end end context 'with :arch' do let(:name) do FactoryGirl.generate :mdm_module_arch_name end let(:bits) do super() << [ :arch, { :name => name } ] end it 'should create an Mdm::Module::Arch' do expect { update_module_details }.to change(Mdm::Module::Arch, :count).by(1) end context 'Mdm::Module::Arch' do subject(:module_arch) do module_detail.archs.last end let(:module_detail) do Mdm::Module::Detail.last end before(:each) do update_module_details end its(:name) { should == name } end end context 'with :author' do let(:email) do FactoryGirl.generate :mdm_module_author_email end let(:name) do FactoryGirl.generate :mdm_module_author_name end let(:bits) do super() << [ :author, { :email => email, :name => name } ] end it 'should create an Mdm::Module::Author' do expect { update_module_details }.to change(Mdm::Module::Author, :count).by(1) end context 'Mdm::Module::Author' do subject(:module_author) do module_detail.authors.last end let(:module_detail) do Mdm::Module::Detail.last end before(:each) do update_module_details end its(:name) { should == name } its(:email) { should == email } end end context 'with :platform' do let(:bits) do super() << [ :platform, { :name => name } ] end let(:name) do FactoryGirl.generate :mdm_module_platform_name end it 'should create an Mdm::Module::Platform' do expect { update_module_details }.to change(Mdm::Module::Platform, :count).by(1) end context 'Mdm::Module::Platform' do subject(:module_platform) do module_detail.platforms.last end let(:module_detail) do Mdm::Module::Detail.last end before(:each) do update_module_details end its(:name) { should == name } end end context 'with :ref' do let(:bits) do super() << [ :ref, { :name => name } ] end let(:name) do FactoryGirl.generate :mdm_module_ref_name end it 'should create an Mdm::Module::Ref' do expect { update_module_details }.to change(Mdm::Module::Ref, :count).by(1) end context 'Mdm::Module::Ref' do subject(:module_ref) do module_detail.refs.last end let(:module_detail) do Mdm::Module::Detail.last end before(:each) do update_module_details end its(:name) { should == name } end end context 'with :target' do let(:bits) do super() << [ :target, { :index => index, :name => name } ] end let(:index) do FactoryGirl.generate :mdm_module_target_index end let(:name) do FactoryGirl.generate :mdm_module_target_name end it 'should create an Mdm::Module::Target' do expect { update_module_details }.to change(Mdm::Module::Target, :count).by(1) end context 'Mdm::Module::Target' do subject(:module_target) do module_detail.targets.last end let(:module_detail) do Mdm::Module::Detail.last end before(:each) do update_module_details end its(:index) { should == index } its(:name) { should == name } end end end end it_should_behave_like 'Msf::DBManager#update_module_details with module', :reference_name => 'admin/2wire/xslt_password_reset', :type => 'auxiliary' it_should_behave_like 'Msf::DBManager#update_module_details with module', :reference_name => 'generic/none', :type => 'encoder' it_should_behave_like 'Msf::DBManager#update_module_details with module', :reference_name => 'windows/smb/ms08_067_netapi', :type => 'exploit' it_should_behave_like 'Msf::DBManager#update_module_details with module', :reference_name => 'x64/simple', :type => 'nop' # @todo determine how to load a single payload to test payload type outside of msfconsole it_should_behave_like 'Msf::DBManager#update_module_details with module', :reference_name => 'windows/escalate/screen_unlock', :type => 'post' end context 'without migrated' do it 'should not create an Mdm::Module::Detail' do expect { update_module_details }.to_not change(Mdm::Module::Detail, :count) end end end end