threatinator 0.1.1

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 (219) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +23 -0
  3. data/CONTRIBUTING.md +119 -0
  4. data/Gemfile +28 -0
  5. data/LICENSE +165 -0
  6. data/README.md +45 -0
  7. data/Rakefile +45 -0
  8. data/VERSION +1 -0
  9. data/bin/threatinator +5 -0
  10. data/lib/threatinator.rb +3 -0
  11. data/lib/threatinator/action.rb +14 -0
  12. data/lib/threatinator/actions/list.rb +2 -0
  13. data/lib/threatinator/actions/list/action.rb +53 -0
  14. data/lib/threatinator/actions/list/config.rb +10 -0
  15. data/lib/threatinator/actions/run.rb +2 -0
  16. data/lib/threatinator/actions/run/action.rb +45 -0
  17. data/lib/threatinator/actions/run/config.rb +32 -0
  18. data/lib/threatinator/actions/run/coverage_observer.rb +54 -0
  19. data/lib/threatinator/actions/run/output_config.rb +59 -0
  20. data/lib/threatinator/cli.rb +13 -0
  21. data/lib/threatinator/cli/action_builder.rb +33 -0
  22. data/lib/threatinator/cli/list_action_builder.rb +19 -0
  23. data/lib/threatinator/cli/parser.rb +113 -0
  24. data/lib/threatinator/cli/run_action_builder.rb +41 -0
  25. data/lib/threatinator/config.rb +6 -0
  26. data/lib/threatinator/config/base.rb +35 -0
  27. data/lib/threatinator/config/feed_search.rb +25 -0
  28. data/lib/threatinator/decoder.rb +24 -0
  29. data/lib/threatinator/decoders/gzip.rb +30 -0
  30. data/lib/threatinator/event.rb +27 -0
  31. data/lib/threatinator/event_builder.rb +41 -0
  32. data/lib/threatinator/exceptions.rb +61 -0
  33. data/lib/threatinator/feed.rb +82 -0
  34. data/lib/threatinator/feed_builder.rb +156 -0
  35. data/lib/threatinator/feed_registry.rb +47 -0
  36. data/lib/threatinator/feed_runner.rb +118 -0
  37. data/lib/threatinator/fetcher.rb +22 -0
  38. data/lib/threatinator/fetchers/http.rb +46 -0
  39. data/lib/threatinator/filter.rb +12 -0
  40. data/lib/threatinator/filters/block.rb +18 -0
  41. data/lib/threatinator/filters/comments.rb +16 -0
  42. data/lib/threatinator/filters/whitespace.rb +19 -0
  43. data/lib/threatinator/output.rb +50 -0
  44. data/lib/threatinator/parser.rb +23 -0
  45. data/lib/threatinator/parsers/csv.rb +7 -0
  46. data/lib/threatinator/parsers/csv/parser.rb +77 -0
  47. data/lib/threatinator/parsers/getline.rb +8 -0
  48. data/lib/threatinator/parsers/getline/parser.rb +45 -0
  49. data/lib/threatinator/parsers/json.rb +8 -0
  50. data/lib/threatinator/parsers/json/adapters/oj.rb +65 -0
  51. data/lib/threatinator/parsers/json/parser.rb +45 -0
  52. data/lib/threatinator/parsers/json/record.rb +20 -0
  53. data/lib/threatinator/parsers/xml.rb +8 -0
  54. data/lib/threatinator/parsers/xml/node.rb +79 -0
  55. data/lib/threatinator/parsers/xml/node_builder.rb +39 -0
  56. data/lib/threatinator/parsers/xml/parser.rb +44 -0
  57. data/lib/threatinator/parsers/xml/path.rb +70 -0
  58. data/lib/threatinator/parsers/xml/pattern.rb +53 -0
  59. data/lib/threatinator/parsers/xml/record.rb +14 -0
  60. data/lib/threatinator/parsers/xml/sax_document.rb +64 -0
  61. data/lib/threatinator/plugin_loader.rb +115 -0
  62. data/lib/threatinator/plugins/output/csv.rb +47 -0
  63. data/lib/threatinator/plugins/output/null.rb +17 -0
  64. data/lib/threatinator/plugins/output/rubydebug.rb +16 -0
  65. data/lib/threatinator/property_definer.rb +101 -0
  66. data/lib/threatinator/record.rb +22 -0
  67. data/lib/threatinator/registry.rb +53 -0
  68. data/lib/threatinator/util.rb +15 -0
  69. data/spec/feeds/ET_compromised-ip_reputation_spec.rb +50 -0
  70. data/spec/feeds/alienvault-ip_reputation_spec.rb +50 -0
  71. data/spec/feeds/arbor_fastflux-domain_reputation_spec.rb +50 -0
  72. data/spec/feeds/arbor_ssh-ip_reputation_spec.rb +50 -0
  73. data/spec/feeds/autoshun_shunlist_spec.rb +42 -0
  74. data/spec/feeds/blocklist_de_apache-ip_reputation_spec.rb +50 -0
  75. data/spec/feeds/blocklist_de_bots-ip_reputation_spec.rb +50 -0
  76. data/spec/feeds/blocklist_de_ftp-ip_reputation_spec.rb +50 -0
  77. data/spec/feeds/blocklist_de_imap-ip_reputation_spec.rb +50 -0
  78. data/spec/feeds/blocklist_de_pop3-ip_reputation_spec.rb +50 -0
  79. data/spec/feeds/blocklist_de_proftpd-ip_reputation_spec.rb +50 -0
  80. data/spec/feeds/blocklist_de_sip-ip_reputation_spec.rb +50 -0
  81. data/spec/feeds/blocklist_de_ssh-ip_reputation_spec.rb +50 -0
  82. data/spec/feeds/blocklist_de_strongips-ip_reputation_spec.rb +50 -0
  83. data/spec/feeds/ciarmy-ip_reputation_spec.rb +50 -0
  84. data/spec/feeds/cruzit-ip_reputation_spec.rb +50 -0
  85. data/spec/feeds/dan_me_uk_torlist-ip_reputation_spec.rb +50 -0
  86. data/spec/feeds/data/ET_compromised-ip_reputation.txt +11 -0
  87. data/spec/feeds/data/alienvault-ip_reputation.txt +18 -0
  88. data/spec/feeds/data/arbor_domainlist.txt +11 -0
  89. data/spec/feeds/data/arbor_ssh.txt +16 -0
  90. data/spec/feeds/data/autoshun_shunlist.csv +20 -0
  91. data/spec/feeds/data/blocklist_de_apache-ip-reputation.txt +17 -0
  92. data/spec/feeds/data/blocklist_de_bots-ip-reputation.txt +15 -0
  93. data/spec/feeds/data/blocklist_de_ftp-ip-reputation.txt +7 -0
  94. data/spec/feeds/data/blocklist_de_imap-ip-reputation.txt +8 -0
  95. data/spec/feeds/data/blocklist_de_pop3-ip-reputation.txt +11 -0
  96. data/spec/feeds/data/blocklist_de_proftpd-ip-reputation.txt +12 -0
  97. data/spec/feeds/data/blocklist_de_sip-ip-reputation.txt +9 -0
  98. data/spec/feeds/data/blocklist_de_ssh-ip-reputation.txt +10 -0
  99. data/spec/feeds/data/blocklist_de_strongips-ip-reputation.txt +11 -0
  100. data/spec/feeds/data/ciarmy-ip-reputation.txt +11 -0
  101. data/spec/feeds/data/cruzit-ip-reputation.txt +14 -0
  102. data/spec/feeds/data/dan_me_uk_torlist-ip-reputation.txt +11 -0
  103. data/spec/feeds/data/dshield_topattackers.xml +4 -0
  104. data/spec/feeds/data/feodo_domainlist.txt +18 -0
  105. data/spec/feeds/data/feodo_iplist.txt +20 -0
  106. data/spec/feeds/data/infiltrated_iplist.txt +16 -0
  107. data/spec/feeds/data/malc0de_domainlist.txt +18 -0
  108. data/spec/feeds/data/malc0de_iplist.txt +14 -0
  109. data/spec/feeds/data/mirc_domainlist.txt +31 -0
  110. data/spec/feeds/data/nothink_irc_iplist.txt +14 -0
  111. data/spec/feeds/data/nothink_ssh_iplist.txt +10 -0
  112. data/spec/feeds/data/openbl_iplist.txt +12 -0
  113. data/spec/feeds/data/palevo_domainlist.txt +25 -0
  114. data/spec/feeds/data/palevo_iplist.txt +24 -0
  115. data/spec/feeds/data/phishtank-sample.json.gz +0 -0
  116. data/spec/feeds/data/spyeye_domainlist.txt +16 -0
  117. data/spec/feeds/data/spyeye_iplist.txt +19 -0
  118. data/spec/feeds/data/t-arend-de_ssh_iplist.txt +17 -0
  119. data/spec/feeds/data/the_haleys_ssh_iplist.txt +12 -0
  120. data/spec/feeds/data/yourcmc_ssh-ip_reputation.txt +27 -0
  121. data/spec/feeds/data/zeus-ip_reputation.txt +285 -0
  122. data/spec/feeds/data/zeus_domainlist.txt +27 -0
  123. data/spec/feeds/dshield_attackers-top1000_spec.rb +43 -0
  124. data/spec/feeds/feodo-domain_reputation_spec.rb +50 -0
  125. data/spec/feeds/feodo-ip_reputation_spec.rb +50 -0
  126. data/spec/feeds/infiltrated-ip_reputation_spec.rb +50 -0
  127. data/spec/feeds/malc0de-domain_reputation_spec.rb +50 -0
  128. data/spec/feeds/malc0de-ip_reputation_spec.rb +50 -0
  129. data/spec/feeds/mirc-domain_reputation_spec.rb +50 -0
  130. data/spec/feeds/nothink_irc-ip_reputation_spec.rb +50 -0
  131. data/spec/feeds/nothink_ssh-ip_reputation_spec.rb +50 -0
  132. data/spec/feeds/openbl-ip_reputation_spec.rb +50 -0
  133. data/spec/feeds/palevo-domain_reputation_spec.rb +50 -0
  134. data/spec/feeds/palevo-ip_reputation_spec.rb +50 -0
  135. data/spec/feeds/phishtank_spec.rb +45 -0
  136. data/spec/feeds/spyeye-domain_reputation_spec.rb +50 -0
  137. data/spec/feeds/spyeye-ip_reputation_spec.rb +50 -0
  138. data/spec/feeds/t-arend-de_ssh-ip_reputation_spec.rb +50 -0
  139. data/spec/feeds/the_haleys_ssh-ip_reputation_spec.rb +50 -0
  140. data/spec/feeds/yourcmc_ssh-ip_reputation_spec.rb +50 -0
  141. data/spec/feeds/zeus-domain_reputation_spec.rb +50 -0
  142. data/spec/feeds/zeus-ip_reputation_spec.rb +50 -0
  143. data/spec/fixtures/feed/provider1/feed1.feed +6 -0
  144. data/spec/fixtures/parsers/test.xml +13 -0
  145. data/spec/fixtures/parsers/test_self_closing.xml +20 -0
  146. data/spec/fixtures/plugins/bad/threatinator/plugins/test_error1/plugin.rb +1 -0
  147. data/spec/fixtures/plugins/bad/threatinator/plugins/test_missing1/plugin.rb +0 -0
  148. data/spec/fixtures/plugins/fake.rb +19 -0
  149. data/spec/fixtures/plugins/good/threatinator/plugins/test_type1/plugin_a.rb +8 -0
  150. data/spec/fixtures/plugins/good/threatinator/plugins/test_type1/plugin_b.rb +8 -0
  151. data/spec/fixtures/plugins/good/threatinator/plugins/test_type2/plugin_c.rb +8 -0
  152. data/spec/fixtures/plugins/good/threatinator/plugins/test_type2/plugin_d.rb +8 -0
  153. data/spec/fixtures/plugins/good/threatinator/plugins/test_type3/plugin_e.rb +8 -0
  154. data/spec/fixtures/plugins/good/threatinator/plugins/test_type3/plugin_f.rb +8 -0
  155. data/spec/spec_helper.rb +52 -0
  156. data/spec/support/bad_feeds/missing_fetcher.feed +7 -0
  157. data/spec/support/bad_feeds/missing_name.feed +6 -0
  158. data/spec/support/bad_feeds/missing_parser.feed +3 -0
  159. data/spec/support/bad_feeds/missing_provider.feed +5 -0
  160. data/spec/support/factories/event.rb +27 -0
  161. data/spec/support/factories/feed.rb +32 -0
  162. data/spec/support/factories/feed_builder.rb +65 -0
  163. data/spec/support/factories/feed_registry.rb +8 -0
  164. data/spec/support/factories/output.rb +11 -0
  165. data/spec/support/factories/record.rb +17 -0
  166. data/spec/support/factories/xml_node.rb +33 -0
  167. data/spec/support/helpers/io.rb +11 -0
  168. data/spec/support/helpers/models.rb +13 -0
  169. data/spec/support/shared/action_builder.rb +47 -0
  170. data/spec/support/shared/decoder.rb +70 -0
  171. data/spec/support/shared/feeds.rb +218 -0
  172. data/spec/support/shared/fetcher.rb +48 -0
  173. data/spec/support/shared/filter.rb +14 -0
  174. data/spec/support/shared/io-like.rb +7 -0
  175. data/spec/support/shared/output.rb +120 -0
  176. data/spec/support/shared/parsers.rb +51 -0
  177. data/spec/support/shared/record.rb +111 -0
  178. data/spec/threatinator/actions/list/action_spec.rb +93 -0
  179. data/spec/threatinator/actions/run/action_spec.rb +89 -0
  180. data/spec/threatinator/actions/run/config_spec.rb +39 -0
  181. data/spec/threatinator/actions/run/coverage_observer_spec.rb +116 -0
  182. data/spec/threatinator/actions/run/output_config_spec.rb +89 -0
  183. data/spec/threatinator/cli/list_action_builder_spec.rb +57 -0
  184. data/spec/threatinator/cli/run_action_builder_spec.rb +133 -0
  185. data/spec/threatinator/cli_spec.rb +175 -0
  186. data/spec/threatinator/config/base_spec.rb +39 -0
  187. data/spec/threatinator/config/feed_search_spec.rb +76 -0
  188. data/spec/threatinator/decoders/gzip_spec.rb +75 -0
  189. data/spec/threatinator/event_builder_spec.rb +33 -0
  190. data/spec/threatinator/event_spec.rb +30 -0
  191. data/spec/threatinator/feed_builder_spec.rb +636 -0
  192. data/spec/threatinator/feed_registry_spec.rb +198 -0
  193. data/spec/threatinator/feed_runner_spec.rb +155 -0
  194. data/spec/threatinator/feed_spec.rb +169 -0
  195. data/spec/threatinator/fetcher_spec.rb +12 -0
  196. data/spec/threatinator/fetchers/http_spec.rb +32 -0
  197. data/spec/threatinator/filter_spec.rb +13 -0
  198. data/spec/threatinator/filters/block_spec.rb +16 -0
  199. data/spec/threatinator/filters/comments_spec.rb +13 -0
  200. data/spec/threatinator/filters/whitespace_spec.rb +12 -0
  201. data/spec/threatinator/parser_spec.rb +13 -0
  202. data/spec/threatinator/parsers/csv/parser_spec.rb +202 -0
  203. data/spec/threatinator/parsers/getline/parser_spec.rb +83 -0
  204. data/spec/threatinator/parsers/json/parser_spec.rb +106 -0
  205. data/spec/threatinator/parsers/json/record_spec.rb +30 -0
  206. data/spec/threatinator/parsers/xml/node_spec.rb +335 -0
  207. data/spec/threatinator/parsers/xml/parser_spec.rb +263 -0
  208. data/spec/threatinator/parsers/xml/path_spec.rb +209 -0
  209. data/spec/threatinator/parsers/xml/pattern_spec.rb +72 -0
  210. data/spec/threatinator/parsers/xml/record_spec.rb +27 -0
  211. data/spec/threatinator/plugin_loader_spec.rb +238 -0
  212. data/spec/threatinator/plugins/output/csv_spec.rb +46 -0
  213. data/spec/threatinator/plugins/output/null_spec.rb +17 -0
  214. data/spec/threatinator/plugins/output/rubydebug_spec.rb +37 -0
  215. data/spec/threatinator/property_definer_spec.rb +155 -0
  216. data/spec/threatinator/record_spec.rb +19 -0
  217. data/spec/threatinator/registry_spec.rb +97 -0
  218. data/spec/threatinator/runner_spec.rb +273 -0
  219. metadata +376 -0
