-
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added
Ronin::Recon::Config
(issue #124).
- Loading branch information
1 parent
8d20be7
commit 23e2bb5
Showing
18 changed files
with
1,120 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,371 @@ | ||
# frozen_string_literal: true | ||
# | ||
# ronin-recon - A micro-framework and tool for performing reconnaissance. | ||
# | ||
# Copyright (c) 2023-2024 Hal Brodigan (postmodern.mod3@gmail.com) | ||
# | ||
# ronin-recon is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU Lesser General Public License as published | ||
# by the Free Software Foundation, either version 3 of the License, or | ||
# (at your option) any later version. | ||
# | ||
# ronin-recon is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU Lesser General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU Lesser General Public License | ||
# along with ronin-recon. If not, see <https://www.gnu.org/licenses/>. | ||
# | ||
|
||
require 'ronin/recon/exceptions' | ||
|
||
require 'ronin/core/home' | ||
require 'set' | ||
|
||
module Ronin | ||
module Recon | ||
# | ||
# Represents configuration for the recon engine. | ||
# | ||
class Config | ||
|
||
# | ||
# Represents the set of workers to use. | ||
# | ||
# @api private | ||
# | ||
class Workers | ||
|
||
include Enumerable | ||
|
||
# Specifies which workers to use. | ||
# | ||
# @return [Set<String>] | ||
attr_reader :workers | ||
|
||
# | ||
# Initializes the workers. | ||
# | ||
# @param [Set<String>, Array<String>] workers | ||
# The set of worker IDs. | ||
# | ||
# @raise [ArgumentError] | ||
# The given workers argument was not a Set, Array, or Hash. | ||
# | ||
def initialize(workers) | ||
case workers | ||
when Set then @workers = workers.dup | ||
when Array then @workers = workers.to_set | ||
when Hash | ||
@workers = DEFAULT.dup | ||
|
||
workers.each do |worker_id,enabled| | ||
if enabled then add(worker_id) | ||
else delete(worker_id) | ||
end | ||
end | ||
else | ||
raise(ArgumentError,"workers value must be a Set, Array, or Hash: #{workers.inspect}") | ||
end | ||
end | ||
|
||
# The default workers configuration. | ||
DEFAULT = Set[ | ||
'dns/lookup', | ||
'dns/mailservers', | ||
'dns/nameservers', | ||
'dns/reverse_lookup', | ||
'dns/srv_enum', | ||
'dns/subdomain_enum', | ||
'dns/suffix_enum', | ||
'net/ip_range_enum', | ||
'net/port_scan', | ||
'net/service_id', | ||
'ssl/cert_grab', | ||
'ssl/cert_enum', | ||
# NOTE: disabled due to rate limiting issues | ||
# 'ssl/cert_sh', | ||
'web/dir_enum', | ||
'web/email_addresses', | ||
'web/spider' | ||
] | ||
|
||
# | ||
# Initializes the default workers. | ||
# | ||
# @return [Workers] | ||
# | ||
def self.default | ||
new(DEFAULT) | ||
end | ||
|
||
# | ||
# Adds a worker to the workers. | ||
# | ||
# @param [String] worker_id | ||
# The worker ID to add. | ||
# | ||
# @return [self] | ||
# | ||
# @api public | ||
# | ||
def add(worker_id) | ||
@workers.add(worker_id) | ||
return self | ||
end | ||
|
||
# | ||
# Deletes a worker from the workers. | ||
# | ||
# @param [String] worker_id | ||
# The worker ID to disable. | ||
# | ||
# @api public | ||
# | ||
def delete(worker_id) | ||
@workers.delete(worker_id) | ||
return self | ||
end | ||
|
||
# | ||
# Determines if the worker is enabled in the workers. | ||
# | ||
# @param [String] worker_id | ||
# The worker ID to search for. | ||
# | ||
# @return [Boolean] | ||
# | ||
# @api public | ||
# | ||
def include?(worker_id) | ||
@workers.include?(worker_id) | ||
end | ||
|
||
# | ||
# Enumerates over each worker in the set. | ||
# | ||
# @yield [worker_id] | ||
# The given block will be passed each worker ID in the set. | ||
# | ||
# @yieldparam [String] worker_id | ||
# A worker ID in the set. | ||
# | ||
# @return [Enumerator] | ||
# If no block is given, an Enumerator will be returned. | ||
# | ||
def each(&block) | ||
@workers.each(&block) | ||
end | ||
|
||
# | ||
# Compares the workers to another object. | ||
# | ||
# @param [Object] other | ||
# The other object. | ||
# | ||
# @return [Boolean] | ||
# | ||
def eql?(other) | ||
self.class == other.class && @workers == other.workers | ||
end | ||
|
||
alias == eql? | ||
|
||
end | ||
|
||
# The workers to use. | ||
# | ||
# @return [Workers] | ||
# | ||
# @api public | ||
attr_reader :workers | ||
|
||
# Params for individual workers. | ||
# | ||
# @return [Hash{String => Hash{Symbol => Object}}] | ||
# | ||
# @api public | ||
attr_reader :params | ||
|
||
# Concurrency values for individual workers. | ||
# | ||
# @return [Hash{String => Integer}] | ||
# | ||
# @api public | ||
attr_reader :concurrency | ||
|
||
# | ||
# Initializes the recon engine configuration. | ||
# | ||
# @param [Workers] workers | ||
# The workers to use. | ||
# | ||
# @param [Hash{String => Hash{Symbol => Object}}] params | ||
# The params for individual workers. | ||
# | ||
# @param [Hash{String => Hash{Symbol => Object}}] concurrency | ||
# The concurrency values for individual workers. | ||
# | ||
def initialize(workers: Workers.default, params: {}, concurrency: {}) | ||
@workers = workers | ||
@params = params | ||
@concurrency = concurrency | ||
end | ||
|
||
# | ||
# Validates the loaded configuration data. | ||
# | ||
# @param [Object] data | ||
# The loaded configuration data. | ||
# | ||
# @raise [InvalidConfig] | ||
# The configuration data is not a Hash, does not contain Symbol keys, | ||
# or does not contain Hashes. | ||
# | ||
# @return [true] | ||
# The configuration data is valid. | ||
# | ||
def self.validate(data) | ||
unless data.kind_of?(Hash) | ||
raise(InvalidConfig,"must contain a Hash: #{data.inspect}") | ||
end | ||
|
||
if (workers = data[:workers]) | ||
unless (workers.kind_of?(Hash) || workers.kind_of?(Array)) | ||
raise(InvalidConfig,"workers value must be a Hash or an Array: #{workers.inspect}") | ||
end | ||
end | ||
|
||
if (params_value = data[:params]) | ||
unless params_value.kind_of?(Hash) | ||
raise(InvalidConfig,"params value must be a Hash: #{params_value.inspect}") | ||
end | ||
|
||
params_value.each do |worker_id,params_hash| | ||
unless worker_id.kind_of?(String) | ||
raise(InvalidConfig,"worker ID must be a String: #{worker_id.inspect}") | ||
end | ||
|
||
unless params_hash.kind_of?(Hash) | ||
raise(InvalidConfig,"params value for worker (#{worker_id.inspect}) must be a Hash: #{params_hash.inspect}") | ||
end | ||
|
||
params_hash.each_key do |param_key| | ||
unless param_key.kind_of?(Symbol) | ||
raise(InvalidConfig,"param key for worker (#{worker_id.inspect}) must be a Symbol: #{param_key.inspect}") | ||
end | ||
end | ||
end | ||
end | ||
|
||
if (concurrency_value = data[:concurrency]) | ||
unless concurrency_value.kind_of?(Hash) | ||
raise(InvalidConfig,"concurrency value must be a Hash: #{concurrency_value.inspect}") | ||
end | ||
|
||
concurrency_value.each do |worker_id,concurrency| | ||
unless worker_id.kind_of?(String) | ||
raise(InvalidConfig,"worker ID must be a String: #{worker_id.inspect}") | ||
end | ||
|
||
unless concurrency.kind_of?(Integer) | ||
raise(InvalidConfig,"concurrency value for worker (#{worker_id.inspect}) must be an Integer: #{concurrency.inspect}") | ||
end | ||
end | ||
end | ||
|
||
return true | ||
end | ||
|
||
# | ||
# Loads configuration from a YAML file. | ||
# | ||
# @param [String] path | ||
# The path to the YAML configuration file. | ||
# | ||
# @raise [InvalidConfigFile] | ||
# The configuration file contained invalid YAML. | ||
# | ||
def self.load(path) | ||
yaml = YAML.load_file(path) | ||
|
||
begin | ||
validate(yaml) | ||
rescue InvalidConfig => error | ||
raise(InvalidConfigFile,"invalid config file (#{path.inspect}): #{error.message}") | ||
end | ||
|
||
workers = if (workers_value = yaml[:workers]) | ||
Workers.new(workers_value) | ||
else | ||
Workers.default | ||
end | ||
|
||
params = yaml.fetch(:params,{}) | ||
concurrency = yaml.fetch(:concurrency,{}) | ||
|
||
return new(workers: workers, params: params, concurrency: concurrency) | ||
end | ||
|
||
# The path to the `~/.config/ronin-recon/config.yml` file. | ||
DEFAULT_PATH = File.join(Core::Home.config_dir('ronin-recon'),'config.yml') | ||
|
||
# | ||
# The default configuration to use. | ||
# | ||
# @return [Config] | ||
# | ||
# @api public | ||
# | ||
def self.default | ||
if File.file?(DEFAULT_PATH) | ||
load(DEFAULT_PATH) | ||
else | ||
new | ||
end | ||
end | ||
|
||
# | ||
# Overrides the workers. | ||
# | ||
# @param [Workers, Set<String>, Array<String>] new_workers | ||
# The new workers value. | ||
# | ||
# @return [Workers] | ||
# The new workers value. | ||
# | ||
# @raise [ArgumentError] | ||
# An invalid workers value was given. | ||
# | ||
# @api public | ||
# | ||
def workers=(new_workers) | ||
@workers = case new_workers | ||
when Workers then new_workers | ||
when Set, Array then Workers.new(new_workers) | ||
else | ||
raise(ArgumentError,"new workers value must be a #{Workers}, Set, or Array: #{new_workers}") | ||
end | ||
end | ||
|
||
# | ||
# Compares the configuration with another object. | ||
# | ||
# @param [Object] other | ||
# The other object. | ||
# | ||
# @return [Boolean] | ||
# | ||
def eql?(other) | ||
self.class == other.class && | ||
@workers == other.workers && | ||
@params == other.params && | ||
@concurrency == other.concurrency | ||
end | ||
|
||
alias == eql? | ||
|
||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.