mihari 5.6.2 → 5.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (267) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +5 -1
  3. data/README.md +1 -0
  4. data/config.ru +1 -1
  5. data/lib/mihari/actor.rb +18 -2
  6. data/lib/mihari/analyzers/base.rb +13 -12
  7. data/lib/mihari/analyzers/binaryedge.rb +4 -1
  8. data/lib/mihari/analyzers/censys.rb +4 -2
  9. data/lib/mihari/analyzers/circl.rb +4 -1
  10. data/lib/mihari/analyzers/crtsh.rb +4 -1
  11. data/lib/mihari/analyzers/dnstwister.rb +4 -1
  12. data/lib/mihari/analyzers/feed.rb +3 -0
  13. data/lib/mihari/analyzers/fofa.rb +65 -0
  14. data/lib/mihari/analyzers/greynoise.rb +4 -1
  15. data/lib/mihari/analyzers/hunterhow.rb +6 -1
  16. data/lib/mihari/analyzers/onyphe.rb +4 -1
  17. data/lib/mihari/analyzers/otx.rb +4 -1
  18. data/lib/mihari/analyzers/passivetotal.rb +4 -1
  19. data/lib/mihari/analyzers/pulsedive.rb +3 -0
  20. data/lib/mihari/analyzers/securitytrails.rb +4 -1
  21. data/lib/mihari/analyzers/shodan.rb +4 -1
  22. data/lib/mihari/analyzers/urlscan.rb +4 -1
  23. data/lib/mihari/analyzers/virustotal.rb +4 -1
  24. data/lib/mihari/analyzers/virustotal_intelligence.rb +4 -1
  25. data/lib/mihari/analyzers/zoomeye.rb +5 -2
  26. data/lib/mihari/cli/alert.rb +3 -0
  27. data/lib/mihari/cli/base.rb +3 -0
  28. data/lib/mihari/cli/database.rb +3 -0
  29. data/lib/mihari/cli/main.rb +3 -0
  30. data/lib/mihari/cli/rule.rb +3 -0
  31. data/lib/mihari/clients/base.rb +3 -0
  32. data/lib/mihari/clients/binaryedge.rb +5 -2
  33. data/lib/mihari/clients/censys.rb +7 -4
  34. data/lib/mihari/clients/circl.rb +3 -0
  35. data/lib/mihari/clients/crtsh.rb +3 -0
  36. data/lib/mihari/clients/dnstwister.rb +3 -0
  37. data/lib/mihari/clients/fofa.rb +83 -0
  38. data/lib/mihari/clients/greynoise.rb +5 -2
  39. data/lib/mihari/clients/hunterhow.rb +5 -2
  40. data/lib/mihari/clients/misp.rb +3 -0
  41. data/lib/mihari/clients/onyphe.rb +5 -2
  42. data/lib/mihari/clients/otx.rb +3 -0
  43. data/lib/mihari/clients/passivetotal.rb +3 -0
  44. data/lib/mihari/clients/publsedive.rb +4 -1
  45. data/lib/mihari/clients/securitytrails.rb +3 -0
  46. data/lib/mihari/clients/shodan.rb +5 -2
  47. data/lib/mihari/clients/the_hive.rb +3 -0
  48. data/lib/mihari/clients/urlscan.rb +7 -4
  49. data/lib/mihari/clients/virustotal.rb +5 -2
  50. data/lib/mihari/clients/zoomeye.rb +3 -0
  51. data/lib/mihari/commands/alert.rb +9 -16
  52. data/lib/mihari/commands/database.rb +3 -0
  53. data/lib/mihari/commands/rule.rb +10 -1
  54. data/lib/mihari/commands/search.rb +13 -29
  55. data/lib/mihari/commands/version.rb +3 -0
  56. data/lib/mihari/commands/web.rb +4 -1
  57. data/lib/mihari/config.rb +139 -150
  58. data/lib/mihari/constants.rb +1 -1
  59. data/lib/mihari/database.rb +6 -0
  60. data/lib/mihari/emitters/base.rb +13 -11
  61. data/lib/mihari/emitters/database.rb +4 -1
  62. data/lib/mihari/emitters/misp.rb +7 -4
  63. data/lib/mihari/emitters/slack.rb +3 -3
  64. data/lib/mihari/emitters/the_hive.rb +3 -3
  65. data/lib/mihari/emitters/webhook.rb +4 -3
  66. data/lib/mihari/enrichers/base.rb +15 -9
  67. data/lib/mihari/enrichers/google_public_dns.rb +6 -5
  68. data/lib/mihari/enrichers/ipinfo.rb +11 -9
  69. data/lib/mihari/enrichers/shodan.rb +4 -6
  70. data/lib/mihari/enrichers/whois.rb +12 -9
  71. data/lib/mihari/entities/tag.rb +1 -0
  72. data/lib/mihari/errors.rb +6 -0
  73. data/lib/mihari/feed/parser.rb +3 -0
  74. data/lib/mihari/feed/reader.rb +3 -0
  75. data/lib/mihari/http.rb +6 -0
  76. data/lib/mihari/mixins/autonomous_system.rb +3 -0
  77. data/lib/mihari/mixins/configurable.rb +3 -0
  78. data/lib/mihari/mixins/error_notification.rb +3 -0
  79. data/lib/mihari/mixins/falsepositive.rb +3 -0
  80. data/lib/mihari/mixins/refang.rb +3 -0
  81. data/lib/mihari/mixins/retriable.rb +6 -2
  82. data/lib/mihari/models/alert.rb +7 -4
  83. data/lib/mihari/models/artifact.rb +6 -0
  84. data/lib/mihari/models/autonomous_system.rb +4 -1
  85. data/lib/mihari/models/cpe.rb +4 -1
  86. data/lib/mihari/models/dns.rb +4 -1
  87. data/lib/mihari/models/geolocation.rb +4 -1
  88. data/lib/mihari/models/port.rb +4 -1
  89. data/lib/mihari/models/reverse_dns.rb +4 -1
  90. data/lib/mihari/models/rule.rb +6 -3
  91. data/lib/mihari/models/tag.rb +3 -0
  92. data/lib/mihari/models/tagging.rb +3 -0
  93. data/lib/mihari/models/whois.rb +4 -3
  94. data/lib/mihari/rule.rb +31 -12
  95. data/lib/mihari/schemas/alert.rb +3 -0
  96. data/lib/mihari/schemas/analyzer.rb +11 -0
  97. data/lib/mihari/schemas/emitter.rb +3 -0
  98. data/lib/mihari/schemas/enricher.rb +3 -0
  99. data/lib/mihari/schemas/macros.rb +4 -0
  100. data/lib/mihari/schemas/mixins.rb +5 -0
  101. data/lib/mihari/schemas/rule.rb +3 -0
  102. data/lib/mihari/service.rb +26 -0
  103. data/lib/mihari/services/alert_builder.rb +85 -9
  104. data/lib/mihari/services/alert_runner.rb +8 -19
  105. data/lib/mihari/services/rule_builder.rb +13 -12
  106. data/lib/mihari/services/rule_runner.rb +7 -32
  107. data/lib/mihari/structs/binaryedge.rb +22 -28
  108. data/lib/mihari/structs/censys.rb +48 -141
  109. data/lib/mihari/structs/config.rb +19 -30
  110. data/lib/mihari/structs/filters.rb +38 -0
  111. data/lib/mihari/structs/fofa.rb +47 -0
  112. data/lib/mihari/structs/google_public_dns.rb +10 -32
  113. data/lib/mihari/structs/greynoise.rb +33 -90
  114. data/lib/mihari/structs/hunterhow.rb +24 -28
  115. data/lib/mihari/structs/ipinfo.rb +14 -37
  116. data/lib/mihari/structs/onyphe.rb +31 -80
  117. data/lib/mihari/structs/shodan.rb +47 -114
  118. data/lib/mihari/structs/urlscan.rb +24 -69
  119. data/lib/mihari/structs/virustotal_intelligence.rb +20 -64
  120. data/lib/mihari/type_checker.rb +4 -0
  121. data/lib/mihari/types.rb +3 -0
  122. data/lib/mihari/version.rb +1 -1
  123. data/lib/mihari/web/api.rb +15 -10
  124. data/lib/mihari/web/app.rb +64 -56
  125. data/lib/mihari/web/endpoints/alerts.rb +127 -85
  126. data/lib/mihari/web/endpoints/artifacts.rb +91 -79
  127. data/lib/mihari/web/endpoints/configs.rb +18 -13
  128. data/lib/mihari/web/endpoints/ip_addresses.rb +35 -15
  129. data/lib/mihari/web/endpoints/rules.rb +236 -187
  130. data/lib/mihari/web/endpoints/tags.rb +42 -35
  131. data/lib/mihari/web/middleware/connection_adapter.rb +16 -9
  132. data/lib/mihari/web/middleware/error_notification_adapter.rb +17 -10
  133. data/lib/mihari/web/public/assets/{index-28d4c79d.js → index-07fafab5.js} +31 -30
  134. data/lib/mihari/web/public/assets/mode-yaml-24faa242.js +8 -0
  135. data/lib/mihari/web/public/index.html +1 -1
  136. data/lib/mihari.rb +24 -6
  137. data/mihari.gemspec +9 -2
  138. data/mkdocs.yml +4 -2
  139. metadata +38 -133
  140. data/docs/alternatives.md +0 -5
  141. data/docs/analyzers/binaryedge.md +0 -26
  142. data/docs/analyzers/censys.md +0 -31
  143. data/docs/analyzers/circl.md +0 -37
  144. data/docs/analyzers/crtsh.md +0 -26
  145. data/docs/analyzers/dnstwister.md +0 -25
  146. data/docs/analyzers/feed.md +0 -73
  147. data/docs/analyzers/greynoise.md +0 -26
  148. data/docs/analyzers/hunterhow.md +0 -33
  149. data/docs/analyzers/index.md +0 -103
  150. data/docs/analyzers/onyphe.md +0 -26
  151. data/docs/analyzers/otx.md +0 -28
  152. data/docs/analyzers/passivetotal.md +0 -52
  153. data/docs/analyzers/pulsedive.md +0 -28
  154. data/docs/analyzers/securitytrails.md +0 -41
  155. data/docs/analyzers/shodan.md +0 -26
  156. data/docs/analyzers/urlscan.md +0 -28
  157. data/docs/analyzers/virustotal.md +0 -43
  158. data/docs/analyzers/virustotal_intelligence.md +0 -33
  159. data/docs/analyzers/zoomeye.md +0 -38
  160. data/docs/configuration.md +0 -35
  161. data/docs/emitters/database.md +0 -22
  162. data/docs/emitters/hive.md +0 -26
  163. data/docs/emitters/index.md +0 -36
  164. data/docs/emitters/misp.md +0 -21
  165. data/docs/emitters/slack.md +0 -21
  166. data/docs/emitters/webhook.md +0 -63
  167. data/docs/enrichers/google_public_dns.md +0 -19
  168. data/docs/enrichers/index.md +0 -35
  169. data/docs/enrichers/ipinfo.md +0 -26
  170. data/docs/enrichers/shodan.md +0 -22
  171. data/docs/enrichers/whois.md +0 -17
  172. data/docs/github_actions.md +0 -43
  173. data/docs/index.md +0 -11
  174. data/docs/installation.md +0 -31
  175. data/docs/requirements.md +0 -13
  176. data/docs/rule.md +0 -168
  177. data/docs/tags.md +0 -3
  178. data/docs/usage.md +0 -103
  179. data/frontend/.eslintrc.cjs +0 -22
  180. data/frontend/.gitignore +0 -31
  181. data/frontend/.prettierrc.json +0 -8
  182. data/frontend/README.md +0 -3
  183. data/frontend/env.d.ts +0 -5
  184. data/frontend/index.html +0 -21
  185. data/frontend/package-lock.json +0 -7219
  186. data/frontend/package.json +0 -67
  187. data/frontend/public/favicon.ico +0 -0
  188. data/frontend/scripts/swagger_doc_to_yaml.rb +0 -23
  189. data/frontend/src/App.vue +0 -27
  190. data/frontend/src/ace-config.ts +0 -6
  191. data/frontend/src/api-helper.ts +0 -111
  192. data/frontend/src/api.ts +0 -105
  193. data/frontend/src/components/ErrorMessage.vue +0 -31
  194. data/frontend/src/components/Loading.vue +0 -15
  195. data/frontend/src/components/Navbar.vue +0 -42
  196. data/frontend/src/components/Pagination.vue +0 -119
  197. data/frontend/src/components/alert/Alert.vue +0 -87
  198. data/frontend/src/components/alert/Alerts.vue +0 -63
  199. data/frontend/src/components/alert/AlertsWithPagination.vue +0 -90
  200. data/frontend/src/components/alert/AlertsWrapper.vue +0 -128
  201. data/frontend/src/components/alert/Form.vue +0 -182
  202. data/frontend/src/components/artifact/AS.vue +0 -29
  203. data/frontend/src/components/artifact/Artifact.vue +0 -287
  204. data/frontend/src/components/artifact/ArtifactTag.vue +0 -64
  205. data/frontend/src/components/artifact/ArtifactTags.vue +0 -29
  206. data/frontend/src/components/artifact/ArtifactWrapper.vue +0 -57
  207. data/frontend/src/components/artifact/CPEs.vue +0 -23
  208. data/frontend/src/components/artifact/DnsRecords.vue +0 -38
  209. data/frontend/src/components/artifact/Ports.vue +0 -23
  210. data/frontend/src/components/artifact/ReverseDnsNames.vue +0 -31
  211. data/frontend/src/components/artifact/Tags.vue +0 -29
  212. data/frontend/src/components/artifact/WhoisRecord.vue +0 -44
  213. data/frontend/src/components/config/Configs.vue +0 -65
  214. data/frontend/src/components/config/ConfigsWrapper.vue +0 -32
  215. data/frontend/src/components/link/Link.vue +0 -32
  216. data/frontend/src/components/link/Links.vue +0 -42
  217. data/frontend/src/components/rule/EditRule.vue +0 -72
  218. data/frontend/src/components/rule/EditRuleWrapper.vue +0 -48
  219. data/frontend/src/components/rule/Form.vue +0 -158
  220. data/frontend/src/components/rule/InputForm.vue +0 -45
  221. data/frontend/src/components/rule/NewRule.vue +0 -57
  222. data/frontend/src/components/rule/Rule.vue +0 -100
  223. data/frontend/src/components/rule/RuleWrapper.vue +0 -53
  224. data/frontend/src/components/rule/Rules.vue +0 -84
  225. data/frontend/src/components/rule/RulesWrapper.vue +0 -121
  226. data/frontend/src/components/rule/YAML.vue +0 -37
  227. data/frontend/src/components/tag/Tag.vue +0 -65
  228. data/frontend/src/components/tag/Tags.vue +0 -37
  229. data/frontend/src/countries.ts +0 -350
  230. data/frontend/src/index.ts +0 -20
  231. data/frontend/src/links/anyrun.ts +0 -19
  232. data/frontend/src/links/base.ts +0 -14
  233. data/frontend/src/links/censys.ts +0 -20
  234. data/frontend/src/links/crtsh.ts +0 -20
  235. data/frontend/src/links/dnslytics.ts +0 -38
  236. data/frontend/src/links/greynoise.ts +0 -20
  237. data/frontend/src/links/index.ts +0 -40
  238. data/frontend/src/links/intezer.ts +0 -20
  239. data/frontend/src/links/otx.ts +0 -33
  240. data/frontend/src/links/securitytrails.ts +0 -38
  241. data/frontend/src/links/shodan.ts +0 -20
  242. data/frontend/src/links/urlscan.ts +0 -50
  243. data/frontend/src/links/virustotal.ts +0 -72
  244. data/frontend/src/main.ts +0 -41
  245. data/frontend/src/router/index.ts +0 -57
  246. data/frontend/src/rule.ts +0 -14
  247. data/frontend/src/shims-vue.d.ts +0 -6
  248. data/frontend/src/swagger.yaml +0 -771
  249. data/frontend/src/types.ts +0 -188
  250. data/frontend/src/utils.ts +0 -54
  251. data/frontend/src/views/Alerts.vue +0 -20
  252. data/frontend/src/views/Artifact.vue +0 -39
  253. data/frontend/src/views/Configs.vue +0 -20
  254. data/frontend/src/views/EditRule.vue +0 -39
  255. data/frontend/src/views/NewRule.vue +0 -26
  256. data/frontend/src/views/Rule.vue +0 -39
  257. data/frontend/src/views/Rules.vue +0 -20
  258. data/frontend/tests/utils.spec.ts +0 -9
  259. data/frontend/tsconfig.app.json +0 -21
  260. data/frontend/tsconfig.json +0 -14
  261. data/frontend/tsconfig.node.json +0 -13
  262. data/frontend/tsconfig.vitest.json +0 -12
  263. data/frontend/vite.config.ts +0 -24
  264. data/frontend/vitest.config.ts +0 -21
  265. data/lib/mihari/services/alert_proxy.rb +0 -92
  266. data/lib/mihari/templates/rule.yml.erb +0 -5
  267. data/lib/mihari/web/public/assets/mode-yaml-a21faa53.js +0 -8
