2013-04-01 21:06:40 +00:00
|
|
|
#
|
|
|
|
# Specs
|
|
|
|
#
|
|
|
|
|
|
|
|
require 'spec_helper'
|
|
|
|
|
|
|
|
#
|
|
|
|
# Project
|
|
|
|
#
|
|
|
|
|
|
|
|
require 'metasploit/framework/database'
|
|
|
|
require 'msf/core'
|
|
|
|
|
|
|
|
describe Msf::DBManager do
|
2013-04-20 21:44:42 +00:00
|
|
|
include_context 'Msf::DBManager'
|
2013-04-01 21:06:40 +00:00
|
|
|
|
2013-04-20 21:44:42 +00:00
|
|
|
subject do
|
|
|
|
db_manager
|
2013-04-01 21:06:40 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
it_should_behave_like 'Msf::DBManager::ImportMsfXml'
|
2013-04-19 15:11:33 +00:00
|
|
|
|
2013-04-25 14:46:53 +00:00
|
|
|
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
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
it 'should not destroy Mdm::Module::Details' do
|
2013-04-25 14:46:53 +00:00
|
|
|
expect {
|
|
|
|
purge_all_module_details
|
2013-04-26 18:14:38 +00:00
|
|
|
}.to_not change(Mdm::Module::Detail, :count)
|
2013-04-25 14:46:53 +00:00
|
|
|
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
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
it 'should destroy all Mdm::Module::Details' do
|
2013-04-25 14:46:53 +00:00
|
|
|
expect {
|
|
|
|
purge_all_module_details
|
2013-04-26 18:14:38 +00:00
|
|
|
}.to change(Mdm::Module::Detail, :count).by(-module_detail_count)
|
2013-04-25 14:46:53 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'without migrated' do
|
2013-04-26 18:14:38 +00:00
|
|
|
it 'should not destroy Mdm::Module::Details' do
|
2013-04-25 14:46:53 +00:00
|
|
|
expect {
|
|
|
|
purge_all_module_details
|
2013-04-26 18:14:38 +00:00
|
|
|
}.to_not change(Mdm::Module::Detail, :count)
|
2013-04-25 14:46:53 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-04-19 15:11:33 +00:00
|
|
|
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'
|
|
|
|
|
|
|
|
mock(
|
|
|
|
'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 = mock('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
|
2013-05-09 18:25:26 +00:00
|
|
|
FactoryGirl.generate :mdm_host_arch
|
2013-04-19 15:11:33 +00:00
|
|
|
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 == [] }
|
2013-04-19 17:07:12 +00:00
|
|
|
its(:exploited_at) { should be_within(1.second).of(Time.now.utc) }
|
2013-04-19 15:11:33 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2013-04-19 17:07:12 +00:00
|
|
|
its(:attempted_at) { should be_within(1.second).of(Time.now.utc) }
|
2013-04-19 15:11:33 +00:00
|
|
|
# @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 }
|
2013-04-19 17:07:12 +00:00
|
|
|
its(:last_seen) { should be_within(1.second).of(Time.now.utc) }
|
2013-04-19 15:11:33 +00:00
|
|
|
its(:local_id) { should == session.sid }
|
2013-04-19 17:07:12 +00:00
|
|
|
its(:opened_at) { should be_within(1.second).of(Time.now.utc) }
|
2013-04-19 15:11:33 +00:00
|
|
|
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
|
|
|
|
mock('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
|
2013-04-25 14:46:53 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
context 'with matching Mdm::Module::Detail' do
|
2013-04-25 14:46:53 +00:00
|
|
|
let(:mtype) do
|
|
|
|
module_detail.mtype
|
|
|
|
end
|
|
|
|
|
|
|
|
let(:refname) do
|
|
|
|
module_detail.refname
|
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
it 'should destroy Mdm::Module::Detail' do
|
2013-04-25 14:46:53 +00:00
|
|
|
expect {
|
|
|
|
remove_module_details
|
2013-04-26 18:14:38 +00:00
|
|
|
}.to change(Mdm::Module::Detail, :count).by(-1)
|
2013-04-25 14:46:53 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
context 'without matching Mdm::Module::Detail' do
|
|
|
|
it 'should not destroy Mdm::Module::Detail' do
|
2013-04-25 14:46:53 +00:00
|
|
|
expect {
|
|
|
|
remove_module_details
|
2013-04-26 18:14:38 +00:00
|
|
|
}.to_not change(Mdm::Module::Detail, :count)
|
2013-04-25 14:46:53 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'without migrated' do
|
2013-04-26 18:14:38 +00:00
|
|
|
it 'should not destroy Mdm::Module::Detail' do
|
2013-04-25 14:46:53 +00:00
|
|
|
expect {
|
|
|
|
remove_module_details
|
2013-04-26 18:14:38 +00:00
|
|
|
}.to_not change(Mdm::Module::Detail, :count)
|
2013-04-25 14:46:53 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context '#search_modules' do
|
2013-04-26 18:14:38 +00:00
|
|
|
subject(:search_modules) do
|
|
|
|
db_manager.search_modules(search_string)
|
|
|
|
end
|
|
|
|
|
|
|
|
let(:module_details) do
|
|
|
|
search_modules.to_a
|
2013-04-25 14:46:53 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
context 'with app keyword' do
|
|
|
|
let(:search_string) do
|
|
|
|
"app:#{app}"
|
|
|
|
end
|
|
|
|
|
|
|
|
before(:each) do
|
2013-05-09 18:25:26 +00:00
|
|
|
Mdm::Module::Detail::STANCES.each do |stance|
|
2013-04-25 14:46:53 +00:00
|
|
|
FactoryGirl.create(:mdm_module_detail, :stance => stance)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with client' do
|
|
|
|
let(:app) do
|
|
|
|
'client'
|
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
it "should match Mdm::Module::Detail#stance 'passive'" do
|
2013-04-25 14:46:53 +00:00
|
|
|
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
|
|
|
|
|
2013-05-09 18:25:26 +00:00
|
|
|
it "should match Mdm::Module::Detail#stance 'aggressive'" do
|
2013-04-25 14:46:53 +00:00
|
|
|
module_details.count.should > 0
|
|
|
|
|
|
|
|
module_details.all? { |module_detail|
|
2013-05-09 18:25:26 +00:00
|
|
|
module_detail.stance == 'aggressive'
|
2013-04-25 14:46:53 +00:00
|
|
|
}.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
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
context 'with Mdm::Module::Author#email' do
|
2013-04-25 14:46:53 +00:00
|
|
|
let(:author) do
|
|
|
|
target_module_author.email
|
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
it 'should match Mdm::Module::Author#email' do
|
2013-04-25 14:46:53 +00:00
|
|
|
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
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
context 'with Mdm::Module::Author#name' do
|
2013-04-25 14:46:53 +00:00
|
|
|
let(:author) do
|
|
|
|
# use inspect to quote space in name
|
|
|
|
target_module_author.name.inspect
|
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
it 'should match Mdm::Module::Author#name' do
|
2013-04-25 14:46:53 +00:00
|
|
|
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
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
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
|
2013-04-25 14:46:53 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
context 'with Mdm::Module::Detail#fullname' do
|
2013-04-25 14:46:53 +00:00
|
|
|
let(:name) do
|
|
|
|
target_module_detail.fullname
|
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
it 'should match Mdm::Module::Detail#fullname' do
|
2013-04-25 14:46:53 +00:00
|
|
|
module_details.count.should > 0
|
|
|
|
|
|
|
|
module_details.all? { |module_detail|
|
|
|
|
module_detail.fullname == target_module_detail.fullname
|
|
|
|
}.should be_true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
context 'with Mdm::Module::Detail#name' do
|
2013-04-25 14:46:53 +00:00
|
|
|
let(:name) do
|
2013-04-26 18:14:38 +00:00
|
|
|
# use inspect so spaces are inside quotes
|
|
|
|
target_module_detail.name.inspect
|
2013-04-25 14:46:53 +00:00
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
it 'should match Mdm::Module::Detail#name' do
|
2013-04-25 14:46:53 +00:00
|
|
|
module_details.count.should > 0
|
|
|
|
|
|
|
|
module_details.all? { |module_detail|
|
|
|
|
module_detail.name == target_module_detail.name
|
|
|
|
}.should be_true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Platform#name or Mdm::Module::Target#name keyword', :os
|
2013-04-25 14:46:53 +00:00
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Ref#name keyword', :osvdb
|
2013-04-25 14:46:53 +00:00
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
it_should_behave_like 'Msf::DBManager#search_modules Mdm::Module::Platform#name or Mdm::Module::Target#name keyword', :platform
|
2013-04-25 14:46:53 +00:00
|
|
|
|
|
|
|
context 'with ref keyword' do
|
|
|
|
let(:ref) do
|
|
|
|
FactoryGirl.generate :mdm_module_ref_name
|
|
|
|
end
|
|
|
|
|
|
|
|
let(:search_string) do
|
2013-04-26 18:14:38 +00:00
|
|
|
# use inspect to quote spaces in string
|
|
|
|
"ref:#{ref.inspect}"
|
2013-04-25 14:46:53 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
let!(:module_ref) do
|
|
|
|
FactoryGirl.create(:mdm_module_ref)
|
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
context 'with Mdm::Module::Ref#name' do
|
2013-04-25 14:46:53 +00:00
|
|
|
let(:ref) do
|
|
|
|
module_ref.name
|
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
it 'should match Mdm::Module::Ref#name' do
|
2013-04-25 14:46:53 +00:00
|
|
|
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
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
context 'without Mdm::Module::Ref#name' do
|
|
|
|
it 'should not match Mdm::Module::Ref#name' do
|
2013-04-25 14:46:53 +00:00
|
|
|
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
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
context 'with Mdm::Module::Ref#name' do
|
2013-04-25 14:46:53 +00:00
|
|
|
let(:type) do
|
|
|
|
target_module_detail.mtype
|
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
it 'should match Mdm::Module::Detail#mtype' do
|
2013-04-25 14:46:53 +00:00
|
|
|
module_details.count.should > 0
|
|
|
|
|
|
|
|
module_details.all? { |module_detail|
|
|
|
|
module_detail.mtype == type
|
|
|
|
}.should be_true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
context 'without Mdm::Module::Detail#mtype' do
|
|
|
|
it 'should not match Mdm::Module::Detail#mtype' do
|
2013-04-25 14:46:53 +00:00
|
|
|
module_details.count.should == 0
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'without keyword' do
|
2013-04-26 18:14:38 +00:00
|
|
|
context 'with Mdm::Module::Action#name' do
|
2013-04-25 14:46:53 +00:00
|
|
|
let(:search_string) do
|
|
|
|
module_action.name
|
|
|
|
end
|
|
|
|
|
|
|
|
let!(:module_action) do
|
|
|
|
FactoryGirl.create(:mdm_module_action)
|
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
it 'should match Mdm::Module::Action#name' do
|
2013-04-25 14:46:53 +00:00
|
|
|
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
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
context 'with Mdm::Module::Arch#name' do
|
2013-04-25 14:46:53 +00:00
|
|
|
let(:search_string) do
|
|
|
|
module_arch.name
|
|
|
|
end
|
|
|
|
|
|
|
|
let!(:module_arch) do
|
|
|
|
FactoryGirl.create(:mdm_module_arch)
|
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
it 'should match Mdm::Module::Arch#name' do
|
2013-04-25 14:46:53 +00:00
|
|
|
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
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
context 'with Mdm::Module::Author#name' do
|
2013-04-25 14:46:53 +00:00
|
|
|
let(:search_string) do
|
|
|
|
module_author.name
|
|
|
|
end
|
|
|
|
|
|
|
|
let!(:module_author) do
|
|
|
|
FactoryGirl.create(:mdm_module_author)
|
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
it 'should match Mdm::Module::Author#name' do
|
2013-04-25 14:46:53 +00:00
|
|
|
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
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
context 'with Mdm::Module::Detail' do
|
2013-04-25 14:46:53 +00:00
|
|
|
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
|
2013-04-26 18:14:38 +00:00
|
|
|
# use inspect to quote spaces in string
|
|
|
|
target_module_detail.description.inspect
|
2013-04-25 14:46:53 +00:00
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
it 'should match Mdm::Module::Detail#description' do
|
2013-04-25 14:46:53 +00:00
|
|
|
module_details.count.should == 1
|
|
|
|
|
|
|
|
module_details.all? { |module_detail|
|
2013-04-26 18:14:38 +00:00
|
|
|
module_detail.description == target_module_detail.description
|
2013-04-25 14:46:53 +00:00
|
|
|
}.should be_true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with #fullname' do
|
|
|
|
let(:search_string) do
|
|
|
|
target_module_detail.fullname
|
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
it 'should match Mdm::Module::Detail#fullname' do
|
2013-04-25 14:46:53 +00:00
|
|
|
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
|
2013-04-26 18:14:38 +00:00
|
|
|
# use inspect to quote spaces in string
|
|
|
|
target_module_detail.name.inspect
|
2013-04-25 14:46:53 +00:00
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
it 'should match Mdm::Module::Detail#name' do
|
2013-04-25 14:46:53 +00:00
|
|
|
module_details.count.should == 1
|
|
|
|
|
|
|
|
module_details.all? { |module_detail|
|
2013-04-26 18:14:38 +00:00
|
|
|
module_detail.name == target_module_detail.name
|
2013-04-25 14:46:53 +00:00
|
|
|
}.should be_true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
context 'with Mdm::Module::Platform#name' do
|
2013-04-25 14:46:53 +00:00
|
|
|
let(:search_string) do
|
|
|
|
module_platform.name
|
|
|
|
end
|
|
|
|
|
|
|
|
let!(:module_platform) do
|
|
|
|
FactoryGirl.create(:mdm_module_platform)
|
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
it 'should match Mdm::Module::Platform#name' do
|
2013-04-25 14:46:53 +00:00
|
|
|
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
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
context 'with Mdm::Module::Ref#name' do
|
2013-04-25 14:46:53 +00:00
|
|
|
let(:search_string) do
|
|
|
|
module_ref.name
|
|
|
|
end
|
|
|
|
|
|
|
|
let!(:module_ref) do
|
|
|
|
FactoryGirl.create(:mdm_module_ref)
|
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
it 'should match Mdm::Module::Ref#name' do
|
2013-04-25 14:46:53 +00:00
|
|
|
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
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
context 'with Mdm::Module::Target#name' do
|
2013-04-25 14:46:53 +00:00
|
|
|
let(:search_string) do
|
|
|
|
module_target.name
|
|
|
|
end
|
|
|
|
|
|
|
|
let!(:module_target) do
|
|
|
|
FactoryGirl.create(:mdm_module_target)
|
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
it 'should match Mdm::Module::Target#name' do
|
2013-04-25 14:46:53 +00:00
|
|
|
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
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
context 'with Mdm::Module::Details' do
|
2013-04-25 14:46:53 +00:00
|
|
|
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
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
context 'with existing Mdm::Module::Detail#file' do
|
|
|
|
context 'with same Mdm::Module::Detail#mtime and File.mtime' do
|
2013-04-25 14:46:53 +00:00
|
|
|
it 'should not update module details' do
|
|
|
|
db_manager.should_not_receive(:update_module_details)
|
|
|
|
|
|
|
|
update_all_module_details
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
context 'without same Mdm::Module::Detail#mtime and File.mtime' do
|
2013-04-25 14:46:53 +00:00
|
|
|
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
|
2013-04-26 18:14:38 +00:00
|
|
|
context 'without existing Mdm::Module::Detail#file' do
|
2013-04-25 14:46:53 +00:00
|
|
|
# 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.exploits
|
|
|
|
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
|
|
|
|
|
2013-05-09 18:25:26 +00:00
|
|
|
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
|
2013-04-25 14:46:53 +00:00
|
|
|
|
|
|
|
update_module_details
|
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
it 'should create an Mdm::Module::Detail' do
|
2013-04-25 14:46:53 +00:00
|
|
|
expect {
|
|
|
|
update_module_details
|
2013-04-26 18:14:38 +00:00
|
|
|
}.to change(Mdm::Module::Detail, :count).by(1)
|
2013-04-25 14:46:53 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
context 'module_to_details_hash' do
|
|
|
|
let(:module_to_details_hash) do
|
|
|
|
{
|
2013-05-09 18:25:26 +00:00
|
|
|
:mtype => module_type,
|
|
|
|
:privileged => privileged,
|
|
|
|
:rank => rank,
|
|
|
|
:refname => module_reference_name,
|
|
|
|
:stance => stance
|
2013-04-25 14:46:53 +00:00
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2013-05-09 18:25:26 +00:00
|
|
|
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
|
|
|
|
|
2013-04-25 14:46:53 +00:00
|
|
|
before(:each) do
|
|
|
|
db_manager.stub(
|
|
|
|
:module_to_details_hash
|
|
|
|
).with(
|
|
|
|
module_instance
|
|
|
|
).and_return(
|
|
|
|
module_to_details_hash
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
context 'Mdm::Module::Detail' do
|
2013-04-25 14:46:53 +00:00
|
|
|
subject(:module_detail) do
|
2013-04-26 18:14:38 +00:00
|
|
|
Mdm::Module::Detail.last
|
2013-04-25 14:46:53 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
before(:each) do
|
|
|
|
update_module_details
|
|
|
|
end
|
|
|
|
|
2013-05-09 18:25:26 +00:00
|
|
|
its(:mtype) { should == module_type }
|
|
|
|
its(:privileged) { should == privileged }
|
|
|
|
its(:rank) { should == rank }
|
2013-04-25 14:46:53 +00:00
|
|
|
its(:ready) { should == true }
|
|
|
|
its(:refname) { should == module_reference_name }
|
2013-05-09 18:25:26 +00:00
|
|
|
its(:stance) { should == stance }
|
2013-04-25 14:46:53 +00:00
|
|
|
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
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
it 'should create an Mdm::Module::Action' do
|
2013-04-25 14:46:53 +00:00
|
|
|
expect {
|
|
|
|
update_module_details
|
2013-04-26 18:14:38 +00:00
|
|
|
}.to change(Mdm::Module::Action, :count).by(1)
|
2013-04-25 14:46:53 +00:00
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
context 'Mdm::Module::Action' do
|
2013-04-25 14:46:53 +00:00
|
|
|
subject(:module_action) do
|
|
|
|
module_detail.actions.last
|
|
|
|
end
|
|
|
|
|
|
|
|
let(:module_detail) do
|
2013-04-26 18:14:38 +00:00
|
|
|
Mdm::Module::Detail.last
|
2013-04-25 14:46:53 +00:00
|
|
|
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
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
it 'should create an Mdm::Module::Arch' do
|
2013-04-25 14:46:53 +00:00
|
|
|
expect {
|
|
|
|
update_module_details
|
2013-04-26 18:14:38 +00:00
|
|
|
}.to change(Mdm::Module::Arch, :count).by(1)
|
2013-04-25 14:46:53 +00:00
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
context 'Mdm::Module::Arch' do
|
2013-04-25 14:46:53 +00:00
|
|
|
subject(:module_arch) do
|
|
|
|
module_detail.archs.last
|
|
|
|
end
|
|
|
|
|
|
|
|
let(:module_detail) do
|
2013-04-26 18:14:38 +00:00
|
|
|
Mdm::Module::Detail.last
|
2013-04-25 14:46:53 +00:00
|
|
|
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
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
it 'should create an Mdm::Module::Author' do
|
2013-04-25 14:46:53 +00:00
|
|
|
expect {
|
|
|
|
update_module_details
|
2013-04-26 18:14:38 +00:00
|
|
|
}.to change(Mdm::Module::Author, :count).by(1)
|
2013-04-25 14:46:53 +00:00
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
context 'Mdm::Module::Author' do
|
2013-04-25 14:46:53 +00:00
|
|
|
subject(:module_author) do
|
|
|
|
module_detail.authors.last
|
|
|
|
end
|
|
|
|
|
|
|
|
let(:module_detail) do
|
2013-04-26 18:14:38 +00:00
|
|
|
Mdm::Module::Detail.last
|
2013-04-25 14:46:53 +00:00
|
|
|
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
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
it 'should create an Mdm::Module::Platform' do
|
2013-04-25 14:46:53 +00:00
|
|
|
expect {
|
|
|
|
update_module_details
|
2013-04-26 18:14:38 +00:00
|
|
|
}.to change(Mdm::Module::Platform, :count).by(1)
|
2013-04-25 14:46:53 +00:00
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
context 'Mdm::Module::Platform' do
|
2013-04-25 14:46:53 +00:00
|
|
|
subject(:module_platform) do
|
|
|
|
module_detail.platforms.last
|
|
|
|
end
|
|
|
|
|
|
|
|
let(:module_detail) do
|
2013-04-26 18:14:38 +00:00
|
|
|
Mdm::Module::Detail.last
|
2013-04-25 14:46:53 +00:00
|
|
|
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
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
it 'should create an Mdm::Module::Ref' do
|
2013-04-25 14:46:53 +00:00
|
|
|
expect {
|
|
|
|
update_module_details
|
2013-04-26 18:14:38 +00:00
|
|
|
}.to change(Mdm::Module::Ref, :count).by(1)
|
2013-04-25 14:46:53 +00:00
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
context 'Mdm::Module::Ref' do
|
2013-04-25 14:46:53 +00:00
|
|
|
subject(:module_ref) do
|
|
|
|
module_detail.refs.last
|
|
|
|
end
|
|
|
|
|
|
|
|
let(:module_detail) do
|
2013-04-26 18:14:38 +00:00
|
|
|
Mdm::Module::Detail.last
|
2013-04-25 14:46:53 +00:00
|
|
|
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
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
it 'should create an Mdm::Module::Target' do
|
2013-04-25 14:46:53 +00:00
|
|
|
expect {
|
|
|
|
update_module_details
|
2013-04-26 18:14:38 +00:00
|
|
|
}.to change(Mdm::Module::Target, :count).by(1)
|
2013-04-25 14:46:53 +00:00
|
|
|
end
|
|
|
|
|
2013-04-26 18:14:38 +00:00
|
|
|
context 'Mdm::Module::Target' do
|
2013-04-25 14:46:53 +00:00
|
|
|
subject(:module_target) do
|
|
|
|
module_detail.targets.last
|
|
|
|
end
|
|
|
|
|
|
|
|
let(:module_detail) do
|
2013-04-26 18:14:38 +00:00
|
|
|
Mdm::Module::Detail.last
|
2013-04-25 14:46:53 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
before(:each) do
|
|
|
|
update_module_details
|
|
|
|
end
|
|
|
|
|
|
|
|
its(:index) { should == index }
|
|
|
|
its(:name) { should == name }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'without migrated' do
|
2013-04-26 18:14:38 +00:00
|
|
|
it 'should not create an Mdm::Module::Detail' do
|
2013-04-25 14:46:53 +00:00
|
|
|
expect {
|
|
|
|
update_module_details
|
2013-04-26 18:14:38 +00:00
|
|
|
}.to_not change(Mdm::Module::Detail, :count)
|
2013-04-25 14:46:53 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2013-04-01 21:06:40 +00:00
|
|
|
end
|