diff --git a/spec/lib/msf/core/option_group_spec.rb b/spec/lib/msf/core/option_group_spec.rb index cc00cc3186..152370887e 100644 --- a/spec/lib/msf/core/option_group_spec.rb +++ b/spec/lib/msf/core/option_group_spec.rb @@ -47,4 +47,92 @@ RSpec.describe Msf::OptionGroup do end end end + + describe '#validate' do + let(:required_option_name) { 'required_name' } + let(:option_names) { ['not_required_name', required_option_name] } + let(:required_names) { [required_option_name] } + let(:options) { instance_double(Msf::OptionContainer) } + let(:datastore) { instance_double(Msf::DataStoreWithFallbacks) } + + context 'when there are no required options' do + subject { described_class.new(name: 'name', description: 'description', option_names: option_names) } + + context 'when no values are set for the options' do + + before(:each) do + allow(options).to receive(:[]).and_return(instance_double(Msf::OptBase)) + allow(datastore).to receive(:[]).and_return(nil) + end + + it 'validates the options in the group' do + expect { subject.validate(options, datastore) }.not_to raise_error(Msf::OptionValidateError) + end + end + + context 'when values are set for the options' do + + before(:each) do + allow(options).to receive(:[]).and_return(instance_double(Msf::OptBase)) + allow(datastore).to receive(:[]).and_return('OptionValue') + end + + it 'validates the options in the group' do + expect { subject.validate(options, datastore) }.not_to raise_error(Msf::OptionValidateError) + end + end + + context 'when the options have not been registered' do + + before(:each) do + allow(options).to receive(:[]).and_return(nil) + end + + it 'does not attempt to validate the options' do + expect { subject.validate(options, datastore) }.not_to raise_error(Msf::OptionValidateError) + end + end + end + + context 'when there is a required option' do + subject { described_class.new(name: 'name', description: 'description', option_names: option_names, required_options: required_names) } + let(:error_message) { "The following options failed to validate: #{required_option_name}." } + + context 'when no values are set for the options' do + + before(:each) do + allow(options).to receive(:[]).and_return(instance_double(Msf::OptBase)) + allow(datastore).to receive(:[]).and_return(nil) + end + + it 'raises an error only for the required option' do + expect { subject.validate(options, datastore) }.to raise_error(Msf::OptionValidateError).with_message(error_message) + end + end + + context 'when values are set for the options' do + + before(:each) do + allow(options).to receive(:[]).and_return(instance_double(Msf::OptBase)) + allow(datastore).to receive(:[]).and_return('OptionValue') + end + + it 'validates the options in the group' do + expect { subject.validate(options, datastore) }.not_to raise_error(Msf::OptionValidateError) + end + end + + context 'when the options have not been registered' do + + before(:each) do + allow(options).to receive(:[]).and_return(nil) + end + + it 'does not attempt to validate the options' do + expect { subject.validate(options, datastore) }.not_to raise_error(Msf::OptionValidateError) + end + end + end + + end end diff --git a/spec/lib/msf/core/optional_session/mssql_spec.rb b/spec/lib/msf/core/optional_session/mssql_spec.rb new file mode 100644 index 0000000000..6b098c09d7 --- /dev/null +++ b/spec/lib/msf/core/optional_session/mssql_spec.rb @@ -0,0 +1,14 @@ +# -*- coding:binary -*- +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Msf::OptionalSession::MSSQL do + subject(:mod) do + mod = ::Msf::Module.new + mod.extend described_class + mod + end + + it_behaves_like Msf::OptionalSession +end diff --git a/spec/lib/msf/core/optional_session/mysql_spec.rb b/spec/lib/msf/core/optional_session/mysql_spec.rb new file mode 100644 index 0000000000..87bc908cca --- /dev/null +++ b/spec/lib/msf/core/optional_session/mysql_spec.rb @@ -0,0 +1,14 @@ +# -*- coding:binary -*- +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Msf::OptionalSession::MySQL do + subject(:mod) do + mod = ::Msf::Module.new + mod.extend described_class + mod + end + + it_behaves_like Msf::OptionalSession +end diff --git a/spec/lib/msf/core/optional_session/postgresql_spec.rb b/spec/lib/msf/core/optional_session/postgresql_spec.rb new file mode 100644 index 0000000000..b48b11add5 --- /dev/null +++ b/spec/lib/msf/core/optional_session/postgresql_spec.rb @@ -0,0 +1,14 @@ +# -*- coding:binary -*- +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Msf::OptionalSession::PostgreSQL do + subject(:mod) do + mod = ::Msf::Module.new + mod.extend described_class + mod + end + + it_behaves_like Msf::OptionalSession +end diff --git a/spec/lib/msf/core/optional_session/smb_spec.rb b/spec/lib/msf/core/optional_session/smb_spec.rb new file mode 100644 index 0000000000..7726bb863c --- /dev/null +++ b/spec/lib/msf/core/optional_session/smb_spec.rb @@ -0,0 +1,14 @@ +# -*- coding:binary -*- +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Msf::OptionalSession::SMB do + subject(:mod) do + mod = ::Msf::Module.new + mod.extend described_class + mod + end + + it_behaves_like Msf::OptionalSession +end diff --git a/spec/support/shared/examples/msf/core/optional_session.rb b/spec/support/shared/examples/msf/core/optional_session.rb new file mode 100644 index 0000000000..c1a1080f21 --- /dev/null +++ b/spec/support/shared/examples/msf/core/optional_session.rb @@ -0,0 +1,110 @@ +# -*- coding:binary -*- +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.shared_examples_for Msf::OptionalSession do + include_context 'Msf::Simple::Framework' + + let(:options) { instance_double(Msf::OptionContainer) } + let(:datastore) { instance_double(Msf::DataStoreWithFallbacks) } + let(:session) { instance_double(Msf::Sessions::SMB) } + let(:session_group) { instance_double(Msf::OptionGroup) } + let(:rhost_group) { instance_double(Msf::OptionGroup) } + let(:groups) do + { + 'SESSION' => session_group, + 'RHOST' => rhost_group + } + end + describe '#validate' do + before(:each) do + allow(options).to receive(:validate) + allow(options).to receive(:[]).and_return(instance_double(Msf::OptBase)) + allow(options).to receive(:groups).and_return(groups) + allow(mod).to receive(:options).and_return(options) + allow(mod).to receive(:datastore).and_return(datastore) + allow(mod).to receive(:framework).and_return(framework) + allow(session_group).to receive(:validate) + allow(rhost_group).to receive(:validate) + end + + context 'when neither SESSION or RHOST are set' do + before(:each) do + allow(mod).to receive(:rhost).and_return(nil) + allow(mod).to receive(:session).and_return(nil) + end + + it 'raises an error' do + expect { mod.validate }.to raise_error(Msf::OptionValidateError).with_message('A SESSION or RHOST must be provided') + end + end + + context 'when both SESSION and RHOST are set' do + before(:each) do + allow(datastore).to receive(:[]).with('SESSION').and_return('SESSION_ID') + allow(datastore).to receive(:[]).with('RHOST').and_return('RHOST_VALUE') + allow(mod).to receive(:session).and_return(session) + allow(mod).to receive(:rhost).and_return('RHOST_VALUE') + end + + it 'validates the SESSION only' do + mod.validate + expect(session_group).to have_received(:validate).once + expect(rhost_group).not_to have_received(:validate) + end + end + + context 'when only RHOST is set' do + before(:each) do + allow(datastore).to receive(:[]).with('RHOST').and_return('RHOST_VALUE') + allow(datastore).to receive(:[]).with('SESSION').and_return(nil) + allow(mod).to receive(:rhost).and_return('RHOST_VALUE') + allow(mod).to receive(:session).and_return(nil) + end + + it 'only validates the RHOST group' do + mod.validate + expect(rhost_group).to have_received(:validate).once + expect(session_group).not_to have_received(:validate) + end + end + + context 'when only SESSION is set' do + before(:each) do + allow(datastore).to receive(:[]).with('SESSION').and_return('SESSION_ID') + allow(datastore).to receive(:[]).with('RHOST').and_return(nil) + allow(mod).to receive(:session).and_return(session) + allow(mod).to receive(:rhost).and_return(nil) + end + + it 'validates the SESSION only' do + mod.validate + expect(session_group).to have_received(:validate).once + expect(rhost_group).not_to have_received(:validate) + end + + context 'when the session is not the correct type' do + before(:each) do + allow(mod).to receive(:session_types).and_return(['correct_session_type']) + allow(session).to receive(:type).and_return('wrong_session_type') + end + + it 'should raise an error about the wrong session type' do + expect { mod.validate }.to raise_error(Msf::OptionValidateError) { |error| expect(error.options).to eq ['SESSION'] } + end + end + + context 'when the session is the correct type' do + before(:each) do + allow(mod).to receive(:session_types).and_return(['correct_session_type']) + allow(session).to receive(:type).and_return('correct_session_type') + end + + it 'should raise an error about the wrong session type' do + expect { mod.validate }.not_to raise_error + end + end + end + end +end