Consolidate option dump remove condition datastore condition on tests

This commit is contained in:
Dean Welch 2024-02-20 14:09:17 +00:00
parent a3d8b0f77a
commit 175d584ff7
3 changed files with 99 additions and 137 deletions

View File

@ -568,116 +568,77 @@ class ReadableText
# @param indent [String] the indentation to use.
# @param missing [Boolean] dump only empty required options.
# @return [String] the string form of the information.
def self.dump_options(mod, indent = '', missing = false)
options = mod.options.map { |_name, option| option }
options_grouped_by_conditions = options.group_by(&:conditions)
def self.dump_options(mod, indent = '', missing = false, advanced: false, evasion: false)
filtered_options = mod.options.filter_map { |_name, opt| opt if opt.advanced? == advanced && opt.evasion? == evasion }
options_with_conditions = ''.dup
options_without_conditions = ''.dup
options_grouped_by_conditions = filtered_options.group_by(&:conditions)
options_grouped_by_conditions.each do |conditions, options|
tbl = Rex::Text::Table.new(
'Indent' => indent.length,
'Columns' =>
[
'Name',
'Current Setting',
'Required',
'Description'
])
option_tables = []
options.sort_by(&:name).each do |opt|
name = opt.name
if mod.datastore.is_a?(Msf::DataStoreWithFallbacks)
val = mod.datastore[name]
else
val = mod.datastore[name].nil? ? opt.default : mod.datastore[name]
end
options_grouped_by_conditions.sort.each do |conditions, options|
tbl = options_table(missing, mod, options, indent)
next if (opt.advanced?)
next if (opt.evasion?)
next if (missing && opt.valid?(val))
desc = opt.desc.dup
# Hint at RPORT proto by regexing mixins
if name == 'RPORT' && opt.kind_of?(Msf::OptPort)
mod.class.included_modules.each do |m|
case m.name
when /tcp/i, /HttpClient$/
desc << ' (TCP)'
break
when /udp/i
desc << ' (UDP)'
break
end
end
end
tbl << [ name, opt.display_value(val), opt.required? ? "yes" : "no", desc ]
end
next if conditions.any? && tbl.rows.empty?
next if tbl.rows.empty?
if conditions.any?
options_with_conditions << "\n\n#{indent}When #{Msf::OptCondition.format_conditions(mod, options.first)}:\n\n"
options_with_conditions << tbl.to_s
option_tables << "#{indent}When #{Msf::OptCondition.format_conditions(mod, options.first)}:\n\n#{tbl}"
else
options_without_conditions << tbl.to_s
option_tables << tbl.to_s
end
end
result = "#{options_without_conditions}#{options_with_conditions}"
result = option_tables.join("\n\n")
result
end
def self.options_table(missing, mod, options, indent)
tbl = Rex::Text::Table.new(
'Indent' => indent.length,
'Columns' =>
[
'Name',
'Current Setting',
'Required',
'Description'
]
)
options.sort_by(&:name).each do |opt|
name = opt.name
if mod.datastore.is_a?(Msf::DataStoreWithFallbacks)
val = mod.datastore[name]
else
val = mod.datastore[name].nil? ? opt.default : mod.datastore[name]
end
next if (missing && opt.valid?(val))
desc = opt.desc.dup
# Hint at RPORT proto by regexing mixins
if name == 'RPORT' && opt.kind_of?(Msf::OptPort)
mod.class.included_modules.each do |m|
case m.name
when /tcp/i, /HttpClient$/
desc << ' (TCP)'
break
when /udp/i
desc << ' (UDP)'
break
end
end
end
tbl << [name, opt.display_value(val), opt.required? ? "yes" : "no", desc]
end
tbl
end
# Dumps the advanced options associated with the supplied module.
#
# @param mod [Msf::Module] the module.
# @param indent [String] the indentation to use.
# @return [String] the string form of the information.
def self.dump_advanced_options(mod, indent = '')
options = mod.options.map { |_name, option| option }
options_grouped_by_conditions = options.group_by(&:conditions)
options_with_conditions = ''.dup
options_without_conditions = ''.dup
options_grouped_by_conditions.each do |conditions, options|
tbl = Rex::Text::Table.new(
'Indent' => indent.length,
'Columns' =>
[
'Name',
'Current Setting',
'Required',
'Description'
])
options.sort_by(&:name).each do |opt|
next unless opt.advanced?
name = opt.name
if mod.datastore.is_a?(Msf::DataStoreWithFallbacks)
val = mod.datastore[name]
else
val = mod.datastore[name].nil? ? opt.default : mod.datastore[name]
end
tbl << [ name, opt.display_value(val), opt.required? ? "yes" : "no", opt.desc ]
end
next if conditions.any? && tbl.rows.empty?
if conditions.any?
options_with_conditions << "\n\n#{indent}Active when #{Msf::OptCondition.format_conditions(mod, options.first)}:\n\n"
options_with_conditions << tbl.to_s
else
options_without_conditions << tbl.to_s
end
end
result = "#{options_without_conditions}#{options_with_conditions}"
result
return dump_options(mod, indent, advanced: true)
end
# Dumps the evasion options associated with the supplied module.
@ -686,46 +647,7 @@ class ReadableText
# @param indent [String] the indentation to use.
# @return [String] the string form of the information.
def self.dump_evasion_options(mod, indent = '')
options = mod.options.map { |_name, option| option }
options_grouped_by_conditions = options.group_by(&:conditions)
options_with_conditions = ''.dup
options_without_conditions = ''.dup
options_grouped_by_conditions.each do |conditions, options|
tbl = Rex::Text::Table.new(
'Indent' => indent.length,
'Columns' =>
[
'Name',
'Current Setting',
'Required',
'Description'
])
options.sort_by(&:name).each do |opt|
next unless opt.evasion?
name = opt.name
if mod.datastore.is_a?(Msf::DataStoreWithFallbacks)
val = mod.datastore[name]
else
val = mod.datastore[name].nil? ? opt.default : mod.datastore[name]
end
tbl << [ name, opt.display_value(val), opt.required? ? "yes" : "no", opt.desc ]
end
next if conditions.any? && tbl.rows.empty?
if conditions.any?
options_with_conditions << "\n\n#{indent}When #{Msf::OptCondition.format_conditions(mod, options.first)}:\n\n"
options_with_conditions << tbl.to_s
else
options_without_conditions << tbl.to_s
end
end
result = "#{options_without_conditions}#{options_with_conditions}"
result
return dump_options(mod, indent, evasion: true)
end
# Dumps the references associated with the supplied module.

