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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -2
- data/Gemfile +18 -13
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/feeds/ET_block-ip_reputation.feed +26 -0
- data/feeds/ET_openbadlist-ip_reputation.feed +35 -0
- data/feeds/bambenek_c2_masterlist-domain_reputation.feed +15 -0
- data/feeds/bambenek_c2_masterlist-ip_reputation.feed +15 -0
- data/feeds/bambenek_dga_feed-domain_reputation.feed +15 -0
- data/feeds/berkeley-ip_reputation.feed +23 -0
- data/feeds/bitcash_cz_blacklist.feed +20 -0
- data/feeds/botscout-ip_reputation.feed +24 -0
- data/feeds/cert_mxpoison-ip_reputation.feed +21 -0
- data/feeds/chaosreigns-ip_reputation.feed +36 -0
- data/feeds/cydef_torexit-ip_reputation.feed +24 -0
- data/feeds/danger_bruteforce-ip_reputation.feed +23 -0
- data/feeds/falconcrest-ip_reputation.feed +18 -0
- data/feeds/h3x_asprox.feed +17 -0
- data/feeds/hosts-file_hphostspartial-domain_reputation.feed +18 -0
- data/feeds/infiltrated_vabl-ip_reputation.feed +29 -0
- data/feeds/isc_suspicious_high-domain_reputation.feed +25 -0
- data/feeds/isc_suspicious_low-domain_reputation.feed +25 -0
- data/feeds/isc_suspicious_medium-domain_reputation.feed +25 -0
- data/feeds/malwaredomainlist-url_reputation.feed +16 -0
- data/feeds/malwaredomains-domain_reputation.feed +27 -0
- data/feeds/malwaredomains_dyndns-domain_reputation.feed +27 -0
- data/feeds/malwaredomains_justdomains-domain_reputation.feed +18 -0
- data/feeds/multiproxy-ip_reputation.feed +20 -0
- data/feeds/openphish-url_reputation.feed +22 -0
- data/feeds/packetmail_perimeterbad-ip_reputation.feed +26 -0
- data/feeds/phishtank.feed +1 -1
- data/feeds/sigmaproject_atma.feed +25 -0
- data/feeds/sigmaproject_spyware.feed +24 -0
- data/feeds/sigmaproject_webexploit.feed +26 -0
- data/feeds/snort_bpf-ip_reputation.feed +19 -0
- data/feeds/steeman-ip_reputation.feed +19 -0
- data/feeds/trustedsec-ip_reputation.feed +17 -0
- data/feeds/virbl-ip_reputation.feed +24 -0
- data/feeds/vxvault-url_reputation.feed +22 -0
- data/feeds/yoyo_adservers-domain_reputation.feed +16 -0
- data/lib/threatinator/actions/run/action.rb +15 -3
- data/lib/threatinator/actions/run/coverage_observer.rb +12 -7
- data/lib/threatinator/actions/run/status_observer.rb +37 -0
- data/lib/threatinator/cli.rb +9 -3
- data/lib/threatinator/cli/parser.rb +14 -4
- data/lib/threatinator/config.rb +1 -0
- data/lib/threatinator/config/logger.rb +14 -0
- data/lib/threatinator/event.rb +28 -18
- data/lib/threatinator/event_builder.rb +52 -23
- data/lib/threatinator/exceptions.rb +3 -6
- data/lib/threatinator/feed.rb +1 -1
- data/lib/threatinator/feed_runner.rb +63 -7
- data/lib/threatinator/logger.rb +66 -0
- data/lib/threatinator/logging.rb +20 -0
- data/lib/threatinator/model/base.rb +23 -0
- data/lib/threatinator/model/collection.rb +64 -0
- data/lib/threatinator/model/observables/fqdn_collection.rb +13 -0
- data/lib/threatinator/model/observables/ipv4.rb +30 -0
- data/lib/threatinator/model/observables/ipv4_collection.rb +14 -0
- data/lib/threatinator/model/observables/url_collection.rb +16 -0
- data/lib/threatinator/model/validations.rb +1 -0
- data/lib/threatinator/model/validations/type.rb +21 -0
- data/lib/threatinator/plugins/output/csv.rb +20 -9
- data/spec/feeds/ET_block-ip_reputation_spec.rb +50 -0
- data/spec/feeds/ET_compromised-ip_reputation_spec.rb +2 -5
- data/spec/feeds/ET_openbadlist-ip_reputation_spec.rb +56 -0
- data/spec/feeds/alienvault-ip_reputation_spec.rb +2 -5
- data/spec/feeds/arbor_fastflux-domain_reputation_spec.rb +0 -3
- data/spec/feeds/arbor_ssh-ip_reputation_spec.rb +2 -5
- data/spec/feeds/autoshun_shunlist_spec.rb +1 -4
- data/spec/feeds/bambenek_c2_masterlist-domain_reputation_spec.rb +39 -0
- data/spec/feeds/bambenek_c2_masterlist-ip_reputation_spec.rb +39 -0
- data/spec/feeds/bambenek_dga_feed-domain_reputation_spec.rb +39 -0
- data/spec/feeds/berkeley-ip_reputation_spec.rb +47 -0
- data/spec/feeds/bitcash_cz_blacklist-ip_reputation_spec.rb +50 -0
- data/spec/feeds/blocklist_de_apache-ip_reputation_spec.rb +2 -5
- data/spec/feeds/blocklist_de_bots-ip_reputation_spec.rb +2 -5
- data/spec/feeds/blocklist_de_ftp-ip_reputation_spec.rb +2 -5
- data/spec/feeds/blocklist_de_imap-ip_reputation_spec.rb +2 -5
- data/spec/feeds/blocklist_de_pop3-ip_reputation_spec.rb +2 -5
- data/spec/feeds/blocklist_de_proftpd-ip_reputation_spec.rb +2 -5
- data/spec/feeds/blocklist_de_sip-ip_reputation_spec.rb +2 -5
- data/spec/feeds/blocklist_de_ssh-ip_reputation_spec.rb +2 -5
- data/spec/feeds/blocklist_de_strongips-ip_reputation_spec.rb +2 -5
- data/spec/feeds/botscout-ip_reputation_spec.rb +50 -0
- data/spec/feeds/cert_mxpoison-ip_reputation_spec.rb +47 -0
- data/spec/feeds/chaosreigns-ip_reputation_spec.rb +50 -0
- data/spec/feeds/ciarmy-ip_reputation_spec.rb +2 -5
- data/spec/feeds/cruzit-ip_reputation_spec.rb +2 -5
- data/spec/feeds/cydef_torexit-ip_reputation_spec.rb +47 -0
- data/spec/feeds/dan_me_uk_torlist-ip_reputation_spec.rb +2 -5
- data/spec/feeds/danger_bruteforce-ip_reputation_spec.rb +47 -0
- data/spec/feeds/data/ET_block-ip_reputation.txt +80 -0
- data/spec/feeds/data/ET_openbadlist-ip_reputation.txt +62 -0
- data/spec/feeds/data/bambenek_c2-dommasterlist.csv +30 -0
- data/spec/feeds/data/bambenek_c2-ipmasterlist.csv +27 -0
- data/spec/feeds/data/bambenek_dga_feed.csv +42 -0
- data/spec/feeds/data/berkeley.txt +29 -0
- data/spec/feeds/data/bitcash_cz_blacklist.txt +7 -0
- data/spec/feeds/data/botscout-ip-reputation.txt +713 -0
- data/spec/feeds/data/cert_mxpoison-ip_reputation.txt +17 -0
- data/spec/feeds/data/chaosreigns-ip-reputation.txt +26 -0
- data/spec/feeds/data/cydef_torexit-ip_reputation.txt +27 -0
- data/spec/feeds/data/danger_bruteforce-ip_reputation.txt +12 -0
- data/spec/feeds/data/falconcrest_iplist.txt +345 -0
- data/spec/feeds/data/h3x_asprox.txt +20 -0
- data/spec/feeds/data/hosts-file_hphostspartial_domainlist.txt +24 -0
- data/spec/feeds/data/infiltrated_vabl_iplist.txt +33 -0
- data/spec/feeds/data/isc_suspicious_high_domainlist.txt +26 -0
- data/spec/feeds/data/isc_suspicious_low_domainlist.txt +34 -0
- data/spec/feeds/data/isc_suspicious_medium_domainlist.txt +32 -0
- data/spec/feeds/data/malwaredomainlist-url-reputation.txt +8 -0
- data/spec/feeds/data/malwaredomains_domainlist.txt +24 -0
- data/spec/feeds/data/malwaredomains_dyndns_domainlist.txt +34 -0
- data/spec/feeds/data/malwaredomains_justdomains_domainlist.txt +18 -0
- data/spec/feeds/data/multiproxy_iplist.txt +15 -0
- data/spec/feeds/data/openphish-url-reputation.txt +16 -0
- data/spec/feeds/data/packetmail_perimeterbad-ip_reputation.txt +44 -0
- data/spec/feeds/data/sigmaproject_atma.return.gz +0 -0
- data/spec/feeds/data/sigmaproject_spyware.return.gz +0 -0
- data/spec/feeds/data/sigmaproject_webexploit.return.gz +0 -0
- data/spec/feeds/data/snort_bpf-ip_reputation.txt +16 -0
- data/spec/feeds/data/steeman-ip-reputation.txt +13 -0
- data/spec/feeds/data/trustedsec-ip-reputation.txt +12 -0
- data/spec/feeds/data/virbl-ip_reputation.txt +14 -0
- data/spec/feeds/data/vxvault-url-reputation.txt +15 -0
- data/spec/feeds/data/yoyo_adservers.txt +25 -0
- data/spec/feeds/dshield_attackers-top1000_spec.rb +1 -4
- data/spec/feeds/falconcrest-ip_reputation_spec.rb +37 -0
- data/spec/feeds/feodo-domain_reputation_spec.rb +0 -3
- data/spec/feeds/feodo-ip_reputation_spec.rb +2 -5
- data/spec/feeds/h3x_asprox-ip_reputation_spec.rb +50 -0
- data/spec/feeds/hosts-file_hphostspartial-domain_reputation_spec.rb +47 -0
- data/spec/feeds/infiltrated-ip_reputation_spec.rb +2 -5
- data/spec/feeds/infiltrated_vabl-ip_reputation_spec.rb +47 -0
- data/spec/feeds/isc_suspicious_high-domain_reputation_spec.rb +47 -0
- data/spec/feeds/isc_suspicious_low-domain_reputation_spec.rb +47 -0
- data/spec/feeds/isc_suspicious_medium-domain_reputation_spec.rb +47 -0
- data/spec/feeds/malc0de-domain_reputation_spec.rb +0 -3
- data/spec/feeds/malc0de-ip_reputation_spec.rb +2 -5
- data/spec/feeds/malwaredomainlist_url_reputation_spec.rb +50 -0
- data/spec/feeds/malwaredomains-domain_reputation_spec.rb +47 -0
- data/spec/feeds/malwaredomains_dyndns-domain_reputation_spec.rb +47 -0
- data/spec/feeds/malwaredomains_justdomains-domain_reputation_spec.rb +47 -0
- data/spec/feeds/mirc-domain_reputation_spec.rb +0 -3
- data/spec/feeds/multiproxy-ip_reputation_spec.rb +47 -0
- data/spec/feeds/nothink_irc-ip_reputation_spec.rb +2 -5
- data/spec/feeds/nothink_ssh-ip_reputation_spec.rb +2 -5
- data/spec/feeds/openbl-ip_reputation_spec.rb +2 -5
- data/spec/feeds/openphish_url_reputation_spec.rb +50 -0
- data/spec/feeds/packetmail_perimeterbad-ip_reputation_spec.rb +47 -0
- data/spec/feeds/palevo-domain_reputation_spec.rb +0 -3
- data/spec/feeds/palevo-ip_reputation_spec.rb +2 -5
- data/spec/feeds/phishtank_spec.rb +2 -5
- data/spec/feeds/sigmaproject_atma_spec.rb +63 -0
- data/spec/feeds/sigmaproject_spyware_spec.rb +64 -0
- data/spec/feeds/sigmaproject_webexploit_spec.rb +63 -0
- data/spec/feeds/snort_bpf-ip_reputation_spec.rb +47 -0
- data/spec/feeds/spyeye-domain_reputation_spec.rb +0 -3
- data/spec/feeds/spyeye-ip_reputation_spec.rb +2 -5
- data/spec/feeds/steeman-ip_reputation_spec.rb +50 -0
- data/spec/feeds/t-arend-de_ssh-ip_reputation_spec.rb +2 -5
- data/spec/feeds/the_haleys_ssh-ip_reputation_spec.rb +2 -5
- data/spec/feeds/trustedsec-ip_reputation_spec.rb +47 -0
- data/spec/feeds/virbl-ip_reputation_spec.rb +47 -0
- data/spec/feeds/vxvault_url_reputation_spec.rb +50 -0
- data/spec/feeds/yourcmc_ssh-ip_reputation_spec.rb +2 -5
- data/spec/feeds/yoyo_adservers_spec.rb +47 -0
- data/spec/feeds/zeus-domain_reputation_spec.rb +0 -3
- data/spec/feeds/zeus-ip_reputation_spec.rb +2 -5
- data/spec/spec_helper.rb +2 -0
- data/spec/support/factories/event.rb +11 -7
- data/spec/support/factories/feed.rb +28 -1
- data/spec/support/factories/ipv4.rb +36 -0
- data/spec/support/factories/url.rb +34 -0
- data/spec/support/shared/feed_runner_observer.rb +136 -0
- data/spec/support/shared/feeds.rb +19 -4
- data/spec/support/shared/model/collection.rb +164 -0
- data/spec/threatinator/actions/run/action_spec.rb +27 -10
- data/spec/threatinator/actions/run/coverage_observer_spec.rb +39 -4
- data/spec/threatinator/actions/run/status_observer_spec.rb +86 -0
- data/spec/threatinator/event_builder_spec.rb +111 -21
- data/spec/threatinator/event_spec.rb +237 -13
- data/spec/threatinator/event_spec.rb.new +319 -0
- data/spec/threatinator/feed_builder_spec.rb +0 -3
- data/spec/threatinator/feed_runner_spec.rb +254 -70
- data/spec/threatinator/logger_spec.rb +29 -0
- data/spec/threatinator/model/observables/fqdn_collection_spec.rb +42 -0
- data/spec/threatinator/model/observables/ipv4_collection_spec.rb +36 -0
- data/spec/threatinator/model/observables/ipv4_spec.rb +75 -0
- data/spec/threatinator/model/observables/url_collection_spec.rb +45 -0
- data/spec/threatinator/model/validations/type_spec.rb +37 -0
- data/spec/threatinator/plugins/output/csv_spec.rb +4 -3
- metadata +216 -19
- data/lib/threatinator/property_definer.rb +0 -101
- data/spec/threatinator/property_definer_spec.rb +0 -155
data/lib/threatinator/feed.rb
CHANGED
|
@@ -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
|
|
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 =
|
|
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
|
-
|
|
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
|
-
@
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
@
|
|
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,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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|