mihari 2.4.0 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +7 -0
- data/.overcommit.yml +12 -0
- data/README.md +1 -9
- data/build_frontend.sh +5 -0
- data/docker/Dockerfile +1 -1
- data/exe/mihari +1 -1
- data/lib/mihari.rb +89 -15
- data/lib/mihari/analyzers/base.rb +49 -8
- data/lib/mihari/analyzers/basic.rb +1 -2
- data/lib/mihari/analyzers/binaryedge.rb +7 -13
- data/lib/mihari/analyzers/censys.rb +26 -63
- data/lib/mihari/analyzers/circl.rb +20 -17
- data/lib/mihari/analyzers/crtsh.rb +6 -13
- data/lib/mihari/analyzers/dnpedia.rb +6 -12
- data/lib/mihari/analyzers/dnstwister.rb +13 -10
- data/lib/mihari/analyzers/onyphe.rb +6 -12
- data/lib/mihari/analyzers/otx.rb +22 -19
- data/lib/mihari/analyzers/passivetotal.rb +22 -21
- data/lib/mihari/analyzers/pulsedive.rb +16 -13
- data/lib/mihari/analyzers/rule.rb +97 -0
- data/lib/mihari/analyzers/securitytrails.rb +22 -19
- data/lib/mihari/analyzers/shodan.rb +7 -13
- data/lib/mihari/analyzers/spyse.rb +12 -19
- data/lib/mihari/analyzers/urlscan.rb +22 -27
- data/lib/mihari/analyzers/virustotal.rb +25 -22
- data/lib/mihari/analyzers/zoomeye.rb +14 -20
- data/lib/mihari/cli/analyzer.rb +44 -0
- data/lib/mihari/cli/base.rb +27 -0
- data/lib/mihari/cli/init.rb +13 -0
- data/lib/mihari/cli/main.rb +30 -0
- data/lib/mihari/cli/mixins/utils.rb +88 -0
- data/lib/mihari/cli/validator.rb +11 -0
- data/lib/mihari/commands/binaryedge.rb +1 -1
- data/lib/mihari/commands/censys.rb +1 -1
- data/lib/mihari/commands/circl.rb +2 -2
- data/lib/mihari/commands/crtsh.rb +1 -1
- data/lib/mihari/commands/dnpedia.rb +1 -1
- data/lib/mihari/commands/dnstwister.rb +2 -2
- data/lib/mihari/commands/init.rb +46 -0
- data/lib/mihari/commands/json.rb +1 -1
- data/lib/mihari/commands/onyphe.rb +1 -1
- data/lib/mihari/commands/otx.rb +2 -2
- data/lib/mihari/commands/passivetotal.rb +2 -2
- data/lib/mihari/commands/pulsedive.rb +2 -2
- data/lib/mihari/commands/search.rb +77 -0
- data/lib/mihari/commands/securitytrails.rb +2 -2
- data/lib/mihari/commands/shodan.rb +1 -1
- data/lib/mihari/commands/spyse.rb +1 -1
- data/lib/mihari/commands/urlscan.rb +2 -2
- data/lib/mihari/commands/validator.rb +38 -0
- data/lib/mihari/commands/virustotal.rb +2 -2
- data/lib/mihari/commands/zoomeye.rb +1 -1
- data/lib/mihari/constraints.rb +5 -0
- data/lib/mihari/database.rb +13 -2
- data/lib/mihari/emitters/base.rb +2 -2
- data/lib/mihari/emitters/database.rb +1 -1
- data/lib/mihari/emitters/misp.rb +3 -1
- data/lib/mihari/emitters/slack.rb +6 -7
- data/lib/mihari/emitters/the_hive.rb +1 -1
- data/lib/mihari/emitters/webhook.rb +2 -9
- data/lib/mihari/mixins/configurable.rb +38 -0
- data/lib/mihari/mixins/configuration.rb +90 -0
- data/lib/mihari/mixins/hash.rb +20 -0
- data/lib/mihari/mixins/refang.rb +21 -0
- data/lib/mihari/mixins/retriable.rb +27 -0
- data/lib/mihari/mixins/rule.rb +79 -0
- data/lib/mihari/models/alert.rb +28 -1
- data/lib/mihari/models/artifact.rb +11 -1
- data/lib/mihari/notifiers/base.rb +9 -1
- data/lib/mihari/notifiers/exception_notifier.rb +50 -0
- data/lib/mihari/notifiers/slack.rb +29 -0
- data/lib/mihari/schemas/analyzer.rb +25 -0
- data/lib/mihari/schemas/configuration.rb +42 -0
- data/lib/mihari/schemas/macros.rb +17 -0
- data/lib/mihari/schemas/rule.rb +72 -0
- data/lib/mihari/serializers/artifact.rb +1 -1
- data/lib/mihari/status.rb +14 -0
- data/lib/mihari/templates/rule.yml.erb +19 -0
- data/lib/mihari/type_checker.rb +8 -3
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/app.rb +2 -1
- data/lib/mihari/web/controllers/analyzers_controller.rb +38 -0
- data/lib/mihari/web/controllers/base_controller.rb +1 -1
- data/lib/mihari/web/public/index.html +1 -21
- data/lib/mihari/web/public/redoc-static.html +338 -461
- data/lib/mihari/web/public/static/js/app.365f1907.js +13 -0
- data/lib/mihari/web/public/static/js/app.365f1907.js.map +1 -0
- data/lib/mihari/web/public/static/js/app.ab213f7c.js +12 -0
- data/lib/mihari/web/public/static/js/app.ab213f7c.js.map +1 -0
- data/mihari.gemspec +16 -9
- metadata +135 -58
- data/.rubocop.yml +0 -161
- data/lib/mihari/analyzers/free_text.rb +0 -48
- data/lib/mihari/analyzers/http_hash.rb +0 -100
- data/lib/mihari/analyzers/passive_dns.rb +0 -59
- data/lib/mihari/analyzers/passive_ssl.rb +0 -55
- data/lib/mihari/analyzers/reverse_whois.rb +0 -55
- data/lib/mihari/analyzers/securitytrails_domain_feed.rb +0 -59
- data/lib/mihari/analyzers/ssh_fingerprint.rb +0 -58
- data/lib/mihari/cli.rb +0 -126
- data/lib/mihari/commands/config.rb +0 -27
- data/lib/mihari/commands/free_text.rb +0 -21
- data/lib/mihari/commands/http_hash.rb +0 -25
- data/lib/mihari/commands/passive_dns.rb +0 -21
- data/lib/mihari/commands/passive_ssl.rb +0 -21
- data/lib/mihari/commands/reverse_whois.rb +0 -21
- data/lib/mihari/commands/securitytrails_domain_feed.rb +0 -23
- data/lib/mihari/commands/ssh_fingerprint.rb +0 -21
- data/lib/mihari/config.rb +0 -85
- data/lib/mihari/configurable.rb +0 -21
- data/lib/mihari/html.rb +0 -43
- data/lib/mihari/retriable.rb +0 -17
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mihari
|
4
|
+
module Mixins
|
5
|
+
module Retriable
|
6
|
+
#
|
7
|
+
# Retry on error
|
8
|
+
#
|
9
|
+
# @param [Integer] times
|
10
|
+
# @param [Integer] interval
|
11
|
+
#
|
12
|
+
# @return [nil]
|
13
|
+
#
|
14
|
+
def retry_on_error(times: 3, interval: 10)
|
15
|
+
try = 0
|
16
|
+
begin
|
17
|
+
try += 1
|
18
|
+
yield
|
19
|
+
rescue Errno::ECONNRESET, Errno::ECONNABORTED, Errno::EPIPE, OpenSSL::SSL::SSLError, Timeout::Error, RetryableError => e
|
20
|
+
sleep interval
|
21
|
+
retry if try < times
|
22
|
+
raise e
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require "date"
|
2
|
+
require "pathname"
|
3
|
+
require "yaml"
|
4
|
+
require "erb"
|
5
|
+
|
6
|
+
module Mihari
|
7
|
+
module Mixins
|
8
|
+
module Rule
|
9
|
+
#
|
10
|
+
# Load rule into hash
|
11
|
+
#
|
12
|
+
# @param [String] path Path to YAML file or YAML string
|
13
|
+
#
|
14
|
+
# @return [Hash]
|
15
|
+
#
|
16
|
+
def load_rule(path)
|
17
|
+
return YAML.safe_load(File.read(path), permitted_classes: [Date], symbolize_names: true) if Pathname(path).exist?
|
18
|
+
|
19
|
+
YAML.safe_load(path, symbolize_names: true)
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# Validate rule schema
|
24
|
+
#
|
25
|
+
# @param [Hash] rule
|
26
|
+
#
|
27
|
+
def validate_rule(rule)
|
28
|
+
error_message = "Failed to parse the input as a rule!"
|
29
|
+
|
30
|
+
contract = Schemas::RuleContract.new
|
31
|
+
begin
|
32
|
+
result = contract.call(rule)
|
33
|
+
unless result.errors.empty?
|
34
|
+
messages = result.errors.messages.map do |message|
|
35
|
+
path = message.path.map(&:to_s).join
|
36
|
+
"#{path} #{message.text}"
|
37
|
+
end
|
38
|
+
puts error_message.colorize(:red)
|
39
|
+
raise ArgumentError, messages.join("\n")
|
40
|
+
end
|
41
|
+
rescue NoMethodError
|
42
|
+
puts error_message.colorize(:red)
|
43
|
+
raise ArgumentError, "Invalid rule schema"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
# Returns a template for rule
|
49
|
+
#
|
50
|
+
# @return [String] A template for rule
|
51
|
+
#
|
52
|
+
def rule_template
|
53
|
+
# Use ERB to fill created_on and updated_on with Date.today
|
54
|
+
data = File.read(File.expand_path("../templates/rule.yml.erb", __dir__))
|
55
|
+
template = ERB.new(data)
|
56
|
+
data = template.result
|
57
|
+
|
58
|
+
# validate the template of rule for just in case
|
59
|
+
rule = YAML.safe_load(data, permitted_classes: [Date], symbolize_names: true)
|
60
|
+
validate_rule rule
|
61
|
+
|
62
|
+
data
|
63
|
+
end
|
64
|
+
|
65
|
+
#
|
66
|
+
# Create (blank) rule file
|
67
|
+
#
|
68
|
+
# @param [String] filename
|
69
|
+
# @param [Dry::Files] files
|
70
|
+
# @param [String] template
|
71
|
+
#
|
72
|
+
# @return [nil]
|
73
|
+
#
|
74
|
+
def initialize_rule_yaml(filename, files = Dry::Files.new, template: rule_template)
|
75
|
+
files.write(filename, template)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/mihari/models/alert.rb
CHANGED
@@ -10,6 +10,21 @@ module Mihari
|
|
10
10
|
has_many :tags, through: :taggings
|
11
11
|
|
12
12
|
class << self
|
13
|
+
#
|
14
|
+
# Search alerts
|
15
|
+
#
|
16
|
+
# @param [String, nil] artifact_data
|
17
|
+
# @param [String, nil] description
|
18
|
+
# @param [String, nil] source
|
19
|
+
# @param [String, nil] tag_name
|
20
|
+
# @param [String, nil] title
|
21
|
+
# @param [String, nil] from_at
|
22
|
+
# @param [String, nil] to_at
|
23
|
+
# @param [Integer, nil] limit
|
24
|
+
# @param [Integer, nil] page
|
25
|
+
#
|
26
|
+
# @return [Array<Hash>]
|
27
|
+
#
|
13
28
|
def search(artifact_data: nil, description: nil, source: nil, tag_name: nil, title: nil, from_at: nil, to_at: nil, limit: 10, page: 1)
|
14
29
|
limit = limit.to_i
|
15
30
|
raise ArgumentError, "limit should be bigger than zero" unless limit.positive?
|
@@ -20,7 +35,6 @@ module Mihari
|
|
20
35
|
offset = (page - 1) * limit
|
21
36
|
|
22
37
|
relation = build_relation(artifact_data: artifact_data, title: title, description: description, source: source, tag_name: tag_name, from_at: from_at, to_at: to_at)
|
23
|
-
# relation = relation.group("alerts.id")
|
24
38
|
|
25
39
|
alerts = relation.limit(limit).offset(offset).order(id: :desc)
|
26
40
|
|
@@ -32,6 +46,19 @@ module Mihari
|
|
32
46
|
end
|
33
47
|
end
|
34
48
|
|
49
|
+
#
|
50
|
+
# Count alerts
|
51
|
+
#
|
52
|
+
# @param [String, nil] artifact_data
|
53
|
+
# @param [String, nil] description
|
54
|
+
# @param [String, nil] source
|
55
|
+
# @param [String, nil] tag_name
|
56
|
+
# @param [String, nil] title
|
57
|
+
# @param [String, nil] from_at
|
58
|
+
# @param [String, nil] to_at
|
59
|
+
#
|
60
|
+
# @return [Integer]
|
61
|
+
#
|
35
62
|
def count(artifact_data: nil, description: nil, source: nil, tag_name: nil, title: nil, from_at: nil, to_at: nil)
|
36
63
|
relation = build_relation(artifact_data: artifact_data, title: title, description: description, source: source, tag_name: tag_name, from_at: from_at, to_at: to_at)
|
37
64
|
relation.distinct("alerts.id").count
|
@@ -16,20 +16,30 @@ end
|
|
16
16
|
module Mihari
|
17
17
|
class Artifact < ActiveRecord::Base
|
18
18
|
include ActiveModel::Validations
|
19
|
+
|
19
20
|
validates_with ArtifactValidator
|
20
21
|
|
21
22
|
def initialize(attributes)
|
22
23
|
super
|
24
|
+
|
23
25
|
self.data_type = TypeChecker.type(data)
|
24
26
|
end
|
25
27
|
|
28
|
+
#
|
29
|
+
# Check uniqueness of artifact
|
30
|
+
#
|
31
|
+
# @param [Boolean] ignore_old_artifacts
|
32
|
+
# @param [Integer] ignore_threshold
|
33
|
+
#
|
34
|
+
# @return [Boolean] true if it is unique. Otherwise false.
|
35
|
+
#
|
26
36
|
def unique?(ignore_old_artifacts: false, ignore_threshold: 0)
|
27
37
|
artifact = self.class.where(data: data).order(created_at: :desc).first
|
28
38
|
return true if artifact.nil?
|
29
39
|
|
30
40
|
return false unless ignore_old_artifacts
|
31
41
|
|
32
|
-
days_before = (-ignore_threshold).days.from_now
|
42
|
+
days_before = (-ignore_threshold).days.from_now.utc
|
33
43
|
# if an artifact is created before {ignore_threshold} days, ignore it
|
34
44
|
# within {ignore_threshold} days, do not ignore it
|
35
45
|
artifact.created_at < days_before
|
@@ -3,11 +3,19 @@
|
|
3
3
|
module Mihari
|
4
4
|
module Notifiers
|
5
5
|
class Base
|
6
|
-
#
|
6
|
+
# Validate notifier availability
|
7
|
+
#
|
8
|
+
# @return [Boolean]
|
9
|
+
#
|
7
10
|
def valid?
|
8
11
|
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
|
9
12
|
end
|
10
13
|
|
14
|
+
#
|
15
|
+
# Send a notification
|
16
|
+
#
|
17
|
+
# @return [nil]
|
18
|
+
#
|
11
19
|
def notify
|
12
20
|
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
|
13
21
|
end
|
@@ -24,10 +24,25 @@ module Mihari
|
|
24
24
|
notify_to_slack(text: clean_message, attachments: attachments) if @slack.valid?
|
25
25
|
end
|
26
26
|
|
27
|
+
#
|
28
|
+
# Send notification to Slack
|
29
|
+
#
|
30
|
+
# @param [String] text
|
31
|
+
# @param [Array<Hash>] attachments
|
32
|
+
#
|
33
|
+
# @return [nil]
|
34
|
+
#
|
27
35
|
def notify_to_slack(text:, attachments:)
|
28
36
|
@slack.notify(text: text, attachments: attachments)
|
29
37
|
end
|
30
38
|
|
39
|
+
#
|
40
|
+
# Send notification to STDOUT
|
41
|
+
#
|
42
|
+
# @param [Exception] exception
|
43
|
+
#
|
44
|
+
# @return [nil]
|
45
|
+
#
|
31
46
|
def notify_to_stdout(exception)
|
32
47
|
text = to_text(exception.class).chomp
|
33
48
|
message = "#{text}: #{exception.message}"
|
@@ -35,6 +50,14 @@ module Mihari
|
|
35
50
|
puts format_backtrace(exception.backtrace) if exception.backtrace
|
36
51
|
end
|
37
52
|
|
53
|
+
#
|
54
|
+
# Convert exception to attachments (for Slack)
|
55
|
+
#
|
56
|
+
# @param [Exception] exception
|
57
|
+
# @param [String] clean_message
|
58
|
+
#
|
59
|
+
# @return [Array<Hash>]
|
60
|
+
#
|
38
61
|
def to_attachments(exception, clean_message)
|
39
62
|
text = to_text(exception.class)
|
40
63
|
backtrace = exception.backtrace
|
@@ -43,12 +66,27 @@ module Mihari
|
|
43
66
|
[color: @color, text: text, fields: fields, mrkdwn_in: %w[text fields]]
|
44
67
|
end
|
45
68
|
|
69
|
+
#
|
70
|
+
# Convert exception class to text
|
71
|
+
#
|
72
|
+
# @param [Class<Exception>] exception_class
|
73
|
+
#
|
74
|
+
# @return [String]
|
75
|
+
#
|
46
76
|
def to_text(exception_class)
|
47
77
|
measure_word = /^[aeiou]/i.match?(exception_class.to_s) ? "An" : "A"
|
48
78
|
exception_name = "*#{measure_word}* `#{exception_class}`"
|
49
79
|
"#{exception_name} *occured in background*\n"
|
50
80
|
end
|
51
81
|
|
82
|
+
#
|
83
|
+
# Convert clean_message and backtrace into fields (for Slack)
|
84
|
+
#
|
85
|
+
# @param [String] clean_message
|
86
|
+
# @param [Array] backtrace
|
87
|
+
#
|
88
|
+
# @return [Array<Hash>]
|
89
|
+
#
|
52
90
|
def to_fields(clean_message, backtrace)
|
53
91
|
fields = [
|
54
92
|
{ title: "Exception", value: clean_message },
|
@@ -62,12 +100,24 @@ module Mihari
|
|
62
100
|
fields
|
63
101
|
end
|
64
102
|
|
103
|
+
#
|
104
|
+
# Hostname of runnning instance
|
105
|
+
#
|
106
|
+
# @return [String]
|
107
|
+
#
|
65
108
|
def hostname
|
66
109
|
Socket.gethostname
|
67
110
|
rescue StandardError => _e
|
68
111
|
"N/A"
|
69
112
|
end
|
70
113
|
|
114
|
+
#
|
115
|
+
# Format backtrace in string
|
116
|
+
#
|
117
|
+
# @param [Array] backtrace
|
118
|
+
#
|
119
|
+
# @return [String]
|
120
|
+
#
|
71
121
|
def format_backtrace(backtrace)
|
72
122
|
return nil unless backtrace
|
73
123
|
|
@@ -9,22 +9,51 @@ module Mihari
|
|
9
9
|
SLACK_CHANNEL_KEY = "SLACK_CHANNEL"
|
10
10
|
DEFAULT_USERNAME = "mihari"
|
11
11
|
|
12
|
+
#
|
13
|
+
# Slack channel to post
|
14
|
+
#
|
15
|
+
# @return [String]
|
16
|
+
#
|
12
17
|
def slack_channel
|
13
18
|
Mihari.config.slack_channel || "#general"
|
14
19
|
end
|
15
20
|
|
21
|
+
#
|
22
|
+
# Slack webhook URL
|
23
|
+
#
|
24
|
+
# @return [String]
|
25
|
+
#
|
16
26
|
def slack_webhook_url
|
17
27
|
Mihari.config.slack_webhook_url
|
18
28
|
end
|
19
29
|
|
30
|
+
#
|
31
|
+
# Check Slack webhook URL is set
|
32
|
+
#
|
33
|
+
# @return [Boolean]
|
34
|
+
#
|
20
35
|
def slack_webhook_url?
|
21
36
|
!Mihari.config.slack_webhook_url.nil?
|
22
37
|
end
|
23
38
|
|
39
|
+
#
|
40
|
+
# Check Slack webhook URL is set. Alias of #slack_webhook_url?.
|
41
|
+
#
|
42
|
+
# @return [Boolean]
|
43
|
+
#
|
24
44
|
def valid?
|
25
45
|
slack_webhook_url?
|
26
46
|
end
|
27
47
|
|
48
|
+
#
|
49
|
+
# Send notification to Slack
|
50
|
+
#
|
51
|
+
# @param [String] text
|
52
|
+
# @param [Array<Hash>] attachments
|
53
|
+
# @param [Boolean] mrkdwn
|
54
|
+
#
|
55
|
+
# @return [nil]
|
56
|
+
#
|
28
57
|
def notify(text:, attachments: [], mrkdwn: true)
|
29
58
|
notifier = ::Slack::Notifier.new(slack_webhook_url, channel: slack_channel, username: DEFAULT_USERNAME)
|
30
59
|
notifier.post(text: text, attachments: attachments, mrkdwn: mrkdwn)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/schema"
|
4
|
+
require "dry/validation"
|
5
|
+
|
6
|
+
require "mihari/schemas/macros"
|
7
|
+
|
8
|
+
module Mihari
|
9
|
+
module Schemas
|
10
|
+
AnalyzerRun = Dry::Schema.Params do
|
11
|
+
required(:title).value(:string)
|
12
|
+
required(:description).value(:string)
|
13
|
+
required(:source).value(:string)
|
14
|
+
required(:artifacts).value(array[:string])
|
15
|
+
|
16
|
+
optional(:tags).value(array[:string]).default([])
|
17
|
+
optional(:ignoreOldArtifacts).value(:bool).default(false)
|
18
|
+
optional(:ignoreThreshold).value(:integer).default(0)
|
19
|
+
end
|
20
|
+
|
21
|
+
class AnalyzerRunContract < Dry::Validation::Contract
|
22
|
+
params(AnalyzerRun)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/schema"
|
4
|
+
require "dry/validation"
|
5
|
+
|
6
|
+
require "mihari/schemas/macros"
|
7
|
+
|
8
|
+
module Mihari
|
9
|
+
module Schemas
|
10
|
+
Configuration = Dry::Schema.Params do
|
11
|
+
optional(:binaryedge_api_key).value(:string)
|
12
|
+
optional(:censys_id).value(:string)
|
13
|
+
optional(:censys_secret).value(:string)
|
14
|
+
optional(:circl_passive_password).value(:string)
|
15
|
+
optional(:circl_passive_username).value(:string)
|
16
|
+
optional(:misp_api_endpoint).value(:string)
|
17
|
+
optional(:misp_api_key).value(:string)
|
18
|
+
optional(:onyphe_api_key).value(:string)
|
19
|
+
optional(:otx_api_key).value(:string)
|
20
|
+
optional(:passivetotal_api_key).value(:string)
|
21
|
+
optional(:passivetotal_username).value(:string)
|
22
|
+
optional(:pulsedive_api_key).value(:string)
|
23
|
+
optional(:securitytrails_api_key).value(:string)
|
24
|
+
optional(:shodan_api_key).value(:string)
|
25
|
+
optional(:slack_channel).value(:string)
|
26
|
+
optional(:slack_webhook_url).value(:string)
|
27
|
+
optional(:spyse_api_key).value(:string)
|
28
|
+
optional(:thehive_api_endpoint).value(:string)
|
29
|
+
optional(:thehive_api_key).value(:string)
|
30
|
+
optional(:urlscan_api_key).value(:string)
|
31
|
+
optional(:virustotal_api_key).value(:string)
|
32
|
+
optional(:zoomeye_api_key).value(:string)
|
33
|
+
optional(:webhook_url).value(:string)
|
34
|
+
optional(:webhook_use_json_body).value(:bool)
|
35
|
+
optional(:database).value(:string)
|
36
|
+
end
|
37
|
+
|
38
|
+
class ConfigurationContract < Dry::Validation::Contract
|
39
|
+
params(Configuration)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/types"
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Schema
|
7
|
+
module Macros
|
8
|
+
class DSL
|
9
|
+
def default(value)
|
10
|
+
schema_dsl.before(:rule_applier) do |result|
|
11
|
+
result.update(name => value) unless result[name]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|