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