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,45 @@
|
|
|
1
|
+
require 'threatinator/record'
|
|
2
|
+
require 'threatinator/parser'
|
|
3
|
+
|
|
4
|
+
module Threatinator
|
|
5
|
+
module Parsers
|
|
6
|
+
module Getline
|
|
7
|
+
# Parses an IO, yielding each 'line' of data as deliniated by :separator.
|
|
8
|
+
# The text matching :separator will be included.
|
|
9
|
+
class Parser < Threatinator::Parser
|
|
10
|
+
attr_reader :separator
|
|
11
|
+
|
|
12
|
+
# @param [Hash] opts
|
|
13
|
+
# @option opts [String] :separator ("\n") A character that will be used
|
|
14
|
+
# to detect the end of a line.
|
|
15
|
+
def initialize(opts = {})
|
|
16
|
+
@separator = opts.delete(:separator) || "\n"
|
|
17
|
+
unless @separator.length == 1
|
|
18
|
+
raise ArgumentError.new(":separator must be exactly one character long")
|
|
19
|
+
end
|
|
20
|
+
super(opts)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def ==(other)
|
|
24
|
+
@separator == other.separator &&
|
|
25
|
+
super(other)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @param [IO] io The IO to be parsed
|
|
29
|
+
# @yield [line] Gives one line to the block
|
|
30
|
+
# @yieldparam line [String] a line from the IO stream.
|
|
31
|
+
def run(io)
|
|
32
|
+
return enum_for(:each) unless block_given?
|
|
33
|
+
lineno = 1
|
|
34
|
+
while str = io.gets(@separator)
|
|
35
|
+
return if str.nil?
|
|
36
|
+
pos_start = io.pos - str.length
|
|
37
|
+
yield Record.new(str, line_number: lineno, pos_start: pos_start, pos_end: io.pos)
|
|
38
|
+
lineno += 1
|
|
39
|
+
end
|
|
40
|
+
nil
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
require 'threatinator/parsers/json'
|
|
2
|
+
require 'threatinator/exceptions'
|
|
3
|
+
require 'oj'
|
|
4
|
+
|
|
5
|
+
module Threatinator::Parsers::JSON::Adapters
|
|
6
|
+
class Oj < ::Oj::ScHandler
|
|
7
|
+
def initialize
|
|
8
|
+
@root = nil
|
|
9
|
+
@depth = 0
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def run(io, &callback)
|
|
13
|
+
@callback = callback
|
|
14
|
+
begin
|
|
15
|
+
::Oj.sc_parse(self, io)
|
|
16
|
+
rescue ::Oj::ParseError => e
|
|
17
|
+
raise Threatinator::Exceptions::ParseError.new(e)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def do_callback(data, key = nil)
|
|
22
|
+
@callback.call(data, key: key)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def hash_start
|
|
26
|
+
ret = {}
|
|
27
|
+
@depth += 1
|
|
28
|
+
@root = ret if @root.nil?
|
|
29
|
+
ret
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def hash_set(h,k,v)
|
|
33
|
+
if @depth == 1
|
|
34
|
+
do_callback(v, k)
|
|
35
|
+
else
|
|
36
|
+
h[k] = v
|
|
37
|
+
end
|
|
38
|
+
v
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def hash_end
|
|
42
|
+
@depth -= 1
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def array_start
|
|
46
|
+
ret = []
|
|
47
|
+
@depth += 1
|
|
48
|
+
@root = ret if @root.nil?
|
|
49
|
+
ret
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def array_append(a,v)
|
|
53
|
+
if @depth == 1
|
|
54
|
+
do_callback(v)
|
|
55
|
+
else
|
|
56
|
+
a << v
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def array_end
|
|
61
|
+
@depth -= 1
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require 'threatinator/parser'
|
|
2
|
+
require 'threatinator/parsers/json/record'
|
|
3
|
+
|
|
4
|
+
module Threatinator
|
|
5
|
+
module Parsers
|
|
6
|
+
module JSON
|
|
7
|
+
class Parser < Threatinator::Parser
|
|
8
|
+
def initialize(opts = {})
|
|
9
|
+
@adapter_class = self.class.adapter_class
|
|
10
|
+
super(opts)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Detects the platform, loads the JSON adapter, and returns the
|
|
14
|
+
# adapter's class.
|
|
15
|
+
def self.adapter_class
|
|
16
|
+
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
|
|
17
|
+
#:nocov:
|
|
18
|
+
raise "JSON parsing not supported for JRuby"
|
|
19
|
+
#:nocov:
|
|
20
|
+
else
|
|
21
|
+
require 'threatinator/parsers/json/adapters/oj'
|
|
22
|
+
return Threatinator::Parsers::JSON::Adapters::Oj
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def ==(other)
|
|
27
|
+
super(other)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# @param [IO] io
|
|
31
|
+
# @yield [record] Gives one line to the block
|
|
32
|
+
# @yieldparam record [Threatinator::Parsers::JSON::Record] a record
|
|
33
|
+
def run(io)
|
|
34
|
+
adapter = @adapter_class.new
|
|
35
|
+
callback = lambda do |object, opts = {}|
|
|
36
|
+
yield Threatinator::Parsers::JSON::Record.new(object, opts)
|
|
37
|
+
end
|
|
38
|
+
adapter.run(io, &callback)
|
|
39
|
+
nil
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require 'threatinator/record'
|
|
2
|
+
|
|
3
|
+
module Threatinator
|
|
4
|
+
module Parsers
|
|
5
|
+
module JSON
|
|
6
|
+
class Record < Threatinator::Record
|
|
7
|
+
attr_reader :key
|
|
8
|
+
def initialize(object, opts = {})
|
|
9
|
+
@key = opts.delete(:key)
|
|
10
|
+
super(object, opts)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def ==(other)
|
|
14
|
+
@key == other.key &&
|
|
15
|
+
super(other)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
module Threatinator
|
|
2
|
+
module Parsers
|
|
3
|
+
module XML
|
|
4
|
+
class Node
|
|
5
|
+
attr_accessor :text
|
|
6
|
+
attr_reader :name
|
|
7
|
+
attr_reader :attrs
|
|
8
|
+
attr_reader :children
|
|
9
|
+
|
|
10
|
+
# @param [String, Symbol] name
|
|
11
|
+
# @param [Hash] opts
|
|
12
|
+
# @option opts [String] :text The text
|
|
13
|
+
# @option opts [Hash] :attrs The attributes
|
|
14
|
+
# @option opts [Array<Threatinator::Parsers::XML::Node>] :children An array
|
|
15
|
+
# of child child nodes that belong to this node.
|
|
16
|
+
def initialize(name, opts = {})
|
|
17
|
+
unless name.kind_of?(::Symbol) or name.kind_of?(::String)
|
|
18
|
+
raise TypeError.new("name must be a String or a Symbol")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
@name = name.to_sym
|
|
22
|
+
@text = opts.delete(:text) || ""
|
|
23
|
+
unless @text.kind_of?(::String)
|
|
24
|
+
raise TypeError.new(":text must be a String")
|
|
25
|
+
end
|
|
26
|
+
@attrs = opts.delete(:attrs) || {}
|
|
27
|
+
unless @attrs.kind_of?(::Hash)
|
|
28
|
+
raise TypeError.new(":text must be a Hash")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
@children = {}
|
|
32
|
+
if _children = opts.delete(:children)
|
|
33
|
+
_children.each do |child|
|
|
34
|
+
add_child(child)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def ==(other)
|
|
40
|
+
@name == other.name &&
|
|
41
|
+
@attrs == other.attrs &&
|
|
42
|
+
@text == other.text &&
|
|
43
|
+
@children == other.children
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def eql?(other)
|
|
47
|
+
other.kind_of?(self.class) &&
|
|
48
|
+
self == other
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# @return [Integer] the number of children
|
|
52
|
+
def num_children
|
|
53
|
+
@children.values.inject(0) {|total, child_set| total + child_set.count}
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# @param [String, Symbol] name The name of the child element
|
|
57
|
+
# @return [Array<Node>] An array containing all the child nodes for the given
|
|
58
|
+
# name. The array will be empty if there are no children by the given name.
|
|
59
|
+
def [](name)
|
|
60
|
+
@children[name.to_sym] || []
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# @return [Array<Symbol>] an array containing all the names of child elements
|
|
64
|
+
def child_names
|
|
65
|
+
@children.keys
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
def add_child(child)
|
|
70
|
+
name = child.name
|
|
71
|
+
unless child_set = @children[name]
|
|
72
|
+
child_set = @children[name] = []
|
|
73
|
+
end
|
|
74
|
+
child_set << child
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require 'threatinator/parsers/xml/node'
|
|
2
|
+
|
|
3
|
+
module Threatinator
|
|
4
|
+
module Parsers
|
|
5
|
+
module XML
|
|
6
|
+
class NodeBuilder
|
|
7
|
+
def initialize(name, attributes)
|
|
8
|
+
@name = name
|
|
9
|
+
@attributes = {}
|
|
10
|
+
@children = []
|
|
11
|
+
@text = ""
|
|
12
|
+
|
|
13
|
+
unless attributes.empty?
|
|
14
|
+
attributes.each { |attr| self.add_attribute(attr.localname, attr.value) }
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def append_text(chars)
|
|
19
|
+
@text << chars
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def add_attribute(name, value)
|
|
23
|
+
@attributes[name.to_sym] = value
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def add_child(node)
|
|
27
|
+
@children << node
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def build
|
|
31
|
+
Threatinator::Parsers::XML::Node.new(@name,
|
|
32
|
+
attrs: @attributes,
|
|
33
|
+
text: @text.strip,
|
|
34
|
+
children: @children)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require 'threatinator/parser'
|
|
2
|
+
require 'nokogiri'
|
|
3
|
+
|
|
4
|
+
module Threatinator
|
|
5
|
+
module Parsers
|
|
6
|
+
module XML
|
|
7
|
+
class Parser < Threatinator::Parser
|
|
8
|
+
require 'threatinator/parsers/xml/path'
|
|
9
|
+
require 'threatinator/parsers/xml/record'
|
|
10
|
+
require 'threatinator/parsers/xml/sax_document'
|
|
11
|
+
|
|
12
|
+
attr_reader :pattern
|
|
13
|
+
# @param [Hash] opts Parameters hash
|
|
14
|
+
# @option opts [Threatinator::Parsers::XML::Pattern] :pattern The pattern
|
|
15
|
+
# object to use for matching chunks of XML
|
|
16
|
+
def initialize(opts = {})
|
|
17
|
+
@pattern = opts.delete(:pattern) or raise ArgumentError.new("Missing argument :pattern")
|
|
18
|
+
@max_cursor_depth = @pattern.max_depth - 1
|
|
19
|
+
super(opts)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def ==(other)
|
|
23
|
+
@pattern == other.pattern &&
|
|
24
|
+
super(other)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @param [IO] io
|
|
28
|
+
# @yield [record] Gives one line to the block
|
|
29
|
+
# @yieldparam record [Threatinator::Parser::XML::Record] a record
|
|
30
|
+
def run(io)
|
|
31
|
+
stack = Path.new
|
|
32
|
+
callback = lambda do |element|
|
|
33
|
+
yield(Threatinator::Parsers::XML::Record.new(element))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
doc = SAXDocument.new(@pattern, callback)
|
|
37
|
+
parser = Nokogiri::XML::SAX::Parser.new(doc)
|
|
38
|
+
parser.parse(io)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
module Threatinator
|
|
2
|
+
module Parsers
|
|
3
|
+
module XML
|
|
4
|
+
class Path
|
|
5
|
+
attr_reader :parts
|
|
6
|
+
|
|
7
|
+
# @param [String, Array, nil] str_or_parts ([]) If set to a String, splits
|
|
8
|
+
# the string by '/' into an array. If set to an Array, sets parts to a
|
|
9
|
+
# duplicate of the array. If set to nil or not specified, defaults to
|
|
10
|
+
# a new array.
|
|
11
|
+
# @raise [TypeError] if something other than a String, Array, or nil is
|
|
12
|
+
# specified for str_or_parts.
|
|
13
|
+
def initialize(str_or_parts = nil)
|
|
14
|
+
@parts =
|
|
15
|
+
case str_or_parts
|
|
16
|
+
when ::String
|
|
17
|
+
if str_or_parts.length == 0 or !str_or_parts.start_with?('/')
|
|
18
|
+
raise ArgumentError.new('str_or_parts must be a String beginning with "/"')
|
|
19
|
+
end
|
|
20
|
+
r = str_or_parts.split('/')
|
|
21
|
+
r.shift
|
|
22
|
+
r
|
|
23
|
+
when ::Array
|
|
24
|
+
str_or_parts.dup
|
|
25
|
+
when nil
|
|
26
|
+
[]
|
|
27
|
+
else
|
|
28
|
+
raise TypeError.new("Expected argument must be a String, Array, or nil")
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def ==(other)
|
|
33
|
+
@parts == other.parts
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def eql?(other)
|
|
37
|
+
other.kind_of?(self.class) &&
|
|
38
|
+
self == other
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# length = 5
|
|
42
|
+
# 0 1 2 3 4
|
|
43
|
+
# /a/b/c/d/e
|
|
44
|
+
# 0 1
|
|
45
|
+
# /d/e
|
|
46
|
+
def end_with?(other_path)
|
|
47
|
+
return false if other_path.length > self.length
|
|
48
|
+
return true if other_path.length == 0
|
|
49
|
+
pos = length - other_path.length
|
|
50
|
+
other_path.parts.each_with_index do |other_part, idx|
|
|
51
|
+
return false unless @parts[(pos + idx)] == other_part
|
|
52
|
+
end
|
|
53
|
+
true
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def push(name)
|
|
57
|
+
@parts.push(name)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def pop
|
|
61
|
+
@parts.pop
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def length
|
|
65
|
+
@parts.length
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|