Skip to content

Commit 91cb94e

Browse files
committed
Add _close method to history manager, use generic method for Msf Config history file
1 parent 487faf9 commit 91cb94e

File tree

7 files changed

+42
-76
lines changed

7 files changed

+42
-76
lines changed

lib/msf/base/config.rb

Lines changed: 13 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -236,46 +236,11 @@ def self.ldap_session_history
236236
self.new.ldap_session_history
237237
end
238238

239-
# Returns the full path to the PostgreSQL session history file.
240-
#
241-
# @return [String] path to the history file.
242-
def self.postgresql_session_history
243-
self.new.postgresql_session_history
244-
end
245-
246-
# Returns the full path to the PostgreSQL interactive query history file
247-
#
248-
# @return [String] path to the interactive query history file.
249-
def self.postgresql_session_interactive_history
250-
self.new.postgresql_session_interactive_history
251-
end
252-
253-
# Returns the full path to the MSSQL session history file.
254-
#
255-
# @return [String] path to the history file.
256-
def self.mssql_session_history
257-
self.new.mssql_session_history
258-
end
259-
260-
# Returns the full path to the MSSQL interactive query history file
261-
#
262-
# @return [String] path to the interactive query history file.
263-
def self.mssql_session_interactive_history
264-
self.new.mssql_session_interactive_history
265-
end
266-
267-
# Returns the full path to the MySQL session history file.
268-
#
269-
# @return [String] path to the history file.
270-
def self.mysql_session_history
271-
self.new.mysql_session_history
272-
end
273-
274239
# Returns the full path to the MySQL interactive query history file
275240
#
276241
# @return [String] path to the interactive query history file.
277-
def self.mysql_session_interactive_history
278-
self.new.mysql_session_interactive_history
242+
def self.history_file_for_session_type(opts)
243+
self.new.history_file_for_session_type(opts)
279244
end
280245

281246
def self.pry_history
@@ -398,28 +363,24 @@ def ldap_session_history
398363
config_directory + FileSep + "ldap_session_history"
399364
end
400365

401-
def postgresql_session_history
402-
histories_directory + FileSep + "postgresql_session_history"
403-
end
366+
def history_options_valid?(opts)
367+
return false if (opts[:session_type].nil? || opts[:interactive].nil?)
404368

405-
def postgresql_session_interactive_history
406-
histories_directory + FileSep + "postgresql_session_interactive_history"
369+
true
407370
end
408371

409-
def mysql_session_history
410-
histories_directory + FileSep + "mysql_session_history"
372+
def interactive_to_string_map(interactive)
373+
# Check for true explicitly rather than just a value that is truthy.
374+
interactive == true ? '_interactive' : ''
411375
end
412376

413-
def mysql_session_interactive_history
414-
histories_directory + FileSep + "mysql_session_interactive_history"
415-
end
377+
def history_file_for_session_type(opts)
378+
return nil unless history_options_valid?(opts)
416379

417-
def mssql_session_history
418-
histories_directory + FileSep + "mssql_session_history"
419-
end
380+
session_type_name = opts[:session_type]
381+
interactive = interactive_to_string_map(opts[:interactive])
420382

421-
def mssql_session_interactive_history
422-
histories_directory + FileSep + "mssql_session_interactive_history"
383+
histories_directory + FileSep + "#{session_type_name}_session#{interactive}_history"
423384
end
424385

425386
def pry_history

lib/rex/post/mssql/ui/console.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ def initialize(session, opts={})
3030
self.client = session.client
3131
envchange = ::Rex::Proto::MSSQL::ClientMixin::ENVCHANGE
3232
prompt = "%undMSSQL @ #{client.peerinfo} (#{client.initial_info_for_envchange(envchange: envchange::DATABASE)[:new]})%clr"
33-
history_manager = Msf::Config.mssql_session_history
34-
super(prompt, '>', history_manager, nil, :mssql)
33+
history_file = Msf::Config.history_file_for_session_type(session_type: session.type, interactive: false)
34+
super(prompt, '>', history_file, nil, :mssql)
3535

3636
# Queued commands array
3737
self.commands = []

lib/rex/post/mysql/ui/console.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ def initialize(session)
2525
self.session = session
2626
self.client = session.client
2727
prompt = "%undMySQL @ #{client.peerinfo} (#{current_database})%clr"
28-
history_manager = Msf::Config.mysql_session_history
29-
super(prompt, '>', history_manager, nil, :mysql)
28+
history_file = Msf::Config.history_file_for_session_type(session_type: session.type, interactive: false)
29+
super(prompt, '>', history_file, nil, :mysql)
3030

3131
# Queued commands array
3232
self.commands = []

lib/rex/post/postgresql/ui/console.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ def initialize(session)
2929
self.session = session
3030
self.client = session.client
3131
prompt = "%undPostgreSQL @ #{client.peerinfo} (#{current_database})%clr"
32-
history_manager = Msf::Config.postgresql_session_history
33-
super(prompt, '>', history_manager, nil, :postgresql)
32+
history_file = Msf::Config.history_file_for_session_type(session_type: session.type, interactive: false)
33+
super(prompt, '>', history_file, nil, :postgresql)
3434

