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
@@ -1,24 +1,44 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- module Endpoints
5
- class IPAddresses < Grape::API
6
- namespace :ip_addresses do
7
- desc "Get an IP address", {
8
- success: Entities::IPAddress,
9
- failure: [{ code: 404, message: "Not found", model: Entities::Message }],
10
- summary: "Get an IP address"
11
- }
12
- params do
13
- requires :ip, type: String, regexp: /\A[0-9.]+\z/
4
+ module Web
5
+ module Endpoints
6
+ #
7
+ # IP address API endpoint
8
+ #
9
+ class IPAddresses < Grape::API
10
+ class IPGetter < Service
11
+ #
12
+ # @param [String] ip
13
+ #
14
+ # @return [Mihari::Structs::IPInfo::Response]
15
+ #
16
+ def call(ip)
17
+ Mihari::Enrichers::IPInfo.new.call ip
18
+ end
14
19
  end
15
- get "/:ip", requirements: { ip: %r{[^/]+} } do
16
- ip = params[:ip].to_s
17
20
 
18
- data = Enrichers::IPInfo.new.query(ip)
19
- error!({ message: "IP:#{ip} is not found" }, 404) if data.nil?
21
+ namespace :ip_addresses do
22
+ desc "Get an IP address", {
23
+ success: Entities::IPAddress,
24
+ failure: [{ code: 404, message: "Not found", model: Entities::Message }],
25
+ summary: "Get an IP address"
26
+ }
27
+ params do
28
+ requires :ip, type: String, regexp: /\A[0-9.]+\z/
29
+ end
30
+ get "/:ip", requirements: { ip: %r{[^/]+} } do
31
+ ip = params[:ip].to_s
32
+ result = IPGetter.result(ip)
33
+ return present(result.value!, with: Entities::IPAddress) if result.success?
20
34
 
21
- present data, with: Entities::IPAddress
35
+ failure = result.failure
36
+ case failure
37
+ when Mihari::StatusCodeError
38
+ error!({ message: "ID:#{id} is not found" }, 404) if failure.status_code == 404
39
+ end
40
+ raise failure
41
+ end
22
42
  end
23
43
  end
24
44
  end
@@ -1,224 +1,273 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- module Endpoints
5
- class Rules < Grape::API
6
- namespace :rules do
7
- desc "Get Rule IDs", {
8
- is_array: true,
9
- success: Entities::RuleIDs,
10
- summary: "Get rule IDs"
11
- }
12
- get "/ids" do
13
- rule_ids = Mihari::Models::Rule.distinct.pluck(:id)
14
- present({ rule_ids: rule_ids }, with: Entities::RuleIDs)
15
- end
4
+ module Web
5
+ module Endpoints
6
+ #
7
+ # Rule API endpoint
8
+ #
9
+ class Rules < Grape::API
10
+ class RuleSearcher < Mihari::Service
11
+ class ResultValue
12
+ # @return [Array<Mihari::Models::Rule>]
13
+ attr_reader :rules
14
+
15
+ # @return [Integer]
16
+ attr_reader :total
17
+
18
+ # @return [Mihari::Structs::Filters::Rule::SearchFilterWithPagination]
19
+ attr_reader :filter
20
+
21
+ #
22
+ # @param [Array<Mihari::Models::Rule>] rules
23
+ # @param [Integer] total
24
+ # @param [Mihari::Structs::Filters::Rule::SearchFilterWithPagination] filter
25
+ #
26
+ def initialize(rules:, total:, filter:)
27
+ @rules = rules
28
+ @total = total
29
+ @filter = filter
30
+ end
31
+ end
16
32
 
