threatinator 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +23 -0
- data/CONTRIBUTING.md +119 -0
- data/Gemfile +28 -0
- data/LICENSE +165 -0
- data/README.md +45 -0
- data/Rakefile +45 -0
- data/VERSION +1 -0
- data/bin/threatinator +5 -0
- data/lib/threatinator.rb +3 -0
- data/lib/threatinator/action.rb +14 -0
- data/lib/threatinator/actions/list.rb +2 -0
- data/lib/threatinator/actions/list/action.rb +53 -0
- data/lib/threatinator/actions/list/config.rb +10 -0
- data/lib/threatinator/actions/run.rb +2 -0
- data/lib/threatinator/actions/run/action.rb +45 -0
- data/lib/threatinator/actions/run/config.rb +32 -0
- data/lib/threatinator/actions/run/coverage_observer.rb +54 -0
- data/lib/threatinator/actions/run/output_config.rb +59 -0
- data/lib/threatinator/cli.rb +13 -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 +113 -0
- data/lib/threatinator/cli/run_action_builder.rb +41 -0
- data/lib/threatinator/config.rb +6 -0
- data/lib/threatinator/config/base.rb +35 -0
- data/lib/threatinator/config/feed_search.rb +25 -0
- data/lib/threatinator/decoder.rb +24 -0
- data/lib/threatinator/decoders/gzip.rb +30 -0
- data/lib/threatinator/event.rb +27 -0
- data/lib/threatinator/event_builder.rb +41 -0
- data/lib/threatinator/exceptions.rb +61 -0
- data/lib/threatinator/feed.rb +82 -0
- data/lib/threatinator/feed_builder.rb +156 -0
- data/lib/threatinator/feed_registry.rb +47 -0
- data/lib/threatinator/feed_runner.rb +118 -0
- data/lib/threatinator/fetcher.rb +22 -0
- data/lib/threatinator/fetchers/http.rb +46 -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/output.rb +50 -0
- data/lib/threatinator/parser.rb +23 -0
- data/lib/threatinator/parsers/csv.rb +7 -0
- data/lib/threatinator/parsers/csv/parser.rb +77 -0
- data/lib/threatinator/parsers/getline.rb +8 -0
- data/lib/threatinator/parsers/getline/parser.rb +45 -0
- data/lib/threatinator/parsers/json.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/xml.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/plugin_loader.rb +115 -0
- data/lib/threatinator/plugins/output/csv.rb +47 -0
- data/lib/threatinator/plugins/output/null.rb +17 -0
- data/lib/threatinator/plugins/output/rubydebug.rb +16 -0
- data/lib/threatinator/property_definer.rb +101 -0
- data/lib/threatinator/record.rb +22 -0
- data/lib/threatinator/registry.rb +53 -0
- data/lib/threatinator/util.rb +15 -0
- data/spec/feeds/ET_compromised-ip_reputation_spec.rb +50 -0
- data/spec/feeds/alienvault-ip_reputation_spec.rb +50 -0
- data/spec/feeds/arbor_fastflux-domain_reputation_spec.rb +50 -0
- data/spec/feeds/arbor_ssh-ip_reputation_spec.rb +50 -0
- data/spec/feeds/autoshun_shunlist_spec.rb +42 -0
- data/spec/feeds/blocklist_de_apache-ip_reputation_spec.rb +50 -0
- data/spec/feeds/blocklist_de_bots-ip_reputation_spec.rb +50 -0
- data/spec/feeds/blocklist_de_ftp-ip_reputation_spec.rb +50 -0
- data/spec/feeds/blocklist_de_imap-ip_reputation_spec.rb +50 -0
- data/spec/feeds/blocklist_de_pop3-ip_reputation_spec.rb +50 -0
- data/spec/feeds/blocklist_de_proftpd-ip_reputation_spec.rb +50 -0
- data/spec/feeds/blocklist_de_sip-ip_reputation_spec.rb +50 -0
- data/spec/feeds/blocklist_de_ssh-ip_reputation_spec.rb +50 -0
- data/spec/feeds/blocklist_de_strongips-ip_reputation_spec.rb +50 -0
- data/spec/feeds/ciarmy-ip_reputation_spec.rb +50 -0
- data/spec/feeds/cruzit-ip_reputation_spec.rb +50 -0
- data/spec/feeds/dan_me_uk_torlist-ip_reputation_spec.rb +50 -0
- data/spec/feeds/data/ET_compromised-ip_reputation.txt +11 -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/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/ciarmy-ip-reputation.txt +11 -0
- data/spec/feeds/data/cruzit-ip-reputation.txt +14 -0
- data/spec/feeds/data/dan_me_uk_torlist-ip-reputation.txt +11 -0
- data/spec/feeds/data/dshield_topattackers.xml +4 -0
- data/spec/feeds/data/feodo_domainlist.txt +18 -0
- data/spec/feeds/data/feodo_iplist.txt +20 -0
- data/spec/feeds/data/infiltrated_iplist.txt +16 -0
- data/spec/feeds/data/malc0de_domainlist.txt +18 -0
- data/spec/feeds/data/malc0de_iplist.txt +14 -0
- data/spec/feeds/data/mirc_domainlist.txt +31 -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/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/spyeye_domainlist.txt +16 -0
- data/spec/feeds/data/spyeye_iplist.txt +19 -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/yourcmc_ssh-ip_reputation.txt +27 -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 +43 -0
- data/spec/feeds/feodo-domain_reputation_spec.rb +50 -0
- data/spec/feeds/feodo-ip_reputation_spec.rb +50 -0
- data/spec/feeds/infiltrated-ip_reputation_spec.rb +50 -0
- data/spec/feeds/malc0de-domain_reputation_spec.rb +50 -0
- data/spec/feeds/malc0de-ip_reputation_spec.rb +50 -0
- data/spec/feeds/mirc-domain_reputation_spec.rb +50 -0
- data/spec/feeds/nothink_irc-ip_reputation_spec.rb +50 -0
- data/spec/feeds/nothink_ssh-ip_reputation_spec.rb +50 -0
- data/spec/feeds/openbl-ip_reputation_spec.rb +50 -0
- data/spec/feeds/palevo-domain_reputation_spec.rb +50 -0
- data/spec/feeds/palevo-ip_reputation_spec.rb +50 -0
- data/spec/feeds/phishtank_spec.rb +45 -0
- data/spec/feeds/spyeye-domain_reputation_spec.rb +50 -0
- data/spec/feeds/spyeye-ip_reputation_spec.rb +50 -0
- data/spec/feeds/t-arend-de_ssh-ip_reputation_spec.rb +50 -0
- data/spec/feeds/the_haleys_ssh-ip_reputation_spec.rb +50 -0
- data/spec/feeds/yourcmc_ssh-ip_reputation_spec.rb +50 -0
- data/spec/feeds/zeus-domain_reputation_spec.rb +50 -0
- data/spec/feeds/zeus-ip_reputation_spec.rb +50 -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 +52 -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 +27 -0
- data/spec/support/factories/feed.rb +32 -0
- data/spec/support/factories/feed_builder.rb +65 -0
- data/spec/support/factories/feed_registry.rb +8 -0
- data/spec/support/factories/output.rb +11 -0
- data/spec/support/factories/record.rb +17 -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/feeds.rb +218 -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/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 +93 -0
- data/spec/threatinator/actions/run/action_spec.rb +89 -0
- data/spec/threatinator/actions/run/config_spec.rb +39 -0
- data/spec/threatinator/actions/run/coverage_observer_spec.rb +116 -0
- data/spec/threatinator/actions/run/output_config_spec.rb +89 -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 +33 -0
- data/spec/threatinator/event_spec.rb +30 -0
- data/spec/threatinator/feed_builder_spec.rb +636 -0
- data/spec/threatinator/feed_registry_spec.rb +198 -0
- data/spec/threatinator/feed_runner_spec.rb +155 -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/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 +46 -0
- data/spec/threatinator/plugins/output/null_spec.rb +17 -0
- data/spec/threatinator/plugins/output/rubydebug_spec.rb +37 -0
- data/spec/threatinator/property_definer_spec.rb +155 -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 +376 -0
|
@@ -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
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
shared_examples_for 'a record when compared to an identically configured record' do
|
|
2
|
+
let(:record1) { described_class.new(data, opts) }
|
|
3
|
+
let(:record2) { described_class.new(data2, opts2) }
|
|
4
|
+
specify "record1 should == record2" do
|
|
5
|
+
expect(record1).to be == record2
|
|
6
|
+
end
|
|
7
|
+
specify "record2 should == record1" do
|
|
8
|
+
expect(record2).to be == record1
|
|
9
|
+
end
|
|
10
|
+
specify "record1 should eql? record2" do
|
|
11
|
+
expect(record1).to eql record2
|
|
12
|
+
end
|
|
13
|
+
specify "record2 should eql? record1" do
|
|
14
|
+
expect(record2).to eql record1
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
shared_examples_for 'a record when compared to a differently configured record' do
|
|
19
|
+
let(:record1) { described_class.new(data, opts) }
|
|
20
|
+
let(:record2) { described_class.new(data2, opts2) }
|
|
21
|
+
specify "record1 should not == record2" do
|
|
22
|
+
expect(record1).not_to be == record2
|
|
23
|
+
end
|
|
24
|
+
specify "record2 should not == record1" do
|
|
25
|
+
expect(record2).not_to be == record1
|
|
26
|
+
end
|
|
27
|
+
specify "record1 should not eql? record2" do
|
|
28
|
+
expect(record1).not_to eql record2
|
|
29
|
+
end
|
|
30
|
+
specify "record2 should not eql? record1" do
|
|
31
|
+
expect(record2).not_to eql record1
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
shared_examples_for 'a record' do
|
|
36
|
+
let(:record) { described_class.new(data, opts) }
|
|
37
|
+
context "when compared to itself" do
|
|
38
|
+
it_should_behave_like 'a record when compared to an identically configured record' do
|
|
39
|
+
let(:data2) { Marshal.load(Marshal.dump(data)) }
|
|
40
|
+
let(:opts2) { Marshal.load(Marshal.dump(opts)) }
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
describe "initialization options" do
|
|
44
|
+
describe "data" do
|
|
45
|
+
it "should be accessible via #data" do
|
|
46
|
+
expect(record.data).to eq(data)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
describe ":line_number" do
|
|
50
|
+
context "when not set" do
|
|
51
|
+
it "should default to nil" do
|
|
52
|
+
expect(record.line_number).to be_nil
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
context "when set" do
|
|
56
|
+
before :each do
|
|
57
|
+
opts[:line_number] = 1234
|
|
58
|
+
end
|
|
59
|
+
it "should be readable via #line_number" do
|
|
60
|
+
expect(record.line_number).to eq(1234)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it_should_behave_like 'a record when compared to an identically configured record' do
|
|
64
|
+
let(:data2) { Marshal.load(Marshal.dump(data)) }
|
|
65
|
+
let(:opts2) { Marshal.load(Marshal.dump(opts)) }
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
describe ":pos_start" do
|
|
70
|
+
context "when not set" do
|
|
71
|
+
it "should default to nil" do
|
|
72
|
+
expect(record.pos_start).to be_nil
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
context "when set" do
|
|
76
|
+
before :each do
|
|
77
|
+
opts[:pos_start] = 999
|
|
78
|
+
end
|
|
79
|
+
it "should be readable via #pos_start" do
|
|
80
|
+
expect(record.pos_start).to eq(999)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it_should_behave_like 'a record when compared to an identically configured record' do
|
|
84
|
+
let(:data2) { Marshal.load(Marshal.dump(data)) }
|
|
85
|
+
let(:opts2) { Marshal.load(Marshal.dump(opts)) }
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
describe ":pos_end" do
|
|
90
|
+
context "when not set" do
|
|
91
|
+
it "should default to nil" do
|
|
92
|
+
expect(record.pos_end).to be_nil
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
context "when set" do
|
|
96
|
+
before :each do
|
|
97
|
+
opts[:pos_end] = 555
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it "should be readable via #pos_end" do
|
|
101
|
+
expect(record.pos_end).to eq(555)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it_should_behave_like 'a record when compared to an identically configured record' do
|
|
105
|
+
let(:data2) { Marshal.load(Marshal.dump(data)) }
|
|
106
|
+
let(:opts2) { Marshal.load(Marshal.dump(opts)) }
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'threatinator/actions/list/action'
|
|
3
|
+
require 'threatinator/actions/list/config'
|
|
4
|
+
|
|
5
|
+
describe Threatinator::Actions::List::Action do
|
|
6
|
+
let(:feed_registry) { build(:feed_registry) }
|
|
7
|
+
let(:config) { Threatinator::Actions::List::Config.new }
|
|
8
|
+
let(:action) { described_class.new(feed_registry, config) }
|
|
9
|
+
|
|
10
|
+
describe "the header row, header separator, and footer separator" do
|
|
11
|
+
it "should vary the width of 'provider' based on the longest provider name" do
|
|
12
|
+
feed_registry.register(build(:feed, :mini, provider: 'A' * 10))
|
|
13
|
+
feed_registry.register(build(:feed, :mini, provider: 'A' * 20))
|
|
14
|
+
feed_registry.register(build(:feed, :mini, provider: 'A' * 30))
|
|
15
|
+
|
|
16
|
+
output = temp_stdout do
|
|
17
|
+
action.exec
|
|
18
|
+
end
|
|
19
|
+
lines = output.lines.to_a
|
|
20
|
+
expect(lines[0]).to eq("provider name type link/path\n")
|
|
21
|
+
expect(lines[1]).to eq("------------------------------ ---- ---- ---------\n")
|
|
22
|
+
expect(lines[-2]).to eq("------------------------------ ---- ---- ---------\n")
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "should vary the width of 'name' based on the longest feed name" do
|
|
26
|
+
feed_registry.register(build(:feed, :mini, name: 'A' * 10))
|
|
27
|
+
feed_registry.register(build(:feed, :mini, name: 'A' * 20))
|
|
28
|
+
feed_registry.register(build(:feed, :mini, name: 'A' * 30))
|
|
29
|
+
|
|
30
|
+
output = temp_stdout do
|
|
31
|
+
action.exec
|
|
32
|
+
end
|
|
33
|
+
lines = output.lines.to_a
|
|
34
|
+
expect(lines[0]).to eq("provider name type link/path\n")
|
|
35
|
+
expect(lines[1]).to eq("-------- ------------------------------ ---- ---------\n")
|
|
36
|
+
expect(lines[-2]).to eq("-------- ------------------------------ ---- ---------\n")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "should vary the width of 'link/path' based on the longest link name" do
|
|
40
|
+
feed_registry.register(build(:feed, :mini, url: 'http://' + ('A' * 10)))
|
|
41
|
+
feed_registry.register(build(:feed, :mini, url: 'http://' + ('A' * 20)))
|
|
42
|
+
feed_registry.register(build(:feed, :mini, url: 'http://' + ('A' * 30)))
|
|
43
|
+
|
|
44
|
+
output = temp_stdout do
|
|
45
|
+
action.exec
|
|
46
|
+
end
|
|
47
|
+
lines = output.lines.to_a
|
|
48
|
+
expect(lines[0]).to eq("provider name type link/path \n")
|
|
49
|
+
expect(lines[1]).to eq("-------- ---- ---- -------------------------------------\n")
|
|
50
|
+
expect(lines[-2]).to eq("-------- ---- ---- -------------------------------------\n")
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
describe "the list of feeds" do
|
|
55
|
+
it "should be sorted by provider name and then feed name" do
|
|
56
|
+
feed_registry.register(build(:feed, provider: 'provider_b', name: 'feed_c' ))
|
|
57
|
+
feed_registry.register(build(:feed, provider: 'provider_a', name: 'feed_d' ))
|
|
58
|
+
feed_registry.register(build(:feed, provider: 'provider_a', name: 'feed_a' ))
|
|
59
|
+
feed_registry.register(build(:feed, provider: 'provider_b', name: 'feed_d' ))
|
|
60
|
+
feed_registry.register(build(:feed, provider: 'provider_a', name: 'feed_c' ))
|
|
61
|
+
feed_registry.register(build(:feed, provider: 'provider_b', name: 'feed_a' ))
|
|
62
|
+
feed_registry.register(build(:feed, provider: 'provider_b', name: 'feed_b' ))
|
|
63
|
+
feed_registry.register(build(:feed, provider: 'provider_a', name: 'feed_b' ))
|
|
64
|
+
|
|
65
|
+
output = temp_stdout do
|
|
66
|
+
action.exec
|
|
67
|
+
end
|
|
68
|
+
lines = output.lines.to_a
|
|
69
|
+
expect(lines[2]).to match(/^provider_a feed_a .*$/)
|
|
70
|
+
expect(lines[3]).to match(/^provider_a feed_b .*$/)
|
|
71
|
+
expect(lines[4]).to match(/^provider_a feed_c .*$/)
|
|
72
|
+
expect(lines[5]).to match(/^provider_a feed_d .*$/)
|
|
73
|
+
expect(lines[6]).to match(/^provider_b feed_a .*$/)
|
|
74
|
+
expect(lines[7]).to match(/^provider_b feed_b .*$/)
|
|
75
|
+
expect(lines[8]).to match(/^provider_b feed_c .*$/)
|
|
76
|
+
expect(lines[9]).to match(/^provider_b feed_d .*$/)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
describe "the footer" do
|
|
81
|
+
it "should indicate the number of feeds" do
|
|
82
|
+
20.times do |i|
|
|
83
|
+
feed_registry.register(build(:feed))
|
|
84
|
+
end
|
|
85
|
+
output = temp_stdout do
|
|
86
|
+
action.exec
|
|
87
|
+
end
|
|
88
|
+
lines = output.lines.to_a
|
|
89
|
+
expect(lines[-1]).to eq("Total: 20\n")
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'threatinator/actions/run/action'
|
|
3
|
+
require 'threatinator/actions/run/config'
|
|
4
|
+
require 'threatinator/plugin_loader'
|
|
5
|
+
|
|
6
|
+
describe Threatinator::Actions::Run::Action do
|
|
7
|
+
let(:feed_registry) { build(:feed_registry) }
|
|
8
|
+
let(:feed_runner) { double('feed runner') }
|
|
9
|
+
let(:plugin_loader) { Threatinator::PluginLoader.new }
|
|
10
|
+
let(:config_class) { Threatinator::Actions::Run::Config.generate(plugin_loader) }
|
|
11
|
+
let(:config) { config_class.new }
|
|
12
|
+
let(:action) { described_class.new(feed_registry, config) }
|
|
13
|
+
let(:feed) { build(:feed, provider: "my_provider", name: "my_name") }
|
|
14
|
+
|
|
15
|
+
before :each do
|
|
16
|
+
feed_registry.register(feed)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
context "when configured with feed provider and name that exists within the registry" do
|
|
20
|
+
let(:output) { double('mock output') }
|
|
21
|
+
let(:observer) { double('observer') }
|
|
22
|
+
before :each do
|
|
23
|
+
config.feed_provider = "my_provider"
|
|
24
|
+
config.feed_name = "my_name"
|
|
25
|
+
|
|
26
|
+
allow(feed_registry).to receive(:get).and_call_original
|
|
27
|
+
allow(config.output).to receive(:build_output).and_return(output)
|
|
28
|
+
allow(Threatinator::FeedRunner).to receive(:new).and_return(feed_runner)
|
|
29
|
+
allow(feed_runner).to receive(:run)
|
|
30
|
+
allow(feed_runner).to receive(:add_observer)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
describe "#exec" do
|
|
34
|
+
before :each do
|
|
35
|
+
@ret = action.exec
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "queries the feed registry for the provider and name" do
|
|
39
|
+
expect(feed_registry).to have_received(:get).with("my_provider", "my_name")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "builds the output using config.output.build_output" do
|
|
43
|
+
expect(config.output).to have_received(:build_output)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "runs the feed" do
|
|
47
|
+
expect(feed_runner).to have_received(:run)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
context "when no observer is configured" do
|
|
52
|
+
before :each do
|
|
53
|
+
config.observers = [ ]
|
|
54
|
+
end
|
|
55
|
+
it "does not add any observers to the feed runner" do
|
|
56
|
+
expect(feed_runner).not_to receive(:add_observer)
|
|
57
|
+
action.exec
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
context "when configured with an observer" do
|
|
62
|
+
before :each do
|
|
63
|
+
config.observers = [ observer ]
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
describe "#exec" do
|
|
67
|
+
it "adds the observer to FeedRunner" do
|
|
68
|
+
expect(feed_runner).to receive(:add_observer).with(observer)
|
|
69
|
+
action.exec
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
context "when the registry does not contain the configured feed_provider or feed_name" do
|
|
76
|
+
before :each do
|
|
77
|
+
config.feed_provider = "unknown_provider"
|
|
78
|
+
config.feed_name = "unknown_feed_name"
|
|
79
|
+
end
|
|
80
|
+
describe "#exec" do
|
|
81
|
+
it "raises Threatinator::Exceptions::UnknownFeed" do
|
|
82
|
+
expect {
|
|
83
|
+
action.exec
|
|
84
|
+
}.to raise_error(Threatinator::Exceptions::UnknownFeed)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'threatinator/actions/run/config'
|
|
3
|
+
require 'threatinator/plugin_loader'
|
|
4
|
+
require 'fixtures/plugins/fake'
|
|
5
|
+
|
|
6
|
+
describe Threatinator::Actions::Run::Config do
|
|
7
|
+
|
|
8
|
+
let(:plugin_loader) { Threatinator::PluginLoader.new }
|
|
9
|
+
|
|
10
|
+
describe ".generate(plugin_loader)" do
|
|
11
|
+
before :each do
|
|
12
|
+
allow(Threatinator::Actions::Run::OutputConfig).to receive(:generate).and_call_original
|
|
13
|
+
plugin_loader.register_plugin(:output, :plugin1, FakeOutputPlugins::Plugin1)
|
|
14
|
+
plugin_loader.register_plugin(:output, :plugin2, FakeOutputPlugins::Plugin2)
|
|
15
|
+
plugin_loader.register_plugin(:output, :plugin3, FakeOutputPlugins::Plugin3)
|
|
16
|
+
@generated_class = described_class.generate(plugin_loader)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
let(:generated_class) { @generated_class }
|
|
20
|
+
|
|
21
|
+
it "returns a subclass of Threatinator::Config::Base" do
|
|
22
|
+
expect(generated_class.superclass).to be(Threatinator::Config::Base)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "generates a new Output config class using the plugin_loader" do
|
|
26
|
+
expect(Threatinator::Actions::Run::OutputConfig).to have_received(:generate).with(plugin_loader)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
describe "an instance" do
|
|
30
|
+
let(:config) { generated_class.new }
|
|
31
|
+
describe "#output" do
|
|
32
|
+
specify "returns an instance of a subclass of Threatinator::Config::Base" do
|
|
33
|
+
expect(config.output.class.superclass).to be(Threatinator::Config::Base)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'threatinator/actions/run/coverage_observer'
|
|
3
|
+
|
|
4
|
+
describe Threatinator::Actions::Run::CoverageObserver do
|
|
5
|
+
before :each do
|
|
6
|
+
@tmpdir = Dir.mktmpdir
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
after :each do
|
|
10
|
+
observer.update(:end)
|
|
11
|
+
FileUtils.remove_entry_secure @tmpdir
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
let(:filename) { File.join(@tmpdir, "coverage.csv") }
|
|
15
|
+
let(:observer) { described_class.new(filename) }
|
|
16
|
+
|
|
17
|
+
context "#update(:start)" do
|
|
18
|
+
it "creates the file specified by filename" do
|
|
19
|
+
expect(File.exist?(filename)).to eq(false)
|
|
20
|
+
observer.update(:start)
|
|
21
|
+
expect(File.exist?(filename)).to eq(true)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
context "#update(:end)" do
|
|
26
|
+
before :each do
|
|
27
|
+
observer.update(:start)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
context "when at least one record has been written" do
|
|
31
|
+
before :each do
|
|
32
|
+
observer.update(:record_parsed, build(:record), [ build(:event) ])
|
|
33
|
+
observer.update(:end)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
specify "the first line is the header" do
|
|
37
|
+
data = File.read(filename)
|
|
38
|
+
expect(data.lines.to_a.first).to eq("status,event_count,line_number,pos_start,pos_end,data\n")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "closes the file so that no more records will be written" do
|
|
42
|
+
data_before = File.read(filename)
|
|
43
|
+
10.times do
|
|
44
|
+
observer.update(:record_missed, build(:record))
|
|
45
|
+
end
|
|
46
|
+
data_after = File.read(filename)
|
|
47
|
+
expect(data_before).to eq(data_after)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
context "#update(:record_filtered, record)" do
|
|
53
|
+
before :each do
|
|
54
|
+
observer.update(:start)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it "writes a csv entry to the file indicating that it was filtered" do
|
|
58
|
+
record = build(:record, line_number: 23, pos_start: 99, pos_end: 105, data: "foobar\r\n")
|
|
59
|
+
observer.update(:record_filtered, record)
|
|
60
|
+
observer.update(:end)
|
|
61
|
+
csv = CSV.read(filename, headers: true, header_converters: :symbol)
|
|
62
|
+
expect(csv[-1].to_hash).to eq(
|
|
63
|
+
status: "filtered",
|
|
64
|
+
event_count: "0",
|
|
65
|
+
line_number: "23",
|
|
66
|
+
pos_start: "99",
|
|
67
|
+
pos_end: "105",
|
|
68
|
+
data: '"foobar\r\n"'
|
|
69
|
+
)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
context "#update(:record_filtered, record)" do
|
|
74
|
+
before :each do
|
|
75
|
+
observer.update(:start)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "writes a csv entry to the file indicating that it was missed" do
|
|
79
|
+
record = build(:record, line_number: 22, pos_start: 98, pos_end: 104, data: "blabla\r\n")
|
|
80
|
+
observer.update(:record_filtered, record)
|
|
81
|
+
observer.update(:end)
|
|
82
|
+
csv = CSV.read(filename, headers: true, header_converters: :symbol)
|
|
83
|
+
expect(csv[-1].to_hash).to eq(
|
|
84
|
+
status: "filtered",
|
|
85
|
+
event_count: "0",
|
|
86
|
+
line_number: "22",
|
|
87
|
+
pos_start: "98",
|
|
88
|
+
pos_end: "104",
|
|
89
|
+
data: '"blabla\r\n"'
|
|
90
|
+
)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
context "#update(:record_parsed, record, events)" do
|
|
95
|
+
before :each do
|
|
96
|
+
observer.update(:start)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
let(:record) { build(:record, line_number: 1, pos_start: 0, pos_end: 10, data: "woofwoof\r\n") }
|
|
100
|
+
let(:events) { [ build(:event), build(:event) ] }
|
|
101
|
+
|
|
102
|
+
it "writes a csv entry to the file indicating that it was parsed, with the number of events" do
|
|
103
|
+
observer.update(:record_parsed, record, events)
|
|
104
|
+
observer.update(:end)
|
|
105
|
+
csv = CSV.read(filename, headers: true, header_converters: :symbol)
|
|
106
|
+
expect(csv[-1].to_hash).to eq(
|
|
107
|
+
status: "parsed",
|
|
108
|
+
event_count: "2",
|
|
109
|
+
line_number: "1",
|
|
110
|
+
pos_start: "0",
|
|
111
|
+
pos_end: "10",
|
|
112
|
+
data: '"woofwoof\r\n"'
|
|
113
|
+
)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|