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,46 @@
1
+ require 'spec_helper'
2
+ require 'threatinator/plugins/output/csv'
3
+ require 'stringio'
4
+
5
+ describe Threatinator::Plugins::Output::Csv do
6
+ let(:config) { Threatinator::Plugins::Output::Csv::Config.new }
7
+ it_should_behave_like "a file-based output plugin", :csv
8
+
9
+ describe "the output" do
10
+ let(:io) { StringIO.new }
11
+ let(:output) { described_class.new(config) }
12
+
13
+ before :each do
14
+ config.io = io
15
+ events.each do |e|
16
+ output.handle_event(e)
17
+ end
18
+ end
19
+
20
+ let(:lines) { io.string.lines.to_a }
21
+ let(:events) {
22
+ ret = []
23
+ 1.upto(10) do |i|
24
+ ret << build(:event, feed_provider: "my_prov#{i}",
25
+ feed_name: "my_name#{i}", type: :scanning,
26
+ ipv4s: ["#{i}.1.1.1","#{i}.1.1.2","#{i}.1.1.3","#{i}.1.1.4","#{i}.1.1.5"],
27
+ fqdns: ["a#{i}.com","b#{i}.com","c#{i}.com","d#{i}.com","e#{i}.com"]
28
+ )
29
+ end
30
+ ret
31
+ }
32
+
33
+
34
+ describe "the header" do
35
+ subject { lines.first }
36
+ it { is_expected.to eq("provider,feed_name,type,ipv4_1,ipv4_2,ipv4_3,ipv4_4,fqdn_1,fqdn_2,fqdn_3,fqdn_4\n") }
37
+ end
38
+
39
+ it "outputs each event, limited to the first four IPs and four fqdns" do
40
+ line2 = lines[1]
41
+ 1.upto(10) do |i|
42
+ expect(lines[i]).to eq("my_prov#{i},my_name#{i},scanning,#{i}.1.1.1,#{i}.1.1.2,#{i}.1.1.3,#{i}.1.1.4,a#{i}.com,b#{i}.com,c#{i}.com,d#{i}.com\n")
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+ require 'threatinator/plugins/output/null'
3
+ require 'stringio'
4
+
5
+ describe Threatinator::Plugins::Output::Null do
6
+ let(:config) { Threatinator::Plugins::Output::Null::Config.new }
7
+ it_should_behave_like "an output plugin", :null
8
+
9
+ describe "#handle_event" do
10
+ let(:output) { described_class.new(config) }
11
+ it "does not call any methods on the event" do
12
+ event = double("event")
13
+ output.handle_event(event)
14
+ end
15
+ end
16
+ end
17
+
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+ require 'threatinator/plugins/output/rubydebug'
3
+ require 'stringio'
4
+
5
+ describe Threatinator::Plugins::Output::Rubydebug do
6
+ let(:config) { Threatinator::Plugins::Output::Rubydebug::Config.new }
7
+
8
+ it_should_behave_like "a file-based output plugin", :rubydebug
9
+
10
+ describe "the output" do
11
+ let(:output) { described_class.new(config) }
12
+ let(:io) { StringIO.new }
13
+ before :each do
14
+ config.io = io
15
+ events.each do |e|
16
+ output.handle_event(e)
17
+ end
18
+ end
19
+
20
+ let(:events) {
21
+ ret = []
22
+ 1.upto(10) do |i|
23
+ ret << build(:event, feed_provider: "my_prov#{i}",
24
+ feed_name: "my_name#{i}", type: :scanning,
25
+ ipv4s: ["#{i}.1.1.1","#{i}.1.1.2","#{i}.1.1.3","#{i}.1.1.4","#{i}.1.1.5"],
26
+ fqdns: ["a#{i}.com","b#{i}.com","c#{i}.com","d#{i}.com","e#{i}.com"]
27
+ )
28
+ end
29
+ ret
30
+ }
31
+
32
+ it "outputs a bunch of stuff" do
33
+ expect(io.string.length).to be > 0
34
+ end
35
+ end
36
+ end
37
+
@@ -0,0 +1,155 @@
1
+ require 'spec_helper'
2
+ require 'threatinator/property_definer'
3
+
4
+ describe Threatinator::PropertyDefiner do
5
+ describe "defining a property" do
6
+ let(:klass) {
7
+ Class.new do
8
+ include Threatinator::PropertyDefiner
9
+ property :my_prop
10
+ end
11
+ }
12
+ it "should add a setter for the property you define" do
13
+ klass = Class.new do
14
+ include Threatinator::PropertyDefiner
15
+ property :some_prop
16
+ end
17
+ expect(klass.new).to respond_to(:some_prop=)
18
+ end
19
+ it "should add a getter for the property you define" do
20
+ klass = Class.new do
21
+ include Threatinator::PropertyDefiner
22
+ property :another_prop
23
+ end
24
+ expect(klass.new).to respond_to(:another_prop)
25
+ end
26
+ end
27
+
28
+ describe "getting and setting properties" do
29
+ let(:klass) {
30
+ Class.new do
31
+ include Threatinator::PropertyDefiner
32
+ property :my_prop
33
+ end
34
+ }
35
+ it "should let me set the value of a property and retrieve it" do
36
+ instance = klass.new
37
+ instance.my_prop = 1234
38
+ expect(instance.my_prop).to eq(1234)
39
+ end
40
+ it "the getter should respond with nil by default" do
41
+ instance = klass.new
42
+ expect(instance.my_prop).to be_nil
43
+ end
44
+ end
45
+ describe ":type (type validation)" do
46
+ context "when :type is set to a class" do
47
+ let(:klass) {
48
+ Class.new do
49
+ include Threatinator::PropertyDefiner
50
+ property :my_prop, type: Integer
51
+ end
52
+ }
53
+ let(:instance) { klass.new }
54
+
55
+ it "should not raise an error if the property is set to a instance of the specified class" do
56
+ expect{instance.my_prop = 1234}.not_to raise_error
57
+ end
58
+
59
+ it "should raise an InvalidAttributeError if the property being set is not of its defined type" do
60
+ expect{instance.my_prop = "asdf"}.to raise_error do |e|
61
+ expect(e).to be_a(Threatinator::Exceptions::InvalidAttributeError)
62
+ expect(e.attribute).to eq(:my_prop)
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ describe ":validate" do
69
+ it "should raise an ArgumentError if :validate is not a proc" do
70
+ expect {
71
+ Class.new do
72
+ include Threatinator::PropertyDefiner
73
+ property :my_prop, validate: 1234
74
+ end
75
+ }.to raise_error(ArgumentError)
76
+
77
+ end
78
+
79
+ it "should call the validate block whenever the associated attribute is set" do
80
+ expect { |b|
81
+ klass = Class.new do
82
+ include Threatinator::PropertyDefiner
83
+ property :my_prop, validate: lambda { |obj, val| b.to_proc.call(obj, val); true }
84
+ end
85
+ instance = klass.new
86
+ instance.my_prop = "value1"
87
+ instance.my_prop = "value2"
88
+ }.to yield_successive_args(
89
+ [kind_of(Threatinator::PropertyDefiner), "value1"],
90
+ [kind_of(Threatinator::PropertyDefiner), "value2"])
91
+ end
92
+
93
+ it "should raise an InvalidAttributeError if the validation block returns false" do
94
+ klass = Class.new do
95
+ include Threatinator::PropertyDefiner
96
+ property :my_prop, validate: lambda { |obj, val| false }
97
+ end
98
+ instance = klass.new
99
+ expect { |b|
100
+ instance.my_prop = "value1"
101
+ }.to raise_error do |e|
102
+ expect(e).to be_a(Threatinator::Exceptions::InvalidAttributeError)
103
+ expect(e.attribute).to eq(:my_prop)
104
+ end
105
+ end
106
+ it "should not raise an error if the validation block returns true" do
107
+ klass = Class.new do
108
+ include Threatinator::PropertyDefiner
109
+ property :my_prop, validate: lambda { |obj, val| true }
110
+ end
111
+ instance = klass.new
112
+ expect { |b|
113
+ instance.my_prop = "value1"
114
+ }.not_to raise_error
115
+ end
116
+ end
117
+
118
+ describe ":default" do
119
+ context "when :default is set to a value" do
120
+ it "should return the object when accessed via a getter" do
121
+ expected_object = "my_val"
122
+ klass = Class.new do
123
+ include Threatinator::PropertyDefiner
124
+ property :my_prop, default: expected_object
125
+ end
126
+ instance = klass.new
127
+ expect(instance.my_prop).to be(expected_object)
128
+ end
129
+ end
130
+ context "when :default is set to a proc" do
131
+ it "should not call the proc if a value is provided" do
132
+ expect { |b|
133
+ klass = Class.new do
134
+ include Threatinator::PropertyDefiner
135
+ property :my_prop, default: b.to_proc
136
+ end
137
+ instance = klass.new
138
+ instance.my_prop = 1234
139
+ }.not_to yield_control
140
+ end
141
+ it "should only call the proc once, and only when it is accessed" do
142
+ expect { |b|
143
+ klass = Class.new do
144
+ include Threatinator::PropertyDefiner
145
+ property :my_prop, default: lambda { b.to_proc.call(); }
146
+ end
147
+ instance = klass.new
148
+ val1 = instance.my_prop
149
+ val2 = instance.my_prop
150
+ val3 = instance.my_prop
151
+ }.to yield_control.exactly(1).times
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+ require 'threatinator/record'
3
+
4
+ describe Threatinator::Record do
5
+ it_should_behave_like 'a record' do
6
+ let(:data) { "asdf" }
7
+ let(:opts) { { } }
8
+ end
9
+ context "two instances with different data" do
10
+ it_should_behave_like 'a record when compared to a differently configured record' do
11
+ let(:data) { "foo" }
12
+ let(:opts) { { } }
13
+
14
+ let(:data2) { "bar" }
15
+ let(:opts2) { {} }
16
+ end
17
+ end
18
+
19
+ end
@@ -0,0 +1,97 @@
1
+ require 'spec_helper'
2
+ require 'threatinator/registry'
3
+
4
+ describe Threatinator::Registry do
5
+ let(:registry) { described_class.new }
6
+ let(:ten_things) { {
7
+ a: Object.new,
8
+ b: Object.new,
9
+ c: Object.new,
10
+ d: Object.new,
11
+ e: Object.new,
12
+ f: Object.new,
13
+ g: Object.new,
14
+ h: Object.new,
15
+ i: Object.new,
16
+ j: Object.new
17
+ } }
18
+
19
+ describe "#clear" do
20
+ it "should remove all existing registrations" do
21
+ expect(registry.count).to eq(0)
22
+ registry.register(:foo, 123)
23
+ registry.register(:bar, 456)
24
+ expect(registry.count).to eq(2)
25
+ registry.clear
26
+ expect(registry.count).to eq(0)
27
+ end
28
+ end
29
+
30
+ describe "#keys" do
31
+ it "returns an array containing all of the keys" do
32
+ expect(registry.keys).to eq([])
33
+ registry.register(:foo, Object.new)
34
+ registry.register(:bar, Object.new)
35
+ registry.register(:woof, Object.new)
36
+ expect(registry.keys).to contain_exactly(:foo, :bar, :woof)
37
+ end
38
+ end
39
+
40
+ describe "#register" do
41
+ it "should register the provided object to the given key" do
42
+ obj = Object.new
43
+ registry.register(:foo, obj)
44
+ expect(registry.get(:foo)).to be(obj)
45
+ end
46
+
47
+ it "should return the object that was registered" do
48
+ obj = Object.new
49
+ expect(registry.register(:foo, obj)).to be(obj)
50
+ end
51
+
52
+ it "should raise a AlreadyRegisteredError if something is already registered with the the given key" do
53
+ obj = Object.new
54
+ registry.register(:foo, obj)
55
+ expect {
56
+ registry.register(:foo, obj)
57
+ }.to raise_error(Threatinator::Exceptions::AlreadyRegisteredError)
58
+ end
59
+ end
60
+
61
+ describe "#each" do
62
+ it "should enumerate through each key and registered object" do
63
+ ten_things.each_pair do |i, thing|
64
+ registry.register(i, thing)
65
+ end
66
+ found_objects = []
67
+ registry.each do |key, obj|
68
+ found_objects << [ key, obj ]
69
+ end
70
+ expect(found_objects).to match_array(ten_things.to_a)
71
+ end
72
+ end
73
+
74
+ describe "#count" do
75
+ it "should return the number of objects contained within the registry" do
76
+ expect(registry.count).to eq(0)
77
+ ten_things.each_pair do |i, thing|
78
+ registry.register(i, thing)
79
+ end
80
+ expect(registry.count).to eq(10)
81
+ end
82
+ end
83
+
84
+ describe "#get" do
85
+ it "should return nil if key isn't registered" do
86
+ expect(registry.get(:foo)).to be_nil
87
+ end
88
+
89
+ it "should return the correct feed for the key" do
90
+ ten_things.each_pair { |i, o| registry.register(i, o) }
91
+ obj = Object.new
92
+ registry.register(:foo, obj)
93
+ expect(registry.get(:foo)).to be(obj)
94
+ end
95
+ end
96
+ end
97
+
@@ -0,0 +1,273 @@
1
+ #require 'spec_helper'
2
+ #require 'threatinator/runner'
3
+ #
4
+ #describe Threatinator::Runner do
5
+ # let(:runner) { Threatinator::Runner.new }
6
+ #
7
+ # def generate_feedfile(filename, provider, name, url = "https://foobar/#{provider}/#{name}.data")
8
+ # File.open(filename, "w") do |fio|
9
+ # fio.write <<EOS
10
+ #provider "#{provider}"
11
+ #name "#{name}"
12
+ #fetch_http('#{url}')
13
+ #
14
+ #parse_eachline(:separator => "\n") do |builder, line|
15
+ #end
16
+ #EOS
17
+ # end
18
+ # end
19
+ #
20
+ # describe "#add_feed_path" do
21
+ # it "add the paths to #feed_paths" do
22
+ # expect(runner.feed_paths).to eq([])
23
+ # runner.add_feed_path("/foo/bar1")
24
+ # expect(runner.feed_paths).to eq(["/foo/bar1"])
25
+ # runner.add_feed_path("/foo/bar2")
26
+ # expect(runner.feed_paths).to eq(["/foo/bar1", "/foo/bar2"])
27
+ # end
28
+ # end
29
+ #
30
+ # describe "#_load_feeds" do
31
+ # before :each do
32
+ # @feed_path1 = Dir.mktmpdir
33
+ # @feed_path2 = Dir.mktmpdir
34
+ # end
35
+ #
36
+ # after :each do
37
+ # FileUtils.remove_entry_secure @feed_path1
38
+ # FileUtils.remove_entry_secure @feed_path2
39
+ # end
40
+ #
41
+ # context "with no feed paths" do
42
+ # it "should not have loaded any feeds" do
43
+ # runner._load_feeds
44
+ # expect(runner.registry.count).to eq(0)
45
+ # end
46
+ # end
47
+ #
48
+ # context "with paths added to the runner" do
49
+ # before :each do
50
+ # runner.add_feed_path(@feed_path1)
51
+ # runner.add_feed_path(@feed_path2)
52
+ # end
53
+ #
54
+ # it "should load feeds from all of the configured paths" do
55
+ # 5.times do |i|
56
+ # generate_feedfile(File.join(@feed_path1, "feed#{i}.feed"), "provider1", "feed#{i}")
57
+ # generate_feedfile(File.join(@feed_path2, "feed#{i}.feed"), "provider2", "feed#{i}")
58
+ # end
59
+ # runner._load_feeds
60
+ # expect(runner.registry.count).to eq(10)
61
+ # end
62
+ #
63
+ # it "should ignore files that don't end with .feed" do
64
+ # generate_feedfile(File.join(@feed_path1, "feed1.fee"), "provider1", "feed1")
65
+ # generate_feedfile(File.join(@feed_path1, "feed1.fed"), "provider1", "feed2")
66
+ # generate_feedfile(File.join(@feed_path1, "feed1.rb"), "provider1", "feed3")
67
+ # generate_feedfile(File.join(@feed_path1, "feed1.feed"), "real_provider", "my_feed")
68
+ # runner._load_feeds
69
+ # expect(runner.registry.count).to eq(1)
70
+ # expect(runner.registry.get("real_provider", "my_feed")).to be_a(Threatinator::Feed)
71
+ # expect(runner.registry.get("provider1", "feed1")).to be_nil
72
+ # expect(runner.registry.get("provider1", "feed2")).to be_nil
73
+ # expect(runner.registry.get("provider1", "feed3")).to be_nil
74
+ # end
75
+ #
76
+ # it "should recurse subdirectories, loading feeds from there" do
77
+ # level1 = File.join(@feed_path1, "level1")
78
+ # level2 = File.join(@feed_path1, "level1", "level2")
79
+ # level3 = File.join(@feed_path1, "level1", "level2", "level3")
80
+ # FileUtils.mkdir_p level1
81
+ # FileUtils.mkdir_p level2
82
+ # FileUtils.mkdir_p level3
83
+ # generate_feedfile(File.join(level1, "feed1.feed"), "provider1", "feed1")
84
+ # generate_feedfile(File.join(level2, "feed2.feed"), "provider1", "feed2")
85
+ # generate_feedfile(File.join(level3, "feed3.feed"), "provider1", "feed3")
86
+ # runner._load_feeds
87
+ # expect(runner.registry.count).to eq(3)
88
+ # end
89
+ #
90
+ # it "should raise an exception if the same feed provider/name combination appears in multiple files" do
91
+ # generate_feedfile(File.join(@feed_path1, "feed1.feed"), "provider1", "feed1")
92
+ # generate_feedfile(File.join(@feed_path1, "feed2.feed"), "provider1", "feed1")
93
+ # expect {
94
+ # runner._load_feeds
95
+ # }.to raise_error(Threatinator::Exceptions::AlreadyRegisteredError)
96
+ # end
97
+ #
98
+ # end
99
+ # end
100
+ #
101
+ # describe "#_register_feed_from_file" do
102
+ # let(:feedfile) {FEED_FIXTURES.join("provider1", "feed1.feed").to_s}
103
+ #
104
+ # it "should return the feed after parsing the file" do
105
+ # ret = runner._register_feed_from_file(feedfile)
106
+ # expect(ret).to be_a(Threatinator::Feed)
107
+ # expect(ret.provider).to eq("provider1")
108
+ # expect(ret.name).to eq("feed1")
109
+ # end
110
+ #
111
+ # it "should have registered the feed" do
112
+ # expect(runner.registry.count).to eq(0)
113
+ # feed = runner._register_feed_from_file(feedfile)
114
+ # expect(runner.registry.count).to eq(1)
115
+ # expect(runner.registry.get(feed.provider, feed.name)).to be(feed)
116
+ # end
117
+ # end
118
+ #
119
+ # describe "#list" do
120
+ # let(:io_out) { StringIO.new }
121
+ #
122
+ # before :each do
123
+ # @feed_path = Dir.mktmpdir
124
+ # runner.add_feed_path(@feed_path)
125
+ # end
126
+ #
127
+ # after :each do
128
+ # FileUtils.remove_entry_secure @feed_path
129
+ # end
130
+ #
131
+ # context "with no feed paths" do
132
+ # it "should output the header" do
133
+ # runner.list(io_out: io_out);
134
+ # lines = io_out.string.lines.to_a
135
+ # expect(lines[0]).to eq("provider name type link/path\n")
136
+ # expect(lines[1]).to eq("-------- ---- ---- ---------\n")
137
+ # end
138
+ #
139
+ # it "should output the footer with a total of 0" do
140
+ # runner.list(io_out: io_out);
141
+ # lines = io_out.string.lines.to_a
142
+ # expect(lines[-2]).to eq("-------- ---- ---- ---------\n")
143
+ # expect(lines[-1]).to eq("Total: 0\n")
144
+ # end
145
+ # end
146
+ #
147
+ # context "with paths added to the runner" do
148
+ # before :each do
149
+ # end
150
+ # describe "the header row, header separator, and footer separator" do
151
+ # it "should vary the width of 'provider' based on the longest provider name" do
152
+ # generate_feedfile(File.join(@feed_path, "feed.feed"), "A" * 10, "x", "http://x")
153
+ # generate_feedfile(File.join(@feed_path, "feed.feed"), "A" * 20, "x", "http://x")
154
+ # generate_feedfile(File.join(@feed_path, "feed.feed"), "A" * 30, "x", "http://x")
155
+ #
156
+ # runner.list(io_out: io_out);
157
+ # lines = io_out.string.lines.to_a
158
+ # expect(lines[0]).to eq("provider name type link/path\n")
159
+ # expect(lines[1]).to eq("------------------------------ ---- ---- ---------\n")
160
+ # expect(lines[-2]).to eq("------------------------------ ---- ---- ---------\n")
161
+ # end
162
+ #
163
+ # it "should vary the width of 'name' based on the longest feed name" do
164
+ # generate_feedfile(File.join(@feed_path, "feed.feed"), 'a', "A" * 10, "http://x")
165
+ # generate_feedfile(File.join(@feed_path, "feed.feed"), 'a', "A" * 20, "http://x")
166
+ # generate_feedfile(File.join(@feed_path, "feed.feed"), 'a', "A" * 30, "http://x")
167
+ #
168
+ # runner.list(io_out: io_out);
169
+ # lines = io_out.string.lines.to_a
170
+ # expect(lines[0]).to eq("provider name type link/path\n")
171
+ # expect(lines[1]).to eq("-------- ------------------------------ ---- ---------\n")
172
+ # expect(lines[-2]).to eq("-------- ------------------------------ ---- ---------\n")
173
+ # end
174
+ #
175
+ # it "should vary the width of 'link/path' based on the longest link name" do
176
+ # generate_feedfile(File.join(@feed_path, "feed.feed"), 'a', 'b', "http://" + ("A" * 10))
177
+ # generate_feedfile(File.join(@feed_path, "feed.feed"), 'a', 'b', "http://" + ("A" * 20))
178
+ # generate_feedfile(File.join(@feed_path, "feed.feed"), 'a', 'b', "http://" + ("A" * 30))
179
+ #
180
+ # runner.list(io_out: io_out);
181
+ # lines = io_out.string.lines.to_a
182
+ # expect(lines[0]).to eq("provider name type link/path \n")
183
+ # expect(lines[1]).to eq("-------- ---- ---- -------------------------------------\n")
184
+ # expect(lines[-2]).to eq("-------- ---- ---- -------------------------------------\n")
185
+ # end
186
+ # end
187
+ #
188
+ # describe "the list of feeds" do
189
+ # it "should be sorted by provider name and then feed name" do
190
+ # generate_feedfile(File.join(@feed_path, "feed1.feed"), 'provider_b', 'feed_c')
191
+ # generate_feedfile(File.join(@feed_path, "feed2.feed"), 'provider_a', 'feed_d')
192
+ # generate_feedfile(File.join(@feed_path, "feed3.feed"), 'provider_a', 'feed_a')
193
+ # generate_feedfile(File.join(@feed_path, "feed4.feed"), 'provider_b', 'feed_d')
194
+ # generate_feedfile(File.join(@feed_path, "feed5.feed"), 'provider_a', 'feed_c')
195
+ # generate_feedfile(File.join(@feed_path, "feed6.feed"), 'provider_b', 'feed_a')
196
+ # generate_feedfile(File.join(@feed_path, "feed7.feed"), 'provider_b', 'feed_b')
197
+ # generate_feedfile(File.join(@feed_path, "feed8.feed"), 'provider_a', 'feed_b')
198
+ #
199
+ # runner.list(io_out: io_out);
200
+ # lines = io_out.string.lines.to_a
201
+ # expect(lines[2]).to eq("provider_a feed_a http https://foobar/provider_a/feed_a.data\n")
202
+ # expect(lines[3]).to eq("provider_a feed_b http https://foobar/provider_a/feed_b.data\n")
203
+ # expect(lines[4]).to eq("provider_a feed_c http https://foobar/provider_a/feed_c.data\n")
204
+ # expect(lines[5]).to eq("provider_a feed_d http https://foobar/provider_a/feed_d.data\n")
205
+ #
206
+ # expect(lines[6]).to eq("provider_b feed_a http https://foobar/provider_b/feed_a.data\n")
207
+ # expect(lines[7]).to eq("provider_b feed_b http https://foobar/provider_b/feed_b.data\n")
208
+ # expect(lines[8]).to eq("provider_b feed_c http https://foobar/provider_b/feed_c.data\n")
209
+ # expect(lines[9]).to eq("provider_b feed_d http https://foobar/provider_b/feed_d.data\n")
210
+ # end
211
+ # end
212
+ #
213
+ # describe "the footer" do
214
+ # it "should indicate the number of feeds" do
215
+ # 20.times do |i|
216
+ # generate_feedfile(File.join(@feed_path, "feed#{i}.feed"), "a#{i}", 'b', "http://" + ("A" * 10))
217
+ # end
218
+ # runner.list(io_out: io_out);
219
+ # lines = io_out.string.lines.to_a
220
+ # expect(lines[-1]).to eq("Total: 20\n")
221
+ # end
222
+ # end
223
+ # end
224
+ # end
225
+ #
226
+ # describe "#run" do
227
+ # before :each do
228
+ # @feed_path = Dir.mktmpdir
229
+ # generate_feedfile(File.join(@feed_path, "feed1.feed"), "provider1", "feed1")
230
+ # runner.add_feed_path(@feed_path)
231
+ # allow(Threatinator::FeedRunner).to receive(:run)
232
+ # end
233
+ #
234
+ # after :each do
235
+ # FileUtils.remove_entry_secure @feed_path
236
+ # end
237
+ #
238
+ # let(:loader) {
239
+ # x = Threatinator::PluginLoader.new
240
+ # x.load_plugins(:output)
241
+ # x }
242
+ #
243
+ #
244
+ # let(:output) { loader[:output][:null] }
245
+ #
246
+ # it "parses all the feeds" do
247
+ # expect(runner).to receive(:_load_feeds).and_call_original
248
+ # runner.run("provider1", "feed1", output)
249
+ # end
250
+ #
251
+ # context "when called with a provider and feed name that does not match a feed" do
252
+ # it "raises Threatinator::Exceptions::UnknownFeed" do
253
+ # expect {
254
+ # runner.run("foobar", "bla", output)
255
+ # }.to raise_error(Threatinator::Exceptions::UnknownFeed)
256
+ # end
257
+ # end
258
+ #
259
+ # context "when called with a provider and feed name that matches a feed" do
260
+ # it "loads the feed by the given provider and name" do
261
+ # expect(runner.registry).to receive(:get).with("provider1", "feed1").and_call_original
262
+ # runner.run("provider1", "feed1", output)
263
+ # end
264
+ #
265
+ # it "runs the feed" do
266
+ # opts_hash = {foo: 123}
267
+ # expect(Threatinator::FeedRunner).to receive(:run).with(kind_of(Threatinator::Feed), output, opts_hash)
268
+ # runner.run("provider1", "feed1", output, opts_hash)
269
+ # end
270
+ # end
271
+ # end
272
+ #
273
+ #end