@@ -9,17 +9,22 @@ require "mihari/web/endpoints/rules"
9
9
  require "mihari/web/endpoints/tags"
10
10
 
11
11
  module Mihari
12
- class API < Grape::API
13
- prefix "api"
14
- format :json
12
+ module Web
13
+ #
14
+ # Grape API
15
+ #
16
+ class API < Grape::API
17
+ prefix "api"
18
+ format :json
15
19
 
16
- mount Endpoints::Alerts
17
- mount Endpoints::Artifacts
18
- mount Endpoints::Configs
19
- mount Endpoints::IPAddresses
20
- mount Endpoints::Rules
21
- mount Endpoints::Tags
20
+ mount Endpoints::Alerts
21
+ mount Endpoints::Artifacts
22
+ mount Endpoints::Configs
23
+ mount Endpoints::IPAddresses
24
+ mount Endpoints::Rules
25
+ mount Endpoints::Tags
22
26
 
23
- add_swagger_documentation(api_version: "v1", info: { title: "Mihari API" })
27
+ add_swagger_documentation(api_version: "v1", info: { title: "Mihari API" })
28
+ end
24
29
  end
25
30
  end
@@ -17,72 +17,80 @@ require "mihari/web/middleware/error_notification_adapter"
17
17
  require "mihari/web/api"
