shadowbq-threatinator 0.5.0

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 (389) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +66 -0
  3. data/CONTRIBUTING.md +119 -0
  4. data/Gemfile +38 -0
  5. data/LICENSE +165 -0
  6. data/README.md +101 -0
  7. data/Rakefile +47 -0
  8. data/VERSION +1 -0
  9. data/bin/threatinator +5 -0
  10. data/bin/threatinator_loader +21 -0
  11. data/feeds/ET_block-ip_reputation.feed +27 -0
  12. data/feeds/ET_compromised-ip_reputation.feed +20 -0
  13. data/feeds/ET_openbadlist-ip_reputation.feed +36 -0
  14. data/feeds/alienvault-ip_reputation.feed +39 -0
  15. data/feeds/arbor_fastflux-domain_reputation.feed +19 -0
  16. data/feeds/arbor_ssh-ip_reputation.feed +24 -0
  17. data/feeds/autoshun_shunlist.feed +17 -0
  18. data/feeds/bambenek_c2_masterlist-domain_reputation.feed +16 -0
  19. data/feeds/bambenek_c2_masterlist-ip_reputation.feed +16 -0
  20. data/feeds/bambenek_dga_feed-domain_reputation.feed +16 -0
  21. data/feeds/berkeley-ip_reputation.feed +25 -0
  22. data/feeds/bitcash_cz_blacklist.feed +22 -0
  23. data/feeds/blocklist_de_apache-ip_reputation.feed +26 -0
  24. data/feeds/blocklist_de_bots-ip_reputation.feed +26 -0
  25. data/feeds/blocklist_de_ftp-ip_reputation.feed +25 -0
  26. data/feeds/blocklist_de_imap-ip_reputation.feed +25 -0
  27. data/feeds/blocklist_de_pop3-ip_reputation.feed +26 -0
  28. data/feeds/blocklist_de_proftpd-ip_reputation.feed +26 -0
  29. data/feeds/blocklist_de_sip-ip_reputation.feed +25 -0
  30. data/feeds/blocklist_de_ssh-ip_reputation.feed +25 -0
  31. data/feeds/blocklist_de_strongips-ip_reputation.feed +25 -0
  32. data/feeds/botscout-ip_reputation.feed +25 -0
  33. data/feeds/cert_mxpoison-ip_reputation.feed +22 -0
  34. data/feeds/chaosreigns-ip_reputation.feed +37 -0
  35. data/feeds/ciarmy-ip_reputation.feed +20 -0
  36. data/feeds/cruzit-ip_reputation.feed +30 -0
  37. data/feeds/cydef_torexit-ip_reputation.feed +25 -0
  38. data/feeds/dan_me_uk_torlist-ip_reputation.feed +25 -0
  39. data/feeds/danger_bruteforce-ip_reputation.feed +24 -0
  40. data/feeds/dshield_attackers-top1000.feed +34 -0
  41. data/feeds/falconcrest-ip_reputation.feed +19 -0
  42. data/feeds/feodo-domain_reputation.feed +19 -0
  43. data/feeds/feodo-ip_reputation.feed +20 -0
  44. data/feeds/h3x_asprox.feed +18 -0
  45. data/feeds/hosts-file_hphostspartial-domain_reputation.feed +19 -0
  46. data/feeds/infiltrated-ip_reputation.feed +26 -0
  47. data/feeds/infiltrated_vabl-ip_reputation.feed +30 -0
  48. data/feeds/isc_suspicious_high-domain_reputation.feed +26 -0
  49. data/feeds/isc_suspicious_low-domain_reputation.feed +26 -0
  50. data/feeds/isc_suspicious_medium-domain_reputation.feed +26 -0
  51. data/feeds/malc0de-domain_reputation.feed +24 -0
  52. data/feeds/malc0de-ip_reputation.feed +26 -0
  53. data/feeds/malwaredomainlist-url_reputation.feed +18 -0
  54. data/feeds/malwaredomains-domain_reputation.feed +29 -0
  55. data/feeds/malwaredomains_dyndns-domain_reputation.feed +29 -0
  56. data/feeds/malwaredomains_justdomains-domain_reputation.feed +20 -0
  57. data/feeds/mirc-domain_reputation.feed +30 -0
  58. data/feeds/multiproxy-ip_reputation.feed +22 -0
  59. data/feeds/nothink_irc-ip_reputation.feed +23 -0
  60. data/feeds/nothink_ssh-ip_reputation.feed +21 -0
  61. data/feeds/openbl-ip_reputation.feed +21 -0
  62. data/feeds/openphish-url_reputation.feed +24 -0
  63. data/feeds/packetmail_perimeterbad-ip_reputation.feed +28 -0
  64. data/feeds/palevo-domain_reputation.feed +22 -0
  65. data/feeds/palevo-ip_reputation.feed +23 -0
  66. data/feeds/phishtank.feed +22 -0
  67. data/feeds/sigmaproject_atma.feed +27 -0
  68. data/feeds/sigmaproject_spyware.feed +28 -0
  69. data/feeds/sigmaproject_webexploit.feed +26 -0
  70. data/feeds/snort_bpf-ip_reputation.feed +19 -0
  71. data/feeds/spyeye-domain_reputation.feed +18 -0
  72. data/feeds/spyeye-ip_reputation.feed +19 -0
  73. data/feeds/steeman-ip_reputation.feed +20 -0
  74. data/feeds/t-arend-de_ssh-ip_reputation.feed +20 -0
  75. data/feeds/the_haleys_ssh-ip_reputation.feed +20 -0
  76. data/feeds/trustedsec-ip_reputation.feed +18 -0
  77. data/feeds/virbl-ip_reputation.feed +25 -0
  78. data/feeds/vxvault-url_reputation.feed +23 -0
  79. data/feeds/yourcmc_ssh-ip_reputation.feed +20 -0
  80. data/feeds/yoyo_adservers-domain_reputation.feed +17 -0
  81. data/feeds/zeus-domain_reputation.feed +19 -0
  82. data/feeds/zeus-ip_reputation.feed +21 -0
  83. data/lib/threatinator/action.rb +14 -0
  84. data/lib/threatinator/actions/list/action.rb +97 -0
  85. data/lib/threatinator/actions/list/config.rb +12 -0
  86. data/lib/threatinator/actions/list.rb +2 -0
  87. data/lib/threatinator/actions/run/action.rb +57 -0
  88. data/lib/threatinator/actions/run/config.rb +32 -0
  89. data/lib/threatinator/actions/run/coverage_observer.rb +59 -0
  90. data/lib/threatinator/actions/run/output_config.rb +59 -0
  91. data/lib/threatinator/actions/run/status_observer.rb +37 -0
  92. data/lib/threatinator/actions/run.rb +2 -0
  93. data/lib/threatinator/cli/action_builder.rb +33 -0
  94. data/lib/threatinator/cli/list_action_builder.rb +19 -0
  95. data/lib/threatinator/cli/parser.rb +123 -0
  96. data/lib/threatinator/cli/run_action_builder.rb +41 -0
  97. data/lib/threatinator/cli.rb +19 -0
  98. data/lib/threatinator/config/base.rb +35 -0
  99. data/lib/threatinator/config/feed_search.rb +25 -0
  100. data/lib/threatinator/config/logger.rb +14 -0
  101. data/lib/threatinator/config.rb +7 -0
  102. data/lib/threatinator/decoder.rb +24 -0
  103. data/lib/threatinator/decoders/gzip.rb +30 -0
  104. data/lib/threatinator/event.rb +63 -0
  105. data/lib/threatinator/event_builder.rb +70 -0
  106. data/lib/threatinator/exceptions.rb +58 -0
  107. data/lib/threatinator/feed.rb +88 -0
  108. data/lib/threatinator/feed_builder.rb +161 -0
  109. data/lib/threatinator/feed_registry.rb +47 -0
  110. data/lib/threatinator/feed_runner.rb +177 -0
  111. data/lib/threatinator/fetcher.rb +22 -0
  112. data/lib/threatinator/fetchers/http.rb +50 -0
  113. data/lib/threatinator/filter.rb +12 -0
  114. data/lib/threatinator/filters/block.rb +18 -0
  115. data/lib/threatinator/filters/comments.rb +16 -0
  116. data/lib/threatinator/filters/whitespace.rb +19 -0
  117. data/lib/threatinator/logger.rb +66 -0
  118. data/lib/threatinator/logging.rb +20 -0
  119. data/lib/threatinator/model/base.rb +23 -0
  120. data/lib/threatinator/model/collection.rb +89 -0
  121. data/lib/threatinator/model/observables/fqdn_collection.rb +15 -0
  122. data/lib/threatinator/model/observables/ipv4.rb +33 -0
  123. data/lib/threatinator/model/observables/ipv4_collection.rb +14 -0
  124. data/lib/threatinator/model/observables/url_collection.rb +16 -0
  125. data/lib/threatinator/model/validations/type.rb +21 -0
  126. data/lib/threatinator/model/validations.rb +1 -0
  127. data/lib/threatinator/output.rb +50 -0
  128. data/lib/threatinator/parser.rb +23 -0
  129. data/lib/threatinator/parsers/csv/parser.rb +77 -0
  130. data/lib/threatinator/parsers/csv.rb +7 -0
  131. data/lib/threatinator/parsers/getline/parser.rb +45 -0
  132. data/lib/threatinator/parsers/getline.rb +8 -0
  133. data/lib/threatinator/parsers/json/adapters/oj.rb +65 -0
  134. data/lib/threatinator/parsers/json/parser.rb +45 -0
  135. data/lib/threatinator/parsers/json/record.rb +20 -0
  136. data/lib/threatinator/parsers/json.rb +8 -0
  137. data/lib/threatinator/parsers/xml/node.rb +79 -0
  138. data/lib/threatinator/parsers/xml/node_builder.rb +39 -0
  139. data/lib/threatinator/parsers/xml/parser.rb +44 -0
  140. data/lib/threatinator/parsers/xml/path.rb +70 -0
  141. data/lib/threatinator/parsers/xml/pattern.rb +53 -0
  142. data/lib/threatinator/parsers/xml/record.rb +14 -0
  143. data/lib/threatinator/parsers/xml/sax_document.rb +64 -0
  144. data/lib/threatinator/parsers/xml.rb +8 -0
  145. data/lib/threatinator/plugin_loader.rb +115 -0
  146. data/lib/threatinator/plugins/output/amqp/config.rb +18 -0
  147. data/lib/threatinator/plugins/output/amqp.rb +41 -0
  148. data/lib/threatinator/plugins/output/csv.rb +58 -0
  149. data/lib/threatinator/plugins/output/json/config.rb +14 -0
  150. data/lib/threatinator/plugins/output/json.rb +53 -0
  151. data/lib/threatinator/plugins/output/null.rb +17 -0
  152. data/lib/threatinator/plugins/output/rubydebug.rb +16 -0
  153. data/lib/threatinator/record.rb +22 -0
  154. data/lib/threatinator/registry.rb +53 -0
  155. data/lib/threatinator/util.rb +15 -0
  156. data/lib/threatinator.rb +3 -0
  157. data/spec/feeds/ET_block-ip_reputation_spec.rb +50 -0
  158. data/spec/feeds/ET_compromised-ip_reputation_spec.rb +47 -0
  159. data/spec/feeds/ET_openbadlist-ip_reputation_spec.rb +56 -0
  160. data/spec/feeds/alienvault-ip_reputation_spec.rb +46 -0
  161. data/spec/feeds/arbor_fastflux-domain_reputation_spec.rb +46 -0
  162. data/spec/feeds/arbor_ssh-ip_reputation_spec.rb +46 -0
  163. data/spec/feeds/autoshun_shunlist_spec.rb +38 -0
  164. data/spec/feeds/bambenek_c2_masterlist-domain_reputation_spec.rb +38 -0
  165. data/spec/feeds/bambenek_c2_masterlist-ip_reputation_spec.rb +39 -0
  166. data/spec/feeds/bambenek_dga_feed-domain_reputation_spec.rb +39 -0
  167. data/spec/feeds/berkeley-ip_reputation_spec.rb +47 -0
  168. data/spec/feeds/bitcash_cz_blacklist-ip_reputation_spec.rb +50 -0
  169. data/spec/feeds/blocklist_de_apache-ip_reputation_spec.rb +47 -0
  170. data/spec/feeds/blocklist_de_bots-ip_reputation_spec.rb +47 -0
  171. data/spec/feeds/blocklist_de_ftp-ip_reputation_spec.rb +47 -0
  172. data/spec/feeds/blocklist_de_imap-ip_reputation_spec.rb +47 -0
  173. data/spec/feeds/blocklist_de_pop3-ip_reputation_spec.rb +47 -0
  174. data/spec/feeds/blocklist_de_proftpd-ip_reputation_spec.rb +47 -0
  175. data/spec/feeds/blocklist_de_sip-ip_reputation_spec.rb +47 -0
  176. data/spec/feeds/blocklist_de_ssh-ip_reputation_spec.rb +47 -0
  177. data/spec/feeds/blocklist_de_strongips-ip_reputation_spec.rb +47 -0
  178. data/spec/feeds/botscout-ip_reputation_spec.rb +50 -0
  179. data/spec/feeds/cert_mxpoison-ip_reputation_spec.rb +47 -0
  180. data/spec/feeds/chaosreigns-ip_reputation_spec.rb +50 -0
  181. data/spec/feeds/ciarmy-ip_reputation_spec.rb +47 -0
  182. data/spec/feeds/cruzit-ip_reputation_spec.rb +47 -0
  183. data/spec/feeds/cydef_torexit-ip_reputation_spec.rb +47 -0
  184. data/spec/feeds/dan_me_uk_torlist-ip_reputation_spec.rb +47 -0
  185. data/spec/feeds/danger_bruteforce-ip_reputation_spec.rb +47 -0
  186. data/spec/feeds/data/ET_block-ip_reputation.txt +80 -0
  187. data/spec/feeds/data/ET_compromised-ip_reputation.txt +11 -0
  188. data/spec/feeds/data/ET_openbadlist-ip_reputation.txt +62 -0
  189. data/spec/feeds/data/alienvault-ip_reputation.txt +18 -0
  190. data/spec/feeds/data/arbor_domainlist.txt +11 -0
  191. data/spec/feeds/data/arbor_ssh.txt +16 -0
  192. data/spec/feeds/data/autoshun_shunlist.csv +20 -0
  193. data/spec/feeds/data/bambenek_c2-dommasterlist.csv +30 -0
  194. data/spec/feeds/data/bambenek_c2-ipmasterlist.csv +27 -0
  195. data/spec/feeds/data/bambenek_dga_feed.csv +42 -0
  196. data/spec/feeds/data/berkeley.txt +29 -0
  197. data/spec/feeds/data/bitcash_cz_blacklist.txt +7 -0
  198. data/spec/feeds/data/blocklist_de_apache-ip-reputation.txt +17 -0
  199. data/spec/feeds/data/blocklist_de_bots-ip-reputation.txt +15 -0
  200. data/spec/feeds/data/blocklist_de_ftp-ip-reputation.txt +7 -0
  201. data/spec/feeds/data/blocklist_de_imap-ip-reputation.txt +8 -0
  202. data/spec/feeds/data/blocklist_de_pop3-ip-reputation.txt +11 -0
  203. data/spec/feeds/data/blocklist_de_proftpd-ip-reputation.txt +12 -0
  204. data/spec/feeds/data/blocklist_de_sip-ip-reputation.txt +9 -0
  205. data/spec/feeds/data/blocklist_de_ssh-ip-reputation.txt +10 -0
  206. data/spec/feeds/data/blocklist_de_strongips-ip-reputation.txt +11 -0
  207. data/spec/feeds/data/botscout-ip-reputation.txt +713 -0
  208. data/spec/feeds/data/cert_mxpoison-ip_reputation.txt +17 -0
  209. data/spec/feeds/data/chaosreigns-ip-reputation.txt +26 -0
  210. data/spec/feeds/data/ciarmy-ip-reputation.txt +11 -0
  211. data/spec/feeds/data/cruzit-ip-reputation.txt +14 -0
  212. data/spec/feeds/data/cydef_torexit-ip_reputation.txt +27 -0
  213. data/spec/feeds/data/dan_me_uk_torlist-ip-reputation.txt +11 -0
  214. data/spec/feeds/data/danger_bruteforce-ip_reputation.txt +12 -0
  215. data/spec/feeds/data/dshield_topattackers.xml +4 -0
  216. data/spec/feeds/data/falconcrest_iplist.txt +345 -0
  217. data/spec/feeds/data/feodo_domainlist.txt +18 -0
  218. data/spec/feeds/data/feodo_iplist.txt +20 -0
  219. data/spec/feeds/data/h3x_asprox.txt +20 -0
  220. data/spec/feeds/data/hosts-file_hphostspartial_domainlist.txt +24 -0
  221. data/spec/feeds/data/infiltrated_iplist.txt +16 -0
  222. data/spec/feeds/data/infiltrated_vabl_iplist.txt +33 -0
  223. data/spec/feeds/data/isc_suspicious_high_domainlist.txt +26 -0
  224. data/spec/feeds/data/isc_suspicious_low_domainlist.txt +34 -0
  225. data/spec/feeds/data/isc_suspicious_medium_domainlist.txt +32 -0
  226. data/spec/feeds/data/malc0de_domainlist.txt +18 -0
  227. data/spec/feeds/data/malc0de_iplist.txt +14 -0
  228. data/spec/feeds/data/malwaredomainlist-url-reputation.txt +8 -0
  229. data/spec/feeds/data/malwaredomains_domainlist.txt +24 -0
  230. data/spec/feeds/data/malwaredomains_dyndns_domainlist.txt +34 -0
  231. data/spec/feeds/data/malwaredomains_justdomains_domainlist.txt +18 -0
  232. data/spec/feeds/data/mirc_domainlist.txt +31 -0
  233. data/spec/feeds/data/multiproxy_iplist.txt +15 -0
  234. data/spec/feeds/data/nothink_irc_iplist.txt +14 -0
  235. data/spec/feeds/data/nothink_ssh_iplist.txt +10 -0
  236. data/spec/feeds/data/openbl_iplist.txt +12 -0
  237. data/spec/feeds/data/openphish-url-reputation.txt +16 -0
  238. data/spec/feeds/data/packetmail_perimeterbad-ip_reputation.txt +44 -0
  239. data/spec/feeds/data/palevo_domainlist.txt +25 -0
  240. data/spec/feeds/data/palevo_iplist.txt +24 -0
  241. data/spec/feeds/data/phishtank-sample.json.gz +0 -0
  242. data/spec/feeds/data/sigmaproject_atma.return.gz +0 -0
  243. data/spec/feeds/data/sigmaproject_spyware.return.gz +0 -0
  244. data/spec/feeds/data/sigmaproject_webexploit.return.gz +0 -0
  245. data/spec/feeds/data/snort_bpf-ip_reputation.txt +16 -0
  246. data/spec/feeds/data/spyeye_domainlist.txt +16 -0
  247. data/spec/feeds/data/spyeye_iplist.txt +19 -0
  248. data/spec/feeds/data/steeman-ip-reputation.txt +13 -0
  249. data/spec/feeds/data/t-arend-de_ssh_iplist.txt +17 -0
  250. data/spec/feeds/data/the_haleys_ssh_iplist.txt +12 -0
  251. data/spec/feeds/data/trustedsec-ip-reputation.txt +12 -0
  252. data/spec/feeds/data/valid.json +2908 -0
  253. data/spec/feeds/data/virbl-ip_reputation.txt +14 -0
  254. data/spec/feeds/data/vxvault-url-reputation.txt +15 -0
  255. data/spec/feeds/data/yourcmc_ssh-ip_reputation.txt +27 -0
  256. data/spec/feeds/data/yoyo_adservers.txt +25 -0
  257. data/spec/feeds/data/zeus-ip_reputation.txt +285 -0
  258. data/spec/feeds/data/zeus_domainlist.txt +27 -0
  259. data/spec/feeds/dshield_attackers-top1000_spec.rb +39 -0
  260. data/spec/feeds/falconcrest-ip_reputation_spec.rb +39 -0
  261. data/spec/feeds/feodo-domain_reputation_spec.rb +47 -0
  262. data/spec/feeds/feodo-ip_reputation_spec.rb +47 -0
  263. data/spec/feeds/h3x_asprox-ip_reputation_spec.rb +50 -0
  264. data/spec/feeds/hosts-file_hphostspartial-domain_reputation_spec.rb +47 -0
  265. data/spec/feeds/infiltrated-ip_reputation_spec.rb +47 -0
  266. data/spec/feeds/infiltrated_vabl-ip_reputation_spec.rb +47 -0
  267. data/spec/feeds/isc_suspicious_high-domain_reputation_spec.rb +47 -0
  268. data/spec/feeds/isc_suspicious_low-domain_reputation_spec.rb +47 -0
  269. data/spec/feeds/isc_suspicious_medium-domain_reputation_spec.rb +47 -0
  270. data/spec/feeds/malc0de-domain_reputation_spec.rb +47 -0
  271. data/spec/feeds/malc0de-ip_reputation_spec.rb +47 -0
  272. data/spec/feeds/malwaredomainlist_url_reputation_spec.rb +50 -0
  273. data/spec/feeds/malwaredomains-domain_reputation_spec.rb +47 -0
  274. data/spec/feeds/malwaredomains_dyndns-domain_reputation_spec.rb +47 -0
  275. data/spec/feeds/malwaredomains_justdomains-domain_reputation_spec.rb +47 -0
  276. data/spec/feeds/mirc-domain_reputation_spec.rb +47 -0
  277. data/spec/feeds/multiproxy-ip_reputation_spec.rb +47 -0
  278. data/spec/feeds/nothink_irc-ip_reputation_spec.rb +47 -0
  279. data/spec/feeds/nothink_ssh-ip_reputation_spec.rb +47 -0
  280. data/spec/feeds/openbl-ip_reputation_spec.rb +47 -0
  281. data/spec/feeds/openphish_url_reputation_spec.rb +50 -0
  282. data/spec/feeds/packetmail_perimeterbad-ip_reputation_spec.rb +47 -0
  283. data/spec/feeds/palevo-domain_reputation_spec.rb +47 -0
  284. data/spec/feeds/palevo-ip_reputation_spec.rb +47 -0
  285. data/spec/feeds/phishtank_spec.rb +41 -0
  286. data/spec/feeds/sigmaproject_atma_spec.rb +62 -0
  287. data/spec/feeds/sigmaproject_spyware_spec.rb +63 -0
  288. data/spec/feeds/sigmaproject_webexploit_spec.rb +62 -0
  289. data/spec/feeds/snort_bpf-ip_reputation_spec.rb +47 -0
  290. data/spec/feeds/spyeye-domain_reputation_spec.rb +47 -0
  291. data/spec/feeds/spyeye-ip_reputation_spec.rb +47 -0
  292. data/spec/feeds/steeman-ip_reputation_spec.rb +50 -0
  293. data/spec/feeds/t-arend-de_ssh-ip_reputation_spec.rb +47 -0
  294. data/spec/feeds/the_haleys_ssh-ip_reputation_spec.rb +47 -0
  295. data/spec/feeds/trustedsec-ip_reputation_spec.rb +47 -0
  296. data/spec/feeds/virbl-ip_reputation_spec.rb +47 -0
  297. data/spec/feeds/vxvault_url_reputation_spec.rb +50 -0
  298. data/spec/feeds/yourcmc_ssh-ip_reputation_spec.rb +47 -0
  299. data/spec/feeds/yoyo_adservers_spec.rb +47 -0
  300. data/spec/feeds/zeus-domain_reputation_spec.rb +47 -0
  301. data/spec/feeds/zeus-ip_reputation_spec.rb +47 -0
  302. data/spec/fixtures/feed/provider1/feed1.feed +6 -0
  303. data/spec/fixtures/parsers/test.xml +13 -0
  304. data/spec/fixtures/parsers/test_self_closing.xml +20 -0
  305. data/spec/fixtures/plugins/bad/threatinator/plugins/test_error1/plugin.rb +1 -0
  306. data/spec/fixtures/plugins/bad/threatinator/plugins/test_missing1/plugin.rb +0 -0
  307. data/spec/fixtures/plugins/fake.rb +19 -0
  308. data/spec/fixtures/plugins/good/threatinator/plugins/test_type1/plugin_a.rb +8 -0
  309. data/spec/fixtures/plugins/good/threatinator/plugins/test_type1/plugin_b.rb +8 -0
  310. data/spec/fixtures/plugins/good/threatinator/plugins/test_type2/plugin_c.rb +8 -0
  311. data/spec/fixtures/plugins/good/threatinator/plugins/test_type2/plugin_d.rb +8 -0
  312. data/spec/fixtures/plugins/good/threatinator/plugins/test_type3/plugin_e.rb +8 -0
  313. data/spec/fixtures/plugins/good/threatinator/plugins/test_type3/plugin_f.rb +8 -0
  314. data/spec/spec_helper.rb +54 -0
  315. data/spec/support/bad_feeds/missing_fetcher.feed +7 -0
  316. data/spec/support/bad_feeds/missing_name.feed +6 -0
  317. data/spec/support/bad_feeds/missing_parser.feed +3 -0
  318. data/spec/support/bad_feeds/missing_provider.feed +5 -0
  319. data/spec/support/factories/event.rb +31 -0
  320. data/spec/support/factories/feed.rb +59 -0
  321. data/spec/support/factories/feed_builder.rb +65 -0
  322. data/spec/support/factories/feed_registry.rb +8 -0
  323. data/spec/support/factories/ipv4.rb +36 -0
  324. data/spec/support/factories/output.rb +11 -0
  325. data/spec/support/factories/record.rb +17 -0
  326. data/spec/support/factories/url.rb +34 -0
  327. data/spec/support/factories/xml_node.rb +33 -0
  328. data/spec/support/helpers/io.rb +11 -0
  329. data/spec/support/helpers/models.rb +13 -0
  330. data/spec/support/shared/action_builder.rb +47 -0
  331. data/spec/support/shared/decoder.rb +70 -0
  332. data/spec/support/shared/feed_runner_observer.rb +136 -0
  333. data/spec/support/shared/feeds.rb +233 -0
  334. data/spec/support/shared/fetcher.rb +48 -0
  335. data/spec/support/shared/filter.rb +14 -0
  336. data/spec/support/shared/io-like.rb +7 -0
  337. data/spec/support/shared/model/collection.rb +164 -0
  338. data/spec/support/shared/output.rb +120 -0
  339. data/spec/support/shared/parsers.rb +51 -0
  340. data/spec/support/shared/record.rb +111 -0
  341. data/spec/threatinator/actions/list/action_spec.rb +148 -0
  342. data/spec/threatinator/actions/run/action_spec.rb +106 -0
  343. data/spec/threatinator/actions/run/config_spec.rb +39 -0
  344. data/spec/threatinator/actions/run/coverage_observer_spec.rb +151 -0
  345. data/spec/threatinator/actions/run/output_config_spec.rb +89 -0
  346. data/spec/threatinator/actions/run/status_observer_spec.rb +86 -0
  347. data/spec/threatinator/cli/list_action_builder_spec.rb +57 -0
  348. data/spec/threatinator/cli/run_action_builder_spec.rb +133 -0
  349. data/spec/threatinator/cli_spec.rb +175 -0
  350. data/spec/threatinator/config/base_spec.rb +39 -0
  351. data/spec/threatinator/config/feed_search_spec.rb +76 -0
  352. data/spec/threatinator/decoders/gzip_spec.rb +75 -0
  353. data/spec/threatinator/event_builder_spec.rb +123 -0
  354. data/spec/threatinator/event_spec.rb +254 -0
  355. data/spec/threatinator/event_spec.rb.new +319 -0
  356. data/spec/threatinator/feed_builder_spec.rb +633 -0
  357. data/spec/threatinator/feed_registry_spec.rb +198 -0
  358. data/spec/threatinator/feed_runner_spec.rb +372 -0
  359. data/spec/threatinator/feed_spec.rb +169 -0
  360. data/spec/threatinator/fetcher_spec.rb +12 -0
  361. data/spec/threatinator/fetchers/http_spec.rb +32 -0
  362. data/spec/threatinator/filter_spec.rb +13 -0
  363. data/spec/threatinator/filters/block_spec.rb +16 -0
  364. data/spec/threatinator/filters/comments_spec.rb +13 -0
  365. data/spec/threatinator/filters/whitespace_spec.rb +12 -0
  366. data/spec/threatinator/logger_spec.rb +29 -0
  367. data/spec/threatinator/model/observables/fqdn_collection_spec.rb +41 -0
  368. data/spec/threatinator/model/observables/ipv4_collection_spec.rb +36 -0
  369. data/spec/threatinator/model/observables/ipv4_spec.rb +75 -0
  370. data/spec/threatinator/model/observables/url_collection_spec.rb +45 -0
  371. data/spec/threatinator/model/validations/type_spec.rb +37 -0
  372. data/spec/threatinator/parser_spec.rb +13 -0
  373. data/spec/threatinator/parsers/csv/parser_spec.rb +202 -0
  374. data/spec/threatinator/parsers/getline/parser_spec.rb +83 -0
  375. data/spec/threatinator/parsers/json/parser_spec.rb +106 -0
  376. data/spec/threatinator/parsers/json/record_spec.rb +30 -0
  377. data/spec/threatinator/parsers/xml/node_spec.rb +335 -0
  378. data/spec/threatinator/parsers/xml/parser_spec.rb +263 -0
  379. data/spec/threatinator/parsers/xml/path_spec.rb +209 -0
  380. data/spec/threatinator/parsers/xml/pattern_spec.rb +72 -0
  381. data/spec/threatinator/parsers/xml/record_spec.rb +27 -0
  382. data/spec/threatinator/plugin_loader_spec.rb +238 -0
  383. data/spec/threatinator/plugins/output/csv_spec.rb +47 -0
  384. data/spec/threatinator/plugins/output/null_spec.rb +17 -0
  385. data/spec/threatinator/plugins/output/rubydebug_spec.rb +37 -0
  386. data/spec/threatinator/record_spec.rb +19 -0
  387. data/spec/threatinator/registry_spec.rb +97 -0
  388. data/spec/threatinator/runner_spec.rb +273 -0
  389. metadata +674 -0
