mihari 5.2.1 → 5.2.3
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 +4 -4
- data/.rubocop.yml +2 -0
- data/lib/mihari/analyzers/base.rb +20 -115
- data/lib/mihari/analyzers/binaryedge.rb +0 -1
- data/lib/mihari/analyzers/censys.rb +26 -3
- data/lib/mihari/analyzers/circl.rb +1 -1
- data/lib/mihari/analyzers/onyphe.rb +1 -1
- data/lib/mihari/analyzers/passivetotal.rb +1 -1
- data/lib/mihari/analyzers/rule.rb +122 -75
- data/lib/mihari/analyzers/shodan.rb +1 -1
- data/lib/mihari/analyzers/urlscan.rb +6 -9
- data/lib/mihari/analyzers/virustotal_intelligence.rb +1 -6
- data/lib/mihari/cli/main.rb +2 -2
- data/lib/mihari/clients/base.rb +1 -1
- data/lib/mihari/commands/database.rb +12 -11
- data/lib/mihari/commands/rule.rb +47 -45
- data/lib/mihari/commands/search.rb +88 -0
- data/lib/mihari/commands/version.rb +8 -6
- data/lib/mihari/commands/web.rb +26 -23
- data/lib/mihari/emitters/base.rb +14 -1
- data/lib/mihari/emitters/database.rb +3 -10
- data/lib/mihari/emitters/misp.rb +16 -5
- data/lib/mihari/emitters/slack.rb +13 -15
- data/lib/mihari/emitters/the_hive.rb +17 -19
- data/lib/mihari/emitters/webhook.rb +23 -23
- data/lib/mihari/enrichers/whois.rb +1 -0
- data/lib/mihari/feed/parser.rb +1 -0
- data/lib/mihari/feed/reader.rb +29 -14
- data/lib/mihari/mixins/configurable.rb +13 -4
- data/lib/mihari/mixins/error_notification.rb +0 -2
- data/lib/mihari/models/artifact.rb +1 -1
- data/lib/mihari/schemas/rule.rb +2 -17
- data/lib/mihari/structs/censys.rb +226 -56
- data/lib/mihari/structs/config.rb +48 -18
- data/lib/mihari/structs/google_public_dns.rb +56 -14
- data/lib/mihari/structs/greynoise.rb +122 -29
- data/lib/mihari/structs/ipinfo.rb +40 -0
- data/lib/mihari/structs/onyphe.rb +112 -26
- data/lib/mihari/structs/rule.rb +4 -2
- data/lib/mihari/structs/shodan.rb +189 -47
- data/lib/mihari/structs/urlscan.rb +123 -20
- data/lib/mihari/structs/virustotal_intelligence.rb +129 -26
- data/lib/mihari/type_checker.rb +10 -8
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari.rb +1 -0
- data/mihari.gemspec +11 -10
- metadata +35 -36
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -43
- data/.github/ISSUE_TEMPLATE/feature_request.md +0 -15
- data/.github/workflows/test.yml +0 -90
- data/config/pre_commit.yml +0 -3
- data/docker/Dockerfile +0 -14
- data/examples/ipinfo_hosted_domains.rb +0 -45
- data/images/Tines-Full_Logo-Tines_Black.png +0 -0
- data/images/alert.png +0 -0
- data/images/logo.png +0 -0
- data/images/misp.png +0 -0
- data/images/overview.jpg +0 -0
- data/images/slack.png +0 -0
- data/images/tines.png +0 -0
- data/images/web_alerts.png +0 -0
- data/images/web_config.png +0 -0
- data/lib/mihari/commands/searcher.rb +0 -61
data/lib/mihari/commands/rule.rb
CHANGED
@@ -5,60 +5,62 @@ require "pathname"
|
|
5
5
|
module Mihari
|
6
6
|
module Commands
|
7
7
|
module Rule
|
8
|
-
|
9
|
-
thor
|
10
|
-
|
11
|
-
|
12
|
-
# Validate format of a rule
|
13
|
-
#
|
14
|
-
# @param [String] path
|
15
|
-
#
|
16
|
-
def validate(path)
|
17
|
-
rule = Structs::Rule.from_path_or_id(path)
|
18
|
-
|
19
|
-
begin
|
20
|
-
rule.validate!
|
21
|
-
Mihari.logger.info "Valid format. The input is parsed as the following:"
|
22
|
-
Mihari.logger.info rule.data.to_yaml
|
23
|
-
rescue RuleValidationError
|
24
|
-
nil
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
desc "init [PATH]", "Initialize a new rule file"
|
29
|
-
#
|
30
|
-
# Initialize a new rule file
|
31
|
-
#
|
32
|
-
# @param [String] path
|
33
|
-
#
|
34
|
-
#
|
35
|
-
def init(path = "./rule.yml")
|
36
|
-
warning = "#{path} exists. Do you want to overwrite it? (y/n)"
|
37
|
-
return if Pathname(path).exist? && !(yes? warning)
|
38
|
-
|
39
|
-
initialize_rule path
|
40
|
-
|
41
|
-
Mihari.logger.info "A new rule is initialized: #{path}."
|
42
|
-
end
|
43
|
-
|
44
|
-
no_commands do
|
8
|
+
class << self
|
9
|
+
def included(thor)
|
10
|
+
thor.class_eval do
|
11
|
+
desc "validate [PATH]", "Validate a rule file"
|
45
12
|
#
|
46
|
-
#
|
13
|
+
# Validate format of a rule
|
47
14
|
#
|
48
|
-
|
49
|
-
|
15
|
+
# @param [String] path
|
16
|
+
#
|
17
|
+
def validate(path)
|
18
|
+
rule = Structs::Rule.from_path_or_id(path)
|
19
|
+
|
20
|
+
begin
|
21
|
+
rule.validate!
|
22
|
+
Mihari.logger.info "Valid format. The input is parsed as the following:"
|
23
|
+
Mihari.logger.info rule.data.to_yaml
|
24
|
+
rescue RuleValidationError
|
25
|
+
nil
|
26
|
+
end
|
50
27
|
end
|
51
28
|
|
29
|
+
desc "init [PATH]", "Initialize a new rule file"
|
52
30
|
#
|
53
|
-
#
|
31
|
+
# Initialize a new rule file
|
54
32
|
#
|
55
33
|
# @param [String] path
|
56
|
-
# @param [Dry::Files] files
|
57
34
|
#
|
58
|
-
# @return [nil]
|
59
35
|
#
|
60
|
-
def
|
61
|
-
|
36
|
+
def init(path = "./rule.yml")
|
37
|
+
warning = "#{path} exists. Do you want to overwrite it? (y/n)"
|
38
|
+
return if Pathname(path).exist? && !(yes? warning)
|
39
|
+
|
40
|
+
initialize_rule path
|
41
|
+
|
42
|
+
Mihari.logger.info "A new rule file has been initialized: #{path}."
|
43
|
+
end
|
44
|
+
|
45
|
+
no_commands do
|
46
|
+
#
|
47
|
+
# @return [Mihari::Structs::Rule]
|
48
|
+
#
|
49
|
+
def rule_template
|
50
|
+
Structs::Rule.from_path File.expand_path("../templates/rule.yml.erb", __dir__)
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
# Create a new rule
|
55
|
+
#
|
56
|
+
# @param [String] path
|
57
|
+
# @param [Dry::Files] files
|
58
|
+
#
|
59
|
+
# @return [nil]
|
60
|
+
#
|
61
|
+
def initialize_rule(path, files = Dry::Files.new)
|
62
|
+
files.write(path, rule_template.yaml)
|
63
|
+
end
|
62
64
|
end
|
63
65
|
end
|
64
66
|
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mihari
|
4
|
+
module Commands
|
5
|
+
module Search
|
6
|
+
class << self
|
7
|
+
class RuleWrapper
|
8
|
+
include Mixins::ErrorNotification
|
9
|
+
|
10
|
+
# @return [Nihari::Structs::Rule]
|
11
|
+
attr_reader :rule
|
12
|
+
|
13
|
+
# @return [Boolean]
|
14
|
+
attr_reader :force_overwrite
|
15
|
+
|
16
|
+
def initialize(rule:, force_overwrite:)
|
17
|
+
@rule = rule
|
18
|
+
@force_overwrite = force_overwrite
|
19
|
+
end
|
20
|
+
|
21
|
+
def force_overwrite?
|
22
|
+
force_overwrite
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# @return [Boolean]
|
27
|
+
#
|
28
|
+
def diff?
|
29
|
+
model = Mihari::Rule.find(rule.id)
|
30
|
+
model.data != rule.data.deep_stringify_keys
|
31
|
+
rescue ActiveRecord::RecordNotFound
|
32
|
+
false
|
33
|
+
end
|
34
|
+
|
35
|
+
def update_or_create
|
36
|
+
rule.model.save
|
37
|
+
end
|
38
|
+
|
39
|
+
def run
|
40
|
+
with_error_notification do
|
41
|
+
alert = rule.analyzer.run
|
42
|
+
if alert
|
43
|
+
data = Mihari::Entities::Alert.represent(alert)
|
44
|
+
puts JSON.pretty_generate(data.as_json)
|
45
|
+
else
|
46
|
+
Mihari.logger.info "There is no new artifact found"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def included(thor)
|
53
|
+
thor.class_eval do
|
54
|
+
desc "search [PATH]", "Search by a rule"
|
55
|
+
method_option :force_overwrite, type: :boolean, aliases: "-f", desc: "Force an overwrite the rule"
|
56
|
+
#
|
57
|
+
# Search by a rule
|
58
|
+
#
|
59
|
+
# @param [String] path_or_id
|
60
|
+
#
|
61
|
+
def search(path_or_id)
|
62
|
+
Mihari::Database.with_db_connection do
|
63
|
+
rule = Structs::Rule.from_path_or_id path_or_id
|
64
|
+
|
65
|
+
begin
|
66
|
+
rule.validate!
|
67
|
+
rescue RuleValidationError
|
68
|
+
return
|
69
|
+
end
|
70
|
+
|
71
|
+
force_overwrite = options["force_overwrite"] || false
|
72
|
+
wrapper = RuleWrapper.new(rule: rule, force_overwrite: force_overwrite)
|
73
|
+
|
74
|
+
if wrapper.diff? && !force_overwrite
|
75
|
+
message = "There is diff in the rule (#{rule.id}). Are you sure you want to overwrite the rule? (y/n)"
|
76
|
+
return unless yes?(message)
|
77
|
+
end
|
78
|
+
|
79
|
+
wrapper.update_or_create
|
80
|
+
wrapper.run
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -3,13 +3,15 @@
|
|
3
3
|
module Mihari
|
4
4
|
module Commands
|
5
5
|
module Version
|
6
|
-
|
7
|
-
thor
|
8
|
-
|
6
|
+
class << self
|
7
|
+
def included(thor)
|
8
|
+
thor.class_eval do
|
9
|
+
map %w[--version -v] => :__print_version
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
11
|
+
desc "--version, -v", "Print the version"
|
12
|
+
def __print_version
|
13
|
+
puts Mihari::VERSION
|
14
|
+
end
|
13
15
|
end
|
14
16
|
end
|
15
17
|
end
|
data/lib/mihari/commands/web.rb
CHANGED
@@ -3,29 +3,32 @@
|
|
3
3
|
module Mihari
|
4
4
|
module Commands
|
5
5
|
module Web
|
6
|
-
|
7
|
-
thor
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
6
|
+
class << self
|
7
|
+
def included(thor)
|
8
|
+
thor.class_eval do
|
9
|
+
desc "web", "Launch the web app"
|
10
|
+
method_option :port, type: :numeric, default: 9292, desc: "Hostname to listen on"
|
11
|
+
method_option :host, type: :string, default: "localhost", desc: "Port to listen on"
|
12
|
+
method_option :threads, type: :string, default: "0:5", desc: "min:max threads to use"
|
13
|
+
method_option :verbose, type: :boolean, default: true, desc: "Report each request"
|
14
|
+
method_option :worker_timeout, type: :numeric, default: 60, desc: "Worker timeout value (in seconds)"
|
15
|
+
method_option :hide_config_values, type: :boolean, default: false,
|
16
|
+
desc: "Whether to hide config values or not"
|
17
|
+
method_option :open, type: :boolean, default: true, desc: "Whether to open the app in browser or not"
|
18
|
+
method_option :rack_env, type: :string, default: "production", desc: "Rack environment"
|
19
|
+
def web
|
20
|
+
Mihari.config.hide_config_values = options["hide_config_values"]
|
21
|
+
# set rack env as production
|
22
|
+
ENV["RACK_ENV"] ||= options["rack_env"]
|
23
|
+
Mihari::App.run!(
|
24
|
+
port: options["port"],
|
25
|
+
host: options["host"],
|
26
|
+
threads: options["threads"],
|
27
|
+
verbose: options["verbose"],
|
28
|
+
worker_timeout: options["worker_timeout"],
|
29
|
+
open: options["open"]
|
30
|
+
)
|
31
|
+
end
|
29
32
|
end
|
30
33
|
end
|
31
34
|
end
|
data/lib/mihari/emitters/base.rb
CHANGED
@@ -6,7 +6,20 @@ module Mihari
|
|
6
6
|
include Mixins::Configurable
|
7
7
|
include Mixins::Retriable
|
8
8
|
|
9
|
-
|
9
|
+
# @return [Array<Mihari::Artifact>]
|
10
|
+
attr_reader :artifacts
|
11
|
+
|
12
|
+
# @return [Mihari::Structs::Rule]
|
13
|
+
attr_reader :rule
|
14
|
+
|
15
|
+
#
|
16
|
+
# @param [Array<Mihari::Artifact>] artifacts
|
17
|
+
# @param [Mihari::Structs::Rule] rule
|
18
|
+
# @param [Hash] **_options
|
19
|
+
#
|
20
|
+
def initialize(artifacts:, rule:, **_options)
|
21
|
+
@artifacts = artifacts
|
22
|
+
@rule = rule
|
10
23
|
end
|
11
24
|
|
12
25
|
class << self
|
@@ -4,28 +4,21 @@ module Mihari
|
|
4
4
|
module Emitters
|
5
5
|
class Database < Base
|
6
6
|
def valid?
|
7
|
-
|
7
|
+
configured?
|
8
8
|
end
|
9
9
|
|
10
10
|
#
|
11
11
|
# Create an alert
|
12
12
|
#
|
13
|
-
# @param [Arra<Mihari::Artifact>] artifacts
|
14
|
-
# @param [Mihari::Structs::Rule] rule
|
15
|
-
#
|
16
13
|
# @return [Mihari::Alert]
|
17
14
|
#
|
18
|
-
def emit
|
15
|
+
def emit
|
19
16
|
return if artifacts.empty?
|
20
17
|
|
21
18
|
tags = rule.tags.filter_map { |name| Tag.find_or_create_by(name: name) }.uniq
|
22
19
|
taggings = tags.map { |tag| Tagging.new(tag_id: tag.id) }
|
23
20
|
|
24
|
-
alert = Alert.new(
|
25
|
-
artifacts: artifacts,
|
26
|
-
taggings: taggings,
|
27
|
-
rule_id: rule.id
|
28
|
-
)
|
21
|
+
alert = Alert.new(artifacts: artifacts, taggings: taggings, rule_id: rule.id)
|
29
22
|
alert.save
|
30
23
|
alert
|
31
24
|
end
|
data/lib/mihari/emitters/misp.rb
CHANGED
@@ -9,11 +9,22 @@ module Mihari
|
|
9
9
|
# @return [String, nil]
|
10
10
|
attr_reader :api_key
|
11
11
|
|
12
|
-
|
13
|
-
|
12
|
+
# @return [Array<Mihari::Artifact>]
|
13
|
+
attr_reader :artifacts
|
14
14
|
|
15
|
-
|
16
|
-
|
15
|
+
# @return [Mihari::Structs::Rule]
|
16
|
+
attr_reader :rule
|
17
|
+
|
18
|
+
#
|
19
|
+
# @param [Array<Mihari::Artifact>] artifacts
|
20
|
+
# @param [Mihari::Structs::Rule] rule
|
21
|
+
# @param [Hash] **options
|
22
|
+
#
|
23
|
+
def initialize(artifacts:, rule:, **options)
|
24
|
+
super(artifacts: artifacts, rule: rule, **options)
|
25
|
+
|
26
|
+
@url = options[:url] || Mihari.config.misp_url
|
27
|
+
@api_key = options[:api_key] || Mihari.config.misp_api_key
|
17
28
|
end
|
18
29
|
|
19
30
|
# @return [Boolean]
|
@@ -40,7 +51,7 @@ module Mihari
|
|
40
51
|
#
|
41
52
|
# @return [::MISP::Event]
|
42
53
|
#
|
43
|
-
def emit
|
54
|
+
def emit
|
44
55
|
return if artifacts.empty?
|
45
56
|
|
46
57
|
client.create_event({
|
@@ -121,11 +121,16 @@ module Mihari
|
|
121
121
|
# @return [String]
|
122
122
|
attr_reader :username
|
123
123
|
|
124
|
-
|
125
|
-
|
124
|
+
#
|
125
|
+
# @param [Array<Mihari::Artifact>] artifacts
|
126
|
+
# @param [Mihari::Structs::Rule] rule
|
127
|
+
# @param [Hash] **_options
|
128
|
+
#
|
129
|
+
def initialize(artifacts:, rule:, **options)
|
130
|
+
super(artifacts: artifacts, rule: rule, **options)
|
126
131
|
|
127
|
-
@webhook_url =
|
128
|
-
@channel =
|
132
|
+
@webhook_url = options[:webhook_url] || Mihari.config.slack_webhook_url
|
133
|
+
@channel = options[:channel] || Mihari.config.slack_channel || DEFAULT_CHANNEL
|
129
134
|
@username = DEFAULT_USERNAME
|
130
135
|
end
|
131
136
|
|
@@ -152,13 +157,11 @@ module Mihari
|
|
152
157
|
end
|
153
158
|
|
154
159
|
#
|
155
|
-
# Build
|
156
|
-
#
|
157
|
-
# @param [Array<Mihari::Artifact>] artifacts
|
160
|
+
# Build attachments
|
158
161
|
#
|
159
162
|
# @return [Array<Mihari::Emitters::Attachment>]
|
160
163
|
#
|
161
|
-
def
|
164
|
+
def attachments
|
162
165
|
artifacts.map do |artifact|
|
163
166
|
Attachment.new(data: artifact.data, data_type: artifact.data_type).to_a
|
164
167
|
end.flatten
|
@@ -167,11 +170,9 @@ module Mihari
|
|
167
170
|
#
|
168
171
|
# Build a text
|
169
172
|
#
|
170
|
-
# @param [Mihari::Structs::Rule] rule
|
171
|
-
#
|
172
173
|
# @return [String]
|
173
174
|
#
|
174
|
-
def
|
175
|
+
def text
|
175
176
|
tags = rule.tags
|
176
177
|
tags = ["N/A"] if tags.empty?
|
177
178
|
|
@@ -182,12 +183,9 @@ module Mihari
|
|
182
183
|
].join("\n")
|
183
184
|
end
|
184
185
|
|
185
|
-
def emit
|
186
|
+
def emit
|
186
187
|
return if artifacts.empty?
|
187
188
|
|
188
|
-
attachments = to_attachments(artifacts)
|
189
|
-
text = to_text(rule)
|
190
|
-
|
191
189
|
notifier.post(text: text, attachments: attachments, mrkdwn: true)
|
192
190
|
end
|
193
191
|
|
@@ -12,12 +12,17 @@ module Mihari
|
|
12
12
|
# @return [String, nil]
|
13
13
|
attr_reader :api_version
|
14
14
|
|
15
|
-
|
16
|
-
|
15
|
+
#
|
16
|
+
# @param [Array<Mihari::Artifact>] artifacts
|
17
|
+
# @param [Mihari::Structs::Rule] rule
|
18
|
+
# @param [Hash] **options
|
19
|
+
#
|
20
|
+
def initialize(artifacts:, rule:, **options)
|
21
|
+
super(artifacts: artifacts, rule: rule, **options)
|
17
22
|
|
18
|
-
@url =
|
19
|
-
@api_key =
|
20
|
-
@api_version =
|
23
|
+
@url = options[:url] || Mihari.config.thehive_url
|
24
|
+
@api_key = options[:api_key] || Mihari.config.thehive_api_key
|
25
|
+
@api_version = options[:api_version] || Mihari.config.thehive_api_version
|
21
26
|
end
|
22
27
|
|
23
28
|
# @return [Boolean]
|
@@ -39,15 +44,11 @@ module Mihari
|
|
39
44
|
#
|
40
45
|
# Create a Hive alert
|
41
46
|
#
|
42
|
-
# @param [Arra<Mihari::Artifact>] artifacts
|
43
|
-
# @param [Mihari::Structs::Rule] rule
|
44
|
-
#
|
45
47
|
# @return [::MISP::Event]
|
46
48
|
#
|
47
|
-
def emit
|
49
|
+
def emit
|
48
50
|
return if artifacts.empty?
|
49
51
|
|
50
|
-
payload = payload(rule: rule, artifacts: artifacts)
|
51
52
|
client.alert(payload)
|
52
53
|
end
|
53
54
|
|
@@ -102,18 +103,15 @@ module Mihari
|
|
102
103
|
#
|
103
104
|
# Build payload for alert
|
104
105
|
#
|
105
|
-
# @
|
106
|
-
# @param [Mihari::Structs::Rule] rule
|
107
|
-
#
|
108
|
-
# @return [<Type>] <description>
|
106
|
+
# @return [Hash]
|
109
107
|
#
|
110
|
-
def payload
|
111
|
-
return v4_payload
|
108
|
+
def payload
|
109
|
+
return v4_payload if normalized_api_version.nil?
|
112
110
|
|
113
|
-
v5_payload
|
111
|
+
v5_payload
|
114
112
|
end
|
115
113
|
|
116
|
-
def v4_payload
|
114
|
+
def v4_payload
|
117
115
|
{
|
118
116
|
title: rule.title,
|
119
117
|
description: rule.description,
|
@@ -130,7 +128,7 @@ module Mihari
|
|
130
128
|
}
|
131
129
|
end
|
132
130
|
|
133
|
-
def v5_payload
|
131
|
+
def v5_payload
|
134
132
|
{
|
135
133
|
title: rule.title,
|
136
134
|
description: rule.description,
|
@@ -55,30 +55,26 @@ module Mihari
|
|
55
55
|
# @return [String, nil]
|
56
56
|
attr_reader :template
|
57
57
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
@url = Addressable::URI.parse(url)
|
67
|
-
@headers = headers
|
68
|
-
@method = method
|
69
|
-
@template = template
|
58
|
+
#
|
59
|
+
# @param [Array<Mihari::Artifact>] artifacts
|
60
|
+
# @param [Mihari::Structs::Rule] rule
|
61
|
+
# @param [Hash] **options
|
62
|
+
#
|
63
|
+
def initialize(artifacts:, rule:, **options)
|
64
|
+
super(artifacts: artifacts, rule: rule, **options)
|
65
|
+
|
66
|
+
@url = Addressable::URI.parse(options[:url])
|
67
|
+
@headers = options[:headers] || {}
|
68
|
+
@method = options[:method] || "POST"
|
69
|
+
@template = options[:template]
|
70
70
|
end
|
71
71
|
|
72
|
-
def emit
|
72
|
+
def emit
|
73
73
|
return if artifacts.empty?
|
74
74
|
|
75
|
-
res = nil
|
76
|
-
|
77
|
-
payload_ = payload_as_string(artifacts: artifacts, rule: rule)
|
78
|
-
payload = JSON.parse(payload_)
|
79
|
-
|
80
75
|
client = Mihari::HTTP.new(url, headers: headers)
|
81
76
|
|
77
|
+
res = nil
|
82
78
|
case method
|
83
79
|
when "GET"
|
84
80
|
res = client.get
|
@@ -100,13 +96,10 @@ module Mihari
|
|
100
96
|
#
|
101
97
|
# Convert payload into string
|
102
98
|
#
|
103
|
-
# @param [Array<Mihari::Artifact>] artifacts
|
104
|
-
# @param [Mihari::Structs::Rule] rule
|
105
|
-
#
|
106
99
|
# @return [String]
|
107
100
|
#
|
108
|
-
def payload_as_string
|
109
|
-
|
101
|
+
def payload_as_string
|
102
|
+
[].tap do |out|
|
110
103
|
options = {}
|
111
104
|
options[:template] = File.read(template) unless template.nil?
|
112
105
|
|
@@ -118,6 +111,13 @@ module Mihari
|
|
118
111
|
out << payload_template.result
|
119
112
|
end.first
|
120
113
|
end
|
114
|
+
|
115
|
+
#
|
116
|
+
# @return [Hash]
|
117
|
+
#
|
118
|
+
def payload
|
119
|
+
JSON.parse payload_as_string
|
120
|
+
end
|
121
121
|
end
|
122
122
|
end
|
123
123
|
end
|
data/lib/mihari/feed/parser.rb
CHANGED
data/lib/mihari/feed/reader.rb
CHANGED
@@ -6,7 +6,23 @@ require "insensitive_hash"
|
|
6
6
|
module Mihari
|
7
7
|
module Feed
|
8
8
|
class Reader
|
9
|
-
|
9
|
+
# @return [String]
|
10
|
+
attr_reader :url
|
11
|
+
|
12
|
+
# @return [Hash]
|
13
|
+
attr_reader :headers
|
14
|
+
|
15
|
+
# @return [Hash, nil]
|
16
|
+
attr_reader :params
|
17
|
+
|
18
|
+
# @return [Hash, nil]
|
19
|
+
attr_reader :json
|
20
|
+
|
21
|
+
# @return [Hash, nil]
|
22
|
+
attr_reader :data
|
23
|
+
|
24
|
+
# @return [String]
|
25
|
+
attr_reader :method
|
10
26
|
|
11
27
|
def initialize(url, headers: {}, method: "GET", params: nil, json: nil, data: nil)
|
12
28
|
@url = Addressable::URI.parse(url)
|
@@ -20,6 +36,9 @@ module Mihari
|
|
20
36
|
headers["content-type"] = "application/json" unless json.nil?
|
21
37
|
end
|
22
38
|
|
39
|
+
#
|
40
|
+
# @return [Array<Hash>]
|
41
|
+
#
|
23
42
|
def read
|
24
43
|
return read_file(url.path) if url.scheme == "file"
|
25
44
|
|
@@ -33,11 +52,9 @@ module Mihari
|
|
33
52
|
|
34
53
|
body = res.body
|
35
54
|
content_type = res["Content-Type"].to_s
|
36
|
-
if content_type.include?("application/json")
|
37
|
-
|
38
|
-
|
39
|
-
convert_as_csv(body)
|
40
|
-
end
|
55
|
+
return convert_as_json(body) if content_type.include?("application/json")
|
56
|
+
|
57
|
+
convert_as_csv(body)
|
41
58
|
end
|
42
59
|
|
43
60
|
#
|
@@ -48,10 +65,10 @@ module Mihari
|
|
48
65
|
# @return [Array<Hash>]
|
49
66
|
#
|
50
67
|
def convert_as_json(text)
|
51
|
-
|
52
|
-
return
|
68
|
+
parsed = JSON.parse(text, symbolize_names: true)
|
69
|
+
return parsed if parsed.is_a?(Array)
|
53
70
|
|
54
|
-
[
|
71
|
+
[parsed]
|
55
72
|
end
|
56
73
|
|
57
74
|
#
|
@@ -77,11 +94,9 @@ module Mihari
|
|
77
94
|
def read_file(path)
|
78
95
|
text = File.read(path)
|
79
96
|
|
80
|
-
if path.end_with?(".json")
|
81
|
-
|
82
|
-
|
83
|
-
convert_as_csv text
|
84
|
-
end
|
97
|
+
return convert_as_json(text) if path.end_with?(".json")
|
98
|
+
|
99
|
+
convert_as_csv text
|
85
100
|
end
|
86
101
|
end
|
87
102
|
end
|