18
18
 
19
19
  module Mihari
20
- class App
21
- def initialize
22
- @filenames = ["", ".html", "index.html", "/index.html"]
23
- @rack_static = Rack::Static.new(
24
- -> { [404, {}, []] },
25
- root: File.expand_path("./public", __dir__),
26
- urls: ["/"]
27
- )
28
- end
29
-
30
- class << self
31
- def instance
32
- @instance ||= Rack::Builder.new do
33
- use Rack::Cors do
34
- allow do
35
- origins "*"
36
- resource "*", headers: :any, methods: %i[get post put delete options]
37
- end
38
- end
20
+ module Web
21
+ #
22
+ # Rack + Grape based web app
23
+ #
24
+ class App
25
+ # @return [Array<String>]
26
+ attr_reader :filenames
39
27
 
40
- use Middleware::ConnectionAdapter
41
- use Middleware::ErrorNotificationAdapter
28
+ # @return [Rack::Static]
29
+ attr_reader :rack_static
42
30
 
43
- run App.new
44
- end.to_app
31
+ def initialize
32
+ @filenames = ["", ".html", "index.html", "/index.html"]
33
+ @rack_static = Rack::Static.new(
34
+ -> { [404, {}, []] },
35
+ root: File.expand_path("./public", __dir__),
36
+ urls: ["/"]
37
+ )
45
38
  end
