mirror of
https://github.com/rapid7/metasploit-framework
synced 2024-11-12 11:52:01 +01:00
Land #14985, Updates the JSON RPC Web service to correctly use framework's database configuration, and adds support for foregrounding the JSON RPC web service
This commit is contained in:
commit
dbd0ac8203
313
lib/msf/core/db_connector.rb
Normal file
313
lib/msf/core/db_connector.rb
Normal file
@ -0,0 +1,313 @@
|
||||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
|
||||
##
|
||||
#
|
||||
# This class is used when wanting to connect/disconnect framework
|
||||
# from a particular database or http service
|
||||
#
|
||||
##
|
||||
module DbConnector
|
||||
DbConfigGroup = 'framework/database'
|
||||
|
||||
#
|
||||
# Connect to a database by using the default framework config, or the config file provided
|
||||
#
|
||||
def self.db_connect_from_config(framework, path = nil)
|
||||
begin
|
||||
conf = Msf::Config.load(path)
|
||||
rescue StandardError => e
|
||||
wlog("Failed to load configuration: #{e}")
|
||||
return {}
|
||||
end
|
||||
|
||||
if conf.group?(DbConfigGroup)
|
||||
conf[DbConfigGroup].each_pair do |k, v|
|
||||
next unless k.downcase == 'default_db'
|
||||
|
||||
ilog 'Default data service found. Attempting to connect...'
|
||||
db_name = v
|
||||
config = load_db_config(db_name, path)
|
||||
if config
|
||||
if framework.db.active && config[:url] !~ /http/
|
||||
ilog 'Existing local data connection found. Disconnecting first.'
|
||||
db_disconnect(framework)
|
||||
end
|
||||
|
||||
return db_connect(framework, config).merge(data_service_name: db_name)
|
||||
else
|
||||
elog "Config entry for '#{db_name}' could not be found. Config file might be corrupt."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
{}
|
||||
end
|
||||
|
||||
# Connect to the required database
|
||||
#
|
||||
# @Example Connect to a remote http service
|
||||
# db_connect(
|
||||
# framework,
|
||||
# {
|
||||
# url: 'https://localhost:5443',
|
||||
# cert: '/Users/user/.msf4/msf-ws-cert.pem',
|
||||
# skip_verify: true,
|
||||
# api_token: 'b1ca123e2f160a8a1fbf79baed180b8dc480de5b994f53eee42e57771e3f65e13bec737e4a4acbb2'
|
||||
# }
|
||||
# )
|
||||
def self.db_connect(framework, opts = {})
|
||||
unless framework.db.driver
|
||||
return { error: 'No database driver installed.'}
|
||||
end
|
||||
|
||||
if !opts[:url] && !opts[:yaml_file]
|
||||
return { error: 'A URL or saved data service name is required.' }
|
||||
end
|
||||
|
||||
if opts[:url] =~ /http/
|
||||
new_conn_type = 'http'
|
||||
else
|
||||
new_conn_type = framework.db.driver
|
||||
end
|
||||
|
||||
# Currently only able to be connected to one DB at a time
|
||||
if framework.db.connection_established?
|
||||
# But the http connection still requires a local database to support AR, so we have to allow that
|
||||
# Don't allow more than one HTTP service, though
|
||||
if new_conn_type != 'http' || framework.db.get_services_metadata.count >= 2
|
||||
return {
|
||||
error: 'Connection already established. Only one connection is allowed at a time. Run db_disconnect first if you wish to connect to a different data service.'
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
if opts[:yaml_file]
|
||||
db_connect_yaml(framework, opts)
|
||||
elsif new_conn_type == 'http'
|
||||
db_connect_http(framework, as_connection_options(opts))
|
||||
elsif new_conn_type == 'postgresql'
|
||||
db_connect_postgresql(framework, as_connection_options(opts))
|
||||
else
|
||||
{
|
||||
error: "This database driver #{new_conn_type} is not currently supported"
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Disconnect from the currently connected database. This will gracefully fallback
|
||||
# from a remote data service to a local postgres instance if configured correctly.
|
||||
#
|
||||
def self.db_disconnect(framework)
|
||||
result = { old_data_service_name: framework.db.name }
|
||||
unless framework.db.driver
|
||||
result[:error] = 'No database driver installed.'
|
||||
return result
|
||||
end
|
||||
|
||||
if framework.db.active
|
||||
if framework.db.driver == 'http'
|
||||
begin
|
||||
framework.db.delete_current_data_service
|
||||
local_db_url = build_postgres_url
|
||||
local_name = data_service_search(url: local_db_url)
|
||||
result[:data_service_name] = local_name
|
||||
rescue StandardError => e
|
||||
result[:error] = e.message
|
||||
end
|
||||
else
|
||||
framework.db.disconnect
|
||||
result[:data_service_name] = nil
|
||||
end
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
#
|
||||
# Connect to a database via the supplied yaml file
|
||||
#
|
||||
def self.db_connect_yaml(framework, opts)
|
||||
file = opts[:yaml_file] || ::File.join(Msf::Config.get_config_root, 'database.yml')
|
||||
file = ::File.expand_path(file)
|
||||
unless ::File.exist?(file)
|
||||
return { error: 'File not found' }
|
||||
end
|
||||
begin
|
||||
db = YAML.load(::File.read(file))['production']
|
||||
rescue => _e
|
||||
return { error: 'File did not contain valid production database credentials' }
|
||||
end
|
||||
|
||||
framework.db.connect(db)
|
||||
|
||||
local_db_url = build_postgres_url
|
||||
local_name = data_service_search(url: local_db_url)
|
||||
return {
|
||||
result: 'Connected to the database specified in the YAML file',
|
||||
data_service_name: local_name
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# Connect to an existing http database
|
||||
#
|
||||
def self.db_connect_http(framework, opts)
|
||||
# local database is required to use Mdm objects
|
||||
unless framework.db.active
|
||||
error = 'No local database connected, meaning some Metasploit features will not be available. A full list of '\
|
||||
'the affected features & database setup instructions can be found here: '\
|
||||
'https://github.com/rapid7/metasploit-framework/wiki/msfdb:-Database-Features-&-How-to-Set-up-a-Database-for-Metasploit'
|
||||
|
||||
return {
|
||||
error: error
|
||||
}
|
||||
end
|
||||
|
||||
uri = db_parse_db_uri_http(opts[:url])
|
||||
|
||||
remote_data_service = Metasploit::Framework::DataService::RemoteHTTPDataService.new(uri.to_s, opts)
|
||||
begin
|
||||
framework.db.register_data_service(remote_data_service)
|
||||
framework.db.workspace = framework.db.default_workspace
|
||||
{
|
||||
result: "Connected to HTTP data service: #{remote_data_service.name}",
|
||||
data_service_name: data_service_search(url: opts[:url])
|
||||
}
|
||||
rescue => e
|
||||
{
|
||||
error: "Failed to connect to the HTTP data service: #{e.message}"
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Connect to an existing Postgres database
|
||||
#
|
||||
def self.db_connect_postgresql(framework, cli_opts)
|
||||
info = db_parse_db_uri_postgresql(cli_opts[:url])
|
||||
opts = { 'adapter' => 'postgresql' }
|
||||
|
||||
opts['username'] = info[:user] if (info[:user])
|
||||
opts['password'] = info[:pass] if (info[:pass])
|
||||
opts['database'] = info[:name]
|
||||
opts['host'] = info[:host] if (info[:host])
|
||||
opts['port'] = info[:port] if (info[:port])
|
||||
|
||||
opts['pass'] ||= ''
|
||||
|
||||
# Do a little legwork to find the real database socket
|
||||
if !opts['host']
|
||||
while(true)
|
||||
done = false
|
||||
dirs = %W{ /var/run/postgresql /tmp }
|
||||
dirs.each do |dir|
|
||||
if ::File.directory?(dir)
|
||||
d = ::Dir.new(dir)
|
||||
d.entries.grep(/^\.s\.PGSQL.(\d+)$/).each do |ent|
|
||||
opts['port'] = ent.split('.')[-1].to_i
|
||||
opts['host'] = dir
|
||||
done = true
|
||||
break
|
||||
end
|
||||
end
|
||||
break if done
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
# Default to loopback
|
||||
unless opts['host']
|
||||
opts['host'] = '127.0.0.1'
|
||||
end
|
||||
|
||||
if framework.db.connect(opts) && framework.db.connection_established?
|
||||
{
|
||||
result: "Connected to Postgres data service: #{info[:host]}/#{info[:name]}",
|
||||
data_service_name: data_service_search(url: opts[:url]) || framework.db.name
|
||||
}
|
||||
else
|
||||
{
|
||||
error: "Failed to connect to the Postgres data service: #{framework.db.error}"
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def self.db_parse_db_uri_postgresql(path)
|
||||
res = {}
|
||||
if path
|
||||
auth, dest = path.split('@')
|
||||
(dest = auth and auth = nil) if not dest
|
||||
# remove optional scheme in database url
|
||||
auth = auth.sub(/^\w+:\/\//, '') if auth
|
||||
res[:user],res[:pass] = auth.split(':') if auth
|
||||
targ,name = dest.split('/')
|
||||
(name = targ and targ = nil) if not name
|
||||
res[:host],res[:port] = targ.split(':') if targ
|
||||
end
|
||||
res[:name] = name || 'metasploit3'
|
||||
res
|
||||
end
|
||||
|
||||
def self.db_parse_db_uri_http(path)
|
||||
URI.parse(path)
|
||||
end
|
||||
|
||||
def self.build_postgres_url
|
||||
conn_params = ApplicationRecord.connection_config
|
||||
url = ''
|
||||
url += "#{conn_params[:username]}" if conn_params[:username]
|
||||
url += ":#{conn_params[:password]}" if conn_params[:password]
|
||||
url += "@#{conn_params[:host]}" if conn_params[:host]
|
||||
url += ":#{conn_params[:port]}" if conn_params[:port]
|
||||
url += "/#{conn_params[:database]}" if conn_params[:database]
|
||||
url
|
||||
end
|
||||
|
||||
#
|
||||
# Search for a human readable data service name based on the search criteria
|
||||
# The search criteria can match against a service name or url
|
||||
#
|
||||
def self.data_service_search(name: nil, url: nil)
|
||||
conf = Msf::Config.load
|
||||
result = nil
|
||||
|
||||
conf.each_pair do |key, value|
|
||||
conf_name = key.split('/').last
|
||||
has_name_match = !name.nil? && (conf_name == name)
|
||||
has_url_match = !url.nil? && (value.is_a?(Hash) && value['url'] == url)
|
||||
if has_name_match || has_url_match
|
||||
result = conf_name
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
def self.load_db_config(db_name, path = nil)
|
||||
conf = Msf::Config.load(path)
|
||||
conf_options = conf["#{DbConfigGroup}/#{db_name}"]
|
||||
return unless conf_options
|
||||
|
||||
conf_options.transform_keys(&:to_sym)
|
||||
end
|
||||
|
||||
def self.as_connection_options(conf_options)
|
||||
opts = {}
|
||||
https_opts = {}
|
||||
if conf_options
|
||||
opts[:url] = conf_options[:url] if conf_options[:url]
|
||||
opts[:api_token] = conf_options[:api_token] if conf_options[:api_token]
|
||||
https_opts[:cert] = conf_options[:cert] if conf_options[:cert]
|
||||
https_opts[:skip_verify] = conf_options[:skip_verify] if conf_options[:skip_verify]
|
||||
else
|
||||
return
|
||||
end
|
||||
|
||||
opts[:https_opts] = https_opts unless https_opts.empty?
|
||||
opts
|
||||
end
|
||||
end
|
||||
end
|
@ -122,7 +122,6 @@ class Msf::DBManager
|
||||
#
|
||||
|
||||
def initialize(framework, opts = {})
|
||||
|
||||
self.framework = framework
|
||||
self.migrated = false
|
||||
self.modules_cached = false
|
||||
@ -176,7 +175,6 @@ class Msf::DBManager
|
||||
end
|
||||
|
||||
def init_db(opts)
|
||||
|
||||
init_success = false
|
||||
|
||||
# Append any migration paths necessary to bring the database online
|
||||
@ -186,28 +184,24 @@ class Msf::DBManager
|
||||
end
|
||||
end
|
||||
|
||||
if connection_established?
|
||||
after_establish_connection
|
||||
configuration_pathname = Metasploit::Framework::Database.configurations_pathname(path: opts['DatabaseYAML'])
|
||||
|
||||
if configuration_pathname.nil?
|
||||
self.error = "No database YAML file"
|
||||
else
|
||||
configuration_pathname = Metasploit::Framework::Database.configurations_pathname(path: opts['DatabaseYAML'])
|
||||
|
||||
if configuration_pathname.nil?
|
||||
self.error = "No database YAML file"
|
||||
if configuration_pathname.readable?
|
||||
# parse specified database YAML file
|
||||
dbinfo = YAML.load_file(configuration_pathname) || {}
|
||||
dbenv = opts['DatabaseEnv'] || Rails.env
|
||||
db_opts = dbinfo[dbenv]
|
||||
else
|
||||
if configuration_pathname.readable?
|
||||
# parse specified database YAML file
|
||||
dbinfo = YAML.load_file(configuration_pathname) || {}
|
||||
dbenv = opts['DatabaseEnv'] || Rails.env
|
||||
db = dbinfo[dbenv]
|
||||
else
|
||||
elog("Warning, #{configuration_pathname} is not readable. Try running as root or chmod.")
|
||||
end
|
||||
elog("Warning, #{configuration_pathname} is not readable. Try running as root or chmod.")
|
||||
end
|
||||
|
||||
if not db
|
||||
elog("No database definition for environment #{dbenv}")
|
||||
else
|
||||
init_success = connect(db)
|
||||
end
|
||||
if db_opts
|
||||
init_success = connect(db_opts)
|
||||
else
|
||||
elog("No database definition for environment #{dbenv}")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -26,6 +26,10 @@ module Msf::WebServices
|
||||
def framework
|
||||
settings.framework
|
||||
end
|
||||
|
||||
def get_db
|
||||
framework.db
|
||||
end
|
||||
end
|
||||
|
||||
def self.registered(app)
|
||||
@ -35,50 +39,41 @@ module Msf::WebServices
|
||||
app.set :data_service_api_token, ENV.fetch('MSF_WS_DATA_SERVICE_API_TOKEN', nil)
|
||||
app.set :data_service_cert, ENV.fetch('MSF_WS_DATA_SERVICE_CERT', nil)
|
||||
app.set :data_service_skip_verify, to_bool(ENV.fetch('MSF_WS_DATA_SERVICE_SKIP_VERIFY', false))
|
||||
|
||||
@@framework = nil
|
||||
# Create simplified instance of the framework
|
||||
app.set :framework, Proc.new {
|
||||
app.set :framework, (proc {
|
||||
@@framework ||= begin
|
||||
init_framework_opts = {
|
||||
'Logger' => ENV.fetch('MSF_WS_DATA_SERVICE_LOGGER', nil)
|
||||
'Logger' => ENV.fetch('MSF_WS_DATA_SERVICE_LOGGER', nil),
|
||||
# SkipDatabaseInit false is the default behavior, however for explicitness - note that framework first
|
||||
# connects to a local database as a pre-requisite to connecting to a remote service to correctly
|
||||
# configure active record
|
||||
'SkipDatabaseInit' => false
|
||||
}
|
||||
framework = Msf::Simple::Framework.create(init_framework_opts)
|
||||
|
||||
if !app.settings.data_service_url.nil? && !app.settings.data_service_url.empty?
|
||||
framework_db_connect_http_data_service(framework: framework,
|
||||
data_service_url: app.settings.data_service_url,
|
||||
api_token: app.settings.data_service_api_token,
|
||||
cert: app.settings.data_service_cert,
|
||||
skip_verify: app.settings.data_service_skip_verify)
|
||||
end
|
||||
Msf::WebServices::FrameworkExtension.db_connect(framework, app)
|
||||
|
||||
framework
|
||||
end
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
def self.framework_db_connect_http_data_service(
|
||||
framework:, data_service_url:, api_token: nil, cert: nil, skip_verify: false)
|
||||
# local database is required to use Mdm objects
|
||||
unless framework.db.active
|
||||
raise "No local database connected"
|
||||
def self.db_connect(framework, app)
|
||||
if !app.settings.data_service_url.nil? && !app.settings.data_service_url.empty?
|
||||
options = {
|
||||
url: app.settings.data_service_url,
|
||||
api_token: app.settings.data_service_api_token,
|
||||
cert: app.settings.data_service_cert,
|
||||
skip_verify: app.settings.data_service_skip_verify
|
||||
}
|
||||
db_result = Msf::DbConnector.db_connect(framework, options)
|
||||
else
|
||||
db_result = Msf::DbConnector.db_connect_from_config(framework)
|
||||
end
|
||||
|
||||
opts = {}
|
||||
https_opts = {}
|
||||
opts[:url] = data_service_url unless data_service_url.nil?
|
||||
opts[:api_token] = api_token unless api_token.nil?
|
||||
https_opts[:cert] = cert unless cert.nil?
|
||||
https_opts[:skip_verify] = skip_verify if skip_verify
|
||||
opts[:https_opts] = https_opts unless https_opts.empty?
|
||||
|
||||
begin
|
||||
uri = URI.parse(data_service_url)
|
||||
remote_data_service = Metasploit::Framework::DataService::RemoteHTTPDataService.new(uri.to_s, opts)
|
||||
framework.db.register_data_service(remote_data_service)
|
||||
framework.db.workspace = framework.db.default_workspace
|
||||
rescue => e
|
||||
raise "Failed to connect to the HTTP data service: #{e.message}"
|
||||
if db_result[:error]
|
||||
raise db_result[:error]
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
require 'rack'
|
||||
require 'metasploit/framework/parsed_options/remote_db'
|
||||
|
||||
# TODO: This functionality isn't fully used currently, it should be integrated and called from the top level msfdb.rb file
|
||||
class Msf::WebServices::HttpDBManagerService
|
||||
|
||||
def start(opts)
|
||||
|
@ -75,6 +75,14 @@ module CommandDispatcher
|
||||
dlog("Call stack:\n#{$@.join("\n")}", 'core', LEV_1)
|
||||
end
|
||||
|
||||
#
|
||||
# Load the configuration required for this CommandDispatcher, configuring
|
||||
# any internal state as required.
|
||||
#
|
||||
def load_config(_path = nil)
|
||||
# noop
|
||||
end
|
||||
|
||||
#
|
||||
# Return the subdir of the `documentation/` directory that should be used
|
||||
# to find usage documentation
|
||||
|
@ -71,6 +71,21 @@ class Db
|
||||
]
|
||||
end
|
||||
|
||||
#
|
||||
# Attempts to connect to the previously configured database, and additionally keeps track of
|
||||
# the currently loaded data service.
|
||||
#
|
||||
def load_config(path = nil)
|
||||
result = Msf::DbConnector.db_connect_from_config(framework, path)
|
||||
|
||||
if result[:error]
|
||||
print_error(result[:error])
|
||||
end
|
||||
if result[:data_service_name]
|
||||
@current_data_service = result[:data_service_name]
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Returns true if the db is connected, prints an error and returns
|
||||
# false if not.
|
||||
@ -401,6 +416,7 @@ class Db
|
||||
mode << :add
|
||||
when '-d','--delete'
|
||||
mode << :delete
|
||||
return
|
||||
when '-c','-C'
|
||||
list = args.shift
|
||||
if(!list)
|
||||
@ -1724,42 +1740,39 @@ class Db
|
||||
return if not db_check_driver
|
||||
|
||||
opts = {}
|
||||
https_opts = {}
|
||||
while (arg = args.shift)
|
||||
case arg
|
||||
when '-h', '--help'
|
||||
cmd_db_connect_help
|
||||
return
|
||||
when '-y', '--yaml'
|
||||
yaml_file = args.shift
|
||||
opts[:yaml_file] = args.shift
|
||||
when '-c', '--cert'
|
||||
https_opts[:cert] = args.shift
|
||||
opts[:cert] = args.shift
|
||||
when '-t', '--token'
|
||||
opts[:api_token] = args.shift
|
||||
when '-l', '--list-services'
|
||||
list_saved_data_services
|
||||
return
|
||||
when '-n', '--name'
|
||||
name = args.shift
|
||||
if name =~ /\/|\[|\]/
|
||||
opts[:name] = args.shift
|
||||
if opts[:name] =~ /\/|\[|\]/
|
||||
print_error "Provided name contains an invalid character. Aborting connection."
|
||||
return
|
||||
end
|
||||
when '--skip-verify'
|
||||
https_opts[:skip_verify] = true
|
||||
opts[:skip_verify] = true
|
||||
else
|
||||
found_name = data_service_search(arg)
|
||||
found_name = ::Msf::DbConnector.data_service_search(name: arg)
|
||||
if found_name
|
||||
opts = load_db_config(found_name)
|
||||
opts = ::Msf::DbConnector.load_db_config(found_name)
|
||||
else
|
||||
opts[:url] = arg
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
opts[:https_opts] = https_opts unless https_opts.empty?
|
||||
|
||||
if !opts[:url] && !yaml_file
|
||||
if !opts[:url] && !opts[:yaml_file]
|
||||
print_error 'A URL or saved data service name is required.'
|
||||
print_line
|
||||
cmd_db_connect_help
|
||||
@ -1786,36 +1799,27 @@ class Db
|
||||
end
|
||||
end
|
||||
|
||||
if yaml_file
|
||||
if (yaml_file and not ::File.exist? ::File.expand_path(yaml_file))
|
||||
print_error("File not found")
|
||||
return
|
||||
end
|
||||
file = yaml_file || ::File.join(Msf::Config.get_config_root, "database.yml")
|
||||
file = ::File.expand_path(file)
|
||||
if (::File.exist? file)
|
||||
db = YAML.load(::File.read(file))['production']
|
||||
framework.db.connect(db)
|
||||
print_line('Connected to the database specified in the YAML file.')
|
||||
return
|
||||
end
|
||||
result = Msf::DbConnector.db_connect(framework, opts)
|
||||
if result[:error]
|
||||
print_error result[:error]
|
||||
return
|
||||
end
|
||||
|
||||
meth = "db_connect_#{new_conn_type}"
|
||||
if(self.respond_to?(meth, true))
|
||||
self.send(meth, opts)
|
||||
else
|
||||
print_error("This database driver #{new_conn_type} is not currently supported")
|
||||
if result[:result]
|
||||
print_status result[:result]
|
||||
end
|
||||
|
||||
if framework.db.active
|
||||
name = opts[:name]
|
||||
if !name || name.empty?
|
||||
if found_name
|
||||
name = found_name
|
||||
elsif result[:data_service_name]
|
||||
name = result[:data_service_name]
|
||||
else
|
||||
name = Rex::Text.rand_text_alphanumeric(8)
|
||||
end
|
||||
end
|
||||
|
||||
save_db_to_config(framework.db, name)
|
||||
@current_data_service = name
|
||||
end
|
||||
@ -1836,25 +1840,20 @@ class Db
|
||||
return
|
||||
end
|
||||
|
||||
db_name = framework.db.name
|
||||
previous_name = framework.db.name
|
||||
result = Msf::DbConnector.db_disconnect(framework)
|
||||
|
||||
if framework.db.active
|
||||
if framework.db.driver == 'http'
|
||||
begin
|
||||
framework.db.delete_current_data_service
|
||||
local_db_url = build_postgres_url
|
||||
local_name = data_service_search(local_db_url)
|
||||
@current_data_service = local_name
|
||||
rescue => e
|
||||
print_error "Unable to disconnect from the data service: #{e.message}"
|
||||
end
|
||||
else
|
||||
framework.db.disconnect
|
||||
@current_data_service = nil
|
||||
end
|
||||
print_line "Successfully disconnected from the data service: #{db_name}."
|
||||
if result[:error]
|
||||
print_error "Unable to disconnect from the data service: #{@current_data_service}"
|
||||
print_error result[:error]
|
||||
elsif result[:old_data_service_name].nil?
|
||||
print_error 'Not currently connected to a data service.'
|
||||
else
|
||||
print_error "Not currently connected to a data service."
|
||||
print_line "Successfully disconnected from the data service: #{previous_name}."
|
||||
@current_data_service = result[:data_service_name]
|
||||
if @current_data_service
|
||||
print_line "Now connected to: #{@current_data_service}."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1912,7 +1911,7 @@ class Db
|
||||
print_error "There was an error saving the data service configuration: #{e.message}"
|
||||
end
|
||||
else
|
||||
url = build_postgres_url
|
||||
url = Msf::DbConnector.build_postgres_url
|
||||
config_opts['url'] = url
|
||||
Msf::Config.save(config_path => config_opts)
|
||||
end
|
||||
@ -1971,106 +1970,11 @@ class Db
|
||||
true
|
||||
end
|
||||
|
||||
#
|
||||
# Database management: Postgres
|
||||
#
|
||||
|
||||
#
|
||||
# Connect to an existing Postgres database
|
||||
#
|
||||
def db_connect_postgresql(cli_opts)
|
||||
info = db_parse_db_uri_postgresql(cli_opts[:url])
|
||||
opts = { 'adapter' => 'postgresql' }
|
||||
|
||||
opts['username'] = info[:user] if (info[:user])
|
||||
opts['password'] = info[:pass] if (info[:pass])
|
||||
opts['database'] = info[:name]
|
||||
opts['host'] = info[:host] if (info[:host])
|
||||
opts['port'] = info[:port] if (info[:port])
|
||||
|
||||
opts['pass'] ||= ''
|
||||
|
||||
# Do a little legwork to find the real database socket
|
||||
if(! opts['host'])
|
||||
while(true)
|
||||
done = false
|
||||
dirs = %W{ /var/run/postgresql /tmp }
|
||||
dirs.each do |dir|
|
||||
if(::File.directory?(dir))
|
||||
d = ::Dir.new(dir)
|
||||
d.entries.grep(/^\.s\.PGSQL.(\d+)$/).each do |ent|
|
||||
opts['port'] = ent.split('.')[-1].to_i
|
||||
opts['host'] = dir
|
||||
done = true
|
||||
break
|
||||
end
|
||||
end
|
||||
break if done
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
# Default to loopback
|
||||
if(! opts['host'])
|
||||
opts['host'] = '127.0.0.1'
|
||||
end
|
||||
|
||||
if framework.db.connect(opts) && framework.db.connection_established?
|
||||
print_line "Connected to Postgres data service: #{info[:host]}/#{info[:name]}"
|
||||
else
|
||||
raise RuntimeError.new("Failed to connect to the Postgres data service: #{framework.db.error}")
|
||||
end
|
||||
end
|
||||
|
||||
def db_connect_http(opts)
|
||||
# local database is required to use Mdm objects
|
||||
unless framework.db.active
|
||||
err_msg = 'No local database connected, meaning some Metasploit features will not be available. A full list of '\
|
||||
'the affected features & database setup instructions can be found here: '\
|
||||
'https://github.com/rapid7/metasploit-framework/wiki/msfdb:-Database-Features-&-How-to-Set-up-a-Database-for-Metasploit'
|
||||
|
||||
print_error(err_msg)
|
||||
return
|
||||
end
|
||||
|
||||
uri = db_parse_db_uri_http(opts[:url])
|
||||
|
||||
remote_data_service = Metasploit::Framework::DataService::RemoteHTTPDataService.new(uri.to_s, opts)
|
||||
begin
|
||||
framework.db.register_data_service(remote_data_service)
|
||||
print_line "Connected to HTTP data service: #{remote_data_service.name}"
|
||||
framework.db.workspace = framework.db.default_workspace
|
||||
rescue => e
|
||||
raise RuntimeError.new("Failed to connect to the HTTP data service: #{e.message}")
|
||||
end
|
||||
end
|
||||
|
||||
def db_parse_db_uri_postgresql(path)
|
||||
res = {}
|
||||
if (path)
|
||||
auth, dest = path.split('@')
|
||||
(dest = auth and auth = nil) if not dest
|
||||
# remove optional scheme in database url
|
||||
auth = auth.sub(/^\w+:\/\//, "") if auth
|
||||
res[:user],res[:pass] = auth.split(':') if auth
|
||||
targ,name = dest.split('/')
|
||||
(name = targ and targ = nil) if not name
|
||||
res[:host],res[:port] = targ.split(':') if targ
|
||||
end
|
||||
res[:name] = name || 'metasploit3'
|
||||
res
|
||||
end
|
||||
|
||||
def db_parse_db_uri_http(path)
|
||||
URI.parse(path)
|
||||
end
|
||||
|
||||
#
|
||||
# Miscellaneous option helpers
|
||||
#
|
||||
|
||||
|
||||
#
|
||||
# Takes +host_ranges+, an Array of RangeWalkers, and chunks it up into
|
||||
# blocks of 1024.
|
||||
@ -2160,36 +2064,6 @@ class Db
|
||||
print_status(output)
|
||||
end
|
||||
|
||||
def data_service_search(search_criteria)
|
||||
conf = Msf::Config.load
|
||||
rv = nil
|
||||
|
||||
conf.each_pair do |k,v|
|
||||
name = k.split('/').last
|
||||
rv = name if name == search_criteria
|
||||
end
|
||||
rv
|
||||
end
|
||||
|
||||
def load_db_config(db_name)
|
||||
conf = Msf::Config.load
|
||||
conf_options = conf["#{DB_CONFIG_PATH}/#{db_name}"]
|
||||
opts = {}
|
||||
https_opts = {}
|
||||
if conf_options
|
||||
opts[:url] = conf_options['url'] if conf_options['url']
|
||||
opts[:api_token] = conf_options['api_token'] if conf_options['api_token']
|
||||
https_opts[:cert] = conf_options['cert'] if conf_options['cert']
|
||||
https_opts[:skip_verify] = conf_options['skip_verify'] if conf_options['skip_verify']
|
||||
else
|
||||
print_error "Unable to locate saved data service with name '#{db_name}'"
|
||||
return
|
||||
end
|
||||
|
||||
opts[:https_opts] = https_opts unless https_opts.empty?
|
||||
opts
|
||||
end
|
||||
|
||||
def list_saved_data_services
|
||||
conf = Msf::Config.load
|
||||
default = nil
|
||||
@ -2217,17 +2091,6 @@ class Db
|
||||
print_line tbl.to_s
|
||||
end
|
||||
|
||||
def build_postgres_url
|
||||
conn_params = ApplicationRecord.connection_config
|
||||
url = ""
|
||||
url += "#{conn_params[:username]}" if conn_params[:username]
|
||||
url += ":#{conn_params[:password]}" if conn_params[:password]
|
||||
url += "@#{conn_params[:host]}" if conn_params[:host]
|
||||
url += ":#{conn_params[:port]}" if conn_params[:port]
|
||||
url += "/#{conn_params[:database]}" if conn_params[:database]
|
||||
url
|
||||
end
|
||||
|
||||
def print_msgs(status_msg, error_msg)
|
||||
status_msg.each do |s|
|
||||
print_status(s)
|
||||
|
@ -16,7 +16,6 @@ class Driver < Msf::Ui::Driver
|
||||
|
||||
ConfigCore = "framework/core"
|
||||
ConfigGroup = "framework/ui/console"
|
||||
DbConfigGroup = "framework/database"
|
||||
|
||||
DefaultPrompt = "%undmsf#{Metasploit::Framework::Version::MAJOR}%clr"
|
||||
DefaultPromptChar = "%clr>"
|
||||
@ -118,12 +117,11 @@ class Driver < Msf::Ui::Driver
|
||||
end
|
||||
|
||||
# Load the other "core" command dispatchers
|
||||
CommandDispatchers.each do |dispatcher|
|
||||
enstack_dispatcher(dispatcher)
|
||||
CommandDispatchers.each do |dispatcher_class|
|
||||
dispatcher = enstack_dispatcher(dispatcher_class)
|
||||
dispatcher.load_config(opts['Config'])
|
||||
end
|
||||
|
||||
load_db_config(opts['Config'])
|
||||
|
||||
begin
|
||||
FeatureManager.instance.load_config
|
||||
rescue StandardException => e
|
||||
@ -223,38 +221,6 @@ class Driver < Msf::Ui::Driver
|
||||
end
|
||||
end
|
||||
|
||||
def load_db_config(path=nil)
|
||||
begin
|
||||
conf = Msf::Config.load(path)
|
||||
rescue
|
||||
wlog("Failed to load configuration: #{$!}")
|
||||
return
|
||||
end
|
||||
|
||||
if conf.group?(DbConfigGroup)
|
||||
conf[DbConfigGroup].each_pair do |k, v|
|
||||
if k.downcase == 'default_db'
|
||||
ilog "Default data service found. Attempting to connect..."
|
||||
default_db_config_path = "#{DbConfigGroup}/#{v}"
|
||||
default_db = conf[default_db_config_path]
|
||||
if default_db
|
||||
connect_string = "db_connect #{v}"
|
||||
|
||||
if framework.db.active && default_db['url'] !~ /http/
|
||||
ilog "Existing local data connection found. Disconnecting first."
|
||||
run_single("db_disconnect")
|
||||
end
|
||||
|
||||
run_single(connect_string)
|
||||
else
|
||||
elog "Config entry for '#{default_db_config_path}' could not be found. Config file might be corrupt."
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Loads configuration for the console.
|
||||
#
|
||||
|
122
msfdb
122
msfdb
@ -58,6 +58,7 @@ require 'msfenv'
|
||||
db_pool: 200,
|
||||
address: 'localhost',
|
||||
port: 5443,
|
||||
daemon: true,
|
||||
ssl: true,
|
||||
ssl_cert: @ws_ssl_cert_default,
|
||||
ssl_key: @ws_ssl_key_default,
|
||||
@ -121,12 +122,10 @@ def run_cmd(cmd, input: nil, env: {})
|
||||
exitstatus = wait_thr.value.exitstatus
|
||||
end
|
||||
|
||||
if exitstatus != 0
|
||||
if @options[:debug]
|
||||
puts "'#{cmd}' returned #{exitstatus}"
|
||||
puts out
|
||||
puts err
|
||||
end
|
||||
if @options[:debug]
|
||||
puts "'#{cmd}' returned #{exitstatus}"
|
||||
puts out
|
||||
puts err
|
||||
end
|
||||
|
||||
exitstatus
|
||||
@ -497,6 +496,44 @@ def init_web_service
|
||||
end
|
||||
end
|
||||
|
||||
def start_web_service_daemon(expect_auth:)
|
||||
if run_cmd("#{thin_cmd} start") == 0
|
||||
# wait until web service is online
|
||||
retry_count = 0
|
||||
response_data = web_service_online_check(expect_auth: expect_auth)
|
||||
is_online = response_data[:state] != :offline
|
||||
while !is_online && retry_count < @options[:retry_max]
|
||||
retry_count += 1
|
||||
if @options[:debug]
|
||||
puts "MSF web service doesn't appear to be online. Sleeping #{@options[:retry_delay]}s until check #{retry_count}/#{@options[:retry_max]}"
|
||||
end
|
||||
sleep(@options[:retry_delay])
|
||||
response_data = web_service_online_check(expect_auth: expect_auth)
|
||||
is_online = response_data[:state] != :offline
|
||||
end
|
||||
|
||||
if response_data[:state] == :online
|
||||
puts "#{'success'.green.bold}"
|
||||
puts 'MSF web service started and online'
|
||||
return true
|
||||
elsif response_data[:state] == :error
|
||||
puts "#{'failed'.red.bold}"
|
||||
print_error 'MSF web service failed and returned the following message:'
|
||||
puts "#{response_data[:message].nil? || response_data[:message].empty? ? "No message returned." : response_data[:message]}"
|
||||
elsif response_data[:state] == :offline
|
||||
puts "#{'failed'.red.bold}"
|
||||
print_error 'A connection with the web service was refused.'
|
||||
end
|
||||
|
||||
puts "Please see #{@ws_log} for additional webservice details."
|
||||
return false
|
||||
else
|
||||
puts "#{'failed'.red.bold}"
|
||||
puts 'Failed to start MSF web service'
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
def start_web_service(expect_auth: true)
|
||||
unless File.file?(@ws_conf)
|
||||
puts "No MSF web service configuration found at #{@ws_conf}, not starting"
|
||||
@ -515,7 +552,6 @@ def start_web_service(expect_auth: true)
|
||||
File.delete(@ws_pid)
|
||||
end
|
||||
|
||||
# daemonize MSF web service
|
||||
print 'Attempting to start MSF web service...'
|
||||
|
||||
unless File.file?(@ws_ssl_key_default)
|
||||
@ -525,40 +561,11 @@ def start_web_service(expect_auth: true)
|
||||
return false
|
||||
end
|
||||
|
||||
if run_cmd("#{thin_cmd} start") == 0
|
||||
# wait until web service is online
|
||||
retry_count = 0
|
||||
response_data = web_service_online_check(expect_auth: expect_auth)
|
||||
is_online = response_data[:state] != :offline
|
||||
while !is_online && retry_count < @options[:retry_max]
|
||||
retry_count += 1
|
||||
if @options[:debug]
|
||||
puts "MSF web service doesn't appear to be online. Sleeping #{@options[:retry_delay]}s until check #{retry_count}/#{@options[:retry_max]}"
|
||||
end
|
||||
sleep(@options[:retry_delay])
|
||||
response_data = web_service_online_check(expect_auth: expect_auth)
|
||||
is_online = response_data[:state] != :offline
|
||||
end
|
||||
|
||||
if response_data[:state] == :online
|
||||
puts "#{'success'.green.bold}"
|
||||
puts 'MSF web service started and online'
|
||||
return true
|
||||
elsif response_data[:state] == :error
|
||||
puts "#{'failed'.red.bold}"
|
||||
print_error 'MSF web service failed and returned the following message:'
|
||||
puts "#{response_data[:message].nil? || response_data[:message].empty? ? "No message returned." : response_data[:message]}"
|
||||
elsif response_data[:state] == :offline
|
||||
puts "#{'failed'.red.bold}"
|
||||
print_error 'A connection with the web service was refused.'
|
||||
end
|
||||
|
||||
puts "Please see #{@ws_log} for additional webservice details."
|
||||
return false
|
||||
if @options[:daemon]
|
||||
start_web_service_daemon(expect_auth: expect_auth)
|
||||
else
|
||||
puts "#{'failed'.red.bold}"
|
||||
puts 'Failed to start MSF web service'
|
||||
return false
|
||||
puts thin_cmd
|
||||
system "#{thin_cmd} start"
|
||||
end
|
||||
end
|
||||
|
||||
@ -719,33 +726,31 @@ def output_web_service_information
|
||||
puts "#{get_web_service_uri(path: '/api/v1/auth/account')}"
|
||||
puts ''
|
||||
|
||||
persist_data_service
|
||||
if @options[:add_data_service]
|
||||
persist_data_service
|
||||
end
|
||||
end
|
||||
|
||||
def persist_data_service
|
||||
data_service_name = "local-#{@options[:ssl] ? 'https' : 'http'}-data-service"
|
||||
if !@options[:add_data_service]
|
||||
return
|
||||
elsif !@options[:data_service_name].nil?
|
||||
data_service_name = @options[:data_service_name]
|
||||
end
|
||||
|
||||
# execute msfconsole commands to add and persist the data service connection
|
||||
connect_cmd = get_db_connect_command(name: data_service_name)
|
||||
cmd = "msfconsole -qx \"#{connect_cmd}; db_save; exit\""
|
||||
cmd = "./msfconsole -qx \"#{get_db_connect_command}; db_save; exit\""
|
||||
if run_cmd(cmd) != 0
|
||||
# attempt to execute msfconsole in the current working directory
|
||||
if run_cmd(cmd, env: {'PATH' => ".:#{ENV["PATH"]}"}) != 0
|
||||
puts 'Failed to run msfconsole and persist the data service connection'
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def get_db_connect_command(name: nil)
|
||||
# build db_connect command based on install options
|
||||
def get_db_connect_command
|
||||
data_service_name = "local-#{@options[:ssl] ? 'https' : 'http'}-data-service"
|
||||
if !@options[:data_service_name].nil?
|
||||
data_service_name = @options[:data_service_name]
|
||||
end
|
||||
|
||||
# build db_remove and db_connect command based on install options
|
||||
connect_cmd = "db_connect"
|
||||
connect_cmd << " --name #{name}" unless name.nil?
|
||||
connect_cmd << " --name #{data_service_name}"
|
||||
connect_cmd << " --token #{@ws_api_token}"
|
||||
connect_cmd << " --cert #{@options[:ssl_cert]}" if @options[:ssl]
|
||||
connect_cmd << " --skip-verify" if skip_ssl_verify?
|
||||
@ -771,13 +776,14 @@ def get_ssl_cert
|
||||
@options[:ssl] ? @options[:ssl_cert] : nil
|
||||
end
|
||||
|
||||
# TODO: In the future this can be replaced by Msf::WebServices::HttpDBManagerService
|
||||
def thin_cmd
|
||||
server_opts = "--rackup #{@ws_conf} --address #{@options[:address]} --port #{@options[:port]}"
|
||||
ssl_opts = @options[:ssl] ? "--ssl --ssl-key-file #{@options[:ssl_key]} --ssl-cert-file #{@options[:ssl_cert]}" : ''
|
||||
ssl_opts << ' --ssl-disable-verify' if skip_ssl_verify?
|
||||
adapter_opts = "--environment #{@options[:ws_env]}"
|
||||
daemon_opts = "--daemonize --log #{@ws_log} --pid #{@ws_pid} --tag #{@ws_tag}"
|
||||
all_opts = [server_opts, ssl_opts, adapter_opts, daemon_opts].reject(&:empty?).join(' ')
|
||||
daemon_opts = "--daemonize --log #{@ws_log} --pid #{@ws_pid} --tag #{@ws_tag}" if @options[:daemon]
|
||||
all_opts = [server_opts, ssl_opts, adapter_opts, daemon_opts].reject(&:blank?).join(' ')
|
||||
|
||||
"thin #{all_opts}"
|
||||
end
|
||||
@ -926,6 +932,10 @@ def parse_args(args)
|
||||
@options[:port] = p
|
||||
}
|
||||
|
||||
opts.on('--[no-]daemon', 'Enable daemon') { |d|
|
||||
@options[:daemon] = d
|
||||
}
|
||||
|
||||
opts.on('--[no-]ssl', "Enable SSL (default: #{@options[:ssl]})") { |s| @options[:ssl] = s }
|
||||
|
||||
opts.on('--ssl-key-file PATH', "Path to private key (default: #{@options[:ssl_key]})") { |p|
|
||||
|
8
spec/file_fixtures/config_files/default_remote_db.ini
Normal file
8
spec/file_fixtures/config_files/default_remote_db.ini
Normal file
@ -0,0 +1,8 @@
|
||||
[framework/database]
|
||||
default_db=local-https-data-service
|
||||
|
||||
[framework/database/local-https-data-service]
|
||||
url=https://localhost:5443
|
||||
cert=/Users/user/.msf4/msf-ws-cert.pem
|
||||
skip_verify=true
|
||||
api_token=b1cd123e2f160a8a1fbf79baed180b8dc480de5b994f53eee42e57771e3f65e13bec737e4a4acbb2
|
130
spec/lib/msf/db_connector_spec.rb
Normal file
130
spec/lib/msf/db_connector_spec.rb
Normal file
@ -0,0 +1,130 @@
|
||||
RSpec.describe Msf::DbConnector do
|
||||
let(:file_fixtures_path) { File.join(Msf::Config.install_root, 'spec', 'file_fixtures') }
|
||||
let(:empty_config_file) { File.join(file_fixtures_path, 'config_files', 'empty.ini') }
|
||||
let(:default_remote_db_config_file) { File.join(file_fixtures_path, 'config_files', 'default_remote_db.ini') }
|
||||
let(:config_file) { empty_config_file }
|
||||
let(:db) do
|
||||
instance_double(
|
||||
Msf::DBManager,
|
||||
connection_established?: false,
|
||||
driver: 'driver',
|
||||
active: true
|
||||
)
|
||||
end
|
||||
let(:framework) do
|
||||
instance_double(
|
||||
::Msf::Framework,
|
||||
version: 'VERSION',
|
||||
db: db
|
||||
)
|
||||
end
|
||||
before :each do
|
||||
allow_any_instance_of(::Msf::Config).to receive(:config_file).and_return(config_file)
|
||||
end
|
||||
|
||||
it { is_expected.to respond_to :db_connect_postgresql }
|
||||
|
||||
describe '#load_db_config' do
|
||||
context 'when the config file does not exist' do
|
||||
let(:config_file) { File.join(file_fixtures_path, 'config_files', 'non_existent_file.ini') }
|
||||
|
||||
it 'returns nil' do
|
||||
expect(subject.load_db_config('local-https-data-service')).to eql(nil)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is no db config present' do
|
||||
let(:config_file) { empty_config_file }
|
||||
|
||||
it 'returns nil' do
|
||||
expect(subject.load_db_config('local-https-data-service')).to eql(nil)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is a default database registered' do
|
||||
let(:config_file) { default_remote_db_config_file }
|
||||
|
||||
it 'returns the cb config' do
|
||||
expected_config = {
|
||||
url: 'https://localhost:5443',
|
||||
cert: '/Users/user/.msf4/msf-ws-cert.pem',
|
||||
skip_verify: 'true',
|
||||
api_token: 'b1cd123e2f160a8a1fbf79baed180b8dc480de5b994f53eee42e57771e3f65e13bec737e4a4acbb2'
|
||||
}
|
||||
expect(subject.load_db_config('local-https-data-service')).to eql(expected_config)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#db_connect_from_config' do
|
||||
let(:db_connect_response) { { result: 'mock result message', data_service_name: 'local-https-data-service' } }
|
||||
before :each do
|
||||
allow(subject).to receive(:db_connect).and_return(db_connect_response)
|
||||
end
|
||||
|
||||
context 'when the config file does not exist' do
|
||||
let(:config_file) { File.join(file_fixtures_path, 'config_files', 'non_existent_file.ini') }
|
||||
|
||||
it 'returns an empty object' do
|
||||
expect(subject.db_connect_from_config(framework)).to eql({})
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is no db config present' do
|
||||
let(:config_file) { empty_config_file }
|
||||
|
||||
it 'returns an empty object' do
|
||||
expect(subject.db_connect_from_config(framework)).to eql({})
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is a default database registered' do
|
||||
let(:config_file) { default_remote_db_config_file }
|
||||
|
||||
it 'returns the db_connect_response' do
|
||||
expected_config = {
|
||||
url: 'https://localhost:5443',
|
||||
cert: '/Users/user/.msf4/msf-ws-cert.pem',
|
||||
skip_verify: 'true',
|
||||
api_token: 'b1cd123e2f160a8a1fbf79baed180b8dc480de5b994f53eee42e57771e3f65e13bec737e4a4acbb2'
|
||||
}
|
||||
expect(subject.db_connect_from_config(framework)).to eql(db_connect_response)
|
||||
expect(subject).to have_received(:db_connect).with(framework, expected_config)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#data_service_search' do
|
||||
context 'when the name is not present' do
|
||||
let(:config_file) { empty_config_file }
|
||||
|
||||
it 'returns nil' do
|
||||
expect(subject.data_service_search(name: 'local-https-data-service')).to eql nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the name is present' do
|
||||
let(:config_file) { default_remote_db_config_file }
|
||||
|
||||
it 'returns the name' do
|
||||
expect(subject.data_service_search(name: 'local-https-data-service')).to eql 'local-https-data-service'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the url is not present' do
|
||||
let(:config_file) { empty_config_file }
|
||||
|
||||
it 'returns nil' do
|
||||
expect(subject.data_service_search(url: 'https://localhost:5443')).to eql nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the url is present' do
|
||||
let(:config_file) { default_remote_db_config_file }
|
||||
|
||||
it 'returns the name' do
|
||||
expect(subject.data_service_search(url: 'https://localhost:5443')).to eql 'local-https-data-service'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -337,7 +337,7 @@ RSpec.describe Msf::Ui::Debug do
|
||||
end
|
||||
|
||||
it 'correctly retrieves and parses an empty config file and datastore' do
|
||||
allow(::Msf::Config).to receive(:config_file).and_return(File.join(file_fixtures_path, 'debug', 'config_files', 'empty.ini'))
|
||||
allow(::Msf::Config).to receive(:config_file).and_return(File.join(file_fixtures_path, 'config_files', 'empty.ini'))
|
||||
|
||||
framework = instance_double(
|
||||
::Msf::Framework,
|
||||
@ -372,7 +372,7 @@ RSpec.describe Msf::Ui::Debug do
|
||||
end
|
||||
|
||||
it 'correctly retrieves and parses a populated global datastore' do
|
||||
allow(::Msf::Config).to receive(:config_file).and_return(File.join(file_fixtures_path, 'debug', 'config_files', 'empty.ini'))
|
||||
allow(::Msf::Config).to receive(:config_file).and_return(File.join(file_fixtures_path, 'config_files', 'empty.ini'))
|
||||
|
||||
framework = instance_double(
|
||||
::Msf::Framework,
|
||||
@ -414,7 +414,7 @@ RSpec.describe Msf::Ui::Debug do
|
||||
end
|
||||
|
||||
it 'correctly retrieves and parses a populated global datastore and current module' do
|
||||
allow(::Msf::Config).to receive(:config_file).and_return(File.join(file_fixtures_path, 'debug', 'config_files', 'empty.ini'))
|
||||
allow(::Msf::Config).to receive(:config_file).and_return(File.join(file_fixtures_path, 'config_files', 'empty.ini'))
|
||||
|
||||
framework = instance_double(
|
||||
::Msf::Framework,
|
||||
@ -465,7 +465,7 @@ RSpec.describe Msf::Ui::Debug do
|
||||
end
|
||||
|
||||
it 'correctly retrieves and parses active module variables' do
|
||||
allow(::Msf::Config).to receive(:config_file).and_return(File.join(file_fixtures_path, 'debug', 'config_files', 'empty.ini'))
|
||||
allow(::Msf::Config).to receive(:config_file).and_return(File.join(file_fixtures_path, 'config_files', 'empty.ini'))
|
||||
|
||||
framework = instance_double(
|
||||
::Msf::Framework,
|
||||
@ -513,7 +513,7 @@ RSpec.describe Msf::Ui::Debug do
|
||||
end
|
||||
|
||||
it 'preferences the framework datastore values over config stored values' do
|
||||
allow(::Msf::Config).to receive(:config_file).and_return(File.join(file_fixtures_path, 'debug', 'config_files', 'module.ini'))
|
||||
allow(::Msf::Config).to receive(:config_file).and_return(File.join(file_fixtures_path, 'config_files', 'module.ini'))
|
||||
|
||||
framework = instance_double(
|
||||
::Msf::Framework,
|
||||
@ -564,7 +564,7 @@ RSpec.describe Msf::Ui::Debug do
|
||||
end
|
||||
|
||||
it 'correctly retrieves and parses Database information' do
|
||||
allow(::Msf::Config).to receive(:config_file).and_return(File.join(file_fixtures_path, 'debug', 'config_files', 'db.ini'))
|
||||
allow(::Msf::Config).to receive(:config_file).and_return(File.join(file_fixtures_path, 'config_files', 'db.ini'))
|
||||
|
||||
framework = instance_double(
|
||||
::Msf::Framework,
|
||||
|
@ -42,9 +42,7 @@ RSpec.describe Msf::Ui::Console::CommandDispatcher::Db do
|
||||
it { is_expected.to respond_to :cmd_workspace_tabs }
|
||||
it { is_expected.to respond_to :commands }
|
||||
it { is_expected.to respond_to :db_check_driver }
|
||||
it { is_expected.to respond_to :db_connect_postgresql }
|
||||
it { is_expected.to respond_to :db_find_tools }
|
||||
it { is_expected.to respond_to :db_parse_db_uri_postgresql }
|
||||
it { is_expected.to respond_to :deprecated_commands }
|
||||
it { is_expected.to respond_to :each_host_range_chunk }
|
||||
it { is_expected.to respond_to :name }
|
||||
|
@ -2,6 +2,7 @@
|
||||
# -*- coding: binary -*-
|
||||
#
|
||||
# Starts the HTTP DB Service interface
|
||||
# TODO: This functionality exists within the top level msfdb.rb, and should be merged
|
||||
|
||||
require 'optparse'
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user