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,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
|