From 0970bdcd87fe032b87784fb68b772bec0ef97e9d Mon Sep 17 00:00:00 2001 From: Jake Webster Date: Thu, 22 Jan 2026 15:04:18 +1000 Subject: [PATCH] TEST: core/ruby method specs --- spec/beef/core/ruby/hash_spec.rb | 99 ++++++++++++++ spec/beef/core/ruby/module_spec.rb | 94 +++++++++++++ spec/beef/core/ruby/print_spec.rb | 191 +++++++++++++++++++++++++++ spec/beef/core/ruby/security_spec.rb | 28 ++++ spec/beef/core/ruby/string_spec.rb | 27 ++++ 5 files changed, 439 insertions(+) create mode 100644 spec/beef/core/ruby/hash_spec.rb create mode 100644 spec/beef/core/ruby/module_spec.rb create mode 100644 spec/beef/core/ruby/print_spec.rb create mode 100644 spec/beef/core/ruby/security_spec.rb create mode 100644 spec/beef/core/ruby/string_spec.rb diff --git a/spec/beef/core/ruby/hash_spec.rb b/spec/beef/core/ruby/hash_spec.rb new file mode 100644 index 000000000..58805941b --- /dev/null +++ b/spec/beef/core/ruby/hash_spec.rb @@ -0,0 +1,99 @@ +# +# Copyright (c) 2006-2026 Wade Alcorn - wade@bindshell.net +# Browser Exploitation Framework (BeEF) - https://beefproject.com +# See the file 'doc/COPYING' for copying permission +# +require 'spec_helper' + +RSpec.describe 'Hash#deep_merge' do + it 'merges two simple hashes' do + hash1 = { a: 1, b: 2 } + hash2 = { c: 3, d: 4 } + result = hash1.deep_merge(hash2) + + expect(result).to eq({ a: 1, b: 2, c: 3, d: 4 }) + end + + it 'overwrites duplicate keys with values from calling hash' do + hash1 = { a: 1, b: 2 } + hash2 = { b: 3, c: 4 } + result = hash1.deep_merge(hash2) + + expect(result[:a]).to eq(1) + expect(result[:b]).to eq(3) # hash2 value overwrites + expect(result[:c]).to eq(4) + end + + it 'recursively merges nested hashes' do + hash1 = { a: { b: 1, c: 2 }, d: 3 } + hash2 = { a: { c: 4, e: 5 }, f: 6 } + result = hash1.deep_merge(hash2) + + expect(result[:a][:b]).to eq(1) + expect(result[:a][:c]).to eq(4) # hash2 value overwrites + expect(result[:a][:e]).to eq(5) + expect(result[:d]).to eq(3) + expect(result[:f]).to eq(6) + end + + it 'handles deeply nested hashes' do + hash1 = { a: { b: { c: 1 } } } + hash2 = { a: { b: { d: 2 } } } + result = hash1.deep_merge(hash2) + + expect(result[:a][:b][:c]).to eq(1) + expect(result[:a][:b][:d]).to eq(2) + end + + it 'does not modify the original hash' do + hash1 = { a: 1 } + hash2 = { b: 2 } + original_hash1 = hash1.dup + + hash1.deep_merge(hash2) + + expect(hash1).to eq(original_hash1) + end + + it 'handles empty hashes' do + hash1 = {} + hash2 = { a: 1 } + result = hash1.deep_merge(hash2) + + expect(result).to eq({ a: 1 }) + end + + it 'handles merging with empty hash' do + hash1 = { a: 1 } + hash2 = {} + result = hash1.deep_merge(hash2) + + expect(result).to eq({ a: 1 }) + end + + it 'handles non-hash values in nested structure' do + hash1 = { a: { b: 1 } } + hash2 = { a: 2 } # a is not a hash in hash2 + result = hash1.deep_merge(hash2) + + expect(result[:a]).to eq(2) # Should overwrite with non-hash value + end + + it 'handles nil values in source hash' do + hash1 = { a: nil, b: 1 } + hash2 = { a: 2, c: 3 } + result = hash1.deep_merge(hash2) + + expect(result[:a]).to eq(2) # Should overwrite nil + expect(result[:b]).to eq(1) + expect(result[:c]).to eq(3) + end + + it 'handles nil values when merging nested hashes' do + hash1 = { a: nil } + hash2 = { a: { b: 1 } } + result = hash1.deep_merge(hash2) + + expect(result[:a]).to eq({ b: 1 }) # Should overwrite nil with hash + end +end diff --git a/spec/beef/core/ruby/module_spec.rb b/spec/beef/core/ruby/module_spec.rb new file mode 100644 index 000000000..bc9b44074 --- /dev/null +++ b/spec/beef/core/ruby/module_spec.rb @@ -0,0 +1,94 @@ +# +# Copyright (c) 2006-2026 Wade Alcorn - wade@bindshell.net +# Browser Exploitation Framework (BeEF) - https://beefproject.com +# See the file 'doc/COPYING' for copying permission +# +require 'spec_helper' + +RSpec.describe 'Module extensions' do + # Create a test module to use in tests + let(:test_module) do + Module.new do + def test_method + 'test' + end + end + end + + describe '#included_in_classes' do + it 'returns an array' do + result = test_module.included_in_classes + expect(result).to be_an(Array) + end + + it 'finds classes that include the module' do + mod = test_module + test_class = Class.new do + include mod + end + + # Force class to be created + test_class.new + + included_classes = mod.included_in_classes + expect(included_classes.map(&:to_s)).to include(test_class.to_s) + end + + it 'returns unique classes only' do + mod = test_module + test_class = Class.new do + include mod + end + + # Force class to be created multiple times + test_class.new + test_class.new + + included_classes = mod.included_in_classes + unique_class_names = included_classes.map(&:to_s) + expect(unique_class_names.count(test_class.to_s)).to eq(1) + end + + it 'returns empty array when module is not included anywhere' do + isolated_module = Module.new + result = isolated_module.included_in_classes + expect(result).to be_an(Array) + # May or may not be empty depending on what's loaded, but should be an array + end + end + + describe '#included_in_modules' do + it 'returns an array' do + result = test_module.included_in_modules + expect(result).to be_an(Array) + end + + it 'finds modules that include the module' do + mod = test_module + including_module = Module.new do + include mod + end + + # Force module to be created + Class.new { include including_module } + + included_modules = mod.included_in_modules + expect(included_modules.map(&:to_s)).to include(including_module.to_s) + end + + it 'returns unique modules only' do + mod = test_module + including_module = Module.new do + include mod + end + + # Force module to be created multiple times + Class.new { include including_module } + Class.new { include including_module } + + included_modules = mod.included_in_modules + unique_module_names = included_modules.map(&:to_s) + expect(unique_module_names.count(including_module.to_s)).to eq(1) + end + end +end diff --git a/spec/beef/core/ruby/print_spec.rb b/spec/beef/core/ruby/print_spec.rb new file mode 100644 index 000000000..fe120163f --- /dev/null +++ b/spec/beef/core/ruby/print_spec.rb @@ -0,0 +1,191 @@ +# +# Copyright (c) 2006-2026 Wade Alcorn - wade@bindshell.net +# Browser Exploitation Framework (BeEF) - https://beefproject.com +# See the file 'doc/COPYING' for copying permission +# +require 'spec_helper' + +RSpec.describe 'Print functions' do + let(:logger) { BeEF.logger } + let(:test_message) { 'test message' } + + before(:each) do + # Mock stdout to avoid cluttering test output + allow($stdout).to receive(:puts) + allow($stdout).to receive(:print) + + # Mock logger methods + allow(logger).to receive(:error) + allow(logger).to receive(:info) + allow(logger).to receive(:warn) + allow(logger).to receive(:debug) + end + + describe '#print_error' do + it 'calls logger.error with the message' do + expect(logger).to receive(:error).with(test_message) + print_error(test_message) + end + + it 'outputs to stdout with timestamp and error prefix' do + expect($stdout).to receive(:puts).with(match(/\[!\] #{test_message}/)) + print_error(test_message) + end + + it 'converts non-string arguments to string' do + expect(logger).to receive(:error).with('123') + print_error(123) + end + end + + describe '#print_info' do + it 'calls logger.info with the message' do + expect(logger).to receive(:info).with(test_message) + print_info(test_message) + end + + it 'outputs to stdout with timestamp and info prefix' do + expect($stdout).to receive(:puts).with(match(/\[\*\] #{test_message}/)) + print_info(test_message) + end + end + + describe '#print_status' do + it 'calls print_info' do + expect(logger).to receive(:info).with(test_message) + print_status(test_message) + end + end + + describe '#print_warning' do + it 'calls logger.warn with the message' do + expect(logger).to receive(:warn).with(test_message) + print_warning(test_message) + end + + it 'outputs to stdout with timestamp and warning prefix' do + expect($stdout).to receive(:puts).with(match(/\[!\] #{test_message}/)) + print_warning(test_message) + end + end + + describe '#print_debug' do + let(:config) { BeEF::Core::Configuration.instance } + + context 'when debug is enabled' do + before do + allow(config).to receive(:get).with('beef.debug').and_return(true) + allow(BeEF::Core::Console::CommandLine).to receive(:parse).and_return({}) + end + + it 'calls logger.debug with the message' do + expect(logger).to receive(:debug).with(test_message) + print_debug(test_message) + end + + it 'outputs to stdout with timestamp and debug prefix' do + expect($stdout).to receive(:puts).with(match(/\[>\] #{test_message}/)) + print_debug(test_message) + end + end + + context 'when verbose flag is set' do + before do + allow(config).to receive(:get).with('beef.debug').and_return(false) + allow(BeEF::Core::Console::CommandLine).to receive(:parse).and_return({ verbose: true }) + end + + it 'calls logger.debug with the message' do + expect(logger).to receive(:debug).with(test_message) + print_debug(test_message) + end + end + + context 'when debug is disabled and verbose is not set' do + before do + allow(config).to receive(:get).with('beef.debug').and_return(false) + allow(BeEF::Core::Console::CommandLine).to receive(:parse).and_return({}) + end + + it 'does not call logger.debug' do + expect(logger).not_to receive(:debug) + print_debug(test_message) + end + + it 'does not output to stdout' do + expect($stdout).not_to receive(:puts) + print_debug(test_message) + end + end + end + + describe '#print_success' do + it 'calls logger.info with the message' do + expect(logger).to receive(:info).with(test_message) + print_success(test_message) + end + + it 'outputs to stdout with timestamp and success prefix' do + expect($stdout).to receive(:puts).with(match(/\[\+\] #{test_message}/)) + print_success(test_message) + end + end + + describe '#print_good' do + it 'calls print_success' do + expect(logger).to receive(:info).with(test_message) + print_good(test_message) + end + end + + describe '#print_more' do + context 'with string input' do + it 'splits string by newlines and formats each line' do + multi_line = "line1\nline2\nline3" + expect($stdout).to receive(:puts).with(match(/line1/)) + expect($stdout).to receive(:puts).with(match(/line2/)) + expect($stdout).to receive(:puts).with(match(/\|_ line3/)) # Last line has |_ + expect(logger).to receive(:info).exactly(3).times + print_more(multi_line) + end + + it 'formats last line with |_ prefix' do + single_line = "single line" + expect($stdout).to receive(:puts).with(match(/\|_ single line/)) + expect(logger).to receive(:info).with(match(/\|_ single line/)) + print_more(single_line) + end + end + + context 'with array input' do + it 'formats each array element as a line' do + lines_array = ["line1", "line2", "line3"] + expect($stdout).to receive(:puts).exactly(3).times + expect(logger).to receive(:info).exactly(3).times + print_more(lines_array) + end + + it 'formats last array element with |_ prefix' do + lines_array = ["line1", "line2"] + expect($stdout).to receive(:puts).with(match(/\| line1/)) + expect($stdout).to receive(:puts).with(match(/\|_ line2/)) + print_more(lines_array) + end + end + end + + describe '#print_over' do + it 'calls logger.info with the message' do + expect(logger).to receive(:info).with(test_message) + print_over(test_message) + end + + it 'outputs formatted message to stdout' do + # print is a private Kernel method, hard to stub directly + # We verify the function executes and calls logger + # The actual output includes ANSI color codes and carriage return + expect { print_over(test_message) }.not_to raise_error + expect(logger).to have_received(:info).with(test_message) + end + end +end diff --git a/spec/beef/core/ruby/security_spec.rb b/spec/beef/core/ruby/security_spec.rb new file mode 100644 index 000000000..ba257bfcd --- /dev/null +++ b/spec/beef/core/ruby/security_spec.rb @@ -0,0 +1,28 @@ +# +# Copyright (c) 2006-2026 Wade Alcorn - wade@bindshell.net +# Browser Exploitation Framework (BeEF) - https://beefproject.com +# See the file 'doc/COPYING' for copying permission +# +require 'spec_helper' + +RSpec.describe 'Security method overrides' do + it 'overrides exec method' do + # The exec method should be overridden to prevent usage + # We can't easily test the exit behavior without forking + # so we just check that the method is overridden + expect(method(:exec).source_location).not_to be_nil + expect(method(:exec).source_location[0]).to include('core/ruby/security.rb') + end + + it 'overrides system method' do + # The system method should be overridden + expect(method(:system).source_location).not_to be_nil + expect(method(:system).source_location[0]).to include('core/ruby/security.rb') + end + + it 'overrides Kernel.system method' do + # Kernel.system should be overridden + expect(Kernel.method(:system).source_location).not_to be_nil + expect(Kernel.method(:system).source_location[0]).to include('core/ruby/security.rb') + end +end diff --git a/spec/beef/core/ruby/string_spec.rb b/spec/beef/core/ruby/string_spec.rb new file mode 100644 index 000000000..c361f75eb --- /dev/null +++ b/spec/beef/core/ruby/string_spec.rb @@ -0,0 +1,27 @@ +# +# Copyright (c) 2006-2026 Wade Alcorn - wade@bindshell.net +# Browser Exploitation Framework (BeEF) - https://beefproject.com +# See the file 'doc/COPYING' for copying permission +# +require 'spec_helper' + +RSpec.describe 'String colorization' do + it 'includes Term::ANSIColor module' do + expect(String.included_modules).to include(Term::ANSIColor) + end + + it 'can use color methods on strings' do + string = 'test' + expect(string.respond_to?(:red)).to be(true) + expect(string.respond_to?(:green)).to be(true) + expect(string.respond_to?(:blue)).to be(true) + end + + it 'applies color methods correctly' do + string = 'hello' + colored = string.red + + expect(colored).to be_a(String) + expect(colored).not_to eq(string) # should now be: "\e[31mhello\e[0m" (red colored hello) + end +end