Merge branch 'master' of git://github.com/rapid7/metasploit-framework

bug/bundler_fix
corelanc0d3r 2012-11-08 11:32:49 +01:00
commit 3273d93960
23 changed files with 851 additions and 100 deletions

2
.gitignore vendored
View File

@ -6,6 +6,8 @@
.yardoc .yardoc
# Mac OS X files # Mac OS X files
.DS_Store .DS_Store
# simplecov coverage data
coverage
data/meterpreter/ext_server_pivot.dll data/meterpreter/ext_server_pivot.dll
data/meterpreter/ext_server_pivot.x64.dll data/meterpreter/ext_server_pivot.x64.dll
doc doc

4
.travis.yml Normal file
View File

@ -0,0 +1,4 @@
language: ruby
rvm:
# - '1.8.7'
- '1.9.3'

View File

@ -24,4 +24,7 @@ end
group :test do group :test do
# testing framework # testing framework
gem 'rspec' gem 'rspec'
# code coverage for tests
# any version newer than 0.5.4 gives an Encoding error when trying to read the source files.
gem 'simplecov', '0.5.4', :require => false
end end

View File

@ -45,6 +45,10 @@ GEM
rspec-expectations (2.11.3) rspec-expectations (2.11.3)
diff-lcs (~> 1.1.3) diff-lcs (~> 1.1.3)
rspec-mocks (2.11.3) rspec-mocks (2.11.3)
simplecov (0.5.4)
multi_json (~> 1.0.3)
simplecov-html (~> 0.5.3)
simplecov-html (0.5.3)
slop (3.3.3) slop (3.3.3)
tzinfo (0.3.33) tzinfo (0.3.33)
yard (0.8.2.1) yard (0.8.2.1)
@ -60,4 +64,5 @@ DEPENDENCIES
rake rake
redcarpet redcarpet
rspec rspec
simplecov (= 0.5.4)
yard yard

View File