46
39
 
47
- def run!(port: 9292, host: "localhost", threads: "0:5", verbose: false, worker_timeout: 60, open: true)
48
- url = "http://#{host}:#{port}"
49
-
50
- # set maximum number of threads to use as PARALLEL_PROCESSOR_COUNT (if it is not set)
51
- # ref. https://github.com/grosser/parallel#tips
52
- # TODO: is this the best way?
53
- _min_thread, max_thread = threads.split(":")
54
- ENV["PARALLEL_PROCESSOR_COUNT"] = max_thread if ENV["PARALLEL_PROCESSOR_COUNT"].nil?
55
- Rackup::Handler::Puma.run(
56
- instance,
57
- Port: port,
58
- Host: host,
59
- Threads: threads,
60
- Verbose: verbose,
61
- worker_timeout: worker_timeout
62
- ) do |_|
63
- Launchy.open(url) if ENV["RACK_ENV"] != "development" && open
64
- rescue Launchy::CommandNotFoundError
65
- # ref. https://github.com/ninoseki/mihari/issues/477
66
- # do nothing
40
+ def call(env)
41
+ status, headers, body = API.call(env)
42
+ return [status, headers, body] unless headers["X-Cascade"] == "pass"
43
+
44
+ # Check if the App wants us to pass the response along to others
45
+ request_path = env["PATH_INFO"]
46
+ filenames.each do |path|
47
+ static_status, static_headers, static_body = rack_static.call(env.merge("PATH_INFO" => request_path + path))
48
+ return [static_status, static_headers, static_body] if static_status != 404
67
49
  end
