mihari 5.4.1 → 5.4.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/frontend/package-lock.json +145 -146
- data/frontend/package.json +8 -8
- data/frontend/src/swagger.yaml +306 -272
- data/lib/mihari/analyzers/base.rb +0 -4
- data/lib/mihari/analyzers/binaryedge.rb +4 -44
- data/lib/mihari/analyzers/censys.rb +4 -20
- data/lib/mihari/analyzers/circl.rb +2 -26
- data/lib/mihari/analyzers/crtsh.rb +2 -17
- data/lib/mihari/analyzers/dnstwister.rb +1 -3
- data/lib/mihari/analyzers/greynoise.rb +5 -4
- data/lib/mihari/analyzers/hunterhow.rb +8 -23
- data/lib/mihari/analyzers/onyphe.rb +5 -39
- data/lib/mihari/analyzers/otx.rb +2 -38
- data/lib/mihari/analyzers/passivetotal.rb +3 -41
- data/lib/mihari/analyzers/securitytrails.rb +3 -41
- data/lib/mihari/analyzers/shodan.rb +7 -39
- data/lib/mihari/analyzers/urlscan.rb +2 -38
- data/lib/mihari/analyzers/virustotal_intelligence.rb +2 -25
- data/lib/mihari/analyzers/zoomeye.rb +17 -83
- data/lib/mihari/cli/alert.rb +11 -0
- data/lib/mihari/cli/main.rb +6 -1
- data/lib/mihari/clients/base.rb +9 -1
- data/lib/mihari/clients/binaryedge.rb +27 -2
- data/lib/mihari/clients/censys.rb +32 -2
- data/lib/mihari/clients/circl.rb +28 -1
- data/lib/mihari/clients/crtsh.rb +9 -2
- data/lib/mihari/clients/dnstwister.rb +4 -2
- data/lib/mihari/clients/greynoise.rb +31 -4
- data/lib/mihari/clients/hunterhow.rb +41 -3
- data/lib/mihari/clients/onyphe.rb +25 -3
- data/lib/mihari/clients/otx.rb +40 -0
- data/lib/mihari/clients/passivetotal.rb +33 -15
- data/lib/mihari/clients/securitytrails.rb +44 -0
- data/lib/mihari/clients/shodan.rb +30 -2
- data/lib/mihari/clients/urlscan.rb +32 -6
- data/lib/mihari/clients/virustotal.rb +29 -4
- data/lib/mihari/clients/zoomeye.rb +53 -2
- data/lib/mihari/commands/alert.rb +42 -0
- data/lib/mihari/commands/rule.rb +2 -2
- data/lib/mihari/commands/search.rb +20 -59
- data/lib/mihari/commands/web.rb +1 -1
- data/lib/mihari/config.rb +2 -2
- data/lib/mihari/emitters/base.rb +1 -1
- data/lib/mihari/emitters/database.rb +2 -2
- data/lib/mihari/errors.rb +23 -2
- data/lib/mihari/http.rb +7 -1
- data/lib/mihari/schemas/alert.rb +14 -0
- data/lib/mihari/services/alert_proxy.rb +106 -0
- data/lib/mihari/services/alert_runner.rb +22 -0
- data/lib/mihari/services/{rule.rb → rule_proxy.rb} +10 -6
- data/lib/mihari/services/rule_runner.rb +49 -0
- data/lib/mihari/structs/censys.rb +11 -11
- data/lib/mihari/structs/greynoise.rb +17 -8
- data/lib/mihari/structs/onyphe.rb +7 -7
- data/lib/mihari/structs/shodan.rb +5 -5
- data/lib/mihari/structs/urlscan.rb +3 -3
- data/lib/mihari/structs/virustotal_intelligence.rb +3 -3
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/endpoints/alerts.rb +22 -0
- data/lib/mihari/web/endpoints/rules.rb +8 -8
- data/lib/mihari/web/public/assets/{index-61dc587c.js → index-4d7eda9f.js} +1 -1
- data/lib/mihari/web/public/index.html +1 -1
- data/lib/mihari/web/public/redoc-static.html +29 -27
- data/lib/mihari.rb +6 -1
- data/mihari.gemspec +9 -10
- metadata +28 -37
- data/Steepfile +0 -31
@@ -0,0 +1,106 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mihari
|
4
|
+
module Services
|
5
|
+
class AlertProxy
|
6
|
+
# @return [Hash]
|
7
|
+
attr_reader :data
|
8
|
+
|
9
|
+
# @return [Array, nil]
|
10
|
+
attr_reader :errors
|
11
|
+
|
12
|
+
#
|
13
|
+
# Initialize
|
14
|
+
#
|
15
|
+
# @param [Hash] data
|
16
|
+
#
|
17
|
+
def initialize(data)
|
18
|
+
@data = data.deep_symbolize_keys
|
19
|
+
|
20
|
+
@errors = nil
|
21
|
+
|
22
|
+
validate
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# @return [Boolean]
|
27
|
+
#
|
28
|
+
def errors?
|
29
|
+
return false if @errors.nil?
|
30
|
+
|
31
|
+
!@errors.empty?
|
32
|
+
end
|
33
|
+
|
34
|
+
def validate
|
35
|
+
contract = Schemas::AlertContract.new
|
36
|
+
result = contract.call(data)
|
37
|
+
|
38
|
+
@data = result.to_h
|
39
|
+
@errors = result.errors
|
40
|
+
end
|
41
|
+
|
42
|
+
def validate!
|
43
|
+
return unless errors?
|
44
|
+
|
45
|
+
Mihari.logger.error "Failed to parse the input as an alert:"
|
46
|
+
Mihari.logger.error JSON.pretty_generate(errors.to_h)
|
47
|
+
|
48
|
+
raise AlertValidationError, errors
|
49
|
+
end
|
50
|
+
|
51
|
+
def [](key)
|
52
|
+
data key.to_sym
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# @return [String]
|
57
|
+
#
|
58
|
+
def rule_id
|
59
|
+
@rule_id ||= data[:rule_id]
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
# @return [Array<Mihari::Artifact>]
|
64
|
+
#
|
65
|
+
def artifacts
|
66
|
+
@artifacts ||= data[:artifacts].map do |data|
|
67
|
+
artifact = Artifact.new(data: data)
|
68
|
+
artifact.rule_id = rule_id
|
69
|
+
artifact
|
70
|
+
end.uniq(&:data).select(&:valid?)
|
71
|
+
end
|
72
|
+
|
73
|
+
#
|
74
|
+
# @return [Mihari::Services::RuleProxy]
|
75
|
+
#
|
76
|
+
def rule
|
77
|
+
@rule ||= Services::RuleProxy.from_model(Mihari::Rule.find(rule_id))
|
78
|
+
end
|
79
|
+
|
80
|
+
class << self
|
81
|
+
#
|
82
|
+
# Load rule from YAML string
|
83
|
+
#
|
84
|
+
# @param [String] yaml
|
85
|
+
#
|
86
|
+
# @return [Mihari::Services::Alert]
|
87
|
+
#
|
88
|
+
def from_yaml(yaml)
|
89
|
+
Services::AlertProxy.new YAML.safe_load(yaml, permitted_classes: [Date, Symbol])
|
90
|
+
rescue Psych::SyntaxError => e
|
91
|
+
raise YAMLSyntaxError, e.message
|
92
|
+
end
|
93
|
+
|
94
|
+
# @param [String] path
|
95
|
+
#
|
96
|
+
# @return [Mihari::Services::Alert, nil]
|
97
|
+
#
|
98
|
+
def from_path(path)
|
99
|
+
return nil unless Pathname(path).exist?
|
100
|
+
|
101
|
+
from_yaml File.read(path)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mihari
|
4
|
+
module Services
|
5
|
+
class AlertRunner
|
6
|
+
# @return [Mihari::Services::AlertProxy]
|
7
|
+
attr_reader :alert
|
8
|
+
|
9
|
+
def initialize(alert)
|
10
|
+
@alert = alert
|
11
|
+
end
|
12
|
+
|
13
|
+
#
|
14
|
+
# @return [Mihari::Alert]
|
15
|
+
#
|
16
|
+
def run
|
17
|
+
emitter = Mihari::Emitters::Database.new(artifacts: alert.artifacts, rule: alert.rule)
|
18
|
+
emitter.emit
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -9,7 +9,11 @@ require "yaml"
|
|
9
9
|
|
10
10
|
module Mihari
|
11
11
|
module Services
|
12
|
-
|
12
|
+
#
|
13
|
+
# proxy (or converter) class for rule
|
14
|
+
# proxying rule schema data into analyzer & model
|
15
|
+
#
|
16
|
+
class RuleProxy
|
13
17
|
include Mixins::FalsePositive
|
14
18
|
|
15
19
|
# @return [Hash]
|
@@ -141,7 +145,7 @@ module Mihari
|
|
141
145
|
#
|
142
146
|
# @return [Mihari::Rule]
|
143
147
|
#
|
144
|
-
def
|
148
|
+
def model
|
145
149
|
rule = Mihari::Rule.find(id)
|
146
150
|
|
147
151
|
rule.title = title
|
@@ -161,7 +165,7 @@ module Mihari
|
|
161
165
|
#
|
162
166
|
# @return [Mihari::Analyzers::Rule]
|
163
167
|
#
|
164
|
-
def
|
168
|
+
def analyzer
|
165
169
|
Mihari::Analyzers::Rule.new self
|
166
170
|
end
|
167
171
|
|
@@ -174,7 +178,7 @@ module Mihari
|
|
174
178
|
# @return [Mihari::Services::Rule]
|
175
179
|
#
|
176
180
|
def from_yaml(yaml)
|
177
|
-
Services::
|
181
|
+
Services::RuleProxy.new YAML.safe_load(ERB.new(yaml).result, permitted_classes: [Date, Symbol])
|
178
182
|
rescue Psych::SyntaxError => e
|
179
183
|
raise YAMLSyntaxError, e.message
|
180
184
|
end
|
@@ -185,7 +189,7 @@ module Mihari
|
|
185
189
|
# @return [Mihari::Services::Rule]
|
186
190
|
#
|
187
191
|
def from_model(model)
|
188
|
-
Services::
|
192
|
+
Services::RuleProxy.new model.data
|
189
193
|
end
|
190
194
|
|
191
195
|
#
|
@@ -211,7 +215,7 @@ module Mihari
|
|
211
215
|
def from_id(id)
|
212
216
|
return nil unless Mihari::Rule.exists?(id)
|
213
217
|
|
214
|
-
Services::
|
218
|
+
Services::RuleProxy.from_model Mihari::Rule.find(id)
|
215
219
|
end
|
216
220
|
|
217
221
|
#
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mihari
|
4
|
+
module Services
|
5
|
+
class RuleRunner
|
6
|
+
include Mixins::ErrorNotification
|
7
|
+
|
8
|
+
# @return [Mihari::Services::RuleProxy]
|
9
|
+
attr_reader :rule
|
10
|
+
|
11
|
+
# @return [Boolean]
|
12
|
+
attr_reader :force_overwrite
|
13
|
+
|
14
|
+
def initialize(rule, force_overwrite:)
|
15
|
+
@rule = rule
|
16
|
+
@force_overwrite = force_overwrite
|
17
|
+
end
|
18
|
+
|
19
|
+
def force_overwrite?
|
20
|
+
force_overwrite
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# @return [Boolean]
|
25
|
+
#
|
26
|
+
def diff?
|
27
|
+
model = Mihari::Rule.find(rule.id)
|
28
|
+
model.data != rule.data.deep_stringify_keys
|
29
|
+
rescue ActiveRecord::RecordNotFound
|
30
|
+
false
|
31
|
+
end
|
32
|
+
|
33
|
+
def update_or_create
|
34
|
+
rule.model.save
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# @return [Mihari::Alert, nil]
|
39
|
+
#
|
40
|
+
def run
|
41
|
+
analyzer = rule.analyzer
|
42
|
+
|
43
|
+
with_error_notification do
|
44
|
+
analyzer.run
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -18,7 +18,7 @@ module Mihari
|
|
18
18
|
#
|
19
19
|
# @return [Mihari::AutonomousSystem]
|
20
20
|
#
|
21
|
-
def
|
21
|
+
def as
|
22
22
|
Mihari::AutonomousSystem.new(asn: normalize_asn(asn))
|
23
23
|
end
|
24
24
|
|
@@ -58,7 +58,7 @@ module Mihari
|
|
58
58
|
#
|
59
59
|
# @return [Mihari::Geolocation] <description>
|
60
60
|
#
|
61
|
-
def
|
61
|
+
def geolocation
|
62
62
|
# sometimes Censys overlooks country
|
63
63
|
# then set geolocation as nil
|
64
64
|
return nil if country.nil?
|
@@ -98,7 +98,7 @@ module Mihari
|
|
98
98
|
#
|
99
99
|
# @return [Mihari::Port]
|
100
100
|
#
|
101
|
-
def
|
101
|
+
def _port
|
102
102
|
Port.new(port: port)
|
103
103
|
end
|
104
104
|
|
@@ -162,20 +162,20 @@ module Mihari
|
|
162
162
|
#
|
163
163
|
# @return [Array<Mihari::Port>]
|
164
164
|
#
|
165
|
-
def
|
166
|
-
services.map(&:
|
165
|
+
def ports
|
166
|
+
services.map(&:_port)
|
167
167
|
end
|
168
168
|
|
169
169
|
#
|
170
170
|
# @return [Mihari::Artifact]
|
171
171
|
#
|
172
|
-
def
|
172
|
+
def artifact
|
173
173
|
Artifact.new(
|
174
174
|
data: ip,
|
175
175
|
metadata: metadata,
|
176
|
-
autonomous_system: autonomous_system.
|
177
|
-
geolocation: location.
|
178
|
-
ports:
|
176
|
+
autonomous_system: autonomous_system.as,
|
177
|
+
geolocation: location.geolocation,
|
178
|
+
ports: ports
|
179
179
|
)
|
180
180
|
end
|
181
181
|
|
@@ -269,8 +269,8 @@ module Mihari
|
|
269
269
|
#
|
270
270
|
# @return [Array<Mihari::Artifact>]
|
271
271
|
#
|
272
|
-
def
|
273
|
-
hits.map(&:
|
272
|
+
def artifacts
|
273
|
+
hits.map(&:artifact)
|
274
274
|
end
|
275
275
|
|
276
276
|
class << self
|
@@ -34,14 +34,14 @@ module Mihari
|
|
34
34
|
#
|
35
35
|
# @return [Mihari::AutonomousSystem]
|
36
36
|
#
|
37
|
-
def
|
37
|
+
def as
|
38
38
|
Mihari::AutonomousSystem.new(asn: normalize_asn(asn))
|
39
39
|
end
|
40
40
|
|
41
41
|
#
|
42
42
|
# @return [Mihari::Geolocation]
|
43
43
|
#
|
44
|
-
def
|
44
|
+
def geolocation
|
45
45
|
Mihari::Geolocation.new(
|
46
46
|
country: country,
|
47
47
|
country_code: country_code
|
@@ -94,12 +94,12 @@ module Mihari
|
|
94
94
|
#
|
95
95
|
# @return [Mihari::Artifact]
|
96
96
|
#
|
97
|
-
def
|
97
|
+
def artifact
|
98
98
|
Mihari::Artifact.new(
|
99
99
|
data: ip,
|
100
100
|
metadata: metadata_,
|
101
|
-
autonomous_system: metadata.
|
102
|
-
geolocation: metadata.
|
101
|
+
autonomous_system: metadata.as,
|
102
|
+
geolocation: metadata.geolocation
|
103
103
|
)
|
104
104
|
end
|
105
105
|
|
@@ -126,6 +126,7 @@ module Mihari
|
|
126
126
|
attribute :data, Types.Array(Datum)
|
127
127
|
attribute :message, Types::String
|
128
128
|
attribute :query, Types::String
|
129
|
+
attribute :scroll, Types::String.optional
|
129
130
|
|
130
131
|
#
|
131
132
|
# @return [Boolean]
|
@@ -162,11 +163,18 @@ module Mihari
|
|
162
163
|
attributes[:query]
|
163
164
|
end
|
164
165
|
|
166
|
+
#
|
167
|
+
# @return [String, nil]
|
168
|
+
#
|
169
|
+
def scroll
|
170
|
+
attributes[:scroll]
|
171
|
+
end
|
172
|
+
|
165
173
|
#
|
166
174
|
# @return [Array<Mihari::Artifact>]
|
167
175
|
#
|
168
|
-
def
|
169
|
-
data.map
|
176
|
+
def artifacts
|
177
|
+
data.map(&:artifact)
|
170
178
|
end
|
171
179
|
|
172
180
|
class << self
|
@@ -182,7 +190,8 @@ module Mihari
|
|
182
190
|
count: d.fetch("count"),
|
183
191
|
data: d.fetch("data").map { |x| Datum.from_dynamic!(x) },
|
184
192
|
message: d.fetch("message"),
|
185
|
-
query: d.fetch("query")
|
193
|
+
query: d.fetch("query"),
|
194
|
+
scroll: d["scroll"]
|
186
195
|
)
|
187
196
|
end
|
188
197
|
end
|
@@ -42,19 +42,19 @@ module Mihari
|
|
42
42
|
#
|
43
43
|
# @return [Mihari::Artifact]
|
44
44
|
#
|
45
|
-
def
|
45
|
+
def artifact
|
46
46
|
Mihari::Artifact.new(
|
47
47
|
data: ip,
|
48
48
|
metadata: metadata,
|
49
|
-
autonomous_system:
|
50
|
-
geolocation:
|
49
|
+
autonomous_system: as,
|
50
|
+
geolocation: geolocation
|
51
51
|
)
|
52
52
|
end
|
53
53
|
|
54
54
|
#
|
55
55
|
# @return [Mihari::Geolocation, nil]
|
56
56
|
#
|
57
|
-
def
|
57
|
+
def geolocation
|
58
58
|
return nil if country_code.nil?
|
59
59
|
|
60
60
|
Mihari::Geolocation.new(
|
@@ -66,7 +66,7 @@ module Mihari
|
|
66
66
|
#
|
67
67
|
# @return [Mihari::AutonomousSystem]
|
68
68
|
#
|
69
|
-
def
|
69
|
+
def as
|
70
70
|
Mihari::AutonomousSystem.new(asn: normalize_asn(asn))
|
71
71
|
end
|
72
72
|
|
@@ -150,8 +150,8 @@ module Mihari
|
|
150
150
|
#
|
151
151
|
# @return [Array<Mihari::Artifact>]
|
152
152
|
#
|
153
|
-
def
|
154
|
-
results.map(&:
|
153
|
+
def artifacts
|
154
|
+
results.map(&:artifact)
|
155
155
|
end
|
156
156
|
|
157
157
|
class << self
|
@@ -24,7 +24,7 @@ module Mihari
|
|
24
24
|
#
|
25
25
|
# @return [Mihari::Geolocation, nil]
|
26
26
|
#
|
27
|
-
def
|
27
|
+
def geolocation
|
28
28
|
return nil if country_name.nil? && country_code.nil?
|
29
29
|
|
30
30
|
Mihari::Geolocation.new(
|
@@ -105,7 +105,7 @@ module Mihari
|
|
105
105
|
#
|
106
106
|
# @return [Mihari::AutonomousSystem, nil]
|
107
107
|
#
|
108
|
-
def
|
108
|
+
def _asn
|
109
109
|
return nil if asn.nil?
|
110
110
|
|
111
111
|
Mihari::AutonomousSystem.new(asn: normalize_asn(asn))
|
@@ -194,7 +194,7 @@ module Mihari
|
|
194
194
|
#
|
195
195
|
# @return [Array<Mihari::Artifact>]
|
196
196
|
#
|
197
|
-
def
|
197
|
+
def artifacts
|
198
198
|
matches.map do |match|
|
199
199
|
metadata = collect_metadata_by_ip(match.ip_str)
|
200
200
|
ports = collect_ports_by_ip(match.ip_str).map do |port|
|
@@ -207,8 +207,8 @@ module Mihari
|
|
207
207
|
Mihari::Artifact.new(
|
208
208
|
data: match.ip_str,
|
209
209
|
metadata: metadata,
|
210
|
-
autonomous_system: match.
|
211
|
-
geolocation: match.location.
|
210
|
+
autonomous_system: match._asn,
|
211
|
+
geolocation: match.location.geolocation,
|
212
212
|
ports: ports,
|
213
213
|
reverse_dns_names: reverse_dns_names
|
214
214
|
)
|
@@ -83,7 +83,7 @@ module Mihari
|
|
83
83
|
#
|
84
84
|
# @return [Array<Mihari::Artifact>]
|
85
85
|
#
|
86
|
-
def
|
86
|
+
def artifacts
|
87
87
|
values = [page.url, page.domain, page.ip].compact
|
88
88
|
values.map do |value|
|
89
89
|
Mihari::Artifact.new(data: value, metadata: metadata)
|
@@ -129,8 +129,8 @@ module Mihari
|
|
129
129
|
#
|
130
130
|
# @return [Array<Mihari::Artifact>]
|
131
131
|
#
|
132
|
-
def
|
133
|
-
results.map(&:
|
132
|
+
def artifacts
|
133
|
+
results.map(&:artifacts).flatten
|
134
134
|
end
|
135
135
|
|
136
136
|
class << self
|
@@ -81,7 +81,7 @@ module Mihari
|
|
81
81
|
#
|
82
82
|
# @return [Mihari::Artifact]
|
83
83
|
#
|
84
|
-
def
|
84
|
+
def artifact
|
85
85
|
Artifact.new(data: value, metadata: metadata)
|
86
86
|
end
|
87
87
|
|
@@ -155,8 +155,8 @@ module Mihari
|
|
155
155
|
#
|
156
156
|
# @return [Array<Mihari::Artifact>]
|
157
157
|
#
|
158
|
-
def
|
159
|
-
data.map(&:
|
158
|
+
def artifacts
|
159
|
+
data.map(&:artifact)
|
160
160
|
end
|
161
161
|
|
162
162
|
class << self
|
data/lib/mihari/version.rb
CHANGED
@@ -67,6 +67,28 @@ module Mihari
|
|
67
67
|
status 204
|
68
68
|
present({ message: "" }, with: Entities::Message)
|
69
69
|
end
|
70
|
+
|
71
|
+
desc "Create an alert", {
|
72
|
+
success: Entities::Alert,
|
73
|
+
summary: "Create an alert"
|
74
|
+
}
|
75
|
+
params do
|
76
|
+
requires :ruleId, type: String, documentation: { param_type: "body" }
|
77
|
+
requires :artifacts, type: Array, documentation: { type: String, is_array: true, param_type: "body" }
|
78
|
+
end
|
79
|
+
post "/" do
|
80
|
+
proxy = Services::AlertProxy.new(params.to_snake_keys)
|
81
|
+
runner = Services::AlertRunner.new(proxy)
|
82
|
+
|
83
|
+
begin
|
84
|
+
alert = runner.run
|
85
|
+
rescue ActiveRecord::RecordNotFound
|
86
|
+
error!({ message: "Rule:#{params["ruleId"]} is not found" }, 404)
|
87
|
+
end
|
88
|
+
|
89
|
+
status 201
|
90
|
+
present alert, with: Entities::Alert
|
91
|
+
end
|
70
92
|
end
|
71
93
|
end
|
72
94
|
end
|
@@ -83,12 +83,12 @@ module Mihari
|
|
83
83
|
id = params["id"].to_s
|
84
84
|
|
85
85
|
begin
|
86
|
-
rule = Mihari::Services::
|
86
|
+
rule = Mihari::Services::RuleProxy.from_model(Mihari::Rule.find(id))
|
87
87
|
rescue ActiveRecord::RecordNotFound
|
88
88
|
error!({ message: "ID:#{id} is not found" }, 404)
|
89
89
|
end
|
90
90
|
|
91
|
-
analyzer = rule.
|
91
|
+
analyzer = rule.analyzer
|
92
92
|
analyzer.run
|
93
93
|
|
94
94
|
status 201
|
@@ -106,7 +106,7 @@ module Mihari
|
|
106
106
|
yaml = params[:yaml]
|
107
107
|
|
108
108
|
begin
|
109
|
-
rule = Services::
|
109
|
+
rule = Services::RuleProxy.from_yaml(yaml)
|
110
110
|
rescue YAMLSyntaxError => e
|
111
111
|
error!({ message: e.message }, 400)
|
112
112
|
end
|
@@ -129,13 +129,13 @@ module Mihari
|
|
129
129
|
end
|
130
130
|
|
131
131
|
begin
|
132
|
-
rule.
|
132
|
+
rule.model.save
|
133
133
|
rescue ActiveRecord::RecordNotUnique
|
134
134
|
error!({ message: "ID:#{rule.id} is already registered" }, 400)
|
135
135
|
end
|
136
136
|
|
137
137
|
status 201
|
138
|
-
present rule.
|
138
|
+
present rule.model, with: Entities::Rule
|
139
139
|
end
|
140
140
|
|
141
141
|
desc "Update a rule", {
|
@@ -157,7 +157,7 @@ module Mihari
|
|
157
157
|
end
|
158
158
|
|
159
159
|
begin
|
160
|
-
rule = Services::
|
160
|
+
rule = Services::RuleProxy.from_yaml(yaml)
|
161
161
|
rescue YAMLSyntaxError => e
|
162
162
|
error!({ message: e.message }, 400)
|
163
163
|
end
|
@@ -172,13 +172,13 @@ module Mihari
|
|
172
172
|
end
|
173
173
|
|
174
174
|
begin
|
175
|
-
rule.
|
175
|
+
rule.model.save
|
176
176
|
rescue ActiveRecord::RecordNotUnique
|
177
177
|
error!({ message: "ID:#{id} is already registered" }, 400)
|
178
178
|
end
|
179
179
|
|
180
180
|
status 201
|
181
|
-
present rule.
|
181
|
+
present rule.model, with: Entities::Rule
|
182
182
|
end
|
183
183
|
|
184
184
|
desc "Delete a rule", {
|
@@ -938,7 +938,7 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
|
|
938
938
|
.ace-tm .ace_indent-guide-active {
|
939
939
|
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAAZSURBVHjaYvj///9/hivKyv8BAAAA//8DACLqBhbvk+/eAAAAAElFTkSuQmCC") right repeat-y;
|
940
940
|
}
|
941
|
-
`}),ace.define("ace/theme/textmate",["require","exports","module","ace/theme/textmate-css","ace/lib/dom"],function(n,r,A){r.isDark=!1,r.cssClass="ace-tm",r.cssText=n("./textmate-css"),r.$id="ace/theme/textmate";var S=n("../lib/dom");S.importCssString(r.cssText,r.cssClass,!1)}),ace.define("ace/config",["require","exports","module","ace/lib/lang","ace/lib/net","ace/lib/dom","ace/lib/app_config","ace/theme/textmate"],function(n,r,A){"no use strict";var S=n("./lib/lang"),E=n("./lib/net"),T=n("./lib/dom"),c=n("./lib/app_config").AppConfig;A.exports=r=new c;var C={packaged:!1,workerPath:null,modePath:null,themePath:null,basePath:"",suffix:".js",$moduleUrls:{},loadWorkerFromBlob:!0,sharedPopups:!1,useStrictCSP:null};r.get=function(l){if(!C.hasOwnProperty(l))throw new Error("Unknown config key: "+l);return C[l]},r.set=function(l,f){if(C.hasOwnProperty(l))C[l]=f;else if(this.setDefaultValue("",l,f)==!1)throw new Error("Unknown config key: "+l);l=="useStrictCSP"&&T.useStrictCSP(f)},r.all=function(){return S.copyObject(C)},r.$modes={},r.moduleUrl=function(l,f){if(C.$moduleUrls[l])return C.$moduleUrls[l];var R=l.split("/");f=f||R[R.length-2]||"";var x=f=="snippets"?"/":"-",L=R[R.length-1];if(f=="worker"&&x=="-"){var M=new RegExp("^"+f+"[\\-_]|[\\-_]"+f+"$","g");L=L.replace(M,"")}(!L||L==f)&&R.length>1&&(L=R[R.length-2]);var V=C[f+"Path"];return V==null?V=C.basePath:x=="/"&&(f=x=""),V&&V.slice(-1)!="/"&&(V+="/"),V+f+x+L+this.get("suffix")},r.setModuleUrl=function(l,f){return C.$moduleUrls[l]=f};var o=function(l,f){if(l==="ace/theme/textmate"||l==="./theme/textmate")return f(null,n("./theme/textmate"));if(a)return a(l,f);console.error("loader is not configured")},a;r.setLoader=function(l){a=l},r.dynamicModules=Object.create(null),r.$loading={},r.$loaded={},r.loadModule=function(l,f){var R,x;Array.isArray(l)&&(x=l[0],l=l[1]);var L=function(M){if(M&&!r.$loading[l])return f&&f(M);if(r.$loading[l]||(r.$loading[l]=[]),r.$loading[l].push(f),!(r.$loading[l].length>1)){var V=function(){o(l,function(D,F){F&&(r.$loaded[l]=F),r._emit("load.module",{name:l,module:F});var P=r.$loading[l];r.$loading[l]=null,P.forEach(function(X){X&&X(F)})})};if(!r.get("packaged"))return V();E.loadScript(r.moduleUrl(l,x),V),$()}};if(r.dynamicModules[l])r.dynamicModules[l]().then(function(M){M.default?L(M.default):L(M)});else{try{R=this.$require(l)}catch{}L(R||r.$loaded[l])}},r.$require=function(l){if(typeof A.require=="function"){var f="require";return A[f](l)}},r.setModuleLoader=function(l,f){r.dynamicModules[l]=f};var $=function(){!C.basePath&&!C.workerPath&&!C.modePath&&!C.themePath&&!Object.keys(C.$moduleUrls).length&&(console.error("Unable to infer path to ace from script src,","use ace.config.set('basePath', 'path') to enable dynamic loading of modes and themes","or with webpack use ace/webpack-resolver"),$=function(){})};r.version="1.24.
|
941
|
+
`}),ace.define("ace/theme/textmate",["require","exports","module","ace/theme/textmate-css","ace/lib/dom"],function(n,r,A){r.isDark=!1,r.cssClass="ace-tm",r.cssText=n("./textmate-css"),r.$id="ace/theme/textmate";var S=n("../lib/dom");S.importCssString(r.cssText,r.cssClass,!1)}),ace.define("ace/config",["require","exports","module","ace/lib/lang","ace/lib/net","ace/lib/dom","ace/lib/app_config","ace/theme/textmate"],function(n,r,A){"no use strict";var S=n("./lib/lang"),E=n("./lib/net"),T=n("./lib/dom"),c=n("./lib/app_config").AppConfig;A.exports=r=new c;var C={packaged:!1,workerPath:null,modePath:null,themePath:null,basePath:"",suffix:".js",$moduleUrls:{},loadWorkerFromBlob:!0,sharedPopups:!1,useStrictCSP:null};r.get=function(l){if(!C.hasOwnProperty(l))throw new Error("Unknown config key: "+l);return C[l]},r.set=function(l,f){if(C.hasOwnProperty(l))C[l]=f;else if(this.setDefaultValue("",l,f)==!1)throw new Error("Unknown config key: "+l);l=="useStrictCSP"&&T.useStrictCSP(f)},r.all=function(){return S.copyObject(C)},r.$modes={},r.moduleUrl=function(l,f){if(C.$moduleUrls[l])return C.$moduleUrls[l];var R=l.split("/");f=f||R[R.length-2]||"";var x=f=="snippets"?"/":"-",L=R[R.length-1];if(f=="worker"&&x=="-"){var M=new RegExp("^"+f+"[\\-_]|[\\-_]"+f+"$","g");L=L.replace(M,"")}(!L||L==f)&&R.length>1&&(L=R[R.length-2]);var V=C[f+"Path"];return V==null?V=C.basePath:x=="/"&&(f=x=""),V&&V.slice(-1)!="/"&&(V+="/"),V+f+x+L+this.get("suffix")},r.setModuleUrl=function(l,f){return C.$moduleUrls[l]=f};var o=function(l,f){if(l==="ace/theme/textmate"||l==="./theme/textmate")return f(null,n("./theme/textmate"));if(a)return a(l,f);console.error("loader is not configured")},a;r.setLoader=function(l){a=l},r.dynamicModules=Object.create(null),r.$loading={},r.$loaded={},r.loadModule=function(l,f){var R,x;Array.isArray(l)&&(x=l[0],l=l[1]);var L=function(M){if(M&&!r.$loading[l])return f&&f(M);if(r.$loading[l]||(r.$loading[l]=[]),r.$loading[l].push(f),!(r.$loading[l].length>1)){var V=function(){o(l,function(D,F){F&&(r.$loaded[l]=F),r._emit("load.module",{name:l,module:F});var P=r.$loading[l];r.$loading[l]=null,P.forEach(function(X){X&&X(F)})})};if(!r.get("packaged"))return V();E.loadScript(r.moduleUrl(l,x),V),$()}};if(r.dynamicModules[l])r.dynamicModules[l]().then(function(M){M.default?L(M.default):L(M)});else{try{R=this.$require(l)}catch{}L(R||r.$loaded[l])}},r.$require=function(l){if(typeof A.require=="function"){var f="require";return A[f](l)}},r.setModuleLoader=function(l,f){r.dynamicModules[l]=f};var $=function(){!C.basePath&&!C.workerPath&&!C.modePath&&!C.themePath&&!Object.keys(C.$moduleUrls).length&&(console.error("Unable to infer path to ace from script src,","use ace.config.set('basePath', 'path') to enable dynamic loading of modes and themes","or with webpack use ace/webpack-resolver"),$=function(){})};r.version="1.24.1"}),ace.define("ace/loader_build",["require","exports","module","ace/lib/fixoldbrowsers","ace/config"],function(n,r,A){n("./lib/fixoldbrowsers");var S=n("./config");S.setLoader(function(C,o){n([C],function(a){o(null,a)})});var E=function(){return this||typeof window<"u"&&window}();A.exports=function(C){S.init=T,S.$require=n,C.require=n},T(!0);function T(C){if(!(!E||!E.document)){S.set("packaged",C||n.packaged||A.packaged||E.define&&(void 0).packaged);var o={},a="",$=document.currentScript||document._currentScript,l=$&&$.ownerDocument||document;$&&$.src&&(a=$.src.split(/[?#]/)[0].split("/").slice(0,-1).join("/")||"");for(var f=l.getElementsByTagName("script"),R=0;R<f.length;R++){var x=f[R],L=x.src||x.getAttribute("src");if(L){for(var M=x.attributes,V=0,D=M.length;V<D;V++){var F=M[V];F.name.indexOf("data-ace-")===0&&(o[c(F.name.replace(/^data-ace-/,""))]=F.value)}var P=L.match(/^(.*)\/ace([\-.]\w+)?\.js(\?|$)/);P&&(a=P[1])}}a&&(o.base=o.base||a,o.packaged=!0),o.basePath=o.base,o.workerPath=o.workerPath||o.base,o.modePath=o.modePath||o.base,o.themePath=o.themePath||o.base,delete o.base;for(var X in o)typeof o[X]<"u"&&S.set(X,o[X])}}function c(C){return C.replace(/-(.)/g,function(o,a){return a.toUpperCase()})}}),ace.define("ace/range",["require","exports","module"],function(n,r,A){var S=function(T,c){return T.row-c.row||T.column-c.column},E=function(){function T(c,C,o,a){this.start={row:c,column:C},this.end={row:o,column:a}}return T.prototype.isEqual=function(c){return this.start.row===c.start.row&&this.end.row===c.end.row&&this.start.column===c.start.column&&this.end.column===c.end.column},T.prototype.toString=function(){return"Range: ["+this.start.row+"/"+this.start.column+"] -> ["+this.end.row+"/"+this.end.column+"]"},T.prototype.contains=function(c,C){return this.compare(c,C)==0},T.prototype.compareRange=function(c){var C,o=c.end,a=c.start;return C=this.compare(o.row,o.column),C==1?(C=this.compare(a.row,a.column),C==1?2:C==0?1:0):C==-1?-2:(C=this.compare(a.row,a.column),C==-1?-1:C==1?42:0)},T.prototype.comparePoint=function(c){return this.compare(c.row,c.column)},T.prototype.containsRange=function(c){return this.comparePoint(c.start)==0&&this.comparePoint(c.end)==0},T.prototype.intersects=function(c){var C=this.compareRange(c);return C==-1||C==0||C==1},T.prototype.isEnd=function(c,C){return this.end.row==c&&this.end.column==C},T.prototype.isStart=function(c,C){return this.start.row==c&&this.start.column==C},T.prototype.setStart=function(c,C){typeof c=="object"?(this.start.column=c.column,this.start.row=c.row):(this.start.row=c,this.start.column=C)},T.prototype.setEnd=function(c,C){typeof c=="object"?(this.end.column=c.column,this.end.row=c.row):(this.end.row=c,this.end.column=C)},T.prototype.inside=function(c,C){return this.compare(c,C)==0?!(this.isEnd(c,C)||this.isStart(c,C)):!1},T.prototype.insideStart=function(c,C){return this.compare(c,C)==0?!this.isEnd(c,C):!1},T.prototype.insideEnd=function(c,C){return this.compare(c,C)==0?!this.isStart(c,C):!1},T.prototype.compare=function(c,C){return!this.isMultiLine()&&c===this.start.row?C<this.start.column?-1:C>this.end.column?1:0:c<this.start.row?-1:c>this.end.row?1:this.start.row===c?C>=this.start.column?0:-1:this.end.row===c?C<=this.end.column?0:1:0},T.prototype.compareStart=function(c,C){return this.start.row==c&&this.start.column==C?-1:this.compare(c,C)},T.prototype.compareEnd=function(c,C){return this.end.row==c&&this.end.column==C?1:this.compare(c,C)},T.prototype.compareInside=function(c,C){return this.end.row==c&&this.end.column==C?1:this.start.row==c&&this.start.column==C?-1:this.compare(c,C)},T.prototype.clipRows=function(c,C){if(this.end.row>C)var o={row:C+1,column:0};else if(this.end.row<c)var o={row:c,column:0};if(this.start.row>C)var a={row:C+1,column:0};else if(this.start.row<c)var a={row:c,column:0};return T.fromPoints(a||this.start,o||this.end)},T.prototype.extend=function(c,C){var o=this.compare(c,C);if(o==0)return this;if(o==-1)var a={row:c,column:C};else var $={row:c,column:C};return T.fromPoints(a||this.start,$||this.end)},T.prototype.isEmpty=function(){return this.start.row===this.end.row&&this.start.column===this.end.column},T.prototype.isMultiLine=function(){return this.start.row!==this.end.row},T.prototype.clone=function(){return T.fromPoints(this.start,this.end)},T.prototype.collapseRows=function(){return this.end.column==0?new T(this.start.row,0,Math.max(this.start.row,this.end.row-1),0):new T(this.start.row,0,this.end.row,0)},T.prototype.toScreenRange=function(c){var C=c.documentToScreenPosition(this.start),o=c.documentToScreenPosition(this.end);return new T(C.row,C.column,o.row,o.column)},T.prototype.moveBy=function(c,C){this.start.row+=c,this.start.column+=C,this.end.row+=c,this.end.column+=C},T}();E.fromPoints=function(T,c){return new E(T.row,T.column,c.row,c.column)},E.comparePoints=S,E.comparePoints=function(T,c){return T.row-c.row||T.column-c.column},r.Range=E}),ace.define("ace/lib/keys",["require","exports","module","ace/lib/oop"],function(n,r,A){/*! @license
|
942
942
|
==========================================================================
|
943
943
|
SproutCore -- JavaScript Application Framework
|
944
944
|
copyright 2006-2009, Sprout Systems Inc., Apple Inc. and contributors.
|
@@ -6,7 +6,7 @@
|
|
6
6
|
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
7
7
|
<link rel="icon" href="/favicon.ico" />
|
8
8
|
<title>Mihari</title>
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
9
|
+
<script type="module" crossorigin src="/assets/index-4d7eda9f.js"></script>
|
10
10
|
<link rel="stylesheet" href="/assets/index-33165282.css">
|
11
11
|
</head>
|
12
12
|
<body>
|