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,30 @@
1
+ require 'spec_helper'
2
+ require 'threatinator/parsers/json/record'
3
+
4
+ describe Threatinator::Parsers::JSON::Record do
5
+ it_should_behave_like 'a record' do
6
+ let(:data) { {"some" => "data"} }
7
+ let(:opts) { { } }
8
+ end
9
+
10
+ context "two instances with different data" do
11
+ it_should_behave_like 'a record when compared to a differently configured record' do
12
+ let(:data) { {"some" => "data"} }
13
+ let(:opts) { { } }
14
+
15
+ let(:data2) { {"some_other" => "data"} }
16
+ let(:opts2) { {} }
17
+ end
18
+ end
19
+
20
+ context "two instances with the same data but a different :key" do
21
+ it_should_behave_like 'a record when compared to a differently configured record' do
22
+ let(:data) { {"some" => "data"} }
23
+ let(:opts) { { key: "foo" } }
24
+
25
+ let(:data2) { {"some" => "data"} }
26
+ let(:opts2) { { key: "bar" } }
27
+ end
28
+ end
29
+ end
30
+
@@ -0,0 +1,335 @@
1
+ require 'spec_helper'
2
+ require 'threatinator/parsers/xml/node'
3
+ require 'set'
4
+
5
+ describe Threatinator::Parsers::XML::Node do
6
+ describe "initializing" do
7
+ describe "name" do
8
+ context "when a symbol is provided" do
9
+ specify "the name is the symbol" do
10
+ node = described_class.new(:foobar)
11
+ expect(node.name).to be(:foobar)
12
+ end
13
+ end
14
+ context "when a string is provided" do
15
+ specify "the name is the symbolized version of the string" do
16
+ node = described_class.new("foobar")
17
+ expect(node.name).to be(:foobar)
18
+ end
19
+ end
20
+ context "when it is neither a string nor a symbol" do
21
+ it "raises a TypeError" do
22
+ expect { described_class.new(Object.new) }.to raise_error(TypeError)
23
+ end
24
+ end
25
+ end
26
+
27
+ describe ":text" do
28
+ context "when a string" do
29
+ specify "the text is the string" do
30
+ node = described_class.new(:foo, text: "hey there big boy")
31
+ expect(node.text).to eq("hey there big boy")
32
+ end
33
+ end
34
+
35
+ context "when nil" do
36
+ specify "the text is an empty string" do
37
+ node = described_class.new(:foo, text: nil)
38
+ expect(node.text).to eq("")
39
+ end
40
+ end
41
+
42
+ context "when not provided" do
43
+ specify "the text is an empty string" do
44
+ node = described_class.new(:foo)
45
+ expect(node.text).to eq("")
46
+ end
47
+ end
48
+ context "when something other than a string or nil" do
49
+ it "raises a TypeError" do
50
+ expect { described_class.new(:foo, text: Object.new) }.to raise_error(TypeError)
51
+ end
52
+ end
53
+ end
54
+
55
+ describe ":attrs" do
56
+ context "when not provided" do
57
+ specify "the attrs are an empty hash" do
58
+ node = described_class.new(:foo)
59
+ expect(node.attrs).to eq({})
60
+ end
61
+ end
62
+ context "when nil" do
63
+ specify "the attrs are an empty hash" do
64
+ node = described_class.new(:foo, attrs: nil)
65
+ expect(node.attrs).to eq({})
66
+ end
67
+ end
68
+ context "when a hash of attributes" do
69
+ specify "the attrs are the provided attributes" do
70
+ node = described_class.new(:foo, attrs: {foo: "asdf", bar: "123"})
71
+ expect(node.attrs).to eq({foo:"asdf", bar:"123"})
72
+ end
73
+ end
74
+ context "when something other than a hash or nil" do
75
+ it "raises a TypeError" do
76
+ expect { described_class.new(:foo, attrs: Object.new) }.to raise_error(TypeError)
77
+ end
78
+ end
79
+ end
80
+
81
+ describe ":children" do
82
+ context "when not provided" do
83
+ specify "it should have no children" do
84
+ node = described_class.new(:foo)
85
+ expect(node.children).to eq({})
86
+ end
87
+ end
88
+
89
+ context "when nil" do
90
+ specify "it should have no children" do
91
+ node = described_class.new(:foo, children: nil)
92
+ expect(node.children).to eq({})
93
+ end
94
+ end
95
+
96
+ context "when a collection of Nodes" do
97
+ specify "it should have all those nodes as children" do
98
+ children = build_list(:xml_node, 20, name: :bla)
99
+ node = described_class.new(:foo, children: children)
100
+ expect(node.num_children).to eq(20)
101
+ expect(node[:bla]).to match_array(children)
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ describe "#==" do
108
+ it "returns true when compared to itself" do
109
+ node = build(:xml_node)
110
+ expect(node).to be == node
111
+ end
112
+
113
+ it "returns true when compared an identically configured node" do
114
+ node1 = build(:xml_node)
115
+ node2 = build(:xml_node)
116
+ expect(node1).to be == node2
117
+ end
118
+
119
+ it "returns true when compared to an object that looks like the node" do
120
+ node1 = build(:xml_node)
121
+ node2 = build(:xml_node)
122
+ fake_node_class = Struct.new(:name, :attrs, :text, :children)
123
+ fake_node = fake_node_class.new(node2.name, node2.attrs, node2.text, node2.children)
124
+ expect(node1).to be == fake_node
125
+ end
126
+
127
+ it "returns false when the attributes are different" do
128
+ node1 = build(:xml_node, attrs: {attr1: "abc"} )
129
+ node2 = build(:xml_node, attrs: {attr1: "xyz"} )
130
+ expect(node1).not_to be == node2
131
+ end
132
+
133
+ it "returns false when the text is different" do
134
+ node1 = build(:xml_node, text: "abc")
135
+ node2 = build(:xml_node, text: "xyz")
136
+ expect(node1).not_to be == node2
137
+ end
138
+
139
+ it "returns false when the children are different" do
140
+ node1 = build(:xml_node, children: [build(:xml_node, name: "a", text: "123"), build(:xml_node, name: "b", text: "xyz")] )
141
+ node2 = build(:xml_node, children: [build(:xml_node, name: "a", text: "456"), build(:xml_node, name: "b", text: "abc")] )
142
+ expect(node1).not_to be == node2
143
+ end
144
+
145
+ it "returns false when the name is different" do
146
+ node1 = build(:xml_node, name: "foo")
147
+ node2 = build(:xml_node, name: "bar")
148
+ expect(node1).not_to be == node2
149
+ end
150
+ end
151
+
152
+ describe "#eql?" do
153
+ it "returns true when compared to itself" do
154
+ node = build(:xml_node)
155
+ expect(node).to be_eql(node)
156
+ end
157
+
158
+ it "returns true when compared an identically configured node" do
159
+ node1 = build(:xml_node)
160
+ node2 = build(:xml_node)
161
+ expect(node1).to be_eql(node2)
162
+ end
163
+
164
+ it "returns false when compared to an object that looks like the node" do
165
+ node1 = build(:xml_node)
166
+ node2 = build(:xml_node)
167
+ fake_node_class = Struct.new(:name, :attrs, :text, :children)
168
+ fake_node = fake_node_class.new(node2.name, node2.attrs, node2.text, node2.children)
169
+ expect(node1).not_to be_eql(fake_node)
170
+ end
171
+
172
+ it "returns false when the attributes are different" do
173
+ node1 = build(:xml_node, attrs: {attr1: "abc"} )
174
+ node2 = build(:xml_node, attrs: {attr1: "xyz"} )
175
+ expect(node1).not_to be_eql(node2)
176
+ end
177
+
178
+ it "returns false when the text is different" do
179
+ node1 = build(:xml_node, text: "abc")
180
+ node2 = build(:xml_node, text: "xyz")
181
+ expect(node1).not_to be_eql(node2)
182
+ end
183
+
184
+ it "returns false when the children are different" do
185
+ node1 = build(:xml_node, children: [build(:xml_node, name: "a", text: "123"), build(:xml_node, name: "b", text: "xyz")] )
186
+ node2 = build(:xml_node, children: [build(:xml_node, name: "a", text: "456"), build(:xml_node, name: "b", text: "abc")] )
187
+ expect(node1).not_to be_eql(node2)
188
+ end
189
+
190
+ it "returns false when the name is different" do
191
+ node1 = build(:xml_node, name: "foo")
192
+ node2 = build(:xml_node, name: "bar")
193
+ expect(node1).not_to be_eql(node2)
194
+ end
195
+ end
196
+
197
+ describe "#equal?" do
198
+ it "returns true tested against itself" do
199
+ node = build(:xml_node)
200
+ expect(node).to be_equal(node)
201
+ end
202
+ it "returns true when compared an identically configured node" do
203
+ node = build(:xml_node)
204
+ node2 = build(:xml_node)
205
+ expect(node).not_to be_equal(node2)
206
+ end
207
+ end
208
+
209
+ describe "#children" do
210
+ let(:children) { [] }
211
+ let(:node) { build(:xml_node, children: children) }
212
+
213
+ it "returns a Hash" do
214
+ expect(node.children).to be_a(Hash)
215
+ end
216
+
217
+ it "is empty when there are no children" do
218
+ expect(node.children).to be_empty
219
+ end
220
+
221
+ context "with children" do
222
+ let(:children) {
223
+ [
224
+ build(:xml_node, name: "a", text: "1"),
225
+ build(:xml_node, name: "a", text: "2"),
226
+ build(:xml_node, name: "b", text: "3"),
227
+ build(:xml_node, name: "c", text: "4")
228
+ ]
229
+ }
230
+
231
+ specify "the keys are all symbols" do
232
+ expect(node.children.keys).to contain_exactly(kind_of(Symbol), kind_of(Symbol), kind_of(Symbol))
233
+ end
234
+ specify "the keys are names of the children" do
235
+ expect(node.children.keys).to contain_exactly(:a, :b, :c)
236
+ end
237
+
238
+ specify "the corresponding values are collections of the child nodes" do
239
+ expect(node.children[:a]).to contain_exactly(
240
+ build(:xml_node, name: "a", text: "1"),
241
+ build(:xml_node, name: "a", text: "2"))
242
+ expect(node.children[:b]).to contain_exactly(
243
+ build(:xml_node, name: "b", text: "3"))
244
+ expect(node.children[:c]).to contain_exactly(
245
+ build(:xml_node, name: "c", text: "4"))
246
+ end
247
+ end
248
+ end
249
+
250
+ describe "#[]" do
251
+ it "returns an empty array if there are no child elements for the given name" do
252
+ node = build(:xml_node)
253
+ expect(node[:foo]).to eq([])
254
+ end
255
+
256
+ it "returns an array the matching child nodes in the order in which they were added" do
257
+ child1 = build(:xml_node, name: :woof)
258
+ child2 = build(:xml_node, name: :woof)
259
+ child3 = build(:xml_node, name: :woof)
260
+ child4 = build(:xml_node, name: :bark)
261
+ child5 = build(:xml_node, name: :bark)
262
+ child6 = build(:xml_node, name: :bark)
263
+ node = build(:xml_node, children: [child1, child4, child2, child5, child3, child6])
264
+ children = node[:woof]
265
+ expect(children[0]).to be(child1)
266
+ expect(children[1]).to be(child2)
267
+ expect(children[2]).to be(child3)
268
+ end
269
+
270
+ it "accepts either a String or a Symbol, treating them equally" do
271
+ child = build(:xml_node, name: :moo)
272
+ node = build(:xml_node, children: [ child ])
273
+ expect(node[:moo]).to eq([child])
274
+ expect(node["moo"]).to eq([child])
275
+ end
276
+ end
277
+
278
+ describe "#child_names" do
279
+ context "with no children" do
280
+ it "returns an empty array" do
281
+ node = build(:xml_node)
282
+ expect(node.child_names).to eq([])
283
+ end
284
+ end
285
+ context "with children" do
286
+ it "returns an array of symbols for all the names of the child elements" do
287
+ children = [
288
+ build(:xml_node, name: :a),
289
+ build(:xml_node, name: :a),
290
+ build(:xml_node, name: :b),
291
+ build(:xml_node, name: :b),
292
+ build(:xml_node, name: :c),
293
+ build(:xml_node, name: :c)
294
+ ]
295
+ node = build(:xml_node, children: children)
296
+ expect(node.child_names).to contain_exactly(:a, :b, :c)
297
+ end
298
+ end
299
+ end
300
+
301
+ describe "#num_children" do
302
+ context "with no children" do
303
+ let(:node) { build(:xml_node) }
304
+ it "returns 0" do
305
+ expect(node.num_children).to eq(0)
306
+ end
307
+ end
308
+
309
+ context "with a bunch of children added" do
310
+ let(:node) { build(:xml_node, children: children) }
311
+ let(:children) { build_list(:xml_node, 50) }
312
+ it "returns the total number of children" do
313
+ expect(node.num_children).to eq(50)
314
+ end
315
+ end
316
+ end
317
+
318
+ describe "#text" do
319
+ it "returns an empty string when the node has no text" do
320
+ node = build(:xml_node)
321
+ expect(node.text).to eq("")
322
+ end
323
+
324
+ it "returns the string of text assigned to the node" do
325
+ node = build(:xml_node, text: "hey there big boy")
326
+ expect(node.text).to eq("hey there big boy")
327
+ end
328
+ end
329
+ describe "#name" do
330
+ it "it returns the name of the node as a symbol" do
331
+ node = build(:xml_node, name: :foobar)
332
+ expect(node.name).to be(:foobar)
333
+ end
334
+ end
335
+ end
@@ -0,0 +1,263 @@
1
+ require 'spec_helper'
2
+ require 'threatinator/parsers/xml'
3
+ require 'threatinator/parsers/xml/pattern'
4
+
5
+ describe Threatinator::Parsers::XML::Parser, :parser do
6
+ context "two instances with identically configured patterns" do
7
+ it_should_behave_like "a parser when compared to an identically configured parser" do
8
+ let(:pattern1) { Threatinator::Parsers::XML::Pattern.new('/foo/bar') }
9
+ let(:pattern2) { Threatinator::Parsers::XML::Pattern.new('/foo/bar') }
10
+ let(:parser1) { described_class.new(pattern: pattern1) }
11
+ let(:parser2) { described_class.new(pattern: pattern2) }
12
+ end
13
+ end
14
+
15
+ context "two instances with differently configured patterns" do
16
+ it_should_behave_like "a parser when compared to a differently configured parser" do
17
+ let(:pattern1) { Threatinator::Parsers::XML::Pattern.new('/foo/bar') }
18
+ let(:pattern2) { Threatinator::Parsers::XML::Pattern.new('//foo/bar') }
19
+ let(:parser1) { described_class.new(pattern: pattern1) }
20
+ let(:parser2) { described_class.new(pattern: pattern2) }
21
+ end
22
+ end
23
+
24
+ shared_examples_for "an XML::Record" do
25
+ it { is_expected.to be_a(Threatinator::Parsers::XML::Record) }
26
+ its(:node) { is_expected.to be_a(Threatinator::Parsers::XML::Node) }
27
+ end
28
+
29
+
30
+ shared_examples_for "a parser matching nothing" do
31
+ it "should yield 0 records" do
32
+ expect(records.count).to eq(0)
33
+ end
34
+ end #
35
+
36
+ shared_context "parsing xml" do
37
+ let(:io) { StringIO.new(xml) }
38
+ let(:parser) { described_class.new(pattern: pattern) }
39
+ let!(:records) {
40
+ ret = []
41
+ parser.run(io) { |r| ret << r }
42
+ ret
43
+ }
44
+ end
45
+
46
+ context "parsing records from test_self_closing.xml" do
47
+ let(:xml) { File.read(parser_data('test_self_closing.xml')) }
48
+ include_context "parsing xml"
49
+
50
+ shared_examples_for "a parser matching each 'z' element" do
51
+ it "should yield 4 records" do
52
+ expect(records.count).to eq(4)
53
+ end
54
+
55
+ describe "record 0" do
56
+ let(:record) { records[0] }
57
+ let(:node) { record.node }
58
+ subject { record }
59
+ it_should_behave_like "an XML::Record"
60
+ describe "the data from the node" do
61
+ subject { node }
62
+ its(:name) { is_expected.to eq(:z) }
63
+ its(:attrs) { is_expected.to eq({foo: "bar1"}) }
64
+ its(:text) { is_expected.to eq("") }
65
+ end
66
+ end
67
+ describe "record 1" do
68
+ let(:record) { records[1] }
69
+ let(:node) { record.node }
70
+ subject { record }
71
+ it_should_behave_like "an XML::Record"
72
+ describe "the data from the node" do
73
+ subject { node }
74
+ its(:name) { is_expected.to eq(:z) }
75
+ its(:attrs) { is_expected.to eq({foo: "bar2"}) }
76
+ its(:text) { is_expected.to eq("") }
77
+ end
78
+ end
79
+ describe "record 2" do
80
+ let(:record) { records[2] }
81
+ let(:node) { record.node }
82
+ subject { record }
83
+ it_should_behave_like "an XML::Record"
84
+ describe "the data from the node" do
85
+ subject { node }
86
+ its(:name) { is_expected.to eq(:z) }
87
+ its(:attrs) { is_expected.to eq({foo: "bar3"}) }
88
+ its(:text) { is_expected.to eq("")}
89
+ end
90
+ end
91
+ describe "record 3" do
92
+ let(:record) { records[3] }
93
+ let(:node) { record.node }
94
+ subject { record }
95
+ it_should_behave_like "an XML::Record"
96
+ describe "the data from the node" do
97
+ subject { node }
98
+ its(:name) { is_expected.to eq(:z) }
99
+ its(:attrs) { is_expected.to eq({foo: "bar4"}) }
100
+ its(:text) { is_expected.to eq("")}
101
+ end
102
+ end
103
+ end #
104
+
105
+ context "with the pattern '/doc/x/y/z'" do
106
+ let(:pattern) { Threatinator::Parsers::XML::Pattern.new("/doc/x/y/z") }
107
+ it_should_behave_like "a parser matching each 'z' element"
108
+ end
109
+
110
+ context "with the pattern '//x/y/z'" do
111
+ let(:pattern) { Threatinator::Parsers::XML::Pattern.new("//x/y/z") }
112
+ it_should_behave_like "a parser matching each 'z' element"
113
+ end
114
+
115
+ context "with the pattern '//z'" do
116
+ let(:pattern) { Threatinator::Parsers::XML::Pattern.new("//z") }
117
+ it_should_behave_like "a parser matching each 'z' element"
118
+ end
119
+
120
+
121
+ context "with the pattern '/y/z/x'" do
122
+ let(:pattern) { Threatinator::Parsers::XML::Pattern.new("/y/z/x") }
123
+ it_should_behave_like "a parser matching nothing"
124
+ end
125
+ end
126
+
127
+ context "parsing records from test.xml" do
128
+ let(:xml) { File.read(parser_data('test.xml')) }
129
+ include_context "parsing xml"
130
+
131
+ shared_examples_for "a parser matching each 'b' element" do
132
+ it "should yield 2 records" do
133
+ expect(records.count).to eq(2)
134
+ end
135
+
136
+ describe "record 0" do
137
+ let(:record) { records[0] }
138
+ let(:node) { record.node }
139
+ subject { record }
140
+ it_should_behave_like "an XML::Record"
141
+ describe "the data from the node" do
142
+ subject { node }
143
+ its(:name) { is_expected.to eq(:b) }
144
+ its(:attrs) { is_expected.to eq({}) }
145
+ its(:text) { is_expected.to eq("")}
146
+ its(:children) {
147
+ is_expected.to match(
148
+ {
149
+ c: a_collection_containing_exactly(
150
+ build(:xml_node, name: 'c', text: 'deep1A'),
151
+ build(:xml_node, name: 'c', text: 'deep1B')
152
+ )
153
+ }
154
+ )
155
+ }
156
+ end
157
+ end
158
+ describe "record 1" do
159
+ let(:record) { records[1] }
160
+ let(:node) { record.node }
161
+ subject { record }
162
+ it_should_behave_like "an XML::Record"
163
+ describe "the data from the node" do
164
+ subject { node }
165
+ its(:name) { is_expected.to eq(:b) }
166
+ its(:attrs) { is_expected.to eq({}) }
167
+ its(:text) { is_expected.to eq("")}
168
+ its(:children) {
169
+ is_expected.to match(
170
+ {
171
+ c: a_collection_containing_exactly(
172
+ build(:xml_node, name: 'c', text: 'deep2A'),
173
+ build(:xml_node, name: 'c', text: 'deep2B')
174
+ )
175
+ }
176
+ )
177
+ }
178
+ end
179
+ end
180
+ end #
181
+
182
+ shared_examples_for "a parser matching each 'c' element" do
183
+ it "should yield 4 records" do
184
+ expect(records.count).to eq(4)
185
+ end
186
+
187
+ describe "record 0" do
188
+ let(:record) { records[0] }
189
+ let(:node) { record.node }
190
+ subject { record }
191
+ it_should_behave_like "an XML::Record"
192
+ describe "the data from the node" do
193
+ subject { node }
194
+ its(:name) { is_expected.to eq(:c) }
195
+ its(:attrs) { is_expected.to eq({}) }
196
+ its(:text) { "deep1A" }
197
+ end
198
+ end
199
+ describe "record 1" do
200
+ let(:record) { records[1] }
201
+ let(:node) { record.node }
202
+ subject { record }
203
+ it_should_behave_like "an XML::Record"
204
+ describe "the data from the node" do
205
+ subject { node }
206
+ its(:name) { is_expected.to eq(:c) }
207
+ its(:attrs) { is_expected.to eq({}) }
208
+ its(:text) { "deep1B" }
209
+ end
210
+ end
211
+ describe "record 2" do
212
+ let(:record) { records[2] }
213
+ let(:node) { record.node }
214
+ subject { record }
215
+ it_should_behave_like "an XML::Record"
216
+ describe "the data from the node" do
217
+ subject { node }
218
+ its(:name) { is_expected.to eq(:c) }
219
+ its(:attrs) { is_expected.to eq({}) }
220
+ its(:text) { "deep2A" }
221
+ end
222
+ end
223
+ describe "record 3" do
224
+ let(:record) { records[3] }
225
+ let(:node) { record.node }
226
+ subject { record }
227
+ it_should_behave_like "an XML::Record"
228
+ describe "the data from the node" do
229
+ subject { node }
230
+ its(:name) { is_expected.to eq(:c) }
231
+ its(:attrs) { is_expected.to eq({}) }
232
+ its(:text) { "deep2B" }
233
+ end
234
+ end
235
+ end #
236
+
237
+ context "with the pattern '/doc/a/b/c'" do
238
+ let(:pattern) { Threatinator::Parsers::XML::Pattern.new("/doc/a/b/c") }
239
+ it_should_behave_like "a parser matching each 'c' element"
240
+ end
241
+
242
+
243
+ context "with the pattern '//a/b/c'" do
244
+ let(:pattern) { Threatinator::Parsers::XML::Pattern.new("//a/b/c") }
245
+ it_should_behave_like "a parser matching each 'c' element"
246
+ end
247
+
248
+ context "with the pattern '//b'" do
249
+ let(:pattern) { Threatinator::Parsers::XML::Pattern.new("//b") }
250
+ it_should_behave_like "a parser matching each 'b' element"
251
+ end
252
+
253
+ context "with the pattern '//c'" do
254
+ let(:pattern) { Threatinator::Parsers::XML::Pattern.new("//a/b/c") }
255
+ it_should_behave_like "a parser matching each 'c' element"
256
+ end
257
+
258
+ context "with the pattern '/c/b/a'" do
259
+ let(:pattern) { Threatinator::Parsers::XML::Pattern.new("/c/b/a") }
260
+ it_should_behave_like "a parser matching nothing"
261
+ end
262
+ end
263
+ end