mirror of
https://github.com/beefproject/beef
synced 2026-06-08 13:15:56 +00:00
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
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -2,6 +2,5 @@
|
||||
--color
|
||||
--require spec_helper
|
||||
-I .
|
||||
--order defined
|
||||
--tag ~run_on_browserstack
|
||||
--tag ~run_on_long_tests
|
||||
--tag ~run_on_long_tests
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
+25
-3
@@ -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/**/<category>_spec.rb` walk every
|
||||
`modules/<category>/**/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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+109
-108
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+104
-2
@@ -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
|
||||
{}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user