threatinator 0.1.6 → 0.2.0

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 (197) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -2
  3. data/Gemfile +18 -13
  4. data/Rakefile +1 -1
  5. data/VERSION +1 -1
  6. data/feeds/ET_block-ip_reputation.feed +26 -0
  7. data/feeds/ET_openbadlist-ip_reputation.feed +35 -0
  8. data/feeds/bambenek_c2_masterlist-domain_reputation.feed +15 -0
  9. data/feeds/bambenek_c2_masterlist-ip_reputation.feed +15 -0
  10. data/feeds/bambenek_dga_feed-domain_reputation.feed +15 -0
  11. data/feeds/berkeley-ip_reputation.feed +23 -0
  12. data/feeds/bitcash_cz_blacklist.feed +20 -0
  13. data/feeds/botscout-ip_reputation.feed +24 -0
  14. data/feeds/cert_mxpoison-ip_reputation.feed +21 -0
  15. data/feeds/chaosreigns-ip_reputation.feed +36 -0
  16. data/feeds/cydef_torexit-ip_reputation.feed +24 -0
  17. data/feeds/danger_bruteforce-ip_reputation.feed +23 -0
  18. data/feeds/falconcrest-ip_reputation.feed +18 -0
  19. data/feeds/h3x_asprox.feed +17 -0
  20. data/feeds/hosts-file_hphostspartial-domain_reputation.feed +18 -0
  21. data/feeds/infiltrated_vabl-ip_reputation.feed +29 -0
  22. data/feeds/isc_suspicious_high-domain_reputation.feed +25 -0
  23. data/feeds/isc_suspicious_low-domain_reputation.feed +25 -0
  24. data/feeds/isc_suspicious_medium-domain_reputation.feed +25 -0
  25. data/feeds/malwaredomainlist-url_reputation.feed +16 -0
  26. data/feeds/malwaredomains-domain_reputation.feed +27 -0
  27. data/feeds/malwaredomains_dyndns-domain_reputation.feed +27 -0
  28. data/feeds/malwaredomains_justdomains-domain_reputation.feed +18 -0
  29. data/feeds/multiproxy-ip_reputation.feed +20 -0
  30. data/feeds/openphish-url_reputation.feed +22 -0
  31. data/feeds/packetmail_perimeterbad-ip_reputation.feed +26 -0
  32. data/feeds/phishtank.feed +1 -1
  33. data/feeds/sigmaproject_atma.feed +25 -0
  34. data/feeds/sigmaproject_spyware.feed +24 -0
  35. data/feeds/sigmaproject_webexploit.feed +26 -0
  36. data/feeds/snort_bpf-ip_reputation.feed +19 -0
  37. data/feeds/steeman-ip_reputation.feed +19 -0
  38. data/feeds/trustedsec-ip_reputation.feed +17 -0
  39. data/feeds/virbl-ip_reputation.feed +24 -0
  40. data/feeds/vxvault-url_reputation.feed +22 -0
  41. data/feeds/yoyo_adservers-domain_reputation.feed +16 -0
  42. data/lib/threatinator/actions/run/action.rb +15 -3
  43. data/lib/threatinator/actions/run/coverage_observer.rb +12 -7
  44. data/lib/threatinator/actions/run/status_observer.rb +37 -0
  45. data/lib/threatinator/cli.rb +9 -3
  46. data/lib/threatinator/cli/parser.rb +14 -4
  47. data/lib/threatinator/config.rb +1 -0
  48. data/lib/threatinator/config/logger.rb +14 -0
  49. data/lib/threatinator/event.rb +28 -18
  50. data/lib/threatinator/event_builder.rb +52 -23
  51. data/lib/threatinator/exceptions.rb +3 -6
  52. data/lib/threatinator/feed.rb +1 -1
  53. data/lib/threatinator/feed_runner.rb +63 -7
  54. data/lib/threatinator/logger.rb +66 -0
  55. data/lib/threatinator/logging.rb +20 -0
  56. data/lib/threatinator/model/base.rb +23 -0
  57. data/lib/threatinator/model/collection.rb +64 -0
  58. data/lib/threatinator/model/observables/fqdn_collection.rb +13 -0
  59. data/lib/threatinator/model/observables/ipv4.rb +30 -0
  60. data/lib/threatinator/model/observables/ipv4_collection.rb +14 -0
  61. data/lib/threatinator/model/observables/url_collection.rb +16 -0
  62. data/lib/threatinator/model/validations.rb +1 -0
  63. data/lib/threatinator/model/validations/type.rb +21 -0
  64. data/lib/threatinator/plugins/output/csv.rb +20 -9
  65. data/spec/feeds/ET_block-ip_reputation_spec.rb +50 -0
  66. data/spec/feeds/ET_compromised-ip_reputation_spec.rb +2 -5
  67. data/spec/feeds/ET_openbadlist-ip_reputation_spec.rb +56 -0
  68. data/spec/feeds/alienvault-ip_reputation_spec.rb +2 -5
  69. data/spec/feeds/arbor_fastflux-domain_reputation_spec.rb +0 -3
  70. data/spec/feeds/arbor_ssh-ip_reputation_spec.rb +2 -5
  71. data/spec/feeds/autoshun_shunlist_spec.rb +1 -4
  72. data/spec/feeds/bambenek_c2_masterlist-domain_reputation_spec.rb +39 -0
  73. data/spec/feeds/bambenek_c2_masterlist-ip_reputation_spec.rb +39 -0
  74. data/spec/feeds/bambenek_dga_feed-domain_reputation_spec.rb +39 -0
  75. data/spec/feeds/berkeley-ip_reputation_spec.rb +47 -0
  76. data/spec/feeds/bitcash_cz_blacklist-ip_reputation_spec.rb +50 -0
  77. data/spec/feeds/blocklist_de_apache-ip_reputation_spec.rb +2 -5
  78. data/spec/feeds/blocklist_de_bots-ip_reputation_spec.rb +2 -5
  79. data/spec/feeds/blocklist_de_ftp-ip_reputation_spec.rb +2 -5
  80. data/spec/feeds/blocklist_de_imap-ip_reputation_spec.rb +2 -5
  81. data/spec/feeds/blocklist_de_pop3-ip_reputation_spec.rb +2 -5
  82. data/spec/feeds/blocklist_de_proftpd-ip_reputation_spec.rb +2 -5
  83. data/spec/feeds/blocklist_de_sip-ip_reputation_spec.rb +2 -5
  84. data/spec/feeds/blocklist_de_ssh-ip_reputation_spec.rb +2 -5
  85. data/spec/feeds/blocklist_de_strongips-ip_reputation_spec.rb +2 -5
  86. data/spec/feeds/botscout-ip_reputation_spec.rb +50 -0
  87. data/spec/feeds/cert_mxpoison-ip_reputation_spec.rb +47 -0
  88. data/spec/feeds/chaosreigns-ip_reputation_spec.rb +50 -0
  89. data/spec/feeds/ciarmy-ip_reputation_spec.rb +2 -5
  90. data/spec/feeds/cruzit-ip_reputation_spec.rb +2 -5
  91. data/spec/feeds/cydef_torexit-ip_reputation_spec.rb +47 -0
  92. data/spec/feeds/dan_me_uk_torlist-ip_reputation_spec.rb +2 -5
  93. data/spec/feeds/danger_bruteforce-ip_reputation_spec.rb +47 -0
  94. data/spec/feeds/data/ET_block-ip_reputation.txt +80 -0
  95. data/spec/feeds/data/ET_openbadlist-ip_reputation.txt +62 -0
  96. data/spec/feeds/data/bambenek_c2-dommasterlist.csv +30 -0
  97. data/spec/feeds/data/bambenek_c2-ipmasterlist.csv +27 -0
  98. data/spec/feeds/data/bambenek_dga_feed.csv +42 -0
  99. data/spec/feeds/data/berkeley.txt +29 -0
  100. data/spec/feeds/data/bitcash_cz_blacklist.txt +7 -0
  101. data/spec/feeds/data/botscout-ip-reputation.txt +713 -0
  102. data/spec/feeds/data/cert_mxpoison-ip_reputation.txt +17 -0
  103. data/spec/feeds/data/chaosreigns-ip-reputation.txt +26 -0
  104. data/spec/feeds/data/cydef_torexit-ip_reputation.txt +27 -0
  105. data/spec/feeds/data/danger_bruteforce-ip_reputation.txt +12 -0
  106. data/spec/feeds/data/falconcrest_iplist.txt +345 -0
  107. data/spec/feeds/data/h3x_asprox.txt +20 -0
  108. data/spec/feeds/data/hosts-file_hphostspartial_domainlist.txt +24 -0
  109. data/spec/feeds/data/infiltrated_vabl_iplist.txt +33 -0
  110. data/spec/feeds/data/isc_suspicious_high_domainlist.txt +26 -0
  111. data/spec/feeds/data/isc_suspicious_low_domainlist.txt +34 -0
  112. data/spec/feeds/data/isc_suspicious_medium_domainlist.txt +32 -0
  113. data/spec/feeds/data/malwaredomainlist-url-reputation.txt +8 -0
  114. data/spec/feeds/data/malwaredomains_domainlist.txt +24 -0
  115. data/spec/feeds/data/malwaredomains_dyndns_domainlist.txt +34 -0
  116. data/spec/feeds/data/malwaredomains_justdomains_domainlist.txt +18 -0
  117. data/spec/feeds/data/multiproxy_iplist.txt +15 -0
  118. data/spec/feeds/data/openphish-url-reputation.txt +16 -0
  119. data/spec/feeds/data/packetmail_perimeterbad-ip_reputation.txt +44 -0
  120. data/spec/feeds/data/sigmaproject_atma.return.gz +0 -0
  121. data/spec/feeds/data/sigmaproject_spyware.return.gz +0 -0
  122. data/spec/feeds/data/sigmaproject_webexploit.return.gz +0 -0
  123. data/spec/feeds/data/snort_bpf-ip_reputation.txt +16 -0
  124. data/spec/feeds/data/steeman-ip-reputation.txt +13 -0
  125. data/spec/feeds/data/trustedsec-ip-reputation.txt +12 -0
  126. data/spec/feeds/data/virbl-ip_reputation.txt +14 -0
  127. data/spec/feeds/data/vxvault-url-reputation.txt +15 -0
  128. data/spec/feeds/data/yoyo_adservers.txt +25 -0
  129. data/spec/feeds/dshield_attackers-top1000_spec.rb +1 -4
  130. data/spec/feeds/falconcrest-ip_reputation_spec.rb +37 -0
  131. data/spec/feeds/feodo-domain_reputation_spec.rb +0 -3
  132. data/spec/feeds/feodo-ip_reputation_spec.rb +2 -5
  133. data/spec/feeds/h3x_asprox-ip_reputation_spec.rb +50 -0
  134. data/spec/feeds/hosts-file_hphostspartial-domain_reputation_spec.rb +47 -0
  135. data/spec/feeds/infiltrated-ip_reputation_spec.rb +2 -5
  136. data/spec/feeds/infiltrated_vabl-ip_reputation_spec.rb +47 -0
  137. data/spec/feeds/isc_suspicious_high-domain_reputation_spec.rb +47 -0
  138. data/spec/feeds/isc_suspicious_low-domain_reputation_spec.rb +47 -0
  139. data/spec/feeds/isc_suspicious_medium-domain_reputation_spec.rb +47 -0
  140. data/spec/feeds/malc0de-domain_reputation_spec.rb +0 -3
  141. data/spec/feeds/malc0de-ip_reputation_spec.rb +2 -5
  142. data/spec/feeds/malwaredomainlist_url_reputation_spec.rb +50 -0
  143. data/spec/feeds/malwaredomains-domain_reputation_spec.rb +47 -0
  144. data/spec/feeds/malwaredomains_dyndns-domain_reputation_spec.rb +47 -0
  145. data/spec/feeds/malwaredomains_justdomains-domain_reputation_spec.rb +47 -0
  146. data/spec/feeds/mirc-domain_reputation_spec.rb +0 -3
  147. data/spec/feeds/multiproxy-ip_reputation_spec.rb +47 -0
  148. data/spec/feeds/nothink_irc-ip_reputation_spec.rb +2 -5
  149. data/spec/feeds/nothink_ssh-ip_reputation_spec.rb +2 -5
  150. data/spec/feeds/openbl-ip_reputation_spec.rb +2 -5
  151. data/spec/feeds/openphish_url_reputation_spec.rb +50 -0
  152. data/spec/feeds/packetmail_perimeterbad-ip_reputation_spec.rb +47 -0
  153. data/spec/feeds/palevo-domain_reputation_spec.rb +0 -3
  154. data/spec/feeds/palevo-ip_reputation_spec.rb +2 -5
  155. data/spec/feeds/phishtank_spec.rb +2 -5
  156. data/spec/feeds/sigmaproject_atma_spec.rb +63 -0
  157. data/spec/feeds/sigmaproject_spyware_spec.rb +64 -0
  158. data/spec/feeds/sigmaproject_webexploit_spec.rb +63 -0
  159. data/spec/feeds/snort_bpf-ip_reputation_spec.rb +47 -0
  160. data/spec/feeds/spyeye-domain_reputation_spec.rb +0 -3
  161. data/spec/feeds/spyeye-ip_reputation_spec.rb +2 -5
  162. data/spec/feeds/steeman-ip_reputation_spec.rb +50 -0
  163. data/spec/feeds/t-arend-de_ssh-ip_reputation_spec.rb +2 -5
  164. data/spec/feeds/the_haleys_ssh-ip_reputation_spec.rb +2 -5
  165. data/spec/feeds/trustedsec-ip_reputation_spec.rb +47 -0
  166. data/spec/feeds/virbl-ip_reputation_spec.rb +47 -0
  167. data/spec/feeds/vxvault_url_reputation_spec.rb +50 -0
  168. data/spec/feeds/yourcmc_ssh-ip_reputation_spec.rb +2 -5
  169. data/spec/feeds/yoyo_adservers_spec.rb +47 -0
  170. data/spec/feeds/zeus-domain_reputation_spec.rb +0 -3
  171. data/spec/feeds/zeus-ip_reputation_spec.rb +2 -5
  172. data/spec/spec_helper.rb +2 -0
  173. data/spec/support/factories/event.rb +11 -7
  174. data/spec/support/factories/feed.rb +28 -1
  175. data/spec/support/factories/ipv4.rb +36 -0
  176. data/spec/support/factories/url.rb +34 -0
  177. data/spec/support/shared/feed_runner_observer.rb +136 -0
  178. data/spec/support/shared/feeds.rb +19 -4
  179. data/spec/support/shared/model/collection.rb +164 -0
  180. data/spec/threatinator/actions/run/action_spec.rb +27 -10
  181. data/spec/threatinator/actions/run/coverage_observer_spec.rb +39 -4
  182. data/spec/threatinator/actions/run/status_observer_spec.rb +86 -0
  183. data/spec/threatinator/event_builder_spec.rb +111 -21
  184. data/spec/threatinator/event_spec.rb +237 -13
  185. data/spec/threatinator/event_spec.rb.new +319 -0
  186. data/spec/threatinator/feed_builder_spec.rb +0 -3
  187. data/spec/threatinator/feed_runner_spec.rb +254 -70
  188. data/spec/threatinator/logger_spec.rb +29 -0
  189. data/spec/threatinator/model/observables/fqdn_collection_spec.rb +42 -0
  190. data/spec/threatinator/model/observables/ipv4_collection_spec.rb +36 -0
  191. data/spec/threatinator/model/observables/ipv4_spec.rb +75 -0
  192. data/spec/threatinator/model/observables/url_collection_spec.rb +45 -0
  193. data/spec/threatinator/model/validations/type_spec.rb +37 -0
  194. data/spec/threatinator/plugins/output/csv_spec.rb +4 -3
  195. metadata +216 -19
  196. data/lib/threatinator/property_definer.rb +0 -101
  197. data/spec/threatinator/property_definer_spec.rb +0 -155