50
+
51
+ [status, headers, body]
68
52
  end
69
- end
70
53
 
71
- def call(env)
72
- # api
73
- api_response = API.call(env)
54
+ class << self
55
+ def instance
56
+ @instance ||= Rack::Builder.new do
57
+ use Rack::Cors do
58
+ allow do
59
+ origins "*"
60
+ resource "*", headers: :any, methods: %i[get post put delete options]
61
+ end
62
+ end
74
63
 
75
- # Check if the App wants us to pass the response along to others
76
- if api_response[1]["X-Cascade"] == "pass"
77
- # static files
78
- request_path = env["PATH_INFO"]
79
- @filenames.each do |path|
80
- response = @rack_static.call(env.merge("PATH_INFO" => request_path + path))
81
- return response if response[0] != 404
64
+ use Middleware::ConnectionAdapter
65
+ use Middleware::ErrorNotificationAdapter
66
+
67
+ run App.new
68
+ end.to_app
82
69
  end
83
- end
84
70
 
85
- api_response
71
+ def run!(port: 9292, host: "localhost", threads: "0:5", verbose: false, worker_timeout: 60, open: true)
72
+ url = "http://#{host}:#{port}"
73
+
74
+ # set maximum number of threads to use as PARALLEL_PROCESSOR_COUNT (if it is not set)
75
+ # ref. https://github.com/grosser/parallel#tips
76
+ # TODO: is this the best way?
77
+ _min_thread, max_thread = threads.split(":")
78
+ ENV["PARALLEL_PROCESSOR_COUNT"] = max_thread if ENV["PARALLEL_PROCESSOR_COUNT"].nil?
79
+ Rackup::Handler::Puma.run(
80
+ instance,
81
+ Port: port,
82
+ Host: host,
83
+ Threads: threads,
84
+ Verbose: verbose,
85
+ worker_timeout: worker_timeout
86
+ ) do |_|
87
+ Launchy.open(url) if ENV["RACK_ENV"] != "development" && open
88
+ rescue Launchy::CommandNotFoundError
89
+ # ref. https://github.com/ninoseki/mihari/issues/477
90
+ # do nothing
91
+ end
92
+ end
93
+ end
86
94
  end
87
95
  end
88
96
  end
@@ -1,109 +1,151 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- module Endpoints
5
- class Alerts < Grape::API
6
- namespace :alerts do
7
- desc "Search alerts", {
8
- is_array: true,
9
- success: Entities::AlertsWithPagination,
10
- failure: [{ code: 404, message: "Not found", model: Entities::Message }],
11
- summary: "Search alerts"
12
- }
13
- params do
14
- optional :page, type: Integer, default: 1
15
- optional :limit, type: Integer, default: 10
4
+ module Web
5
+ module Endpoints
6
+ #
7
+ # Alert API endpoint
8
+ #
9
+ class Alerts < Grape::API
10
+ class AlertSearcher < Mihari::Service
11
+ class ResultValue
12
+ # @return [Array<Mihari::Models::Alert>]
13
+ attr_reader :alerts
16
14
 
17
- optional :artifact, type: String
18
- optional :rule_id, type: String
19
- optional :tag, type: String
15
+ # @return [Integer]
16
+ attr_reader :total
20
17
 