@@ -0,0 +1,3 @@
1
+ module Threatinator
2
+ require 'threatinator/runner'
3
+ end
@@ -0,0 +1,14 @@
1
+ module Threatinator
2
+ class Action
3
+ attr_reader :registry
4
+ def initialize(registry)
5
+ @registry = registry
6
+ end
7
+
8
+ def exec
9
+ #:nocov:
10
+ raise NotImplementedError.new
11
+ #:nocov:
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,2 @@
1
+ require 'threatinator/actions/list/action'
2
+ require 'threatinator/actions/list/config'
@@ -0,0 +1,53 @@
1
+ require 'threatinator/action'
2
+ require 'threatinator/exceptions'
3
+ require 'csv'
4
+
5
+ module Threatinator
6
+ module Actions
7
+ module List
8
+ class Action < Threatinator::Action
9
+ def initialize(registry, config)
10
+ super(registry)
11
+ @config = config
12
+ end
13
+
14
+ def exec
15
+ io_out = $stdout
16
+ feed_info = [['provider', 'name', 'type', 'link/path']]
17
+ registry.each do |feed|
18
+ info = [ feed.provider, feed.name ]
19
+ fetcher = feed.fetcher_builder.call()
20
+ type = "unknown"
21
+ link = "unknown"
22
+ case fetcher
23
+ when Threatinator::Fetchers::Http
24
+ type = "http"
25
+ link = fetcher.url
26
+ end
27
+ info << type
28
+ info << link
29
+ feed_info << info
30
+ end
31
+ return if feed_info.count == 0
32
+ fmts = []
33
+ widths = []
34
+ 0.upto(3) do |i|
35
+ max = feed_info.max { |a,b| a[i].length <=> b[i].length }[i].length
36
+ widths << max
37
+ fmts << "%#{max}s"
38
+ end
39
+ fmt = "%-#{widths[0]}s %-#{widths[1]}s %-#{widths[2]}s %-#{widths[3]}s\n"
40
+ io_out.printf(fmt, *(feed_info.shift))
41
+ sep = widths.map {|x| '-' * x }
42
+ io_out.printf(fmt, *sep)
43
+ feed_info.sort! { |a,b| [a[0], a[1]] <=> [b[0], b[1]] }
44
+ feed_info.each do |info|
45
+ io_out.printf(fmt, *info)
46
+ end
47
+ io_out.printf(fmt, *sep)
48
+ io_out.puts("Total: #{feed_info.count}")
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,10 @@
1
+ require 'threatinator/config/base'
2
+
3
+ module Threatinator
4
+ module Actions
5
+ module List
6
+ class Config < Threatinator::Config::Base
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,2 @@
1
+ require 'threatinator/actions/run/action'
2
+ require 'threatinator/actions/run/config'
@@ -0,0 +1,45 @@
1
+ require 'threatinator/action'
2
+ require 'threatinator/exceptions'
3
+ require 'threatinator/feed_runner'
4
+ require 'csv'
5
+
6
+ module Threatinator
7
+ module Actions
8
+ module Run
9
+ class Action < Threatinator::Action
10
+ def initialize(registry, config)
11
+ super(registry)
12
+ @config = config
13
+ end
14
+
15
+ def build_output
16
+ @config.output.build_output
17
+ end
18
+
19
+ def exec
20
+ opts = {}
21
+
22
+ feed = registry.get(@config.feed_provider, @config.feed_name)
23
+ if feed.nil?
24
+ raise Threatinator::Exceptions::UnknownFeed.new(@config.feed_provider, @config.feed_name)
25
+ end
26
+
27
+ output = build_output
28
+
29
+ feed_runner = Threatinator::FeedRunner.new(feed, output)
30
+
31
+ @config.observers.each do |observer|
32
+ feed_runner.add_observer(observer)
33
+ end
34
+
35
+ feed_runner.run
36
+
37
+ # if feed_report.num_records_missed != 0
38
+ # $stderr.puts "WARNING: #{feed_report.num_records_missed} lines/records were MISSED (neither filtered nor parsed). You may need to update your feed specification! Rerun with --coverage to see which records are parsed/filtered/missed"
39
+ # end
40
+
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,32 @@
1
+ require 'threatinator/config/base'
2
+ require 'threatinator/actions/run/output_config'
3
+
4
+ module Threatinator
5
+ module Actions
6
+ module Run
7
+ module Config
8
+ # @param [Threatinator::PluginLoader] plugin_loader
9
+ # @return [Class] a class that represents the config for Action::Run
10
+ def self.generate(plugin_loader)
11
+ output_config_class = Threatinator::Actions::Run::OutputConfig.generate(plugin_loader)
12
+ config_class = Class.new(Threatinator::Config::Base) do
13
+ attribute :output, output_config_class,
14
+ default: lambda { |c,a| output_config_class.new }
15
+
16
+ attribute :feed_provider, String,
17
+ description: "The feed provider"
18
+
19
+ attribute :feed_name, String,
20
+ description: "The feed name"
21
+
22
+ attribute :fetch_from_file, String,
23
+ description: "Read data from the specified file rather than fetching"
24
+
25
+ attribute :observers, Array, default: lambda {|c,a| Array.new }
26
+ end
27
+ config_class
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,54 @@
1
+ require 'csv'
2
+
3
+ module Threatinator
4
+ module Actions
5
+ module Run
6
+ class CoverageObserver
7
+ attr_reader :filename
8
+ def initialize(filename)
9
+ @filename = filename
10
+ @csv = nil
11
+ end
12
+
13
+ def closed?
14
+ return false if @csv.nil?
15
+ @csv.closed?
16
+ end
17
+
18
+ def open
19
+ @csv = ::CSV.open(@filename, "wb", :headers => [:status, :event_count, :line_number, :pos_start, :pos_end, :data], :write_headers => true)
20
+ end
21
+
22
+ # Handles FeedRunner observations
23
+ def update(message, *args)
24
+ case message
25
+ when :record_missed
26
+ log_record(:missed, args.shift, [])
27
+ when :record_filtered
28
+ log_record(:filtered, args.shift, [])
29
+ when :record_parsed
30
+ log_record(:parsed, args.shift, args.shift)
31
+ when :end
32
+ close
33
+ when :start
34
+ open
35
+ end
36
+ end
37
+
38
+ # @param [Symbol] status :parsed, :missed, :filtered
39
+ # @param [Threatinator::Record] record
40
+ # @param [Array<Threatinator::Event>] events
41
+ def log_record(status, record, events = [])
42
+ return if closed?
43
+ @csv.add_row( [
44
+ status, events.count, record.line_number,
45
+ record.pos_start, record.pos_end, record.data.inspect])
46
+ end
47
+
48
+ def close
49
+ @csv.close unless closed?
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,59 @@
1
+ require 'threatinator/config/base'
2
+ require 'threatinator/exceptions'
3
+
4
+ module Threatinator
5
+ module Actions
6
+ module Run
7
+ module OutputConfigClassMethods
8
+ def set_plugin_loader(pl)
9
+ @plugin_loader = pl
10
+ pl.each(:output) do |type, name, plugin|
11
+ self.attribute name, plugin::Config, default: lambda { |c,a| plugin::Config.new }
12
+ end
13
+ end
14
+
15
+ def get_plugin(name)
16
+ @plugin_loader.get(:output, name)
17
+ end
18
+
19
+ def formats
20
+ @plugin_loader.each(:output).map { |t, k, p| k.to_s }
21
+ end
22
+
23
+ def formats_str
24
+ formats.sort.join(', ')
25
+ end
26
+
27
+ end
28
+
29
+ module OutputConfigMethods
30
+ def build_output
31
+ oc = self.class.get_plugin(self.format)
32
+ if oc.nil?
33
+ raise Threatinator::Exceptions::UnknownPlugin.new("Unknown output plugin: '#{format}'")
34
+ end
35
+ output_config = self[format]
36
+
37
+ if output_config.nil?
38
+ raise Threatinator::Exceptions::CouldNotFindOutputConfigError.new("Could not find output config for '#{format}'. Perhaps there's some load-order issues?")
39
+ end
40
+
41
+ oc.new(output_config)
42
+ end
43
+ end
44
+
45
+ module OutputConfig
46
+ def self.generate(plugin_loader)
47
+ anonymous_class = Class.new(Threatinator::Config::Base) do
48
+ extend OutputConfigClassMethods
49
+ include OutputConfigMethods
50
+ set_plugin_loader(plugin_loader)
51
+ attribute :format, Symbol, default: lambda { |c,a| :csv },
52
+ description: lambda { |c, a| "Output format (#{c.formats_str})" }
53
+ end
54
+ anonymous_class
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,13 @@
1
+ require 'threatinator/cli/parser'
2
+ module Threatinator
3
+ module CLI
4
+ def self.process!(args)
5
+ parser = Parser.new
6
+ ret = parser.parse(args)
7
+ if builder = parser.builder
8
+ builder.build.exec
9
+ end
10
+ ret
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,33 @@
1
+ require 'threatinator/config'
2
+ require 'threatinator/feed_registry'
3
+
4
+ module Threatinator
5
+ module CLI
6
+ class ActionBuilder
7
+ attr_reader :extra_args, :config_hash
8
+
9
+ def initialize(config_hash, extra_args)
10
+ @extra_args = extra_args
11
+ @config_hash = config_hash
12
+ @feed_registry = nil
13
+ end
14
+
15
+ def build
16
+ #:nocov:
17
+ raise NotImplementedError.new("#{self.class}#build not implemented")
18
+ #:nocov:
19
+ end
20
+
21
+ def feed_registry
22
+ return @feed_registry unless @feed_registry.nil?
23
+
24
+ feed_search_hash = config_hash["feed_search"] || {}
25
+ feed_search_config = Threatinator::Config::FeedSearch.new(feed_search_hash)
26
+
27
+ @feed_registry = Threatinator::FeedRegistry.build(feed_search_config)
28
+ end
29
+ end
30
+
31
+ end
32
+ end
33
+
@@ -0,0 +1,19 @@
1
+ require 'threatinator/cli/action_builder'
2
+ require 'threatinator/actions/list'
3
+
4
+ module Threatinator
5
+ module CLI
6
+ class ListActionBuilder < ActionBuilder
7
+ def build
8
+ Threatinator::Actions::List::Action.new(feed_registry, config)
9
+ end
10
+
11
+ def config
12
+ list_hash = config_hash["list"] || {}
13
+ Threatinator::Actions::List::Config.new(list_hash)
14
+ end
15
+
16
+ end
17
+ end
18
+ end
19
+
@@ -0,0 +1,113 @@
1
+ require 'gli'
2
+ require 'threatinator/actions/run'
3
+ require 'threatinator/actions/list'
4
+ require 'threatinator/config/feed_search'
5
+ require 'threatinator/cli/list_action_builder'
6
+ require 'threatinator/cli/run_action_builder'
7
+ require 'threatinator/plugin_loader'
8
+
9
+ module Threatinator
10
+ module CLI
11
+ module Helpers
12
+ def add_cli_args(gli, config_properties)
13
+ config_properties.each do |key, args|
14
+ desc, type, default_value = args
15
+ if type.base == Axiom::Types::Boolean
16
+ gli.switch key, desc: desc
17
+ elsif type.base ==Axiom::Types::Array
18
+ gli.flag key, desc: desc, type: Array
19
+ else
20
+ gli.flag key, desc: desc
21
+ end
22
+ end
23
+ end
24
+
25
+ def clean_opts(opts)
26
+ opts.delete_if {|k, v| k.kind_of?(::Symbol) or (k == 'help') or v.nil? }
27
+ opts
28
+ end
29
+
30
+ def nest_opts(opts)
31
+ config_hash = {}
32
+ opts.each_pair do |key, val|
33
+ key_path = key.to_s.split('.')
34
+ final_key = key_path.pop
35
+ nested_hash = config_hash
36
+ while key_path.length > 0
37
+ part = key_path.shift
38
+ nested_hash[part] ||= {}
39
+ nested_hash = nested_hash[part]
40
+ end
41
+ nested_hash[final_key] = val
42
+ end
43
+ config_hash
44
+ end
45
+
46
+ def fix_opts(opts)
47
+ nest_opts(clean_opts(opts))
48
+ end
49
+ end
50
+
51
+ class Parser
52
+ attr_accessor :builder
53
+ attr_reader :run_action_config_class
54
+
55
+ def initialize
56
+ @builder = nil
57
+ @plugin_loader = Threatinator::PluginLoader.new
58
+ @plugin_loader.load_all_plugins
59
+ @run_action_config_class = Threatinator::Actions::Run::Config.generate(@plugin_loader)
60
+ @mod = _init_mod
61
+ end
62
+
63
+ def parse(args)
64
+ @mod.run(args)
65
+ end
66
+
67
+ def _init_mod
68
+ parser = self
69
+ Module.new do
70
+ extend Helpers
71
+ extend GLI::App
72
+
73
+ program_desc 'Threatinator!'
74
+
75
+ add_cli_args(self, Threatinator::Config::FeedSearch.properties('feed_search'))
76
+
77
+ desc "fetch and parse a feed"
78
+ command :run do |c|
79
+ c.flag 'run.coverage_output', type: String, desc: "Write coverage analysis to the specified file (CSV format)"
80
+
81
+ add_cli_args(c, parser.run_action_config_class.properties('run'))
82
+ c.action do |global_options, options, args|
83
+ if options["run.feed_provider"].nil?
84
+ options["run.feed_provider"] = args.shift
85
+ end
86
+
87
+ if options["run.feed_name"].nil?
88
+ options["run.feed_name"] = args.shift
89
+ end
90
+
91
+ opts = fix_opts(global_options.merge(options))
92
+
93
+ builder = Threatinator::CLI::RunActionBuilder.new(opts, args, parser.run_action_config_class)
94
+ parser.builder = builder
95
+ end
96
+ end
97
+
98
+ desc 'list out all the feeds'
99
+ command :list do |c|
100
+ add_cli_args(c, Threatinator::Actions::List::Config.properties('list'))
101
+ c.action do |global_options, options, args|
102
+ opts = fix_opts(global_options.merge(options))
103
+ parser.builder = Threatinator::CLI::ListActionBuilder.new(opts, args)
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ end # _init_mod
110
+ end
111
+
112
+ end
113
+