@@ -75,7 +75,7 @@ module Threatinator
75
75
 
76
76
  def validate_attribute!(name, val, &block)
77
77
  unless block.call(val) == true
78
- raise Threatinator::Exceptions::InvalidAttributeError.new(name, val)
78
+ raise Threatinator::Exceptions::InvalidAttributeError.new("Invalid attribute (#{name}). Got: #{val.inspect}")
79
79
  end
80
80
  end
81
81
  end
@@ -1,4 +1,5 @@
1
1
  require 'threatinator/event_builder'
2
+ require 'threatinator/logging'
2
3
  require 'observer'
3
4
 
4
5
  module Threatinator
@@ -27,6 +28,10 @@ module Threatinator
27
28
  # - events - The events that were parsed out of the
28
29
  # record
29
30
  #
31
+ # :record_error - Indicates that the record WAS parsed
32
+ # - record - The record
33
+ # - errors - An array of exceptions that caused the error
34
+ #
30
35
  # :end_parse_record - when a record has been parsed
31
36
  # - record - The record
32
37
  #
@@ -34,17 +39,23 @@ module Threatinator
34
39
  #
35
40
  class FeedRunner
36
41
  include Observable
42
+ include Logging
37
43
 
38
44
  # @param [Threatinator::Feed] feed The feed that we want to run.
