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,209 @@
1
+ require 'spec_helper'
2
+ require 'threatinator/parsers/xml/path'
3
+
4
+ describe Threatinator::Parsers::XML::Path do
5
+
6
+ describe 'initializing' do
7
+ context "with str_or_parts not set" do
8
+ let (:path) { described_class.new() }
9
+ describe "#parts" do
10
+ specify "returns an empty array" do
11
+ expect(path.parts).to eq([])
12
+ end
13
+ end
14
+ end
15
+
16
+ context "with str_or_parts set to a String" do
17
+ let (:path) { described_class.new('/a/b/c') }
18
+ describe "#parts" do
19
+ specify "returns the string split by '/'" do
20
+ expect(path.parts).to eq(['a', 'b', 'c'])
21
+ end
22
+ end
23
+ it "raises ArgumentError when the string doesn't begin with '/'" do
24
+ expect {
25
+ described_class.new("a/b/c")
26
+ }.to raise_error(ArgumentError)
27
+ end
28
+ it "raises ArgumentError when the string is empty" do
29
+ expect {
30
+ described_class.new("")
31
+ }.to raise_error(ArgumentError)
32
+ end
33
+ end
34
+
35
+ context "with str_or_parts set to nil" do
36
+ let (:path) { described_class.new(nil) }
37
+ describe "#parts" do
38
+ specify "returns an empty array" do
39
+ expect(path.parts).to eq([])
40
+ end
41
+ end
42
+ end
43
+
44
+ context "with str_or_parts set to an array" do
45
+ let(:array) { ['a', 'b', 'c'] }
46
+ let(:path) { described_class.new(array) }
47
+ describe "#parts" do
48
+ specify "returns an array that =='s the original array" do
49
+ expect(path.parts).to eq(array)
50
+ end
51
+ specify "returns a different array instance than the original array" do
52
+ expect(path.parts).not_to equal(array)
53
+ end
54
+ end
55
+ end
56
+
57
+ it "raises TypeError when path_or_str is set to something other than a String, Array, or nil" do
58
+ expect {
59
+ described_class.new(1234)
60
+ }.to raise_error(TypeError)
61
+ end
62
+ end
63
+
64
+ describe "#==" do
65
+ it "returns true when testing against itself" do
66
+ path = described_class.new('/a/b/c')
67
+ expect(path).to be == path
68
+ end
69
+ it "returns true when testing another path with the same parts" do
70
+ path = described_class.new('/a/b/c')
71
+ path2 = described_class.new(['a', 'b', 'c'])
72
+ expect(path).to be == path2
73
+ end
74
+ it "returns true when testing an object that responds to #parts with the same values" do
75
+ path = described_class.new('/a/b/c')
76
+ object = Class.new do
77
+ def parts
78
+ ['a', 'b', 'c']
79
+ end
80
+ end.new
81
+ expect(path).to be == object
82
+ end
83
+ it "returns false when testing an object that responds to #parts with different values" do
84
+ path = described_class.new('/a/b/c')
85
+ object = Class.new do
86
+ def parts
87
+ ['a', 'b', 'c', 'd']
88
+ end
89
+ end.new
90
+ expect(path).not_to be == object
91
+ end
92
+ it "returns false when testing against another path with different parts" do
93
+ path = described_class.new('/a/b/c')
94
+ path2 = described_class.new('/a/b/c/e')
95
+ expect(path).not_to be == path2
96
+ end
97
+ end
98
+
99
+ describe "#eql?" do
100
+ it "returns true when testing against itself" do
101
+ path = described_class.new('/a/b/c')
102
+ expect(path).to eql path
103
+ end
104
+ it "returns true when testing another path with the same parts" do
105
+ path = described_class.new('/a/b/c')
106
+ path2 = described_class.new(['a', 'b', 'c'])
107
+ expect(path).to eql path2
108
+ end
109
+ it "returns false when testing against another path with different parts" do
110
+ path = described_class.new('/a/b/c')
111
+ path2 = described_class.new('/a/b/c/e')
112
+ expect(path).not_to eql path2
113
+ end
114
+ it "returns false when testing an object that responds to #parts with the same values" do
115
+ path = described_class.new('/a/b/c')
116
+ object = Class.new do
117
+ def parts
118
+ ['a', 'b', 'c']
119
+ end
120
+ end.new
121
+ expect(path).not_to eql object
122
+ end
123
+ it "returns false when testing against something that is not a Path" do
124
+ path = described_class.new('/a/b/c')
125
+ path2 = "hi there"
126
+ expect(path).not_to eql path2
127
+ end
128
+ end
129
+
130
+ describe "#end_with?" do
131
+ let(:path) { described_class.new('/a/b/c') }
132
+
133
+ it "returns true when testing against itself" do
134
+ expect(path.end_with?(path)).to eq(true)
135
+ end
136
+ it "returns true when testing against an identical path" do
137
+ path2 = described_class.new('/a/b/c')
138
+ expect(path.end_with?(path2)).to eq(true)
139
+ end
140
+
141
+ it "returns true when tested against any path that it happens to end with" do
142
+ expect(path.end_with?(described_class.new('/b/c'))).to eq(true)
143
+ expect(path.end_with?(described_class.new('/c'))).to eq(true)
144
+ end
145
+
146
+ it "returns true when tested against a path with no length" do
147
+ expect(path.end_with?(described_class.new('/'))).to eq(true)
148
+ end
149
+
150
+ it "returns false when tested against a path that is longer than itself" do
151
+ expect(path.end_with?(described_class.new('/a/a/b/c'))).to eq(false)
152
+ end
153
+
154
+ it "returns false when tested against a path it does not end with" do
155
+ expect(path.end_with?(described_class.new('/a'))).to eq(false)
156
+ expect(path.end_with?(described_class.new('/a/b'))).to eq(false)
157
+ expect(path.end_with?(described_class.new('/b'))).to eq(false)
158
+ end
159
+ end
160
+
161
+ describe "#push" do
162
+ let(:path) { described_class.new }
163
+ it "adds a new entry to the end of the #parts" do
164
+ path.push('a')
165
+ expect(path.parts).to eq(['a'])
166
+ path.push('b')
167
+ expect(path.parts).to eq(['a', 'b'])
168
+ path.push('c')
169
+ expect(path.parts).to eq(['a', 'b', 'c'])
170
+ end
171
+ end
172
+
173
+ describe "#pop" do
174
+ let(:path) { described_class.new('/a/b/c') }
175
+ it "appends a new entry to the end of the #parts" do
176
+ expect(path.parts).to eq(['a', 'b', 'c'])
177
+ path.pop()
178
+ expect(path.parts).to eq(['a', 'b'])
179
+ path.pop()
180
+ expect(path.parts).to eq(['a'])
181
+ path.pop()
182
+ expect(path.parts).to eq([])
183
+ end
184
+
185
+ it "returns the value popped off the end of the parts" do
186
+ expect(path.pop()).to eq('c')
187
+ expect(path.pop()).to eq('b')
188
+ expect(path.pop()).to eq('a')
189
+ end
190
+
191
+ it "returns nil if there are no parts" do
192
+ path.pop(); path.pop(); path.pop();
193
+ expect(path.pop()).to be_nil
194
+ end
195
+ end
196
+
197
+ describe "#length" do
198
+ let(:path) { described_class.new('/a/b/c') }
199
+ it "returns the number of parts" do
200
+ expect(path.length).to eq(3)
201
+ 10.times { path.push('Z') }
202
+ expect(path.length).to eq(13)
203
+ 20.times { path.pop() }
204
+ expect(path.length).to eq(0)
205
+ end
206
+ end
207
+ end
208
+
209
+
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+ require 'threatinator/parsers/xml/pattern'
3
+
4
+ describe Threatinator::Parsers::XML::Pattern do
5
+
6
+ context "when initialized with" do
7
+ let(:pattern) { described_class.new(pathspec) }
8
+
9
+ context "pathspec: '/a/b/c'" do
10
+ let(:pathspec) { '/a/b/c' }
11
+ describe "#max_depth" do
12
+ it "returns 3" do
13
+ expect(pattern.max_depth).to eq(3)
14
+ end
15
+ end
16
+ describe "#match?" do
17
+ it "returns true for the path '/a/b/c'" do
18
+ path = Threatinator::Parsers::XML::Path.new('/a/b/c')
19
+ expect(pattern.match?(path)).to eq(true)
20
+ end
21
+ it "returns false for the path '/a/b/c/d'" do
22
+ path = Threatinator::Parsers::XML::Path.new('/a/b/c/d')
23
+ expect(pattern.match?(path)).to eq(false)
24
+ end
25
+ it "returns false for the path '/a/b/cd'" do
26
+ path = Threatinator::Parsers::XML::Path.new('/a/b/cd')
27
+ expect(pattern.match?(path)).to eq(false)
28
+ end
29
+ it "returns false for the path '/a/b'" do
30
+ path = Threatinator::Parsers::XML::Path.new('/a/b')
31
+ expect(pattern.match?(path)).to eq(false)
32
+ end
33
+ end
34
+ end
35
+
36
+ context "pathspec: '//b/c'" do
37
+ let(:pathspec) { '//b/c' }
38
+ describe "#max_depth" do
39
+ it "returns Infinity" do
40
+ expect(pattern.max_depth).to eq(Float::INFINITY)
41
+ end
42
+ end
43
+ describe "#match?" do
44
+ it "returns true for the path '/a/b/c'" do
45
+ path = Threatinator::Parsers::XML::Path.new('/a/b/c')
46
+ expect(pattern.match?(path)).to eq(true)
47
+ end
48
+ it "returns true for the path '/b/c'" do
49
+ path = Threatinator::Parsers::XML::Path.new('/b/c')
50
+ expect(pattern.match?(path)).to eq(true)
51
+ end
52
+ it "returns true for the path '/a/a/a/a/a/a/a/a/b/c'" do
53
+ path = Threatinator::Parsers::XML::Path.new('/a/a/a/a/a/a/a/a/b/c')
54
+ expect(pattern.match?(path)).to eq(true)
55
+ end
56
+ it "returns false for the path '/a/b'" do
57
+ path = Threatinator::Parsers::XML::Path.new('/a/b')
58
+ expect(pattern.match?(path)).to eq(false)
59
+ end
60
+ end
61
+ end
62
+
63
+ context "pathspec: '///c'" do
64
+ let(:pathspec) { '///c' }
65
+ it "should raise an ArgumentError" do
66
+ expect {
67
+ pattern
68
+ }.to raise_error(ArgumentError)
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+ require 'threatinator/parsers/xml/record'
3
+
4
+ describe Threatinator::Parsers::XML::Record do
5
+ it_should_behave_like 'a record' do
6
+ let(:data) {
7
+ Threatinator::Parsers::XML::Node.new('foo', text: 'bar')
8
+ }
9
+ let(:opts) { { } }
10
+ end
11
+
12
+ context "two instances with different data" do
13
+ it_should_behave_like 'a record when compared to a differently configured record' do
14
+ let(:data) {
15
+ Threatinator::Parsers::XML::Node.new('foo', text: 'bar')
16
+ }
17
+ let(:opts) { { } }
18
+
19
+ let(:data2) {
20
+ Threatinator::Parsers::XML::Node.new('foo', text: 'bar2')
21
+ }
22
+ let(:opts2) { {} }
23
+ end
24
+ end
25
+ end
26
+
27
+
@@ -0,0 +1,238 @@
1
+ require 'spec_helper'
2
+ require 'threatinator/plugin_loader'
3
+
4
+ shared_context "good test plugins" do
5
+ before :all do
6
+ @good_plugin_path = PLUGIN_FIXTURES.join("good").to_s
7
+ $:.unshift @good_plugin_path
8
+ end
9
+
10
+ after :all do
11
+ $:.delete_if {|x| x == @good_plugin_path}
12
+ end
13
+ end
14
+
15
+ shared_context "bad test plugins" do
16
+ before :all do
17
+ @bad_plugin_path = PLUGIN_FIXTURES.join("bad").to_s
18
+ $:.unshift @bad_plugin_path
19
+ end
20
+
21
+ after :all do
22
+ $:.delete_if {|x| x == @bad_plugin_path}
23
+ end
24
+ end
25
+
26
+ describe Threatinator::PluginLoader do
27
+ include_context "good test plugins"
28
+
29
+ let(:loader) { described_class.new }
30
+
31
+ describe "#load_all_plugins" do
32
+ it "returns itself" do
33
+ expect(loader.load_all_plugins).to be(loader)
34
+ end
35
+
36
+ it "loads all plugins found in the $LOAD_PATH" do
37
+ loader.load_all_plugins
38
+ expect(loader.each(:test_type1).map {|*args| args }).to contain_exactly(
39
+ [:test_type1, :plugin_a, Threatinator::Plugins::TestType1::PluginA],
40
+ [:test_type1, :plugin_b, Threatinator::Plugins::TestType1::PluginB])
41
+ expect(loader.each(:test_type2).map {|*args| args }).to contain_exactly(
42
+ [:test_type2, :plugin_c, Threatinator::Plugins::TestType2::PluginC],
43
+ [:test_type2, :plugin_d, Threatinator::Plugins::TestType2::PluginD])
44
+ expect(loader.each(:test_type3).map {|*args| args }).to contain_exactly(
45
+ [:test_type3, :plugin_e, Threatinator::Plugins::TestType3::PluginE],
46
+ [:test_type3, :plugin_f, Threatinator::Plugins::TestType3::PluginF])
47
+ end
48
+ end
49
+
50
+ describe "#load_plugins" do
51
+ it "returns itself" do
52
+ expect(loader.load_plugins(:test_type1)).to be(loader)
53
+ end
54
+
55
+ it "loads all plugins found in the $LOAD_PATH that are of the specified type" do
56
+ expect { |b| loader.each(:test_type1, &b) }.not_to yield_control
57
+ expect { |b| loader.each(:test_type2, &b) }.not_to yield_control
58
+ expect { |b| loader.each(:test_type3, &b) }.not_to yield_control
59
+
60
+ loader.load_plugins(:test_type1)
61
+ expect(loader.each(:test_type1).map {|*args| args }).to contain_exactly(
62
+ [:test_type1, :plugin_a, Threatinator::Plugins::TestType1::PluginA],
63
+ [:test_type1, :plugin_b, Threatinator::Plugins::TestType1::PluginB])
64
+
65
+ expect { |b| loader.each(:test_type2, &b) }.not_to yield_control
66
+ expect { |b| loader.each(:test_type3, &b) }.not_to yield_control
67
+
68
+
69
+ loader.load_plugins(:test_type2)
70
+ expect(loader.each(:test_type1).map {|*args| args }).to contain_exactly(
71
+ [:test_type1, :plugin_a, Threatinator::Plugins::TestType1::PluginA],
72
+ [:test_type1, :plugin_b, Threatinator::Plugins::TestType1::PluginB])
73
+ expect(loader.each(:test_type2).map {|*args| args }).to contain_exactly(
74
+ [:test_type2, :plugin_c, Threatinator::Plugins::TestType2::PluginC],
75
+ [:test_type2, :plugin_d, Threatinator::Plugins::TestType2::PluginD])
76
+ expect { |b| loader.each(:test_type3, &b) }.not_to yield_control
77
+
78
+
79
+ loader.load_plugins(:test_type3)
80
+ expect(loader.each(:test_type1).map {|*args| args }).to contain_exactly(
81
+ [:test_type1, :plugin_a, Threatinator::Plugins::TestType1::PluginA],
82
+ [:test_type1, :plugin_b, Threatinator::Plugins::TestType1::PluginB])
83
+ expect(loader.each(:test_type2).map {|*args| args }).to contain_exactly(
84
+ [:test_type2, :plugin_c, Threatinator::Plugins::TestType2::PluginC],
85
+ [:test_type2, :plugin_d, Threatinator::Plugins::TestType2::PluginD])
86
+ expect(loader.each(:test_type3).map {|*args| args }).to contain_exactly(
87
+ [:test_type3, :plugin_e, Threatinator::Plugins::TestType3::PluginE],
88
+ [:test_type3, :plugin_f, Threatinator::Plugins::TestType3::PluginF])
89
+ end
90
+
91
+ context "when loading a plugin raises an exception" do
92
+ include_context "bad test plugins"
93
+ it "raises that error upward" do
94
+ expect {
95
+ loader.load_plugins(:test_error1)
96
+ }.to raise_error()
97
+ end
98
+ end
99
+
100
+ context "when a plugin class cannot be found" do
101
+ include_context "bad test plugins"
102
+ it "raises a PluginLoadError" do
103
+ expect {
104
+ loader.load_plugins(:test_missing1)
105
+ }.to raise_error(Threatinator::Exceptions::PluginLoadError)
106
+ end
107
+ end
108
+ end
109
+
110
+ describe "#split_file_name" do
111
+ it "returns nil if the file name is not formed like 'threatinator/plugins/<type>/<name>.rb'" do
112
+
113
+ expect(loader.send(:split_file_name, File.join("foobar", "threatinator", "plugins", "type", "bla", "name.rb"))).to be_nil
114
+ expect(loader.send(:split_file_name, File.join("plugins", "type", "name.rb"))).to be_nil
115
+ expect(loader.send(:split_file_name, File.join("type", "name.rb"))).to be_nil
116
+ expect(loader.send(:split_file_name, "name.rb")).to be_nil
117
+
118
+ end
119
+
120
+ it "returns an array of the requirable path, plugin type, and plugin name" do
121
+
122
+ file_name = File.join("foobar", "threatinator", "plugins", "type", "name.rb")
123
+ expect(loader.send(:split_file_name, file_name)).to eq(['threatinator/plugins/type/name', 'type', 'name'])
124
+ end
125
+
126
+ FILE_NAME1 = File.join("threatinator", "plugins", "some_type", "some_name.rb")
127
+ FILE_NAME2 = File.join("foo", "bar", "lib", "threatinator", "plugins", "some_type", "some_name.rb")
128
+
129
+ shared_examples_for "simple split_file_name example" do
130
+ it { should eq([File.join("threatinator", "plugins", "some_type", "some_name"), "some_type", "some_name"]) }
131
+ end
132
+ describe "the return of split_file_name('#{FILE_NAME1}')" do
133
+ subject { loader.send(:split_file_name, FILE_NAME1) }
134
+ include_examples "simple split_file_name example"
135
+ end
136
+
137
+ describe "the return of split_file_name('#{FILE_NAME2}')" do
138
+ subject { loader.send(:split_file_name, FILE_NAME2) }
139
+ include_examples "simple split_file_name example"
140
+ end
141
+ end
142
+
143
+ describe "#types" do
144
+ context "when no plugins have been loaded" do
145
+ it "returns an empty array" do
146
+ expect(loader.types).to eq([])
147
+ end
148
+ end
149
+
150
+ context "when some plugins have been loaded" do
151
+ it "returns an array of type names (in symbol form)" do
152
+ loader.load_plugins(:test_type1)
153
+ loader.load_plugins(:test_type2)
154
+ loader.load_plugins(:test_type3)
155
+ expect(loader.types).to match_array([:test_type1, :test_type2, :test_type3])
156
+ end
157
+ end
158
+ end
159
+
160
+ describe "#get(type, name)" do
161
+ context "when there are no plugins for the specified type" do
162
+ it "returns nil" do
163
+ expect(loader.get(:test_type1, :name)).to be_nil
164
+ end
165
+ end
166
+
167
+ context "when there are plugins for the specified type" do
168
+ context "when a plugin does not exist for the specified name" do
169
+ it "returns nil" do
170
+ loader.load_plugins(:test_type1)
171
+ expect(loader.get(:test_type1, :foobar)).to be_nil
172
+ end
173
+ end
174
+
175
+ context "when a plugin does exist for the specified name" do
176
+ it "returns the plugin" do
177
+ loader.load_plugins(:test_type1)
178
+ expect(loader.get(:test_type1, :plugin_a)).to eq(Threatinator::Plugins::TestType1::PluginA)
179
+ end
180
+ end
181
+ end
182
+ end
183
+
184
+ describe "#each" do
185
+ context "when there are no plugins loaded" do
186
+ it "doesn't yield anything" do
187
+ expect { |b|
188
+ loader.each(&b)
189
+ }.not_to yield_control
190
+ end
191
+ end
192
+
193
+ context "when there plugins loaded" do
194
+ it "yields the type, name, and plugin for all plugins" do
195
+ loader.load_plugins(:test_type1)
196
+ loader.load_plugins(:test_type2)
197
+ loader.load_plugins(:test_type3)
198
+
199
+ data = []
200
+ loader.each do |type, name, plugin|
201
+ data << [type, name, plugin]
202
+ end
203
+ expect(data).to contain_exactly(
204
+ [:test_type1, :plugin_a, Threatinator::Plugins::TestType1::PluginA],
205
+ [:test_type1, :plugin_b, Threatinator::Plugins::TestType1::PluginB],
206
+ [:test_type2, :plugin_c, Threatinator::Plugins::TestType2::PluginC],
207
+ [:test_type2, :plugin_d, Threatinator::Plugins::TestType2::PluginD],
208
+ [:test_type3, :plugin_e, Threatinator::Plugins::TestType3::PluginE],
209
+ [:test_type3, :plugin_f, Threatinator::Plugins::TestType3::PluginF]
210
+ )
211
+ end
212
+ end
213
+ end
214
+
215
+ describe "#each(type)" do
216
+ context "when there are no plugins for the specified type" do
217
+ it "doesn't yield anything" do
218
+ expect { |b|
219
+ loader.each(:foobar, &b)
220
+ }.not_to yield_control
221
+ end
222
+ end
223
+
224
+ context "when there are plugins for the specified type" do
225
+ it "yields the type, name, and plugin for each plugin of the specified type" do
226
+ loader.load_plugins(:test_type1)
227
+ data = []
228
+ loader.each(:test_type1) do |type, name, plugin|
229
+ data << [type, name, plugin]
230
+ end
231
+ expect(data).to contain_exactly(
232
+ [ :test_type1, :plugin_a, Threatinator::Plugins::TestType1::PluginA],
233
+ [ :test_type1, :plugin_b, Threatinator::Plugins::TestType1::PluginB]
234
+ )
235
+ end
236
+ end
237
+ end
238
+ end