Class: Msf::DataStoreWithFallbacks
- Inherits:
-
Object
- Object
- Msf::DataStoreWithFallbacks
- Defined in:
- lib/msf/core/data_store_with_fallbacks.rb
Overview
The data store is just a bitbucket that holds keyed values. It is used by various classes to hold option values and other state information.
Direct Known Subclasses
Defined Under Namespace
Classes: DataStoreSearchResult
Constant Summary collapse
- GLOBAL_KEYS =
The global framework datastore doesn’t currently import options For now, store an ad-hoc list of keys that the shell handles
This list could be removed if framework’s bootup sequence registers these as datastore options
%w[ ConsoleLogging LogLevel MinimumRank SessionLogging TimestampOutput Prompt PromptChar PromptTimeFormat MeterpreterPrompt SessionTlvLogging ]
Instance Attribute Summary collapse
-
#aliases ⇒ Hash{String => String}
protected
The key is the old option name, the value is the new option name.
-
#defaults ⇒ Hash{String => Msf::OptBase}
protected
These defaults will be used if the user has not explicitly defined a specific datastore value.
-
#options ⇒ Hash{String => Msf::OptBase}
The options associated with this datastore.
-
#user_defined ⇒ Hash<String, Object>
Returns a hash of user-defined datastore values.
Instance Method Summary collapse
-
#[](k) ⇒ Object
Case-insensitive wrapper around hash lookup.
-
#[]=(k, v) ⇒ Object
Clears the imported flag for the supplied key since it’s being set directly.
-
#clear ⇒ Object
Completely clear all values in the data store.
-
#copy ⇒ Msf::DataStore
Return a copy of this datastore.
-
#copy_state(other) ⇒ Msf::DataStore
protected
Copy the state from the other Msf::DataStore.
-
#default?(key) ⇒ TrueClass, FalseClass
Was this entry actually set or just using its default.
-
#delete(key) ⇒ Object
deprecated
Deprecated.
use ##unset instead, or set the value explicitly to nil
-
#each(&block) ⇒ Object
(also: #each_pair)
Overrides the builtin ‘each’ operator to avoid the following exception on Ruby 1.9.2+ “can’t add a new key into hash during iteration”.
- #each_key(&block) ⇒ Object
-
#find_key_case(k) ⇒ String
Case-insensitive key lookup.
-
#from_file(path, name = 'global') ⇒ Object
Imports datastore values from the specified file path using the supplied name.
-
#import_defaults_from_hash(hash, imported_by:) ⇒ nil
Update defaults from a hash.
-
#import_option(key, val, imported = true, imported_by = nil, option = nil) ⇒ Object
deprecated
Deprecated.
Use #import_options
-
#import_options(options, imported_by = nil, overwrite = true) ⇒ Object
This method is a helper method that imports the default value for all of the supplied options.
-
#import_options_from_hash(option_hash, imported = true, imported_by = nil) ⇒ nil
deprecated
Deprecated.
use #merge! instead
-
#import_options_from_s(option_str, delim = nil) ⇒ Object
Imports option values from a whitespace separated string in VAR=VAL format.
-
#initialize ⇒ DataStoreWithFallbacks
constructor
Initializes the data store’s internal state.
-
#key?(key) ⇒ TrueClass, FalseClass
(also: #has_key?, #include?, #member?)
True if the key is present in the user defined values, or within registered options.
-
#key_error_for(key) ⇒ Object
protected
Raised when the specified key is not found.
-
#keys ⇒ Array<String>
The array of user defined datastore values, and registered option names.
-
#length ⇒ Integer
(also: #count, #size)
The length of the registered keys.
-
#merge(other) ⇒ Object
Override merge to ensure we merge the aliases and imported hashes.
-
#merge!(other) ⇒ Object
(also: #update)
Merge the other object into the current datastore’s aliases and imported hashes.
-
#remove_option(name) ⇒ nil
Removes an option and any associated value.
-
#reverse_merge!(other) ⇒ Object
Reverse Merge the other object into the current datastore’s aliases and imported hashes Equivalent to ActiveSupport’s reverse_merge! functionality.
-
#search_for(key) ⇒ DataStoreSearchResult
Search for a value within the current datastore, taking into consideration any registered aliases, fallbacks, etc.
- #search_result(result, value, fallback_key: nil) ⇒ Object protected
-
#store(k, v) ⇒ Object
Case-insensitive wrapper around store; Skips option validation entirely.
-
#to_external_message_h ⇒ Object
Hack on a hack for the external modules.
-
#to_file(path, name = 'global') ⇒ Object
Persists the contents of the data store to a file.
-
#to_h ⇒ Object
Override Hash’s to_h method so we can include the original case of each key (failing to do this breaks a number of places in framework and pro that use serialized datastores).
-
#to_s(delim = ' ') ⇒ Object
Serializes the options in the datastore to a string.
-
#unset(key) ⇒ Object
unset the current key from the datastore.
-
#update_value(k, v) ⇒ Object
Updates a value in the datastore with the specified name, k, to the specified value, v.
Constructor Details
#initialize ⇒ DataStoreWithFallbacks
Initializes the data store’s internal state.
33 34 35 36 37 38 39 40 41 42 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 33 def initialize @options = Hash.new @aliases = Hash.new # default values which will be referenced when not defined by the user @defaults = Hash.new # values explicitly defined, which take precedence over default values @user_defined = Hash.new end |
Instance Attribute Details
#aliases ⇒ Hash{String => String} (protected)
Returns The key is the old option name, the value is the new option name.
477 478 479 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 477 def aliases @aliases end |
#defaults ⇒ Hash{String => Msf::OptBase} (protected)
These defaults will be used if the user has not explicitly defined a specific datastore value. These will be checked as a priority to any options that also provide defaults.
474 475 476 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 474 def defaults @defaults end |
#options ⇒ Hash{String => Msf::OptBase}
Returns The options associated with this datastore. Used for validating values/defaults/etc.
45 46 47 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 45 def @options end |
#user_defined ⇒ Hash<String, Object>
Returns a hash of user-defined datastore values. The returned hash does not include default option values.
52 53 54 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 52 def user_defined @user_defined end |
Instance Method Details
#[](k) ⇒ Object
Case-insensitive wrapper around hash lookup
85 86 87 88 89 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 85 def [](k) search_result = search_for(k) search_result.value end |
#[]=(k, v) ⇒ Object
Clears the imported flag for the supplied key since it’s being set directly.
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 66 def []=(k, v) k = find_key_case(k) opt = @options[k] unless opt.nil? if opt.validate_on_assignment? unless opt.valid?(v, check_empty: false) raise Msf::OptionValidateError.new(["Value '#{v}' is not valid for option '#{k}'"]) end v = opt.normalize(v) end end @user_defined[k] = v end |
#clear ⇒ Object
Completely clear all values in the data store
385 386 387 388 389 390 391 392 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 385 def clear self..clear self.aliases.clear self.defaults.clear self.user_defined.clear self end |
#copy ⇒ Msf::DataStore
Return a copy of this datastore. Only string values will be duplicated, other values will share the same reference
333 334 335 336 337 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 333 def copy new_instance = self.class.new new_instance.copy_state(self) new_instance end |
#copy_state(other) ⇒ Msf::DataStore (protected)
Copy the state from the other Msf::DataStore. The state will be coped in a shallow fashion, other than imported and user_defined strings.
485 486 487 488 489 490 491 492 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 485 def copy_state(other) self. = other..dup self.aliases = other.aliases.dup self.defaults = other.defaults.transform_values { |value| value.kind_of?(String) ? value.dup : value } self.user_defined = other.user_defined.transform_values { |value| value.kind_of?(String) ? value.dup : value } self end |
#default?(key) ⇒ TrueClass, FalseClass
Was this entry actually set or just using its default
58 59 60 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 58 def default?(key) search_for(key).default? end |
#delete(key) ⇒ Object
use ##unset instead, or set the value explicitly to nil
119 120 121 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 119 def delete(key) unset(key) end |
#each(&block) ⇒ Object Also known as: each_pair
Overrides the builtin ‘each’ operator to avoid the following exception on Ruby 1.9.2+
"can't add a new key into hash during iteration"
398 399 400 401 402 403 404 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 398 def each(&block) list = [] self.keys.sort.each do |sidx| list << [sidx, self[sidx]] end list.each(&block) end |
#each_key(&block) ⇒ Object
408 409 410 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 408 def each_key(&block) self.keys.each(&block) end |
#find_key_case(k) ⇒ String
Case-insensitive key lookup
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 416 def find_key_case(k) # Scan each alias looking for a key search_k = k.downcase if self.aliases.has_key?(search_k) search_k = self.aliases[search_k] end # Check to see if we have an exact key match - otherwise we'll have to search manually to check case sensitivity if @user_defined.key?(search_k) || .key?(search_k) return search_k end # Scan each key looking for a match each_key do |rk| if rk.casecmp(search_k) == 0 return rk end end # Fall through to the non-existent value k end |
#from_file(path, name = 'global') ⇒ Object
Imports datastore values from the specified file path using the supplied name
317 318 319 320 321 322 323 324 325 326 327 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 317 def from_file(path, name = 'global') begin ini = Rex::Parser::Ini.from_file(path) rescue return end if ini.group?(name) merge!(ini[name]) end end |
#import_defaults_from_hash(hash, imported_by:) ⇒ nil
Update defaults from a hash. These merged values are not validated by default.
208 209 210 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 208 def import_defaults_from_hash(hash, imported_by:) @defaults.merge!(hash) end |
#import_option(key, val, imported = true, imported_by = nil, option = nil) ⇒ Object
Use #import_options
TODO: Doesn’t normalize data in the same vein as: github.com/rapid7/metasploit-framework/pull/6644
215 216 217 218 219 220 221 222 223 224 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 215 def import_option(key, val, imported = true, imported_by = nil, option = nil) store(key, val) if option option.aliases.each do |a| @aliases[a.downcase] = key.downcase end end @options[key] = option end |
#import_options(options, imported_by = nil, overwrite = true) ⇒ Object
This method is a helper method that imports the default value for all of the supplied options
141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 141 def (, imported_by = nil, overwrite = true) .each_option do |name, option| if self.[name].nil? || overwrite key = name option.aliases.each do |a| @aliases[a.downcase] = key.downcase end @options[key] = option end end end |
#import_options_from_hash(option_hash, imported = true, imported_by = nil) ⇒ nil
use #merge! instead
Imports values from a hash and stores them in the datastore.
199 200 201 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 199 def (option_hash, imported = true, imported_by = nil) merge!(option_hash) end |
#import_options_from_s(option_str, delim = nil) ⇒ Object
Imports option values from a whitespace separated string in VAR=VAL format.
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 157 def (option_str, delim = nil) hash = {} # Figure out the delimiter, default to space. if (delim.nil?) delim = /\s/ if (option_str.split('=').length <= 2 or option_str.index(',') != nil) delim = ',' end end # Split on the delimiter option_str.split(delim).each { |opt| var, val = opt.split('=', 2) next if (var =~ /^\s+$/) # Invalid parse? Raise an exception and let those bastards know. if (var == nil or val == nil) var = "unknown" if (!var) raise Rex::ArgumentParseError, "Invalid option specified: #{var}", caller end # Remove trailing whitespaces from the value val.gsub!(/\s+$/, '') # Store the value hash[var] = val } merge!(hash) end |
#key?(key) ⇒ TrueClass, FalseClass Also known as: has_key?, include?, member?
Returns True if the key is present in the user defined values, or within registered options. False otherwise.
241 242 243 244 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 241 def key?(key) matching_key = find_key_case(key) keys.include?(matching_key) end |
#key_error_for(key) ⇒ Object (protected)
Raised when the specified key is not found
496 497 498 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 496 def key_error_for(key) ::KeyError.new "key not found: #{key.inspect}" end |
#keys ⇒ Array<String>
Returns The array of user defined datastore values, and registered option names.
227 228 229 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 227 def keys (@user_defined.keys + @options.keys).uniq(&:downcase) end |
#length ⇒ Integer Also known as: count, size
Returns The length of the registered keys.
232 233 234 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 232 def length keys.length end |
#merge(other) ⇒ Object
Override merge to ensure we merge the aliases and imported hashes
377 378 379 380 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 377 def merge(other) ds = self.copy ds.merge!(other) end |
#merge!(other) ⇒ Object Also known as: update
Merge the other object into the current datastore’s aliases and imported hashes
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 343 def merge!(other) if other.is_a?(DataStoreWithFallbacks) self.aliases.merge!(other.aliases) self..merge!(other.) self.defaults.merge!(other.defaults) other.user_defined.each do |k, v| @user_defined[find_key_case(k)] = v end else other.each do |k, v| self.store(k, v) end end self end |
#remove_option(name) ⇒ nil
Removes an option and any associated value
128 129 130 131 132 133 134 135 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 128 def remove_option(name) k = find_key_case(name) @user_defined.delete(k) @aliases.delete_if { |_, v| v.casecmp?(k) } @options.delete_if { |option_name, _v| option_name.casecmp?(k) || option_name.casecmp?(name) } nil end |
#reverse_merge!(other) ⇒ Object
Reverse Merge the other object into the current datastore’s aliases and imported hashes Equivalent to ActiveSupport’s reverse_merge! functionality.
367 368 369 370 371 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 367 def reverse_merge!(other) raise ArgumentError, "invalid error type #{other.class}, expected ::Msf::DataStore" unless other.is_a?(Msf::DataStoreWithFallbacks) copy_state(other.merge(self)) end |
#search_for(key) ⇒ DataStoreSearchResult
Search for a value within the current datastore, taking into consideration any registered aliases, fallbacks, etc.
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 443 def search_for(key) k = find_key_case(key) return search_result(:user_defined, @user_defined[k]) if @user_defined.key?(k) option = @options.fetch(k) { @options.find { |option_name, _option| option_name.casecmp?(k) }&.last } if option # If the key isn't present - check any additional fallbacks that have been registered with the option. # i.e. handling the scenario of SMBUser not being explicitly set, but the option has registered a more # generic 'Username' fallback option.fallbacks.each do |fallback| fallback_search = search_for(fallback) if fallback_search.found? return search_result(:option_fallback, fallback_search.value, fallback_key: fallback) end end end # Checking for imported default values, ignoring case again imported_default_match = @defaults.find { |default_key, _default_value| default_key.casecmp?(k) } return search_result(:imported_default, imported_default_match.last) if imported_default_match return search_result(:option_default, option.default) if option search_result(:not_found, nil) end |
#search_result(result, value, fallback_key: nil) ⇒ Object (protected)
542 543 544 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 542 def search_result(result, value, fallback_key: nil) DataStoreSearchResult.new(result, value, namespace: :global_data_store, fallback_key: fallback_key) end |
#store(k, v) ⇒ Object
Case-insensitive wrapper around store; Skips option validation entirely
94 95 96 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 94 def store(k,v) @user_defined[find_key_case(k)] = v end |
#to_external_message_h ⇒ Object
Hack on a hack for the external modules
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 275 def datastore_hash = {} array_nester = ->(arr) do if arr.first.is_a? Array arr.map &array_nester else arr.map { |item| item.to_s.dup.force_encoding('UTF-8') } end end self.keys.each do |k| # TODO arbitrary depth if self[k].is_a? Array datastore_hash[k.to_s.dup.force_encoding('UTF-8')] = array_nester.call(self[k]) else datastore_hash[k.to_s.dup.force_encoding('UTF-8')] = self[k].to_s.dup.force_encoding('UTF-8') end end datastore_hash end |
#to_file(path, name = 'global') ⇒ Object
Persists the contents of the data store to a file
300 301 302 303 304 305 306 307 308 309 310 311 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 300 def to_file(path, name = 'global') ini = Rex::Parser::Ini.new(path) ini.add_group(name) # Save all user-defined options to the file. @user_defined.each_pair { |k, v| ini[name][k] = v } ini.to_file(path) end |
#to_h ⇒ Object
Override Hash’s to_h method so we can include the original case of each key (failing to do this breaks a number of places in framework and pro that use serialized datastores)
266 267 268 269 270 271 272 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 266 def to_h datastore_hash = {} self.keys.each do |k| datastore_hash[k.to_s] = self[k].to_s end datastore_hash end |
#to_s(delim = ' ') ⇒ Object
Serializes the options in the datastore to a string.
253 254 255 256 257 258 259 260 261 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 253 def to_s(delim = ' ') str = '' keys.sort.each { |key| str << "#{key}=#{self[key]}" + ((str.length) ? delim : '') } str end |
#unset(key) ⇒ Object
unset the current key from the datastore
109 110 111 112 113 114 115 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 109 def unset(key) k = find_key_case(key) search_result = search_for(k) @user_defined.delete(k) search_result.value end |
#update_value(k, v) ⇒ Object
Updates a value in the datastore with the specified name, k, to the specified value, v. Skips option validation entirely.
102 103 104 |
# File 'lib/msf/core/data_store_with_fallbacks.rb', line 102 def update_value(k, v) store(k, v) end |