17
- desc "Search rules", {
18
- is_array: true,
19
- success: Entities::RulesWithPagination,
20
- failure: [{ code: 404, message: "Not found", model: Entities::Message }],
21
- summary: "Search rules"
22
- }
23
- params do
24
- optional :page, type: Integer, default: 1
25
- optional :limit, type: Integer, default: 10
26
-
27
- optional :title, type: String
28
- optional :description, type: String
29
- optional :tag, type: String
30
-
31
- optional :fromAt, type: DateTime
32
- optional :toAt, type: DateTime
33
+ #
34
+ # @params [Hash]
35
+ #
36
+ # @return [ResultValue]
37
+ #
38
+ def call(params)
39
+ filter = params.to_h.to_snake_keys
40
+
41
+ # normalize keys
42
+ filter["tag_name"] = filter["tag"]
43
+ # symbolize hash keys
44
+ filter = filter.to_h.symbolize_keys
45
+
46
+ search_filter_with_pagination = Mihari::Structs::Filters::Rule::SearchFilterWithPagination.new(**filter)
47
+ rules = Mihari::Models::Rule.search(search_filter_with_pagination)
48
+ total = Mihari::Models::Rule.count(search_filter_with_pagination.without_pagination)
49
+
50
+ ResultValue.new(rules: rules, total: total, filter: filter)
51
+ end
33
52
  end
34
- get "/" do
35
- filter = params.to_h.to_snake_keys
36
-
37
- # normalize keys
38
- filter["tag_name"] = filter["tag"]
39
- # symbolize hash keys
40
- filter = filter.to_h.symbolize_keys
41
-
42
- search_filter_with_pagenation = Structs::Filters::Rule::SearchFilterWithPagination.new(**filter)
43
- rules = Mihari::Models::Rule.search(search_filter_with_pagenation)
44
- total = Mihari::Models::Rule.count(search_filter_with_pagenation.without_pagination)
45
-
46
- present(
47
- { rules: rules,
48
- total: total,
49
- current_page: filter[:page].to_i,
50
- page_size: filter[:limit].to_i },
51
- with: Entities::RulesWithPagination
52
- )
53
+
54
+ class RuleGetter < Service
55
+ #
56
+ # @params [String] id
57
+ #
58
+ # @return [Mihari::Models::Rule]
59
+ #
60
+ def call(id)
61
+ Mihari::Models::Rule.find id
62
+ end
53
63
  end
54
64
 
55
- desc "Get a rule", {
56
- success: Entities::Rule,
57
- failure: [{ code: 404, message: "Not found", model: Entities::Message }],
58
- summary: "Get a rule"
59
- }
60
- params do
61
- requires :id, type: String
65
+ class RuleRunner < Service
66
+ #
67
+ # @param [String] id
68
+ #
69
+ def call(id)
70
+ rule = Mihari::Rule.from_model(Mihari::Models::Rule.find(id))
71
+ rule.call
72
+ end
62
73
  end
63
- get "/:id" do
64
- extend Dry::Monads[:result, :try]
65
74
 
66
- id = params["id"].to_s
75
+ class RuleCreator < Service
76
+ #
77
+ # @params [String]
78
+ #
79
+ # @return [Mihari::Models::Rule]
80
+ #
81
+ def call(yaml)
82
+ rule = Rule.from_yaml(yaml)
67
83
 
68
- result = Try do
69
- Mihari::Models::Rule.find(id)
70
- end.to_result
84
+ found = Mihari::Models::Rule.find_by_id(rule.id)
85
+ error!({ message: "ID:#{rule.id} is already registered" }, 400) unless found.nil?
71
86
 
72
- return present(result.value!, with: Entities::Rule) if result.success?
87
+ rule.model.save
88
+ rule
89
+ end
90
+ end
73
91
 
74
- failure = result.failure
75
- case failure
76
- when ActiveRecord::RecordNotFound
77
- error!({ message: "ID:#{id} is not found" }, 404)
78
- else
79
- raise failure
92
+ class RuleUpdater < Service
93
+ #
94
+ # @params [String] id
95
+ # @params [String] yaml
96
+ #
97
+ # @return [Mihari::Models::Rule]
98
+ #
99
+ def call(id:, yaml:)
100
+ Mihari::Models::Rule.find(id)
101
+
102
+ rule = Rule.from_yaml(yaml)
103
+ rule.model.save
104
+ rule
80
105
  end
81
106
  end
82
107
 
83
- desc "Run a rule", {
84
- success: Entities::Message,
85
- summary: "Run a rule"
86
- }
87
- params do
88
- requires :id, type: String
108
+ class RuleDestroyer < Service
109
+ #
110
+ # @param [String] id
111
+ #
112
+ def call(id)
113
+ Mihari::Models::Rule.find(id).destroy
114
+ end
89
115
  end
90
- get "/:id/run" do
91
- extend Dry::Monads[:result, :try]
92
116
 
