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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +25 -3
  3. data/lib/mihari/analyzers/rule.rb +49 -6
  4. data/lib/mihari/constants.rb +2 -0
  5. data/lib/mihari/emitters/base.rb +3 -0
  6. data/lib/mihari/emitters/http.rb +127 -0
  7. data/lib/mihari/emitters/webhook.rb +7 -16
  8. data/lib/mihari/entities/rule.rb +5 -0
  9. data/lib/mihari/feed/reader.rb +6 -4
  10. data/lib/mihari/http.rb +19 -14
  11. data/lib/mihari/schemas/rule.rb +30 -4
  12. data/lib/mihari/structs/rule.rb +1 -0
  13. data/lib/mihari/types.rb +11 -3
  14. data/lib/mihari/version.rb +1 -1
  15. data/lib/mihari/web/public/index.html +1 -1
  16. data/lib/mihari/web/public/redoc-static.html +57 -51
  17. data/lib/mihari/web/public/static/css/app.0de4b715.css +1 -0
  18. data/lib/mihari/web/public/static/css/chunk-vendors.c57bb3fd.css +7 -0
  19. data/lib/mihari/web/public/static/fonts/fa-brands-400.edf40f86.woff2 +0 -0
  20. data/lib/mihari/web/public/static/fonts/fa-brands-400.f7223235.ttf +0 -0
  21. data/lib/mihari/web/public/static/fonts/fa-regular-400.3665ebc7.woff2 +0 -0
  22. data/lib/mihari/web/public/static/fonts/fa-regular-400.a7fde52b.ttf +0 -0
  23. data/lib/mihari/web/public/static/fonts/fa-solid-900.0d2abd43.woff2 +0 -0
  24. data/lib/mihari/web/public/static/fonts/fa-solid-900.5b03221c.ttf +0 -0
  25. data/lib/mihari/web/public/static/fonts/fa-v4compatibility.42932bea.ttf +0 -0
  26. data/lib/mihari/web/public/static/js/app-legacy.e451304b.js +2 -0
  27. data/lib/mihari/web/public/static/js/app-legacy.e451304b.js.map +1 -0
  28. data/lib/mihari/web/public/static/js/app.e74e91d7.js +2 -0
  29. data/lib/mihari/web/public/static/js/app.e74e91d7.js.map +1 -0
  30. data/lib/mihari/web/public/static/js/chunk-vendors-legacy.41357cdf.js +25 -0
  31. data/lib/mihari/web/public/static/js/chunk-vendors-legacy.41357cdf.js.map +1 -0
  32. data/lib/mihari/web/public/static/js/chunk-vendors.c5525f1e.js +31 -0
  33. data/lib/mihari/web/public/static/js/chunk-vendors.c5525f1e.js.map +1 -0
  34. data/lib/mihari.rb +11 -9
  35. data/mihari.gemspec +10 -5
  36. data/sig/lib/mihari/constants.rbs +2 -0
  37. data/sig/lib/mihari/emitters/http.rbs +35 -0
  38. data/sig/lib/mihari/http.rbs +2 -3
  39. data/sig/lib/mihari/structs/rule.rbs +1 -3
  40. data/sig/lib/mihari/types.rbs +2 -0
  41. metadata +76 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 853724da55a4225403595284e6437da9816d016d99e380d239507e1eecdf35cd
4
- data.tar.gz: 35e5ef84d38c67eaa1670a5e6261aed5c629966921ca3b14fb2509c9e3993c45
3
+ metadata.gz: dc5b6ab7daa3030a0ef0778df2753247bde10b978be709102ecf1be60b672a9c
4
+ data.tar.gz: 1c1c283cf1e2989e9e94e0aa582567dd71b7900f5c5faa40a0ce3f1b327982a3
5
5
  SHA512:
