mihari 4.2.0 → 4.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +25 -3
- data/lib/mihari/analyzers/rule.rb +49 -6
- data/lib/mihari/constants.rb +2 -0
- data/lib/mihari/emitters/base.rb +3 -0
- data/lib/mihari/emitters/http.rb +127 -0
- data/lib/mihari/emitters/webhook.rb +7 -16
- data/lib/mihari/entities/rule.rb +5 -0
- data/lib/mihari/feed/reader.rb +6 -4
- data/lib/mihari/http.rb +19 -14
- data/lib/mihari/schemas/rule.rb +30 -4
- data/lib/mihari/structs/rule.rb +1 -0
- data/lib/mihari/types.rb +11 -3
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/public/index.html +1 -1
- data/lib/mihari/web/public/redoc-static.html +57 -51
- data/lib/mihari/web/public/static/css/app.0de4b715.css +1 -0
- data/lib/mihari/web/public/static/css/chunk-vendors.c57bb3fd.css +7 -0
- data/lib/mihari/web/public/static/fonts/fa-brands-400.edf40f86.woff2 +0 -0
- data/lib/mihari/web/public/static/fonts/fa-brands-400.f7223235.ttf +0 -0
- data/lib/mihari/web/public/static/fonts/fa-regular-400.3665ebc7.woff2 +0 -0
- data/lib/mihari/web/public/static/fonts/fa-regular-400.a7fde52b.ttf +0 -0
- data/lib/mihari/web/public/static/fonts/fa-solid-900.0d2abd43.woff2 +0 -0
- data/lib/mihari/web/public/static/fonts/fa-solid-900.5b03221c.ttf +0 -0
- data/lib/mihari/web/public/static/fonts/fa-v4compatibility.42932bea.ttf +0 -0
- data/lib/mihari/web/public/static/js/app-legacy.e451304b.js +2 -0
- data/lib/mihari/web/public/static/js/app-legacy.e451304b.js.map +1 -0
- data/lib/mihari/web/public/static/js/app.e74e91d7.js +2 -0
- data/lib/mihari/web/public/static/js/app.e74e91d7.js.map +1 -0
- data/lib/mihari/web/public/static/js/chunk-vendors-legacy.41357cdf.js +25 -0
- data/lib/mihari/web/public/static/js/chunk-vendors-legacy.41357cdf.js.map +1 -0
- data/lib/mihari/web/public/static/js/chunk-vendors.c5525f1e.js +31 -0
- data/lib/mihari/web/public/static/js/chunk-vendors.c5525f1e.js.map +1 -0
- data/lib/mihari.rb +11 -9
- data/mihari.gemspec +10 -5
- data/sig/lib/mihari/constants.rbs +2 -0
- data/sig/lib/mihari/emitters/http.rbs +35 -0
- data/sig/lib/mihari/http.rbs +2 -3
- data/sig/lib/mihari/structs/rule.rbs +1 -3
- data/sig/lib/mihari/types.rbs +2 -0
- metadata +76 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dc5b6ab7daa3030a0ef0778df2753247bde10b978be709102ecf1be60b672a9c
|
4
|
+
data.tar.gz: 1c1c283cf1e2989e9e94e0aa582567dd71b7900f5c5faa40a0ce3f1b327982a3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 918ee19022035b5f5e3db9a00318253803b1cc430b45676225af94861fe6dc6fe51343545d224e6094f09ad37dc003713fbbcb1777904b01205903e22d05bf23
|
7
|
+
data.tar.gz: c5ce99eb3d8e01b1b2a8ac51916afa172c1fecb55611a3b8aa76e686c72801beb11135ed1c8aed31e11b693b7467dbe513902f55e229c834bbfcb09460ba4202
|
data/.github/workflows/test.yml
CHANGED
@@ -1,9 +1,13 @@
|
|
1
1
|
name: Ruby CI
|
2
2
|
|
3
|
-
on:
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [master]
|
6
|
+
pull_request:
|
7
|
+
branches: [master]
|
4
8
|
|
5
9
|
jobs:
|
6
|
-
|
10
|
+
test:
|
7
11
|
runs-on: ubuntu-latest
|
8
12
|
|
9
13
|
services:
|
@@ -39,7 +43,7 @@ jobs:
|
|
39
43
|
strategy:
|
40
44
|
fail-fast: false
|
41
45
|
matrix:
|
42
|
-
ruby: [2.7, "3.0"]
|
46
|
+
ruby: [2.7, "3.0", 3.1]
|
43
47
|
|
44
48
|
steps:
|
45
49
|
- uses: actions/checkout@v3
|
@@ -65,3 +69,21 @@ jobs:
|
|
65
69
|
DATABASE: mysql2://mysql:mysql@127.0.0.1:3306/test
|
66
70
|
run: |
|
67
71
|
bundle exec rake
|
72
|
+
|
73
|
+
- name: Coveralls Parallel
|
74
|
+
uses: coverallsapp/github-action@master
|
75
|
+
with:
|
76
|
+
github-token: ${{ secrets.github_token }}
|
77
|
+
flag-name: run-${{ matrix.ruby-version }}
|
78
|
+
parallel: true
|
79
|
+
|
80
|
+
coverage:
|
81
|
+
name: Coverage
|
82
|
+
needs: test
|
83
|
+
runs-on: ubuntu-latest
|
84
|
+
steps:
|
85
|
+
- name: Coveralls Finished
|
86
|
+
uses: coverallsapp/github-action@master
|
87
|
+
with:
|
88
|
+
github-token: ${{ secrets.github_token }}
|
89
|
+
parallel-finished: true
|
@@ -28,6 +28,15 @@ module Mihari
|
|
28
28
|
"zoomeye" => ZoomEye
|
29
29
|
}.freeze
|
30
30
|
|
31
|
+
EMITTER_TO_CLASS = {
|
32
|
+
"database" => Emitters::Database,
|
33
|
+
"http" => Emitters::HTTP,
|
34
|
+
"misp" => Emitters::MISP,
|
35
|
+
"slack" => Emitters::Slack,
|
36
|
+
"the_hive" => Emitters::TheHive,
|
37
|
+
"webhook" => Emitters::Webhook
|
38
|
+
}.freeze
|
39
|
+
|
31
40
|
class Rule < Base
|
32
41
|
include Mixins::DisallowedDataValue
|
33
42
|
include Mixins::Rule
|
@@ -41,6 +50,8 @@ module Mihari
|
|
41
50
|
option :allowed_data_types, default: proc { ALLOWED_DATA_TYPES }
|
42
51
|
option :disallowed_data_values, default: proc { [] }
|
43
52
|
|
53
|
+
option :emitters, optional: true
|
54
|
+
|
44
55
|
attr_reader :source
|
45
56
|
|
46
57
|
def initialize(**kwargs)
|
@@ -48,6 +59,8 @@ module Mihari
|
|
48
59
|
|
49
60
|
@source = id
|
50
61
|
|
62
|
+
@emitters = emitters || DEFAULT_EMITTERS
|
63
|
+
|
51
64
|
validate_analyzer_configurations
|
52
65
|
end
|
53
66
|
|
@@ -59,18 +72,20 @@ module Mihari
|
|
59
72
|
def artifacts
|
60
73
|
artifacts = []
|
61
74
|
|
62
|
-
queries.each do |
|
63
|
-
|
75
|
+
queries.each do |original_params|
|
76
|
+
parmas = original_params.deep_dup
|
77
|
+
|
78
|
+
analyzer_name = parmas[:analyzer]
|
64
79
|
klass = get_analyzer_class(analyzer_name)
|
65
80
|
|
66
|
-
query =
|
81
|
+
query = parmas[:query]
|
67
82
|
|
68
83
|
# set interval in the top level
|
69
|
-
options =
|
84
|
+
options = parmas[:options] || {}
|
70
85
|
interval = options[:interval]
|
71
|
-
|
86
|
+
parmas[:interval] = interval if interval
|
72
87
|
|
73
|
-
analyzer = klass.new(query, **
|
88
|
+
analyzer = klass.new(query, **parmas)
|
74
89
|
|
75
90
|
# Use #normalized_artifacts method to get atrifacts as Array<Mihari::Artifact>
|
76
91
|
# So Mihari::Artifact object has "source" attribute (e.g. "Shodan")
|
@@ -120,6 +135,34 @@ module Mihari
|
|
120
135
|
|
121
136
|
private
|
122
137
|
|
138
|
+
#
|
139
|
+
# Get emitter class
|
140
|
+
#
|
141
|
+
# @param [String] emitter_name
|
142
|
+
#
|
143
|
+
# @return [Class<Mihari::Emitters::Base>] emitter class
|
144
|
+
#
|
145
|
+
def get_emitter_class(emitter_name)
|
146
|
+
emitter = EMITTER_TO_CLASS[emitter_name]
|
147
|
+
return emitter if emitter
|
148
|
+
|
149
|
+
raise ArgumentError, "#{emitter_name} is not supported"
|
150
|
+
end
|
151
|
+
|
152
|
+
def valid_emitters
|
153
|
+
@valid_emitters ||= emitters.filter_map do |original_params|
|
154
|
+
params = original_params.deep_dup
|
155
|
+
|
156
|
+
name = params[:emitter]
|
157
|
+
params.delete(:emitter)
|
158
|
+
|
159
|
+
klass = get_emitter_class(name)
|
160
|
+
emitter = klass.new(**params)
|
161
|
+
|
162
|
+
emitter.valid? ? emitter : nil
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
123
166
|
#
|
124
167
|
# Get analyzer class
|
125
168
|
#
|
data/lib/mihari/constants.rb
CHANGED
data/lib/mihari/emitters/base.rb
CHANGED
@@ -0,0 +1,127 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "erb"
|
4
|
+
|
5
|
+
module Mihari
|
6
|
+
module Emitters
|
7
|
+
class PayloadTemplate < ERB
|
8
|
+
def self.template
|
9
|
+
%{
|
10
|
+
{
|
11
|
+
"title": "<%= @title %>",
|
12
|
+
"description": "<%= @description %>",
|
13
|
+
"source": "<%= @source %>",
|
14
|
+
"artifacts": [
|
15
|
+
<% @artifacts.each_with_index do |artifact, idx| %>
|
16
|
+
"<%= artifact.data %>"
|
17
|
+
<%= ',' if idx < (@artifacts.length - 1) %>
|
18
|
+
<% end %>
|
19
|
+
],
|
20
|
+
"tags": [
|
21
|
+
<% @tags.each_with_index do |tag, idx| %>
|
22
|
+
"<%= tag %>"
|
23
|
+
<%= ',' if idx < (@tags.length - 1) %>
|
24
|
+
<% end %>
|
25
|
+
]
|
26
|
+
}
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(title:, description:, artifacts:, source:, tags:, options: {})
|
31
|
+
@title = title
|
32
|
+
@description = description
|
33
|
+
@artifacts = artifacts
|
34
|
+
@source = source
|
35
|
+
@tags = tags
|
36
|
+
|
37
|
+
@template = options.fetch(:template, self.class.template)
|
38
|
+
super(@template)
|
39
|
+
end
|
40
|
+
|
41
|
+
def result
|
42
|
+
super(binding)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class HTTP < Base
|
47
|
+
# @return [Addressable::URI, nil]
|
48
|
+
attr_reader :uri
|
49
|
+
|
50
|
+
# @return [Hash]
|
51
|
+
attr_reader :http_request_headers
|
52
|
+
|
53
|
+
# @return [String]
|
54
|
+
attr_reader :http_request_method
|
55
|
+
|
56
|
+
# @return [String, nil]
|
57
|
+
attr_reader :template
|
58
|
+
|
59
|
+
def initialize(*args, **kwargs)
|
60
|
+
super(*args, **kwargs)
|
61
|
+
|
62
|
+
uri = kwargs[:url] || kwargs[:uri]
|
63
|
+
http_request_headers = kwargs[:http_request_headers] || {}
|
64
|
+
http_request_method = kwargs[:http_request_method] || "POST"
|
65
|
+
template = kwargs[:template]
|
66
|
+
|
67
|
+
@uri = Addressable::URI.parse(uri) if uri
|
68
|
+
@http_request_headers = http_request_headers
|
69
|
+
@http_request_method = http_request_method
|
70
|
+
@template = template
|
71
|
+
end
|
72
|
+
|
73
|
+
def emit(title:, description:, artifacts:, source:, tags:)
|
74
|
+
return if artifacts.empty?
|
75
|
+
|
76
|
+
res = nil
|
77
|
+
|
78
|
+
payload_ = payload_as_string(
|
79
|
+
title: title,
|
80
|
+
description: description,
|
81
|
+
artifacts: artifacts,
|
82
|
+
source: source,
|
83
|
+
tags: tags
|
84
|
+
)
|
85
|
+
payload = JSON.parse(payload_)
|
86
|
+
|
87
|
+
client = Mihari::HTTP.new(uri, headers: http_request_headers, payload: payload)
|
88
|
+
|
89
|
+
case http_request_method
|
90
|
+
when "GET"
|
91
|
+
res = client.get
|
92
|
+
when "POST"
|
93
|
+
res = client.post
|
94
|
+
end
|
95
|
+
|
96
|
+
res
|
97
|
+
end
|
98
|
+
|
99
|
+
def valid?
|
100
|
+
return false if uri.nil?
|
101
|
+
|
102
|
+
["http", "https"].include? uri.scheme.downcase
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def payload_as_string(title:, description:, artifacts:, source:, tags:)
|
108
|
+
@payload_as_string ||= [].tap do |out|
|
109
|
+
options = {}
|
110
|
+
unless template.nil?
|
111
|
+
options[:template] = File.read(template)
|
112
|
+
end
|
113
|
+
|
114
|
+
payload_template = PayloadTemplate.new(
|
115
|
+
title: title,
|
116
|
+
description: description,
|
117
|
+
artifacts: artifacts,
|
118
|
+
source: source,
|
119
|
+
tags: tags,
|
120
|
+
options: options
|
121
|
+
)
|
122
|
+
out << payload_template.result
|
123
|
+
end.first
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -11,20 +11,11 @@ module Mihari
|
|
11
11
|
def emit(title:, description:, artifacts:, source:, tags:)
|
12
12
|
return if artifacts.empty?
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
title: title,
|
17
|
-
description: description,
|
18
|
-
artifacts: artifacts.map(&:data),
|
19
|
-
source: source,
|
20
|
-
tags: tags
|
21
|
-
}
|
14
|
+
headers = { 'content-type': "application/x-www-form-urlencoded" }
|
15
|
+
headers["content-type"] = "application/json" if use_json_body?
|
22
16
|
|
23
|
-
|
24
|
-
|
25
|
-
else
|
26
|
-
Net::HTTP.post_form(uri, data)
|
27
|
-
end
|
17
|
+
emitter = Emitters::HTTP.new(uri: Mihari.config.webhook_url)
|
18
|
+
emitter.emit(title: title, description: description, artifacts: artifacts, source: source, tags: tags)
|
28
19
|
end
|
29
20
|
|
30
21
|
private
|
@@ -45,16 +36,16 @@ module Mihari
|
|
45
36
|
#
|
46
37
|
# Check whether a webhook URL is set or not
|
47
38
|
#
|
48
|
-
# @return [
|
39
|
+
# @return [Boolean]
|
49
40
|
#
|
50
41
|
def webhook_url?
|
51
42
|
!webhook_url.nil?
|
52
43
|
end
|
53
44
|
|
54
45
|
#
|
55
|
-
# Check whether to use JSON body or
|
46
|
+
# Check whether to use JSON body or not
|
56
47
|
#
|
57
|
-
# @return [
|
48
|
+
# @return [Boolean]
|
58
49
|
#
|
59
50
|
def use_json_body?
|
60
51
|
@use_json_body ||= Mihari.config.webhook_use_json_body
|
data/lib/mihari/entities/rule.rb
CHANGED
@@ -8,6 +8,10 @@ module Mihari
|
|
8
8
|
expose :interval, documentation: { type: Integer, required: false }
|
9
9
|
end
|
10
10
|
|
11
|
+
class Emitter < Grape::Entity
|
12
|
+
expose :emitter, documentation: { type: String, required: true }
|
13
|
+
end
|
14
|
+
|
11
15
|
class Rule < Grape::Entity
|
12
16
|
expose :id, documentation: { type: String, required: true }
|
13
17
|
|
@@ -16,6 +20,7 @@ module Mihari
|
|
16
20
|
expose :title, documentation: { type: String, required: true }
|
17
21
|
expose :description, documentation: { type: String, required: true }
|
18
22
|
expose :queries, using: Entities::Query, documentation: { type: Entities::Query, is_array: true, required: true }
|
23
|
+
expose :emitters, using: Entities::Emitter, documentation: { type: Entities::Emitter, is_array: true, required: false }
|
19
24
|
expose :tags, documentation: { type: String, is_array: true }
|
20
25
|
expose :allowed_data_types, documentation: { type: String, is_array: true }, as: :allowedDtaTypes
|
21
26
|
expose :disallowed_data_values, documentation: { type: String, is_array: true }, as: :disallowedDataValues
|
data/lib/mihari/feed/reader.rb
CHANGED
@@ -1,25 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "csv"
|
4
|
+
require "insensitive_hash"
|
4
5
|
|
5
6
|
module Mihari
|
6
7
|
module Feed
|
7
8
|
class Reader
|
8
|
-
attr_reader :uri, :http_request_headers, :http_request_method, :
|
9
|
+
attr_reader :uri, :http_request_headers, :http_request_method, :http_request_payload
|
9
10
|
|
10
11
|
def initialize(uri, http_request_headers: {}, http_request_method: "GET", http_request_payload_type: nil, http_request_payload: {})
|
11
12
|
@uri = Addressable::URI.parse(uri)
|
12
|
-
@http_request_headers = http_request_headers
|
13
|
+
@http_request_headers = http_request_headers.insensitive
|
13
14
|
@http_request_method = http_request_method
|
14
|
-
@http_request_payload_type = http_request_payload_type
|
15
15
|
@http_request_payload = http_request_payload
|
16
|
+
|
17
|
+
http_request_headers["content-type"] = http_request_payload_type if http_request_payload_type
|
16
18
|
end
|
17
19
|
|
18
20
|
def read
|
19
21
|
return read_file(uri.path) if uri.scheme == "file"
|
20
22
|
|
21
23
|
res = nil
|
22
|
-
client = HTTP.new(uri, headers: http_request_headers, payload: http_request_payload
|
24
|
+
client = HTTP.new(uri, headers: http_request_headers, payload: http_request_payload)
|
23
25
|
|
24
26
|
res = client.get if http_request_method == "GET"
|
25
27
|
res = client.post if http_request_method == "POST"
|
data/lib/mihari/http.rb
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "insensitive_hash"
|
4
|
+
|
3
5
|
module Mihari
|
4
6
|
class HTTP
|
5
|
-
attr_reader :uri, :headers, :
|
7
|
+
attr_reader :uri, :headers, :payload
|
6
8
|
|
7
|
-
def initialize(uri, headers: {},
|
8
|
-
@uri = Addressable::URI.parse(uri)
|
9
|
-
@headers = headers
|
10
|
-
@payload_type = payload_type
|
9
|
+
def initialize(uri, headers: {}, payload: {})
|
10
|
+
@uri = uri.is_a?(Addressable::URI) ? uri : Addressable::URI.parse(uri)
|
11
|
+
@headers = headers.insensitive
|
11
12
|
@payload = payload
|
12
13
|
end
|
13
14
|
|
@@ -31,12 +32,10 @@ module Mihari
|
|
31
32
|
def post
|
32
33
|
post = Net::HTTP::Post.new(uri)
|
33
34
|
|
34
|
-
case
|
35
|
+
case content_type
|
35
36
|
when "application/json"
|
36
|
-
headers["content-type"] = "application/json" unless headers.key?("content-type")
|
37
37
|
post.body = JSON.generate(payload)
|
38
38
|
when "application/x-www-form-urlencoded"
|
39
|
-
headers["content-type"] = "application/x-www-form-urlencoded" unless headers.key?("content-type")
|
40
39
|
post.set_form_data(payload)
|
41
40
|
end
|
42
41
|
|
@@ -44,19 +43,23 @@ module Mihari
|
|
44
43
|
end
|
45
44
|
|
46
45
|
class << self
|
47
|
-
def get(uri, headers: {},
|
48
|
-
client = new(uri, headers: headers,
|
46
|
+
def get(uri, headers: {}, payload: {})
|
47
|
+
client = new(uri, headers: headers, payload: payload)
|
49
48
|
client.get
|
50
49
|
end
|
51
50
|
|
52
|
-
def post(uri, headers: {},
|
53
|
-
client = new(uri, headers: headers,
|
51
|
+
def post(uri, headers: {}, payload: {})
|
52
|
+
client = new(uri, headers: headers, payload: payload)
|
54
53
|
client.post
|
55
54
|
end
|
56
55
|
end
|
57
56
|
|
58
57
|
private
|
59
58
|
|
59
|
+
def content_type
|
60
|
+
headers["content-type"] || "application/json"
|
61
|
+
end
|
62
|
+
|
60
63
|
#
|
61
64
|
# Get options for HTTP request
|
62
65
|
#
|
@@ -84,8 +87,10 @@ module Mihari
|
|
84
87
|
|
85
88
|
res = http.request(req)
|
86
89
|
|
87
|
-
|
88
|
-
|
90
|
+
unless res.is_a?(Net::HTTPSuccess)
|
91
|
+
code = res.code.to_i
|
92
|
+
raise HttpError, "Unsuccessful response code returned: #{code}"
|
93
|
+
end
|
89
94
|
|
90
95
|
res
|
91
96
|
end
|
data/lib/mihari/schemas/rule.rb
CHANGED
@@ -42,14 +42,26 @@ module Mihari
|
|
42
42
|
Feed = Dry::Schema.Params do
|
43
43
|
required(:analyzer).value(Types::String.enum("feed"))
|
44
44
|
required(:query).value(:string)
|
45
|
-
required(:http_request_method).value(Types::FeedHttpRequestMethods).default("GET")
|
46
|
-
required(:http_request_headers).value(:hash).default({})
|
47
|
-
required(:http_request_payload).value(:hash).default({})
|
48
45
|
required(:selector).value(:string)
|
49
|
-
optional(:
|
46
|
+
optional(:http_request_method).value(Types::HttpRequestMethods).default("GET")
|
47
|
+
optional(:http_request_headers).value(:hash).default({})
|
48
|
+
optional(:http_request_payload).value(:hash).default({})
|
49
|
+
optional(:http_request_payload_type).value(Types::HttpRequestPayloadTypes)
|
50
50
|
optional(:options).hash(AnalyzerOptions)
|
51
51
|
end
|
52
52
|
|
53
|
+
Emitter = Dry::Schema.Params do
|
54
|
+
required(:emitter).value(Types::EmitterTypes)
|
55
|
+
end
|
56
|
+
|
57
|
+
HTTP = Dry::Schema.Params do
|
58
|
+
required(:emitter).value(Types::String.enum("http"))
|
59
|
+
required(:url).value(:string)
|
60
|
+
optional(:http_request_method).value(Types::HttpRequestMethods).default("POST")
|
61
|
+
optional(:http_request_headers).value(:hash).default({})
|
62
|
+
optional(:template).value(:string)
|
63
|
+
end
|
64
|
+
|
53
65
|
Rule = Dry::Schema.Params do
|
54
66
|
required(:title).value(:string)
|
55
67
|
required(:description).value(:string)
|
@@ -63,11 +75,25 @@ module Mihari
|
|
63
75
|
|
64
76
|
required(:queries).value(:array).each { Analyzer | Spyse | ZoomEye | Urlscan | Crtsh | Feed }
|
65
77
|
|
78
|
+
optional(:emitters).value(:array).each { Emitter | HTTP }
|
79
|
+
|
66
80
|
optional(:allowed_data_types).value(array[Types::DataTypes]).default(ALLOWED_DATA_TYPES)
|
67
81
|
optional(:disallowed_data_values).value(array[:string]).default([])
|
68
82
|
|
69
83
|
optional(:ignore_old_artifacts).value(:bool).default(false)
|
70
84
|
optional(:ignore_threshold).value(:integer).default(0)
|
85
|
+
|
86
|
+
before(:key_coercer) do |result|
|
87
|
+
# it looks like that dry-schema v1.9.1 has an issue with setting an array of schemas as a default value
|
88
|
+
# e.g. optional(:emitters).value(:array).each { Emitter | HTTP }.default(DEFAULT_EMITTERS) does not work well
|
89
|
+
# so let's do a dirty hack...
|
90
|
+
h = result.to_h
|
91
|
+
|
92
|
+
emitters = h[:emitters]
|
93
|
+
h[:emitters] = emitters || DEFAULT_EMITTERS
|
94
|
+
|
95
|
+
h
|
96
|
+
end
|
71
97
|
end
|
72
98
|
|
73
99
|
class RuleContract < Dry::Validation::Contract
|
data/lib/mihari/structs/rule.rb
CHANGED
@@ -133,6 +133,7 @@ module Mihari
|
|
133
133
|
queries: self[:queries],
|
134
134
|
allowed_data_types: self[:allowed_data_types],
|
135
135
|
disallowed_data_values: self[:disallowed_data_values],
|
136
|
+
emitters: self[:emitters],
|
136
137
|
id: id
|
137
138
|
)
|
138
139
|
analyzer.ignore_old_artifacts = self[:ignore_old_artifacts]
|
data/lib/mihari/types.rb
CHANGED
@@ -22,7 +22,6 @@ module Mihari
|
|
22
22
|
"circl",
|
23
23
|
"dnpedia",
|
24
24
|
"dnstwister",
|
25
|
-
"feed",
|
26
25
|
"greynoise",
|
27
26
|
"onyphe",
|
28
27
|
"otx",
|
@@ -38,7 +37,16 @@ module Mihari
|
|
38
37
|
"vt"
|
39
38
|
)
|
40
39
|
|
41
|
-
|
42
|
-
|
40
|
+
HttpRequestMethods = Types::String.enum("GET", "POST")
|
41
|
+
HttpRequestPayloadTypes = Types::String.enum("application/json", "application/x-www-form-urlencoded")
|
42
|
+
|
43
|
+
EmitterTypes = Types::String.enum(
|
44
|
+
"database",
|
45
|
+
"http",
|
46
|
+
"misp",
|
47
|
+
"slack",
|
48
|
+
"the_hive",
|
49
|
+
"webhook"
|
50
|
+
)
|
43
51
|
end
|
44
52
|
end
|
data/lib/mihari/version.rb
CHANGED
@@ -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><script defer="defer" type="module" src="/static/js/chunk-vendors.
|
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><script defer="defer" type="module" src="/static/js/chunk-vendors.c5525f1e.js"></script><script defer="defer" type="module" src="/static/js/app.e74e91d7.js"></script><link href="/static/css/chunk-vendors.c57bb3fd.css" rel="stylesheet"><link href="/static/css/app.0de4b715.css" rel="stylesheet"><script defer="defer" src="/static/js/chunk-vendors-legacy.41357cdf.js" nomodule></script><script defer="defer" src="/static/js/app-legacy.e451304b.js" nomodule></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></body></html>
|