mihari 5.1.0 → 5.1.2
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/.gitmodules +0 -3
- data/.rubocop.yml +6 -0
- data/README.md +0 -1
- data/lib/mihari/analyzers/base.rb +32 -27
- data/lib/mihari/analyzers/binaryedge.rb +17 -9
- data/lib/mihari/analyzers/censys.rb +10 -54
- data/lib/mihari/analyzers/circl.rb +7 -6
- data/lib/mihari/analyzers/crtsh.rb +12 -7
- data/lib/mihari/analyzers/dnstwister.rb +7 -7
- data/lib/mihari/analyzers/feed.rb +33 -10
- data/lib/mihari/analyzers/greynoise.rb +8 -33
- data/lib/mihari/analyzers/onyphe.rb +10 -36
- data/lib/mihari/analyzers/otx.rb +4 -3
- data/lib/mihari/analyzers/passivetotal.rb +8 -7
- data/lib/mihari/analyzers/pulsedive.rb +8 -7
- data/lib/mihari/analyzers/rule.rb +0 -1
- data/lib/mihari/analyzers/securitytrails.rb +8 -10
- data/lib/mihari/analyzers/shodan.rb +16 -90
- data/lib/mihari/analyzers/urlscan.rb +16 -6
- data/lib/mihari/analyzers/virustotal.rb +8 -6
- data/lib/mihari/analyzers/virustotal_intelligence.rb +12 -7
- data/lib/mihari/analyzers/zoomeye.rb +13 -10
- data/lib/mihari/clients/base.rb +53 -0
- data/lib/mihari/clients/binaryedge.rb +38 -0
- data/lib/mihari/clients/censys.rb +42 -0
- data/lib/mihari/clients/circl.rb +59 -0
- data/lib/mihari/clients/crtsh.rb +31 -0
- data/lib/mihari/clients/dnstwister.rb +40 -0
- data/lib/mihari/clients/greynoise.rb +34 -0
- data/lib/mihari/clients/misp.rb +29 -0
- data/lib/mihari/clients/onyphe.rb +35 -0
- data/lib/mihari/clients/otx.rb +49 -0
- data/lib/mihari/clients/passivetotal.rb +69 -0
- data/lib/mihari/clients/publsedive.rb +56 -0
- data/lib/mihari/clients/securitytrails.rb +94 -0
- data/lib/mihari/clients/shodan.rb +41 -0
- data/lib/mihari/clients/the_hive.rb +33 -0
- data/lib/mihari/clients/urlscan.rb +33 -0
- data/lib/mihari/clients/virustotal.rb +62 -0
- data/lib/mihari/clients/zoomeye.rb +74 -0
- data/lib/mihari/commands/database.rb +1 -6
- data/lib/mihari/commands/searcher.rb +1 -2
- data/lib/mihari/database.rb +9 -0
- data/lib/mihari/emitters/misp.rb +13 -20
- data/lib/mihari/emitters/the_hive.rb +3 -5
- data/lib/mihari/emitters/webhook.rb +2 -2
- data/lib/mihari/feed/reader.rb +14 -11
- data/lib/mihari/http.rb +29 -21
- data/lib/mihari/mixins/retriable.rb +3 -1
- data/lib/mihari/schemas/analyzer.rb +5 -4
- data/lib/mihari/structs/censys.rb +62 -0
- data/lib/mihari/structs/greynoise.rb +43 -0
- data/lib/mihari/structs/onyphe.rb +45 -0
- data/lib/mihari/structs/shodan.rb +83 -0
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/middleware/connection_adapter.rb +1 -3
- data/lib/mihari/web/public/assets/{index-63900d73.js → index-7d0fb8c4.js} +2 -2
- data/lib/mihari/web/public/index.html +1 -1
- data/lib/mihari/web/public/redoc-static.html +2 -2
- data/lib/mihari.rb +21 -2
- data/mihari.gemspec +15 -23
- metadata +55 -264
- data/lib/mihari/analyzers/clients/otx.rb +0 -36
- data/lib/mihari/analyzers/dnpedia.rb +0 -37
- data/lib/mihari/mixins/database.rb +0 -16
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mihari
|
4
|
+
module Clients
|
5
|
+
class ZoomEye < Base
|
6
|
+
attr_reader :api_key
|
7
|
+
|
8
|
+
#
|
9
|
+
# @param [String] base_url
|
10
|
+
# @param [String, nil] api_key
|
11
|
+
# @param [Hash] headers
|
12
|
+
#
|
13
|
+
def initialize(base_url = "https://api.zoomeye.org", api_key:, headers: {})
|
14
|
+
raise(ArgumentError, "'api_key' argument is required") unless api_key
|
15
|
+
|
16
|
+
headers["api-key"] = api_key
|
17
|
+
super(base_url, headers: headers)
|
18
|
+
end
|
19
|
+
|
20
|
+
#
|
21
|
+
# Search the Host devices
|
22
|
+
#
|
23
|
+
# @param [String] query Query string
|
24
|
+
# @param [Integer, nil] page The page number to paging(default:1)
|
25
|
+
# @param [String, nil] facets A comma-separated list of properties to get summary information on query
|
26
|
+
#
|
27
|
+
# @return [Hash]
|
28
|
+
#
|
29
|
+
def host_search(query, page: nil, facets: nil)
|
30
|
+
params = {
|
31
|
+
query: query,
|
32
|
+
page: page,
|
33
|
+
facets: facets
|
34
|
+
}.compact
|
35
|
+
|
36
|
+
_get("/host/search", params: params)
|
37
|
+
end
|
38
|
+
|
39
|
+
#
|
40
|
+
# Search the Web technologies
|
41
|
+
#
|
42
|
+
# @param [String] query Query string
|
43
|
+
# @param [Integer, nil] page The page number to paging(default:1)
|
44
|
+
# @param [String, nil] facets A comma-separated list of properties to get summary information on query
|
45
|
+
#
|
46
|
+
# @return [Hash]
|
47
|
+
#
|
48
|
+
def web_search(query, page: nil, facets: nil)
|
49
|
+
params = {
|
50
|
+
query: query,
|
51
|
+
page: page,
|
52
|
+
facets: facets
|
53
|
+
}.compact
|
54
|
+
|
55
|
+
_get("/web/search", params: params)
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
#
|
61
|
+
# @param [String] path
|
62
|
+
# @param [Hash] params
|
63
|
+
#
|
64
|
+
# @return [Hash, nil]
|
65
|
+
#
|
66
|
+
def _get(path, params: {})
|
67
|
+
res = get(path, params: params)
|
68
|
+
JSON.parse(res.body.to_s)
|
69
|
+
rescue HTTPError
|
70
|
+
nil
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -3,8 +3,6 @@
|
|
3
3
|
module Mihari
|
4
4
|
module Commands
|
5
5
|
module Database
|
6
|
-
include Mixins::Database
|
7
|
-
|
8
6
|
def self.included(thor)
|
9
7
|
thor.class_eval do
|
10
8
|
desc "migrate", "Migrate DB schemas"
|
@@ -12,14 +10,11 @@ module Mihari
|
|
12
10
|
#
|
13
11
|
# @param [String] direction
|
14
12
|
#
|
15
|
-
#
|
16
13
|
def migrate(direction = "up")
|
17
14
|
verbose = options["verbose"]
|
18
15
|
ActiveRecord::Migration.verbose = verbose
|
19
16
|
|
20
|
-
with_db_connection
|
21
|
-
Mihari::Database.migrate(direction.to_sym)
|
22
|
-
end
|
17
|
+
Mihari::Database.with_db_connection { Mihari::Database.migrate(direction.to_sym) }
|
23
18
|
end
|
24
19
|
end
|
25
20
|
end
|
@@ -3,7 +3,6 @@
|
|
3
3
|
module Mihari
|
4
4
|
module Commands
|
5
5
|
module Searcher
|
6
|
-
include Mixins::Database
|
7
6
|
include Mixins::ErrorNotification
|
8
7
|
|
9
8
|
def self.included(thor)
|
@@ -16,7 +15,7 @@ module Mihari
|
|
16
15
|
# @param [String] path_or_id
|
17
16
|
#
|
18
17
|
def search(path_or_id)
|
19
|
-
with_db_connection do
|
18
|
+
Mihari::Database.with_db_connection do
|
20
19
|
rule = Structs::Rule.from_path_or_id path_or_id
|
21
20
|
|
22
21
|
# validate
|
data/lib/mihari/database.rb
CHANGED
@@ -164,6 +164,15 @@ module Mihari
|
|
164
164
|
|
165
165
|
ActiveRecord::Base.clear_active_connections!
|
166
166
|
end
|
167
|
+
|
168
|
+
def with_db_connection
|
169
|
+
Mihari::Database.connect
|
170
|
+
yield
|
171
|
+
rescue ActiveRecord::StatementInvalid
|
172
|
+
Mihari.logger.error("You haven't finished the DB migration! Please run 'mihari db migrate'.")
|
173
|
+
ensure
|
174
|
+
Mihari::Database.close
|
175
|
+
end
|
167
176
|
end
|
168
177
|
end
|
169
178
|
end
|
data/lib/mihari/emitters/misp.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "misp"
|
4
|
-
|
5
3
|
module Mihari
|
6
4
|
module Emitters
|
7
5
|
class MISP < Base
|
@@ -16,11 +14,6 @@ module Mihari
|
|
16
14
|
|
17
15
|
@url = kwargs[:url] || Mihari.config.misp_url
|
18
16
|
@api_key = kwargs[:api_key] || Mihari.config.misp_api_key
|
19
|
-
|
20
|
-
::MISP.configure do |config|
|
21
|
-
config.api_endpoint = url
|
22
|
-
config.api_key = api_key
|
23
|
-
end
|
24
17
|
end
|
25
18
|
|
26
19
|
# @return [Boolean]
|
@@ -50,17 +43,13 @@ module Mihari
|
|
50
43
|
def emit(rule:, artifacts:, **_options)
|
51
44
|
return if artifacts.empty?
|
52
45
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
event.add_tag name: tag
|
61
|
-
end
|
62
|
-
|
63
|
-
event.create
|
46
|
+
client.create_event({
|
47
|
+
Event: {
|
48
|
+
info: rule.title
|
49
|
+
},
|
50
|
+
Attribute: artifacts.map { |artifact| build_attribute(artifact) },
|
51
|
+
Tag: rule.tags.map { |tag| { name: tag } }
|
52
|
+
})
|
64
53
|
end
|
65
54
|
|
66
55
|
private
|
@@ -69,15 +58,19 @@ module Mihari
|
|
69
58
|
%w[misp_url misp_api_key]
|
70
59
|
end
|
71
60
|
|
61
|
+
def client
|
62
|
+
@client ||= Clients::MISP.new(url, api_key: api_key)
|
63
|
+
end
|
64
|
+
|
72
65
|
#
|
73
66
|
# Build a MISP attribute
|
74
67
|
#
|
75
68
|
# @param [Mihari::Artifact] artifact
|
76
69
|
#
|
77
|
-
# @return [
|
70
|
+
# @return [Hash]
|
78
71
|
#
|
79
72
|
def build_attribute(artifact)
|
80
|
-
|
73
|
+
{ value: artifact.data, type: to_misp_type(type: artifact.data_type, value: artifact.data) }
|
81
74
|
end
|
82
75
|
|
83
76
|
#
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "hachi"
|
4
|
-
|
5
3
|
module Mihari
|
6
4
|
module Emitters
|
7
5
|
class TheHive < Base
|
@@ -50,7 +48,7 @@ module Mihari
|
|
50
48
|
return if artifacts.empty?
|
51
49
|
|
52
50
|
payload = payload(rule: rule, artifacts: artifacts)
|
53
|
-
|
51
|
+
client.alert(payload)
|
54
52
|
end
|
55
53
|
|
56
54
|
#
|
@@ -79,8 +77,8 @@ module Mihari
|
|
79
77
|
%w[thehive_url thehive_api_key]
|
80
78
|
end
|
81
79
|
|
82
|
-
def
|
83
|
-
@
|
80
|
+
def client
|
81
|
+
@client ||= Clients::TheHive.new(url, api_key: api_key, api_version: normalized_api_version)
|
84
82
|
end
|
85
83
|
|
86
84
|
#
|
@@ -77,13 +77,13 @@ module Mihari
|
|
77
77
|
payload_ = payload_as_string(artifacts: artifacts, rule: rule)
|
78
78
|
payload = JSON.parse(payload_)
|
79
79
|
|
80
|
-
client = Mihari::HTTP.new(url, headers: headers
|
80
|
+
client = Mihari::HTTP.new(url, headers: headers)
|
81
81
|
|
82
82
|
case method
|
83
83
|
when "GET"
|
84
84
|
res = client.get
|
85
85
|
when "POST"
|
86
|
-
res = client.post
|
86
|
+
res = client.post(json: payload)
|
87
87
|
end
|
88
88
|
|
89
89
|
res
|
data/lib/mihari/feed/reader.rb
CHANGED
@@ -6,25 +6,28 @@ require "insensitive_hash"
|
|
6
6
|
module Mihari
|
7
7
|
module Feed
|
8
8
|
class Reader
|
9
|
-
attr_reader :
|
9
|
+
attr_reader :url, :headers, :params, :json, :data, :method
|
10
10
|
|
11
|
-
def initialize(
|
12
|
-
@
|
13
|
-
@
|
14
|
-
@
|
15
|
-
@http_request_payload = http_request_payload
|
11
|
+
def initialize(url, headers: {}, method: "GET", params: nil, json: nil, data: nil)
|
12
|
+
@url = Addressable::URI.parse(url)
|
13
|
+
@headers = headers.insensitive
|
14
|
+
@method = method
|
16
15
|
|
17
|
-
|
16
|
+
@params = params
|
17
|
+
@json = json
|
18
|
+
@data = data
|
19
|
+
|
20
|
+
headers["content-type"] = "application/json" unless json.nil?
|
18
21
|
end
|
19
22
|
|
20
23
|
def read
|
21
|
-
return read_file(
|
24
|
+
return read_file(url.path) if url.scheme == "file"
|
22
25
|
|
23
26
|
res = nil
|
24
|
-
client = HTTP.new(
|
27
|
+
client = HTTP.new(url, headers: headers)
|
25
28
|
|
26
|
-
res = client.get if
|
27
|
-
res = client.post if
|
29
|
+
res = client.get(params: params) if method == "GET"
|
30
|
+
res = client.post(params: params, json: json, data: data) if method == "POST"
|
28
31
|
|
29
32
|
return [] if res.nil?
|
30
33
|
|
data/lib/mihari/http.rb
CHANGED
@@ -4,54 +4,62 @@ require "insensitive_hash"
|
|
4
4
|
|
5
5
|
module Mihari
|
6
6
|
class HTTP
|
7
|
-
|
7
|
+
# @return [String]
|
8
|
+
attr_reader :url
|
8
9
|
|
9
|
-
|
10
|
+
# @return [Hash]
|
11
|
+
attr_reader :headers
|
12
|
+
|
13
|
+
def initialize(url, headers: {})
|
10
14
|
@url = url.is_a?(URI) ? url : URI(url.to_s)
|
11
15
|
@headers = headers.insensitive
|
12
|
-
@payload = payload
|
13
16
|
end
|
14
17
|
|
15
18
|
#
|
16
19
|
# Make a GET request
|
17
20
|
#
|
21
|
+
# @param [Hash, nil] params
|
22
|
+
#
|
18
23
|
# @return [Net::HTTPResponse]
|
19
24
|
#
|
20
|
-
def get
|
25
|
+
def get(params: nil)
|
21
26
|
new_url = url.deep_dup
|
22
|
-
new_url.query = Addressable::URI.form_encode(
|
27
|
+
new_url.query = Addressable::URI.form_encode(params) unless (params || {}).empty?
|
23
28
|
|
24
29
|
get = Net::HTTP::Get.new(new_url)
|
25
30
|
request get
|
26
31
|
end
|
27
32
|
|
28
33
|
#
|
29
|
-
# Make a POST
|
34
|
+
# Make a POST requesti
|
35
|
+
#
|
36
|
+
# @param [Hash, nil] params
|
37
|
+
# @param [Hash, nil] json
|
38
|
+
# @param [Hash, nil] data
|
30
39
|
#
|
31
40
|
# @return [Net::HTTPResponse]
|
32
41
|
#
|
33
|
-
def post
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
end
|
42
|
+
def post(params: nil, json: nil, data: nil)
|
43
|
+
new_url = url.deep_dup
|
44
|
+
new_url.query = Addressable::URI.form_encode(params) unless (params || {}).empty?
|
45
|
+
|
46
|
+
post = Net::HTTP::Post.new(new_url)
|
47
|
+
|
48
|
+
post.body = JSON.generate(json) if json
|
49
|
+
post.set_form_data(data) if data
|
42
50
|
|
43
51
|
request post
|
44
52
|
end
|
45
53
|
|
46
54
|
class << self
|
47
|
-
def get(url, headers: {}, params:
|
48
|
-
client = new(url, headers: headers
|
49
|
-
client.get
|
55
|
+
def get(url, headers: {}, params: nil)
|
56
|
+
client = new(url, headers: headers)
|
57
|
+
client.get(params: params)
|
50
58
|
end
|
51
59
|
|
52
|
-
def post(url, headers: {},
|
53
|
-
client = new(url, headers: headers
|
54
|
-
client.post
|
60
|
+
def post(url, headers: {}, params: nil, json: nil, data: nil)
|
61
|
+
client = new(url, headers: headers)
|
62
|
+
client.post(params: params, json: json, data: data)
|
55
63
|
end
|
56
64
|
end
|
57
65
|
|
@@ -82,10 +82,11 @@ module Mihari
|
|
82
82
|
required(:analyzer).value(Types::String.enum("feed"))
|
83
83
|
required(:query).value(:string)
|
84
84
|
required(:selector).value(:string)
|
85
|
-
optional(:
|
86
|
-
optional(:
|
87
|
-
optional(:
|
88
|
-
optional(:
|
85
|
+
optional(:method).value(Types::HTTPRequestMethods).default("GET")
|
86
|
+
optional(:headers).value(:hash).default({})
|
87
|
+
optional(:params).value(:hash)
|
88
|
+
optional(:data).value(:hash)
|
89
|
+
optional(:json).value(:hash)
|
89
90
|
optional(:options).hash(AnalyzerOptions)
|
90
91
|
end
|
91
92
|
end
|
@@ -4,8 +4,17 @@ module Mihari
|
|
4
4
|
module Structs
|
5
5
|
module Censys
|
6
6
|
class AutonomousSystem < Dry::Struct
|
7
|
+
include Mixins::AutonomousSystem
|
8
|
+
|
7
9
|
attribute :asn, Types::Int
|
8
10
|
|
11
|
+
#
|
12
|
+
# @return [Mihari::AutonomousSystem]
|
13
|
+
#
|
14
|
+
def to_as
|
15
|
+
Mihari::AutonomousSystem.new(asn: normalize_asn(asn))
|
16
|
+
end
|
17
|
+
|
9
18
|
def self.from_dynamic!(d)
|
10
19
|
d = Types::Hash[d]
|
11
20
|
new(
|
@@ -18,6 +27,20 @@ module Mihari
|
|
18
27
|
attribute :country, Types::String.optional
|
19
28
|
attribute :country_code, Types::String.optional
|
20
29
|
|
30
|
+
#
|
31
|
+
# @return [Mihari::Geolocation] <description>
|
32
|
+
#
|
33
|
+
def to_geolocation
|
34
|
+
# sometimes Censys overlooks country
|
35
|
+
# then set geolocation as nil
|
36
|
+
return nil if country.nil?
|
37
|
+
|
38
|
+
Mihari::Geolocation.new(
|
39
|
+
country: country,
|
40
|
+
country_code: country_code
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
21
44
|
def self.from_dynamic!(d)
|
22
45
|
d = Types::Hash[d]
|
23
46
|
new(
|
@@ -30,6 +53,13 @@ module Mihari
|
|
30
53
|
class Service < Dry::Struct
|
31
54
|
attribute :port, Types::Integer
|
32
55
|
|
56
|
+
#
|
57
|
+
# @return [Mihari::Port]
|
58
|
+
#
|
59
|
+
def to_port
|
60
|
+
Port.new(port: port)
|
61
|
+
end
|
62
|
+
|
33
63
|
def self.from_dynamic!(d)
|
34
64
|
d = Types::Hash[d]
|
35
65
|
new(
|
@@ -45,6 +75,29 @@ module Mihari
|
|
45
75
|
attribute :metadata, Types::Hash
|
46
76
|
attribute :services, Types.Array(Service)
|
47
77
|
|
78
|
+
#
|
79
|
+
# @return [Array<Mihari::Port>]
|
80
|
+
#
|
81
|
+
def to_ports
|
82
|
+
services.map(&:to_port)
|
83
|
+
end
|
84
|
+
|
85
|
+
#
|
86
|
+
# @param [String] source
|
87
|
+
#
|
88
|
+
# @return [Mihari::Artifact]
|
89
|
+
#
|
90
|
+
def to_artifact(source = "Censys")
|
91
|
+
Artifact.new(
|
92
|
+
data: ip,
|
93
|
+
source: source,
|
94
|
+
metadata: metadata,
|
95
|
+
autonomous_system: autonomous_system.to_as,
|
96
|
+
geolocation: location.to_geolocation,
|
97
|
+
ports: to_ports
|
98
|
+
)
|
99
|
+
end
|
100
|
+
|
48
101
|
def self.from_dynamic!(d)
|
49
102
|
d = Types::Hash[d]
|
50
103
|
new(
|
@@ -76,6 +129,15 @@ module Mihari
|
|
76
129
|
attribute :hits, Types.Array(Hit)
|
77
130
|
attribute :links, Links
|
78
131
|
|
132
|
+
#
|
133
|
+
# @param [String] source
|
134
|
+
#
|
135
|
+
# @return [Array<Mihari::Artifact>]
|
136
|
+
#
|
137
|
+
def to_artifacts(source = "Censys")
|
138
|
+
hits.map { |hit| hit.to_artifact(source) }
|
139
|
+
end
|
140
|
+
|
79
141
|
def self.from_dynamic!(d)
|
80
142
|
d = Types::Hash[d]
|
81
143
|
new(
|
@@ -4,10 +4,29 @@ module Mihari
|
|
4
4
|
module Structs
|
5
5
|
module GreyNoise
|
6
6
|
class Metadata < Dry::Struct
|
7
|
+
include Mixins::AutonomousSystem
|
8
|
+
|
7
9
|
attribute :country, Types::String
|
8
10
|
attribute :country_code, Types::String
|
9
11
|
attribute :asn, Types::String
|
10
12
|
|
13
|
+
#
|
14
|
+
# @return [Mihari::AutonomousSystem]
|
15
|
+
#
|
16
|
+
def to_as
|
17
|
+
Mihari::AutonomousSystem.new(asn: normalize_asn(asn))
|
18
|
+
end
|
19
|
+
|
20
|
+
#
|
21
|
+
# @return [Mihari::Geolocation]
|
22
|
+
#
|
23
|
+
def to_geolocation
|
24
|
+
Mihari::Geolocation.new(
|
25
|
+
country: country,
|
26
|
+
country_code: country_code
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
11
30
|
def self.from_dynamic!(d)
|
12
31
|
d = Types::Hash[d]
|
13
32
|
new(
|
@@ -23,6 +42,21 @@ module Mihari
|
|
23
42
|
attribute :metadata, Metadata
|
24
43
|
attribute :metadata_, Types::Hash
|
25
44
|
|
45
|
+
#
|
46
|
+
# @param [String] source
|
47
|
+
#
|
48
|
+
# @return [Mihari::Artifact]
|
49
|
+
#
|
50
|
+
def to_artifact(source = "GreyNoise")
|
51
|
+
Mihari::Artifact.new(
|
52
|
+
data: ip,
|
53
|
+
source: source,
|
54
|
+
metadata: metadata_,
|
55
|
+
autonomous_system: metadata.to_as,
|
56
|
+
geolocation: metadata.to_geolocation
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
26
60
|
def self.from_dynamic!(d)
|
27
61
|
d = Types::Hash[d]
|
28
62
|
new(
|
@@ -40,6 +74,15 @@ module Mihari
|
|
40
74
|
attribute :message, Types::String
|
41
75
|
attribute :query, Types::String
|
42
76
|
|
77
|
+
#
|
78
|
+
# @param [String] source
|
79
|
+
#
|
80
|
+
# @return [Array<Mihari::Artifact>]
|
81
|
+
#
|
82
|
+
def to_artifacts(source = "GreyNoise")
|
83
|
+
data.map { |datum| datum.to_artifact(source) }
|
84
|
+
end
|
85
|
+
|
43
86
|
def self.from_dynamic!(d)
|
44
87
|
d = Types::Hash[d]
|
45
88
|
new(
|
@@ -4,11 +4,47 @@ module Mihari
|
|
4
4
|
module Structs
|
5
5
|
module Onyphe
|
6
6
|
class Result < Dry::Struct
|
7
|
+
include Mixins::AutonomousSystem
|
8
|
+
|
7
9
|
attribute :asn, Types::String
|
8
10
|
attribute :country_code, Types::String.optional
|
9
11
|
attribute :ip, Types::String
|
10
12
|
attribute :metadata, Types::Hash
|
11
13
|
|
14
|
+
#
|
15
|
+
# @param [String] source
|
16
|
+
#
|
17
|
+
# @return [Mihari::Artifact]
|
18
|
+
#
|
19
|
+
def to_artifact(source = "Onyphe")
|
20
|
+
Mihari::Artifact.new(
|
21
|
+
data: ip,
|
22
|
+
source: source,
|
23
|
+
metadata: metadata,
|
24
|
+
autonomous_system: to_as,
|
25
|
+
geolocation: to_geolocation
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
#
|
30
|
+
# @return [Mihari::Geolocation, nil]
|
31
|
+
#
|
32
|
+
def to_geolocation
|
33
|
+
return nil if country_code.nil?
|
34
|
+
|
35
|
+
Mihari::Geolocation.new(
|
36
|
+
country: NormalizeCountry(country_code, to: :short),
|
37
|
+
country_code: country_code
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# @return [Mihari::AutonomousSystem]
|
43
|
+
#
|
44
|
+
def to_as
|
45
|
+
Mihari::AutonomousSystem.new(asn: normalize_asn(asn))
|
46
|
+
end
|
47
|
+
|
12
48
|
def self.from_dynamic!(d)
|
13
49
|
d = Types::Hash[d]
|
14
50
|
new(
|
@@ -30,6 +66,15 @@ module Mihari
|
|
30
66
|
attribute :status, Types::String
|
31
67
|
attribute :total, Types::Int
|
32
68
|
|
69
|
+
#
|
70
|
+
# @param [String] source
|
71
|
+
#
|
72
|
+
# @return [Array<Mihari::Artifact>]
|
73
|
+
#
|
74
|
+
def to_artifacts(source = "Onyphe")
|
75
|
+
results.map { |result| result.to_artifact(source) }
|
76
|
+
end
|
77
|
+
|
33
78
|
def self.from_dynamic!(d)
|
34
79
|
d = Types::Hash[d]
|
35
80
|
new(
|