39
45
  # @param [Threatinator::Output] output_formatter
40
46
  def initialize(feed, output_formatter, opts = {})
41
47
  @feed = feed
42
48
  @output_formatter = output_formatter
43
- @event_builder = Threatinator::EventBuilder.new(@feed)
44
49
  @feed_filters = @feed.filter_builders.map { |x| x.call }
45
50
  @decoders = @feed.decoder_builders.map { |x| x.call }
46
51
  @parser_block = @feed.parser_block
47
- @create_event_proc = @event_builder.create_event_proc()
52
+ @create_event_proc = self.method(:create_event).to_proc
53
+
54
+ @event_builder = Threatinator::EventBuilder.new(@feed.provider, @feed.name)
55
+
56
+ @total_events_built = 0
57
+ @event_errors = []
58
+ @built_events = []
48
59
  end
49
60
 
50
61
  # @param [Hash] opts The options hash
@@ -53,20 +64,30 @@ module Threatinator
53
64
  # @option opts [Boolean] :skip_decoding (false) Skip all decoding if set
54
65
  # to true. Useful for testing.
55
66
  def run(opts = {})
67
+ ios = [ ]
68
+ logger.debug("#run starting #{@feed.provider}:#{@feed.name}") if logger.debug?
69
+ start = Time.now
56
70
  changed(true); notify_observers(:start)