21
- optional :fromAt, type: DateTime
22
- optional :toAt, type: DateTime
23
- end
24
- get "/" do
25
- filter = params.to_h.to_snake_keys
18
+ # @return [Mihari::Structs::Filters::Alert::SearchFilterWithPagination]
19
+ attr_reader :filter
20
+
21
+ #
22
+ # @param [Array<Mihari::Models::Alert>] alerts
23
+ # @param [Integer] total
24
+ # @param [Mihari::Structs::Filters::Alert::SearchFilterWithPagination] filter
25
+ #
26
+ def initialize(alerts:, total:, filter:)
27
+ @alerts = alerts
28
+ @total = total
29
+ @filter = filter
30
+ end
31
+ end
32
+
33
+ #
34
+ # @param [Hash] params
35
+ #
36
+ # @return [ResultValue]
37
+ #
38
+ def call(params)
39
+ filter = params.to_h.to_snake_keys
26
40
 
27
- # normalize keys
28
- filter["artifact_data"] = filter["artifact"]
29
- filter["tag_name"] = filter["tag"]
30
- # symbolize hash keys
31
- filter = filter.to_h.symbolize_keys
41
+ # normalize keys
42
+ filter["artifact_data"] = filter["artifact"]
43
+ filter["tag_name"] = filter["tag"]
44
+ # symbolize hash keys
45
+ filter = filter.to_h.symbolize_keys
32
46
 
33
- search_filter_with_pagination = Structs::Filters::Alert::SearchFilterWithPagination.new(**filter)
34
- alerts = Mihari::Models::Alert.search(search_filter_with_pagination)
35
- total = Mihari::Models::Alert.count(search_filter_with_pagination.without_pagination)
47
+ search_filter_with_pagination = Structs::Filters::Alert::SearchFilterWithPagination.new(**filter)
48
+ alerts = Mihari::Models::Alert.search(search_filter_with_pagination)
49
+ total = Mihari::Models::Alert.count(search_filter_with_pagination.without_pagination)
36
50
 
37
- present(
38
- {
39
- alerts: alerts,
40
- total: total,
41
- current_page: filter[:page].to_i,
42
- page_size: filter[:limit].to_i
43
- },
44
- with: Entities::AlertsWithPagination
45
- )
51
+ ResultValue.new(alerts: alerts, total: total, filter: filter)
52
+ end
46
53
  end
47
54
 
48
- desc "Delete an alert", {
49
- success: Entities::Message,
50
- failure: [{ code: 404, message: "Not found", model: Entities::Message }],
51
- summary: "Delete an alert"
52
- }
53
- params do
54
- requires :id, type: Integer
55
+ class AlertCreator < Service
56
+ #
57
+ # @param [Hash] params
58
+ #
59
+ # @return [Mihari::Models::Alert]
60
+ #
61
+ def call(params)
62
+ proxy = Services::AlertProxy.new(**params.to_snake_keys)
63
+ Services::AlertRunner.call proxy
64
+ end
55
65
  end
56
- delete "/:id" do
57
- extend Dry::Monads[:result, :try]
58
66
 
59
- id = params["id"].to_i
67
+ class AlertDestroyer < Service
68
+ #
69
+ # @param [String] id
70
+ #
71
+ def call(id)
72
+ Mihari::Models::Alert.find(id).destroy
73
+ end
74
+ end
60
75
 
61
- result = Try do
62
- alert = Mihari::Models::Alert.find(id)
63
- alert.destroy
64
- end.to_result
76
+ namespace :alerts do
77
+ desc "Search alerts", {
78
+ is_array: true,
79
+ success: Entities::AlertsWithPagination,
80
+ failure: [{ code: 404, message: "Not found", model: Entities::Message }],
81
+ summary: "Search alerts"
82
+ }
83
+ params do
84
+ optional :page, type: Integer, default: 1
85
+ optional :limit, type: Integer, default: 10
86
+ optional :artifact, type: String
87
+ optional :rule_id, type: String
88
+ optional :tag, type: String
89
+ optional :fromAt, type: DateTime
90
+ optional :toAt, type: DateTime
91
+ end
92
+ get "/" do
93
+ value = AlertSearcher.call(params.to_h)
94
+ present(
95
+ {
96
+ alerts: value.alerts,
97
+ total: value.total,
98
+ current_page: value.filter[:page].to_i,
99
+ page_size: value.filter[:limit].to_i
100
+ },
101
+ with: Entities::AlertsWithPagination
102
+ )
103
+ end
65
104
 
66
- if result.success?
67
- status 204
68
- return present({ message: "" }, with: Entities::Message)
105
+ desc "Delete an alert", {
106
+ success: Entities::Message,
107
+ failure: [{ code: 404, message: "Not found", model: Entities::Message }],
108
+ summary: "Delete an alert"
109
+ }
110
+ params do
111
+ requires :id, type: Integer
69
112
  end
113
+ delete "/:id" do
114
+ id = params["id"].to_i
115
+ result = AlertDestroyer.result(id)
116
+ if result.success?
117
+ status 204
118
+ return present({ message: "" }, with: Entities::Message)
119
+ end
70
120
 
