recog 3.1.1 → 3.1.2

Sign up to get free protection for your applications and to get access to all the features.
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