93
- id = params["id"].to_s
117
+ namespace :rules do
118
+ desc "Get Rule IDs", {
119
+ is_array: true,
120
+ success: Entities::RuleIDs,
121
+ summary: "Get rule IDs"
122
+ }
123
+ get "/ids" do
124
+ rule_ids = Mihari::Models::Rule.distinct.pluck(:id)
125
+ present({ rule_ids: rule_ids }, with: Entities::RuleIDs)
126
+ end
94
127
 
95
- result = Try { Rule.from_model(Mihari::Models::Rule.find(id)) }.to_result
96
- if result.success?
97
- result.value!.analyzer.run
98
- status 201
99
- return present({ message: "ID:#{id} is ran successfully" }, with: Entities::Message)
128
+ desc "Search rules", {
129
+ is_array: true,
130
+ success: Entities::RulesWithPagination,
131
+ failure: [{ code: 404, message: "Not found", model: Entities::Message }],
132
+ summary: "Search rules"
133
+ }
134
+ params do
135
+ optional :page, type: Integer, default: 1
136
+ optional :limit, type: Integer, default: 10
137
+ optional :title, type: String
138
+ optional :description, type: String
139
+ optional :tag, type: String
140
+ optional :fromAt, type: DateTime
141
+ optional :toAt, type: DateTime
142
+ end
143
+ get "/" do
144
+ value = RuleSearcher.call(params.to_h)
145
+ present({
146
+ rules: value.rules,
147
+ total: value.total,
148
+ current_page: value.filter[:page].to_i,
149
+ page_size: value.filter[:limit].to_i
150
+ },
151
+ with: Entities::RulesWithPagination)
100
152
  end
101
153
 
102
- failure = result.failure
103
- case failure
104
- when ActiveRecord::RecordNotFound
105
- error!({ message: "ID:#{id} is not found" }, 404)
106
- else
107
- raise failure
154
+ desc "Get a rule", {
155
+ success: Entities::Rule,
156
+ failure: [{ code: 404, message: "Not found", model: Entities::Message }],
157
+ summary: "Get a rule"
158
+ }
159
+ params do
160
+ requires :id, type: String
108
161
  end
109
- end
162
+ get "/:id" do
163
+ id = params[:id].to_s
164
+ result = RuleGetter.result(params[:id].to_s)
165
+ return present(result.value!, with: Entities::Rule) if result.success?
110
166
 
111
- desc "Create a rule", {
112
- success: Entities::Rule,
113
- summary: "Create a rule"
114
- }
115
- params do
116
- requires :yaml, type: String, documentation: { param_type: "body" }
117
- end
118
- post "/" do
119
- extend Dry::Monads[:result, :try]
120
-
121
- yaml = params[:yaml]
122
- result = Try { Rule.from_yaml(yaml) }.to_result.bind do |rule|
123
- Try do
124
- found = Mihari::Models::Rule.find_by_id(rule.id)
125
- error!({ message: "ID:#{rule.id} is already registered" }, 400) unless found.nil?
126
- rule
127
- end.to_result
128
- end.bind do |rule|
129
- Try do
130
- rule.model.save
131
- rule
132
- end.to_result
133
- end
134
-
135
- if result.success?
136
- status 201
137
- return present(result.value!.model, with: Entities::Rule)
138
- end
139
-
140
- failure = result.failure
141
- case failure
142
- when Psych::SyntaxError
143
- error!({ message: failure.message }, 400)
144
- when ValidationError
145
- error!({ message: "Data format is invalid", details: failure.errors.to_h }, 400)
146
- else
167
+ failure = result.failure
168
+ case failure
169
+ when ActiveRecord::RecordNotFound
170
+ error!({ message: "ID:#{id} is not found" }, 404)
171
+ end
147
172
  raise failure
148
173
  end
149
- end
150
-
151
- desc "Update a rule", {
152
- success: Entities::Rule,
153
- summary: "Update a rule"
154
- }
155
- params do
156
- requires :id, type: String, documentation: { param_type: "body" }
157
- requires :yaml, type: String, documentation: { param_type: "body" }
158
- end
159
- put "/" do
160
- extend Dry::Monads[:result, :try]
161
174
 
