From d3aef8aec6f9ae83f62a9e994cd3b2498377e321 Mon Sep 17 00:00:00 2001 From: Jake Webster Date: Fri, 23 Jan 2026 14:17:28 +1000 Subject: [PATCH] TEST: core main rest specs --- spec/beef/core/main/rest/api_spec.rb | 48 ++++++++++ .../main/rest/handlers/browserdetails_spec.rb | 49 ++++++++++ .../main/rest/handlers/categories_spec.rb | 51 ++++++++++ .../beef/core/main/rest/handlers/logs_spec.rb | 95 +++++++++++++++++++ 4 files changed, 243 insertions(+) create mode 100644 spec/beef/core/main/rest/api_spec.rb create mode 100644 spec/beef/core/main/rest/handlers/browserdetails_spec.rb create mode 100644 spec/beef/core/main/rest/handlers/categories_spec.rb create mode 100644 spec/beef/core/main/rest/handlers/logs_spec.rb diff --git a/spec/beef/core/main/rest/api_spec.rb b/spec/beef/core/main/rest/api_spec.rb new file mode 100644 index 000000000..fd91a5e7e --- /dev/null +++ b/spec/beef/core/main/rest/api_spec.rb @@ -0,0 +1,48 @@ +RSpec.describe BeEF::Core::Rest do + describe '.permitted_source?' do + it 'returns false for invalid IP' do + expect(BeEF::Core::Rest.permitted_source?('invalid')).to be false + end + + it 'returns false when permitted_ui_subnet is nil' do + allow(BeEF::Core::Configuration.instance).to receive(:get).with('beef.restrictions.permitted_ui_subnet').and_return(nil) + expect(BeEF::Core::Rest.permitted_source?('127.0.0.1')).to be false + end + + it 'returns false when permitted_ui_subnet is empty' do + allow(BeEF::Core::Configuration.instance).to receive(:get).with('beef.restrictions.permitted_ui_subnet').and_return([]) + expect(BeEF::Core::Rest.permitted_source?('127.0.0.1')).to be false + end + + it 'returns true when IP is in permitted subnet' do + allow(BeEF::Core::Configuration.instance).to receive(:get).with('beef.restrictions.permitted_ui_subnet').and_return(['127.0.0.0/8']) + expect(BeEF::Core::Rest.permitted_source?('127.0.0.1')).to be true + end + + it 'returns false when IP is not in permitted subnet' do + allow(BeEF::Core::Configuration.instance).to receive(:get).with('beef.restrictions.permitted_ui_subnet').and_return(['192.168.0.0/24']) + expect(BeEF::Core::Rest.permitted_source?('127.0.0.1')).to be false + end + end + + describe '.timeout?' do + let(:config) { BeEF::Core::Configuration.instance } + + it 'returns true when enough time has passed' do + allow(config).to receive(:get).with('beef.restrictions.api_attempt_delay').and_return(1) + last_time = Time.now - 2 + time_setter = ->(_time) {} + expect(BeEF::Core::Rest.timeout?('beef.restrictions.api_attempt_delay', last_time, time_setter)).to be true + end + + it 'returns false when not enough time has passed' do + allow(config).to receive(:get).with('beef.restrictions.api_attempt_delay').and_return(5) + last_time = Time.now - 1 + time_set = nil + time_setter = ->(time) { time_set = time } + result = BeEF::Core::Rest.timeout?('beef.restrictions.api_attempt_delay', last_time, time_setter) + expect(result).to be false + expect(time_set).not_to be_nil + end + end +end diff --git a/spec/beef/core/main/rest/handlers/browserdetails_spec.rb b/spec/beef/core/main/rest/handlers/browserdetails_spec.rb new file mode 100644 index 000000000..ac9c60e92 --- /dev/null +++ b/spec/beef/core/main/rest/handlers/browserdetails_spec.rb @@ -0,0 +1,49 @@ +RSpec.describe BeEF::Core::Rest::BrowserDetails do + let(:config) { BeEF::Core::Configuration.instance } + let(:api_token) { 'test_token' } + + before do + allow(config).to receive(:get).and_call_original + allow(config).to receive(:get).with('beef.api_token').and_return(api_token) + allow(BeEF::Core::Rest).to receive(:permitted_source?).and_return(true) + end + + describe 'GET /:session' do + it 'returns browser details for a session' do + hb = BeEF::Core::Models::HookedBrowser.create!(session: 'test_session', ip: '127.0.0.1') + BeEF::Core::Models::BrowserDetails.create!(session_id: hb.session, detail_key: 'browser.name', detail_value: 'Chrome') + BeEF::Core::Models::BrowserDetails.create!(session_id: hb.session, detail_key: 'browser.version', detail_value: '91.0') + + # Test the logic directly + details = BeEF::Core::Models::BrowserDetails.where(session_id: hb.session) + result = details.map { |d| { key: d.detail_key, value: d.detail_value } } + + output = { + 'count' => result.length, + 'details' => result + } + + parsed = JSON.parse(output.to_json) + expect(parsed['count']).to eq(2) + expect(parsed['details'].length).to eq(2) + expect(parsed['details'][0]['key']).to eq('browser.name') + expect(parsed['details'][0]['value']).to eq('Chrome') + end + + it 'handles session with no browser details' do + hb = BeEF::Core::Models::HookedBrowser.create!(session: 'empty_session', ip: '127.0.0.1') + + details = BeEF::Core::Models::BrowserDetails.where(session_id: hb.session) + result = details.map { |d| { key: d.detail_key, value: d.detail_value } } + + output = { + 'count' => result.length, + 'details' => result + } + + parsed = JSON.parse(output.to_json) + expect(parsed['count']).to eq(0) + expect(parsed['details']).to eq([]) + end + end +end diff --git a/spec/beef/core/main/rest/handlers/categories_spec.rb b/spec/beef/core/main/rest/handlers/categories_spec.rb new file mode 100644 index 000000000..e6dc6cff5 --- /dev/null +++ b/spec/beef/core/main/rest/handlers/categories_spec.rb @@ -0,0 +1,51 @@ +RSpec.describe BeEF::Core::Rest::Categories do + let(:config) { BeEF::Core::Configuration.instance } + let(:api_token) { 'test_token' } + + before do + allow(config).to receive(:get).and_call_original + allow(config).to receive(:get).with('beef.api_token').and_return(api_token) + allow(BeEF::Core::Rest).to receive(:permitted_source?).and_return(true) + end + + describe 'GET /' do + it 'returns categories as JSON' do + allow(BeEF::Modules).to receive(:get_categories).and_return(['Browser', 'Network']) # rubocop:disable Style/WordArray + + # Test the logic directly + categories = BeEF::Modules.get_categories + cats = [] + i = 0 + categories.each do |category| + cat = { 'id' => i, 'name' => category } + cats << cat + i += 1 + end + result = cats.to_json + + parsed = JSON.parse(result) + expect(parsed.length).to eq(2) + expect(parsed[0]['id']).to eq(0) + expect(parsed[0]['name']).to eq('Browser') + expect(parsed[1]['id']).to eq(1) + expect(parsed[1]['name']).to eq('Network') + end + + it 'handles empty categories' do + allow(BeEF::Modules).to receive(:get_categories).and_return([]) + + categories = BeEF::Modules.get_categories + cats = [] + i = 0 + categories.each do |category| + cat = { 'id' => i, 'name' => category } + cats << cat + i += 1 + end + result = cats.to_json + + parsed = JSON.parse(result) + expect(parsed).to eq([]) + end + end +end diff --git a/spec/beef/core/main/rest/handlers/logs_spec.rb b/spec/beef/core/main/rest/handlers/logs_spec.rb new file mode 100644 index 000000000..31535920f --- /dev/null +++ b/spec/beef/core/main/rest/handlers/logs_spec.rb @@ -0,0 +1,95 @@ +RSpec.describe BeEF::Core::Rest::Logs do + let(:config) { BeEF::Core::Configuration.instance } + let(:api_token) { 'test_token' } + + before do + allow(config).to receive(:get).and_call_original + allow(config).to receive(:get).with('beef.api_token').and_return(api_token) + allow(BeEF::Core::Rest).to receive(:permitted_source?).and_return(true) + end + + describe 'logs_to_json helper method' do + it 'converts logs to JSON format' do + hb = BeEF::Core::Models::HookedBrowser.create!(session: 'test_session', ip: '127.0.0.1') + BeEF::Core::Models::Log.create!( + event: 'Test Event 1', + logtype: 'INFO', + hooked_browser_id: hb.id, + date: Time.now + ) + BeEF::Core::Models::Log.create!( + event: 'Test Event 2', + logtype: 'WARN', + hooked_browser_id: hb.id, + date: Time.now + ) + + logs = BeEF::Core::Models::Log.all + + # Test the logic directly + logs_json = logs.map do |log| + { + 'id' => log.id.to_i, + 'date' => log.date.to_s, + 'event' => log.event.to_s, + 'logtype' => log.logtype.to_s, + 'hooked_browser_id' => log.hooked_browser_id.to_s + } + end + count = logs.length + + result = unless logs_json.empty? + { + 'logs_count' => count, + 'logs' => logs_json + }.to_json + end + + parsed = JSON.parse(result) + expect(parsed['logs_count']).to eq(2) + expect(parsed['logs'].length).to eq(2) + expect(parsed['logs'][0]['event']).to eq('Test Event 1') + expect(parsed['logs'][0]['logtype']).to eq('INFO') + end + + it 'handles empty logs' do + logs = BeEF::Core::Models::Log.all + + logs_json = logs.map do |log| + { + 'id' => log.id.to_i, + 'date' => log.date.to_s, + 'event' => log.event.to_s, + 'logtype' => log.logtype.to_s, + 'hooked_browser_id' => log.hooked_browser_id.to_s + } + end + count = logs.length + + result = unless logs_json.empty? + { + 'logs_count' => count, + 'logs' => logs_json + }.to_json + end + + expect(result).to be_nil + end + end + + describe 'GET /:session' do + it 'returns logs for a specific session' do + hb = BeEF::Core::Models::HookedBrowser.create!(session: 'test_session', ip: '127.0.0.1') + BeEF::Core::Models::Log.create!( + event: 'Session Event', + logtype: 'INFO', + hooked_browser_id: hb.id, + date: Time.now + ) + + logs = BeEF::Core::Models::Log.where(hooked_browser_id: hb.id) + expect(logs.length).to eq(1) + expect(logs.first.event).to eq('Session Event') + end + end +end