From 36b41e1aef642808df439879766c0ff4d9bd615e Mon Sep 17 00:00:00 2001 From: zinduolis Date: Mon, 25 May 2026 15:08:14 +1000 Subject: [PATCH] FIX: revert accidental PDF mutation; stub File.open in Hook_default_browser pre_send; consolidate BRANCH_COVERAGE into BeefTestConfig; replace 'next unless' guards with describe-level conditionals; per-example stub_const for extension constants; correct has_valid_browser_details_chars? assertions --- .gitignore | 6 + .rspec | 3 +- Rakefile | 4 +- .../bounce_to_ie_configured.pdf | 2 +- spec/README.md | 28 +- spec/beef/core/filter/base_spec.rb | 12 +- spec/beef/modules/browser/browser_spec.rb | 116 ++++---- .../chrome_extensions_spec.rb | 69 +++-- spec/beef/modules/debug/debug_spec.rb | 75 +++-- spec/beef/modules/exploits/exploits_spec.rb | 134 +++++---- spec/beef/modules/host/host_spec.rb | 198 +++++++------ spec/beef/modules/ipec/ipec_spec.rb | 143 +++++----- spec/beef/modules/misc/misc_spec.rb | 217 +++++++-------- spec/beef/modules/network/network_spec.rb | 260 ++++++++---------- .../modules/persistence/persistence_spec.rb | 79 +++--- spec/beef/modules/phonegap/phonegap_spec.rb | 71 ++--- .../social_engineering_spec.rb | 133 +++++---- spec/spec_helper.rb | 106 ++++++- spec/support/module_spec_helper.rb | 3 +- 19 files changed, 886 insertions(+), 773 deletions(-) diff --git a/.gitignore b/.gitignore index 64282a1c5..c261e2701 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,12 @@ beef.log test/msf-test extensions/admin_ui/media/javascript-min/ custom-config.yaml + +# Generated at runtime by Hook_default_browser#pre_send +modules/host/hook_default_browser/bounce_to_ie_configured.pdf + +# Generated at runtime by browser/spyder_eye#post_execute (and possibly other capture modules) +screenshot_*.png .DS_Store .gitignore .rvmrc diff --git a/.rspec b/.rspec index 9e7a5ec96..7fe3cb532 100644 --- a/.rspec +++ b/.rspec @@ -2,6 +2,5 @@ --color --require spec_helper -I . ---order defined --tag ~run_on_browserstack ---tag ~run_on_long_tests \ No newline at end of file +--tag ~run_on_long_tests diff --git a/Rakefile b/Rakefile index 2709cd268..9b25479d6 100644 --- a/Rakefile +++ b/Rakefile @@ -8,13 +8,13 @@ require 'rspec/core/rake_task' task default: ['short'] # Run rspec with an explicit file list (avoids envs that only run 557). -# Note: when run with all 81 files, module specs load after extensions; the Dns stub in +# Note: when run with the full file list, module specs load after extensions; the Dns stub in # network_spec must not run before the real Dns extension (dns_spec) or you get "superclass mismatch". desc 'Run short spec suite (all specs except browserstack/long)' task :short do short_files = Dir[File.join(Dir.pwd, 'spec', '**', '*_spec.rb')].sort $stderr.puts "[rake short] spec files=#{short_files.size}" - abort '[rake short] Expected 81+ spec files; check you are in project root.' if short_files.size < 80 + abort '[rake short] Expected 60+ spec files; check you are in project root.' if short_files.size < 60 opts = [ '--tag', '~run_on_browserstack', '--tag', '~run_on_long_tests' diff --git a/modules/host/hook_default_browser/bounce_to_ie_configured.pdf b/modules/host/hook_default_browser/bounce_to_ie_configured.pdf index 372b6e4d7..21549a4b0 100644 --- a/modules/host/hook_default_browser/bounce_to_ie_configured.pdf +++ b/modules/host/hook_default_browser/bounce_to_ie_configured.pdf @@ -46,7 +46,7 @@ endobj << /Length 1708 >>stream -app.launchURL("http://127.0.0.1:127.0.0.1/demos/report.html",true); +app.launchURL("http://0.0.0.0:3000/demos/report.html",true); endstream endobj 7 0 obj diff --git a/spec/README.md b/spec/README.md index f6b1bbdee..2a524a6cd 100644 --- a/spec/README.md +++ b/spec/README.md @@ -78,9 +78,13 @@ bundle exec rake coverage_complete ## Key Improvements -### ✅ **Eliminated Global Constants** -- Replaced `BRANCH_COVERAGE` constants with centralized `BeefTestConfig` module -- No more "already initialized constant" warnings +### ✅ **Eliminated Global Constant Collisions** +- Branch-coverage data for the dynamic module spec loaders lives in + `BeefTestConfig.branch_coverage_for(:component)` (in `spec/spec_helper.rb`). +- Each module spec file pulls its own component (`:browser`, `:exploits`, `:host`, + `:misc`, `:network`, `:social_engineering`) into a uniquely-named local + constant (e.g. `BRANCH_COVERAGE_HOST`), so loading the full suite no longer + triggers "already initialized constant" warnings or silently overwrites data. ### ✅ **Simplified Coverage Logic** - Cleaner filtering using `track_files` instead of complex `add_filter` logic @@ -101,6 +105,24 @@ bundle exec rake coverage_complete - Follows RSpec best practices - Better separation of concerns +## Module Spec Pattern (dynamic loaders) + +Specs under `spec/beef/modules/**/_spec.rb` walk every +`modules//**/module.rb` file and generate a `RSpec.describe` block per +class. They cover: + +- `.options` returns an `Array` (when defined) +- `#pre_send` runs without raising (when defined) +- `#post_execute` runs without raising (when defined) +- An extra `#post_execute` example with a realistic datastore for any module + listed in `BeefTestConfig.branch_coverage_for(:category)` + +Trade-off: this is a coverage-driving baseline. The `expect { ... }.not_to +raise_error` assertion catches load failures, missing constants, nil crashes, +and undefined methods, but it does not assert behavioural correctness. Add +targeted assertions for high-value modules (see the `Wordpress_add_user` and +`Test_get_variable` examples in `spec/beef/modules/misc/misc_spec.rb`). + ## Coverage Focus Areas - **core**: Framework core functionality diff --git a/spec/beef/core/filter/base_spec.rb b/spec/beef/core/filter/base_spec.rb index 04275ecc8..d818bf9e4 100644 --- a/spec/beef/core/filter/base_spec.rb +++ b/spec/beef/core/filter/base_spec.rb @@ -400,14 +400,14 @@ RSpec.describe BeEF::Filters do expect(BeEF::Filters.has_valid_browser_details_chars?('')).to be(false) end - it 'returns false when string only has allowed chars' do - # Method returns true when regex matches (invalid char found); false when only valid chars - expect(BeEF::Filters.has_valid_browser_details_chars?('abc')).to be(false) - expect(BeEF::Filters.has_valid_browser_details_chars?('a-b (c)')).to be(false) + it 'returns true when string only has allowed chars' do + # Method returns nil-on-no-match: nil.nil? == true, so a fully-valid string returns true. + expect(BeEF::Filters.has_valid_browser_details_chars?('abc')).to be(true) + expect(BeEF::Filters.has_valid_browser_details_chars?('a-b (c)')).to be(true) end - it 'returns true when string contains disallowed character' do - expect(BeEF::Filters.has_valid_browser_details_chars?('ab@c')).to be(true) + it 'returns false when string contains disallowed character' do + expect(BeEF::Filters.has_valid_browser_details_chars?('ab@c')).to be(false) end end diff --git a/spec/beef/modules/browser/browser_spec.rb b/spec/beef/modules/browser/browser_spec.rb index 319457051..df19163c2 100644 --- a/spec/beef/modules/browser/browser_spec.rb +++ b/spec/beef/modules/browser/browser_spec.rb @@ -12,8 +12,8 @@ require_relative '../../../spec_helper' project_root = File.expand_path('../../../../', __dir__) browser_module_paths = Dir[File.join(project_root, 'modules/browser/**/module.rb')].sort -# Load branch coverage data from centralized config -BRANCH_COVERAGE = BeefTestConfig.branch_coverage_for(:browser) +# Load branch coverage data from centralised config +BRANCH_COVERAGE_BROWSER = BeefTestConfig.branch_coverage_for(:browser) browser_module_paths.each do |path| rel = path.sub("#{project_root}/", '').sub(/\.rb$/, '') @@ -27,69 +27,75 @@ browser_module_paths.each do |path| mod = Object.const_get(klass_name) RSpec.describe mod do - describe '.options' do - it 'returns an Array when defined' do - next unless described_class.respond_to?(:options) + if described_class.respond_to?(:options) + describe '.options' do + it 'returns an Array' do + config = instance_double(BeEF::Core::Configuration) + allow(config).to receive(:beef_host).and_return('127.0.0.1') + allow(config).to receive(:beef_proto).and_return('http') + allow(config).to receive(:beef_port).and_return('3000') + allow(config).to receive(:beef_url_str).and_return('http://127.0.0.1:3000') + allow(config).to receive(:get).with(anything).and_return('127.0.0.1') + allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - config = instance_double(BeEF::Core::Configuration) - allow(config).to receive(:beef_host).and_return('127.0.0.1') - allow(config).to receive(:beef_proto).and_return('http') - allow(config).to receive(:beef_port).and_return('3000') - allow(config).to receive(:beef_url_str).and_return('http://127.0.0.1:3000') - allow(config).to receive(:get).with(anything).and_return('127.0.0.1') - allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - - opts = described_class.options - expect(opts).to be_an(Array) + expect(described_class.options).to be_an(Array) + end end end - describe '#pre_send' do - it 'runs without error when defined' do - next unless described_class.method_defined?(:pre_send) - - handler = instance_double('AssetHandler') - allow(handler).to receive(:unbind).and_return(nil) - allow(handler).to receive(:bind).and_return(nil) - allow(handler).to receive(:bind_raw).and_return(nil) - allow(handler).to receive(:remap).and_return(nil) - allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) - config = instance_double(BeEF::Core::Configuration) - allow(config).to receive(:get).with(anything).and_return('127.0.0.1') - allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - instance = build_command_instance(described_class, 'result' => '', 'results' => '', 'cid' => '0') - expect { run_pre_send(instance) }.not_to raise_error + if described_class.method_defined?(:pre_send) + describe '#pre_send' do + it 'runs without error' do + handler = instance_double('AssetHandler') + allow(handler).to receive(:unbind).and_return(nil) + allow(handler).to receive(:bind).and_return(nil) + allow(handler).to receive(:bind_raw).and_return(nil) + allow(handler).to receive(:remap).and_return(nil) + allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) + config = instance_double(BeEF::Core::Configuration) + allow(config).to receive(:get).with(anything).and_return('127.0.0.1') + allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) + instance = build_command_instance(described_class, 'result' => '', 'results' => '', 'cid' => '0') + expect { run_pre_send(instance) }.not_to raise_error + end end end - describe '#post_execute' do - it 'runs without error when defined' do - next unless described_class.method_defined?(:post_execute) + if described_class.method_defined?(:post_execute) + describe '#post_execute' do + it 'runs without error' do + handler = instance_double('AssetHandler') + allow(handler).to receive(:unbind) + allow(handler).to receive(:bind) + allow(handler).to receive(:remap) + allow(handler).to receive(:bind_raw) + allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) + # Some modules (e.g. Spyder_eye) write screenshot files to $home_dir on the success path. + # Stub File.open with a block-aware double so the test never touches the real filesystem. + file_double = double('File').as_null_object + allow(File).to receive(:open) do |*_args, &block| + block ? block.call(file_double) : file_double + end - handler = instance_double('AssetHandler') - allow(handler).to receive(:unbind) - allow(handler).to receive(:bind) - allow(handler).to receive(:remap) - allow(handler).to receive(:bind_raw) - allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) + instance = build_command_instance(described_class, 'result' => '', 'results' => '', 'cid' => '0') + expect { run_post_execute(instance) }.not_to raise_error + end - instance = build_command_instance(described_class, 'result' => '', 'results' => '', 'cid' => '0') - expect { run_post_execute(instance) }.not_to raise_error - end + if BRANCH_COVERAGE_BROWSER[branch_key] + it 'runs branch path with realistic datastore' do + branch = BRANCH_COVERAGE_BROWSER[branch_key] - it 'runs branch path when in BRANCH_COVERAGE' do - branch = BRANCH_COVERAGE[branch_key] - next unless described_class.method_defined?(:post_execute) && branch - - handler = instance_double('AssetHandler') - allow(handler).to receive(:unbind) - allow(handler).to receive(:bind) - allow(handler).to receive(:remap) - allow(handler).to receive(:bind_raw) - allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) - allow(BeEF::Core::Models::BrowserDetails).to receive(:set) - instance = build_command_instance(described_class, branch[:datastore]) - expect { run_post_execute(instance) }.not_to raise_error + handler = instance_double('AssetHandler') + allow(handler).to receive(:unbind) + allow(handler).to receive(:bind) + allow(handler).to receive(:remap) + allow(handler).to receive(:bind_raw) + allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) + allow(BeEF::Core::Models::BrowserDetails).to receive(:set) + instance = build_command_instance(described_class, branch[:datastore]) + expect { run_post_execute(instance) }.not_to raise_error + end + end end end end diff --git a/spec/beef/modules/chrome_extensions/chrome_extensions_spec.rb b/spec/beef/modules/chrome_extensions/chrome_extensions_spec.rb index f6a74c149..9f19b73b2 100644 --- a/spec/beef/modules/chrome_extensions/chrome_extensions_spec.rb +++ b/spec/beef/modules/chrome_extensions/chrome_extensions_spec.rb @@ -23,49 +23,48 @@ chrome_module_paths.each do |path| mod = Object.const_get(klass_name) RSpec.describe mod do - describe '.options' do - it 'returns an Array when defined' do - next unless described_class.respond_to?(:options) + if described_class.respond_to?(:options) + describe '.options' do + it 'returns an Array' do + config = instance_double(BeEF::Core::Configuration) + allow(config).to receive(:beef_host).and_return('127.0.0.1') + allow(config).to receive(:get).with(anything).and_return('127.0.0.1') + allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - config = instance_double(BeEF::Core::Configuration) - allow(config).to receive(:beef_host).and_return('127.0.0.1') - allow(config).to receive(:get).with(anything).and_return('127.0.0.1') - allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - - opts = described_class.options - expect(opts).to be_an(Array) + expect(described_class.options).to be_an(Array) + end end end - describe '#pre_send' do - it 'runs without error when defined' do - next unless described_class.method_defined?(:pre_send) - - handler = instance_double('AssetHandler') - allow(handler).to receive(:unbind).and_return(nil) - allow(handler).to receive(:bind).and_return(nil) - allow(handler).to receive(:bind_raw).and_return(nil) - allow(handler).to receive(:remap).and_return(nil) - allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) - config = instance_double(BeEF::Core::Configuration) - allow(config).to receive(:get).with(anything).and_return('127.0.0.1') - allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - instance = build_command_instance(described_class, 'return' => '', 'result' => '', 'cid' => '0') - expect { run_pre_send(instance) }.not_to raise_error + if described_class.method_defined?(:pre_send) + describe '#pre_send' do + it 'runs without error' do + handler = instance_double('AssetHandler') + allow(handler).to receive(:unbind).and_return(nil) + allow(handler).to receive(:bind).and_return(nil) + allow(handler).to receive(:bind_raw).and_return(nil) + allow(handler).to receive(:remap).and_return(nil) + allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) + config = instance_double(BeEF::Core::Configuration) + allow(config).to receive(:get).with(anything).and_return('127.0.0.1') + allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) + instance = build_command_instance(described_class, 'return' => '', 'result' => '', 'cid' => '0') + expect { run_pre_send(instance) }.not_to raise_error + end end end - describe '#post_execute' do - it 'runs without error when defined' do - next unless described_class.method_defined?(:post_execute) + if described_class.method_defined?(:post_execute) + describe '#post_execute' do + it 'runs without error' do + handler = instance_double('AssetHandler') + allow(handler).to receive(:unbind) + allow(handler).to receive(:bind) + allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) - handler = instance_double('AssetHandler') - allow(handler).to receive(:unbind) - allow(handler).to receive(:bind) - allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) - - instance = build_command_instance(described_class, 'return' => '', 'result' => '', 'cid' => '0') - expect { run_post_execute(instance) }.not_to raise_error + instance = build_command_instance(described_class, 'return' => '', 'result' => '', 'cid' => '0') + expect { run_post_execute(instance) }.not_to raise_error + end end end end diff --git a/spec/beef/modules/debug/debug_spec.rb b/spec/beef/modules/debug/debug_spec.rb index 722d8e7d9..003481c50 100644 --- a/spec/beef/modules/debug/debug_spec.rb +++ b/spec/beef/modules/debug/debug_spec.rb @@ -24,52 +24,51 @@ debug_module_paths.each do |path| mod = Object.const_get(klass_name) RSpec.describe mod do - describe '.options' do - it 'returns an Array when defined' do - next unless described_class.respond_to?(:options) + if described_class.respond_to?(:options) + describe '.options' do + it 'returns an Array' do + config = instance_double(BeEF::Core::Configuration) + allow(config).to receive(:beef_host).and_return('127.0.0.1') + allow(config).to receive(:beef_port).and_return('3000') + allow(config).to receive(:beef_proto).and_return('http') + allow(config).to receive(:get).with(anything).and_return('127.0.0.1') + allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - config = instance_double(BeEF::Core::Configuration) - allow(config).to receive(:beef_host).and_return('127.0.0.1') - allow(config).to receive(:beef_port).and_return('3000') - allow(config).to receive(:beef_proto).and_return('http') - allow(config).to receive(:get).with(anything).and_return('127.0.0.1') - allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - - opts = described_class.options - expect(opts).to be_an(Array) + expect(described_class.options).to be_an(Array) + end end end - describe '#pre_send' do - it 'runs without error when defined' do - next unless described_class.method_defined?(:pre_send) - - handler = instance_double('AssetHandler') - allow(handler).to receive(:unbind).and_return(nil) - allow(handler).to receive(:bind).and_return(nil) - allow(handler).to receive(:bind_raw).and_return(nil) - allow(handler).to receive(:bind_redirect).with(anything, anything).and_return(nil) - allow(handler).to receive(:remap).and_return(nil) - allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) - config = instance_double(BeEF::Core::Configuration) - allow(config).to receive(:get).with(anything).and_return('127.0.0.1') - allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - instance = build_command_instance(described_class, 'result' => '', 'results' => '', 'cid' => '0') - expect { run_pre_send(instance) }.not_to raise_error + if described_class.method_defined?(:pre_send) + describe '#pre_send' do + it 'runs without error' do + handler = instance_double('AssetHandler') + allow(handler).to receive(:unbind).and_return(nil) + allow(handler).to receive(:bind).and_return(nil) + allow(handler).to receive(:bind_raw).and_return(nil) + allow(handler).to receive(:bind_redirect).with(anything, anything).and_return(nil) + allow(handler).to receive(:remap).and_return(nil) + allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) + config = instance_double(BeEF::Core::Configuration) + allow(config).to receive(:get).with(anything).and_return('127.0.0.1') + allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) + instance = build_command_instance(described_class, 'result' => '', 'results' => '', 'cid' => '0') + expect { run_pre_send(instance) }.not_to raise_error + end end end - describe '#post_execute' do - it 'runs without error when defined' do - next unless described_class.method_defined?(:post_execute) + if described_class.method_defined?(:post_execute) + describe '#post_execute' do + it 'runs without error' do + handler = instance_double('AssetHandler') + allow(handler).to receive(:unbind) + allow(handler).to receive(:bind) + allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) - handler = instance_double('AssetHandler') - allow(handler).to receive(:unbind) - allow(handler).to receive(:bind) - allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) - - instance = build_command_instance(described_class, 'result' => '', 'results' => '', 'cid' => '0') - expect { run_post_execute(instance) }.not_to raise_error + instance = build_command_instance(described_class, 'result' => '', 'results' => '', 'cid' => '0') + expect { run_post_execute(instance) }.not_to raise_error + end end end end diff --git a/spec/beef/modules/exploits/exploits_spec.rb b/spec/beef/modules/exploits/exploits_spec.rb index b94953986..2690be841 100644 --- a/spec/beef/modules/exploits/exploits_spec.rb +++ b/spec/beef/modules/exploits/exploits_spec.rb @@ -4,7 +4,8 @@ # See the file 'doc/COPYING' for copying permission # # Unit tests for every module under modules/exploits/. -# Branch coverage: extra post_execute for modules with conditional branches. +# Branch coverage: extra post_execute for modules with conditional branches +# (driven by BeefTestConfig.branch_coverage_for(:exploits) in spec_helper.rb). # require_relative '../../../spec_helper' @@ -12,16 +13,7 @@ require_relative '../../../spec_helper' project_root = File.expand_path('../../../../', __dir__) exploit_module_paths = Dir[File.join(project_root, 'modules/exploits/**/module.rb')].sort -BRANCH_COVERAGE = { - 'modules/exploits/vtiger_crm_upload_exploit' => { datastore: { 'result' => 'ok', 'cid' => '0' } }, - 'modules/exploits/router/asus_rt_n12e_get_info' => { - datastore: { - 'result' => '', 'results' => 'ip=192.168.1.1&clients=192.168.1.2,00:11:22:33:44:55&wanip=1.2.3.4&netmask=255.255.255.0&gateway=192.168.1.1&dns=8.8.8.8 8.8.4.4', - 'beefhook' => '1', 'cid' => '0' - }, - config_get: { 'beef.extension.network.enable' => true } - } -}.freeze +BRANCH_COVERAGE_EXPLOITS = BeefTestConfig.branch_coverage_for(:exploits) exploit_module_paths.each do |path| rel = path.sub("#{project_root}/", '').sub(/\.rb$/, '') @@ -35,77 +27,83 @@ exploit_module_paths.each do |path| mod = Object.const_get(klass_name) RSpec.describe mod do - describe '.options' do - it 'returns an Array when defined' do - next unless described_class.respond_to?(:options) + if described_class.respond_to?(:options) + describe '.options' do + it 'returns an Array' do + config = instance_double(BeEF::Core::Configuration) + allow(config).to receive(:beef_host).and_return('127.0.0.1') + allow(config).to receive(:get).with(anything).and_return('127.0.0.1') + allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - config = instance_double(BeEF::Core::Configuration) - allow(config).to receive(:beef_host).and_return('127.0.0.1') - allow(config).to receive(:get).with(anything).and_return('127.0.0.1') - allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - - opts = described_class.options - expect(opts).to be_an(Array) + expect(described_class.options).to be_an(Array) + end end end - describe '#pre_send' do - it 'runs without error when defined' do - next unless described_class.method_defined?(:pre_send) - - handler = instance_double('AssetHandler') - allow(handler).to receive(:unbind).and_return(nil) - allow(handler).to receive(:bind).and_return(nil) - allow(handler).to receive(:bind_raw).and_return(nil) - allow(handler).to receive(:remap).and_return(nil) - allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) - config = instance_double(BeEF::Core::Configuration) - allow(config).to receive(:get).with(anything).and_return('127.0.0.1') - allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - instance = build_command_instance(described_class, {}) - expect { run_pre_send(instance) }.not_to raise_error + if described_class.method_defined?(:pre_send) + describe '#pre_send' do + it 'runs without error' do + handler = instance_double('AssetHandler') + allow(handler).to receive(:unbind).and_return(nil) + allow(handler).to receive(:bind).and_return(nil) + allow(handler).to receive(:bind_raw).and_return(nil) + allow(handler).to receive(:remap).and_return(nil) + allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) + config = instance_double(BeEF::Core::Configuration) + allow(config).to receive(:get).with(anything).and_return('127.0.0.1') + allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) + instance = build_command_instance(described_class, {}) + expect { run_pre_send(instance) }.not_to raise_error + end end end - describe '#post_execute' do - it 'runs without error when defined' do - next unless described_class.method_defined?(:post_execute) - - handler = instance_double('AssetHandler') - allow(handler).to receive(:unbind) - allow(handler).to receive(:bind) - allow(handler).to receive(:remap) - allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) - - instance = build_command_instance(described_class, {}) - expect { run_post_execute(instance) }.not_to raise_error - end - - it 'runs branch path when in BRANCH_COVERAGE' do - raw = BRANCH_COVERAGE[branch_key] - next unless described_class.method_defined?(:post_execute) && raw - - branches = raw.is_a?(Array) ? raw : [raw] - branches.each do |branch| + if described_class.method_defined?(:post_execute) + describe '#post_execute' do + it 'runs without error' do handler = instance_double('AssetHandler') allow(handler).to receive(:unbind) allow(handler).to receive(:bind) allow(handler).to receive(:remap) allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) - allow(BeEF::Core::Models::NetworkService).to receive(:create) - allow(BeEF::Core::Models::NetworkHost).to receive(:create) - allow(BeEF::Filters).to receive(:is_valid_ip?).and_return(true) - if branch[:config_get] - config = instance_double(BeEF::Core::Configuration) - allow(config).to receive(:beef_host).and_return('127.0.0.1') - allow(config).to receive(:get).with(anything) do |key| - branch[:config_get].key?(key) ? branch[:config_get][key] : '127.0.0.1' - end - allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - end - instance = build_command_instance(described_class, branch[:datastore]) + + instance = build_command_instance(described_class, {}) expect { run_post_execute(instance) }.not_to raise_error end + + if BRANCH_COVERAGE_EXPLOITS[branch_key] + it 'runs branch path with realistic datastore' do + raw = BRANCH_COVERAGE_EXPLOITS[branch_key] + branches = raw.is_a?(Array) ? raw : [raw] + + # NetworkService / NetworkHost belong to the network extension and may not be + # loaded during a modules-only spec run. Provide class doubles per-example + # so `allow(...).to receive(:create)` resolves regardless. + stub_const('BeEF::Core::Models::NetworkService', Class.new { def self.create(*); end }) unless defined?(BeEF::Core::Models::NetworkService) + stub_const('BeEF::Core::Models::NetworkHost', Class.new { def self.create(*); end }) unless defined?(BeEF::Core::Models::NetworkHost) + + branches.each do |branch| + handler = instance_double('AssetHandler') + allow(handler).to receive(:unbind) + allow(handler).to receive(:bind) + allow(handler).to receive(:remap) + allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) + allow(BeEF::Core::Models::NetworkService).to receive(:create) + allow(BeEF::Core::Models::NetworkHost).to receive(:create) + allow(BeEF::Filters).to receive(:is_valid_ip?).and_return(true) + if branch[:config_get] + config = instance_double(BeEF::Core::Configuration) + allow(config).to receive(:beef_host).and_return('127.0.0.1') + allow(config).to receive(:get).with(anything) do |key| + branch[:config_get].key?(key) ? branch[:config_get][key] : '127.0.0.1' + end + allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) + end + instance = build_command_instance(described_class, branch[:datastore]) + expect { run_post_execute(instance) }.not_to raise_error + end + end + end end end end diff --git a/spec/beef/modules/host/host_spec.rb b/spec/beef/modules/host/host_spec.rb index f75dc71e7..9b865315e 100644 --- a/spec/beef/modules/host/host_spec.rb +++ b/spec/beef/modules/host/host_spec.rb @@ -5,7 +5,8 @@ # # Unit tests for every module under modules/host/. # Each directory is tested for .options (when present), #pre_send, and #post_execute. -# Branch coverage: extra post_execute runs for modules listed in BRANCH_COVERAGE. +# Branch coverage: extra post_execute runs for modules listed in +# BeefTestConfig.branch_coverage_for(:host) (defined in spec_helper.rb). # require_relative '../../../spec_helper' @@ -13,25 +14,7 @@ require_relative '../../../spec_helper' project_root = File.expand_path('../../../../', __dir__) host_module_paths = Dir[File.join(project_root, 'modules/host/**/module.rb')].sort -# Per-module datastore + config stubs to hit conditional branches in post_execute (e.g. NetworkService/NetworkHost path). -BRANCH_COVERAGE = { - 'modules/host/detect_cups' => { - datastore: { 'results' => 'proto=http&ip=1.2.3.4&port=631&cups=Installed', 'beefhook' => '1', 'result' => '', 'cid' => '0' }, - config_get: { 'beef.extension.network.enable' => true } - }, - 'modules/host/detect_airdroid' => { - datastore: { 'results' => 'proto=http&ip=1.2.3.4&port=8888&airdroid=Installed', 'airdroid' => '', 'beefhook' => '1', 'result' => '', 'cid' => '0' }, - config_get: { 'beef.extension.network.enable' => true } - }, - 'modules/host/get_internal_ip_java' => { - datastore: { 'results' => '192.168.1.1', 'Result' => '', 'result' => '', 'beefhook' => '1', 'cid' => '0' }, - config_get: { 'beef.extension.network.enable' => true } - }, - 'modules/host/get_internal_ip_webrtc' => { - datastore: { 'results' => 'IP is 192.168.1.1', 'Result' => '', 'result' => '', 'beefhook' => '1', 'cid' => '0' }, - config_get: { 'beef.extension.network.enable' => true } - } -}.freeze +BRANCH_COVERAGE_HOST = BeefTestConfig.branch_coverage_for(:host) host_module_paths.each do |path| rel = path.sub("#{project_root}/", '').sub(/\.rb$/, '') @@ -45,90 +28,103 @@ host_module_paths.each do |path| mod = Object.const_get(klass_name) RSpec.describe mod do - describe '.options' do - it 'returns an Array when defined' do - next unless described_class.respond_to?(:options) + if described_class.respond_to?(:options) + describe '.options' do + it 'returns an Array' do + config = instance_double(BeEF::Core::Configuration) + allow(config).to receive(:beef_host).and_return('127.0.0.1') + allow(config).to receive(:beef_url_str).and_return('http://127.0.0.1:3000') + allow(config).to receive(:get).with(anything).and_return('127.0.0.1') + allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - config = instance_double(BeEF::Core::Configuration) - allow(config).to receive(:beef_host).and_return('127.0.0.1') - allow(config).to receive(:beef_url_str).and_return('http://127.0.0.1:3000') - allow(config).to receive(:get).with(anything).and_return('127.0.0.1') - allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - - opts = described_class.options - expect(opts).to be_an(Array) - end - end - - describe '#pre_send' do - it 'runs without error when defined' do - next unless described_class.method_defined?(:pre_send) - - handler = instance_double('AssetHandler') - allow(handler).to receive(:unbind).and_return(nil) - allow(handler).to receive(:bind).and_return(nil) - allow(handler).to receive(:bind_raw).and_return(nil) - allow(handler).to receive(:remap).and_return(nil) - allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) - config = instance_double(BeEF::Core::Configuration) - allow(config).to receive(:get).with(anything).and_return('127.0.0.1') - allow(config).to receive(:beef_host).and_return('127.0.0.1') - allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - instance = build_command_instance(described_class, 'result' => '', 'results' => '', 'cid' => '0') - expect { run_pre_send(instance) }.not_to raise_error - end - end - - describe '#post_execute' do - it 'runs without error when defined' do - next unless described_class.method_defined?(:post_execute) - - handler = instance_double('AssetHandler') - allow(handler).to receive(:unbind) - allow(handler).to receive(:bind) - allow(handler).to receive(:remap) - allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) - - file_double = double('File', write: nil, close: nil) - allow(File).to receive(:open).and_return(file_double) - allow(BeEF::Core::Models::Command).to receive(:save_result) - allow_any_instance_of(described_class).to receive(:ip).and_return('0.0.0.0') - allow_any_instance_of(described_class).to receive(:timestamp).and_return('0') - - # Minimal datastore so modules that call .sub/.to_s on keys don't get nil - instance = build_command_instance(described_class, 'result' => '', 'results' => '', 'cid' => '0') - expect { run_post_execute(instance) }.not_to raise_error - end - - it 'runs branch path when in BRANCH_COVERAGE' do - branch = BRANCH_COVERAGE[branch_key] - next unless described_class.method_defined?(:post_execute) && branch - - handler = instance_double('AssetHandler') - allow(handler).to receive(:unbind) - allow(handler).to receive(:bind) - allow(handler).to receive(:remap) - allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) - file_double = double('File', write: nil, close: nil) - allow(File).to receive(:open).and_return(file_double) - allow(BeEF::Core::Models::Command).to receive(:save_result) - allow_any_instance_of(described_class).to receive(:ip).and_return('0.0.0.0') - allow_any_instance_of(described_class).to receive(:timestamp).and_return('0') - allow(BeEF::Core::Models::NetworkService).to receive(:create) - allow(BeEF::Core::Models::NetworkHost).to receive(:create) - allow(BeEF::Core::Models::BrowserDetails).to receive(:get).and_return('Linux') - allow(BeEF::Filters).to receive(:is_valid_ip?).and_return(true) - - config = instance_double(BeEF::Core::Configuration) - allow(config).to receive(:beef_host).and_return('127.0.0.1') - allow(config).to receive(:beef_url_str).and_return('http://127.0.0.1:3000') - allow(config).to receive(:get).with(anything) do |key| - branch[:config_get].key?(key) ? branch[:config_get][key] : '127.0.0.1' + expect(described_class.options).to be_an(Array) end - allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) + end + end - instance = build_command_instance(described_class, branch[:datastore]) - expect { run_post_execute(instance) }.not_to raise_error + if described_class.method_defined?(:pre_send) + describe '#pre_send' do + it 'runs without error' do + handler = instance_double('AssetHandler') + allow(handler).to receive(:unbind).and_return(nil) + allow(handler).to receive(:bind).and_return(nil) + allow(handler).to receive(:bind_raw).and_return(nil) + allow(handler).to receive(:remap).and_return(nil) + allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) + config = instance_double(BeEF::Core::Configuration) + allow(config).to receive(:get).with(anything).and_return('127.0.0.1') + allow(config).to receive(:beef_host).and_return('127.0.0.1') + allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) + # Stub File.open so Hook_default_browser#pre_send doesn't read/write the real + # bounce_to_ie_configured.pdf in modules/host/hook_default_browser/. + # The source calls File.open(path, 'w') without a block and File.open(path, 'r') { |f| ... } with one. + file_double = double('File').as_null_object + allow(File).to receive(:open) do |*_args, &block| + block ? block.call(file_double) : file_double + end + instance = build_command_instance(described_class, 'result' => '', 'results' => '', 'cid' => '0') + expect { run_pre_send(instance) }.not_to raise_error + end + end + end + + if described_class.method_defined?(:post_execute) + describe '#post_execute' do + it 'runs without error' do + handler = instance_double('AssetHandler') + allow(handler).to receive(:unbind) + allow(handler).to receive(:bind) + allow(handler).to receive(:remap) + allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) + + file_double = double('File').as_null_object + allow(File).to receive(:open).and_return(file_double) + allow(BeEF::Core::Models::Command).to receive(:save_result) + allow_any_instance_of(described_class).to receive(:ip).and_return('0.0.0.0') + allow_any_instance_of(described_class).to receive(:timestamp).and_return('0') + + # Minimal datastore so modules that call .sub/.to_s on keys don't get nil + instance = build_command_instance(described_class, 'result' => '', 'results' => '', 'cid' => '0') + expect { run_post_execute(instance) }.not_to raise_error + end + + if BRANCH_COVERAGE_HOST[branch_key] + it 'runs branch path with realistic datastore' do + branch = BRANCH_COVERAGE_HOST[branch_key] + + # NetworkService / NetworkHost belong to the network extension and may not be + # loaded during a modules-only spec run. Provide class doubles per-example + # so `allow(...).to receive(:create)` resolves regardless. + stub_const('BeEF::Core::Models::NetworkService', Class.new { def self.create(*); end }) unless defined?(BeEF::Core::Models::NetworkService) + stub_const('BeEF::Core::Models::NetworkHost', Class.new { def self.create(*); end }) unless defined?(BeEF::Core::Models::NetworkHost) + + handler = instance_double('AssetHandler') + allow(handler).to receive(:unbind) + allow(handler).to receive(:bind) + allow(handler).to receive(:remap) + allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) + file_double = double('File').as_null_object + allow(File).to receive(:open).and_return(file_double) + allow(BeEF::Core::Models::Command).to receive(:save_result) + allow_any_instance_of(described_class).to receive(:ip).and_return('0.0.0.0') + allow_any_instance_of(described_class).to receive(:timestamp).and_return('0') + allow(BeEF::Core::Models::NetworkService).to receive(:create) + allow(BeEF::Core::Models::NetworkHost).to receive(:create) + allow(BeEF::Core::Models::BrowserDetails).to receive(:get).and_return('Linux') + allow(BeEF::Filters).to receive(:is_valid_ip?).and_return(true) + + config = instance_double(BeEF::Core::Configuration) + allow(config).to receive(:beef_host).and_return('127.0.0.1') + allow(config).to receive(:beef_url_str).and_return('http://127.0.0.1:3000') + allow(config).to receive(:get).with(anything) do |key| + branch[:config_get].key?(key) ? branch[:config_get][key] : '127.0.0.1' + end + allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) + + instance = build_command_instance(described_class, branch[:datastore]) + expect { run_post_execute(instance) }.not_to raise_error + end + end end end end diff --git a/spec/beef/modules/ipec/ipec_spec.rb b/spec/beef/modules/ipec/ipec_spec.rb index c9be33f20..b86116849 100644 --- a/spec/beef/modules/ipec/ipec_spec.rb +++ b/spec/beef/modules/ipec/ipec_spec.rb @@ -5,30 +5,34 @@ # # Unit tests for every module under modules/ipec/. # +# Some ipec modules reference BeEF::Extension::ETag and +# BeEF::Extension::ServerClientDnsTunnel which are not loaded in unit tests. +# We stub these per-example with stub_const so the stubs don't leak across +# the suite or shadow the real extensions when they happen to be loaded. +# require_relative '../../../spec_helper' -# Stub extensions that some ipec modules reference (not loaded in unit tests) -module BeEF - module Extension - ETag = ::Module.new unless const_defined?(:ETag) - ServerClientDnsTunnel = ::Module.new unless const_defined?(:ServerClientDnsTunnel) - end -end -BeEF::Extension::ETag.const_set(:ETagMessages, ::Class.new do - def self.instance - @instance ||= Struct.new(:messages).new({}) - end -end) unless BeEF::Extension::ETag.const_defined?(:ETagMessages) -BeEF::Extension::ServerClientDnsTunnel.const_set(:Server, ::Class.new do - def self.instance - @instance ||= Struct.new(:messages).new({}) - end -end) unless BeEF::Extension::ServerClientDnsTunnel.const_defined?(:Server) - project_root = File.expand_path('../../../../', __dir__) paths = Dir[File.join(project_root, 'modules/ipec/**/module.rb')].sort +# Lightweight stand-ins for the extension singletons; no real behaviour required. +def stub_etag_messages_class + Class.new do + def self.instance + @instance ||= Struct.new(:messages).new({}) + end + end +end + +def stub_dns_tunnel_server_class + Class.new do + def self.instance + @instance ||= Struct.new(:messages).new({}) + end + end +end + paths.each do |path| rel = path.sub("#{project_root}/", '').sub(/\.rb$/, '') require_path = File.join('../../../../', rel) @@ -40,58 +44,69 @@ paths.each do |path| mod = Object.const_get(klass_name) RSpec.describe mod do - describe '.options' do - it 'returns an Array when defined' do - next unless described_class.respond_to?(:options) - config = instance_double(BeEF::Core::Configuration) - allow(config).to receive(:beef_host).and_return('127.0.0.1') - allow(config).to receive(:beef_port).and_return('3000') - allow(config).to receive(:beef_proto).and_return('http') - allow(config).to receive(:get).with(anything).and_return('127.0.0.1') - allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - expect(described_class.options).to be_an(Array) - end + before(:each) do + # Per-example stubs (auto-removed after each example). + BeEF::Extension.const_set(:ETag, Module.new) unless BeEF::Extension.const_defined?(:ETag) + BeEF::Extension.const_set(:ServerClientDnsTunnel, Module.new) unless BeEF::Extension.const_defined?(:ServerClientDnsTunnel) + stub_const('BeEF::Extension::ETag::ETagMessages', stub_etag_messages_class) unless BeEF::Extension::ETag.const_defined?(:ETagMessages) + stub_const('BeEF::Extension::ServerClientDnsTunnel::Server', stub_dns_tunnel_server_class) unless BeEF::Extension::ServerClientDnsTunnel.const_defined?(:Server) end - describe '#pre_send' do - it 'runs without error when defined' do - next unless described_class.method_defined?(:pre_send) - handler = instance_double('AssetHandler') - allow(handler).to receive(:unbind).and_return(nil) - allow(handler).to receive(:bind).and_return(nil) - allow(handler).to receive(:bind_raw).and_return(nil) - allow(handler).to receive(:remap).and_return(nil) - allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) - config = instance_double(BeEF::Core::Configuration) - allow(config).to receive(:get).with(anything) do |key| - case key - when 'beef.extension.etag.enable' then true - when 'beef.extension.s2c_dns_tunnel.enable' then true - when 'beef.extension.s2c_dns_tunnel.zone' then 'example.com' - else '127.0.0.1' - end + if described_class.respond_to?(:options) + describe '.options' do + it 'returns an Array' do + config = instance_double(BeEF::Core::Configuration) + allow(config).to receive(:beef_host).and_return('127.0.0.1') + allow(config).to receive(:beef_port).and_return('3000') + allow(config).to receive(:beef_proto).and_return('http') + allow(config).to receive(:get).with(anything).and_return('127.0.0.1') + allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) + expect(described_class.options).to be_an(Array) end - allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - instance = build_command_instance(described_class, [{'name' => 'data', 'value' => 'test'}]) - expect { run_pre_send(instance) }.not_to raise_error end end - describe '#post_execute' do - it 'runs without error when defined' do - next unless described_class.method_defined?(:post_execute) - handler = instance_double('AssetHandler') - allow(handler).to receive(:unbind) - allow(handler).to receive(:bind) - allow(handler).to receive(:remap) - allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) - file_double = double('File', write: nil, close: nil) - allow(File).to receive(:open).and_return(file_double) - allow(BeEF::Core::Models::Command).to receive(:save_result) - allow_any_instance_of(described_class).to receive(:ip).and_return('0.0.0.0') - allow_any_instance_of(described_class).to receive(:timestamp).and_return('0') - instance = build_command_instance(described_class, 'result' => '', 'results' => '', 'cid' => '0') - expect { run_post_execute(instance) }.not_to raise_error + if described_class.method_defined?(:pre_send) + describe '#pre_send' do + it 'runs without error' do + handler = instance_double('AssetHandler') + allow(handler).to receive(:unbind).and_return(nil) + allow(handler).to receive(:bind).and_return(nil) + allow(handler).to receive(:bind_raw).and_return(nil) + allow(handler).to receive(:remap).and_return(nil) + allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) + config = instance_double(BeEF::Core::Configuration) + allow(config).to receive(:get).with(anything) do |key| + case key + when 'beef.extension.etag.enable' then true + when 'beef.extension.s2c_dns_tunnel.enable' then true + when 'beef.extension.s2c_dns_tunnel.zone' then 'example.com' + else '127.0.0.1' + end + end + allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) + instance = build_command_instance(described_class, [{ 'name' => 'data', 'value' => 'test' }]) + expect { run_pre_send(instance) }.not_to raise_error + end + end + end + + if described_class.method_defined?(:post_execute) + describe '#post_execute' do + it 'runs without error' do + handler = instance_double('AssetHandler') + allow(handler).to receive(:unbind) + allow(handler).to receive(:bind) + allow(handler).to receive(:remap) + allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) + file_double = double('File').as_null_object + allow(File).to receive(:open).and_return(file_double) + allow(BeEF::Core::Models::Command).to receive(:save_result) + allow_any_instance_of(described_class).to receive(:ip).and_return('0.0.0.0') + allow_any_instance_of(described_class).to receive(:timestamp).and_return('0') + instance = build_command_instance(described_class, 'result' => '', 'results' => '', 'cid' => '0') + expect { run_post_execute(instance) }.not_to raise_error + end end end end diff --git a/spec/beef/modules/misc/misc_spec.rb b/spec/beef/modules/misc/misc_spec.rb index bfee72efc..afb4499ea 100644 --- a/spec/beef/modules/misc/misc_spec.rb +++ b/spec/beef/modules/misc/misc_spec.rb @@ -4,7 +4,8 @@ # See the file 'doc/COPYING' for copying permission # # Unit tests for every module under modules/misc/ (including subdirs). -# Branch coverage: extra post_execute runs for modules in BRANCH_COVERAGE. +# Branch coverage: extra post_execute runs for modules in +# BeefTestConfig.branch_coverage_for(:misc) (defined in spec_helper.rb). # require_relative '../../../spec_helper' @@ -12,9 +13,7 @@ require_relative '../../../spec_helper' project_root = File.expand_path('../../../../', __dir__) paths = Dir[File.join(project_root, 'modules/misc/**/module.rb')].sort -BRANCH_COVERAGE = { - 'modules/misc/wordpress_post_auth_rce' => { datastore: { 'result' => 'ok', 'cid' => '0' } } -}.freeze +BRANCH_COVERAGE_MISC = BeefTestConfig.branch_coverage_for(:misc) paths.each do |path| rel = path.sub("#{project_root}/", '').sub(/\.rb$/, '') @@ -28,134 +27,136 @@ paths.each do |path| mod = Object.const_get(klass_name) RSpec.describe mod do - describe '.options' do - it 'returns an Array when defined' do - next unless described_class.respond_to?(:options) - - config = instance_double(BeEF::Core::Configuration) - allow(config).to receive(:beef_host).and_return('127.0.0.1') - allow(config).to receive(:beef_port).and_return('3000') - allow(config).to receive(:beef_proto).and_return('http') - allow(config).to receive(:get).with(anything).and_return('127.0.0.1') - allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - expect(described_class.options).to be_an(Array) - end - end - - # Specific test for Wordpress_add_user to ensure options method executes fully - if klass_name == 'Wordpress_add_user' + if described_class.respond_to?(:options) describe '.options' do - it 'includes wordpress path and user options' do + it 'returns an Array' do config = instance_double(BeEF::Core::Configuration) allow(config).to receive(:beef_host).and_return('127.0.0.1') allow(config).to receive(:beef_port).and_return('3000') allow(config).to receive(:beef_proto).and_return('http') allow(config).to receive(:get).with(anything).and_return('127.0.0.1') allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) + expect(described_class.options).to be_an(Array) + end + end - options = described_class.options - expect(options).to be_an(Array) - expect(options.length).to eq(6) # wp_path + 5 user options + # Targeted structural assertions for selected misc modules. Higher value than + # the generic smoke test: checks option count, names, types and shape. + if klass_name == 'Wordpress_add_user' + describe '.options (Wordpress_add_user specifics)' do + it 'includes wordpress path and user options' do + config = instance_double(BeEF::Core::Configuration) + allow(config).to receive(:beef_host).and_return('127.0.0.1') + allow(config).to receive(:beef_port).and_return('3000') + allow(config).to receive(:beef_proto).and_return('http') + allow(config).to receive(:get).with(anything).and_return('127.0.0.1') + allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - # Check that wp_path option exists (from parent) - wp_path_option = options.find { |opt| opt['name'] == 'wp_path' } - expect(wp_path_option).to be_present - expect(wp_path_option['value']).to eq('/') + options = described_class.options + expect(options).to be_an(Array) + expect(options.length).to eq(5) # wp_path (parent) + username, password, email, role - # Check that username option exists - username_option = options.find { |opt| opt['name'] == 'username' } - expect(username_option).to be_present - expect(username_option['value']).to eq('beef') + wp_path_option = options.find { |opt| opt['name'] == 'wp_path' } + expect(wp_path_option).not_to be_nil + expect(wp_path_option['value']).to eq('/') - # Check that password option exists and has a generated value - password_option = options.find { |opt| opt['name'] == 'password' } - expect(password_option).to be_present - expect(password_option['value']).to be_a(String) - expect(password_option['value'].length).to eq(10) # SecureRandom.hex(5) = 10 chars + username_option = options.find { |opt| opt['name'] == 'username' } + expect(username_option).not_to be_nil + expect(username_option['value']).to eq('beef') + + password_option = options.find { |opt| opt['name'] == 'password' } + expect(password_option).not_to be_nil + expect(password_option['value']).to be_a(String) + expect(password_option['value'].length).to eq(10) # SecureRandom.hex(5) = 10 chars + + role_option = options.find { |opt| opt['name'] == 'role' } + expect(role_option).not_to be_nil + expect(role_option['value']).to eq('administrator') + end + end + end + + if klass_name == 'Test_get_variable' + describe '.options (Test_get_variable specifics)' do + it 'returns payload_name option with correct structure' do + config = instance_double(BeEF::Core::Configuration) + allow(config).to receive(:beef_host).and_return('127.0.0.1') + allow(config).to receive(:beef_port).and_return('3000') + allow(config).to receive(:beef_proto).and_return('http') + allow(config).to receive(:get).with(anything).and_return('127.0.0.1') + allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) + + options = described_class.options + expect(options).to be_an(Array) + expect(options.length).to eq(1) + + option = options.first + expect(option['name']).to eq('payload_name') + expect(option['ui_label']).to eq('Payload Name') + expect(option['type']).to eq('text') + expect(option['value']).to eq('message') + expect(option['width']).to eq('400px') + end end end end - # Specific test for Test_get_variable to ensure options method executes fully - if klass_name == 'Test_get_variable' - describe '.options' do - it 'returns payload_name option with correct structure' do + if described_class.method_defined?(:pre_send) + describe '#pre_send' do + it 'runs without error' do + handler = instance_double('AssetHandler') + allow(handler).to receive(:unbind).and_return(nil) + allow(handler).to receive(:bind).and_return(nil) + allow(handler).to receive(:bind_raw).and_return(nil) + allow(handler).to receive(:remap).and_return(nil) + allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) config = instance_double(BeEF::Core::Configuration) - allow(config).to receive(:beef_host).and_return('127.0.0.1') - allow(config).to receive(:beef_port).and_return('3000') - allow(config).to receive(:beef_proto).and_return('http') allow(config).to receive(:get).with(anything).and_return('127.0.0.1') allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - - options = described_class.options - expect(options).to be_an(Array) - expect(options.length).to eq(1) - - option = options.first - expect(option['name']).to eq('payload_name') - expect(option['ui_label']).to eq('Payload Name') - expect(option['type']).to eq('text') - expect(option['value']).to eq('message') - expect(option['width']).to eq('400px') + allow(IO).to receive(:popen).and_return(StringIO.new('')) + instance = build_command_instance(described_class, 'result' => '', 'results' => '', 'cid' => '0') + expect { run_pre_send(instance) }.not_to raise_error end end end - describe '#pre_send' do - it 'runs without error when defined' do - next unless described_class.method_defined?(:pre_send) + if described_class.method_defined?(:post_execute) + describe '#post_execute' do + it 'runs without error' do + handler = instance_double('AssetHandler') + allow(handler).to receive(:unbind) + allow(handler).to receive(:bind) + allow(handler).to receive(:remap) + allow(handler).to receive(:bind_raw) + allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) + file_double = double('File').as_null_object + allow(File).to receive(:open).and_return(file_double) + allow(BeEF::Core::Models::Command).to receive(:save_result) + allow_any_instance_of(described_class).to receive(:ip).and_return('0.0.0.0') + allow_any_instance_of(described_class).to receive(:timestamp).and_return('0') + instance = build_command_instance(described_class, 'result' => '', 'results' => '', 'cid' => '0') + expect { run_post_execute(instance) }.not_to raise_error + end - handler = instance_double('AssetHandler') - allow(handler).to receive(:unbind).and_return(nil) - allow(handler).to receive(:bind).and_return(nil) - allow(handler).to receive(:bind_raw).and_return(nil) - allow(handler).to receive(:remap).and_return(nil) - allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) - config = instance_double(BeEF::Core::Configuration) - allow(config).to receive(:get).with(anything).and_return('127.0.0.1') - allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - allow(IO).to receive(:popen).and_return(StringIO.new('')) - instance = build_command_instance(described_class, 'result' => '', 'results' => '', 'cid' => '0') - expect { run_pre_send(instance) }.not_to raise_error - end - end + if BRANCH_COVERAGE_MISC[branch_key] + it 'runs branch path with realistic datastore' do + branch = BRANCH_COVERAGE_MISC[branch_key] - describe '#post_execute' do - it 'runs without error when defined' do - next unless described_class.method_defined?(:post_execute) - - handler = instance_double('AssetHandler') - allow(handler).to receive(:unbind) - allow(handler).to receive(:bind) - allow(handler).to receive(:remap) - allow(handler).to receive(:bind_raw) - allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) - file_double = double('File', write: nil, close: nil) - allow(File).to receive(:open).and_return(file_double) - allow(BeEF::Core::Models::Command).to receive(:save_result) - allow_any_instance_of(described_class).to receive(:ip).and_return('0.0.0.0') - allow_any_instance_of(described_class).to receive(:timestamp).and_return('0') - instance = build_command_instance(described_class, 'result' => '', 'results' => '', 'cid' => '0') - expect { run_post_execute(instance) }.not_to raise_error - end - - it 'runs branch path when in BRANCH_COVERAGE' do - branch = BRANCH_COVERAGE[branch_key] - next unless described_class.method_defined?(:post_execute) && branch - - handler = instance_double('AssetHandler') - allow(handler).to receive(:unbind) - allow(handler).to receive(:bind) - allow(handler).to receive(:remap) - allow(handler).to receive(:bind_raw) - allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) - file_double = double('File', write: nil, close: nil) - allow(File).to receive(:open).and_return(file_double) - allow(BeEF::Core::Models::Command).to receive(:save_result) - allow_any_instance_of(described_class).to receive(:ip).and_return('0.0.0.0') - allow_any_instance_of(described_class).to receive(:timestamp).and_return('0') - instance = build_command_instance(described_class, branch[:datastore]) - expect { run_post_execute(instance) }.not_to raise_error + handler = instance_double('AssetHandler') + allow(handler).to receive(:unbind) + allow(handler).to receive(:bind) + allow(handler).to receive(:remap) + allow(handler).to receive(:bind_raw) + allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) + file_double = double('File').as_null_object + allow(File).to receive(:open).and_return(file_double) + allow(BeEF::Core::Models::Command).to receive(:save_result) + allow_any_instance_of(described_class).to receive(:ip).and_return('0.0.0.0') + allow_any_instance_of(described_class).to receive(:timestamp).and_return('0') + instance = build_command_instance(described_class, branch[:datastore]) + expect { run_post_execute(instance) }.not_to raise_error + end + end end end end diff --git a/spec/beef/modules/network/network_spec.rb b/spec/beef/modules/network/network_spec.rb index d26ba6f13..cf79799e4 100644 --- a/spec/beef/modules/network/network_spec.rb +++ b/spec/beef/modules/network/network_spec.rb @@ -5,74 +5,27 @@ # # Unit tests for every module under modules/network/ (including ADC). # +# Branch coverage data lives in BeefTestConfig.branch_coverage_for(:network) +# (see spec_helper.rb). Per-example stub_const calls inject a Dns::Server +# stand-in only when the real Dns extension is not loaded, avoiding the +# "superclass mismatch for class Server" hazard that arises if the stub +# leaks into the suite before the real extension's spec loads. +# require_relative '../../../spec_helper' -# Stub Dns extension only when the real one is not loaded (e.g. rake coverage:modules). -# If we stub when the real extension loads later (e.g. dns_spec), we get "superclass mismatch for class Server". -# When the real Dns is already loaded (rake short), we stub Server.instance in the pre_send example instead. -unless BeEF::Extension.const_defined?(:Dns) - BeEF::Extension.const_set(:Dns, Module.new) - dns_server_instance = Object.new - def dns_server_instance.add_rule(*) 1 end - def dns_server_instance.remove_rule!(*) nil end - dns_server = Class.new do - define_singleton_method(:instance) { dns_server_instance } - end - BeEF::Extension::Dns.const_set(:Server, dns_server) -end +BRANCH_COVERAGE_NETWORK = BeefTestConfig.branch_coverage_for(:network) -# Per-module datastore + config to hit conditional branches in post_execute (network extension + regex paths). -BRANCH_COVERAGE = { - 'modules/network/port_scanner' => { - datastore: { 'results' => 'ip=1.2.3.4&port=HTTP: Port 80 is OPEN Apache', 'beefhook' => '1', 'result' => '', 'port' => '', 'cid' => '0' }, - config_get: { 'beef.extension.network.enable' => true } - }, - 'modules/network/ping_sweep' => { - datastore: { 'results' => 'ip=192.168.1.1&ping=10ms', 'beefhook' => '1', 'result' => '', 'cid' => '0' }, - config_get: { 'beef.extension.network.enable' => true } - }, - 'modules/network/ping_sweep_ff' => { - datastore: { 'results' => 'host=192.168.1.1 is alive', 'beefhook' => '1', 'result' => '', 'host' => '', 'cid' => '0' }, - config_get: { 'beef.extension.network.enable' => true } - }, - 'modules/network/get_http_servers' => { - datastore: { 'results' => 'proto=http&ip=1.2.3.4&port=80&url=http://1.2.3.4/', 'beefhook' => '1', 'result' => '', 'url' => '', 'cid' => '0' }, - config_get: { 'beef.extension.network.enable' => true } - }, - 'modules/network/cross_origin_scanner_cors' => { - datastore: { 'results' => 'proto=http&ip=1.2.3.4&port=80&status=200', 'beefhook' => '1', 'result' => '', 'cid' => '0' }, - config_get: { 'beef.extension.network.enable' => true } - }, - 'modules/network/detect_burp' => { - datastore: { 'results' => 'has_burp=true&response=PROXY 127.0.0.1:8080', 'beefhook' => '1', 'result' => '', 'cid' => '0' }, - config_get: { 'beef.extension.network.enable' => true } - }, - 'modules/network/get_ntop_network_hosts' => { - datastore: { 'results' => 'proto=http&ip=1.2.3.4&port=3000&data={"hostNumIpAddress":"192.168.1.1"}', 'beefhook' => '1', 'result' => '', 'cid' => '0' }, - config_get: { 'beef.extension.network.enable' => true } - }, - 'modules/network/internal_network_fingerprinting' => { - datastore: { 'results' => 'proto=http&ip=1.2.3.4&port=80&discovered=Apache&url=http://test/', 'beefhook' => '1', 'result' => '', 'discovered' => '', 'url' => '', 'cid' => '0' }, - config_get: { 'beef.extension.network.enable' => true } - }, - 'modules/network/get_proxy_servers_wpad' => [ - { datastore: { 'results' => 'proxies=PROXY 192.168.1.1:3128', 'beefhook' => '1', 'result' => '', 'cid' => '0' }, config_get: { 'beef.extension.network.enable' => true } }, - { datastore: { 'results' => 'proxies=SOCKS 192.168.1.1:1080', 'beefhook' => '1', 'result' => '', 'cid' => '0' }, config_get: { 'beef.extension.network.enable' => true } } - ], - 'modules/network/jslanscanner' => [ - { datastore: { 'results' => 'proto=http&ip=1.2.3.4&port=80&service=HTTP', 'beefhook' => '1', 'result' => '', 'cid' => '0' }, config_get: { 'beef.extension.network.enable' => true } }, - { datastore: { 'results' => 'ip=1.2.3.4&device=Router', 'beefhook' => '1', 'result' => '', 'cid' => '0' }, config_get: { 'beef.extension.network.enable' => true } } - ], - 'modules/network/fetch_port_scanner' => { - datastore: { 'result' => 'ok', 'beefhook' => '1', 'cid' => '0' }, - config_get: { 'beef.extension.network.enable' => true } - }, - 'modules/network/cross_origin_scanner_flash' => [ - { datastore: { 'results' => 'ip=1.2.3.4&status=alive', 'result' => '', 'beefhook' => '1', 'cid' => '0' }, config_get: { 'beef.extension.network.enable' => true } }, - { datastore: { 'results' => 'proto=http&ip=1.2.3.4&port=80&title=Apache', 'result' => '', 'beefhook' => '1', 'cid' => '0' }, config_get: { 'beef.extension.network.enable' => true } } - ] -}.freeze +# Lightweight Dns server stand-in used when the real BeEF::Extension::Dns is +# not loaded (e.g. when running modules-only specs). +DNS_SERVER_STUB = Class.new do + def self.instance + @instance ||= Object.new.tap do |o| + def o.add_rule(*) 1 end + def o.remove_rule!(*) nil end + end + end +end project_root = File.expand_path('../../../../', __dir__) paths = Dir[File.join(project_root, 'modules/network/**/module.rb')].sort @@ -89,85 +42,70 @@ paths.each do |path| mod = Object.const_get(klass_name) RSpec.describe mod do - # Irc_nat_pinning calls sleep 30 in post_execute; stub so the suite doesn't block. before(:each) do + # Irc_nat_pinning calls sleep 30 in post_execute; suppress so the suite doesn't block. allow(Kernel).to receive(:sleep) allow_any_instance_of(described_class).to receive(:sleep).and_return(nil) - end - describe '.options' do - it 'returns an Array when defined' do - next unless described_class.respond_to?(:options) - config = instance_double(BeEF::Core::Configuration) - allow(config).to receive(:beef_host).and_return('127.0.0.1') - allow(config).to receive(:beef_port).and_return('3000') - allow(config).to receive(:beef_proto).and_return('http') - allow(config).to receive(:get).with(anything).and_return('127.0.0.1') - allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - expect(described_class.options).to be_an(Array) + # Provide a per-example Dns::Server stand-in only if the real extension hasn't been loaded. + # Using stub_const keeps the stub scoped to this example only. + unless defined?(BeEF::Extension::Dns) && BeEF::Extension::Dns.const_defined?(:Server) + BeEF::Extension.const_set(:Dns, Module.new) unless BeEF::Extension.const_defined?(:Dns) + stub_const('BeEF::Extension::Dns::Server', DNS_SERVER_STUB) end end - describe '#pre_send' do - it 'runs without error when defined' do - next unless described_class.method_defined?(:pre_send) - # When real Dns extension is loaded (rake short), stub Server.instance so Dns_rebinding works - if BeEF::Extension.const_defined?(:Dns) && BeEF::Extension::Dns.const_defined?(:Server) - dns_instance = double('DnsServer', add_rule: 1, remove_rule!: nil) - allow(BeEF::Extension::Dns::Server).to receive(:instance).and_return(dns_instance) + if described_class.respond_to?(:options) + describe '.options' do + it 'returns an Array' do + config = instance_double(BeEF::Core::Configuration) + allow(config).to receive(:beef_host).and_return('127.0.0.1') + allow(config).to receive(:beef_port).and_return('3000') + allow(config).to receive(:beef_proto).and_return('http') + allow(config).to receive(:get).with(anything).and_return('127.0.0.1') + allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) + expect(described_class.options).to be_an(Array) end - handler = instance_double('AssetHandler') - allow(handler).to receive(:unbind).and_return(nil) - allow(handler).to receive(:bind).and_return(nil) - allow(handler).to receive(:bind_raw).and_return(nil) - allow(handler).to receive(:bind_socket).and_return(nil) - allow(handler).to receive(:unbind_socket).and_return(nil) - allow(handler).to receive(:bind_cached).and_return(nil) - allow(handler).to receive(:remap).and_return(nil) - allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) - config = instance_double(BeEF::Core::Configuration) - allow(config).to receive(:get).with(anything) do |key| - case key - when 'beef.extension.dns_rebinding' then { 'address_http_external' => '127.0.0.1', 'port_proxy' => '1234' } - when 'beef.module.dns_rebinding.domain' then 'example.com' - else '127.0.0.1' + end + end + + if described_class.method_defined?(:pre_send) + describe '#pre_send' do + it 'runs without error' do + # If the real Dns extension is loaded (e.g. rake short), stub Server.instance so Dns_rebinding works. + if defined?(BeEF::Extension::Dns) && BeEF::Extension::Dns.const_defined?(:Server) + dns_instance = double('DnsServer', add_rule: 1, remove_rule!: nil) + allow(BeEF::Extension::Dns::Server).to receive(:instance).and_return(dns_instance) end + handler = instance_double('AssetHandler') + allow(handler).to receive(:unbind).and_return(nil) + allow(handler).to receive(:bind).and_return(nil) + allow(handler).to receive(:bind_raw).and_return(nil) + allow(handler).to receive(:bind_socket).and_return(nil) + allow(handler).to receive(:unbind_socket).and_return(nil) + allow(handler).to receive(:bind_cached).and_return(nil) + allow(handler).to receive(:remap).and_return(nil) + allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) + config = instance_double(BeEF::Core::Configuration) + allow(config).to receive(:get).with(anything) do |key| + case key + when 'beef.extension.dns_rebinding' then { 'address_http_external' => '127.0.0.1', 'port_proxy' => '1234' } + when 'beef.module.dns_rebinding.domain' then 'example.com' + else '127.0.0.1' + end + end + allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) + # Dns_rebinding expects @datastore as Array of option hashes (target, domain, url_callback). + datastore = described_class.name.include?('Dns_rebinding') ? [{ 'value' => '192.168.0.1' }, { 'value' => 'example.com' }, {}] : { 'result' => '', 'results' => '', 'cid' => '0' } + instance = build_command_instance(described_class, datastore) + expect { run_pre_send(instance) }.not_to raise_error end - allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - # Dns_rebinding expects @datastore as Array of option hashes (target, domain, url_callback) - datastore = described_class.name.include?('Dns_rebinding') ? [{ 'value' => '192.168.0.1' }, { 'value' => 'example.com' }, {}] : { 'result' => '', 'results' => '', 'cid' => '0' } - instance = build_command_instance(described_class, datastore) - expect { run_pre_send(instance) }.not_to raise_error end end - describe '#post_execute' do - it 'runs without error when defined' do - next unless described_class.method_defined?(:post_execute) - handler = instance_double('AssetHandler') - allow(handler).to receive(:unbind) - allow(handler).to receive(:bind) - allow(handler).to receive(:bind_socket) - allow(handler).to receive(:unbind_socket) - allow(handler).to receive(:remap) - allow(handler).to receive(:bind_cached) - allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) - file_double = double('File', write: nil, close: nil) - allow(File).to receive(:open).and_return(file_double) - allow(BeEF::Core::Models::Command).to receive(:save_result) - allow_any_instance_of(described_class).to receive(:ip).and_return('0.0.0.0') - allow_any_instance_of(described_class).to receive(:timestamp).and_return('0') - allow(Kernel).to receive(:sleep) - instance = build_command_instance(described_class, 'result' => '', 'results' => '', 'cid' => '0') - expect { run_post_execute(instance) }.not_to raise_error - end - - it 'runs branch path when in BRANCH_COVERAGE' do - raw = BRANCH_COVERAGE[branch_key] - next unless described_class.method_defined?(:post_execute) && raw - - branches = raw.is_a?(Array) ? raw : [raw] - branches.each do |branch| + if described_class.method_defined?(:post_execute) + describe '#post_execute' do + it 'runs without error' do handler = instance_double('AssetHandler') allow(handler).to receive(:unbind) allow(handler).to receive(:bind) @@ -176,28 +114,58 @@ paths.each do |path| allow(handler).to receive(:remap) allow(handler).to receive(:bind_cached) allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) - file_double = double('File', write: nil, close: nil) + file_double = double('File').as_null_object allow(File).to receive(:open).and_return(file_double) allow(BeEF::Core::Models::Command).to receive(:save_result) allow_any_instance_of(described_class).to receive(:ip).and_return('0.0.0.0') allow_any_instance_of(described_class).to receive(:timestamp).and_return('0') - allow(Kernel).to receive(:sleep) - allow(BeEF::Core::Models::NetworkService).to receive(:create) - allow(BeEF::Core::Models::NetworkHost).to receive(:create) - allow(BeEF::Filters).to receive(:is_valid_ip?).and_return(true) - - config = instance_double(BeEF::Core::Configuration) - allow(config).to receive(:beef_host).and_return('127.0.0.1') - allow(config).to receive(:beef_port).and_return('3000') - allow(config).to receive(:beef_proto).and_return('http') - allow(config).to receive(:get).with(anything) do |key| - branch[:config_get] && branch[:config_get].key?(key) ? branch[:config_get][key] : '127.0.0.1' - end - allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - - instance = build_command_instance(described_class, branch[:datastore]) + instance = build_command_instance(described_class, 'result' => '', 'results' => '', 'cid' => '0') expect { run_post_execute(instance) }.not_to raise_error end + + if BRANCH_COVERAGE_NETWORK[branch_key] + it 'runs branch path with realistic datastore' do + raw = BRANCH_COVERAGE_NETWORK[branch_key] + branches = raw.is_a?(Array) ? raw : [raw] + + # NetworkService / NetworkHost belong to the network extension and may not be + # loaded during a modules-only spec run. Provide class doubles per-example + # so `allow(...).to receive(:create)` resolves regardless. + stub_const('BeEF::Core::Models::NetworkService', Class.new { def self.create(*); end }) unless defined?(BeEF::Core::Models::NetworkService) + stub_const('BeEF::Core::Models::NetworkHost', Class.new { def self.create(*); end }) unless defined?(BeEF::Core::Models::NetworkHost) + + branches.each do |branch| + handler = instance_double('AssetHandler') + allow(handler).to receive(:unbind) + allow(handler).to receive(:bind) + allow(handler).to receive(:bind_socket) + allow(handler).to receive(:unbind_socket) + allow(handler).to receive(:remap) + allow(handler).to receive(:bind_cached) + allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) + file_double = double('File').as_null_object + allow(File).to receive(:open).and_return(file_double) + allow(BeEF::Core::Models::Command).to receive(:save_result) + allow_any_instance_of(described_class).to receive(:ip).and_return('0.0.0.0') + allow_any_instance_of(described_class).to receive(:timestamp).and_return('0') + allow(BeEF::Core::Models::NetworkService).to receive(:create) + allow(BeEF::Core::Models::NetworkHost).to receive(:create) + allow(BeEF::Filters).to receive(:is_valid_ip?).and_return(true) + + config = instance_double(BeEF::Core::Configuration) + allow(config).to receive(:beef_host).and_return('127.0.0.1') + allow(config).to receive(:beef_port).and_return('3000') + allow(config).to receive(:beef_proto).and_return('http') + allow(config).to receive(:get).with(anything) do |key| + branch[:config_get] && branch[:config_get].key?(key) ? branch[:config_get][key] : '127.0.0.1' + end + allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) + + instance = build_command_instance(described_class, branch[:datastore]) + expect { run_post_execute(instance) }.not_to raise_error + end + end + end end end end diff --git a/spec/beef/modules/persistence/persistence_spec.rb b/spec/beef/modules/persistence/persistence_spec.rb index 138340499..b05f990f2 100644 --- a/spec/beef/modules/persistence/persistence_spec.rb +++ b/spec/beef/modules/persistence/persistence_spec.rb @@ -22,50 +22,53 @@ paths.each do |path| mod = Object.const_get(klass_name) RSpec.describe mod do - describe '.options' do - it 'returns an Array when defined' do - next unless described_class.respond_to?(:options) - config = instance_double(BeEF::Core::Configuration) - allow(config).to receive(:beef_host).and_return('127.0.0.1') - allow(config).to receive(:beef_port).and_return('3000') - allow(config).to receive(:beef_proto).and_return('http') - allow(config).to receive(:get).with(anything).and_return('127.0.0.1') - allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - expect(described_class.options).to be_an(Array) + if described_class.respond_to?(:options) + describe '.options' do + it 'returns an Array' do + config = instance_double(BeEF::Core::Configuration) + allow(config).to receive(:beef_host).and_return('127.0.0.1') + allow(config).to receive(:beef_port).and_return('3000') + allow(config).to receive(:beef_proto).and_return('http') + allow(config).to receive(:get).with(anything).and_return('127.0.0.1') + allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) + expect(described_class.options).to be_an(Array) + end end end - describe '#pre_send' do - it 'runs without error when defined' do - next unless described_class.method_defined?(:pre_send) - handler = instance_double('AssetHandler') - allow(handler).to receive(:unbind).and_return(nil) - allow(handler).to receive(:bind).and_return(nil) - allow(handler).to receive(:bind_raw).and_return(nil) - allow(handler).to receive(:remap).and_return(nil) - allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) - config = instance_double(BeEF::Core::Configuration) - allow(config).to receive(:get).with(anything).and_return('/hook.js') - allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - instance = build_command_instance(described_class, 'result' => '', 'results' => '', 'cid' => '0') - expect { run_pre_send(instance) }.not_to raise_error + if described_class.method_defined?(:pre_send) + describe '#pre_send' do + it 'runs without error' do + handler = instance_double('AssetHandler') + allow(handler).to receive(:unbind).and_return(nil) + allow(handler).to receive(:bind).and_return(nil) + allow(handler).to receive(:bind_raw).and_return(nil) + allow(handler).to receive(:remap).and_return(nil) + allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) + config = instance_double(BeEF::Core::Configuration) + allow(config).to receive(:get).with(anything).and_return('/hook.js') + allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) + instance = build_command_instance(described_class, 'result' => '', 'results' => '', 'cid' => '0') + expect { run_pre_send(instance) }.not_to raise_error + end end end - describe '#post_execute' do - it 'runs without error when defined' do - next unless described_class.method_defined?(:post_execute) - handler = instance_double('AssetHandler') - allow(handler).to receive(:unbind) - allow(handler).to receive(:bind) - allow(handler).to receive(:remap) - allow(handler).to receive(:bind_raw) - allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) - file_double = double('File', write: nil, close: nil) - allow(File).to receive(:open).and_return(file_double) - allow(BeEF::Core::Models::Command).to receive(:save_result) - instance = build_command_instance(described_class, 'result' => '', 'results' => '', 'cid' => '0') - expect { run_post_execute(instance) }.not_to raise_error + if described_class.method_defined?(:post_execute) + describe '#post_execute' do + it 'runs without error' do + handler = instance_double('AssetHandler') + allow(handler).to receive(:unbind) + allow(handler).to receive(:bind) + allow(handler).to receive(:remap) + allow(handler).to receive(:bind_raw) + allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) + file_double = double('File').as_null_object + allow(File).to receive(:open).and_return(file_double) + allow(BeEF::Core::Models::Command).to receive(:save_result) + instance = build_command_instance(described_class, 'result' => '', 'results' => '', 'cid' => '0') + expect { run_post_execute(instance) }.not_to raise_error + end end end end diff --git a/spec/beef/modules/phonegap/phonegap_spec.rb b/spec/beef/modules/phonegap/phonegap_spec.rb index 642d8519d..9d9956714 100644 --- a/spec/beef/modules/phonegap/phonegap_spec.rb +++ b/spec/beef/modules/phonegap/phonegap_spec.rb @@ -23,46 +23,49 @@ paths.each do |path| mod = Object.const_get(klass_name) RSpec.describe mod do - describe '.options' do - it 'returns an Array when defined' do - next unless described_class.respond_to?(:options) - config = instance_double(BeEF::Core::Configuration) - allow(config).to receive(:beef_host).and_return('127.0.0.1') - allow(config).to receive(:beef_proto).and_return('http') - allow(config).to receive(:beef_port).and_return('3000') - allow(config).to receive(:hook_file_path).and_return('/hook.js') - allow(config).to receive(:get).with(anything).and_return('127.0.0.1') - allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - expect(described_class.options).to be_an(Array) + if described_class.respond_to?(:options) + describe '.options' do + it 'returns an Array' do + config = instance_double(BeEF::Core::Configuration) + allow(config).to receive(:beef_host).and_return('127.0.0.1') + allow(config).to receive(:beef_proto).and_return('http') + allow(config).to receive(:beef_port).and_return('3000') + allow(config).to receive(:hook_file_path).and_return('/hook.js') + allow(config).to receive(:get).with(anything).and_return('127.0.0.1') + allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) + expect(described_class.options).to be_an(Array) + end end end - describe '#pre_send' do - it 'runs without error when defined' do - next unless described_class.method_defined?(:pre_send) - handler = instance_double('AssetHandler') - allow(handler).to receive(:unbind).and_return(nil) - allow(handler).to receive(:bind).and_return(nil) - allow(handler).to receive(:bind_raw).and_return(nil) - allow(handler).to receive(:remap).and_return(nil) - allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) - config = instance_double(BeEF::Core::Configuration) - allow(config).to receive(:get).with(anything).and_return('127.0.0.1') - allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - instance = build_command_instance(described_class, 'result' => '', 'Result' => '', 'cid' => '0') - expect { run_pre_send(instance) }.not_to raise_error + if described_class.method_defined?(:pre_send) + describe '#pre_send' do + it 'runs without error' do + handler = instance_double('AssetHandler') + allow(handler).to receive(:unbind).and_return(nil) + allow(handler).to receive(:bind).and_return(nil) + allow(handler).to receive(:bind_raw).and_return(nil) + allow(handler).to receive(:remap).and_return(nil) + allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) + config = instance_double(BeEF::Core::Configuration) + allow(config).to receive(:get).with(anything).and_return('127.0.0.1') + allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) + instance = build_command_instance(described_class, 'result' => '', 'Result' => '', 'cid' => '0') + expect { run_pre_send(instance) }.not_to raise_error + end end end - describe '#post_execute' do - it 'runs without error when defined' do - next unless described_class.method_defined?(:post_execute) - handler = instance_double('AssetHandler') - allow(handler).to receive(:unbind) - allow(handler).to receive(:bind) - allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) - instance = build_command_instance(described_class, 'result' => '', 'Result' => '', 'cid' => '0') - expect { run_post_execute(instance) }.not_to raise_error + if described_class.method_defined?(:post_execute) + describe '#post_execute' do + it 'runs without error' do + handler = instance_double('AssetHandler') + allow(handler).to receive(:unbind) + allow(handler).to receive(:bind) + allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) + instance = build_command_instance(described_class, 'result' => '', 'Result' => '', 'cid' => '0') + expect { run_post_execute(instance) }.not_to raise_error + end end end end diff --git a/spec/beef/modules/social_engineering/social_engineering_spec.rb b/spec/beef/modules/social_engineering/social_engineering_spec.rb index 0ac827b94..628737860 100644 --- a/spec/beef/modules/social_engineering/social_engineering_spec.rb +++ b/spec/beef/modules/social_engineering/social_engineering_spec.rb @@ -4,7 +4,8 @@ # See the file 'doc/COPYING' for copying permission # # Unit tests for every module under modules/social_engineering/. -# Branch coverage: extra post_execute for KILLFRAME / conditional paths. +# Branch coverage data and pre_send skip list live in BeefTestConfig +# (see spec_helper.rb). # require_relative '../../../spec_helper' @@ -12,16 +13,8 @@ require_relative '../../../spec_helper' project_root = File.expand_path('../../../../', __dir__) paths = Dir[File.join(project_root, 'modules/social_engineering/**/module.rb')].sort -BRANCH_COVERAGE = { - 'modules/social_engineering/fake_lastpass' => { datastore: { 'meta' => 'KILLFRAME', 'result' => '', 'results' => '', 'answer' => '', 'cid' => '0' } }, - 'modules/social_engineering/fake_evernote_clipper' => { datastore: { 'meta' => 'KILLFRAME', 'result' => '', 'results' => '', 'answer' => '', 'cid' => '0' } } -}.freeze - -# Modules whose pre_send needs Zip, real filesystem, or logger; skip generic pre_send example -PRE_SEND_SKIP = %w[ - Firefox_extension_bindshell Firefox_extension_dropper Firefox_extension_reverse_shell - Text_to_voice Ui_abuse_ie -].freeze +BRANCH_COVERAGE_SOCENG = BeefTestConfig.branch_coverage_for(:social_engineering) +PRE_SEND_SKIP_SOCENG = BeefTestConfig.pre_send_skip_for(:social_engineering) paths.each do |path| rel = path.sub("#{project_root}/", '').sub(/\.rb$/, '') @@ -35,73 +28,75 @@ paths.each do |path| mod = Object.const_get(klass_name) RSpec.describe mod do - describe '.options' do - it 'returns an Array when defined' do - next unless described_class.respond_to?(:options) - config = instance_double(BeEF::Core::Configuration) - allow(config).to receive(:beef_host).and_return('127.0.0.1') - allow(config).to receive(:beef_proto).and_return('http') - allow(config).to receive(:beef_port).and_return('3000') - allow(config).to receive(:beef_url_str).and_return('http://127.0.0.1:3000') - allow(config).to receive(:get).with(anything) do |key| - key == 'beef.module.simple_hijacker.templates' ? [] : '127.0.0.1' + if described_class.respond_to?(:options) + describe '.options' do + it 'returns an Array' do + config = instance_double(BeEF::Core::Configuration) + allow(config).to receive(:beef_host).and_return('127.0.0.1') + allow(config).to receive(:beef_proto).and_return('http') + allow(config).to receive(:beef_port).and_return('3000') + allow(config).to receive(:beef_url_str).and_return('http://127.0.0.1:3000') + allow(config).to receive(:get).with(anything) do |key| + key == 'beef.module.simple_hijacker.templates' ? [] : '127.0.0.1' + end + allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) + expect(described_class.options).to be_an(Array) end - allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - expect(described_class.options).to be_an(Array) end end - describe '#pre_send' do - it 'runs without error when defined' do - next unless described_class.method_defined?(:pre_send) - next if PRE_SEND_SKIP.include?(described_class.name) - - handler = instance_double('AssetHandler') - allow(handler).to receive(:unbind).and_return(nil) - allow(handler).to receive(:bind).and_return(nil) - allow(handler).to receive(:bind_raw).with(anything, anything, anything, anything, anything).and_return(nil) - allow(handler).to receive(:remap).and_return(nil) - allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) - config = instance_double(BeEF::Core::Configuration) - allow(config).to receive(:get).with(anything).and_return('127.0.0.1') - allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) - allow(IO).to receive(:popen).and_return(StringIO.new('')) - # Many pre_send implementations expect @datastore to be an Array of option hashes (name/value) - pre_send_datastore = [ - { 'name' => 'payload', 'value' => 'cmd' }, - { 'name' => 'payload_handler', 'value' => 'http://127.0.0.1:8080' }, - { 'name' => 'extension_name', 'value' => 'TestExt' }, - { 'name' => 'xpi_name', 'value' => 'test.xpi' }, - { 'name' => 'lport', 'value' => '4444' } - ] - instance = build_command_instance(described_class, pre_send_datastore) - expect { run_pre_send(instance) }.not_to raise_error + if described_class.method_defined?(:pre_send) && !PRE_SEND_SKIP_SOCENG.include?(klass_name) + describe '#pre_send' do + it 'runs without error' do + handler = instance_double('AssetHandler') + allow(handler).to receive(:unbind).and_return(nil) + allow(handler).to receive(:bind).and_return(nil) + allow(handler).to receive(:bind_raw).with(anything, anything, anything, anything, anything).and_return(nil) + allow(handler).to receive(:remap).and_return(nil) + allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) + config = instance_double(BeEF::Core::Configuration) + allow(config).to receive(:get).with(anything).and_return('127.0.0.1') + allow(BeEF::Core::Configuration).to receive(:instance).and_return(config) + allow(IO).to receive(:popen).and_return(StringIO.new('')) + # Many pre_send implementations expect @datastore to be an Array of option hashes (name/value) + pre_send_datastore = [ + { 'name' => 'payload', 'value' => 'cmd' }, + { 'name' => 'payload_handler', 'value' => 'http://127.0.0.1:8080' }, + { 'name' => 'extension_name', 'value' => 'TestExt' }, + { 'name' => 'xpi_name', 'value' => 'test.xpi' }, + { 'name' => 'lport', 'value' => '4444' } + ] + instance = build_command_instance(described_class, pre_send_datastore) + expect { run_pre_send(instance) }.not_to raise_error + end end end - describe '#post_execute' do - it 'runs without error when defined' do - next unless described_class.method_defined?(:post_execute) - handler = instance_double('AssetHandler') - allow(handler).to receive(:unbind) - allow(handler).to receive(:bind) - allow(handler).to receive(:remap) - allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) - instance = build_command_instance(described_class, 'result' => '', 'results' => '', 'answer' => '', 'cid' => '0') - expect { run_post_execute(instance) }.not_to raise_error - end + if described_class.method_defined?(:post_execute) + describe '#post_execute' do + it 'runs without error' do + handler = instance_double('AssetHandler') + allow(handler).to receive(:unbind) + allow(handler).to receive(:bind) + allow(handler).to receive(:remap) + allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) + instance = build_command_instance(described_class, 'result' => '', 'results' => '', 'answer' => '', 'cid' => '0') + expect { run_post_execute(instance) }.not_to raise_error + end - it 'runs branch path when in BRANCH_COVERAGE' do - branch = BRANCH_COVERAGE[branch_key] - next unless described_class.method_defined?(:post_execute) && branch + if BRANCH_COVERAGE_SOCENG[branch_key] + it 'runs branch path with realistic datastore' do + branch = BRANCH_COVERAGE_SOCENG[branch_key] - handler = instance_double('AssetHandler') - allow(handler).to receive(:unbind) - allow(handler).to receive(:bind) - allow(handler).to receive(:remap) - allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) - instance = build_command_instance(described_class, branch[:datastore]) - expect { run_post_execute(instance) }.not_to raise_error + handler = instance_double('AssetHandler') + allow(handler).to receive(:unbind) + allow(handler).to receive(:bind) + allow(handler).to receive(:remap) + allow(BeEF::Core::NetworkStack::Handlers::AssetHandler).to receive(:instance).and_return(handler) + instance = build_command_instance(described_class, branch[:datastore]) + expect { run_post_execute(instance) }.not_to raise_error + end + end end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 4afa5bb52..35d307118 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -7,8 +7,31 @@ # Coverage must start before loading application code. require 'simplecov' and SimpleCov.start if ENV['COVERAGE'] -# Load test configuration for branch coverage data +# Branch-coverage datastore/config fixtures for the dynamic module spec loaders. +# Each spec file under spec/beef/modules/* uses BeefTestConfig.branch_coverage_for(:component) +# to drive an extra post_execute example with realistic data, hitting conditional branches +# that the generic smoke test misses. +# +# Centralising these here: +# - removes duplicate top-level BRANCH_COVERAGE constant definitions across spec files +# (which previously triggered "already initialized constant" warnings and silent overrides), +# - makes the test data discoverable from one place, and +# - matches what spec/README.md claims. module BeefTestConfig + PRE_SEND_SKIP = { + # Modules whose pre_send needs Zip, real filesystem access, or speech tooling we + # don't want to invoke in unit tests; the dynamic loader skips the generic pre_send + # example for these described_class names. + social_engineering: %w[ + Firefox_extension_bindshell Firefox_extension_dropper Firefox_extension_reverse_shell + Text_to_voice Ui_abuse_ie + ].freeze + }.freeze + + def self.pre_send_skip_for(component) + PRE_SEND_SKIP[component] || [] + end + def self.branch_coverage_for(component) case component when :browser @@ -19,6 +42,40 @@ module BeefTestConfig 'modules/browser/detect_foxit' => { datastore: { 'results' => 'foxit=Yes', 'result' => '', 'cid' => '0', 'beefhook' => '1' } }, 'modules/browser/detect_activex' => { datastore: { 'results' => 'activex=Yes', 'activex' => '', 'result' => '', 'cid' => '0', 'beefhook' => '1' } } } + when :exploits + { + 'modules/exploits/vtiger_crm_upload_exploit' => { datastore: { 'result' => 'ok', 'cid' => '0' } }, + 'modules/exploits/router/asus_rt_n12e_get_info' => { + datastore: { + 'result' => '', 'results' => 'ip=192.168.1.1&clients=192.168.1.2,00:11:22:33:44:55&wanip=1.2.3.4&netmask=255.255.255.0&gateway=192.168.1.1&dns=8.8.8.8 8.8.4.4', + 'beefhook' => '1', 'cid' => '0' + }, + config_get: { 'beef.extension.network.enable' => true } + } + } + when :host + { + 'modules/host/detect_cups' => { + datastore: { 'results' => 'proto=http&ip=1.2.3.4&port=631&cups=Installed', 'beefhook' => '1', 'result' => '', 'cid' => '0' }, + config_get: { 'beef.extension.network.enable' => true } + }, + 'modules/host/detect_airdroid' => { + datastore: { 'results' => 'proto=http&ip=1.2.3.4&port=8888&airdroid=Installed', 'airdroid' => '', 'beefhook' => '1', 'result' => '', 'cid' => '0' }, + config_get: { 'beef.extension.network.enable' => true } + }, + 'modules/host/get_internal_ip_java' => { + datastore: { 'results' => '192.168.1.1', 'Result' => '', 'result' => '', 'beefhook' => '1', 'cid' => '0' }, + config_get: { 'beef.extension.network.enable' => true } + }, + 'modules/host/get_internal_ip_webrtc' => { + datastore: { 'results' => 'IP is 192.168.1.1', 'Result' => '', 'result' => '', 'beefhook' => '1', 'cid' => '0' }, + config_get: { 'beef.extension.network.enable' => true } + } + } + when :misc + { + 'modules/misc/wordpress_post_auth_rce' => { datastore: { 'result' => 'ok', 'cid' => '0' } } + } when :network { 'modules/network/port_scanner' => { @@ -28,7 +85,52 @@ module BeefTestConfig 'modules/network/ping_sweep' => { datastore: { 'results' => 'ip=192.168.1.1&ping=10ms', 'beefhook' => '1', 'result' => '', 'cid' => '0' }, config_get: { 'beef.extension.network.enable' => true } - } + }, + 'modules/network/ping_sweep_ff' => { + datastore: { 'results' => 'host=192.168.1.1 is alive', 'beefhook' => '1', 'result' => '', 'host' => '', 'cid' => '0' }, + config_get: { 'beef.extension.network.enable' => true } + }, + 'modules/network/get_http_servers' => { + datastore: { 'results' => 'proto=http&ip=1.2.3.4&port=80&url=http://1.2.3.4/', 'beefhook' => '1', 'result' => '', 'url' => '', 'cid' => '0' }, + config_get: { 'beef.extension.network.enable' => true } + }, + 'modules/network/cross_origin_scanner_cors' => { + datastore: { 'results' => 'proto=http&ip=1.2.3.4&port=80&status=200', 'beefhook' => '1', 'result' => '', 'cid' => '0' }, + config_get: { 'beef.extension.network.enable' => true } + }, + 'modules/network/detect_burp' => { + datastore: { 'results' => 'has_burp=true&response=PROXY 127.0.0.1:8080', 'beefhook' => '1', 'result' => '', 'cid' => '0' }, + config_get: { 'beef.extension.network.enable' => true } + }, + 'modules/network/get_ntop_network_hosts' => { + datastore: { 'results' => 'proto=http&ip=1.2.3.4&port=3000&data={"hostNumIpAddress":"192.168.1.1"}', 'beefhook' => '1', 'result' => '', 'cid' => '0' }, + config_get: { 'beef.extension.network.enable' => true } + }, + 'modules/network/internal_network_fingerprinting' => { + datastore: { 'results' => 'proto=http&ip=1.2.3.4&port=80&discovered=Apache&url=http://test/', 'beefhook' => '1', 'result' => '', 'discovered' => '', 'url' => '', 'cid' => '0' }, + config_get: { 'beef.extension.network.enable' => true } + }, + 'modules/network/get_proxy_servers_wpad' => [ + { datastore: { 'results' => 'proxies=PROXY 192.168.1.1:3128', 'beefhook' => '1', 'result' => '', 'cid' => '0' }, config_get: { 'beef.extension.network.enable' => true } }, + { datastore: { 'results' => 'proxies=SOCKS 192.168.1.1:1080', 'beefhook' => '1', 'result' => '', 'cid' => '0' }, config_get: { 'beef.extension.network.enable' => true } } + ], + 'modules/network/jslanscanner' => [ + { datastore: { 'results' => 'proto=http&ip=1.2.3.4&port=80&service=HTTP', 'beefhook' => '1', 'result' => '', 'cid' => '0' }, config_get: { 'beef.extension.network.enable' => true } }, + { datastore: { 'results' => 'ip=1.2.3.4&device=Router', 'beefhook' => '1', 'result' => '', 'cid' => '0' }, config_get: { 'beef.extension.network.enable' => true } } + ], + 'modules/network/fetch_port_scanner' => { + datastore: { 'result' => 'ok', 'beefhook' => '1', 'cid' => '0' }, + config_get: { 'beef.extension.network.enable' => true } + }, + 'modules/network/cross_origin_scanner_flash' => [ + { datastore: { 'results' => 'ip=1.2.3.4&status=alive', 'result' => '', 'beefhook' => '1', 'cid' => '0' }, config_get: { 'beef.extension.network.enable' => true } }, + { datastore: { 'results' => 'proto=http&ip=1.2.3.4&port=80&title=Apache', 'result' => '', 'beefhook' => '1', 'cid' => '0' }, config_get: { 'beef.extension.network.enable' => true } } + ] + } + when :social_engineering + { + 'modules/social_engineering/fake_lastpass' => { datastore: { 'meta' => 'KILLFRAME', 'result' => '', 'results' => '', 'answer' => '', 'cid' => '0' } }, + 'modules/social_engineering/fake_evernote_clipper' => { datastore: { 'meta' => 'KILLFRAME', 'result' => '', 'results' => '', 'answer' => '', 'cid' => '0' } } } else {} diff --git a/spec/support/module_spec_helper.rb b/spec/support/module_spec_helper.rb index 8a86d17fd..552ee7915 100644 --- a/spec/support/module_spec_helper.rb +++ b/spec/support/module_spec_helper.rb @@ -49,7 +49,8 @@ end RSpec.configure do |config| config.include ModuleSpecHelper - # Stub Kernel.sleep for all module specs so modules that call sleep (e.g. Irc_nat_pinning's post_execute sleep 30) don't slow the suite. + # Stub Kernel.sleep for all module specs so modules that call sleep + # (e.g. Irc_nat_pinning's post_execute sleep 30) don't slow the suite. config.before(:each) do |example| if example.metadata[:file_path]&.include?('spec/beef/modules') allow(Kernel).to receive(:sleep)