View File

@ -54,8 +54,15 @@ RSpec.describe Msf::Serializer::ReadableText do
]
end
let(:default_evasion_module_options) do
[
Msf::OptInt.new('EVASION_TEST_OPTION', [ true, 'The evasion test option'])
]
end
let(:module_options) { default_module_options }
let(:advanced_module_options) { default_advanced_module_options }
let(:evasion_module_options) { default_evasion_module_options }
# (see Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::Options#kerberos_auth_options)
def kerberos_auth_options(protocol:, auth_methods:)
@ -83,6 +90,7 @@ RSpec.describe Msf::Serializer::ReadableText do
mod = mod_klass.new
mod.send(:register_options, module_options)
mod.send(:register_advanced_options, advanced_module_options)
mod.send(:register_evasion_options, evasion_module_options)
mock_framework = instance_double(::Msf::Framework, datastore: Msf::DataStore.new)
allow(mod).to receive(:framework).and_return(mock_framework)
mod
@ -105,7 +113,7 @@ RSpec.describe Msf::Serializer::ReadableText do
allow(Rex::Text::Table).to receive(:wrapped_tables?).and_return(true)
end
describe '.dump_datastore', if: ENV['DATASTORE_FALLBACKS'] do
describe '.dump_datastore' do
context 'when the datastore is empty' do
it 'returns the datastore as a table' do
expect(described_class.dump_datastore('Table name', Msf::DataStore.new, indent_length)).to match_table <<~TABLE
@ -126,6 +134,7 @@ RSpec.describe Msf::Serializer::ReadableText do
Name Value
---- -----
DigestAlgorithm SHA256
EVASION_TEST_OPTION
FloatValue 5
NewOptionName
OptionWithModuleDefault false
@ -144,7 +153,7 @@ RSpec.describe Msf::Serializer::ReadableText do
end
end
describe '.dump_options', if: ENV['DATASTORE_FALLBACKS'] do
describe '.dump_options' do
context 'when missing is false' do
it 'returns the options as a table' do
expect(described_class.dump_options(aux_mod_with_set_options, indent_string, false)).to match_table <<~TABLE
@ -175,7 +184,7 @@ RSpec.describe Msf::Serializer::ReadableText do
end
end
describe '.dump_advanced_options', if: ENV['DATASTORE_FALLBACKS'] do
describe '.dump_advanced_options' do
context 'when kerberos options are present' do
let(:advanced_module_options) do
[
@ -194,7 +203,7 @@ RSpec.describe Msf::Serializer::ReadableText do
Winrm::Auth auto yes The Authentication mechanism to use (Accepted: auto, ntlm, kerberos, plaintext)
Active when Winrm::Auth is kerberos:
When Winrm::Auth is kerberos:
Name Current Setting Required Description
---- --------------- -------- -----------
@ -207,6 +216,37 @@ RSpec.describe Msf::Serializer::ReadableText do
end
end
describe '.dump_evasion_options' do
context 'when kerberos options are present' do
let(:evasion_module_options) do
[
*default_evasion_module_options,
*kerberos_auth_options(protocol: 'Winrm', auth_methods: Msf::Exploit::Remote::AuthOption::WINRM_OPTIONS),
]
end
it 'returns the options as a table' do
expect(described_class.dump_evasion_options(aux_mod_with_set_options, indent_string)).to match_table <<~TABLE
Name Current Setting Required Description
---- --------------- -------- -----------
EVASION_TEST_OPTION yes The evasion test option
Winrm::Auth auto yes The Authentication mechanism to use (Accepted: auto, ntlm, kerberos, plaintext)
When Winrm::Auth is kerberos:
Name Current Setting Required Description
---- --------------- -------- -----------
DomainControllerRhost no The resolvable rhost for the Domain Controller
Winrm::Krb5Ccname no The ccache file to use for kerberos authentication
Winrm::KrbOfferedEncryptionTypes AES256,AES128,RC4-HMAC,DES-CBC-MD5,DES3-CBC-SHA1 yes Kerberos encryption types to offer
Winrm::Rhostname no The rhostname which is required for kerberos - the SPN
TABLE
end
end
end
describe '.dump_description' do
context 'when the module description is nil' do
it 'dumps the module description' do

View File

@ -83,7 +83,7 @@ RSpec.describe Msf::Ui::Console::CommandDispatcher::Core do
end
end
describe '#cmd_set', if: ENV['DATASTORE_FALLBACKS'] do
describe '#cmd_set' do
let(:mod) { nil }
before(:each) do
@ -198,7 +198,7 @@ RSpec.describe Msf::Ui::Console::CommandDispatcher::Core do
end
end
describe '#cmd_unset', if: ENV['DATASTORE_FALLBACKS'] do
describe '#cmd_unset' do
let(:mod) { nil }
before(:each) do