recog 3.1.1 → 3.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/Gemfile +6 -0
  4. data/Rakefile +7 -5
  5. data/lib/recog/db.rb +67 -68
  6. data/lib/recog/db_manager.rb +22 -21
  7. data/lib/recog/fingerprint/regexp_factory.rb +10 -13
  8. data/lib/recog/fingerprint/test.rb +9 -8
  9. data/lib/recog/fingerprint.rb +252 -262
  10. data/lib/recog/fingerprint_parse_error.rb +3 -1
  11. data/lib/recog/formatter.rb +41 -39
  12. data/lib/recog/match_reporter.rb +82 -83
  13. data/lib/recog/matcher.rb +37 -40
  14. data/lib/recog/matcher_factory.rb +7 -6
  15. data/lib/recog/nizer.rb +218 -224
  16. data/lib/recog/verifier.rb +30 -28
  17. data/lib/recog/verify_reporter.rb +69 -73
  18. data/lib/recog/version.rb +3 -1
  19. data/lib/recog.rb +2 -0
  20. data/recog/bin/recog_match +21 -20
  21. data/recog/xml/apache_modules.xml +2 -0
  22. data/recog/xml/dhcp_vendor_class.xml +1 -1
  23. data/recog/xml/favicons.xml +133 -1
  24. data/recog/xml/ftp_banners.xml +1 -1
  25. data/recog/xml/html_title.xml +140 -1
  26. data/recog/xml/http_cookies.xml +20 -2
  27. data/recog/xml/http_servers.xml +38 -17
  28. data/recog/xml/http_wwwauth.xml +17 -4
  29. data/recog/xml/mdns_device-info_txt.xml +49 -15
  30. data/recog/xml/sip_banners.xml +0 -2
  31. data/recog/xml/sip_user_agents.xml +1 -1
  32. data/recog/xml/snmp_sysdescr.xml +1 -2
  33. data/recog/xml/ssh_banners.xml +8 -0
  34. data/recog/xml/telnet_banners.xml +3 -2
  35. data/recog/xml/tls_jarm.xml +1 -1
  36. data/recog/xml/x11_banners.xml +1 -0
  37. data/recog/xml/x509_issuers.xml +1 -1
  38. data/recog/xml/x509_subjects.xml +0 -1
  39. data/recog.gemspec +14 -13
  40. data/spec/lib/recog/db_spec.rb +37 -36
  41. data/spec/lib/recog/fingerprint/regexp_factory_spec.rb +19 -20
  42. data/spec/lib/recog/fingerprint_spec.rb +44 -42
  43. data/spec/lib/recog/formatter_spec.rb +20 -18
  44. data/spec/lib/recog/match_reporter_spec.rb +35 -30
  45. data/spec/lib/recog/nizer_spec.rb +85 -101
  46. data/spec/lib/recog/verify_reporter_spec.rb +45 -44
  47. data/spec/spec_helper.rb +2 -1
  48. data.tar.gz.sig +1 -3
  49. metadata +3 -3
  50. metadata.gz.sig +0 -0
@@ -1,51 +1,53 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Recog
2
- class Formatter
3
- COLORS = {
4
- :red => 31,
5
- :yellow => 33,
6
- :green => 32,
7
- :white => 15
8
- }
9
-
10
- attr_reader :options, :output
11
-
12
- def initialize(options, output)
13
- @options = options
14
- @output = output || StringIO.new
15
- end
4
+ class Formatter
5
+ COLORS = {
6
+ red: 31,
7
+ yellow: 33,
8
+ green: 32,
9
+ white: 15
10
+ }.freeze
16
11
 
17
- def status_message(text)
18
- output.puts color(text, :white)
19
- end
12
+ attr_reader :options, :output
20
13
 
21
- def success_message(text)
22
- output.puts color(text, :green)
23
- end
14
+ def initialize(options, output)
15
+ @options = options
16
+ @output = output || StringIO.new
17
+ end
24
18
 
25
- def warning_message(text)
26
- output.puts color(text, :yellow)
27
- end
19
+ def status_message(text)
20
+ output.puts color(text, :white)
21
+ end
28
22
 
29
- def failure_message(text)
30
- output.puts color(text, :red)
31
- end
23
+ def success_message(text)
24
+ output.puts color(text, :green)
25
+ end
32
26
 
33
- private
27
+ def warning_message(text)
28
+ output.puts color(text, :yellow)
29
+ end
34
30
 
35
- def color_enabled?
36
- options.color
37
- end
31
+ def failure_message(text)
32
+ output.puts color(text, :red)
33
+ end
38
34
 
39
- def color(text, color_code)
40
- color_enabled? ? colorize(text, color_code) : text
41
- end
35
+ private
42
36
 
43
- def colorize(text, color_code)
44
- "\e[#{color_code_for(color_code)}m#{text}\e[0m"
45
- end
37
+ def color_enabled?
38
+ options.color
39
+ end
46
40
 