6
- metadata.gz: 50855245bf70579da57c26859d2ab491e5238f7200c1de50cc7f46cafcd94c57e411e0fdbffdd60ddb00df2c8783ed051c87be24963fe10c5459784f337bd8d3
7
- data.tar.gz: 436e6c0f3ceacb5b72f4212e83832759320cce109310736d87f6d1bfb49ac61f32a25a4d44cf133d2d5794466dea72bd69244997ccbbc6525418bc9aef403d0f
6
+ metadata.gz: 918ee19022035b5f5e3db9a00318253803b1cc430b45676225af94861fe6dc6fe51343545d224e6094f09ad37dc003713fbbcb1777904b01205903e22d05bf23
7
+ data.tar.gz: c5ce99eb3d8e01b1b2a8ac51916afa172c1fecb55611a3b8aa76e686c72801beb11135ed1c8aed31e11b693b7467dbe513902f55e229c834bbfcb09460ba4202
@@ -1,9 +1,13 @@
1
1
  name: Ruby CI
2
2
 
3
- on: [pull_request]
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ pull_request:
7
+ branches: [master]
4
8
 
5
9
  jobs:
6
- build:
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 |params|
63
- analyzer_name = params[:analyzer]
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 = params[:query]
81
+ query = parmas[:query]
67
82
 
68
83
  # set interval in the top level
69
- options = params[:options] || {}
84
+ options = parmas[:options] || {}
70
85
  interval = options[:interval]
71
- params[:interval] = interval if interval
86
+ parmas[:interval] = interval if interval
72
87
 
73
- analyzer = klass.new(query, **params)
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
  #
@@ -2,4 +2,6 @@
2
2
 
3
3
  module Mihari
4
4
  ALLOWED_DATA_TYPES = ["hash", "ip", "domain", "url", "mail"].freeze
5
+
6
+ DEFAULT_EMITTERS = ["database", "misp", "slack", "the_hive", "webhook"].map { |name| { emitter: name } }.freeze
5
7
  end
@@ -6,6 +6,9 @@ module Mihari
6
6
  include Mixins::Configurable
7
7
  include Mixins::Retriable
8
8
 
9
+ def initialize(*)
10
+ end
11
+
9
12
  class << self
10
13
  def inherited(child)
11
14
  super
@@ -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
- uri = Addressable::URI.parse(Mihari.config.webhook_url)
15
- data = {
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
- if use_json_body?
24
- Net::HTTP.post(uri, data.to_json, "Content-Type" => "application/json")
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 [<Type>] <description>
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 NOT
46
+ # Check whether to use JSON body or not
56
47
  #
57
- # @return [<Type>] <description>
48
+ # @return [Boolean]
58
49
  #
59
50
  def use_json_body?
60
51
  @use_json_body ||= Mihari.config.webhook_use_json_body
@@ -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
@@ -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, :http_request_payload_type, :http_request_payload
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, payload_type: http_request_payload_type)
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, :payload_type, :payload
7
+ attr_reader :uri, :headers, :payload
6
8
 
7
- def initialize(uri, headers: {}, payload_type: nil, payload: {})
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 payload_type
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: {}, payload_type: nil, payload: {})
48
- client = new(uri, headers: headers, payload_type: payload_type, payload: payload)
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: {}, payload_type: nil, payload: {})
53
- client = new(uri, headers: headers, payload_type: payload_type, payload: payload)
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
- code = res.code.to_i
88
- raise HttpError, "Unsupported response code returned: #{code}" if code != 200
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
@@ -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(:http_request_payload_type).value(Types::FeedHttpRequestPayloadTypes)
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
@@ -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
- FeedHttpRequestMethods = Types::String.enum("GET", "POST")
42
- FeedHttpRequestPayloadTypes = Types::String.enum("application/json", "application/x-www-form-urlencoded")
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- VERSION = "4.2.0"
4
+ VERSION = "4.3.0"
5
5
  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><script defer="defer" type="module" src="/static/js/chunk-vendors.15e84e22.js"></script><script defer="defer" type="module" src="/static/js/app.4818aedd.js"></script><link href="/static/css/chunk-vendors.3ed9b08e.css" rel="stylesheet"><link href="/static/css/app.43138058.css" rel="stylesheet"><script defer="defer" src="/static/js/chunk-vendors-legacy.c99e452e.js" nomodule></script><script defer="defer" src="/static/js/app-legacy.46b666f0.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>
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>