@ -1,5 +1,5 @@
Metasploit [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/rapid7/metasploit-framework) Metasploit [![Build Status](https://travis-ci.org/rapid7/metasploit-framework.png)](https://travis-ci.org/rapid7/metasploit-framework) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/rapid7/metasploit-framework)
== ==
The Metasploit Framework is released under a BSD-style license. See The Metasploit Framework is released under a BSD-style license. See
COPYING for more details. COPYING for more details.

View File

@ -0,0 +1,38 @@
# Base error class for all error under {Msf::Modules}
class Msf::Modules::Error < StandardError
def initialize(attributes={})
@module_path = attributes[:module_path]
@module_reference_name = attributes[:module_reference_name]
message_parts = []
message_parts << "Failed to load module"
if module_reference_name or module_path
clause_parts = []
if module_reference_name
clause_parts << module_reference_name
end
if module_path
clause_parts << "from #{module_path}"
end
clause = clause_parts.join(' ')
message_parts << "(#{clause})"
end
causal_message = attributes[:causal_message]
if causal_message
message_parts << "due to #{causal_message}"
end
message = message_parts.join(' ')
super(message)
end
attr_reader :module_reference_name
attr_reader :module_path
end

View File

@ -3,6 +3,7 @@
# #
require 'msf/core/modules/loader' require 'msf/core/modules/loader'
require 'msf/core/modules/namespace' require 'msf/core/modules/namespace'
require 'msf/core/modules/metasploit_class_compatibility_error'
require 'msf/core/modules/version_compatibility_error' require 'msf/core/modules/version_compatibility_error'
# Responsible for loading modules for {Msf::ModuleManager}. # Responsible for loading modules for {Msf::ModuleManager}.
@ -117,12 +118,17 @@ class Msf::Modules::Loader::Base
metasploit_class = nil metasploit_class = nil
module_content = read_module_content(parent_path, type, module_reference_name)
if module_content.empty?
# read_module_content is responsible for calling {#load_error}, so just return here.
return false
end
loaded = namespace_module_transaction(type + "/" + module_reference_name, :reload => reload) { |namespace_module| loaded = namespace_module_transaction(type + "/" + module_reference_name, :reload => reload) { |namespace_module|
# set the parent_path so that the module can be reloaded with #load_module # set the parent_path so that the module can be reloaded with #load_module
namespace_module.parent_path = parent_path namespace_module.parent_path = parent_path
module_content = read_module_content(parent_path, type, module_reference_name)
begin begin
namespace_module.module_eval_with_lexical_scope(module_content, module_path) namespace_module.module_eval_with_lexical_scope(module_content, module_path)
# handle interrupts as pass-throughs unlike other Exceptions so users can bail with Ctrl+C # handle interrupts as pass-throughs unlike other Exceptions so users can bail with Ctrl+C
@ -133,45 +139,33 @@ class Msf::Modules::Loader::Base
begin begin
namespace_module.version_compatible!(module_path, module_reference_name) namespace_module.version_compatible!(module_path, module_reference_name)
rescue Msf::Modules::VersionCompatibilityError => version_compatibility_error rescue Msf::Modules::VersionCompatibilityError => version_compatibility_error
error_message = "Failed to load module (#{module_path}) due to error and #{version_compatibility_error}" load_error(module_path, version_compatibility_error)
else else
error_message = "#{error.class} #{error}" load_error(module_path, error)
end end
# record the error message without the backtrace for the console
module_manager.module_load_error_by_path[module_path] = error_message
error_message_with_backtrace = "#{error_message}:\n#{error.backtrace.join("\n")}"
elog(error_message_with_backtrace)
return false return false
end end
begin begin
namespace_module.version_compatible!(module_path, module_reference_name) namespace_module.version_compatible!(module_path, module_reference_name)
rescue Msf::Modules::VersionCompatibilityError => version_compatibility_error rescue Msf::Modules::VersionCompatibilityError => version_compatibility_error
error_message = version_compatibility_error.to_s load_error(module_path, version_compatibility_error)
elog(error_message)
module_manager.module_load_error_by_path[module_path] = error_message
return false return false
end end
metasploit_class = namespace_module.metasploit_class begin
metasploit_class = namespace_module.metasploit_class!(module_path, module_reference_name)
unless metasploit_class rescue Msf::Modules::MetasploitClassCompatibilityError => error
error_message = "Missing Metasploit class constant" load_error(module_path, error)
elog(error_message)
module_manager.module_load_error_by_path[module_path] = error_message
return false return false
end end
unless usable?(metasploit_class) unless usable?(metasploit_class)
ilog( ilog(
"Skipping module #{module_reference_name} under #{parent_path} because is_usable returned false.", "Skipping module (#{module_reference_name} from #{module_path}) because is_usable returned false.",
'core', 'core',
LEV_1 LEV_1
) )
@ -409,6 +403,29 @@ class Msf::Modules::Loader::Base
raise ::NotImplementedError raise ::NotImplementedError
end end
# Records the load error to {Msf::ModuleManager::Loading#module_load_error_by_path} and the log.
#
# @param [String] module_path Path to the module as returned by {#module_path}.
# @param [Exception, #class, #to_s, #backtrace] error the error that cause the module not to load.
# @return [void]
#
# @see #module_path
def load_error(module_path, error)
# module_load_error_by_path does not get the backtrace because the value is echoed to the msfconsole where
# backtraces should not appear.
module_manager.module_load_error_by_path[module_path] = "#{error.class} #{error}"
log_lines = []
log_lines << "#{module_path} failed to load due to the following error:"
log_lines << error.class.to_s
log_lines << error.to_s
log_lines << "Call stack:"
log_lines += error.backtrace
log_message = log_lines.join("\n")
elog(log_message)
end
# @return [Msf::ModuleManager] The module manager for which this loader is loading modules. # @return [Msf::ModuleManager] The module manager for which this loader is loading modules.
attr_reader :module_manager attr_reader :module_manager

View File

@ -75,6 +75,7 @@ class Msf::Modules::Loader::Directory < Msf::Modules::Loader::Base
module_content = '' module_content = ''
begin
# force to read in binary mode so Pro modules won't be truncated on Windows # force to read in binary mode so Pro modules won't be truncated on Windows
File.open(full_path, 'rb') do |f| File.open(full_path, 'rb') do |f|
# Pass the size of the file as it leads to faster reads due to fewer buffer resizes. Greatest effect on Windows. # Pass the size of the file as it leads to faster reads due to fewer buffer resizes. Greatest effect on Windows.
@ -83,6 +84,9 @@ class Msf::Modules::Loader::Directory < Msf::Modules::Loader::Base
# @see https://github.com/ruby/ruby/blob/ruby_1_9_3/io.c#L2038 # @see https://github.com/ruby/ruby/blob/ruby_1_9_3/io.c#L2038
module_content = f.read(f.stat.size) module_content = f.read(f.stat.size)
end end
rescue Errno::ENOENT => error
load_error(full_path, error)
end
module_content module_content
end end

View File

@ -0,0 +1,13 @@
require 'msf/core/modules/error'
# Error raised by {Msf::Modules::Namespace#metasploit_class!} if it cannot the namespace_module does not have a constant
# with {Msf::Framework::Major} or lower as a number after 'Metasploit', which indicates a compatible Msf::Module.
class Msf::Modules::MetasploitClassCompatibilityError < Msf::Modules::Error
def initialize(attributes={})
super_attributes = {
:causal_message => 'Missing compatible Metasploit<major_version> class constant',
}.merge(attributes)
super(super_attributes)
end
end

View File

@ -10,8 +10,6 @@ module Msf::Modules::Namespace
# @return [nil] if such as class is not defined. # @return [nil] if such as class is not defined.
def metasploit_class def metasploit_class
metasploit_class = nil metasploit_class = nil
# don't search ancestors for the metasploit_class
#inherit = false
::Msf::Framework::Major.downto(1) do |major| ::Msf::Framework::Major.downto(1) do |major|
# Since we really only care about the deepest namespace, we don't # Since we really only care about the deepest namespace, we don't
@ -29,6 +27,19 @@ module Msf::Modules::Namespace
metasploit_class metasploit_class
end end
def metasploit_class!(module_path, module_reference_name)
metasploit_class = self.metasploit_class
unless metasploit_class
raise Msf::Modules::MetasploitClassCompatibilityError.new(
:module_path => module_path,
:module_reference_name => module_reference_name
)
end
metasploit_class
end
# Raises an error unless {Msf::Framework::VersionCore} and {Msf::Framework::VersionAPI} meet the minimum required # Raises an error unless {Msf::Framework::VersionCore} and {Msf::Framework::VersionAPI} meet the minimum required
# versions defined in RequiredVersions in the module content. # versions defined in RequiredVersions in the module content.
# #

View File

@ -1,20 +1,43 @@
require 'msf/core/modules/error'
# Error raised by {Msf::Modules::Namespace#version_compatible!} on {Msf::Modules::Loader::Base#create_namespace_module} # Error raised by {Msf::Modules::Namespace#version_compatible!} on {Msf::Modules::Loader::Base#create_namespace_module}
# if the API or Core version does not meet the minimum requirements defined in the RequiredVersions constant in the # if the API or Core version does not meet the minimum requirements defined in the RequiredVersions constant in the
# {Msf::Modules::Loader::Base#read_module_content module content}. # {Msf::Modules::Loader::Base#read_module_content module content}.
class Msf::Modules::VersionCompatibilityError < StandardError class Msf::Modules::VersionCompatibilityError < Msf::Modules::Error
# @param [Hash{Symbol => Float}] attributes # @param [Hash{Symbol => Float}] attributes
# @option attributes [Float] :minimum_api_version The minimum {Msf::Framework::VersionAPI} as defined in # @option attributes [Float] :minimum_api_version The minimum {Msf::Framework::VersionAPI} as defined in
# RequiredVersions. # RequiredVersions.
# @option attributes [Float] :minimum_core_version The minimum {Msf::Framework::VersionCore} as defined in # @option attributes [Float] :minimum_core_version The minimum {Msf::Framework::VersionCore} as defined in
# RequiredVersions. # RequiredVersions.
def initialize(attributes={}) def initialize(attributes={})
@module_path = attributes[:module_path]
@module_reference_name = attributes[:module_reference_name]
@minimum_api_version = attributes[:minimum_api_version] @minimum_api_version = attributes[:minimum_api_version]
@minimum_core_version = attributes[:minimum_core_version] @minimum_core_version = attributes[:minimum_core_version]
super("Failed to reload module (#{module_reference_name} from #{module_path}) due to version check " \ message_parts = []
"(requires API:#{minimum_api_version} Core:#{minimum_core_version})") message_parts << 'version check'
if minimum_api_version or minimum_core_version
clause_parts = []
if minimum_api_version
clause_parts << "API >= #{minimum_api_version}"
end
if minimum_core_version
clause_parts << "Core >= #{minimum_core_version}"
end
clause = clause_parts.join(' and ')
message_parts << "(requires #{clause})"
end
causal_message = message_parts.join(' ')
super_attributes = {
:causal_message => causal_message
}.merge(attributes)
super(super_attributes)
end end
# @return [Float] The minimum value of {Msf::Framework::VersionAPI} for the module to be compatible. # @return [Float] The minimum value of {Msf::Framework::VersionAPI} for the module to be compatible.

View File

@ -0,0 +1,101 @@
require 'spec_helper'
describe Msf::Modules::Error do
context 'instance methods' do
context '#initialize' do
include_context 'Msf::Modules::Error attributes'
context 'with :causal_message' do
subject do
described_class.new(:causal_message => causal_message)
end
it 'should include causal_message in error' do
subject.to_s.should == "Failed to load module due to #{causal_message}"
end
end
context 'with :causal_message and :module_path' do
subject do
described_class.new(
:causal_message => causal_message,
:module_path => module_path
)
end
it 'should include causal_message and module_path in error' do
subject.to_s.should == "Failed to load module (from #{module_path}) due to #{causal_message}"
end
end
context 'with :causal_message and :module_reference_name' do
subject do
described_class.new(
:causal_message => causal_message,
:module_reference_name => module_reference_name
)
end
it 'should include causal_message and module_reference_name in error' do
subject.to_s.should == "Failed to load module (#{module_reference_name}) due to #{causal_message}"
end
end
context 'with :causal_message, :module_path, and :module_reference_nam' do
subject do
described_class.new(
:causal_message => causal_message,
:module_path => module_path,
:module_reference_name => module_reference_name
)
end
it 'should include causal_message, module_path, and module_reference_name in error' do
subject.to_s.should == "Failed to load module (#{module_reference_name} from #{module_path}) due to #{causal_message}"
end
end
context 'with :module_path' do
subject do
described_class.new(:module_path => module_path)
end
it 'should use :module_path for module_path' do
subject.module_path.should == module_path
end
it 'should include module_path in error' do
subject.to_s.should == "Failed to load module (from #{module_path})"
end
end
context 'with :module_path and :module_reference_name' do
subject do
described_class.new(
:module_path => module_path,
:module_reference_name => module_reference_name
)
end
it 'should include module_path and module_reference_name in error' do
subject.to_s.should == "Failed to load module (#{module_reference_name} from #{module_path})"
end
end
context 'with :module_reference_name' do
subject do
described_class.new(:module_reference_name => module_reference_name)
end
it 'should use :module_reference_name for module_reference_name' do
subject.module_reference_name.should == module_reference_name
end
it 'should include module_reference_name in error' do
subject.to_s.should == "Failed to load module (#{module_reference_name})"
end
end
end
end
end

View File

@ -3,6 +3,8 @@ require 'spec_helper'
require 'msf/core' require 'msf/core'
describe Msf::Modules::Loader::Base do describe Msf::Modules::Loader::Base do
include_context 'Msf::Modules::Loader::Base'
let(:described_class_pathname) do let(:described_class_pathname) do
root_pathname.join('lib', 'msf', 'core', 'modules', 'loader', 'base.rb') root_pathname.join('lib', 'msf', 'core', 'modules', 'loader', 'base.rb')
end end
@ -37,18 +39,6 @@ describe Msf::Modules::Loader::Base do
'rspec/mock' 'rspec/mock'
end end
let(:parent_path) do
parent_pathname.to_s
end
let(:parent_pathname) do
root_pathname.join('modules')
end
let(:root_pathname) do
Pathname.new(Msf::Config.install_root)
end
let(:type) do let(:type) do
Msf::MODULE_AUX Msf::MODULE_AUX
end end
@ -230,7 +220,7 @@ describe Msf::Modules::Loader::Base do
context 'instance methods' do context 'instance methods' do
let(:module_manager) do let(:module_manager) do
mock('Module Manager') mock('Module Manager', :module_load_error_by_path => {})
end end
subject do subject do
@ -323,13 +313,14 @@ describe Msf::Modules::Loader::Base do
end end
it 'should call #namespace_module_transaction with the module full name and :reload => true' do it 'should call #namespace_module_transaction with the module full name and :reload => true' do
subject.stub(:read_module_content => module_content)
subject.should_receive(:namespace_module_transaction).with(module_full_name, hash_including(:reload => true)) subject.should_receive(:namespace_module_transaction).with(module_full_name, hash_including(:reload => true))
subject.load_module(parent_path, type, module_reference_name) subject.load_module(parent_path, type, module_reference_name)
end end
it 'should set the parent_path on the namespace_module to match the parent_path passed to #load_module' do it 'should set the parent_path on the namespace_module to match the parent_path passed to #load_module' do
module_manager.stub(:module_load_error_by_path => {})
module_manager.stub(:on_module_load) module_manager.stub(:on_module_load)
subject.stub(:read_module_content => module_content) subject.stub(:read_module_content => module_content)
@ -339,7 +330,6 @@ describe Msf::Modules::Loader::Base do
end end
it 'should call #read_module_content to get the module content so that #read_module_content can be overridden to change loading behavior' do it 'should call #read_module_content to get the module content so that #read_module_content can be overridden to change loading behavior' do
module_manager.stub(:module_load_error_by_path => {})
module_manager.stub(:on_module_load) module_manager.stub(:on_module_load)
subject.should_receive(:read_module_content).with(parent_path, type, module_reference_name).and_return(module_content) subject.should_receive(:read_module_content).with(parent_path, type, module_reference_name).and_return(module_content)
@ -348,7 +338,6 @@ describe Msf::Modules::Loader::Base do
it 'should call namespace_module.module_eval_with_lexical_scope with the module_path' do it 'should call namespace_module.module_eval_with_lexical_scope with the module_path' do
subject.stub(:read_module_content => malformed_module_content) subject.stub(:read_module_content => malformed_module_content)
module_manager.stub(:module_load_error_by_path => {})
module_manager.stub(:on_module_load) module_manager.stub(:on_module_load)
# if the module eval error includes the module_path then the module_path was passed along correctly # if the module eval error includes the module_path then the module_path was passed along correctly
@ -356,13 +345,29 @@ describe Msf::Modules::Loader::Base do
subject.load_module(parent_path, type, module_reference_name, :reload => true).should be_false subject.load_module(parent_path, type, module_reference_name, :reload => true).should be_false
end end
context 'with empty module content' do
before(:each) do
subject.stub(:read_module_content).with(parent_path, type, module_reference_name).and_return('')
end
it 'should return false' do
subject.load_module(parent_path, type, module_reference_name).should be_false
end
it 'should not attempt to make a new namespace_module' do
subject.should_not_receive(:namespace_module_transaction)
subject.load_module(parent_path, type, module_reference_name).should be_false
end
end
context 'with errors from namespace_module_eval_with_lexical_scope' do context 'with errors from namespace_module_eval_with_lexical_scope' do
before(:each) do before(:each) do
@namespace_module = mock('Namespace Module') @namespace_module = mock('Namespace Module')
@namespace_module.stub(:parent_path=) @namespace_module.stub(:parent_path=)
subject.stub(:namespace_module_transaction).and_yield(@namespace_module) subject.stub(:namespace_module_transaction).and_yield(@namespace_module)
subject.stub(:read_module_content) module_content = mock('Module Content', :empty? => false)
subject.stub(:read_module_content).and_return(module_content)
end end
context 'with Interrupt' do context 'with Interrupt' do
@ -409,16 +414,8 @@ describe Msf::Modules::Loader::Base do
@namespace_module.stub(:version_compatible!).with(module_path, module_reference_name) @namespace_module.stub(:version_compatible!).with(module_path, module_reference_name)
end end
it 'should report error class and string in module_manager.module_load_error_by_path' do it 'should record the load error using the original error' do
subject.load_module(parent_path, type, module_reference_name).should be_false subject.should_receive(:load_error).with(module_path, error)
@module_load_error_by_path[module_path].should == "#{error_class} #{error}"
end
it 'should report error class, string, and backtrace in the log' do
subject.should_receive(:elog).with(
# don't use join on backtrace as that will match implementation too closely
"#{error_class} #{error}:\n#{backtrace[0]}\n#{backtrace[1]}"
)
subject.load_module(parent_path, type, module_reference_name).should be_false subject.load_module(parent_path, type, module_reference_name).should be_false
end end
end end
@ -448,18 +445,8 @@ describe Msf::Modules::Loader::Base do
) )
end end
it 'should report module_path and version compatibility error string in module_manager.module_load_error_by_path' do it 'should record the load error using the Msf::Modules::VersionCompatibilityError' do
subject.load_module(parent_path, type, module_reference_name).should be_false subject.should_receive(:load_error).with(module_path, version_compatibility_error)
@module_load_error_by_path[module_path].should include(module_path)
@module_load_error_by_path[module_path].should include(version_compatibility_error.to_s)
end
it 'should report backtrace of original error in the log' do
formatted_backtrace = "\n#{backtrace[0]}\n#{backtrace[1]}"
escaped_backtrace = Regexp.escape(formatted_backtrace)
subject.should_receive(:elog).with(/#{escaped_backtrace}/)
subject.load_module(parent_path, type, module_reference_name).should be_false subject.load_module(parent_path, type, module_reference_name).should be_false
end end
end end
@ -479,7 +466,7 @@ describe Msf::Modules::Loader::Base do
@namespace_module.stub(:module_eval_with_lexical_scope).with(module_content, module_path) @namespace_module.stub(:module_eval_with_lexical_scope).with(module_content, module_path)
metasploit_class = mock('Metasploit Class', :parent => @namespace_module) metasploit_class = mock('Metasploit Class', :parent => @namespace_module)
@namespace_module.stub(:metasploit_class => metasploit_class) @namespace_module.stub(:metasploit_class! => metasploit_class)
subject.stub(:namespace_module_transaction).and_yield(@namespace_module) subject.stub(:namespace_module_transaction).and_yield(@namespace_module)
@ -521,13 +508,8 @@ describe Msf::Modules::Loader::Base do
) )
end end
it 'should report error in module_manage.module_load_error_by_path' do it 'should record the load error' do
subject.load_module(parent_path, type, module_reference_name).should be_false subject.should_receive(:load_error).with(module_path, version_compatibility_error)
@module_load_error_by_path[module_path].should == version_compatibility_error.to_s
end
it 'should log error' do
subject.should_receive(:elog).with(version_compatibility_error.to_s)
subject.load_module(parent_path, type, module_reference_name).should be_false subject.load_module(parent_path, type, module_reference_name).should be_false
end end
@ -548,24 +530,27 @@ describe Msf::Modules::Loader::Base do
end end
context 'without metasploit_class' do context 'without metasploit_class' do
let(:error) do
Msf::Modules::MetasploitClassCompatibilityError.new(
:module_path => module_path,
:module_reference_name => module_reference_name
)
end
before(:each) do before(:each) do
@namespace_module.stub(:metasploit_class).and_return(nil) @namespace_module.stub(:metasploit_class!).with(module_path, module_reference_name).and_raise(error)
end end
let(:error_message) do it 'should record load error' do
'Missing Metasploit class constant' subject.should_receive(
end :load_error
).with(
it 'should log missing Metasploit class' do module_path,
subject.should_receive(:elog).with(error_message) kind_of(Msf::Modules::MetasploitClassCompatibilityError)
)
subject.load_module(parent_path, type, module_reference_name).should be_false subject.load_module(parent_path, type, module_reference_name).should be_false
end end
it 'should record error in module_manager.module_load_error_by_path' do
subject.load_module(parent_path, type, module_reference_name).should be_false
@module_load_error_by_path[module_path].should == error_message
end
it 'should return false' do it 'should return false' do
subject.load_module(parent_path, type, module_reference_name).should be_false subject.load_module(parent_path, type, module_reference_name).should be_false
end end
@ -583,7 +568,7 @@ describe Msf::Modules::Loader::Base do
end end
before(:each) do before(:each) do
@namespace_module.stub(:metasploit_class => metasploit_class) @namespace_module.stub(:metasploit_class! => metasploit_class)
end end
it 'should check if it is usable' do it 'should check if it is usable' do

View File

@ -2,6 +2,126 @@ require 'spec_helper'
require 'msf/core' require 'msf/core'
require 'msf/core/modules/loader/directory' require 'msf/core/modules/loader/directory'
describe Msf::Modules::Loader::Directory do require 'msf/core'
describe Msf::Modules::Loader::Directory do
context 'instance methods' do
include_context 'Msf::Modules::Loader::Base'
let(:module_manager) do
mock('Module Manager')
end
let(:module_path) do
"#{parent_path}/exploits/#{module_reference_name}.rb"
end
let(:type) do
'exploit'
end
subject do
described_class.new(module_manager)
end
context '#load_module' do
context 'with existent module_path' do
let(:framework) do
framework = mock('Msf::Framework', :datastore => {})
events = mock('Events')
events.stub(:on_module_load)
events.stub(:on_module_created)
framework.stub(:events => events)
framework
end
let(:module_full_name) do
"#{type}/#{module_reference_name}"
end
let(:module_manager) do
Msf::ModuleManager.new(framework)
end
let(:module_reference_name) do
'windows/smb/ms08_067_netapi'
end
it 'should load a module that can be created' do
subject.load_module(parent_path, type, module_reference_name).should be_true
created_module = module_manager.create(module_full_name)
created_module.name.should == 'Microsoft Server Service Relative Path Stack Corruption'
end
end
context 'without existent module_path' do
let(:module_reference_name) do
'osx/armle/safari_libtiff'
end
let(:error) do
Errno::ENOENT.new(module_path)
end
before(:each) do
module_manager.stub(:file_changed? => true)
module_manager.stub(:module_load_error_by_path => {})
end
it 'should not raise an error' do
File.exist?(module_path).should be_false
expect {
subject.load_module(parent_path, type, module_reference_name)
}.to_not raise_error
end
it 'should return false' do
File.exist?(module_path).should be_false
subject.load_module(parent_path, type, module_reference_name).should be_false
end
end
end
context '#read_module_content' do
context 'with non-existent module_path' do
let(:module_reference_name) do
'osx/armle/safari_libtiff'
end
before(:each) do
subject.stub(:load_error).with(module_path, kind_of(Errno::ENOENT))
end
# this ensures that the File.exist?(module_path) checks are checking the same path as the code under test
it 'should attempt to open the expected module_path' do
File.should_receive(:open).with(module_path, 'rb')
File.exist?(module_path).should be_false
subject.send(:read_module_content, parent_path, type, module_reference_name)
end
it 'should not raise an error' do
expect {
subject.send(:read_module_content, parent_path, type, module_reference_name)
}.to_not raise_error
end
it 'should return an empty string' do
subject.send(:read_module_content, parent_path, type, module_reference_name).should == ''
end
it 'should record the load error' do
subject.should_receive(:load_error).with(module_path, kind_of(Errno::ENOENT))
subject.send(:read_module_content, parent_path, type, module_reference_name).should == ''
end
end
end
end
end end

View File

@ -0,0 +1,7 @@
require 'spec_helper'
require 'msf/core/modules/metasploit_class_compatibility_error'
describe Msf::Modules::MetasploitClassCompatibilityError do
it_should_behave_like 'Msf::Modules::Error subclass #initialize'
end

View File

@ -0,0 +1,267 @@
require 'spec_helper'
require 'msf/core'
require 'msf/core/modules/namespace'
describe Msf::Modules::Namespace do
let(:module_path) do
"parent/path/type_directory/#{module_reference_name}.rb"
end
let(:module_reference_name) do
'module/reference/name'
end
subject do
mod = Module.new
mod.extend described_class
mod
end
context 'metasploit_class' do
before(:each) do
if major
subject.const_set("Metasploit#{major}", Class.new)
end
end
context 'without Metasploit<n> constant defined' do
let(:major) do
nil
end
it 'should not be defined' do
metasploit_constants = subject.constants.select { |constant|
constant.to_s =~ /Metasploit/
}
metasploit_constants.should be_empty
end
end
context 'with Metasploit1 constant defined' do
let(:major) do
1
end
it 'should be defined' do
subject.const_defined?('Metasploit1').should be_true
end
it 'should return the class' do
subject.metasploit_class.should be_a Class
end
end
context 'with Metasploit2 constant defined' do
let(:major) do
2
end
it 'should be defined' do
subject.const_defined?('Metasploit2').should be_true
end
it 'should return the class' do
subject.metasploit_class.should be_a Class
end
end
context 'with Metasploit3 constant defined' do
let(:major) do
3
end
it 'should be defined' do
subject.const_defined?('Metasploit3').should be_true
end
it 'should return the class' do
subject.metasploit_class.should be_a Class
end
end
context 'with Metasploit4 constant defined' do
let(:major) do
4
end
it 'should be defined' do
subject.const_defined?('Metasploit4').should be_true
end
it 'should return the class' do
subject.metasploit_class.should be_a Class
end
end
context 'with Metasploit5 constant defined' do
let(:major) do
5
end
it 'should be defined' do
subject.const_defined?('Metasploit5').should be_true
end
it 'should be newer than Msf::Framework::Major' do
major.should > Msf::Framework::Major
end
it 'should return nil' do
subject.metasploit_class.should be_nil
end
end
end
context 'metasploit_class!' do
it 'should call metasploit_class' do
subject.should_receive(:metasploit_class).and_return(Class.new)
subject.metasploit_class!(module_path, module_reference_name)
end
context 'with metasploit_class' do
let(:metasploit_class) do
Class.new
end
before(:each) do
subject.stub(:metasploit_class => metasploit_class)
end
it 'should return the metasploit_class' do
subject.metasploit_class!(module_path, module_reference_name).should == metasploit_class
end
end
context 'without metasploit_class' do
before(:each) do
subject.stub(:metasploit_class => nil)
end
it 'should raise a Msf::Modules::MetasploitClassCompatibilityError' do
expect {
subject.metasploit_class!(module_path, module_reference_name)
}.to raise_error(Msf::Modules::MetasploitClassCompatibilityError)
end
context 'the Msf::Modules::MetasploitClassCompatibilityError' do
it 'should include the module path' do
error = nil
begin
subject.metasploit_class!(module_path, module_reference_name)
rescue Msf::Modules::MetasploitClassCompatibilityError => error
end
error.should_not be_nil
error.to_s.should include(module_path)
end
it 'should include the module reference name' do
error = nil
begin
subject.metasploit_class!(module_path, module_reference_name)
rescue Msf::Modules::MetasploitClassCompatibilityError => error
end
error.should_not be_nil
error.to_s.should include(module_reference_name)
end
end
end
end
context 'version_compatible!' do
context 'without RequiredVersions' do
it 'should not be defined' do
subject.const_defined?('RequiredVersions').should be_false
end
it 'should not raise an error' do
expect {
subject.version_compatible!(module_path, module_reference_name)
}.to_not raise_error
end
end
context 'with RequiredVersions defined' do
let(:minimum_api_version) do
1
end
let(:minimum_core_version) do
1
end
before(:each) do
subject.const_set(
:RequiredVersions,
[
minimum_core_version,
minimum_api_version
]
)
end
context 'with minimum Core version' do
it 'should be <= Msf::Framework::VersionCore' do
minimum_core_version.should <= Msf::Framework::VersionCore
end
context 'without minimum API version' do
let(:minimum_api_version) do
2
end
it 'should be > Msf::Framework::VersionAPI' do
minimum_api_version.should > Msf::Framework::VersionAPI
end
it_should_behave_like 'Msf::Modules::VersionCompatibilityError'
end
context 'with minimum API version' do
it 'should not raise an error' do
expect {
subject.version_compatible!(module_path, module_reference_name)
}.to_not raise_error(Msf::Modules::VersionCompatibilityError)
end
end
end
context 'without minimum Core version' do
let(:minimum_core_version) do
5
end
it 'should be > Msf::Framework::VersionCore' do
minimum_core_version.should > Msf::Framework::VersionCore
end
context 'without minimum API version' do
let(:minimum_api_version) do
2
end
it 'should be > Msf::Framework::VersionAPI' do
minimum_api_version.should > Msf::Framework::VersionAPI
end
it_should_behave_like 'Msf::Modules::VersionCompatibilityError'
end
context 'with minimum API version' do
it 'should be <= Msf::Framework::VersionAPI' do
minimum_api_version <= Msf::Framework::VersionAPI
end
it_should_behave_like 'Msf::Modules::VersionCompatibilityError'
end
end
end
end
end

View File

@ -0,0 +1,62 @@
require 'spec_helper'
describe Msf::Modules::VersionCompatibilityError do
it_should_behave_like 'Msf::Modules::Error subclass #initialize' do
let(:minimum_api_version) do
1
end
let(:minimum_core_version) do
2
end
it 'should say cause was version check' do
subject.to_s.should match(/due to version check/)
end
context 'with :minimum_api_version' do
subject do
described_class.new(
:minimum_api_version => minimum_api_version
)
end
it 'should set minimum_api_version' do
subject.minimum_api_version.should == minimum_api_version
end
it 'should include minimum_api_version in error' do
subject.to_s.should match(/due to version check \(requires API >= #{minimum_api_version}\)/)
end
end
context 'with :minimum_api_version and :minimum_core_version' do
subject do
described_class.new(
:minimum_api_version => minimum_api_version,
:minimum_core_version => minimum_core_version
)
end
it 'should include minimum_api_version and minimum_core_version in error' do
subject.to_s.should match(/due to version check \(requires API >= #{minimum_api_version} and Core >= #{minimum_core_version}\)/)
end
end
context 'with :minimum_core_version' do
subject do
described_class.new(
:minimum_core_version => minimum_core_version
)
end
it 'should set minimum_core_version' do
subject.minimum_core_version.should == minimum_core_version
end
it 'should include minimum_core_version in error' do
subject.to_s.should match(/due to version check \(requires Core >= #{minimum_core_version}\)/)
end
end
end
end

View File

@ -8,6 +8,11 @@ root_pathname = spec_pathname.join('..').expand_path
lib_pathname = root_pathname.join('lib') lib_pathname = root_pathname.join('lib')
$LOAD_PATH.unshift(lib_pathname.to_s) $LOAD_PATH.unshift(lib_pathname.to_s)
# must be first require and started before any other requires so that it can measure coverage of all following required
# code. It is after the rubygems and bundler only because Bundler.setup supplies the LOAD_PATH to simplecov.
require 'simplecov'
SimpleCov.start
require 'rspec/core' require 'rspec/core'
# Requires supporting ruby files with custom matchers and macros, etc, # Requires supporting ruby files with custom matchers and macros, etc,

View File

@ -0,0 +1,13 @@
shared_context 'Msf::Modules::Error attributes' do
let(:causal_message) do
'rspec'
end
let(:module_path) do
"parent/path/type/#{module_reference_name}.rb"
end
let(:module_reference_name) do
'module/reference/name'
end
end

View File

@ -0,0 +1,13 @@
shared_context "Msf::Modules::Loader::Base" do
let(:parent_path) do
parent_pathname.to_s
end
let(:parent_pathname) do
root_pathname.join('modules')
end
let(:root_pathname) do
Pathname.new(Msf::Config.install_root)
end
end

View File

@ -0,0 +1,26 @@
shared_examples_for 'Msf::Modules::Error subclass #initialize' do
context 'instance methods' do
context '#initialize' do
include_context 'Msf::Modules::Error attributes'
subject do
described_class.new(
:module_path => module_path,
:module_reference_name => module_reference_name
)
end
it 'should include causal message in error' do
subject.to_s.should match(/due to .*/)
end
it 'should set module_path' do
subject.module_path.should == module_path
end
it 'should set module_reference_name' do
subject.module_reference_name.should == module_reference_name
end
end
end
end

View File

@ -0,0 +1,32 @@
shared_examples_for 'Msf::Modules::VersionCompatibilityError' do
let(:error) do
begin
subject.version_compatible!(module_path, module_reference_name)
rescue Msf::Modules::VersionCompatibilityError => error
end
error
end
it 'should be raised' do
expect {
subject.version_compatible!(module_path, module_reference_name)
}.to raise_error(Msf::Modules::VersionCompatibilityError)
end
it 'should include minimum API version' do
error.to_s.should include(minimum_api_version.to_s)
end
it 'should include minimum Core version' do
error.to_s.should include(minimum_core_version.to_s)
end
it 'should include module path' do
error.to_s.should include(module_path)
end
it 'should include module reference name' do
error.to_s.should include(module_reference_name)
end
end