162
- id = params[:id]
163
- yaml = params[:yaml]
175
+ desc "Run a rule", {
176
+ success: Entities::Message,
177
+ summary: "Run a rule"
178
+ }
179
+ params do
180
+ requires :id, type: String
181
+ end
182
+ get "/:id/run" do
183
+ id = params[:id].to_s
184
+ result = RuleRunner.result(id)
185
+ if result.success?
186
+ status 201
187
+ return present({ message: "ID:#{id}} ran successfully" }, with: Entities::Message)
188
+ end
164
189
 
165
- result = Try do
166
- Mihari::Models::Rule.find(id)
167
- end.to_result.bind do |_|
168
- Try { Rule.from_yaml(yaml) }.to_result
169
- end.bind do |rule|
170
- Try do
171
- rule.model.save
172
- rule
173
- end.to_result
174
- end
175
-
176
- if result.success?
177
- status 201
178
- return present(result.value!.model, with: Entities::Rule)
179
- end
180
-
181
- failure = result.failure
182
- case failure
183
- when ActiveRecord::RecordNotFound
184
- error!({ message: "ID:#{id} is not found" }, 404)
185
- when Psych::SyntaxError
186
- error!({ message: failure.message }, 400)
187
- when ValidationError
188
- error!({ message: "Data format is invalid", details: failure.errors.to_h }, 400)
189
- else
190
+ failure = result.failure
191
+ case failure
192
+ when ActiveRecord::RecordNotFound
193
+ error!({ message: "ID:#{id} is not found" }, 404)
194
+ end
190
195
  raise failure
191
196
  end
192
- end
193
197
 
194
- desc "Delete a rule", {
195
- success: Entities::Message,
196
- failure: [{ code: 404, message: "Not found", model: Entities::Message }],
197
- summary: "Delete a rule"
198
- }
199
- params do
200
- requires :id, type: String
201
- end
202
- delete "/:id" do
203
- extend Dry::Monads[:result, :try]
198
+ desc "Create a rule", {
199
+ success: Entities::Rule,
200
+ summary: "Create a rule"
201
+ }
202
+ params do
203
+ requires :yaml, type: String, documentation: { param_type: "body" }
204
+ end
205
+ post "/" do
206
+ result = RuleCreator.result(params[:yaml])
207
+ if result.success?
208
+ status 201
209
+ return present(result.value!.model, with: Entities::Rule)
210
+ end
204
211
 
205
- id = params["id"].to_s
212
+ failure = result.failure
213
+ case failure
214
+ when Psych::SyntaxError
215
+ error!({ message: failure.message }, 400)
216
+ when ValidationError
217
+ error!({ message: "Rule format is invalid", details: failure.errors.to_h }, 400)
218
+ end
219
+ raise failure
220
+ end
206
221
 
207
- result = Try do
208
- rule = Mihari::Models::Rule.find(id)
209
- rule.destroy
210
- end.to_result
222
+ desc "Update a rule", {
223
+ success: Entities::Rule,
224
+ summary: "Update a rule"
225
+ }
226
+ params do
227
+ requires :id, type: String, documentation: { param_type: "body" }
228
+ requires :yaml, type: String, documentation: { param_type: "body" }
229
+ end
230
+ put "/" do
231
+ id = params[:id].to_s
232
+ result = RuleUpdater.result(id: id, yaml: params[:yaml].to_s)
233
+ if result.success?
234
+ status 201
235
+ return present(result.value!.model, with: Entities::Rule)
236
+ end
237
+
238
+ failure = result.failure
239
+ case failure
240
+ when ActiveRecord::RecordNotFound
241
+ error!({ message: "ID:#{id} is not found" }, 404)
242
+ when Psych::SyntaxError
243
+ error!({ message: failure.message }, 400)
244
+ when ValidationError
245
+ error!({ message: "Rule format is invalid", details: failure.errors.to_h }, 400)
246
+ end
247
+ raise failure
248
+ end
211
249
 
212
- if result.success?
213
- status 204
214
- return present({ message: "ID:#{id} is deleted" }, with: Entities::Message)
250
+ desc "Delete a rule", {
251
+ success: Entities::Message,
252
+ failure: [{ code: 404, message: "Not found", model: Entities::Message }],
253
+ summary: "Delete a rule"
254
+ }
255
+ params do
256
+ requires :id, type: String
215
257
  end