47
- def color_code_for(code)
48
- COLORS.fetch(code) { COLORS.fetch(:white) }
41
+ def color(text, color_code)
42
+ color_enabled? ? colorize(text, color_code) : text
43
+ end
44
+
45
+ def colorize(text, color_code)
46
+ "\e[#{color_code_for(color_code)}m#{text}\e[0m"
47
+ end
48
+
49
+ def color_code_for(code)
50
+ COLORS.fetch(code) { COLORS.fetch(:white) }
51
+ end
49
52
  end
50
53
  end
51
- end
@@ -1,111 +1,110 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'json'
2
4
 
3
5
  module Recog
4
- class MatchReporter
5
- attr_reader :formatter
6
- attr_reader :line_count, :match_count, :fail_count
7
-
8
- def initialize(options, formatter)
9
- @options = options
10
- @formatter = formatter
11
- reset_counts
12
- end
6
+ class MatchReporter
7
+ attr_reader :formatter, :line_count, :match_count, :fail_count
13
8
 
14
- def report
15
- reset_counts
16
- yield self
17
- summarize unless @options.quiet
18
- end
9
+ def initialize(options, formatter)
10
+ @options = options
11
+ @formatter = formatter
12
+ reset_counts
13
+ end
19
14
 
20
- def stop?
21
- return false unless @options.fail_fast
22
- @fail_count >= @options.stop_after
23
- end
15
+ def report
16
+ reset_counts
17
+ yield self
18
+ summarize unless @options.quiet
19
+ end
24
20
 
25
- def increment_line_count
26
- @line_count += 1
27
- end
21
+ def stop?
22
+ return false unless @options.fail_fast
28
23
 
29
- def match(match_data)
30
- @match_count += 1
31
- if @options.json_format
32
- # remove data field from all matches and promote to a top-level field
33
- data_field = match_data[0]["data"]
34
- match_data.each { |h| h.delete("data") }
35
- new_object = {
36
- 'data' => data_field,
37
- }
38
-
39
- if @options.multi_match
40
- new_object['matches'] = match_data
24
+ @fail_count >= @options.stop_after
25
+ end
26
+
27
+ def increment_line_count
28
+ @line_count += 1
29
+ end
30
+
31
+ def match(match_data)
32
+ @match_count += 1
33
+ if @options.json_format
34
+ # remove data field from all matches and promote to a top-level field
35
+ data_field = match_data[0]['data']
36
+ match_data.each { |h| h.delete('data') }
37
+ new_object = {
38
+ 'data' => data_field
39
+ }
40
+
41
+ if @options.multi_match
42
+ new_object['matches'] = match_data
43
+ else
44
+ new_object['match'] = match_data[0]
45
+ end
46
+ msg = new_object.to_json
41
47
  else
42
- new_object['match'] = match_data[0]
48
+ match_prefix = match_data.size > 1 ? 'MATCHES' : 'MATCH'
49
+ msg = "#{match_prefix}: #{match_data.map(&:inspect).join(',')}"
43
50
  end
44
- msg = new_object.to_json
45
- else
46
- match_prefix = match_data.size > 1 ? 'MATCHES' : 'MATCH'
47
- msg = "#{match_prefix}: #{match_data.map(&:inspect).join(',')}"
51
+ formatter.success_message(msg.to_s)
48
52
  end
49
- formatter.success_message("#{msg}")
50
- end
51
53
 
52
- def failure(text)
53
- @fail_count += 1
54
- if @options.json_format
55
- new_object = {
56
- 'data' => text,
57
- 'match_failure' => true
58
- }
59
- if @options.multi_match
60
- new_object['matches'] = nil
54
+ def failure(text)
55
+ @fail_count += 1
56
+ if @options.json_format
57
+ new_object = {
58
+ 'data' => text,
59
+ 'match_failure' => true
60
+ }
61
+ if @options.multi_match
62
+ new_object['matches'] = nil
63
+ else
64
+ new_object['match'] = nil
65
+ end
66
+ msg = new_object.to_json
61
67
  else
62
- new_object['match'] = nil
68
+ msg = "FAIL: #{text}"
63
69
  end
64
- msg = new_object.to_json
65
- else
66
- msg = "FAIL: #{text}"
70
+ formatter.failure_message(msg.to_s)
67
71
  end
68
- formatter.failure_message("#{msg}")
69
- end
70
72
 
71
- def print_summary
72
- colorize_summary(summary_line)
73
- end
73
+ def print_summary
74
+ colorize_summary(summary_line)
75
+ end
74
76
 
75
- private
77
+ private
76
78
 
77
- def reset_counts
78
- @line_count = @match_count = @fail_count = 0
79
- end
79
+ def reset_counts
80
+ @line_count = @match_count = @fail_count = 0
81
+ end
80
82
 
81
- def detail?
82
- @options.detail
83
- end
83
+ def detail?
84
+ @options.detail
85
+ end
86
+
87
+ def summarize
88
+ return unless detail?
84
89
 
85
- def summarize
86
- if detail?
87
90
  print_lines_processed
88
91
  print_summary
89
92
  end
90
- end
91
93
 
92
- def print_lines_processed
93
- formatter.status_message("\nProcessed #{line_count} lines")
94
- end
94
+ def print_lines_processed
95
+ formatter.status_message("\nProcessed #{line_count} lines")
96
+ end
95
97
 