57
71
  skip_decoding = !!opts.delete(:skip_decoding)
58
72
 
73
+
59
74
  unless io = opts.delete(:io)
60
75
  fetcher = @feed.fetcher_builder.call()
61
76
  changed(true); notify_observers(:start_fetch)
62
77
  io = fetcher.fetch()
63
78
  changed(true); notify_observers(:end_fetch)
79
+ else
80
+ logger.debug('#run Skipping fetch. IO object was provided')
64
81
  end
65
82
 
83
+ ios << io
84
+
66
85
  unless skip_decoding == true
67
86
  changed(true); notify_observers(:start_decode)
68
87
  @decoders.each do |decoder|
69
- io = decoder.decode(io)
88
+ new_io = decoder.decode(io)
89
+ ios << new_io
90
+ io = new_io
70
91
  end
71
92
  changed(true); notify_observers(:end_decode)
72
93
  end
@@ -78,11 +99,38 @@ module Threatinator
78
99
  end
79
100
 
80
101
  changed(true); notify_observers(:end)
102
+
103
+ logger.debug("#run finished #{@feed.provider}:#{@feed.name} in #{Time.now - start} seconds") if logger.debug?
81
104
  nil
105
+ ensure
106
+ # Close all IO objects that we've seen.
107
+ while some_io = ios.pop
108
+ unless some_io.closed?
109
+ begin
110
+ some_io.close
111
+ rescue => e
112
+ logger.warn("Failed to close IO: #{e} #{e.message}")
113
+ end
114
+ end
115
+ end
116
+
117
+ end
118
+
119
+ def create_event
120
+ @event_builder.reset
121
+ @event_errors.clear
122
+ yield(@event_builder)
123
+ begin
124
+ event = @event_builder.build
125
+ @total_events_built += 1
126
+ @built_events << event
127
+ rescue Threatinator::Exceptions::EventBuildError => e
128
+ @event_errors << e
129
+ end
82
130
  end