71
- failure = result.failure
72
- case failure
73
- when ActiveRecord::RecordNotFound
74
- error!({ message: "ID:#{id} is not found" }, 404)
75
- else
121
+ failure = result.failure
122
+ case failure
123
+ when ActiveRecord::RecordNotFound
124
+ error!({ message: "ID:#{id} is not found" }, 404)
125
+ end
76
126
  raise failure
77
127
  end
78
- end
79
-
80
- desc "Create an alert", {
81
- success: Entities::Alert,
82
- summary: "Create an alert"
83
- }
84
- params do
85
- requires :ruleId, type: String, documentation: { param_type: "body" }
86
- requires :artifacts, type: Array, documentation: { type: String, is_array: true, param_type: "body" }
87
- end
88
- post "/" do
89
- extend Dry::Monads[:result, :try]
90
-
91
- result = Try do
92
- proxy = Services::AlertProxy.new(**params.to_snake_keys)
93
- runner = Services::AlertRunner.new(proxy)
94
- runner.run
95
- end.to_result
96
128
 
97
- if result.success?
98
- status 201
99
- return present(result.value!, with: Entities::Alert)
129
+ desc "Create an alert", {
130
+ success: Entities::Alert,
131
+ summary: "Create an alert"
132
+ }
133
+ params do
134
+ requires :ruleId, type: String, documentation: { param_type: "body" }
135
+ requires :artifacts, type: Array, documentation: { type: String, is_array: true, param_type: "body" }
100
136
  end
137
+ post "/" do
138
+ result = AlertCreator.result(params)
139
+ if result.success?
140
+ status 201
141
+ return present(result.value!, with: Entities::Alert)
142
+ end
101
143
 
102
- failure = result.failure
103
- case failure
104
- when ActiveRecord::RecordNotFound
105
- error!({ message: "Rule:#{params["ruleId"]} is not found" }, 404)
106
- else
144
+ failure = result.failure
145
+ case failure
146
+ when ActiveRecord::RecordNotFound
147
+ error!({ message: "Rule:#{params["ruleId"]} is not found" }, 404)
148
+ end
107
149
  raise failure
108
150
  end
109
151
  end
