shadowbq-threatinator 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +66 -0
- data/CONTRIBUTING.md +119 -0
- data/Gemfile +38 -0
- data/LICENSE +165 -0
- data/README.md +101 -0
- data/Rakefile +47 -0
- data/VERSION +1 -0
- data/bin/threatinator +5 -0
- data/bin/threatinator_loader +21 -0
- data/feeds/ET_block-ip_reputation.feed +27 -0
- data/feeds/ET_compromised-ip_reputation.feed +20 -0
- data/feeds/ET_openbadlist-ip_reputation.feed +36 -0
- data/feeds/alienvault-ip_reputation.feed +39 -0
- data/feeds/arbor_fastflux-domain_reputation.feed +19 -0
- data/feeds/arbor_ssh-ip_reputation.feed +24 -0
- data/feeds/autoshun_shunlist.feed +17 -0
- data/feeds/bambenek_c2_masterlist-domain_reputation.feed +16 -0
- data/feeds/bambenek_c2_masterlist-ip_reputation.feed +16 -0
- data/feeds/bambenek_dga_feed-domain_reputation.feed +16 -0
- data/feeds/berkeley-ip_reputation.feed +25 -0
- data/feeds/bitcash_cz_blacklist.feed +22 -0
- data/feeds/blocklist_de_apache-ip_reputation.feed +26 -0
- data/feeds/blocklist_de_bots-ip_reputation.feed +26 -0
- data/feeds/blocklist_de_ftp-ip_reputation.feed +25 -0
- data/feeds/blocklist_de_imap-ip_reputation.feed +25 -0
- data/feeds/blocklist_de_pop3-ip_reputation.feed +26 -0
- data/feeds/blocklist_de_proftpd-ip_reputation.feed +26 -0
- data/feeds/blocklist_de_sip-ip_reputation.feed +25 -0
- data/feeds/blocklist_de_ssh-ip_reputation.feed +25 -0
- data/feeds/blocklist_de_strongips-ip_reputation.feed +25 -0
- data/feeds/botscout-ip_reputation.feed +25 -0
- data/feeds/cert_mxpoison-ip_reputation.feed +22 -0
- data/feeds/chaosreigns-ip_reputation.feed +37 -0
- data/feeds/ciarmy-ip_reputation.feed +20 -0
- data/feeds/cruzit-ip_reputation.feed +30 -0
- data/feeds/cydef_torexit-ip_reputation.feed +25 -0
- data/feeds/dan_me_uk_torlist-ip_reputation.feed +25 -0
- data/feeds/danger_bruteforce-ip_reputation.feed +24 -0
- data/feeds/dshield_attackers-top1000.feed +34 -0
- data/feeds/falconcrest-ip_reputation.feed +19 -0
- data/feeds/feodo-domain_reputation.feed +19 -0
- data/feeds/feodo-ip_reputation.feed +20 -0
- data/feeds/h3x_asprox.feed +18 -0
- data/feeds/hosts-file_hphostspartial-domain_reputation.feed +19 -0
- data/feeds/infiltrated-ip_reputation.feed +26 -0
- data/feeds/infiltrated_vabl-ip_reputation.feed +30 -0
- data/feeds/isc_suspicious_high-domain_reputation.feed +26 -0
- data/feeds/isc_suspicious_low-domain_reputation.feed +26 -0
- data/feeds/isc_suspicious_medium-domain_reputation.feed +26 -0
- data/feeds/malc0de-domain_reputation.feed +24 -0
- data/feeds/malc0de-ip_reputation.feed +26 -0
- data/feeds/malwaredomainlist-url_reputation.feed +18 -0
- data/feeds/malwaredomains-domain_reputation.feed +29 -0
- data/feeds/malwaredomains_dyndns-domain_reputation.feed +29 -0
- data/feeds/malwaredomains_justdomains-domain_reputation.feed +20 -0
- data/feeds/mirc-domain_reputation.feed +30 -0
- data/feeds/multiproxy-ip_reputation.feed +22 -0
- data/feeds/nothink_irc-ip_reputation.feed +23 -0
- data/feeds/nothink_ssh-ip_reputation.feed +21 -0
- data/feeds/openbl-ip_reputation.feed +21 -0
- data/feeds/openphish-url_reputation.feed +24 -0
- data/feeds/packetmail_perimeterbad-ip_reputation.feed +28 -0
- data/feeds/palevo-domain_reputation.feed +22 -0
- data/feeds/palevo-ip_reputation.feed +23 -0
- data/feeds/phishtank.feed +22 -0
- data/feeds/sigmaproject_atma.feed +27 -0
- data/feeds/sigmaproject_spyware.feed +28 -0
- data/feeds/sigmaproject_webexploit.feed +26 -0
- data/feeds/snort_bpf-ip_reputation.feed +19 -0
- data/feeds/spyeye-domain_reputation.feed +18 -0
- data/feeds/spyeye-ip_reputation.feed +19 -0
- data/feeds/steeman-ip_reputation.feed +20 -0
- data/feeds/t-arend-de_ssh-ip_reputation.feed +20 -0
- data/feeds/the_haleys_ssh-ip_reputation.feed +20 -0
- data/feeds/trustedsec-ip_reputation.feed +18 -0
- data/feeds/virbl-ip_reputation.feed +25 -0
- data/feeds/vxvault-url_reputation.feed +23 -0
- data/feeds/yourcmc_ssh-ip_reputation.feed +20 -0
- data/feeds/yoyo_adservers-domain_reputation.feed +17 -0
- data/feeds/zeus-domain_reputation.feed +19 -0
- data/feeds/zeus-ip_reputation.feed +21 -0
- data/lib/threatinator/action.rb +14 -0
- data/lib/threatinator/actions/list/action.rb +97 -0
- data/lib/threatinator/actions/list/config.rb +12 -0
- data/lib/threatinator/actions/list.rb +2 -0
- data/lib/threatinator/actions/run/action.rb +57 -0
- data/lib/threatinator/actions/run/config.rb +32 -0
- data/lib/threatinator/actions/run/coverage_observer.rb +59 -0
- data/lib/threatinator/actions/run/output_config.rb +59 -0
- data/lib/threatinator/actions/run/status_observer.rb +37 -0
- data/lib/threatinator/actions/run.rb +2 -0
- data/lib/threatinator/cli/action_builder.rb +33 -0
- data/lib/threatinator/cli/list_action_builder.rb +19 -0
- data/lib/threatinator/cli/parser.rb +123 -0
- data/lib/threatinator/cli/run_action_builder.rb +41 -0
- data/lib/threatinator/cli.rb +19 -0
- data/lib/threatinator/config/base.rb +35 -0
- data/lib/threatinator/config/feed_search.rb +25 -0
- data/lib/threatinator/config/logger.rb +14 -0
- data/lib/threatinator/config.rb +7 -0
- data/lib/threatinator/decoder.rb +24 -0
- data/lib/threatinator/decoders/gzip.rb +30 -0
- data/lib/threatinator/event.rb +63 -0
- data/lib/threatinator/event_builder.rb +70 -0
- data/lib/threatinator/exceptions.rb +58 -0
- data/lib/threatinator/feed.rb +88 -0
- data/lib/threatinator/feed_builder.rb +161 -0
- data/lib/threatinator/feed_registry.rb +47 -0
- data/lib/threatinator/feed_runner.rb +177 -0
- data/lib/threatinator/fetcher.rb +22 -0
- data/lib/threatinator/fetchers/http.rb +50 -0
- data/lib/threatinator/filter.rb +12 -0
- data/lib/threatinator/filters/block.rb +18 -0
- data/lib/threatinator/filters/comments.rb +16 -0
- data/lib/threatinator/filters/whitespace.rb +19 -0
- data/lib/threatinator/logger.rb +66 -0
- data/lib/threatinator/logging.rb +20 -0
- data/lib/threatinator/model/base.rb +23 -0
- data/lib/threatinator/model/collection.rb +89 -0
- data/lib/threatinator/model/observables/fqdn_collection.rb +15 -0
- data/lib/threatinator/model/observables/ipv4.rb +33 -0
- data/lib/threatinator/model/observables/ipv4_collection.rb +14 -0
- data/lib/threatinator/model/observables/url_collection.rb +16 -0
- data/lib/threatinator/model/validations/type.rb +21 -0
- data/lib/threatinator/model/validations.rb +1 -0
- data/lib/threatinator/output.rb +50 -0
- data/lib/threatinator/parser.rb +23 -0
- data/lib/threatinator/parsers/csv/parser.rb +77 -0
- data/lib/threatinator/parsers/csv.rb +7 -0
- data/lib/threatinator/parsers/getline/parser.rb +45 -0
- data/lib/threatinator/parsers/getline.rb +8 -0
- data/lib/threatinator/parsers/json/adapters/oj.rb +65 -0
- data/lib/threatinator/parsers/json/parser.rb +45 -0
- data/lib/threatinator/parsers/json/record.rb +20 -0
- data/lib/threatinator/parsers/json.rb +8 -0
- data/lib/threatinator/parsers/xml/node.rb +79 -0
- data/lib/threatinator/parsers/xml/node_builder.rb +39 -0
- data/lib/threatinator/parsers/xml/parser.rb +44 -0
- data/lib/threatinator/parsers/xml/path.rb +70 -0
- data/lib/threatinator/parsers/xml/pattern.rb +53 -0
- data/lib/threatinator/parsers/xml/record.rb +14 -0
- data/lib/threatinator/parsers/xml/sax_document.rb +64 -0
- data/lib/threatinator/parsers/xml.rb +8 -0
- data/lib/threatinator/plugin_loader.rb +115 -0
- data/lib/threatinator/plugins/output/amqp/config.rb +18 -0
- data/lib/threatinator/plugins/output/amqp.rb +41 -0
- data/lib/threatinator/plugins/output/csv.rb +58 -0
- data/lib/threatinator/plugins/output/json/config.rb +14 -0
- data/lib/threatinator/plugins/output/json.rb +53 -0
- data/lib/threatinator/plugins/output/null.rb +17 -0
- data/lib/threatinator/plugins/output/rubydebug.rb +16 -0
- data/lib/threatinator/record.rb +22 -0
- data/lib/threatinator/registry.rb +53 -0
- data/lib/threatinator/util.rb +15 -0
- data/lib/threatinator.rb +3 -0
- data/spec/feeds/ET_block-ip_reputation_spec.rb +50 -0
- data/spec/feeds/ET_compromised-ip_reputation_spec.rb +47 -0
- data/spec/feeds/ET_openbadlist-ip_reputation_spec.rb +56 -0
- data/spec/feeds/alienvault-ip_reputation_spec.rb +46 -0
- data/spec/feeds/arbor_fastflux-domain_reputation_spec.rb +46 -0
- data/spec/feeds/arbor_ssh-ip_reputation_spec.rb +46 -0
- data/spec/feeds/autoshun_shunlist_spec.rb +38 -0
- data/spec/feeds/bambenek_c2_masterlist-domain_reputation_spec.rb +38 -0
- data/spec/feeds/bambenek_c2_masterlist-ip_reputation_spec.rb +39 -0
- data/spec/feeds/bambenek_dga_feed-domain_reputation_spec.rb +39 -0
- data/spec/feeds/berkeley-ip_reputation_spec.rb +47 -0
- data/spec/feeds/bitcash_cz_blacklist-ip_reputation_spec.rb +50 -0
- data/spec/feeds/blocklist_de_apache-ip_reputation_spec.rb +47 -0
- data/spec/feeds/blocklist_de_bots-ip_reputation_spec.rb +47 -0
- data/spec/feeds/blocklist_de_ftp-ip_reputation_spec.rb +47 -0
- data/spec/feeds/blocklist_de_imap-ip_reputation_spec.rb +47 -0
- data/spec/feeds/blocklist_de_pop3-ip_reputation_spec.rb +47 -0
- data/spec/feeds/blocklist_de_proftpd-ip_reputation_spec.rb +47 -0
- data/spec/feeds/blocklist_de_sip-ip_reputation_spec.rb +47 -0
- data/spec/feeds/blocklist_de_ssh-ip_reputation_spec.rb +47 -0
- data/spec/feeds/blocklist_de_strongips-ip_reputation_spec.rb +47 -0
- data/spec/feeds/botscout-ip_reputation_spec.rb +50 -0
- data/spec/feeds/cert_mxpoison-ip_reputation_spec.rb +47 -0
- data/spec/feeds/chaosreigns-ip_reputation_spec.rb +50 -0
- data/spec/feeds/ciarmy-ip_reputation_spec.rb +47 -0
- data/spec/feeds/cruzit-ip_reputation_spec.rb +47 -0
- data/spec/feeds/cydef_torexit-ip_reputation_spec.rb +47 -0
- data/spec/feeds/dan_me_uk_torlist-ip_reputation_spec.rb +47 -0
- data/spec/feeds/danger_bruteforce-ip_reputation_spec.rb +47 -0
- data/spec/feeds/data/ET_block-ip_reputation.txt +80 -0
- data/spec/feeds/data/ET_compromised-ip_reputation.txt +11 -0
- data/spec/feeds/data/ET_openbadlist-ip_reputation.txt +62 -0
- data/spec/feeds/data/alienvault-ip_reputation.txt +18 -0
- data/spec/feeds/data/arbor_domainlist.txt +11 -0
- data/spec/feeds/data/arbor_ssh.txt +16 -0
- data/spec/feeds/data/autoshun_shunlist.csv +20 -0
- data/spec/feeds/data/bambenek_c2-dommasterlist.csv +30 -0
- data/spec/feeds/data/bambenek_c2-ipmasterlist.csv +27 -0
- data/spec/feeds/data/bambenek_dga_feed.csv +42 -0
- data/spec/feeds/data/berkeley.txt +29 -0
- data/spec/feeds/data/bitcash_cz_blacklist.txt +7 -0
- data/spec/feeds/data/blocklist_de_apache-ip-reputation.txt +17 -0
- data/spec/feeds/data/blocklist_de_bots-ip-reputation.txt +15 -0
- data/spec/feeds/data/blocklist_de_ftp-ip-reputation.txt +7 -0
- data/spec/feeds/data/blocklist_de_imap-ip-reputation.txt +8 -0
- data/spec/feeds/data/blocklist_de_pop3-ip-reputation.txt +11 -0
- data/spec/feeds/data/blocklist_de_proftpd-ip-reputation.txt +12 -0
- data/spec/feeds/data/blocklist_de_sip-ip-reputation.txt +9 -0
- data/spec/feeds/data/blocklist_de_ssh-ip-reputation.txt +10 -0
- data/spec/feeds/data/blocklist_de_strongips-ip-reputation.txt +11 -0
- data/spec/feeds/data/botscout-ip-reputation.txt +713 -0
- data/spec/feeds/data/cert_mxpoison-ip_reputation.txt +17 -0
- data/spec/feeds/data/chaosreigns-ip-reputation.txt +26 -0
- data/spec/feeds/data/ciarmy-ip-reputation.txt +11 -0
- data/spec/feeds/data/cruzit-ip-reputation.txt +14 -0
- data/spec/feeds/data/cydef_torexit-ip_reputation.txt +27 -0
- data/spec/feeds/data/dan_me_uk_torlist-ip-reputation.txt +11 -0
- data/spec/feeds/data/danger_bruteforce-ip_reputation.txt +12 -0
- data/spec/feeds/data/dshield_topattackers.xml +4 -0
- data/spec/feeds/data/falconcrest_iplist.txt +345 -0
- data/spec/feeds/data/feodo_domainlist.txt +18 -0
- data/spec/feeds/data/feodo_iplist.txt +20 -0
- data/spec/feeds/data/h3x_asprox.txt +20 -0
- data/spec/feeds/data/hosts-file_hphostspartial_domainlist.txt +24 -0
- data/spec/feeds/data/infiltrated_iplist.txt +16 -0
- data/spec/feeds/data/infiltrated_vabl_iplist.txt +33 -0
- data/spec/feeds/data/isc_suspicious_high_domainlist.txt +26 -0
- data/spec/feeds/data/isc_suspicious_low_domainlist.txt +34 -0
- data/spec/feeds/data/isc_suspicious_medium_domainlist.txt +32 -0
- data/spec/feeds/data/malc0de_domainlist.txt +18 -0
- data/spec/feeds/data/malc0de_iplist.txt +14 -0
- data/spec/feeds/data/malwaredomainlist-url-reputation.txt +8 -0
- data/spec/feeds/data/malwaredomains_domainlist.txt +24 -0
- data/spec/feeds/data/malwaredomains_dyndns_domainlist.txt +34 -0
- data/spec/feeds/data/malwaredomains_justdomains_domainlist.txt +18 -0
- data/spec/feeds/data/mirc_domainlist.txt +31 -0
- data/spec/feeds/data/multiproxy_iplist.txt +15 -0
- data/spec/feeds/data/nothink_irc_iplist.txt +14 -0
- data/spec/feeds/data/nothink_ssh_iplist.txt +10 -0
- data/spec/feeds/data/openbl_iplist.txt +12 -0
- data/spec/feeds/data/openphish-url-reputation.txt +16 -0
- data/spec/feeds/data/packetmail_perimeterbad-ip_reputation.txt +44 -0
- data/spec/feeds/data/palevo_domainlist.txt +25 -0
- data/spec/feeds/data/palevo_iplist.txt +24 -0
- data/spec/feeds/data/phishtank-sample.json.gz +0 -0
- data/spec/feeds/data/sigmaproject_atma.return.gz +0 -0
- data/spec/feeds/data/sigmaproject_spyware.return.gz +0 -0
- data/spec/feeds/data/sigmaproject_webexploit.return.gz +0 -0
- data/spec/feeds/data/snort_bpf-ip_reputation.txt +16 -0
- data/spec/feeds/data/spyeye_domainlist.txt +16 -0
- data/spec/feeds/data/spyeye_iplist.txt +19 -0
- data/spec/feeds/data/steeman-ip-reputation.txt +13 -0
- data/spec/feeds/data/t-arend-de_ssh_iplist.txt +17 -0
- data/spec/feeds/data/the_haleys_ssh_iplist.txt +12 -0
- data/spec/feeds/data/trustedsec-ip-reputation.txt +12 -0
- data/spec/feeds/data/valid.json +2908 -0
- data/spec/feeds/data/virbl-ip_reputation.txt +14 -0
- data/spec/feeds/data/vxvault-url-reputation.txt +15 -0
- data/spec/feeds/data/yourcmc_ssh-ip_reputation.txt +27 -0
- data/spec/feeds/data/yoyo_adservers.txt +25 -0
- data/spec/feeds/data/zeus-ip_reputation.txt +285 -0
- data/spec/feeds/data/zeus_domainlist.txt +27 -0
- data/spec/feeds/dshield_attackers-top1000_spec.rb +39 -0
- data/spec/feeds/falconcrest-ip_reputation_spec.rb +39 -0
- data/spec/feeds/feodo-domain_reputation_spec.rb +47 -0
- data/spec/feeds/feodo-ip_reputation_spec.rb +47 -0
- data/spec/feeds/h3x_asprox-ip_reputation_spec.rb +50 -0
- data/spec/feeds/hosts-file_hphostspartial-domain_reputation_spec.rb +47 -0
- data/spec/feeds/infiltrated-ip_reputation_spec.rb +47 -0
- data/spec/feeds/infiltrated_vabl-ip_reputation_spec.rb +47 -0
- data/spec/feeds/isc_suspicious_high-domain_reputation_spec.rb +47 -0
- data/spec/feeds/isc_suspicious_low-domain_reputation_spec.rb +47 -0
- data/spec/feeds/isc_suspicious_medium-domain_reputation_spec.rb +47 -0
- data/spec/feeds/malc0de-domain_reputation_spec.rb +47 -0
- data/spec/feeds/malc0de-ip_reputation_spec.rb +47 -0
- data/spec/feeds/malwaredomainlist_url_reputation_spec.rb +50 -0
- data/spec/feeds/malwaredomains-domain_reputation_spec.rb +47 -0
- data/spec/feeds/malwaredomains_dyndns-domain_reputation_spec.rb +47 -0
- data/spec/feeds/malwaredomains_justdomains-domain_reputation_spec.rb +47 -0
- data/spec/feeds/mirc-domain_reputation_spec.rb +47 -0
- data/spec/feeds/multiproxy-ip_reputation_spec.rb +47 -0
- data/spec/feeds/nothink_irc-ip_reputation_spec.rb +47 -0
- data/spec/feeds/nothink_ssh-ip_reputation_spec.rb +47 -0
- data/spec/feeds/openbl-ip_reputation_spec.rb +47 -0
- data/spec/feeds/openphish_url_reputation_spec.rb +50 -0
- data/spec/feeds/packetmail_perimeterbad-ip_reputation_spec.rb +47 -0
- data/spec/feeds/palevo-domain_reputation_spec.rb +47 -0
- data/spec/feeds/palevo-ip_reputation_spec.rb +47 -0
- data/spec/feeds/phishtank_spec.rb +41 -0
- data/spec/feeds/sigmaproject_atma_spec.rb +62 -0
- data/spec/feeds/sigmaproject_spyware_spec.rb +63 -0
- data/spec/feeds/sigmaproject_webexploit_spec.rb +62 -0
- data/spec/feeds/snort_bpf-ip_reputation_spec.rb +47 -0
- data/spec/feeds/spyeye-domain_reputation_spec.rb +47 -0
- data/spec/feeds/spyeye-ip_reputation_spec.rb +47 -0
- data/spec/feeds/steeman-ip_reputation_spec.rb +50 -0
- data/spec/feeds/t-arend-de_ssh-ip_reputation_spec.rb +47 -0
- data/spec/feeds/the_haleys_ssh-ip_reputation_spec.rb +47 -0
- data/spec/feeds/trustedsec-ip_reputation_spec.rb +47 -0
- data/spec/feeds/virbl-ip_reputation_spec.rb +47 -0
- data/spec/feeds/vxvault_url_reputation_spec.rb +50 -0
- data/spec/feeds/yourcmc_ssh-ip_reputation_spec.rb +47 -0
- data/spec/feeds/yoyo_adservers_spec.rb +47 -0
- data/spec/feeds/zeus-domain_reputation_spec.rb +47 -0
- data/spec/feeds/zeus-ip_reputation_spec.rb +47 -0
- data/spec/fixtures/feed/provider1/feed1.feed +6 -0
- data/spec/fixtures/parsers/test.xml +13 -0
- data/spec/fixtures/parsers/test_self_closing.xml +20 -0
- data/spec/fixtures/plugins/bad/threatinator/plugins/test_error1/plugin.rb +1 -0
- data/spec/fixtures/plugins/bad/threatinator/plugins/test_missing1/plugin.rb +0 -0
- data/spec/fixtures/plugins/fake.rb +19 -0
- data/spec/fixtures/plugins/good/threatinator/plugins/test_type1/plugin_a.rb +8 -0
- data/spec/fixtures/plugins/good/threatinator/plugins/test_type1/plugin_b.rb +8 -0
- data/spec/fixtures/plugins/good/threatinator/plugins/test_type2/plugin_c.rb +8 -0
- data/spec/fixtures/plugins/good/threatinator/plugins/test_type2/plugin_d.rb +8 -0
- data/spec/fixtures/plugins/good/threatinator/plugins/test_type3/plugin_e.rb +8 -0
- data/spec/fixtures/plugins/good/threatinator/plugins/test_type3/plugin_f.rb +8 -0
- data/spec/spec_helper.rb +54 -0
- data/spec/support/bad_feeds/missing_fetcher.feed +7 -0
- data/spec/support/bad_feeds/missing_name.feed +6 -0
- data/spec/support/bad_feeds/missing_parser.feed +3 -0
- data/spec/support/bad_feeds/missing_provider.feed +5 -0
- data/spec/support/factories/event.rb +31 -0
- data/spec/support/factories/feed.rb +59 -0
- data/spec/support/factories/feed_builder.rb +65 -0
- data/spec/support/factories/feed_registry.rb +8 -0
- data/spec/support/factories/ipv4.rb +36 -0
- data/spec/support/factories/output.rb +11 -0
- data/spec/support/factories/record.rb +17 -0
- data/spec/support/factories/url.rb +34 -0
- data/spec/support/factories/xml_node.rb +33 -0
- data/spec/support/helpers/io.rb +11 -0
- data/spec/support/helpers/models.rb +13 -0
- data/spec/support/shared/action_builder.rb +47 -0
- data/spec/support/shared/decoder.rb +70 -0
- data/spec/support/shared/feed_runner_observer.rb +136 -0
- data/spec/support/shared/feeds.rb +233 -0
- data/spec/support/shared/fetcher.rb +48 -0
- data/spec/support/shared/filter.rb +14 -0
- data/spec/support/shared/io-like.rb +7 -0
- data/spec/support/shared/model/collection.rb +164 -0
- data/spec/support/shared/output.rb +120 -0
- data/spec/support/shared/parsers.rb +51 -0
- data/spec/support/shared/record.rb +111 -0
- data/spec/threatinator/actions/list/action_spec.rb +148 -0
- data/spec/threatinator/actions/run/action_spec.rb +106 -0
- data/spec/threatinator/actions/run/config_spec.rb +39 -0
- data/spec/threatinator/actions/run/coverage_observer_spec.rb +151 -0
- data/spec/threatinator/actions/run/output_config_spec.rb +89 -0
- data/spec/threatinator/actions/run/status_observer_spec.rb +86 -0
- data/spec/threatinator/cli/list_action_builder_spec.rb +57 -0
- data/spec/threatinator/cli/run_action_builder_spec.rb +133 -0
- data/spec/threatinator/cli_spec.rb +175 -0
- data/spec/threatinator/config/base_spec.rb +39 -0
- data/spec/threatinator/config/feed_search_spec.rb +76 -0
- data/spec/threatinator/decoders/gzip_spec.rb +75 -0
- data/spec/threatinator/event_builder_spec.rb +123 -0
- data/spec/threatinator/event_spec.rb +254 -0
- data/spec/threatinator/event_spec.rb.new +319 -0
- data/spec/threatinator/feed_builder_spec.rb +633 -0
- data/spec/threatinator/feed_registry_spec.rb +198 -0
- data/spec/threatinator/feed_runner_spec.rb +372 -0
- data/spec/threatinator/feed_spec.rb +169 -0
- data/spec/threatinator/fetcher_spec.rb +12 -0
- data/spec/threatinator/fetchers/http_spec.rb +32 -0
- data/spec/threatinator/filter_spec.rb +13 -0
- data/spec/threatinator/filters/block_spec.rb +16 -0
- data/spec/threatinator/filters/comments_spec.rb +13 -0
- data/spec/threatinator/filters/whitespace_spec.rb +12 -0
- data/spec/threatinator/logger_spec.rb +29 -0
- data/spec/threatinator/model/observables/fqdn_collection_spec.rb +41 -0
- data/spec/threatinator/model/observables/ipv4_collection_spec.rb +36 -0
- data/spec/threatinator/model/observables/ipv4_spec.rb +75 -0
- data/spec/threatinator/model/observables/url_collection_spec.rb +45 -0
- data/spec/threatinator/model/validations/type_spec.rb +37 -0
- data/spec/threatinator/parser_spec.rb +13 -0
- data/spec/threatinator/parsers/csv/parser_spec.rb +202 -0
- data/spec/threatinator/parsers/getline/parser_spec.rb +83 -0
- data/spec/threatinator/parsers/json/parser_spec.rb +106 -0
- data/spec/threatinator/parsers/json/record_spec.rb +30 -0
- data/spec/threatinator/parsers/xml/node_spec.rb +335 -0
- data/spec/threatinator/parsers/xml/parser_spec.rb +263 -0
- data/spec/threatinator/parsers/xml/path_spec.rb +209 -0
- data/spec/threatinator/parsers/xml/pattern_spec.rb +72 -0
- data/spec/threatinator/parsers/xml/record_spec.rb +27 -0
- data/spec/threatinator/plugin_loader_spec.rb +238 -0
- data/spec/threatinator/plugins/output/csv_spec.rb +47 -0
- data/spec/threatinator/plugins/output/null_spec.rb +17 -0
- data/spec/threatinator/plugins/output/rubydebug_spec.rb +37 -0
- data/spec/threatinator/record_spec.rb +19 -0
- data/spec/threatinator/registry_spec.rb +97 -0
- data/spec/threatinator/runner_spec.rb +273 -0
- metadata +674 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'active_model'
|
2
|
+
require 'active_model/validations'
|
3
|
+
require 'threatinator/model/validations'
|
4
|
+
require 'threatinator/exceptions'
|
5
|
+
|
6
|
+
module Threatinator
|
7
|
+
module Model
|
8
|
+
class Base
|
9
|
+
include ActiveModel::Validations
|
10
|
+
include Threatinator::Model::Validations
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
validate!
|
14
|
+
end
|
15
|
+
|
16
|
+
def validate!
|
17
|
+
unless valid?
|
18
|
+
raise Threatinator::Exceptions::InvalidAttributeError, errors.full_messages.join("\n")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'threatinator/exceptions'
|
2
|
+
require 'set'
|
3
|
+
|
4
|
+
module Threatinator
|
5
|
+
module Model
|
6
|
+
class Collection
|
7
|
+
def initialize(values = [])
|
8
|
+
@collection = Set.new
|
9
|
+
values.each do |v|
|
10
|
+
self << v
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def valid_member?(v)
|
15
|
+
#:nocov:
|
16
|
+
raise NotImplementedError, "#valid_member? not implemented"
|
17
|
+
#:nocov:
|
18
|
+
end
|
19
|
+
|
20
|
+
def <<(v)
|
21
|
+
unless valid_member?(v)
|
22
|
+
raise Threatinator::Exceptions::InvalidAttributeError, "Invalid member: #{v.class} '#{v.inspect}'"
|
23
|
+
end
|
24
|
+
@collection << v
|
25
|
+
end
|
26
|
+
|
27
|
+
def include?(member)
|
28
|
+
@collection.include?(member)
|
29
|
+
end
|
30
|
+
alias_method :member?, :include?
|
31
|
+
|
32
|
+
# @return [Boolean] true if empty, false otherwise
|
33
|
+
def empty?
|
34
|
+
@collection.empty?
|
35
|
+
end
|
36
|
+
|
37
|
+
def collect!
|
38
|
+
block_given? or return enum_for(__method__)
|
39
|
+
@collection.replace(@collection.class.new(@collection) { |o| yield(o) })
|
40
|
+
end
|
41
|
+
|
42
|
+
def delete(o)
|
43
|
+
@collection.delete(o)
|
44
|
+
end
|
45
|
+
|
46
|
+
def delete?(o)
|
47
|
+
@collection.delete?(o)
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [Integer] the number of members in the collection
|
51
|
+
def count
|
52
|
+
@collection.count
|
53
|
+
end
|
54
|
+
alias_method :size, :count
|
55
|
+
alias_method :length, :count
|
56
|
+
|
57
|
+
def to_ary
|
58
|
+
@collection.to_a
|
59
|
+
end
|
60
|
+
alias_method :to_a, :to_ary
|
61
|
+
|
62
|
+
# [31] pry(#<Threatinator::Plugins::Output::Json>)> event.urls.each{ |uri| p uri.to_s }
|
63
|
+
# "http://teamadrenaline.com/js/t1.exe"
|
64
|
+
# => [#<Addressable::URI:0x114c6ec URI:http://teamadrenaline.com/js/t1.exe>]
|
65
|
+
def each
|
66
|
+
return to_enum(:each) unless block_given?
|
67
|
+
@collection.each { |v| yield v }
|
68
|
+
end
|
69
|
+
|
70
|
+
def list
|
71
|
+
@collection.to_a.collect {|item|
|
72
|
+
item.to_s
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
def ==(other)
|
77
|
+
if self.equal?(other)
|
78
|
+
return true
|
79
|
+
elsif other.instance_of?(self.class)
|
80
|
+
@collection == other.instance_variable_get(:@collection)
|
81
|
+
else
|
82
|
+
false
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'threatinator/model/collection'
|
2
|
+
require 'domain_name_validator'
|
3
|
+
|
4
|
+
module Threatinator
|
5
|
+
module Model
|
6
|
+
module Observables
|
7
|
+
class FqdnCollection < Threatinator::Model::Collection
|
8
|
+
def valid_member?(v)
|
9
|
+
#v.is_a?(::String)
|
10
|
+
::DomainNameValidator.new.validate(v.to_s)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'threatinator/model/base'
|
2
|
+
require 'ip'
|
3
|
+
require 'equalizer'
|
4
|
+
|
5
|
+
module Threatinator
|
6
|
+
module Model
|
7
|
+
module Observables
|
8
|
+
class Ipv4 < Threatinator::Model::Base
|
9
|
+
include Equalizer.new(:ipv4)
|
10
|
+
attr_reader :ipv4
|
11
|
+
|
12
|
+
validates_each :ipv4 do |record, attr, value|
|
13
|
+
if value.is_a?(::IP::V4)
|
14
|
+
record.errors.add(attr, 'prefix length is not 32 bits') unless value.pfxlen == 32
|
15
|
+
else
|
16
|
+
record.errors.add(attr, 'not an IP::V4 object')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# @param [Hash] opts
|
21
|
+
# @option opts [IP::V4] :ipv4 An ipv4 object
|
22
|
+
def initialize(opts = {})
|
23
|
+
@ipv4 = opts.delete(:ipv4)
|
24
|
+
super()
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
return ipv4.to_s
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'threatinator/model/collection'
|
2
|
+
require 'threatinator/model/observables/ipv4'
|
3
|
+
|
4
|
+
module Threatinator
|
5
|
+
module Model
|
6
|
+
module Observables
|
7
|
+
class Ipv4Collection < Threatinator::Model::Collection
|
8
|
+
def valid_member?(v)
|
9
|
+
v.kind_of?(Threatinator::Model::Observables::Ipv4)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'threatinator/model/collection'
|
2
|
+
require 'addressable/uri'
|
3
|
+
|
4
|
+
module Threatinator
|
5
|
+
module Model
|
6
|
+
module Observables
|
7
|
+
class UrlCollection < Threatinator::Model::Collection
|
8
|
+
def valid_member?(v)
|
9
|
+
v.is_a?(::Addressable::URI) &&
|
10
|
+
v.absolute?
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'active_model'
|
2
|
+
require 'active_model/validations'
|
3
|
+
|
4
|
+
module Threatinator
|
5
|
+
module Model
|
6
|
+
module Validations
|
7
|
+
class TypeValidator < ActiveModel::EachValidator
|
8
|
+
def initialize(options)
|
9
|
+
@type = options.delete(:with)
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
def validate_each(record, name, value)
|
14
|
+
unless value.is_a?(@type)
|
15
|
+
record.errors.add name, "Expected to be #{@type}, got #{value.class} #{value.inspect}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'threatinator/model/validations/type'
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'threatinator/config/base'
|
2
|
+
|
3
|
+
module Threatinator
|
4
|
+
class Output
|
5
|
+
def initialize(config)
|
6
|
+
end
|
7
|
+
|
8
|
+
def handle_event(event)
|
9
|
+
#:nocov:
|
10
|
+
raise NotImplementedError.new("#{self.class}#handle_event is not implemented")
|
11
|
+
#:nocov:
|
12
|
+
end
|
13
|
+
|
14
|
+
def finish
|
15
|
+
#:nocov:
|
16
|
+
raise NotImplementedError.new("#{self.class}#finish is not implemented")
|
17
|
+
#:nocov:
|
18
|
+
end
|
19
|
+
|
20
|
+
class Config < Threatinator::Config::Base
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class FileBasedOutput < Output
|
25
|
+
attr_reader :output_io
|
26
|
+
protected :output_io
|
27
|
+
|
28
|
+
def initialize(config)
|
29
|
+
super(config)
|
30
|
+
if io = config.io
|
31
|
+
@output_io = io
|
32
|
+
elsif filename = config.filename
|
33
|
+
@output_io = File.open(filename, 'w:UTF-8')
|
34
|
+
else
|
35
|
+
@output_io = $stdout.dup
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def finish
|
40
|
+
@output_io.close
|
41
|
+
end
|
42
|
+
|
43
|
+
class Config < superclass::Config
|
44
|
+
attribute :filename, String,
|
45
|
+
description: "Path to the file where output will be written"
|
46
|
+
|
47
|
+
attribute :io
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Threatinator
|
2
|
+
class Parser
|
3
|
+
# @param [Hash] opts An options hash. See subclasses for details.
|
4
|
+
def initialize(opts = {})
|
5
|
+
end
|
6
|
+
|
7
|
+
# Runs the parser against the provided io, yielding records.
|
8
|
+
# @param [IO] io The IO to be parsed.
|
9
|
+
def run(io)
|
10
|
+
raise NotImplementedError.new("#{self.class}#run not implemented!")
|
11
|
+
end
|
12
|
+
|
13
|
+
def ==(other)
|
14
|
+
true
|
15
|
+
end
|
16
|
+
|
17
|
+
def eql?(other)
|
18
|
+
self.class == other.class &&
|
19
|
+
self == other
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'threatinator/record'
|
2
|
+
require 'threatinator/parser'
|
3
|
+
require 'csv'
|
4
|
+
|
5
|
+
module Threatinator
|
6
|
+
module Parsers
|
7
|
+
module CSV
|
8
|
+
# Parses an IO, yielding a record with a CSV::Row.
|
9
|
+
class Parser < Threatinator::Parser
|
10
|
+
attr_reader :csv_opts, :row_separator, :col_separator, :headers
|
11
|
+
|
12
|
+
# @param [Hash] opts
|
13
|
+
# @option opts [String, :auto] :row_separator A string that represent the row
|
14
|
+
# separator. Identical to ::CSV.new's :row_sep.
|
15
|
+
# @option opts [String] :col_separator A string that represents the column
|
16
|
+
# separator. Identical to ::CSV.new's :col_sep.
|
17
|
+
# @option opts [Array<String>, :first_row, true, false] :headers The header
|
18
|
+
# configuration. Identical to ::CSV.new's :headers.
|
19
|
+
# @option opts [Hash] :csv_opts A hash of options that will be passed to
|
20
|
+
# Ruby's CSV.new.
|
21
|
+
# @see ::CSV
|
22
|
+
def initialize(opts = {})
|
23
|
+
@csv_opts = {}.merge(opts.delete(:csv_opts) || {})
|
24
|
+
@row_separator = opts.delete(:row_separator)
|
25
|
+
@col_separator = opts.delete(:col_separator)
|
26
|
+
@headers = opts.delete(:headers)
|
27
|
+
|
28
|
+
super(opts)
|
29
|
+
end
|
30
|
+
|
31
|
+
def ==(other)
|
32
|
+
@csv_opts == other.csv_opts &&
|
33
|
+
@row_separator == other.row_separator &&
|
34
|
+
@col_separator == other.col_separator &&
|
35
|
+
@headers == other.headers &&
|
36
|
+
super(other)
|
37
|
+
end
|
38
|
+
|
39
|
+
def _build_csv_opts
|
40
|
+
opts = {}.merge(@csv_opts)
|
41
|
+
opts[:return_headers] = true
|
42
|
+
opts[:row_sep] = @row_separator unless @row_separator.nil?
|
43
|
+
opts[:col_sep] = @col_separator unless @col_separator.nil?
|
44
|
+
opts[:headers] = @headers unless @headers.nil?
|
45
|
+
opts
|
46
|
+
end
|
47
|
+
|
48
|
+
# @param [IO] io
|
49
|
+
# @yield [record] Gives one line to the block
|
50
|
+
# @yieldparam record [Record] a record
|
51
|
+
def run(io)
|
52
|
+
lineno = 1
|
53
|
+
previous_pos = io.pos
|
54
|
+
csv = ::CSV.new(io, _build_csv_opts())
|
55
|
+
csv.each do |row|
|
56
|
+
begin
|
57
|
+
if row.kind_of?(::CSV::Row)
|
58
|
+
next if row.header_row?
|
59
|
+
row = row.to_hash
|
60
|
+
end
|
61
|
+
|
62
|
+
yield Record.new(row,
|
63
|
+
line_number: lineno,
|
64
|
+
pos_start: previous_pos,
|
65
|
+
pos_end: io.pos)
|
66
|
+
|
67
|
+
ensure
|
68
|
+
previous_pos = io.pos
|
69
|
+
lineno += 1
|
70
|
+
end
|
71
|
+
end
|
72
|
+
nil
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'threatinator/record'
|
2
|
+
require 'threatinator/parser'
|
3
|
+
|
4
|
+
module Threatinator
|
5
|
+
module Parsers
|
6
|
+
module Getline
|
7
|
+
# Parses an IO, yielding each 'line' of data as deliniated by :separator.
|
8
|
+
# The text matching :separator will be included.
|
9
|
+
class Parser < Threatinator::Parser
|
10
|
+
attr_reader :separator
|
11
|
+
|
12
|
+
# @param [Hash] opts
|
13
|
+
# @option opts [String] :separator ("\n") A character that will be used
|
14
|
+
# to detect the end of a line.
|
15
|
+
def initialize(opts = {})
|
16
|
+
@separator = opts.delete(:separator) || "\n"
|
17
|
+
unless @separator.length == 1
|
18
|
+
raise ArgumentError.new(":separator must be exactly one character long")
|
19
|
+
end
|
20
|
+
super(opts)
|
21
|
+
end
|
22
|
+
|
23
|
+
def ==(other)
|
24
|
+
@separator == other.separator &&
|
25
|
+
super(other)
|
26
|
+
end
|
27
|
+
|
28
|
+
# @param [IO] io The IO to be parsed
|
29
|
+
# @yield [line] Gives one line to the block
|
30
|
+
# @yieldparam line [String] a line from the IO stream.
|
31
|
+
def run(io)
|
32
|
+
return enum_for(:each) unless block_given?
|
33
|
+
lineno = 1
|
34
|
+
while str = io.gets(@separator)
|
35
|
+
return if str.nil?
|
36
|
+
pos_start = io.pos - str.length
|
37
|
+
yield Record.new(str, line_number: lineno, pos_start: pos_start, pos_end: io.pos)
|
38
|
+
lineno += 1
|
39
|
+
end
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'threatinator/parsers/json'
|
2
|
+
require 'threatinator/exceptions'
|
3
|
+
require 'oj'
|
4
|
+
|
5
|
+
module Threatinator::Parsers::JSON::Adapters
|
6
|
+
class Oj < ::Oj::ScHandler
|
7
|
+
def initialize
|
8
|
+
@root = nil
|
9
|
+
@depth = 0
|
10
|
+
end
|
11
|
+
|
12
|
+
def run(io, &callback)
|
13
|
+
@callback = callback
|
14
|
+
begin
|
15
|
+
::Oj.sc_parse(self, io)
|
16
|
+
rescue ::Oj::ParseError => e
|
17
|
+
raise Threatinator::Exceptions::ParseError.new(e)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def do_callback(data, key = nil)
|
22
|
+
@callback.call(data, key: key)
|
23
|
+
end
|
24
|
+
|
25
|
+
def hash_start
|
26
|
+
ret = {}
|
27
|
+
@depth += 1
|
28
|
+
@root = ret if @root.nil?
|
29
|
+
ret
|
30
|
+
end
|
31
|
+
|
32
|
+
def hash_set(h,k,v)
|
33
|
+
if @depth == 1
|
34
|
+
do_callback(v, k)
|
35
|
+
else
|
36
|
+
h[k] = v
|
37
|
+
end
|
38
|
+
v
|
39
|
+
end
|
40
|
+
|
41
|
+
def hash_end
|
42
|
+
@depth -= 1
|
43
|
+
end
|
44
|
+
|
45
|
+
def array_start
|
46
|
+
ret = []
|
47
|
+
@depth += 1
|
48
|
+
@root = ret if @root.nil?
|
49
|
+
ret
|
50
|
+
end
|
51
|
+
|
52
|
+
def array_append(a,v)
|
53
|
+
if @depth == 1
|
54
|
+
do_callback(v)
|
55
|
+
else
|
56
|
+
a << v
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def array_end
|
61
|
+
@depth -= 1
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'threatinator/parser'
|
2
|
+
require 'threatinator/parsers/json/record'
|
3
|
+
|
4
|
+
module Threatinator
|
5
|
+
module Parsers
|
6
|
+
module JSON
|
7
|
+
class Parser < Threatinator::Parser
|
8
|
+
def initialize(opts = {})
|
9
|
+
@adapter_class = self.class.adapter_class
|
10
|
+
super(opts)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Detects the platform, loads the JSON adapter, and returns the
|
14
|
+
# adapter's class.
|
15
|
+
def self.adapter_class
|
16
|
+
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
|
17
|
+
#:nocov:
|
18
|
+
raise "JSON parsing not supported for JRuby"
|
19
|
+
#:nocov:
|
20
|
+
else
|
21
|
+
require 'threatinator/parsers/json/adapters/oj'
|
22
|
+
return Threatinator::Parsers::JSON::Adapters::Oj
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def ==(other)
|
27
|
+
super(other)
|
28
|
+
end
|
29
|
+
|
30
|
+
# @param [IO] io
|
31
|
+
# @yield [record] Gives one line to the block
|
32
|
+
# @yieldparam record [Threatinator::Parsers::JSON::Record] a record
|
33
|
+
def run(io)
|
34
|
+
adapter = @adapter_class.new
|
35
|
+
callback = lambda do |object, opts = {}|
|
36
|
+
yield Threatinator::Parsers::JSON::Record.new(object, opts)
|
37
|
+
end
|
38
|
+
adapter.run(io, &callback)
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'threatinator/record'
|
2
|
+
|
3
|
+
module Threatinator
|
4
|
+
module Parsers
|
5
|
+
module JSON
|
6
|
+
class Record < Threatinator::Record
|
7
|
+
attr_reader :key
|
8
|
+
def initialize(object, opts = {})
|
9
|
+
@key = opts.delete(:key)
|
10
|
+
super(object, opts)
|
11
|
+
end
|
12
|
+
|
13
|
+
def ==(other)
|
14
|
+
@key == other.key &&
|
15
|
+
super(other)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Threatinator
|
2
|
+
module Parsers
|
3
|
+
module XML
|
4
|
+
class Node
|
5
|
+
attr_accessor :text
|
6
|
+
attr_reader :name
|
7
|
+
attr_reader :attrs
|
8
|
+
attr_reader :children
|
9
|
+
|
10
|
+
# @param [String, Symbol] name
|
11
|
+
# @param [Hash] opts
|
12
|
+
# @option opts [String] :text The text
|
13
|
+
# @option opts [Hash] :attrs The attributes
|
14
|
+
# @option opts [Array<Threatinator::Parsers::XML::Node>] :children An array
|
15
|
+
# of child child nodes that belong to this node.
|
16
|
+
def initialize(name, opts = {})
|
17
|
+
unless name.kind_of?(::Symbol) or name.kind_of?(::String)
|
18
|
+
raise TypeError.new("name must be a String or a Symbol")
|
19
|
+
end
|
20
|
+
|
21
|
+
@name = name.to_sym
|
22
|
+
@text = opts.delete(:text) || ""
|
23
|
+
unless @text.kind_of?(::String)
|
24
|
+
raise TypeError.new(":text must be a String")
|
25
|
+
end
|
26
|
+
@attrs = opts.delete(:attrs) || {}
|
27
|
+
unless @attrs.kind_of?(::Hash)
|
28
|
+
raise TypeError.new(":text must be a Hash")
|
29
|
+
end
|
30
|
+
|
31
|
+
@children = {}
|
32
|
+
if _children = opts.delete(:children)
|
33
|
+
_children.each do |child|
|
34
|
+
add_child(child)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def ==(other)
|
40
|
+
@name == other.name &&
|
41
|
+
@attrs == other.attrs &&
|
42
|
+
@text == other.text &&
|
43
|
+
@children == other.children
|
44
|
+
end
|
45
|
+
|
46
|
+
def eql?(other)
|
47
|
+
other.kind_of?(self.class) &&
|
48
|
+
self == other
|
49
|
+
end
|
50
|
+
|
51
|
+
# @return [Integer] the number of children
|
52
|
+
def num_children
|
53
|
+
@children.values.inject(0) {|total, child_set| total + child_set.count}
|
54
|
+
end
|
55
|
+
|
56
|
+
# @param [String, Symbol] name The name of the child element
|
57
|
+
# @return [Array<Node>] An array containing all the child nodes for the given
|
58
|
+
# name. The array will be empty if there are no children by the given name.
|
59
|
+
def [](name)
|
60
|
+
@children[name.to_sym] || []
|
61
|
+
end
|
62
|
+
|
63
|
+
# @return [Array<Symbol>] an array containing all the names of child elements
|
64
|
+
def child_names
|
65
|
+
@children.keys
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
def add_child(child)
|
70
|
+
name = child.name
|
71
|
+
unless child_set = @children[name]
|
72
|
+
child_set = @children[name] = []
|
73
|
+
end
|
74
|
+
child_set << child
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|