83
131
 
84
132
  def parse_record(record)
85
- @event_builder.clear
133
+ @built_events.clear
86
134
  events = []
87
135
  changed(true); notify_observers(:start_parse_record, record)
88
136
 
@@ -90,12 +138,20 @@ module Threatinator
90
138
  changed(true); notify_observers(:record_filtered, record)
91
139
  return
92
140
  end
141
+
93
142
  @parser_block.call(@create_event_proc, record)
94
- if @event_builder.count == 0
143
+
144
+ if @event_errors.count > 0
145
+ changed(true); notify_observers(:record_error, record, @event_errors)
146
+ position = "line: #{record.line_number}, start: #{record.pos_start}, end: #{record.pos_end}"
147
+ messages = @event_errors.map { |e| e.to_s }.join(', ')
148
+ logger.debug("Error generating event from record (#{position}): #{messages}")
149
+ elsif @built_events.count == 0
95
150
  changed(true); notify_observers(:record_missed, record)
96
- # Keep track of the fact that this line did not generate any events?
151
+ position = "line: #{record.line_number}, start: #{record.pos_start}, end: #{record.pos_end}"
152
+ logger.debug("Expected event to be generated, but got none from record (#{position})")
97
153
  else
98
- @event_builder.each_built_event do |event|
154
+ @built_events.each do |event|
99
155
  events << event