@@ -1,23 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- module Endpoints
5
- class Artifacts < Grape::API
6
- namespace :artifacts do
7
- desc "Get an artifact", {
8
- success: Entities::Artifact,
9
- failure: [{ code: 404, message: "Not found", model: Entities::Message }],
10
- summary: "Get an artifact"
11
- }
12
- params do
13
- requires :id, type: Integer
14
- end
15
- get "/:id" do
16
- extend Dry::Monads[:result, :try]
17
-
18
- id = params[:id].to_i
19
-
20
- result = Try do
4
+ module Web
5
+ module Endpoints
6
+ #
7
+ # Artifact API endpoint
8
+ #
9
+ class Artifacts < Grape::API
10
+ class ArtifactGetter < Service
11
+ #
12
+ # @param [Integer] id
13
+ #
14
+ # @return [Mihari::Models::Artifact]
15
+ #
16
+ def call(id)
21
17
  artifact = Mihari::Models::Artifact.includes(
22
18
  :autonomous_system,
23
19
  :geolocation,
@@ -28,38 +24,19 @@ module Mihari
28
24
  # TODO: improve queries
29
25
  alert_ids = Mihari::Models::Artifact.where(data: artifact.data).pluck(:alert_id)
30
26
  tag_ids = Mihari::Models::Tagging.where(alert_id: alert_ids).pluck(:tag_id)
31
- tag_names = Mihari::Models::Tag.where(id: tag_ids).distinct.pluck(:name)
27
+ tags = Mihari::Models::Tag.where(id: tag_ids)
32
28
 
33
- artifact.tags = tag_names
29
+ artifact.tags = tags
34
30
 
35
31
  artifact
36
- end.to_result
37
-
38
- return present(result.value!, with: Entities::Artifact) if result.success?
39
-
40
- failure = result.failure
41
- case failure
42
- when ActiveRecord::RecordNotFound
43
- error!({ message: "ID:#{id} is not found" }, 404)
44
- else
45
- raise failure
46
32
  end
47
33
  end
48
34
 
49
- desc "Enrich an artifact", {
50
- success: Entities::Message,
51
- failure: [{ code: 404, message: "Not found", model: Entities::Message }],
52
- summary: "Enrich an artifact"
53
- }
54
- params do
55
- requires :id, type: Integer
56
- end
57
- get "/:id/enrich" do
58
- extend Dry::Monads[:result, :try]
59
-
60
- id = params["id"].to_i
61
-
62
- result = Try do
35
+ class ArtifactEnricher < Service
36
+ #
37
+ # @param [String] id
38
+ #
39
+ def call(id)
63
40
  artifact = Mihari::Models::Artifact.includes(
64
41
  :autonomous_system,
65
42
  :geolocation,
@@ -72,50 +49,85 @@ module Mihari
72
49
 
73
50
  artifact.enrich_all
74
51
  artifact.save
75
- end.to_result
76
-
77
- if result.success?
78
- status 201
79
- return present({ message: "" }, with: Entities::Message)
80
- end
81
-
82
- failure = result.failure
83
- case failure
84
- when ActiveRecord::RecordNotFound
85
- error!({ message: "ID:#{id} is not found" }, 404)
86
- else
87
- raise failure
88
52
  end
89
53
  end
90
54
 
91
- desc "Delete an artifact", {
92
- success: Entities::Message,
93
- failure: [{ code: 404, message: "Not found", model: Entities::Message }],
94
- summary: "Delete an artifact"
95
- }
96
- params do
97
- requires :id, type: Integer
55
+ class ArtifactDestroyer < Service
56
+ #
57
+ # @param [Integer] id
58
+ #
59
+ def call(id)
60
+ Mihari::Models::Artifact.find(id).destroy
61
+ end
98
62
  end
99
- delete "/:id" do
100
- extend Dry::Monads[:result, :try]
101
63
 
102
- id = params["id"].to_i
103
-
104
- result = Try do
105
- alert = Mihari::Models::Artifact.find(id)
106
- alert.destroy
107
- end.to_result
64
+ namespace :artifacts do
65
+ desc "Get an artifact", {
66
+ success: Entities::Artifact,
67
+ failure: [{ code: 404, message: "Not found", model: Entities::Message }],
68
+ summary: "Get an artifact"
69
+ }
70
+ params do
71
+ requires :id, type: Integer
72
+ end
73
+ get "/:id" do
74
+ id = params[:id].to_i
75
+ result = ArtifactGetter.result(id)
76
+ return present(result.value!, with: Entities::Artifact) if result.success?
77
+
78
+ failure = result.failure
79
+ case failure
80
+ when ActiveRecord::RecordNotFound
81
+ error!({ message: "ID:#{id} is not found" }, 404)
82
+ end
83
+ raise failure
84
+ end
108
85
 
109
- if result.success?
110
- status 204
111
- return present({ message: "" }, with: Entities::Message)
86
+ desc "Enrich an artifact", {
87
+ success: Entities::Message,
88
+ failure: [{ code: 404, message: "Not found", model: Entities::Message }],
89
+ summary: "Enrich an artifact"
90
+ }
91
+ params do
92
+ requires :id, type: Integer
93
+ end
94
+ get "/:id/enrich" do
95
+ id = params["id"].to_i
96
+ result = ArtifactEnricher.result(id)
97
+ if result.success?
98
+ status 201
99
+ return present({ message: "" }, with: Entities::Message)
100
+ end
101
+
102
+ failure = result.failure
103
+ case failure
104
+ when ActiveRecord::RecordNotFound
105
+ error!({ message: "ID:#{id} is not found" }, 404)
106
+ end
107
+ raise failure
112
108
  end
113
109
 
114
- failure = result.failure
115
- case failure
116
- when ActiveRecord::RecordNotFound
117
- error!({ message: "ID:#{id} is not found" }, 404)
118
- else
110
+ desc "Delete an artifact", {
111
+ success: Entities::Message,
112
+ failure: [{ code: 404, message: "Not found", model: Entities::Message }],
113
+ summary: "Delete an artifact"
114
+ }
115
+ params do
116
+ requires :id, type: Integer
117
+ end
118
+ delete "/:id" do
119
+ id = params["id"].to_i
120
+ result = ArtifactDestroyer.result(id)
121
+ if result.success?
122
+ status 204
123
+ return present({ message: "" }, with: Entities::Message)
124
+ end
125
+
126
+ failure = result.failure
127
+ case failure
128
+ when ActiveRecord::RecordNotFound
129
+ error!({ message: "ID:#{id} is not found" }, 404)
130
+ end
119
131
  raise failure
120
132
  end
121
133
  end
@@ -1,20 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- module Endpoints
5
- class Configs < Grape::API
6
- namespace :configs do
7
- desc "Get configs", {
8
- is_array: true,
9
- success: Entities::Config,
10
- summary: "Get configs"
11
- }
12
- get "/" do
13
- configs = (Mihari.analyzers + Mihari.emitters + Mihari.enrichers).filter_map do |klass|
14
- Mihari::Structs::Config.from_class(klass)
15
- end
4
+ module Web
5
+ module Endpoints
6
+ #
7
+ # Config API endpoint
8
+ #
9
+ class Configs < Grape::API
10
+ namespace :configs do
11
+ desc "Get configs", {
12
+ is_array: true,
13
+ success: Entities::Config,
14
+ summary: "Get configs"
15
+ }
16
+ get "/" do
17
+ configs = (Mihari.analyzers + Mihari.emitters + Mihari.enrichers).filter_map do |klass|
18
+ Mihari::Structs::Config.from_class(klass)
19
+ end
16
20
 
17
- present(configs, with: Entities::Config)
21
+ present(configs, with: Entities::Config)
22
+ end
18
23
  end
19
24
  end
20
25
  end