96
- def summary_line
97
- summary = "SUMMARY: "
98
- summary << "#{match_count} matches"
99
- summary << " and #{fail_count} failures"
100
- summary
101
- end
98
+ def summary_line
99
+ "SUMMARY: #{match_count} matches and #{fail_count} failures"
100
+ end
102
101
 
103
- def colorize_summary(summary)
104
- if @fail_count > 0
105
- formatter.failure_message(summary)
106
- else
107
- formatter.success_message(summary)
102
+ def colorize_summary(summary)
103
+ if @fail_count > 0
104
+ formatter.failure_message(summary)
105
+ else
106
+ formatter.success_message(summary)
107
+ end
108
108
  end
109
109
  end
110
110
  end
111
- end
data/lib/recog/matcher.rb CHANGED
@@ -1,60 +1,57 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Recog
2
- class Matcher
3
- attr_reader :fingerprints, :reporter, :multi_match
4
-
5
- # @param fingerprints Array of [Recog::Fingerprint] The list of fingerprints from the Recog DB to find possible matches.
6
- # @param reporter [Recog::MatchReporter] The reporting structure that holds the matches and fails
7
- # @param multi_match [Boolean] specifies whether or not to use multi-match (true) or not (false)
8
- def initialize(fingerprints, reporter, multi_match)
9
- @fingerprints = fingerprints
10
- @reporter = reporter
11
- @multi_match = multi_match
12
- end
4
+ class Matcher
5
+ attr_reader :fingerprints, :reporter, :multi_match
6
+
7
+ # @param fingerprints Array of [Recog::Fingerprint] The list of fingerprints from the Recog DB to find possible matches.
8
+ # @param reporter [Recog::MatchReporter] The reporting structure that holds the matches and fails
9
+ # @param multi_match [Boolean] specifies whether or not to use multi-match (true) or not (false)
10
+ def initialize(fingerprints, reporter, multi_match)
11
+ @fingerprints = fingerprints
12
+ @reporter = reporter
13
+ @multi_match = multi_match
14
+ end
13
15
 
14
- # @param banners_file [String] The source of banners to attempt to match against the Recog DB.
15
- def match_banners(banners_file)
16
- reporter.report do
16
+ # @param banners_file [String] The source of banners to attempt to match against the Recog DB.
17
+ def match_banners(banners_file)
18
+ reporter.report do
19
+ fd = $stdin
20
+ file_source = false
17
21
 
18
- fd = $stdin
19
- file_source = false
22
+ if banners_file && (banners_file != '-')
23
+ fd = File.open(banners_file, 'rb')
24
+ file_source = true
25
+ end
20
26
 
21
- if banners_file and banners_file != "-"
22
- fd = File.open(banners_file, "rb")
23
- file_source = true
24
- end
27
+ fd.each_line do |line|
28
+ reporter.increment_line_count
25
29
 
26
- fd.each_line do |line|
27
- reporter.increment_line_count
30
+ line = line.to_s.unpack('C*').pack('C*').strip.gsub(/\\[rn]/, '')
31
+ found_extractions = false
28
32
 
29
- line = line.to_s.unpack("C*").pack("C*").strip.gsub(/\\[rn]/, '')
30
- found_extractions = false
33
+ extraction_data = []
34
+ fingerprints.each do |fp|
35
+ extractions = fp.match(line)
36
+ next unless extractions
31
37
 
32
- extraction_data = []
33
- fingerprints.each do |fp|
34
- extractions = fp.match(line)
35
- if extractions
36
38
  found_extractions = true
37
39
  extractions['data'] = line
38
40
  extraction_data << extractions
39
41
  break unless multi_match
40
42
  end
41
- end
42
43
 
43
- if found_extractions
44
- reporter.match extraction_data
45
- else
46
- reporter.failure line
47
- end
44
+ if found_extractions
45
+ reporter.match extraction_data
46
+ else
47
+ reporter.failure line
48
+ end
48
49
 
49
- if reporter.stop?
50
- break
50
+ break if reporter.stop?
51
51
  end
52
52
 
53
+ fd.close if file_source
53
54
  end
54
-
55
- fd.close if file_source
56
-
57
55
  end
58
56
  end
59
57
  end
60
- end
@@ -1,14 +1,15 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  require 'recog/matcher'
3
4
  require 'recog/formatter'
4
5
  require 'recog/match_reporter'
5
6
 
6
7
  module Recog
7
- module MatcherFactory
8
- def self.build(options)
9
- formatter = Formatter.new(options, $stdout)
10
- reporter = MatchReporter.new(options, formatter)
11
- Matcher.new(options.fingerprints, reporter, options.multi_match)
8
+ module MatcherFactory
9
+ def self.build(options)
10
+ formatter = Formatter.new(options, $stdout)
11
+ reporter = MatchReporter.new(options, formatter)
12
+ Matcher.new(options.fingerprints, reporter, options.multi_match)
13
+ end
12
14
  end
13
15
  end
14
- end