100
156
  @output_formatter.handle_event(event)
101
157
  end
@@ -0,0 +1,66 @@
1
+ require 'log4r'
2
+
3
+ module Threatinator
4
+ module Logger
5
+ # The log levels don't get defined until the first time a logger is
6
+ # initialized. So, we call the root logger once just to get this to happen.
7
+ Log4r::RootLogger.instance
8
+
9
+ def self.logger_for(name)
10
+ ::Log4r::Logger[name] || ::Log4r::Logger.new(name)
11
+ end
12
+
13
+ def self.default_logger
14
+ return @logger unless @logger.nil?
15
+
16
+ @logger = logger_for('Threatinator')
17
+ formatter = ::Log4r::PatternFormatter.new(:pattern => '[%d] %l %C: %M')
18
+
19
+ console_outputter = ::Log4r::StderrOutputter.new('console', formatter: formatter)
20
+ @logger.add console_outputter
21
+
22
+ @logger.level = ::Log4r::INFO
23
+ @logger
24
+ end
25
+
26
+ # @param [Threatinator::Config::Logger] config Logging configuration object
27
+ def self.configure_logger(config)
28
+ if config.level
29
+ if l = self.levels.index(config.level)
30
+ default_logger.level = l
31
+ else
32
+ default_logger.warn("Ignoring unknown logging level: #{config.level.inspect}.")
33
+ end
34
+ end
35
+ end
36
+
37
+ def self.level
38
+ default_logger.level
39
+ end
40
+
41
+ def self.level=(l)
42
+ default_logger.level = l
43
+ end
44
+
45
+ def self.level_string
46
+ levels[level]
47
+ end
48
+
49
+ def self.levels
50
+ default_logger.levels
51
+ end
52
+
53
+ # Initializes the default logger. This allows us to pull the logger levels.
54
+ self.default_logger()
55
+
56
+ module Levels
57
+ ALL = ::Log4r::ALL
58
+ DEBUG = ::Log4r::DEBUG
59
+ INFO = ::Log4r::INFO
60
+ WARN = ::Log4r::WARN
61
+ ERROR = ::Log4r::ERROR
62
+ FATAL = ::Log4r::FATAL
63
+ OFF = ::Log4r::OFF
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,20 @@
1
+ require 'threatinator/logger'
2
+
3
+ module Threatinator
4
+ # Mixin for mixing logging facilities into classes.
5
+ module Logging
6
+ def self.included(base)
7
+ base.extend(LoggingClassMethods)
8
+ end
9
+
10
+ def logger
11
+ @logger ||= self.class.logger
12
+ end
13
+ end
14
+
15
+ module LoggingClassMethods
16
+ def logger
17
+ @logger ||= Threatinator::Logger.logger_for(self.name)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,23 @@
1
+ require 'active_model'
2
+ require 'active_model/validations'
3
+ require 'threatinator/model/validations'
4
+ require 'threatinator/exceptions'
5
+
6
+ module Threatinator
7
+ module Model
8
+ class Base
9
+ include ActiveModel::Validations
10
+ include Threatinator::Model::Validations
11
+
12
+ def initialize
13
+ validate!
14
+ end
15
+
16
+ def validate!
17
+ unless valid?
18
+ raise Threatinator::Exceptions::InvalidAttributeError, errors.full_messages.join("\n")
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,64 @@
1
+ require 'threatinator/exceptions'
2
+ require 'set'
3
+
4
+ module Threatinator
5
+ module Model
6
+ class Collection
7
+ def initialize(values = [])
8
+ @collection = Set.new
9
+ values.each do |v|
10
+ self << v
11
+ end
12
+ end
13
+
14
+ def valid_member?(v)
15
+ #:nocov:
16
+ raise NotImplementedError, "#valid_member? not implemented"
17
+ #:nocov:
18
+ end
19
+
20
+ def <<(v)
21
+ unless valid_member?(v)
22
+ raise Threatinator::Exceptions::InvalidAttributeError, "Invalid member: #{v.class} '#{v.inspect}'"
23
+ end
24
+ @collection << v
25
+ end
26
+
27
+ def include?(member)
28
+ @collection.include?(member)
29
+ end
30
+
31
+ # @return [Boolean] true if empty, false otherwise
32
+ def empty?
33
+ @collection.empty?
34
+ end
35
+
36
+ # @return [Integer] the number of members in the collection
37
+ def count
38
+ @collection.count
39
+ end
40
+
41
+ def to_ary
42
+ @collection.to_a
43
+ end
44
+ alias_method :to_a, :to_ary
45
+
46
+ def each
47
+ return to_enum(:each) unless block_given?
48
+ @collection.each { |v| yield v }
49
+ end
50
+
51
+ def ==(other)
52
+ if self.equal?(other)
53
+ return true
54
+ elsif other.instance_of?(self.class)
55
+ @collection == other.instance_variable_get(:@collection)
56
+ else
57
+ false
58
+ end
59
+ end
60
+
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,13 @@
1
+ require 'threatinator/model/collection'
2
+
3
+ module Threatinator
4
+ module Model
5
+ module Observables
6
+ class FqdnCollection < Threatinator::Model::Collection
7
+ def valid_member?(v)
8
+ v.is_a?(::String)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,30 @@
1
+ require 'threatinator/model/base'
2
+ require 'ip'
3
+ require 'equalizer'
4
+
5
+ module Threatinator
6
+ module Model
7
+ module Observables
8
+ class Ipv4 < Threatinator::Model::Base
9
+ include Equalizer.new(:ipv4)
10
+ attr_reader :ipv4
11
+
12
+ validates_each :ipv4 do |record, attr, value|
13
+ if value.is_a?(::IP::V4)
14
+ record.errors.add(attr, 'prefix length is not 32 bits') unless value.pfxlen == 32
15
+ else
16
+ record.errors.add(attr, 'not an IP::V4 object')
17
+ end
18
+ end
19
+
20
+ # @param [Hash] opts
21
+ # @option opts [IP::V4] :ipv4 An ipv4 object
22
+ def initialize(opts = {})
23
+ @ipv4 = opts.delete(:ipv4)
24
+ super()
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+
@@ -0,0 +1,14 @@
1
+ require 'threatinator/model/collection'
2
+ require 'threatinator/model/observables/ipv4'
3
+
4
+ module Threatinator
5
+ module Model
6
+ module Observables
7
+ class Ipv4Collection < Threatinator::Model::Collection
8
+ def valid_member?(v)
9
+ v.kind_of?(Threatinator::Model::Observables::Ipv4)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,16 @@
1
+ require 'threatinator/model/collection'
2
+ require 'addressable/uri'
3
+
4
+ module Threatinator
5
+ module Model
6
+ module Observables
7
+ class UrlCollection < Threatinator::Model::Collection
8
+ def valid_member?(v)
9
+ v.is_a?(::Addressable::URI) &&
10
+ v.absolute?
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+
@@ -0,0 +1 @@
1
+ require 'threatinator/model/validations/type'
@@ -0,0 +1,21 @@
1
+ require 'active_model'
2
+ require 'active_model/validations'
3
+
4
+ module Threatinator
5
+ module Model
6
+ module Validations
7
+ class TypeValidator < ActiveModel::EachValidator
8
+ def initialize(options)
9
+ @type = options.delete(:with)
10
+ super
11
+ end
12
+
13
+ def validate_each(record, name, value)
14
+ unless value.is_a?(@type)
15
+ record.errors.add name, "Expected to be #{@type}, got #{value.class} #{value.inspect}"
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -22,23 +22,34 @@ module Threatinator
22
22
  :fqdn_1,