258
+ delete "/:id" do
259
+ id = params[:id].to_s
260
+ result = RuleDestroyer.result(id)
261
+ if result.success?
262
+ status 204
263
+ return present({ message: "ID:#{id} is deleted" }, with: Entities::Message)
264
+ end
216
265
 
217
- failure = result.failure
218
- case failure
219
- when ActiveRecord::RecordNotFound
220
- error!({ message: "ID:#{id} is not found" }, 404)
221
- else
266
+ failure = result.failure
267
+ case failure
268
+ when ActiveRecord::RecordNotFound
269
+ error!({ message: "ID:#{id} is not found" }, 404)
270
+ end
222
271
  raise failure
223
272
  end
224
273
  end
@@ -1,46 +1,53 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- module Endpoints
5
- class Tags < Grape::API
6
- namespace :tags do
7
- desc "Get tags", {
8
- is_array: true,
9
- success: Entities::Tags,
10
- summary: "Get tags"
11
- }
12
- get "/" do
13
- tags = Mihari::Models::Tag.distinct.pluck(:name)
14
- present({ tags: tags }, with: Entities::Tags)
15
- end
16
-
17
- desc "Delete a tag", {
18
- success: Entities::Message,
19
- failure: [{ code: 404, message: "Not found", model: Entities::Message }],
20
- summary: "Delete a tag"
21
- }
22
- params do
23
- requires :name, type: String
4
+ module Web
5
+ module Endpoints
6
+ #
7
+ # Tag API endpoint
8
+ #
9
+ class Tags < Grape::API
10
+ class TagDestroyer < Service
11
+ #
12
+ # @param [Integer] id
13
+ #
14
+ def call(id)
15
+ Mihari::Models::Tag.find(id).destroy
16
+ end
24
17
  end
25
- delete "/:name" do
26
- extend Dry::Monads[:result, :try]
27
18
 
28
- name = params[:name].to_s
29
-
30
- result = Try do
31
- Mihari::Models::Tag.where(name: name).destroy_all
32
- end.to_result
19
+ namespace :tags do
20
+ desc "Get tags", {
21
+ is_array: true,
22
+ success: Entities::Tags,
23
+ summary: "Get tags"
24
+ }
25
+ get "/" do
26
+ tags = Mihari::Models::Tag.distinct.pluck(:name)
27
+ present({ tags: tags }, with: Entities::Tags)
28
+ end
33
29
 
34
- if result.success?
35
- status 204
36
- return present({ message: "" }, with: Entities::Message)
30
+ desc "Delete a tag", {
31
+ success: Entities::Message,
32
+ failure: [{ code: 404, message: "Not found", model: Entities::Message }],
33
+ summary: "Delete a tag"
34
+ }
35
+ params do
36
+ requires :id, type: Integer
37
37
  end
38
+ delete "/:id" do
39
+ id = params[:id].to_i
40
+ result = TagDestroyer.result(id)
41
+ if result.success?
42
+ status 204
43
+ return present({ message: "" }, with: Entities::Message)
44
+ end
38
45
 
39
- failure = result.failure
40
- case failure
41
- when ActiveRecord::RecordNotFound
42
- error!({ message: "Name:#{name} is not found" }, 404)
43
- else
46
+ failure = result.failure
47
+ case failure
48
+ when ActiveRecord::RecordNotFound
49
+ error!({ message: "ID:#{id} is not found" }, 404)
50
+ end
44
51
  raise failure
45
52
  end
46
53
  end
@@ -1,15 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Mihari
2
- module Middleware
3
- class ConnectionAdapter
4
- def initialize(app)
5
- @app = app
6
- end
4
+ module Web
5
+ module Middleware
6
+ #
7
+ # DB connection adapter for Rack app
8
+ #
9
+ class ConnectionAdapter
10
+ def initialize(app)
11
+ @app = app
12
+ end
7
13
 
8
- def call(env)
9
- Mihari::Database.with_db_connection do
10
- status, headers, body = @app.call(env)
14
+ def call(env)
15
+ Mihari::Database.with_db_connection do
16
+ status, headers, body = @app.call(env)
11
17
 
12
- [status, headers, body]
18
+ [status, headers, body]
19
+ end
13
20
  end
14
21
  end
15
22
  end