threatinator 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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,12 @@
1
+ require 'spec_helper'
2
+ require 'threatinator/fetcher'
3
+
4
+ describe Threatinator::Fetcher do
5
+ let(:fetcher) { Threatinator::Fetcher.new }
6
+ describe "#fetch" do
7
+ it "should raise NotImplementedError because it's not implemented" do
8
+ expect {fetcher.fetch}.to raise_error(NotImplementedError)
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+ require 'threatinator/fetchers/http'
3
+
4
+ describe Threatinator::Fetchers::Http do
5
+ let(:url) { "http://foobar.com/bla.json" }
6
+ let(:fetcher_builder) { lambda { described_class.new(url: url) } }
7
+ let(:fetcher_builder_different) { lambda { described_class.new(url: "http://foobar.com/sdf.json") } }
8
+ let(:fetcher) { fetcher_builder.call() }
9
+
10
+ it_should_behave_like "a fetcher" do
11
+ before :each do
12
+ stub_request(:get, url).to_return(:body => expected_data)
13
+ end
14
+ end
15
+
16
+ describe "#url" do
17
+ it "should return the value of the URL" do
18
+ expect(fetcher.url).to eq(url)
19
+ end
20
+ end
21
+
22
+ [404, 500].each do |status_code|
23
+ context "when an HTTP response has a status code of #{status_code}" do
24
+ it_should_behave_like "a #fetch call that failed" do
25
+ before :each do
26
+ stub_request(:get, url)
27
+ .to_return(:status => status_code)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+ require 'threatinator/filter'
3
+
4
+ describe Threatinator::Filter do
5
+ let(:filter) { Threatinator::Filter.new }
6
+ describe "#filter?(record)" do
7
+ it "should raise NotImplementedError because it's not implemented" do
8
+ expect {filter.filter?(1234)}.to raise_error(NotImplementedError)
9
+ end
10
+ end
11
+ end
12
+
13
+
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+ require 'threatinator/record'
3
+ require 'threatinator/filters/block'
4
+
5
+ describe Threatinator::Filters::Block do
6
+ it_should_behave_like "a filter" do
7
+ let(:filter_block) {
8
+ lambda do |record|
9
+ record.data =~ /^FILTERME$/
10
+ end
11
+ }
12
+ let(:filter) { described_class.new(filter_block) }
13
+ let(:should_filter) { Threatinator::Record.new("FILTERME") }
14
+ let(:shouldnt_filter) { Threatinator::Record.new("I'm OK!") }
15
+ end
16
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+ require 'threatinator/record'
3
+ require 'threatinator/filters/comments'
4
+
5
+ describe Threatinator::Filters::Comments do
6
+ it_should_behave_like "a filter" do
7
+ let(:filter) { described_class.new() }
8
+ let(:should_filter) { Threatinator::Record.new("# Here's my comment") }
9
+ let(:shouldnt_filter) { Threatinator::Record.new("I'm OK # wooo!") }
10
+ end
11
+ end
12
+
13
+
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+ require 'threatinator/record'
3
+ require 'threatinator/filters/whitespace'
4
+
5
+ describe Threatinator::Filters::Whitespace do
6
+ it_should_behave_like "a filter" do
7
+ let(:filter) { described_class.new() }
8
+ let(:should_filter) { Threatinator::Record.new(" ") }
9
+ let(:shouldnt_filter) { Threatinator::Record.new(" I'm OK!") }
10
+ end
11
+ end
12
+
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+ require 'threatinator/parser'
3
+
4
+ describe Threatinator::Parser do
5
+ let(:parser) { Threatinator::Parser.new() }
6
+ describe "#run" do
7
+ it "should raise NotImplementedError because it's not implemented" do
8
+ expect { |b| parser.run(double("io"), &b) }.to raise_error(NotImplementedError)
9
+ end
10
+ end
11
+ end
12
+
13
+
@@ -0,0 +1,202 @@
1
+ require 'spec_helper'
2
+ require 'threatinator/parsers/csv/parser'
3
+ require 'stringio'
4
+ require 'threatinator/record'
5
+
6
+ describe Threatinator::Parsers::CSV::Parser do
7
+ it_should_behave_like "a parser when compared to an identically configured parser" do
8
+ let(:parser1) { described_class.new(:row_separator => "X") }
9
+ let(:parser2) { described_class.new(:row_separator => "X") }
10
+ end
11
+
12
+ context "two instances with differently configured row_separators" do
13
+ it_should_behave_like "a parser when compared to a differently configured parser" do
14
+ let(:parser1) { described_class.new(:row_separator => "X") }
15
+ let(:parser2) { described_class.new(:row_separator => "Y") }
16
+ end
17
+ end
18
+
19
+ context "by default" do
20
+ it "should parse \\n as a row separator" do
21
+ str = "abc\ndef\nhij"
22
+ sio = StringIO.new(str)
23
+ parser = described_class.new()
24
+
25
+ expect { |b|
26
+ parser.run(sio, &b)
27
+ }.to yield_successive_args(
28
+ Threatinator::Record.new(['abc'], line_number: 1, pos_start: 0, pos_end: 4),
29
+ Threatinator::Record.new(['def'], line_number: 2, pos_start: 4, pos_end: 8),
30
+ Threatinator::Record.new(['hij'], line_number: 3, pos_start: 8, pos_end: 11),
31
+ )
32
+ end
33
+
34
+ it "should parse \\r\\n as a row separator" do
35
+ str = "abc\r\ndef\r\nhij"
36
+ sio = StringIO.new(str)
37
+ parser = described_class.new()
38
+
39
+ expect { |b|
40
+ parser.run(sio, &b)
41
+ }.to yield_successive_args(
42
+ Threatinator::Record.new(['abc'], line_number: 1, pos_start: 0, pos_end: 5),
43
+ Threatinator::Record.new(['def'], line_number: 2, pos_start: 5, pos_end: 10),
44
+ Threatinator::Record.new(['hij'], line_number: 3, pos_start: 10, pos_end: 13),
45
+ )
46
+ end
47
+
48
+ it "should parse \\r as a row separator" do
49
+ str = "abc\rdef\rhij"
50
+ sio = StringIO.new(str)
51
+ parser = described_class.new()
52
+
53
+ expect { |b|
54
+ parser.run(sio, &b)
55
+ }.to yield_successive_args(
56
+ Threatinator::Record.new(['abc'], line_number: 1, pos_start: 0, pos_end: 4),
57
+ Threatinator::Record.new(['def'], line_number: 2, pos_start: 4, pos_end: 8),
58
+ Threatinator::Record.new(['hij'], line_number: 3, pos_start: 8, pos_end: 11),
59
+ )
60
+ end
61
+
62
+ it "should use commas as column separators" do
63
+ str = "1,2,3\n4,5,6\n7,8,9"
64
+ sio = StringIO.new(str)
65
+ parser = described_class.new()
66
+
67
+ expect { |b|
68
+ parser.run(sio, &b)
69
+ }.to yield_successive_args(
70
+ Threatinator::Record.new(['1','2','3'], line_number: 1, pos_start: 0, pos_end: 6),
71
+ Threatinator::Record.new(['4','5','6'], line_number: 2, pos_start: 6, pos_end: 12),
72
+ Threatinator::Record.new(['7','8','9'], line_number: 3, pos_start: 12, pos_end: 17),
73
+ )
74
+ end
75
+
76
+ it "should ignore separators contained in between a set of double-quotes" do
77
+ str = "1,\"2\",3\n\"4,5\",6\n\"7,8,9\""
78
+ sio = StringIO.new(str)
79
+ parser = described_class.new()
80
+
81
+ expect { |b|
82
+ parser.run(sio, &b)
83
+ }.to yield_successive_args(
84
+ Threatinator::Record.new(['1','2','3'], line_number: 1, pos_start: 0, pos_end: 8),
85
+ Threatinator::Record.new(['4,5','6'], line_number: 2, pos_start: 8, pos_end: 16),
86
+ Threatinator::Record.new(['7,8,9'], line_number: 3, pos_start: 16, pos_end: 23),
87
+ )
88
+ end
89
+ end
90
+
91
+ describe "configuration options" do
92
+ describe ":row_separator" do
93
+ it "should specify the row separator" do
94
+ str = "1,2,3X4,5,6X7,8,9"
95
+ sio = StringIO.new(str)
96
+ parser = described_class.new(row_separator: "X")
97
+
98
+ expect { |b|
99
+ parser.run(sio, &b)
100
+ }.to yield_successive_args(
101
+ Threatinator::Record.new(['1','2','3'], line_number: 1, pos_start: 0, pos_end: 6),
102
+ Threatinator::Record.new(['4','5','6'], line_number: 2, pos_start: 6, pos_end: 12),
103
+ Threatinator::Record.new(['7','8','9'], line_number: 3, pos_start: 12, pos_end: 17),
104
+ )
105
+ end
106
+ it "should be accessible via #row_separator" do
107
+ parser = described_class.new(row_separator: "X")
108
+ expect(parser.row_separator).to eq("X")
109
+ end
110
+ end
111
+
112
+ describe ":col_separator" do
113
+ it "should specify the column separator" do
114
+ str = "1X2X3\n4X5X6\n7X8X9"
115
+ sio = StringIO.new(str)
116
+ parser = described_class.new(col_separator: "X")
117
+
118
+ expect { |b|
119
+ parser.run(sio, &b)
120
+ }.to yield_successive_args(
121
+ Threatinator::Record.new(['1','2','3'], line_number: 1, pos_start: 0, pos_end: 6),
122
+ Threatinator::Record.new(['4','5','6'], line_number: 2, pos_start: 6, pos_end: 12),
123
+ Threatinator::Record.new(['7','8','9'], line_number: 3, pos_start: 12, pos_end: 17),
124
+ )
125
+ end
126
+ it "should be accessible via #col_separator" do
127
+ parser = described_class.new(col_separator: "X")
128
+ expect(parser.col_separator).to eq("X")
129
+ end
130
+ end
131
+ describe ":headers" do
132
+ it "should be accessible via #headers" do
133
+ parser = described_class.new(headers: :first_row)
134
+ expect(parser.headers).to eq(:first_row)
135
+ end
136
+ context "when set to false" do
137
+ it "should not use any headers" do
138
+ str = "1,2,3\n4,5,6\n7,8,9"
139
+ sio = StringIO.new(str)
140
+ parser = described_class.new(headers: false)
141
+
142
+ expect { |b|
143
+ parser.run(sio, &b)
144
+ }.to yield_successive_args(
145
+ Threatinator::Record.new(['1','2','3'], line_number: 1, pos_start: 0, pos_end: 6),
146
+ Threatinator::Record.new(['4','5','6'], line_number: 2, pos_start: 6, pos_end: 12),
147
+ Threatinator::Record.new(['7','8','9'], line_number: 3, pos_start: 12, pos_end: 17),
148
+ )
149
+ end
150
+ end
151
+ context "when set to true" do
152
+ it "should read the first row as the headers, returning Hashes with the headers as keys" do
153
+
154
+ str = "a,b,c\n1,2,3\n4,5,6\n7,8,9"
155
+ sio = StringIO.new(str)
156
+ parser = described_class.new(headers: true)
157
+
158
+ expect { |b|
159
+ parser.run(sio, &b)
160
+ }.to yield_successive_args(
161
+ Threatinator::Record.new({'a'=>'1','b'=>'2','c'=>'3'}, line_number: 2, pos_start: 6, pos_end: 12),
162
+ Threatinator::Record.new({'a'=>'4','b'=>'5','c'=>'6'}, line_number: 3, pos_start: 12, pos_end: 18),
163
+ Threatinator::Record.new({'a'=>'7','b'=>'8','c'=>'9'}, line_number: 4, pos_start: 18, pos_end: 23)
164
+ )
165
+ end
166
+ end
167
+ context "when set to :first_row" do
168
+ it "should read the first row as the headers, returning Hashes with the headers as keys" do
169
+
170
+ str = "a,b,c\n1,2,3\n4,5,6\n7,8,9"
171
+ sio = StringIO.new(str)
172
+ parser = described_class.new(headers: :first_row)
173
+
174
+ expect { |b|
175
+ parser.run(sio, &b)
176
+ }.to yield_successive_args(
177
+ Threatinator::Record.new({'a'=>'1','b'=>'2','c'=>'3'}, line_number: 2, pos_start: 6, pos_end: 12),
178
+ Threatinator::Record.new({'a'=>'4','b'=>'5','c'=>'6'}, line_number: 3, pos_start: 12, pos_end: 18),
179
+ Threatinator::Record.new({'a'=>'7','b'=>'8','c'=>'9'}, line_number: 4, pos_start: 18, pos_end: 23)
180
+ )
181
+ end
182
+ end
183
+ context "when set to an array of strings" do
184
+ it "should use the array as the headers, returning Hashes with the headers as keys" do
185
+
186
+ str = "1,2,3\n4,5,6\n7,8,9"
187
+ sio = StringIO.new(str)
188
+ parser = described_class.new(headers: ['a', 'b', 'c'])
189
+
190
+ expect { |b|
191
+ parser.run(sio, &b)
192
+ }.to yield_successive_args(
193
+ Threatinator::Record.new({'a'=>'1','b'=>'2','c'=>'3'}, line_number: 2, pos_start: 0, pos_end: 6),
194
+ Threatinator::Record.new({'a'=>'4','b'=>'5','c'=>'6'}, line_number: 3, pos_start: 6, pos_end: 12),
195
+ Threatinator::Record.new({'a'=>'7','b'=>'8','c'=>'9'}, line_number: 4, pos_start: 12, pos_end: 17)
196
+ )
197
+ end
198
+ end
199
+ end
200
+ end
201
+ end
202
+
@@ -0,0 +1,83 @@
1
+ require 'spec_helper'
2
+ require 'threatinator/parsers/getline/parser'
3
+ require 'stringio'
4
+ require 'threatinator/record'
5
+
6
+ describe Threatinator::Parsers::Getline::Parser do
7
+ it_should_behave_like "a parser when compared to an identically configured parser" do
8
+ let(:parser1) { described_class.new(:separator => "X") }
9
+ let(:parser2) { described_class.new(:separator => "X") }
10
+ end
11
+
12
+ context "two instances with differently configured separators" do
13
+ it_should_behave_like "a parser when compared to a differently configured parser" do
14
+ let(:parser1) { described_class.new(:separator => "X") }
15
+ let(:parser2) { described_class.new(:separator => "Y") }
16
+ end
17
+ end
18
+
19
+ it "should default to newline as a separator" do
20
+ str = "line 1\nline 2\nline3!"
21
+ sio = StringIO.new(str)
22
+ parser = described_class.new()
23
+
24
+ expect { |b|
25
+ parser.run(sio, &b)
26
+ }.to yield_successive_args(
27
+ Threatinator::Record.new("line 1\n", line_number: 1, pos_start: 0, pos_end: 7),
28
+ Threatinator::Record.new("line 2\n", line_number: 2, pos_start: 7, pos_end: 14),
29
+ Threatinator::Record.new("line3!", line_number: 3, pos_start: 14, pos_end: 20)
30
+ )
31
+ end
32
+ describe ":separator" do
33
+ it "should be accessible via the #separator method" do
34
+ parser = described_class.new(:separator => "Z")
35
+ expect(parser.separator).to eq("Z")
36
+ end
37
+ it "should specify a separator" do
38
+ str = "line 1\0line 2\0line3!"
39
+ sio = StringIO.new(str)
40
+ parser = described_class.new(:separator => "\0")
41
+
42
+ expect { |b|
43
+ parser.run(sio, &b)
44
+ }.to yield_successive_args(
45
+ Threatinator::Record.new("line 1\0", line_number: 1, pos_start: 0, pos_end: 7),
46
+ Threatinator::Record.new("line 2\0", line_number: 2, pos_start: 7, pos_end: 14),
47
+ Threatinator::Record.new("line3!", line_number: 3, pos_start: 14, pos_end: 20)
48
+ )
49
+ end
50
+ end
51
+
52
+ it "shouldn't split anything up if the separator isn't found" do
53
+ str = "line 1\0line 2\0line3!"
54
+ sio = StringIO.new(str)
55
+ parser = described_class.new(:separator => "\n")
56
+
57
+ expect { |b|
58
+ parser.run(sio, &b)
59
+ }.to yield_successive_args(
60
+ Threatinator::Record.new("line 1\0line 2\0line3!", line_number: 1, pos_start: 0, pos_end: 20)
61
+ )
62
+ end
63
+
64
+ it "should raise an ArgumentError if the separator isn't a single character" do
65
+ sio = StringIO.new()
66
+ expect { described_class.new(:separator => "asdf") }.to raise_error(ArgumentError)
67
+ end
68
+
69
+ it "should work on really, really long lines" do
70
+ line1 = ("A" * 100_000) + "\n"
71
+ line2 = ("B" * 100_000) + "\n"
72
+ line3 = "C" * 100_000
73
+ record1 = Threatinator::Record.new(line1, :line_number => 1, :pos_start => 0, :pos_end => 100_001)
74
+ record2 = Threatinator::Record.new(line2, :line_number => 2, :pos_start => 100_001, :pos_end => 200_002)
75
+ record3 = Threatinator::Record.new(line3, :line_number => 3, :pos_start => 200_002, :pos_end => 300_002)
76
+ sio = StringIO.new(line1 + line2 + line3)
77
+ parser = described_class.new(:separator => "\n")
78
+
79
+ expect { |b|
80
+ parser.run(sio, &b)
81
+ }.to yield_successive_args(record1, record2, record3)
82
+ end
83
+ end
@@ -0,0 +1,106 @@
1
+ require 'spec_helper'
2
+ require 'threatinator/parsers/json/parser'
3
+ require 'stringio'
4
+ require 'multi_json'
5
+
6
+ describe Threatinator::Parsers::JSON::Parser do
7
+ it_should_behave_like "a parser when compared to an identically configured parser" do
8
+ let(:parser1) { described_class.new() }
9
+ let(:parser2) { described_class.new() }
10
+ end
11
+
12
+ let(:element1) {
13
+ {
14
+ 'id' => 1,
15
+ 'marbles' => [
16
+ { 'color' => 'red', 'count' => 20 },
17
+ { 'color' => 'blue', 'count' => 15 }
18
+ ],
19
+ 'owner' => { 'name' => 'Billy', 'age' => '6' }
20
+ }
21
+ }
22
+ let(:element2) {
23
+ {
24
+ 'id' => 2,
25
+ 'marbles' => [
26
+ { 'color' => 'red', 'count' => 3 },
27
+ { 'color' => 'green', 'count' => 37 }
28
+ ],
29
+ 'owner' => { 'name' => 'Sarah', 'age' => '7' }
30
+ }
31
+ }
32
+ let(:element3) {
33
+ {
34
+ 'id' => 3,
35
+ 'marbles' => [
36
+ { 'color' => 'purple', 'count' => 73 }
37
+ ],
38
+ 'owner' => { 'name' => 'Phil', 'age' => '5' }
39
+ }
40
+ }
41
+
42
+ context "when parsing a JSON string whose root element is an Array" do
43
+ let(:json_string) { MultiJson.dump([element1, element2, element3]) }
44
+ let(:io) { StringIO.new(json_string) }
45
+
46
+ it "should yield each of the array's elements as the :data of a Parsers::JSON::Record" do
47
+ parser = described_class.new()
48
+
49
+ expect { |b|
50
+ parser.run(io, &b)
51
+ }.to yield_successive_args(
52
+ Threatinator::Parsers::JSON::Record.new(element1),
53
+ Threatinator::Parsers::JSON::Record.new(element2),
54
+ Threatinator::Parsers::JSON::Record.new(element3)
55
+ )
56
+ end
57
+ end
58
+
59
+ context "when parsing a JSON string whose root element is a Hash" do
60
+ let(:json_string) { MultiJson.dump({'foo' => element1, 'bar' => element2, 'bla' => element3}) }
61
+ let(:io) { StringIO.new(json_string) }
62
+
63
+ it "should yield each of the hash's key/value pairs as the :key and :data of a Parsers::JSON::Record" do
64
+ parser = described_class.new()
65
+
66
+ expect { |b|
67
+ parser.run(io, &b)
68
+ }.to yield_successive_args(
69
+ Threatinator::Parsers::JSON::Record.new(element1, key: 'foo'),
70
+ Threatinator::Parsers::JSON::Record.new(element2, key: 'bar'),
71
+ Threatinator::Parsers::JSON::Record.new(element3, key: 'bla')
72
+ )
73
+ end
74
+ end
75
+
76
+ context "when parsing something that is not JSON data" do
77
+ let(:json_string) { "hi there! this is my data feed" }
78
+ let(:io) { StringIO.new(json_string) }
79
+
80
+ it "should raise a exception Threatinator::Exceptions::ParseError" do
81
+ parser = described_class.new()
82
+
83
+ expect { |b|
84
+ parser.run(io, &b)
85
+ }.to raise_error(Threatinator::Exceptions::ParseError)
86
+ end
87
+ end
88
+
89
+ context "when parsing a JSON that is truncated" do
90
+ let(:json_string) { MultiJson.dump({'foo' => element1, 'bar' => element2, 'bla' => element3})[0..-3] }
91
+ let(:io) { StringIO.new(json_string) }
92
+
93
+ it "should yield as many records as possible before raising a ParseError" do
94
+ parser = described_class.new()
95
+
96
+ expect { |b|
97
+ expect {
98
+ parser.run(io, &b)
99
+ }.to raise_error(Threatinator::Exceptions::ParseError)
100
+ }.to yield_successive_args(
101
+ Threatinator::Parsers::JSON::Record.new(element1, key: 'foo'),
102
+ Threatinator::Parsers::JSON::Record.new(element2, key: 'bar')
103
+ )
104
+ end
105
+ end
106
+ end