require 'spec_helper' require 'ronin/exploits/mixins/has_targets' require 'ronin/exploits/exploit' describe Ronin::Exploits::Mixins::HasTargets do module TestHasTargets class WithNoTargets < Ronin::Exploits::Exploit include Ronin::Exploits::Mixins::HasTargets end class WithOneTarget < Ronin::Exploits::Exploit include Ronin::Exploits::Mixins::HasTargets target arch: :x86_64, os: :linux, foo: 42 end class InheritesTargets < WithOneTarget end class InheritesAndAddsTargets < WithOneTarget target arch: :arm, os: :linux, foo: 1337 end class WitthMultipleTargets < Ronin::Exploits::Exploit include Ronin::Exploits::Mixins::HasTargets target arch: :x86_64, os: :linux, foo: 42 target arch: :arm, os: :linux, foo: 1337 end end describe ".targets" do subject { test_class } context "with there are no targets" do let(:test_class) { TestHasTargets::WithNoTargets } it "must default to []" do expect(subject.targets).to eq([]) end end context "with at least one target" do let(:test_class) { TestHasTargets::WithOneTarget } it "must contain Ronin::Exploits::Target objects" do expect(subject.targets).to all(be_kind_of(Ronin::Exploits::Target)) end end context "when the super-class defines it's own targets" do let(:test_class) { TestHasTargets::InheritesTargets } let(:super_class) { test_class.superclass } it "must inherit the targets from the super-class" do expect(subject.targets).to eq(super_class.targets) end context "but the sub-class defines additional targets" do let(:test_class) { TestHasTargets::InheritesAndAddsTargets } it "must combine the sub-classes targets with the super-classes" do expect(subject.targets).to include(*super_class.targets) end it "must not modify the superclasses targets" do expect(super_class.targets).to_not contain_exactly(*subject.targets) end end end end describe ".target" do context "when given keyword arguments" do module TestHasTargets class ExploitWithTargetWithKeywordArgs < Ronin::Exploits::Exploit include Ronin::Exploits::Mixins::HasTargets target arch: :x86_64, os: :linux, foo: 42 end end subject { TestHasTargets::ExploitWithTargetWithKeywordArgs } let(:target) { subject.targets.last } it "must initialize a new Ronin::Exploits::Target and add it to .targets" do expect(target).to be_kind_of(Ronin::Exploits::Target) expect(target.arch).to eq(:x86_64) expect(target.os).to eq(:linux) expect(target.foo).to eq(42) end context "and when given a block" do module TestHasTargets class ExploitWithTargetWithKeywordArgsAndBlock < Ronin::Exploits::Exploit include Ronin::Exploits::Mixins::HasTargets target arch: :x86_64, os: :linux do |t| t.foo = 42 end end end subject { TestHasTargets::ExploitWithTargetWithKeywordArgsAndBlock } let(:target) { subject.targets.last } it "must initialize a new Ronin::Exploits::Target and add it to .targets" do expect(target).to be_kind_of(Ronin::Exploits::Target) expect(target.arch).to eq(:x86_64) expect(target.os).to eq(:linux) expect(target.foo).to eq(42) end end end end module TestHasTargets class ExampleExploit < Ronin::Exploits::Exploit include Ronin::Exploits::Mixins::HasTargets target arch: :x86_64, os: :linux, os_version: '5.18.1', software: 'Apache', version: '2.4.53', foo: 1 target arch: :arm, os: :macos, os_version: '10.13', software: 'nginx', version: '1.22.0', foo: 2 end end let(:test_class) { TestHasTargets::ExampleExploit } subject { test_class.new } describe "#initialize" do context "when not given the target: keyword argument" do it "must default #target to nil" do expect(subject.target).to be(nil) end end context "when given the target: keyword argument" do context "and it's a Hash" do subject do test_class.new(target: {os: :macos, os_version: '10.13'}) end it "must select the matching target from .targets and set #target" do expect(subject.target).to be(test_class.targets[1]) end end context "and it's an Integer" do let(:target_index) { 1 } subject { test_class.new(target: target_index) } it "must select the target from .targets at the given index" do expect(subject.target).to be(test_class.targets[target_index]) end end end end describe "#target=" do context "when given a Target object" do let(:target ) { Ronin::Exploits::Target.new } before { subject.target = target } it "must set #target to nil" do expect(subject.target).to be(target) end end context "when given an Integer" do let(:target_index) { 1 } before { subject.target = target_index } it "must set the target from .targets at the given index" do expect(subject.target).to be(test_class.targets[target_index]) end context "but the index is out of bounds" do let(:index) { 9000 } it do expect { subject.target = index }.to raise_error(described_class::NoMatchingTarget,"target index is out of bounds: #{index.inspect}") end end end context "when given a Target object" do before do subject.target = Ronin::Exploits::Target.new subject.target = nil end it "must set #target to nil" do expect(subject.target).to be(nil) end end end describe "#perform_validate" do context "when #target is set" do before { subject.target = subject.class.targets[0] } it "must not raise NoTargetSelected" do expect { subject.perform_validate }.to_not raise_error end it "must call #validate_params first" do expect(subject).to receive(:validate_params) subject.perform_validate end end context "when #target is not set" do it do expect { subject.perform_validate }.to raise_error(described_class::NoTargetSelected,"no target was selected") end end end describe "#select_target" do context "when given no keyword arguments" do subject do test_class.new(target: {os: :macos, os_version: '10.13'}) end it "must return the first target in .targets and set #target" do subject.select_target expect(subject.target).to be(test_class.targets.first) end end context "when given the arch: keyword argument" do let(:arch) { :arm } it "must find the target in .targets with the matching #arch" do subject.select_target(arch: arch) expect(subject.target.arch).to eq(arch) end end context "when given the os: keyword argument" do let(:os) { :macos } it "must find the target in .targets with the matching #os" do subject.select_target(os: os) expect(subject.target.os).to eq(os) end end context "when given the os_version: keyword argument" do let(:os_version) { '10.13' } it "must find the target in .targets with the matching #os_version" do subject.select_target(os_version: os_version) expect(subject.target.os_version).to eq(os_version) end end context "when given the software: keyword argument" do let(:software) { 'nginx' } it "must find the target in .targets with the matching #software" do subject.select_target(software: software) expect(subject.target.software).to eq(software) end end context "when given the version: keyword argument" do let(:version) { '1.22.0' } it "must find the target in .targets with the matching #version" do subject.select_target(version: version) expect(subject.target.version).to eq(version) end end context "when given multiple keyword arguments" do let(:arch) { :arm } let(:os) { :macos } let(:os_version) { '10.13' } let(:software) { 'nginx' } let(:version) { '1.22.0' } it "must find the target in .targets which matches all given values" do subject.select_target( arch: arch, os: os, os_version: os_version, software: software, version: version ) expect(subject.target.arch).to eq(arch) expect(subject.target.os).to eq(os) expect(subject.target.os_version).to eq(os_version) expect(subject.target.software).to eq(software) expect(subject.target.version).to eq(version) end end context "but when no matching target could be found in .targets" do it do expect { subject.select_target(arch: :foo) }.to raise_error(described_class::NoMatchingTarget,"could not find any matching targets") end end end describe "#arch" do context "when a target has been set" do subject do test_class.new(target: {arch: :arm}) end it "must return the #target's #arch" do expect(subject.arch).to eq(subject.target.arch) end end context "when no target has been set" do subject { test_class.new } it "must return nil" do expect(subject.arch).to be(nil) end end end describe "#os" do context "when a target has been set" do subject do test_class.new(target: {os: :macos}) end it "must return the #target's #os" do expect(subject.os).to eq(subject.target.os) end end context "when no target has been set" do subject { test_class.new } it "must return nil" do expect(subject.os).to be(nil) end end end describe "#os_version" do context "when a target has been set" do subject do test_class.new(target: {os_version: '10.13'}) end it "must return the #target's #os_version" do expect(subject.os_version).to eq(subject.target.os_version) end end context "when no target has been set" do subject { test_class.new } it "must return nil" do expect(subject.os_version).to be(nil) end end end describe "#software" do context "when a target has been set" do subject do test_class.new(target: {software: 'nginx'}) end it "must return the #target's #software" do expect(subject.software).to eq(subject.target.software) end end context "when no target has been set" do subject { test_class.new } it "must return nil" do expect(subject.software).to be(nil) end end end describe "#version" do context "when a target has been set" do subject do test_class.new(target: {version: '1.22.0'}) end it "must return the #target's #version" do expect(subject.version).to eq(subject.target.version) end end context "when no target has been set" do subject { test_class.new } it "must return nil" do expect(subject.version).to be(nil) end end end end