mihari 4.2.0 → 4.3.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/.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>
|