class Recog::Fingerprint
A fingerprint that can be {#match matched} against a particular kind of fingerprintable data, e.g. an HTTP `Server` header
Attributes
A human readable name describing this fingerprint @return (see parse_description)
Collection of indexes for capture groups created by {#match}
@return (see parse_params)
Regular expression pulled from the {DB} xml file.
@see create_regexp @return [Regexp] the Regexp to try when calling {#match}
Collection of example strings that should {#match} our {#regex}
@return (see parse_examples)
Public Class Methods
@param xml [Nokogiri::XML::Element]
# File lib/recog/fingerprint.rb, line 30 def initialize(xml) @name = parse_description(xml) @regex = create_regexp(xml) @params = {} @tests = [] parse_examples(xml) parse_params(xml) end
Public Instance Methods
Attempt to match the given string.
@param match_string [String] @return [Hash,nil] Keys will be host, service, and os attributes
# File lib/recog/fingerprint.rb, line 44 def match(match_string) # match_string.force_encoding('BINARY') if match_string match_data = @regex.match(match_string) return if match_data.nil? result = { 'matched' => @name } @params.each_pair do |k,v| pos = v[0] if pos == 0 # A match offset of 0 means this param has a hardcoded value result[k] = v[1] else # A match offset other than 0 means the value should come from # the corresponding match result index result[k] = match_data[ pos ] end end return result end
Ensure all the {#params} are valid
@yieldparam status [Symbol] One of `:warn`, `:fail`, or `:success` to
indicate whether a param is valid
@yieldparam message [String] A human-readable string explaining the
%x`status`
# File lib/recog/fingerprint.rb, line 70 def verify_params(&block) return if params.empty? params.each do |param_name, pos_value| pos, value = pos_value next unless pos != 0 && !value.to_s.empty? yield :fail, "'#{@name}'s #{param_name} is a non-zero pos but specifies a value of '#{value}'" end end
Ensure all the {#tests} actually match the fingerprint and return the expected capture groups.
@yieldparam status [Symbol] One of `:warn`, `:fail`, or `:success` to
indicate whether a test worked
@yieldparam message [String] A human-readable string explaining the
%x`status`
# File lib/recog/fingerprint.rb, line 86 def verify_tests(&block) if tests.size == 0 yield :warn, "'#{@name}' has no test cases" end tests.each do |test| result = match(test.content) if result.nil? yield :fail, "'#{@name}' failed to match #{test.content.inspect} with #{@regex}'" next end message = test status = :success # Ensure that all the attributes as provided by the example were parsed # out correctly and match the capture group values we expect. test.attributes.each do |k, v| if !result.has_key?(k) || result[k] != v message = "'#{@name}' failed to find expected capture group #{k} '#{v}'" status = :fail break end end yield status, message end end
Private Instance Methods
@param xml [Nokogiri::XML::Element] @return [Regexp]
# File lib/recog/fingerprint.rb, line 117 def create_regexp(xml) pattern = xml['pattern'] flags = xml['flags'].to_s.split(',') RegexpFactory.build(pattern, flags) end
@param xml [Nokogiri::XML::Element] @return [String] Contents of the source XML's `description` tag
# File lib/recog/fingerprint.rb, line 125 def parse_description(xml) element = xml.xpath('description') element.empty? ? '' : element.first.content.to_s.gsub(/\s+/, ' ').strip end
@param xml [Nokogiri::XML::Element] @return [void]
# File lib/recog/fingerprint.rb, line 132 def parse_examples(xml) elements = xml.xpath('example') elements.each do |elem| # convert nokogiri Attributes into a hash of name => value attrs = elem.attributes.values.reduce({}) { |a,e| a.merge(e.name => e.value) } @tests << Test.new(elem.content, attrs) end nil end
@param xml [Nokogiri::XML::Element] @return [Hash<String,Array>] Keys are things like `“os.name”`, values are a two
element Array. The first element is an index for the capture group that returns that thing. If the index is 0, the second element is a static value for that thing; otherwise it is undefined.
# File lib/recog/fingerprint.rb, line 149 def parse_params(xml) @params = {}.tap do |h| xml.xpath('param').each do |param| name = param['name'] pos = param['pos'].to_i value = param['value'].to_s h[name] = [pos, value] end end nil end