lib/wpscan/finders/dynamic_finder/version/config_parser.rb
# frozen_string_literal: true
module WPScan
module Finders
module DynamicFinder
module Version
# Version finder using by parsing config files, such as composer.json
# and so on
class ConfigParser < Finders::DynamicFinder::Version::Finder
ALLOWED_PARSERS = [JSON, YAML].freeze
def self.child_class_constants
@child_class_constants ||= super.merge(
PARSER: nil, KEY: nil, PATTERN: /(?<v>\d+\.[.\d]+)/, CONFIDENCE: 70
)
end
# @param [ String ] body
# @return [ Hash, nil ] The parsed body, with an available parser, if possible
def parse(body)
parsers = ALLOWED_PARSERS.include?(self.class::PARSER) ? [self.class::PARSER] : ALLOWED_PARSERS
parsers.each do |parser|
parsed = parser.respond_to?(:safe_load) ? parser.safe_load(body) : parser.load(body)
return parsed if parsed.is_a?(Hash) || parsed.is_a?(Array)
rescue StandardError
next
end
nil # Make sure nil is returned in case none of the parsers managed to parse the body correctly
end
# No Passive way
def passive(opts = {}); end
# @param [ Typhoeus::Response ] response
# @param [ Hash ] opts
# @return [ Version ]
def find(response, _opts = {})
parsed_body = parse(response.body)
# Create indexes for the #dig, digits are converted to integers
indexes = self.class::KEY.split(':').map { |e| e == e.to_i.to_s ? e.to_i : e }
return unless (data = parsed_body&.dig(*indexes)) && data =~ self.class::PATTERN
create_version(
Regexp.last_match[:v],
interesting_entries: ["#{response.effective_url}, Match: '#{Regexp.last_match}'"]
)
end
end
end
end
end
end