mihari 3.7.1 → 3.9.0
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/README.md +1 -3
- data/lib/mihari/analyzers/rule.rb +24 -19
- data/lib/mihari/analyzers/virustotal_intelligence.rb +63 -0
- data/lib/mihari/cli/analyzer.rb +2 -0
- data/lib/mihari/commands/passivetotal.rb +1 -0
- data/lib/mihari/commands/virustotal.rb +1 -0
- data/lib/mihari/commands/virustotal_intelligence.rb +22 -0
- data/lib/mihari/commands/web.rb +9 -5
- data/lib/mihari/database.rb +1 -1
- data/lib/mihari/models/alert.rb +28 -46
- data/lib/mihari/structs/alert.rb +45 -0
- data/lib/mihari/structs/virustotal_intelligence.rb +75 -0
- data/lib/mihari/types.rb +18 -3
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/app.rb +2 -2
- data/lib/mihari/web/controllers/alerts_controller.rb +21 -28
- data/lib/mihari/web/public/index.html +1 -1
- data/lib/mihari/web/public/redoc-static.html +2 -2
- data/lib/mihari/web/public/static/js/app.378da3dc.js +50 -0
- data/lib/mihari/web/public/static/js/app.378da3dc.js.map +1 -0
- data/lib/mihari/web/public/static/js/app.a862ebca.js +50 -0
- data/lib/mihari/web/public/static/js/app.a862ebca.js.map +1 -0
- data/lib/mihari.rb +29 -27
- data/mihari.gemspec +5 -5
- data/sig/lib/mihari/analyzers/rule.rbs +3 -3
- data/sig/lib/mihari/analyzers/virustotal_intelligence.rbs +32 -0
- data/sig/lib/mihari/models/alert.rbs +3 -31
- data/sig/lib/mihari/structs/alert.rbs +27 -0
- data/sig/lib/mihari/structs/virustotal_intelligence.rbs +33 -0
- data/sig/lib/mihari/web/app.rbs +1 -1
- metadata +23 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 47f7a6231abe8eee1a1a4e4bc6f905d5f08fba2b68b1435d1621ab85e9e583c0
|
4
|
+
data.tar.gz: 591a8c71130095d6ac963fb333179d087f00727f45ab545e4a674a3c42a2af46
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: da1c99c406086a9d8d029f8421a3316a5a6976c5b8fbc839a910bd0d61f04a3297614582974dbda925722308d206db9d2889fe44ae213df2650f1482cc15e918
|
7
|
+
data.tar.gz: edf130cb5027bbb6abfea72b150266b8768494b627162da814c93b38d54fe2597a870d78cd9f8f33238512e5a3205cb84a67b9da0589b71393b67fef062ad9b1
|
data/README.md
CHANGED
@@ -46,7 +46,7 @@ Mihari supports the following services by default.
|
|
46
46
|
- [Shodan](https://shodan.io)
|
47
47
|
- [Spyse](https://spyse.com)
|
48
48
|
- [urlscan.io](https://urlscan.io)
|
49
|
-
- [VirusTotal](http://virustotal.com)
|
49
|
+
- [VirusTotal](http://virustotal.com) & [VirusTotal Intelligence](https://www.virustotal.com/gui/intelligence-overview)
|
50
50
|
- [ZoomEye](https://zoomeye.org)
|
51
51
|
|
52
52
|
## Docs
|
@@ -64,5 +64,3 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
64
64
|
## Acknowledgement
|
65
65
|
|
66
66
|
Mihari is proudly supported by [Tines.io](https://tines.io?utm_source=github&utm_medium=sponsorship&utm_campaign=ninoseki), The SOAR Platform for Enterprise Security Teams.
|
67
|
-
|
68
|
-
$ bundle exec rbs -rpathname --repo=gem_rbs/gems -ractivesupport -ractionpack -ractivejob -ractivemodel -ractionview -ractiverecord -rrailties -I sig validate
|
@@ -4,6 +4,30 @@ require "uuidtools"
|
|
4
4
|
|
5
5
|
module Mihari
|
6
6
|
module Analyzers
|
7
|
+
ANALYZER_TO_CLASS = {
|
8
|
+
"binaryedge" => BinaryEdge,
|
9
|
+
"censys" => Censys,
|
10
|
+
"circl" => CIRCL,
|
11
|
+
"crtsh" => Crtsh,
|
12
|
+
"dnpedia" => DNPedia,
|
13
|
+
"dnstwister" => DNSTwister,
|
14
|
+
"onyphe" => Onyphe,
|
15
|
+
"otx" => OTX,
|
16
|
+
"passivetotal" => PassiveTotal,
|
17
|
+
"pt" => PassiveTotal,
|
18
|
+
"pulsedive" => Pulsedive,
|
19
|
+
"securitytrails" => SecurityTrails,
|
20
|
+
"shodan" => Shodan,
|
21
|
+
"spyse" => Spyse,
|
22
|
+
"st" => SecurityTrails,
|
23
|
+
"urlscan" => Urlscan,
|
24
|
+
"virustotal_intelligence" => VirusTotalIntelligence,
|
25
|
+
"virustotal" => VirusTotal,
|
26
|
+
"vt_intel" => VirusTotalIntelligence,
|
27
|
+
"vt" => VirusTotal,
|
28
|
+
"zoomeye" => ZoomEye
|
29
|
+
}.freeze
|
30
|
+
|
7
31
|
class Rule < Base
|
8
32
|
include Mihari::Mixins::DisallowedDataValue
|
9
33
|
|
@@ -26,25 +50,6 @@ module Mihari
|
|
26
50
|
validate_analyzer_configurations
|
27
51
|
end
|
28
52
|
|
29
|
-
ANALYZER_TO_CLASS = {
|
30
|
-
"binaryedge" => BinaryEdge,
|
31
|
-
"censys" => Censys,
|
32
|
-
"circl" => CIRCL,
|
33
|
-
"crtsh" => Crtsh,
|
34
|
-
"dnpedia" => DNPedia,
|
35
|
-
"dnstwister" => DNSTwister,
|
36
|
-
"onyphe" => Onyphe,
|
37
|
-
"otx" => OTX,
|
38
|
-
"passivetotal" => PassiveTotal,
|
39
|
-
"pulsedive" => Pulsedive,
|
40
|
-
"securitytrails" => SecurityTrails,
|
41
|
-
"shodan" => Shodan,
|
42
|
-
"spyse" => Spyse,
|
43
|
-
"urlscan" => Urlscan,
|
44
|
-
"virustotal" => VirusTotal,
|
45
|
-
"zoomeye" => ZoomEye
|
46
|
-
}.freeze
|
47
|
-
|
48
53
|
#
|
49
54
|
# Returns a list of artifacts matched with queries
|
50
55
|
#
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "virustotal"
|
4
|
+
|
5
|
+
module Mihari
|
6
|
+
module Analyzers
|
7
|
+
class VirusTotalIntelligence < Base
|
8
|
+
param :query
|
9
|
+
option :title, default: proc { "VirusTotal Intelligence search" }
|
10
|
+
option :description, default: proc { "query = #{query}" }
|
11
|
+
option :tags, default: proc { [] }
|
12
|
+
|
13
|
+
def initialize(*args, **kwargs)
|
14
|
+
super
|
15
|
+
|
16
|
+
@query = query
|
17
|
+
end
|
18
|
+
|
19
|
+
def artifacts
|
20
|
+
responses = search_witgh_cursor
|
21
|
+
responses.map do |response|
|
22
|
+
response.data.map(&:value)
|
23
|
+
end.flatten.compact.uniq
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def configuration_keys
|
29
|
+
%w[virustotal_api_key]
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# VT API
|
34
|
+
#
|
35
|
+
# @return [::VirusTotal::API]
|
36
|
+
#
|
37
|
+
def api
|
38
|
+
@api = ::VirusTotal::API.new(key: Mihari.config.virustotal_api_key)
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# Search with cursor
|
43
|
+
#
|
44
|
+
# @return [Array<Structs::VirusTotalIntelligence::Response>]
|
45
|
+
#
|
46
|
+
def search_witgh_cursor
|
47
|
+
cursor = nil
|
48
|
+
responses = []
|
49
|
+
|
50
|
+
loop do
|
51
|
+
response = Structs::VirusTotalIntelligence::Response.from_dynamic!(api.intelligence.search(query, cursor: cursor))
|
52
|
+
responses << response
|
53
|
+
|
54
|
+
break if response.meta.cursor.nil?
|
55
|
+
|
56
|
+
cursor = response.meta.cursor
|
57
|
+
end
|
58
|
+
|
59
|
+
responses
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/mihari/cli/analyzer.rb
CHANGED
@@ -14,6 +14,7 @@ require "mihari/commands/securitytrails"
|
|
14
14
|
require "mihari/commands/shodan"
|
15
15
|
require "mihari/commands/spyse"
|
16
16
|
require "mihari/commands/urlscan"
|
17
|
+
require "mihari/commands/virustotal_intelligence"
|
17
18
|
require "mihari/commands/virustotal"
|
18
19
|
require "mihari/commands/zoomeye"
|
19
20
|
|
@@ -42,6 +43,7 @@ module Mihari
|
|
42
43
|
include Mihari::Commands::Spyse
|
43
44
|
include Mihari::Commands::Urlscan
|
44
45
|
include Mihari::Commands::VirusTotal
|
46
|
+
include Mihari::Commands::VirusTotalIntelligence
|
45
47
|
include Mihari::Commands::ZoomEye
|
46
48
|
end
|
47
49
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mihari
|
4
|
+
module Commands
|
5
|
+
module VirusTotalIntelligence
|
6
|
+
def self.included(thor)
|
7
|
+
thor.class_eval do
|
8
|
+
desc "virustotal_intelligence [QUERY]", "VirusTotal Intelligence search"
|
9
|
+
method_option :title, type: :string, desc: "title"
|
10
|
+
method_option :description, type: :string, desc: "description"
|
11
|
+
method_option :tags, type: :array, desc: "tags"
|
12
|
+
def virustotal_intelligence(query)
|
13
|
+
with_error_handling do
|
14
|
+
run_analyzer Analyzers::VirusTotalIntelligence, query: query, options: options
|
15
|
+
end
|
16
|
+
end
|
17
|
+
map "vt_intel" => :virustotal_intelligence
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/mihari/commands/web.rb
CHANGED
@@ -6,19 +6,23 @@ module Mihari
|
|
6
6
|
def self.included(thor)
|
7
7
|
thor.class_eval do
|
8
8
|
desc "web", "Launch the web app"
|
9
|
-
method_option :port, type: :numeric, default: 9292
|
10
|
-
method_option :host, type: :string, default: "localhost"
|
9
|
+
method_option :port, type: :numeric, default: 9292, desc: "Hostname to listen on"
|
10
|
+
method_option :host, type: :string, default: "localhost", desc: "Port to listen on"
|
11
|
+
method_option :threads, type: :string, default: "0:16", desc: "min:max threads to use"
|
12
|
+
method_option :verbose, type: :boolean, default: true, desc: "Report each request"
|
11
13
|
method_option :config, type: :string, desc: "Path to the config file"
|
12
14
|
def web
|
13
|
-
port = options["port"]
|
14
|
-
host = options["host"]
|
15
|
+
port = options["port"]
|
16
|
+
host = options["host"]
|
17
|
+
threads = options["threads"]
|
18
|
+
verbose = options["verbose"]
|
15
19
|
|
16
20
|
load_configuration
|
17
21
|
|
18
22
|
# set rack env as production
|
19
23
|
ENV["RACK_ENV"] ||= "production"
|
20
24
|
|
21
|
-
Mihari::App.run!(port: port, host: host)
|
25
|
+
Mihari::App.run!(port: port, host: host, threads: threads, verbose: verbose)
|
22
26
|
end
|
23
27
|
end
|
24
28
|
end
|
data/lib/mihari/database.rb
CHANGED
data/lib/mihari/models/alert.rb
CHANGED
@@ -13,36 +13,20 @@ module Mihari
|
|
13
13
|
#
|
14
14
|
# Search alerts
|
15
15
|
#
|
16
|
-
# @param [
|
17
|
-
# @param [String, nil] description
|
18
|
-
# @param [String, nil] source
|
19
|
-
# @param [String, nil] tag_name
|
20
|
-
# @param [String, nil] title
|
21
|
-
# @param [DateTime, nil] from_at
|
22
|
-
# @param [DateTime, nil] to_at
|
23
|
-
# @param [Integer, nil] limit
|
24
|
-
# @param [Integer, nil] page
|
16
|
+
# @param [Structs::Alert::SearchFilterWithPagination] filter
|
25
17
|
#
|
26
18
|
# @return [Array<Hash>]
|
27
19
|
#
|
28
|
-
def search(
|
29
|
-
limit = limit.to_i
|
20
|
+
def search(filter)
|
21
|
+
limit = filter.limit.to_i
|
30
22
|
raise ArgumentError, "limit should be bigger than zero" unless limit.positive?
|
31
23
|
|
32
|
-
page = page.to_i
|
24
|
+
page = filter.page.to_i
|
33
25
|
raise ArgumentError, "page should be bigger than zero" unless page.positive?
|
34
26
|
|
35
27
|
offset = (page - 1) * limit
|
36
28
|
|
37
|
-
relation = build_relation(
|
38
|
-
artifact_data: artifact_data,
|
39
|
-
title: title,
|
40
|
-
description: description,
|
41
|
-
source: source,
|
42
|
-
tag_name: tag_name,
|
43
|
-
from_at: from_at,
|
44
|
-
to_at: to_at
|
45
|
-
)
|
29
|
+
relation = build_relation(filter.without_pagination)
|
46
30
|
|
47
31
|
# TODO: improve queires
|
48
32
|
alert_ids = relation.limit(limit).offset(offset).order(id: :desc).pluck(:id).uniq
|
@@ -60,45 +44,43 @@ module Mihari
|
|
60
44
|
# Count alerts
|
61
45
|
#
|
62
46
|
# @param [String, nil] artifact_data
|
63
|
-
# @param [String, nil] description
|
64
|
-
# @param [String, nil] source
|
65
|
-
# @param [String, nil] tag_name
|
66
|
-
# @param [String, nil] title
|
67
|
-
# @param [DateTime, nil] from_at
|
68
|
-
# @param [DateTime, nil] to_at
|
69
47
|
#
|
70
48
|
# @return [Integer]
|
71
49
|
#
|
72
|
-
def count(
|
73
|
-
relation = build_relation(
|
74
|
-
artifact_data: artifact_data,
|
75
|
-
title: title,
|
76
|
-
description: description,
|
77
|
-
source: source,
|
78
|
-
tag_name: tag_name,
|
79
|
-
from_at: from_at,
|
80
|
-
to_at: to_at
|
81
|
-
)
|
50
|
+
def count(filter)
|
51
|
+
relation = build_relation(filter)
|
82
52
|
relation.distinct("alerts.id").count
|
83
53
|
end
|
84
54
|
|
85
55
|
private
|
86
56
|
|
87
|
-
def build_relation(
|
88
|
-
|
57
|
+
def build_relation(filter)
|
58
|
+
artifact_ids = []
|
59
|
+
artifact = Artifact.includes(:autonomous_system, :dns_records, :reverse_dns_names)
|
60
|
+
artifact = artifact.where(data: filter.artifact_data) if filter.artifact_data
|
61
|
+
artifact = artifact.where(autonomous_system: { asn: filter.asn }) if filter.asn
|
62
|
+
artifact = artifact.where(dns_records: { value: filter.dns_record }) if filter.dns_record
|
63
|
+
artifact = artifact.where(reverse_dns_names: { name: filter.reverse_dns_name }) if filter.reverse_dns_name
|
64
|
+
# get artifact ids if there is any valid filter for artifact
|
65
|
+
if filter.has_valid_artifact_filters
|
66
|
+
artifact_ids = artifact.pluck(:id)
|
67
|
+
# set invalid ID if nothing is matched with the filters
|
68
|
+
artifact_ids = [-1] if artifact_ids.empty?
|
69
|
+
end
|
89
70
|
|
71
|
+
relation = self
|
90
72
|
relation = relation.includes(:artifacts, :tags)
|
91
73
|
|
92
|
-
relation = relation.where(artifacts: {
|
93
|
-
relation = relation.where(tags: { name: tag_name }) if tag_name
|
74
|
+
relation = relation.where(artifacts: { id: artifact_ids }) unless artifact_ids.empty?
|
75
|
+
relation = relation.where(tags: { name: filter.tag_name }) if filter.tag_name
|
94
76
|
|
95
|
-
relation = relation.where(source: source) if source
|
96
|
-
relation = relation.where(title: title) if title
|
77
|
+
relation = relation.where(source: filter.source) if filter.source
|
78
|
+
relation = relation.where(title: filter.title) if filter.title
|
97
79
|
|
98
|
-
relation = relation.filter(description: { like: "%#{description}%" }) if description
|
80
|
+
relation = relation.filter(description: { like: "%#{filter.description}%" }) if filter.description
|
99
81
|
|
100
|
-
relation = relation.filter(created_at: { gte: from_at }) if from_at
|
101
|
-
relation = relation.filter(created_at: { lte: to_at }) if to_at
|
82
|
+
relation = relation.filter(created_at: { gte: filter.from_at }) if filter.from_at
|
83
|
+
relation = relation.filter(created_at: { lte: filter.to_at }) if filter.to_at
|
102
84
|
|
103
85
|
relation
|
104
86
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require "json"
|
2
|
+
require "dry/struct"
|
3
|
+
|
4
|
+
module Mihari
|
5
|
+
module Structs
|
6
|
+
module Alert
|
7
|
+
class SearchFilter < Dry::Struct
|
8
|
+
attribute? :artifact_data, Types::String.optional
|
9
|
+
attribute? :description, Types::String.optional
|
10
|
+
attribute? :source, Types::String.optional
|
11
|
+
attribute? :tag_name, Types::String.optional
|
12
|
+
attribute? :title, Types::String.optional
|
13
|
+
attribute? :from_at, Types::DateTime.optional
|
14
|
+
attribute? :to_at, Types::DateTime.optional
|
15
|
+
attribute? :asn, Types::Int.optional
|
16
|
+
attribute? :dns_record, Types::String.optional
|
17
|
+
attribute? :reverse_dns_name, Types::String.optional
|
18
|
+
|
19
|
+
def has_valid_artifact_filters
|
20
|
+
!(artifact_data || asn || dns_record || reverse_dns_name).nil?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class SearchFilterWithPagination < SearchFilter
|
25
|
+
attribute? :page, Types::Int.default(1)
|
26
|
+
attribute? :limit, Types::Int.default(10)
|
27
|
+
|
28
|
+
def without_pagination
|
29
|
+
SearchFilter.new(
|
30
|
+
artifact_data: artifact_data,
|
31
|
+
description: description,
|
32
|
+
from_at: from_at,
|
33
|
+
source: source,
|
34
|
+
tag_name: tag_name,
|
35
|
+
title: title,
|
36
|
+
to_at: to_at,
|
37
|
+
asn: asn,
|
38
|
+
dns_record: dns_record,
|
39
|
+
reverse_dns_name: reverse_dns_name
|
40
|
+
)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require "json"
|
2
|
+
require "dry/struct"
|
3
|
+
|
4
|
+
module Mihari
|
5
|
+
module Structs
|
6
|
+
module VirusTotalIntelligence
|
7
|
+
class ContextAttributes < Dry::Struct
|
8
|
+
attribute :url, Types.Array(Types::String).optional
|
9
|
+
|
10
|
+
def self.from_dynamic!(d)
|
11
|
+
d = Types::Hash[d]
|
12
|
+
new(
|
13
|
+
url: d["url"]
|
14
|
+
)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Datum < Dry::Struct
|
19
|
+
attribute :type, Types::String
|
20
|
+
attribute :id, Types::String
|
21
|
+
attribute :context_attributes, ContextAttributes.optional
|
22
|
+
|
23
|
+
def value
|
24
|
+
case type
|
25
|
+
when "file"
|
26
|
+
id
|
27
|
+
when "url"
|
28
|
+
(context_attributes.url || []).first
|
29
|
+
when "domain"
|
30
|
+
id
|
31
|
+
when "ip_address"
|
32
|
+
id
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.from_dynamic!(d)
|
37
|
+
d = Types::Hash[d]
|
38
|
+
|
39
|
+
context_attributes = nil
|
40
|
+
context_attributes = ContextAttributes.from_dynamic!(d.fetch("context_attributes")) if d.key?("context_attributes")
|
41
|
+
|
42
|
+
new(
|
43
|
+
type: d.fetch("type"),
|
44
|
+
id: d.fetch("id"),
|
45
|
+
context_attributes: context_attributes
|
46
|
+
)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class Meta < Dry::Struct
|
51
|
+
attribute :cursor, Types::String.optional
|
52
|
+
|
53
|
+
def self.from_dynamic!(d)
|
54
|
+
d = Types::Hash[d]
|
55
|
+
new(
|
56
|
+
cursor: d["cursor"]
|
57
|
+
)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class Response < Dry::Struct
|
62
|
+
attribute :meta, Meta
|
63
|
+
attribute :data, Types.Array(Datum)
|
64
|
+
|
65
|
+
def self.from_dynamic!(d)
|
66
|
+
d = Types::Hash[d]
|
67
|
+
new(
|
68
|
+
meta: Meta.from_dynamic!(d.fetch("meta")),
|
69
|
+
data: d.fetch("data").map { |x| Datum.from_dynamic!(x) }
|
70
|
+
)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/lib/mihari/types.rb
CHANGED
@@ -9,13 +9,28 @@ module Mihari
|
|
9
9
|
Hash = Strict::Hash
|
10
10
|
String = Strict::String
|
11
11
|
Double = Strict::Float | Strict::Integer
|
12
|
+
DateTime = Strict::DateTime
|
12
13
|
|
13
14
|
DataTypes = Types::String.enum(*ALLOWED_DATA_TYPES)
|
14
15
|
|
15
16
|
AnalyzerTypes = Types::String.enum(
|
16
|
-
"binaryedge",
|
17
|
-
"
|
18
|
-
"
|
17
|
+
"binaryedge",
|
18
|
+
"censys",
|
19
|
+
"circl",
|
20
|
+
"dnpedia",
|
21
|
+
"dnstwister",
|
22
|
+
"onyphe",
|
23
|
+
"otx",
|
24
|
+
"passivetotal",
|
25
|
+
"pt",
|
26
|
+
"pulsedive",
|
27
|
+
"securitytrails",
|
28
|
+
"shodan",
|
29
|
+
"st",
|
30
|
+
"virustotal_intelligence",
|
31
|
+
"virustotal",
|
32
|
+
"vt_intel",
|
33
|
+
"vt"
|
19
34
|
)
|
20
35
|
end
|
21
36
|
end
|
data/lib/mihari/version.rb
CHANGED
data/lib/mihari/web/app.rb
CHANGED
@@ -37,10 +37,10 @@ module Mihari
|
|
37
37
|
use Mihari::Controllers::TagsController
|
38
38
|
|
39
39
|
class << self
|
40
|
-
def run!(port: 9292, host: "localhost")
|
40
|
+
def run!(port: 9292, host: "localhost", threads: "0:16", verbose: false)
|
41
41
|
url = "http://#{host}:#{port}"
|
42
42
|
|
43
|
-
Rack::Handler::Puma.run
|
43
|
+
Rack::Handler::Puma.run(self, Port: port, Host: host, Threads: threads, Verbose: verbose) do |server|
|
44
44
|
Launchy.open url
|
45
45
|
|
46
46
|
[:INT, :TERM].each do |sig|
|
@@ -15,39 +15,32 @@ module Mihari
|
|
15
15
|
param :to_at, DateTime
|
16
16
|
param :toAt, DateTime
|
17
17
|
|
18
|
+
param :asn, Integer
|
19
|
+
param :dns_record, String
|
20
|
+
param :dnsRecord, String
|
21
|
+
param :reverse_dns_name, String
|
22
|
+
param :reverseDnsName, String
|
23
|
+
|
24
|
+
# set page & limit
|
18
25
|
page = params["page"] || 1
|
19
|
-
page = page.to_i
|
26
|
+
params["page"] = page.to_i
|
27
|
+
|
20
28
|
limit = 10
|
29
|
+
params["limit"] = 10
|
21
30
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
31
|
+
# normalize keys
|
32
|
+
params["artifact_data"] = params["artifact"]
|
33
|
+
params["from_at"] = params["from_at"] || params["fromAt"]
|
34
|
+
params["to_at"] = params["to_at"] || params["toAt"]
|
35
|
+
params["dns_record"] = params["dns_record"] || params["dnsRecord"]
|
36
|
+
params["reverse_dns_name"] = params["reverse_dns_name"] || params["reverseDnsName"]
|
27
37
|
|
28
|
-
|
29
|
-
|
38
|
+
# symbolize hash keys
|
39
|
+
filter = params.to_h.transform_keys(&:to_sym)
|
30
40
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
from_at: from_at,
|
35
|
-
limit: limit,
|
36
|
-
page: page,
|
37
|
-
source: source,
|
38
|
-
tag_name: tag_name,
|
39
|
-
title: title,
|
40
|
-
to_at: to_at
|
41
|
-
)
|
42
|
-
total = Mihari::Alert.count(
|
43
|
-
artifact_data: artifact_data,
|
44
|
-
description: description,
|
45
|
-
from_at: from_at,
|
46
|
-
source: source,
|
47
|
-
tag_name: tag_name,
|
48
|
-
title: title,
|
49
|
-
to_at: to_at
|
50
|
-
)
|
41
|
+
search_filter_with_pagenation = Structs::Alert::SearchFilterWithPagination.new(**filter)
|
42
|
+
alerts = Mihari::Alert.search(search_filter_with_pagenation)
|
43
|
+
total = Mihari::Alert.count(search_filter_with_pagenation.without_pagination)
|
51
44
|
|
52
45
|
json({ alerts: alerts, total: total, current_page: page, page_size: limit })
|
53
46
|
end
|
@@ -1 +1 @@
|
|
1
|
-
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/static/favicon.ico"><title>Mihari</title><link href="/static/js/app.
|
1
|
+
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/static/favicon.ico"><title>Mihari</title><link href="/static/js/app.378da3dc.js" rel="preload" as="script"></head><body><noscript><strong>We're sorry but Mihari doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/static/js/app.378da3dc.js"></script></body></html>
|