@@ -0,0 +1,233 @@
1
+ require 'spec_helper'
2
+ require 'threatinator/feed_builder'
3
+ require 'threatinator/feed_runner'
4
+ require 'threatinator/plugins/output/null'
5
+ require 'pathname'
6
+
7
+ shared_context 'a parsed record' do
8
+ # Expects :observer
9
+ before :each do
10
+ @record, @status, @events = observer.first
11
+ end
12
+ let(:record) { @record }
13
+ let(:events) { @events }
14
+ let(:status) { @status }
15
+ end
16
+
17
+ shared_context 'a parsed feed' do
18
+ # expects :observer
19
+ let(:events) { observer.events }
20
+ let(:records) { observer.records }
21
+ let(:num_records) { observer.count }
22
+ let(:num_records_filtered) { observer.num_records_filtered }
23
+ let(:num_records_parsed) { observer.num_records_parsed }
24
+ let(:num_records_missed) { observer.num_records_missed }
25
+ let(:num_records_errored) { observer.num_records_errored }
26
+
27
+ it "should have 0 error records" do
28
+ expect(num_records_errored).to eq(0)
29
+ end
30
+
31
+ it "should have missed 0 records" do
32
+ expect(num_records_missed).to eq(0)
33
+ end
34
+ end
35
+
36
+ shared_context 'for feeds', :feed => lambda { true } do
37
+ _feed_path = Pathname.new(self.description)
38
+ if _feed_path.relative?
39
+ # It's relative to the root of our project.
40
+ _feed_path = PROJECT_ROOT + _feed_path
41
+ end
42
+ _feed_path = _feed_path.expand_path
43
+
44
+ before :all do
45
+ @feed_builder = Threatinator::FeedBuilder.from_file(_feed_path.to_s)
46
+ end
47
+
48
+ let(:feed_path) { feed_path.to_s }
49
+ let(:output_formatter) { Threatinator::Plugins::Output::Null.new(Threatinator::Plugins::Output::Null::Config.new) }
50
+ let(:feed_runner) { Threatinator::FeedRunner.new(feed, output_formatter) }
51
+ let(:feed) { @feed_builder.build() }
52
+ end
53
+
54
+ shared_examples_for 'any feed', :feed do
55
+ # Expects :provider, :name, :feed
56
+ subject { feed }
57
+ it { should be_a(Threatinator::Feed) }
58
+
59
+ describe "#provider" do
60
+ subject { feed.provider }
61
+ it { is_expected.to be_a(::String) }
62
+ it { is_expected.to eq(provider) }
63
+ end
64
+
65
+ describe "#name" do
66
+ subject { feed.name}
67
+ it { is_expected.to be_a(::String) }
68
+ it { is_expected.to eq(name) }
69
+ end
70
+
71
+ describe "#parser_block" do
72
+ subject { feed.parser_block }
73
+ it { is_expected.to be_a(::Proc) }
74
+ end
75
+
76
+ describe "#fetcher_builder" do
77
+ subject { feed.fetcher_builder }
78
+ it { is_expected.to be_a ::Proc }
79
+ specify "when called, it should generate a kind of Threatinator::Fetcher" do
80
+ expect(subject.call).to be_kind_of(Threatinator::Fetcher)
81
+ end
82
+ end
83
+
84
+ describe "#parser_builder" do
85
+ subject { feed.parser_builder }
86
+ it { is_expected.to be_a ::Proc }
87
+
88
+ specify "when called, it should generate a kind of Threatinator::Parser" do
89
+ expect(subject.call).to be_kind_of(Threatinator::Parser)
90
+ end
91
+ end
92
+
93
+ describe "#filter_builders" do
94
+ subject { feed.filter_builders }
95
+
96
+ it "should be an Array of Proc objects" do
97
+ expect(subject).to be_an ::Array
98
+ end
99
+
100
+ specify "each Proc, when called, should generate an object that responds to :filter?" do
101
+ subject.each do |filter_builder|
102
+ expect(filter_builder.call).to respond_to(:filter?)
103
+ end
104
+ end
105
+ end
106
+
107
+ describe "#decoder_builders" do
108
+ subject { feed.decoder_builders }
109
+
110
+ it "should be an Array of Proc objects" do
111
+ expect(subject).to be_an ::Array
112
+ end
113
+
114
+ specify "each Proc, when called, should generate a kind of Threatinator::Decoder" do
115
+ subject.each do |decoder_builder|
116
+ expect(decoder_builder.call).to be_kind_of(Threatinator::Decoder)
117
+ end
118
+ end
119
+ end
120
+ end
121
+
122
+ module FeedHelpers
123
+ class FeedRunnerObserver
124
+ include Enumerable
125
+ attr_reader :records, :statuses, :events, :num_records_filtered,
126
+ :num_records_missed, :num_records_parsed, :num_records_errored
127
+
128
+ def initialize
129
+ @records = []
130
+ @statuses = []
131
+ @events = []
132
+ @num_records_filtered = 0
133
+ @num_records_parsed = 0
134
+ @num_records_missed = 0
135
+ @num_records_errored = 0
136
+ end
137
+
138
+ def each
139
+ @records.each_with_index do |record, i|
140
+ yield(record, @statuses[i], @events[i])
141
+ end
142
+ end
143
+
144
+ # Handles FeedRunner observations
145
+ def update(message, *args)
146
+ case message
147
+ when :record_missed
148
+ @records << args.shift
149
+ @statuses << :missed
150
+ @events << []
151
+ @num_records_missed += 1
152
+ when :record_filtered
153
+ @records << args.shift
154
+ @statuses << :filtered
155
+ @events << []
156
+ @num_records_filtered += 1
157
+ when :record_parsed
158
+ @records << args.shift
159
+ @statuses << :parsed
160
+ @events << args.shift
161
+ @num_records_parsed += 1
162
+ when :record_error
163
+ @records << args.shift
164
+ @statuses << :error
165
+ @events << []
166
+ @num_records_errored += 1
167
+ end
168
+ end
169
+ end
170
+
171
+ module FeedHelperMethods
172
+ def it_fetches_url(url)
173
+ describe 'fetching' do
174
+ it "should fetch the url #{url}" do
175
+ stub_request(:get, url)
176
+ feed.fetcher_builder.call().fetch()
177
+ expect(a_request(:get, url)).to have_been_made
178
+ end
179
+ end
180
+ end
181
+
182
+ def feed_data(filename)
183
+ (FEED_DATA_ROOT + filename).to_s
184
+ end
185
+
186
+ def describe_parsing_a_record(data, &block)
187
+ context("parsing a record from '#{data}'", :caller => caller) do
188
+ let(:observer) { FeedRunnerObserver.new }
189
+ before :each do
190
+ sio = StringIO.new(data)
191
+ feed_runner.add_observer(observer)
192
+ feed_runner.run(:io => sio, :skip_decoding => true)
193
+ @status, @record, @events = observer.first
194
+ end
195
+
196
+ after :each do
197
+ feed_runner.delete_observer(observer)
198
+ end
199
+
200
+ it "should have handled exactly 1 record" do
201
+ expect(observer.count).to eq(1)
202
+ end
203
+
204
+ describe "the record" do
205
+ include_context 'a parsed record'
206
+ instance_exec(&block)
207
+ end
208
+ end
209
+ end
210
+
211
+ def describe_parsing_the_file(filename, &block)
212
+ filepath = Pathname.new(filename)
213
+ relative_filename = filepath.relative_path_from(PROJECT_ROOT).to_s
214
+
215
+ context("parsing the file '#{relative_filename}'", :caller => caller) do
216
+ let(:observer) { FeedRunnerObserver.new }
217
+ before :each do
218
+ fio = File.open(filename, 'r')
219
+ feed_runner.add_observer(observer)
220
+ feed_runner.run(:io => fio)
221
+ fio.close unless fio.closed?
222
+ end
223
+
224
+ after :each do
225
+ feed_runner.delete_observer(observer)
226
+ end
227
+
228
+ include_context "a parsed feed"
229
+ instance_exec(&block)
230
+ end
231
+ end
232
+ end
233
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples_for "a fetcher" do
4
+ let(:expected_data) { "here's some data" }
5
+
6
+ subject { fetcher }
7
+ it { should respond_to(:fetch) }
8
+
9
+ it "should == itself" do
10
+ expect(fetcher).to be == fetcher
11
+ end
12
+
13
+ it "should == an identically configured instance" do
14
+ expect(fetcher_builder.call()).to be == fetcher_builder.call()
15
+ end
16
+
17
+ it "should not == a differently configured instance" do
18
+ expect(fetcher_builder.call()).not_to be == fetcher_builder_different.call()
19
+ end
20
+
21
+ it "should eql?(itself)" do
22
+ expect(fetcher).to eql(fetcher)
23
+ end
24
+
25
+ it "should eql? an identically configured instance" do
26
+ expect(fetcher_builder.call()).to eql(fetcher_builder.call())
27
+ end
28
+
29
+ it "should not eql? an differently configured instance" do
30
+ expect(fetcher_builder.call()).not_to eql(fetcher_builder_different.call())
31
+ end
32
+
33
+ context "the object returned by #fetch" do
34
+ subject { fetcher.fetch }
35
+
36
+ it_should_behave_like "an IO-like object"
37
+
38
+ it "should return the expected data when #read" do
39
+ expect(subject.read()).to eq(expected_data)
40
+ end
41
+ end
42
+ end
43
+
44
+ shared_examples_for "a #fetch call that failed" do
45
+ it "should raise Threatinator::Exceptions::FetchFailed" do
46
+ expect { fetcher.fetch() }.to raise_error(Threatinator::Exceptions::FetchFailed)
47
+ end
48
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples_for "a filter" do
4
+ # expects :filter, :should_filter, and :shouldnt_filter
5
+
6
+ describe "#filter?" do
7
+ it "should return true for data that is meant to be filtered" do
8
+ expect(filter.filter?(should_filter)).to eq(true)
9
+ end
10
+ it "should return false for data that is meant to be filtered" do
11
+ expect(filter.filter?(shouldnt_filter)).to eq(false)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,7 @@
1
+
2
+ shared_examples_for "an IO-like object" do
3
+ it { is_expected.to respond_to(:close) }
4
+ it { is_expected.to respond_to(:read) }
5
+ it { is_expected.to respond_to(:gets) }
6
+ it { is_expected.to respond_to(:pos) }
7
+ end
@@ -0,0 +1,164 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples_for "a model collection" do
4
+ # expect generate_ten_valid_members, generate_invalid_members
5
+ let(:collection) { described_class.new }
6
+
7
+ describe "initialize" do
8
+ context "when no parameters are provided" do
9
+ specify "the collection is empty" do
10
+ x = described_class.new
11
+ expect(x).to be_empty
12
+ end
13
+ end
14
+
15
+ context "when an array of members is provided" do
16
+ it "calls #<< for each member, adding it to the collection" do
17
+ members = generate_ten_valid_members()
18
+ expect_any_instance_of(described_class).to receive(:<<).exactly(10).times.and_call_original
19
+ x = described_class.new(members)
20
+ expect(x.to_a).to match_array(members)
21
+ end
22
+ end
23
+ end
24
+ describe "#<<(member)" do
25
+ it "adds the member to the collection" do
26
+ member = generate_ten_valid_members()[0]
27
+ expect(collection.include?(member)).to eq(false)
28
+ collection << member
29
+ expect(collection.include?(member)).to eq(true)
30
+ end
31
+
32
+ it "does not add the member if it is equal to any member of the collection" do
33
+ member = generate_ten_valid_members()[0]
34
+ expect(collection.count).to eq(0)
35
+ collection << member
36
+ expect(collection.count).to eq(1)
37
+ collection << member
38
+ expect(collection.count).to eq(1)
39
+ end
40
+
41
+ it "raises InvalidAttributeError if the added member is not valid for the collection" do
42
+ expect {
43
+ collection << generate_invalid_members()[0]
44
+ }.to raise_error(Threatinator::Exceptions::InvalidAttributeError)
45
+ end
46
+ end
47
+
48
+ describe "#valid_member?(member)" do
49
+ it "returns true if the member is valid" do
50
+ expect(collection.valid_member?(generate_ten_valid_members()[0])).to eq(true)
51
+ end
52
+ it "returns false if the member is not valid" do
53
+ expect(collection.valid_member?(generate_invalid_members()[0])).to eq(false)
54
+ end
55
+ end
56
+
57
+ describe "#include?(member)" do
58
+ let(:member) { generate_ten_valid_members()[0] }
59
+ it "returns true if the member is in the collection" do
60
+ collection << member
61
+ expect(collection).to include(member)
62
+ end
63
+
64
+ it "returns false if the member is not in the collection" do
65
+ expect(collection).not_to include(member)
66
+ end
67
+ end
68
+
69
+ describe "#empty?" do
70
+ it "returns true when the collection is empty" do
71
+ expect(collection).to be_empty
72
+ end
73
+ it "returns false when something has been added to the collection" do
74
+ member = generate_ten_valid_members()[0]
75
+ collection << member
76
+ expect(collection).not_to be_empty
77
+ end
78
+ end
79
+
80
+ describe "#count" do
81
+ it "returns 0 when the collection is empty" do
82
+ expect(collection.count).to eq(0)
83
+ end
84
+
85
+ it "returns the number of members in the collection" do
86
+ members = generate_ten_valid_members()
87
+ members.each do |member|
88
+ collection << member
89
+ end
90
+ expect(collection.count).to eq(members.count)
91
+ end
92
+ end
93
+
94
+ [:to_ary, :to_a].each do |method_name|
95
+ describe "##{method_name}" do
96
+ it "returns an empty array instance when the collection is empty" do
97
+ expect(collection.send(method_name)).to eq([])
98
+ end
99
+
100
+ it "returns an array containing members that have been added" do
101
+ members = generate_ten_valid_members()
102
+ members.each do |member|
103
+ collection << member
104
+ end
105
+ expect(collection.send(method_name)).to match_array(members)
106
+ end
107
+ end
108
+ end
109
+
110
+ describe "#each" do
111
+ it "returns an Enumerator if no block is provided" do
112
+ expect(collection.each).to be_a(::Enumerator)
113
+ end
114
+
115
+ it "yields each member of the collection" do
116
+ members = generate_ten_valid_members()
117
+ members.each do |member|
118
+ collection << member
119
+ end
120
+
121
+ actual = []
122
+ collection.each do |member|
123
+ actual << member
124
+ end
125
+
126
+ expect(actual).to match_array(members)
127
+ end
128
+ end
129
+
130
+ describe "#==(other)" do
131
+ before :each do
132
+ end
133
+
134
+ it "returns true when compared to itself" do
135
+ collection = described_class.new(generate_ten_valid_members())
136
+ expect(collection).to be == collection
137
+ end
138
+
139
+ it "returns true when compared to a collection with equal members" do
140
+ collection = described_class.new(generate_ten_valid_members())
141
+ collection2 = described_class.new(generate_ten_valid_members())
142
+ expect(collection).to be == collection2
143
+ end
144
+
145
+ it "returns false if the number of members is different" do
146
+ collection = described_class.new(generate_ten_valid_members()[0..5])
147
+ collection2 = described_class.new(generate_ten_valid_members())
148
+ expect(collection).not_to be == collection2
149
+ end
150
+
151
+ it "returns false if the members are different" do
152
+ collection = described_class.new(generate_ten_valid_members()[0..4])
153
+ collection2 = described_class.new(generate_ten_valid_members()[5..9])
154
+ expect(collection).not_to be == collection2
155
+ end
156
+
157
+ it "returns false when compared to something other than a collection" do
158
+ members = generate_ten_valid_members()
159
+ collection = described_class.new(members)
160
+ expect(collection).not_to be == members
161
+ end
162
+ end
163
+ end
164
+
@@ -0,0 +1,120 @@
1
+ require 'spec_helper'
2
+ require 'tempfile'
3
+ require 'threatinator/plugin_loader'
4
+
5
+ shared_examples_for "an output plugin" do |name|
6
+ let(:output) { described_class.new(config) }
7
+
8
+ specify "the plugin loader should find the class by (#{name.inspect})" do
9
+ loader = Threatinator::PluginLoader.new
10
+ loader.load_plugins(:output)
11
+ expect(loader.get(:output, name.to_sym)).to be(described_class)
12
+ end
13
+ end
14
+
15
+ shared_examples_for "a file-based output plugin" do |name|
16
+ it_should_behave_like "an output plugin", name
17
+
18
+ let(:event) { build(:event) }
19
+
20
+ describe "config.io => IO" do
21
+ let(:output) { described_class.new(config) }
22
+ let(:io) { StringIO.new }
23
+ before :each do
24
+ config.io = io
25
+ end
26
+
27
+ describe "#handle_event" do
28
+ before :each do
29
+ output.handle_event(event)
30
+ end
31
+
32
+ it "writes data to the provided IO" do
33
+ expect(io.string).not_to be_empty
34
+ end
35
+ end
36
+
37
+ describe "#finish" do
38
+ before :each do
39
+ allow(io).to receive(:close)
40
+ output.finish
41
+ end
42
+
43
+ it "closes the IO" do
44
+ expect(io).to have_received(:close)
45
+ end
46
+ end
47
+ end
48
+
49
+ describe "config.filename => String" do
50
+ let(:output) { described_class.new(config) }
51
+ before :each do
52
+ @tempfile = Tempfile.new("asdf")
53
+ @tempfile.close
54
+ @filepath = @tempfile.path
55
+ config.filename = @filepath
56
+ end
57
+
58
+ let(:filepath) { @filepath }
59
+
60
+ describe "#finish" do
61
+ it "closes the filehandle" do
62
+ expect(output.instance_variable_get(:"@output_io")).to receive(:close)
63
+ output.finish
64
+ end
65
+ end
66
+
67
+ describe "#handle_event" do
68
+ before :each do
69
+ output.handle_event(event)
70
+ end
71
+ specify "writes data to the path specified by :filename" do
72
+ output.finish
73
+ expect(File.read(@filepath)).not_to be_empty
74
+ end
75
+ end
76
+ end
77
+
78
+ describe "without specifying config.io or config.filename" do
79
+ before :each do
80
+ @orig_stdout = $stdout
81
+ @duped_io = StringIO.new
82
+ $stdout = double("stdout").as_null_object
83
+ allow($stdout).to receive(:dup).and_return(@duped_io)
84
+ @output = described_class.new(config)
85
+ end
86
+
87
+ let(:output) { @output }
88
+
89
+ after :each do
90
+ $stdout = @orig_stdout
91
+ end
92
+
93
+ it "uses a duplicate of $stdout" do
94
+ expect($stdout).to have_received(:dup)
95
+ end
96
+
97
+ describe "#handle_event" do
98
+ before :each do
99
+ output.handle_event(event)
100
+ end
101
+
102
+ it "writes to the dupe of $stdout" do
103
+ expect(@duped_io.string).not_to be_empty
104
+ end
105
+ end
106
+
107
+ describe "#finish" do
108
+ before :each do
109
+ allow(@duped_io).to receive(:close).and_call_original
110
+ output.finish
111
+ end
112
+
113
+ it "closes the dup of $stdout" do
114
+ expect(@duped_io).to have_received(:close)
115
+ end
116
+ end
117
+
118
+ end
119
+ end
120
+
@@ -0,0 +1,51 @@
1
+ shared_examples_for "a parser when compared to an identically configured parser" do
2
+ it "parser1 should eql? parser2" do
3
+ expect(parser1).to eql(parser2)
4
+ end
5
+ it "parser2 should eql? parser1" do
6
+ expect(parser2).to eql(parser1)
7
+ end
8
+ it "parser1 should == parser2" do
9
+ expect(parser1).to be == parser2
10
+ end
11
+ it "parser2 should == parser1" do
12
+ expect(parser2).to be == parser1
13
+ end
14
+ it "parser1 should not be parser2" do
15
+ expect(parser1).not_to be parser2
16
+ end
17
+ it "parser2 should not be parser1" do
18
+ expect(parser2).not_to be parser1
19
+ end
20
+ end
21
+
22
+ shared_examples_for "a parser when compared to a differently configured parser" do
23
+ it "parser1 should not eql? parser2" do
24
+ expect(parser1).not_to eql(parser2)
25
+ end
26
+ it "parser2 should not eql? parser1" do
27
+ expect(parser2).not_to eql(parser1)
28
+ end
29
+ it "parser1 should not == parser2" do
30
+ expect(parser1).not_to be == parser2
31
+ end
32
+ it "parser2 should not == parser1" do
33
+ expect(parser2).not_to be == parser1
34
+ end
35
+ it "parser1 should not be parser2" do
36
+ expect(parser1).not_to be parser2
37
+ end
38
+ it "parser2 should not be parser1" do
39
+ expect(parser2).not_to be parser1
40
+ end
41
+ end
42
+
43
+ module ParserHelpers
44
+ def parser_data(filename)
45
+ PARSER_FIXTURES.join(filename).to_s
46
+ end
47
+ end
48
+
49
+ shared_context "a parser", :parser do
50
+ include ParserHelpers
51
+ end