3535
# Queued commands array
3636
self.commands = []

lib/rex/post/sql/ui/console/interactive_sql_client.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ def _winch
6969
def _multiline_with_fallback
7070
name = session.type
7171
query = {}
72-
history_file = Msf::Config.send("#{name}_session_interactive_history")
72+
history_file = Msf::Config.history_file_for_session_type(session_type: name, interactive: true)
73+
return { status: :fail, errors: ["Unable to get history file for session type: #{name}"] } if history_file.nil?
7374

7475
# Multiline (Reline) and fallback (Readline) have separate history contexts as they are two different libraries.
7576
framework.history_manager.with_context(history_file: history_file , name: name, input_library: :reline) do

lib/rex/ui/text/shell/history_manager.rb

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,10 @@ def _debug=(value)
6161
@debug = value
6262
end
6363

64-
# Return a queue of threads that have not yet finished saving the history file to disk.
65-
# @return [Queue] a queue of threads that have not yet finished saving the history file to disk.
66-
def _remaining_work
67-
@remaining_work
64+
def _close
65+
event = { type: :close }
66+
@write_queue << event
67+
@remaining_work << event
6868
end
6969

7070
private
@@ -149,20 +149,19 @@ def load_history_file(context)
149149
history = map_library_to_history(context[:input_library])
150150

151151
begin
152-
File.open(history_file, 'r') do |f|
152+
File.open(history_file, 'rb') do |f|
153153
clear_library(context[:input_library])
154-
f.each do |line|
155-
chomped_line = line.chomp
154+
f.each_line(chomp: true) do |line|
156155
if context[:input_library] == :reline && history.last&.end_with?("\\")
157156
history.last.delete_suffix!("\\")
158-
history.last << "\n" << chomped_line
157+
history.last << "\n" << line
159158
else
160-
history << chomped_line
159+
history << line
161160
end
162161
end
163-
end
162+
end
164163
rescue Errno::EACCES, Errno::ENOENT => e
165-
$stderr.puts("Failed to open history file: #{history_file} with error: #{e}") if debug?
164+
elog "Failed to open history file: #{history_file} with error: #{e}"
166165
end
167166
end
168167

@@ -202,8 +201,10 @@ def write_history_file(history_file, cmds)
202201
remaining_work_ref = @remaining_work
203202

204203
@write_thread ||= Rex::ThreadFactory.spawn("HistoryManagerWriter", false) do
205-
while (write_queue_ref.size > 0 && event = write_queue_ref.pop)
204+
while (event = write_queue_ref.pop)
206205
begin
206+
break if event[:type] == :close
207+
207208
history_file = event[:history_file]
208209
cmds = event[:cmds]
209210

spec/lib/rex/ui/text/shell/history_manager_spec.rb

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
require 'tempfile'
77

88
RSpec.describe Rex::Ui::Text::Shell::HistoryManager do
9+
include_context 'wait_for_expect'
10+
911
subject { described_class.send(:new) }
1012
let(:readline_available) { false }
1113
let(:reline_available) { false }
@@ -139,6 +141,7 @@
139141
after(:each) do
140142
# https://ruby-doc.org/stdlib-2.5.3/libdoc/tempfile/rdoc/Tempfile.html#class-Tempfile-label-Explicit+close
141143
history_file.unlink
144+
subject._close
142145
end
143146

144147
[
@@ -148,7 +151,6 @@
148151
].each do |test|
149152
context "when storing #{test[:history_size]} lines" do
150153
it "correctly stores #{test[:expected_size]} lines" do
151-
allow(subject).to receive(:_remaining_work).and_call_original
152154
allow(subject).to receive(:store_history_file).and_call_original
153155
allow(subject).to receive(:map_library_to_history).and_return(history_mock)
154156

@@ -161,9 +163,9 @@
161163

162164
subject.send(:store_history_file, context)
163165

164-
sleep(0.1) until subject._remaining_work.empty?
165-
166-
expect(history_file.read.split("\n").count).to eq(test[:expected_size])
166+
wait_for_expect do
167+
expect(history_file.read.split("\n").count).to eq(test[:expected_size])
168+
end
167169
end
168170
end
169171
end
@@ -177,12 +179,13 @@
177179

178180
after(:each) do
179181
history_file.unlink
182+
subject._close
180183
end
181184

182185
context 'when history file is not accessible' do
183186
it 'the library history remains unchanged' do
184187
allow(subject).to receive(:map_library_to_history).and_return(history_mock)
185-
history_file = ::File.join('does/not/exist/history')
188+
history_file = ::File.join('does', 'not', 'exist', 'history')
186189
context = { input_library: :readline, history_file: history_file, name: 'history' }
187190

188191
subject.send(:load_history_file, context)

0 commit comments

Comments
 (0)