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,30 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'threatinator/parsers/json/record'
|
|
3
|
+
|
|
4
|
+
describe Threatinator::Parsers::JSON::Record do
|
|
5
|
+
it_should_behave_like 'a record' do
|
|
6
|
+
let(:data) { {"some" => "data"} }
|
|
7
|
+
let(:opts) { { } }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
context "two instances with different data" do
|
|
11
|
+
it_should_behave_like 'a record when compared to a differently configured record' do
|
|
12
|
+
let(:data) { {"some" => "data"} }
|
|
13
|
+
let(:opts) { { } }
|
|
14
|
+
|
|
15
|
+
let(:data2) { {"some_other" => "data"} }
|
|
16
|
+
let(:opts2) { {} }
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
context "two instances with the same data but a different :key" do
|
|
21
|
+
it_should_behave_like 'a record when compared to a differently configured record' do
|
|
22
|
+
let(:data) { {"some" => "data"} }
|
|
23
|
+
let(:opts) { { key: "foo" } }
|
|
24
|
+
|
|
25
|
+
let(:data2) { {"some" => "data"} }
|
|
26
|
+
let(:opts2) { { key: "bar" } }
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'threatinator/parsers/xml/node'
|
|
3
|
+
require 'set'
|
|
4
|
+
|
|
5
|
+
describe Threatinator::Parsers::XML::Node do
|
|
6
|
+
describe "initializing" do
|
|
7
|
+
describe "name" do
|
|
8
|
+
context "when a symbol is provided" do
|
|
9
|
+
specify "the name is the symbol" do
|
|
10
|
+
node = described_class.new(:foobar)
|
|
11
|
+
expect(node.name).to be(:foobar)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
context "when a string is provided" do
|
|
15
|
+
specify "the name is the symbolized version of the string" do
|
|
16
|
+
node = described_class.new("foobar")
|
|
17
|
+
expect(node.name).to be(:foobar)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
context "when it is neither a string nor a symbol" do
|
|
21
|
+
it "raises a TypeError" do
|
|
22
|
+
expect { described_class.new(Object.new) }.to raise_error(TypeError)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
describe ":text" do
|
|
28
|
+
context "when a string" do
|
|
29
|
+
specify "the text is the string" do
|
|
30
|
+
node = described_class.new(:foo, text: "hey there big boy")
|
|
31
|
+
expect(node.text).to eq("hey there big boy")
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
context "when nil" do
|
|
36
|
+
specify "the text is an empty string" do
|
|
37
|
+
node = described_class.new(:foo, text: nil)
|
|
38
|
+
expect(node.text).to eq("")
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
context "when not provided" do
|
|
43
|
+
specify "the text is an empty string" do
|
|
44
|
+
node = described_class.new(:foo)
|
|
45
|
+
expect(node.text).to eq("")
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
context "when something other than a string or nil" do
|
|
49
|
+
it "raises a TypeError" do
|
|
50
|
+
expect { described_class.new(:foo, text: Object.new) }.to raise_error(TypeError)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
describe ":attrs" do
|
|
56
|
+
context "when not provided" do
|
|
57
|
+
specify "the attrs are an empty hash" do
|
|
58
|
+
node = described_class.new(:foo)
|
|
59
|
+
expect(node.attrs).to eq({})
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
context "when nil" do
|
|
63
|
+
specify "the attrs are an empty hash" do
|
|
64
|
+
node = described_class.new(:foo, attrs: nil)
|
|
65
|
+
expect(node.attrs).to eq({})
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
context "when a hash of attributes" do
|
|
69
|
+
specify "the attrs are the provided attributes" do
|
|
70
|
+
node = described_class.new(:foo, attrs: {foo: "asdf", bar: "123"})
|
|
71
|
+
expect(node.attrs).to eq({foo:"asdf", bar:"123"})
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
context "when something other than a hash or nil" do
|
|
75
|
+
it "raises a TypeError" do
|
|
76
|
+
expect { described_class.new(:foo, attrs: Object.new) }.to raise_error(TypeError)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
describe ":children" do
|
|
82
|
+
context "when not provided" do
|
|
83
|
+
specify "it should have no children" do
|
|
84
|
+
node = described_class.new(:foo)
|
|
85
|
+
expect(node.children).to eq({})
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
context "when nil" do
|
|
90
|
+
specify "it should have no children" do
|
|
91
|
+
node = described_class.new(:foo, children: nil)
|
|
92
|
+
expect(node.children).to eq({})
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
context "when a collection of Nodes" do
|
|
97
|
+
specify "it should have all those nodes as children" do
|
|
98
|
+
children = build_list(:xml_node, 20, name: :bla)
|
|
99
|
+
node = described_class.new(:foo, children: children)
|
|
100
|
+
expect(node.num_children).to eq(20)
|
|
101
|
+
expect(node[:bla]).to match_array(children)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
describe "#==" do
|
|
108
|
+
it "returns true when compared to itself" do
|
|
109
|
+
node = build(:xml_node)
|
|
110
|
+
expect(node).to be == node
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it "returns true when compared an identically configured node" do
|
|
114
|
+
node1 = build(:xml_node)
|
|
115
|
+
node2 = build(:xml_node)
|
|
116
|
+
expect(node1).to be == node2
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it "returns true when compared to an object that looks like the node" do
|
|
120
|
+
node1 = build(:xml_node)
|
|
121
|
+
node2 = build(:xml_node)
|
|
122
|
+
fake_node_class = Struct.new(:name, :attrs, :text, :children)
|
|
123
|
+
fake_node = fake_node_class.new(node2.name, node2.attrs, node2.text, node2.children)
|
|
124
|
+
expect(node1).to be == fake_node
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
it "returns false when the attributes are different" do
|
|
128
|
+
node1 = build(:xml_node, attrs: {attr1: "abc"} )
|
|
129
|
+
node2 = build(:xml_node, attrs: {attr1: "xyz"} )
|
|
130
|
+
expect(node1).not_to be == node2
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it "returns false when the text is different" do
|
|
134
|
+
node1 = build(:xml_node, text: "abc")
|
|
135
|
+
node2 = build(:xml_node, text: "xyz")
|
|
136
|
+
expect(node1).not_to be == node2
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
it "returns false when the children are different" do
|
|
140
|
+
node1 = build(:xml_node, children: [build(:xml_node, name: "a", text: "123"), build(:xml_node, name: "b", text: "xyz")] )
|
|
141
|
+
node2 = build(:xml_node, children: [build(:xml_node, name: "a", text: "456"), build(:xml_node, name: "b", text: "abc")] )
|
|
142
|
+
expect(node1).not_to be == node2
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it "returns false when the name is different" do
|
|
146
|
+
node1 = build(:xml_node, name: "foo")
|
|
147
|
+
node2 = build(:xml_node, name: "bar")
|
|
148
|
+
expect(node1).not_to be == node2
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
describe "#eql?" do
|
|
153
|
+
it "returns true when compared to itself" do
|
|
154
|
+
node = build(:xml_node)
|
|
155
|
+
expect(node).to be_eql(node)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
it "returns true when compared an identically configured node" do
|
|
159
|
+
node1 = build(:xml_node)
|
|
160
|
+
node2 = build(:xml_node)
|
|
161
|
+
expect(node1).to be_eql(node2)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
it "returns false when compared to an object that looks like the node" do
|
|
165
|
+
node1 = build(:xml_node)
|
|
166
|
+
node2 = build(:xml_node)
|
|
167
|
+
fake_node_class = Struct.new(:name, :attrs, :text, :children)
|
|
168
|
+
fake_node = fake_node_class.new(node2.name, node2.attrs, node2.text, node2.children)
|
|
169
|
+
expect(node1).not_to be_eql(fake_node)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
it "returns false when the attributes are different" do
|
|
173
|
+
node1 = build(:xml_node, attrs: {attr1: "abc"} )
|
|
174
|
+
node2 = build(:xml_node, attrs: {attr1: "xyz"} )
|
|
175
|
+
expect(node1).not_to be_eql(node2)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
it "returns false when the text is different" do
|
|
179
|
+
node1 = build(:xml_node, text: "abc")
|
|
180
|
+
node2 = build(:xml_node, text: "xyz")
|
|
181
|
+
expect(node1).not_to be_eql(node2)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
it "returns false when the children are different" do
|
|
185
|
+
node1 = build(:xml_node, children: [build(:xml_node, name: "a", text: "123"), build(:xml_node, name: "b", text: "xyz")] )
|
|
186
|
+
node2 = build(:xml_node, children: [build(:xml_node, name: "a", text: "456"), build(:xml_node, name: "b", text: "abc")] )
|
|
187
|
+
expect(node1).not_to be_eql(node2)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
it "returns false when the name is different" do
|
|
191
|
+
node1 = build(:xml_node, name: "foo")
|
|
192
|
+
node2 = build(:xml_node, name: "bar")
|
|
193
|
+
expect(node1).not_to be_eql(node2)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
describe "#equal?" do
|
|
198
|
+
it "returns true tested against itself" do
|
|
199
|
+
node = build(:xml_node)
|
|
200
|
+
expect(node).to be_equal(node)
|
|
201
|
+
end
|
|
202
|
+
it "returns true when compared an identically configured node" do
|
|
203
|
+
node = build(:xml_node)
|
|
204
|
+
node2 = build(:xml_node)
|
|
205
|
+
expect(node).not_to be_equal(node2)
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
describe "#children" do
|
|
210
|
+
let(:children) { [] }
|
|
211
|
+
let(:node) { build(:xml_node, children: children) }
|
|
212
|
+
|
|
213
|
+
it "returns a Hash" do
|
|
214
|
+
expect(node.children).to be_a(Hash)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
it "is empty when there are no children" do
|
|
218
|
+
expect(node.children).to be_empty
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
context "with children" do
|
|
222
|
+
let(:children) {
|
|
223
|
+
[
|
|
224
|
+
build(:xml_node, name: "a", text: "1"),
|
|
225
|
+
build(:xml_node, name: "a", text: "2"),
|
|
226
|
+
build(:xml_node, name: "b", text: "3"),
|
|
227
|
+
build(:xml_node, name: "c", text: "4")
|
|
228
|
+
]
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
specify "the keys are all symbols" do
|
|
232
|
+
expect(node.children.keys).to contain_exactly(kind_of(Symbol), kind_of(Symbol), kind_of(Symbol))
|
|
233
|
+
end
|
|
234
|
+
specify "the keys are names of the children" do
|
|
235
|
+
expect(node.children.keys).to contain_exactly(:a, :b, :c)
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
specify "the corresponding values are collections of the child nodes" do
|
|
239
|
+
expect(node.children[:a]).to contain_exactly(
|
|
240
|
+
build(:xml_node, name: "a", text: "1"),
|
|
241
|
+
build(:xml_node, name: "a", text: "2"))
|
|
242
|
+
expect(node.children[:b]).to contain_exactly(
|
|
243
|
+
build(:xml_node, name: "b", text: "3"))
|
|
244
|
+
expect(node.children[:c]).to contain_exactly(
|
|
245
|
+
build(:xml_node, name: "c", text: "4"))
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
describe "#[]" do
|
|
251
|
+
it "returns an empty array if there are no child elements for the given name" do
|
|
252
|
+
node = build(:xml_node)
|
|
253
|
+
expect(node[:foo]).to eq([])
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
it "returns an array the matching child nodes in the order in which they were added" do
|
|
257
|
+
child1 = build(:xml_node, name: :woof)
|
|
258
|
+
child2 = build(:xml_node, name: :woof)
|
|
259
|
+
child3 = build(:xml_node, name: :woof)
|
|
260
|
+
child4 = build(:xml_node, name: :bark)
|
|
261
|
+
child5 = build(:xml_node, name: :bark)
|
|
262
|
+
child6 = build(:xml_node, name: :bark)
|
|
263
|
+
node = build(:xml_node, children: [child1, child4, child2, child5, child3, child6])
|
|
264
|
+
children = node[:woof]
|
|
265
|
+
expect(children[0]).to be(child1)
|
|
266
|
+
expect(children[1]).to be(child2)
|
|
267
|
+
expect(children[2]).to be(child3)
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
it "accepts either a String or a Symbol, treating them equally" do
|
|
271
|
+
child = build(:xml_node, name: :moo)
|
|
272
|
+
node = build(:xml_node, children: [ child ])
|
|
273
|
+
expect(node[:moo]).to eq([child])
|
|
274
|
+
expect(node["moo"]).to eq([child])
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
describe "#child_names" do
|
|
279
|
+
context "with no children" do
|
|
280
|
+
it "returns an empty array" do
|
|
281
|
+
node = build(:xml_node)
|
|
282
|
+
expect(node.child_names).to eq([])
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
context "with children" do
|
|
286
|
+
it "returns an array of symbols for all the names of the child elements" do
|
|
287
|
+
children = [
|
|
288
|
+
build(:xml_node, name: :a),
|
|
289
|
+
build(:xml_node, name: :a),
|
|
290
|
+
build(:xml_node, name: :b),
|
|
291
|
+
build(:xml_node, name: :b),
|
|
292
|
+
build(:xml_node, name: :c),
|
|
293
|
+
build(:xml_node, name: :c)
|
|
294
|
+
]
|
|
295
|
+
node = build(:xml_node, children: children)
|
|
296
|
+
expect(node.child_names).to contain_exactly(:a, :b, :c)
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
describe "#num_children" do
|
|
302
|
+
context "with no children" do
|
|
303
|
+
let(:node) { build(:xml_node) }
|
|
304
|
+
it "returns 0" do
|
|
305
|
+
expect(node.num_children).to eq(0)
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
context "with a bunch of children added" do
|
|
310
|
+
let(:node) { build(:xml_node, children: children) }
|
|
311
|
+
let(:children) { build_list(:xml_node, 50) }
|
|
312
|
+
it "returns the total number of children" do
|
|
313
|
+
expect(node.num_children).to eq(50)
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
describe "#text" do
|
|
319
|
+
it "returns an empty string when the node has no text" do
|
|
320
|
+
node = build(:xml_node)
|
|
321
|
+
expect(node.text).to eq("")
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
it "returns the string of text assigned to the node" do
|
|
325
|
+
node = build(:xml_node, text: "hey there big boy")
|
|
326
|
+
expect(node.text).to eq("hey there big boy")
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
describe "#name" do
|
|
330
|
+
it "it returns the name of the node as a symbol" do
|
|
331
|
+
node = build(:xml_node, name: :foobar)
|
|
332
|
+
expect(node.name).to be(:foobar)
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
end
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'threatinator/parsers/xml'
|
|
3
|
+
require 'threatinator/parsers/xml/pattern'
|
|
4
|
+
|
|
5
|
+
describe Threatinator::Parsers::XML::Parser, :parser do
|
|
6
|
+
context "two instances with identically configured patterns" do
|
|
7
|
+
it_should_behave_like "a parser when compared to an identically configured parser" do
|
|
8
|
+
let(:pattern1) { Threatinator::Parsers::XML::Pattern.new('/foo/bar') }
|
|
9
|
+
let(:pattern2) { Threatinator::Parsers::XML::Pattern.new('/foo/bar') }
|
|
10
|
+
let(:parser1) { described_class.new(pattern: pattern1) }
|
|
11
|
+
let(:parser2) { described_class.new(pattern: pattern2) }
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
context "two instances with differently configured patterns" do
|
|
16
|
+
it_should_behave_like "a parser when compared to a differently configured parser" do
|
|
17
|
+
let(:pattern1) { Threatinator::Parsers::XML::Pattern.new('/foo/bar') }
|
|
18
|
+
let(:pattern2) { Threatinator::Parsers::XML::Pattern.new('//foo/bar') }
|
|
19
|
+
let(:parser1) { described_class.new(pattern: pattern1) }
|
|
20
|
+
let(:parser2) { described_class.new(pattern: pattern2) }
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
shared_examples_for "an XML::Record" do
|
|
25
|
+
it { is_expected.to be_a(Threatinator::Parsers::XML::Record) }
|
|
26
|
+
its(:node) { is_expected.to be_a(Threatinator::Parsers::XML::Node) }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
shared_examples_for "a parser matching nothing" do
|
|
31
|
+
it "should yield 0 records" do
|
|
32
|
+
expect(records.count).to eq(0)
|
|
33
|
+
end
|
|
34
|
+
end #
|
|
35
|
+
|
|
36
|
+
shared_context "parsing xml" do
|
|
37
|
+
let(:io) { StringIO.new(xml) }
|
|
38
|
+
let(:parser) { described_class.new(pattern: pattern) }
|
|
39
|
+
let!(:records) {
|
|
40
|
+
ret = []
|
|
41
|
+
parser.run(io) { |r| ret << r }
|
|
42
|
+
ret
|
|
43
|
+
}
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
context "parsing records from test_self_closing.xml" do
|
|
47
|
+
let(:xml) { File.read(parser_data('test_self_closing.xml')) }
|
|
48
|
+
include_context "parsing xml"
|
|
49
|
+
|
|
50
|
+
shared_examples_for "a parser matching each 'z' element" do
|
|
51
|
+
it "should yield 4 records" do
|
|
52
|
+
expect(records.count).to eq(4)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
describe "record 0" do
|
|
56
|
+
let(:record) { records[0] }
|
|
57
|
+
let(:node) { record.node }
|
|
58
|
+
subject { record }
|
|
59
|
+
it_should_behave_like "an XML::Record"
|
|
60
|
+
describe "the data from the node" do
|
|
61
|
+
subject { node }
|
|
62
|
+
its(:name) { is_expected.to eq(:z) }
|
|
63
|
+
its(:attrs) { is_expected.to eq({foo: "bar1"}) }
|
|
64
|
+
its(:text) { is_expected.to eq("") }
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
describe "record 1" do
|
|
68
|
+
let(:record) { records[1] }
|
|
69
|
+
let(:node) { record.node }
|
|
70
|
+
subject { record }
|
|
71
|
+
it_should_behave_like "an XML::Record"
|
|
72
|
+
describe "the data from the node" do
|
|
73
|
+
subject { node }
|
|
74
|
+
its(:name) { is_expected.to eq(:z) }
|
|
75
|
+
its(:attrs) { is_expected.to eq({foo: "bar2"}) }
|
|
76
|
+
its(:text) { is_expected.to eq("") }
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
describe "record 2" do
|
|
80
|
+
let(:record) { records[2] }
|
|
81
|
+
let(:node) { record.node }
|
|
82
|
+
subject { record }
|
|
83
|
+
it_should_behave_like "an XML::Record"
|
|
84
|
+
describe "the data from the node" do
|
|
85
|
+
subject { node }
|
|
86
|
+
its(:name) { is_expected.to eq(:z) }
|
|
87
|
+
its(:attrs) { is_expected.to eq({foo: "bar3"}) }
|
|
88
|
+
its(:text) { is_expected.to eq("")}
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
describe "record 3" do
|
|
92
|
+
let(:record) { records[3] }
|
|
93
|
+
let(:node) { record.node }
|
|
94
|
+
subject { record }
|
|
95
|
+
it_should_behave_like "an XML::Record"
|
|
96
|
+
describe "the data from the node" do
|
|
97
|
+
subject { node }
|
|
98
|
+
its(:name) { is_expected.to eq(:z) }
|
|
99
|
+
its(:attrs) { is_expected.to eq({foo: "bar4"}) }
|
|
100
|
+
its(:text) { is_expected.to eq("")}
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end #
|
|
104
|
+
|
|
105
|
+
context "with the pattern '/doc/x/y/z'" do
|
|
106
|
+
let(:pattern) { Threatinator::Parsers::XML::Pattern.new("/doc/x/y/z") }
|
|
107
|
+
it_should_behave_like "a parser matching each 'z' element"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
context "with the pattern '//x/y/z'" do
|
|
111
|
+
let(:pattern) { Threatinator::Parsers::XML::Pattern.new("//x/y/z") }
|
|
112
|
+
it_should_behave_like "a parser matching each 'z' element"
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
context "with the pattern '//z'" do
|
|
116
|
+
let(:pattern) { Threatinator::Parsers::XML::Pattern.new("//z") }
|
|
117
|
+
it_should_behave_like "a parser matching each 'z' element"
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
context "with the pattern '/y/z/x'" do
|
|
122
|
+
let(:pattern) { Threatinator::Parsers::XML::Pattern.new("/y/z/x") }
|
|
123
|
+
it_should_behave_like "a parser matching nothing"
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
context "parsing records from test.xml" do
|
|
128
|
+
let(:xml) { File.read(parser_data('test.xml')) }
|
|
129
|
+
include_context "parsing xml"
|
|
130
|
+
|
|
131
|
+
shared_examples_for "a parser matching each 'b' element" do
|
|
132
|
+
it "should yield 2 records" do
|
|
133
|
+
expect(records.count).to eq(2)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
describe "record 0" do
|
|
137
|
+
let(:record) { records[0] }
|
|
138
|
+
let(:node) { record.node }
|
|
139
|
+
subject { record }
|
|
140
|
+
it_should_behave_like "an XML::Record"
|
|
141
|
+
describe "the data from the node" do
|
|
142
|
+
subject { node }
|
|
143
|
+
its(:name) { is_expected.to eq(:b) }
|
|
144
|
+
its(:attrs) { is_expected.to eq({}) }
|
|
145
|
+
its(:text) { is_expected.to eq("")}
|
|
146
|
+
its(:children) {
|
|
147
|
+
is_expected.to match(
|
|
148
|
+
{
|
|
149
|
+
c: a_collection_containing_exactly(
|
|
150
|
+
build(:xml_node, name: 'c', text: 'deep1A'),
|
|
151
|
+
build(:xml_node, name: 'c', text: 'deep1B')
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
describe "record 1" do
|
|
159
|
+
let(:record) { records[1] }
|
|
160
|
+
let(:node) { record.node }
|
|
161
|
+
subject { record }
|
|
162
|
+
it_should_behave_like "an XML::Record"
|
|
163
|
+
describe "the data from the node" do
|
|
164
|
+
subject { node }
|
|
165
|
+
its(:name) { is_expected.to eq(:b) }
|
|
166
|
+
its(:attrs) { is_expected.to eq({}) }
|
|
167
|
+
its(:text) { is_expected.to eq("")}
|
|
168
|
+
its(:children) {
|
|
169
|
+
is_expected.to match(
|
|
170
|
+
{
|
|
171
|
+
c: a_collection_containing_exactly(
|
|
172
|
+
build(:xml_node, name: 'c', text: 'deep2A'),
|
|
173
|
+
build(:xml_node, name: 'c', text: 'deep2B')
|
|
174
|
+
)
|
|
175
|
+
}
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end #
|
|
181
|
+
|
|
182
|
+
shared_examples_for "a parser matching each 'c' element" do
|
|
183
|
+
it "should yield 4 records" do
|
|
184
|
+
expect(records.count).to eq(4)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
describe "record 0" do
|
|
188
|
+
let(:record) { records[0] }
|
|
189
|
+
let(:node) { record.node }
|
|
190
|
+
subject { record }
|
|
191
|
+
it_should_behave_like "an XML::Record"
|
|
192
|
+
describe "the data from the node" do
|
|
193
|
+
subject { node }
|
|
194
|
+
its(:name) { is_expected.to eq(:c) }
|
|
195
|
+
its(:attrs) { is_expected.to eq({}) }
|
|
196
|
+
its(:text) { "deep1A" }
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
describe "record 1" do
|
|
200
|
+
let(:record) { records[1] }
|
|
201
|
+
let(:node) { record.node }
|
|
202
|
+
subject { record }
|
|
203
|
+
it_should_behave_like "an XML::Record"
|
|
204
|
+
describe "the data from the node" do
|
|
205
|
+
subject { node }
|
|
206
|
+
its(:name) { is_expected.to eq(:c) }
|
|
207
|
+
its(:attrs) { is_expected.to eq({}) }
|
|
208
|
+
its(:text) { "deep1B" }
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
describe "record 2" do
|
|
212
|
+
let(:record) { records[2] }
|
|
213
|
+
let(:node) { record.node }
|
|
214
|
+
subject { record }
|
|
215
|
+
it_should_behave_like "an XML::Record"
|
|
216
|
+
describe "the data from the node" do
|
|
217
|
+
subject { node }
|
|
218
|
+
its(:name) { is_expected.to eq(:c) }
|
|
219
|
+
its(:attrs) { is_expected.to eq({}) }
|
|
220
|
+
its(:text) { "deep2A" }
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
describe "record 3" do
|
|
224
|
+
let(:record) { records[3] }
|
|
225
|
+
let(:node) { record.node }
|
|
226
|
+
subject { record }
|
|
227
|
+
it_should_behave_like "an XML::Record"
|
|
228
|
+
describe "the data from the node" do
|
|
229
|
+
subject { node }
|
|
230
|
+
its(:name) { is_expected.to eq(:c) }
|
|
231
|
+
its(:attrs) { is_expected.to eq({}) }
|
|
232
|
+
its(:text) { "deep2B" }
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
end #
|
|
236
|
+
|
|
237
|
+
context "with the pattern '/doc/a/b/c'" do
|
|
238
|
+
let(:pattern) { Threatinator::Parsers::XML::Pattern.new("/doc/a/b/c") }
|
|
239
|
+
it_should_behave_like "a parser matching each 'c' element"
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
context "with the pattern '//a/b/c'" do
|
|
244
|
+
let(:pattern) { Threatinator::Parsers::XML::Pattern.new("//a/b/c") }
|
|
245
|
+
it_should_behave_like "a parser matching each 'c' element"
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
context "with the pattern '//b'" do
|
|
249
|
+
let(:pattern) { Threatinator::Parsers::XML::Pattern.new("//b") }
|
|
250
|
+
it_should_behave_like "a parser matching each 'b' element"
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
context "with the pattern '//c'" do
|
|
254
|
+
let(:pattern) { Threatinator::Parsers::XML::Pattern.new("//a/b/c") }
|
|
255
|
+
it_should_behave_like "a parser matching each 'c' element"
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
context "with the pattern '/c/b/a'" do
|
|
259
|
+
let(:pattern) { Threatinator::Parsers::XML::Pattern.new("/c/b/a") }
|
|
260
|
+
it_should_behave_like "a parser matching nothing"
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
end
|