23
23
  :fqdn_2,
24
24
  :fqdn_3,
25
- :fqdn_4
25
+ :fqdn_4,
26
+ :url_1,
27
+ :url_2,
28
+ :url_3,
29
+ :url_4
26
30
  ])
27
31
  end
28
32
 
29
33
  def handle_event(event)
34
+ ipv4s = event.ipv4s.to_a[0..3].map { |o| o.nil? ? nil : o.ipv4.to_addr }
35
+ fqdns = event.fqdns.to_a[0..3]
36
+ urls = event.urls.to_a[0..3].map {|x| x.to_s }
30
37
  @csv.add_row([
31
38
  event.feed_provider,
32
39
  event.feed_name,
33
40
  event.type,
34
- event.ipv4s[0],
35
- event.ipv4s[1],
36
- event.ipv4s[2],
37
- event.ipv4s[3],
38
- event.fqdns[0],
39
- event.fqdns[1],
40
- event.fqdns[2],
41
- event.fqdns[3]
41
+ ipv4s[0],
42
+ ipv4s[1],
43
+ ipv4s[2],
44
+ ipv4s[3],
45
+ fqdns[0],
46
+ fqdns[1],
47
+ fqdns[2],
48
+ fqdns[3],
49
+ urls[0],
50
+ urls[1],
51
+ urls[2],
52
+ urls[3],
42
53
  ])
43
54
  end
44
55
  end