mihari 5.6.1 → 5.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (153) 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/docs/analyzers/fofa.md +31 -0
  6. data/docs/analyzers/index.md +1 -0
  7. data/frontend/package-lock.json +183 -186
  8. data/frontend/package.json +10 -10
  9. data/frontend/src/components/alert/Form.vue +1 -14
  10. data/frontend/src/components/artifact/AS.vue +2 -8
  11. data/frontend/src/components/artifact/DnsRecords.vue +2 -8
  12. data/frontend/src/components/artifact/ReverseDnsNames.vue +2 -10
  13. data/frontend/src/components/artifact/WhoisRecord.vue +1 -1
  14. data/lib/mihari/{base.rb → actor.rb} +27 -3
  15. data/lib/mihari/analyzers/base.rb +16 -20
  16. data/lib/mihari/analyzers/binaryedge.rb +4 -1
  17. data/lib/mihari/analyzers/censys.rb +5 -3
  18. data/lib/mihari/analyzers/circl.rb +4 -1
  19. data/lib/mihari/analyzers/crtsh.rb +4 -1
  20. data/lib/mihari/analyzers/dnstwister.rb +4 -1
  21. data/lib/mihari/analyzers/feed.rb +3 -0
  22. data/lib/mihari/analyzers/fofa.rb +65 -0
  23. data/lib/mihari/analyzers/greynoise.rb +4 -1
  24. data/lib/mihari/analyzers/hunterhow.rb +7 -2
  25. data/lib/mihari/analyzers/onyphe.rb +4 -1
  26. data/lib/mihari/analyzers/otx.rb +4 -1
  27. data/lib/mihari/analyzers/passivetotal.rb +5 -2
  28. data/lib/mihari/analyzers/pulsedive.rb +4 -1
  29. data/lib/mihari/analyzers/securitytrails.rb +5 -2
  30. data/lib/mihari/analyzers/shodan.rb +4 -1
  31. data/lib/mihari/analyzers/urlscan.rb +5 -2
  32. data/lib/mihari/analyzers/virustotal.rb +9 -6
  33. data/lib/mihari/analyzers/virustotal_intelligence.rb +4 -1
  34. data/lib/mihari/analyzers/zoomeye.rb +8 -5
  35. data/lib/mihari/cli/alert.rb +3 -0
  36. data/lib/mihari/cli/base.rb +3 -0
  37. data/lib/mihari/cli/database.rb +3 -0
  38. data/lib/mihari/cli/main.rb +3 -0
  39. data/lib/mihari/cli/rule.rb +3 -0
  40. data/lib/mihari/clients/base.rb +3 -0
  41. data/lib/mihari/clients/binaryedge.rb +5 -2
  42. data/lib/mihari/clients/censys.rb +7 -4
  43. data/lib/mihari/clients/circl.rb +3 -0
  44. data/lib/mihari/clients/crtsh.rb +5 -2
  45. data/lib/mihari/clients/dnstwister.rb +3 -0
  46. data/lib/mihari/clients/fofa.rb +83 -0
  47. data/lib/mihari/clients/greynoise.rb +5 -2
  48. data/lib/mihari/clients/hunterhow.rb +5 -2
  49. data/lib/mihari/clients/misp.rb +3 -0
  50. data/lib/mihari/clients/onyphe.rb +5 -2
  51. data/lib/mihari/clients/otx.rb +3 -0
  52. data/lib/mihari/clients/passivetotal.rb +7 -4
  53. data/lib/mihari/clients/publsedive.rb +4 -1
  54. data/lib/mihari/clients/securitytrails.rb +6 -3
  55. data/lib/mihari/clients/shodan.rb +5 -2
  56. data/lib/mihari/clients/the_hive.rb +3 -0
  57. data/lib/mihari/clients/urlscan.rb +7 -4
  58. data/lib/mihari/clients/virustotal.rb +5 -2
  59. data/lib/mihari/clients/zoomeye.rb +3 -0
  60. data/lib/mihari/commands/alert.rb +5 -14
  61. data/lib/mihari/commands/database.rb +3 -0
  62. data/lib/mihari/commands/rule.rb +11 -11
  63. data/lib/mihari/commands/search.rb +9 -6
  64. data/lib/mihari/commands/version.rb +3 -0
  65. data/lib/mihari/commands/web.rb +4 -1
  66. data/lib/mihari/config.rb +139 -150
  67. data/lib/mihari/constants.rb +1 -1
  68. data/lib/mihari/database.rb +6 -0
  69. data/lib/mihari/emitters/base.rb +16 -25
  70. data/lib/mihari/emitters/database.rb +10 -9
  71. data/lib/mihari/emitters/misp.rb +20 -41
  72. data/lib/mihari/emitters/slack.rb +16 -13
  73. data/lib/mihari/emitters/the_hive.rb +18 -46
  74. data/lib/mihari/emitters/webhook.rb +34 -23
  75. data/lib/mihari/enrichers/base.rb +16 -15
  76. data/lib/mihari/enrichers/google_public_dns.rb +6 -5
  77. data/lib/mihari/enrichers/ipinfo.rb +10 -8
  78. data/lib/mihari/enrichers/shodan.rb +4 -6
  79. data/lib/mihari/enrichers/whois.rb +13 -10
  80. data/lib/mihari/errors.rb +6 -0
  81. data/lib/mihari/feed/parser.rb +3 -0
  82. data/lib/mihari/feed/reader.rb +3 -0
  83. data/lib/mihari/http.rb +6 -0
  84. data/lib/mihari/mixins/autonomous_system.rb +3 -0
  85. data/lib/mihari/mixins/configurable.rb +3 -0
  86. data/lib/mihari/mixins/error_notification.rb +3 -0
  87. data/lib/mihari/mixins/falsepositive.rb +3 -0
  88. data/lib/mihari/mixins/refang.rb +3 -0
  89. data/lib/mihari/mixins/retriable.rb +6 -2
  90. data/lib/mihari/models/alert.rb +78 -73
  91. data/lib/mihari/models/artifact.rb +186 -178
  92. data/lib/mihari/models/autonomous_system.rb +25 -20
  93. data/lib/mihari/models/cpe.rb +24 -19
  94. data/lib/mihari/models/dns.rb +27 -22
  95. data/lib/mihari/models/geolocation.rb +25 -20
  96. data/lib/mihari/models/port.rb +24 -19
  97. data/lib/mihari/models/reverse_dns.rb +24 -19
  98. data/lib/mihari/models/rule.rb +71 -66
  99. data/lib/mihari/models/tag.rb +8 -3
  100. data/lib/mihari/models/tagging.rb +8 -3
  101. data/lib/mihari/models/whois.rb +20 -17
  102. data/lib/mihari/rule.rb +357 -0
  103. data/lib/mihari/schemas/alert.rb +3 -0
  104. data/lib/mihari/schemas/analyzer.rb +105 -87
  105. data/lib/mihari/schemas/emitter.rb +12 -5
  106. data/lib/mihari/schemas/enricher.rb +11 -4
  107. data/lib/mihari/schemas/macros.rb +4 -0
  108. data/lib/mihari/schemas/mixins.rb +20 -0
  109. data/lib/mihari/schemas/rule.rb +6 -10
  110. data/lib/mihari/service.rb +16 -0
  111. data/lib/mihari/services/alert_builder.rb +8 -5
  112. data/lib/mihari/services/alert_proxy.rb +16 -7
  113. data/lib/mihari/services/alert_runner.rb +10 -14
  114. data/lib/mihari/services/rule_builder.rb +10 -7
  115. data/lib/mihari/services/rule_runner.rb +11 -13
  116. data/lib/mihari/structs/binaryedge.rb +14 -29
  117. data/lib/mihari/structs/censys.rb +54 -133
  118. data/lib/mihari/structs/config.rb +20 -31
  119. data/lib/mihari/structs/filters.rb +38 -0
  120. data/lib/mihari/structs/fofa.rb +44 -0
  121. data/lib/mihari/structs/google_public_dns.rb +10 -28
  122. data/lib/mihari/structs/greynoise.rb +38 -89
  123. data/lib/mihari/structs/hunterhow.rb +27 -25
  124. data/lib/mihari/structs/ipinfo.rb +14 -35
  125. data/lib/mihari/structs/onyphe.rb +36 -81
  126. data/lib/mihari/structs/shodan.rb +53 -118
  127. data/lib/mihari/structs/urlscan.rb +27 -66
  128. data/lib/mihari/structs/virustotal_intelligence.rb +23 -59
  129. data/lib/mihari/type_checker.rb +4 -0
  130. data/lib/mihari/types.rb +3 -0
  131. data/lib/mihari/version.rb +1 -1
  132. data/lib/mihari/web/api.rb +15 -10
  133. data/lib/mihari/web/app.rb +59 -54
  134. data/lib/mihari/web/endpoints/alerts.rb +94 -89
  135. data/lib/mihari/web/endpoints/artifacts.rb +115 -110
  136. data/lib/mihari/web/endpoints/configs.rb +18 -13
  137. data/lib/mihari/web/endpoints/ip_addresses.rb +21 -16
  138. data/lib/mihari/web/endpoints/rules.rb +202 -204
  139. data/lib/mihari/web/endpoints/tags.rb +41 -36
  140. data/lib/mihari/web/middleware/connection_adapter.rb +16 -9
  141. data/lib/mihari/web/middleware/error_notification_adapter.rb +17 -10
  142. data/lib/mihari/web/public/assets/{index-9cc489e6.js → index-821134e2.js} +54 -54
  143. data/lib/mihari/web/public/assets/mode-yaml-24faa242.js +8 -0
  144. data/lib/mihari/web/public/index.html +1 -1
  145. data/lib/mihari.rb +30 -13
  146. data/mihari.gemspec +9 -3
  147. data/mkdocs.yml +3 -2
  148. data/requirements.txt +1 -1
  149. metadata +44 -26
  150. data/lib/mihari/analyzers/rule.rb +0 -232
  151. data/lib/mihari/services/rule_proxy.rb +0 -182
  152. data/lib/mihari/templates/rule.yml.erb +0 -5
  153. 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,77 @@ 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
20
+ module Web
21
+ #
22
+ # Rack + Grape based web app
23
+ #
24
+ class App
25
+ def initialize
26
+ @filenames = ["", ".html", "index.html", "/index.html"]
27
+ @rack_static = Rack::Static.new(
28
+ -> { [404, {}, []] },
29
+ root: File.expand_path("./public", __dir__),
30
+ urls: ["/"]
31
+ )
32
+ end
29
33
 
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]
34
+ class << self
35
+ def instance
36
+ @instance ||= Rack::Builder.new do
37
+ use Rack::Cors do
38
+ allow do
39
+ origins "*"
40
+ resource "*", headers: :any, methods: %i[get post put delete options]
41
+ end
37
42
  end
38
- end
39
43
 
40
- use Middleware::ConnectionAdapter
41
- use Middleware::ErrorNotificationAdapter
44
+ use Middleware::ConnectionAdapter
45
+ use Middleware::ErrorNotificationAdapter
42
46
 
43
- run App.new
44
- end.to_app
45
- end
47
+ run App.new
48
+ end.to_app
49
+ end
46
50
 
47
- def run!(port: 9292, host: "localhost", threads: "0:5", verbose: false, worker_timeout: 60, open: true)
48
- url = "http://#{host}:#{port}"
51
+ def run!(port: 9292, host: "localhost", threads: "0:5", verbose: false, worker_timeout: 60, open: true)
52
+ url = "http://#{host}:#{port}"
49
53
 
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
54
+ # set maximum number of threads to use as PARALLEL_PROCESSOR_COUNT (if it is not set)
55
+ # ref. https://github.com/grosser/parallel#tips
56
+ # TODO: is this the best way?
57
+ _min_thread, max_thread = threads.split(":")
58
+ ENV["PARALLEL_PROCESSOR_COUNT"] = max_thread if ENV["PARALLEL_PROCESSOR_COUNT"].nil?
59
+ Rackup::Handler::Puma.run(
60
+ instance,
61
+ Port: port,
62
+ Host: host,
63
+ Threads: threads,
64
+ Verbose: verbose,
65
+ worker_timeout: worker_timeout
66
+ ) do |_|
67
+ Launchy.open(url) if ENV["RACK_ENV"] != "development" && open
68
+ rescue Launchy::CommandNotFoundError
69
+ # ref. https://github.com/ninoseki/mihari/issues/477
70
+ # do nothing
71
+ end
67
72
  end
68
73
  end
69
- end
70
74
 
71
- def call(env)
72
- # api
73
- api_response = API.call(env)
75
+ def call(env)
76
+ # api
77
+ api_response = API.call(env)
74
78
 
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
79
+ # Check if the App wants us to pass the response along to others
80
+ if api_response[1]["X-Cascade"] == "pass"
81
+ # static files
82
+ request_path = env["PATH_INFO"]
83
+ @filenames.each do |path|
84
+ response = @rack_static.call(env.merge("PATH_INFO" => request_path + path))
85
+ return response if response[0] != 404
86
+ end
82
87
  end
83
- end
84
88
 
85
- api_response
89
+ api_response
90
+ end
86
91
  end
87
92
  end
88
93
  end
@@ -1,110 +1,115 @@
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
+ namespace :alerts do
11
+ desc "Search alerts", {
12
+ is_array: true,
13
+ success: Entities::AlertsWithPagination,
14
+ failure: [{ code: 404, message: "Not found", model: Entities::Message }],
15
+ summary: "Search alerts"
16
+ }
17
+ params do
18
+ optional :page, type: Integer, default: 1
19
+ optional :limit, type: Integer, default: 10
16
20
 
17
- optional :artifact, type: String
18
- optional :rule_id, type: String
19
- optional :tag, type: String
21
+ optional :artifact, type: String
22
+ optional :rule_id, type: String
23
+ optional :tag, type: String
20
24
 
21
- optional :fromAt, type: DateTime
22
- optional :toAt, type: DateTime
23
- end
24
- get "/" do
25
- filter = params.to_h.to_snake_keys
25
+ optional :fromAt, type: DateTime
26
+ optional :toAt, type: DateTime
27
+ end
28
+ get "/" do
29
+ filter = params.to_h.to_snake_keys
26
30
 
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
31
+ # normalize keys
32
+ filter["artifact_data"] = filter["artifact"]
33
+ filter["tag_name"] = filter["tag"]
34
+ # symbolize hash keys
35
+ filter = filter.to_h.symbolize_keys
32
36
 
33
- search_filter_with_pagination = Structs::Filters::Alert::SearchFilterWithPagination.new(**filter)
34
- alerts = Mihari::Alert.search(search_filter_with_pagination)
35
- total = Mihari::Alert.count(search_filter_with_pagination.without_pagination)
37
+ search_filter_with_pagination = Structs::Filters::Alert::SearchFilterWithPagination.new(**filter)
38
+ alerts = Mihari::Models::Alert.search(search_filter_with_pagination)
39
+ total = Mihari::Models::Alert.count(search_filter_with_pagination.without_pagination)
36
40
 
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
- )
46
- end
41
+ present(
42
+ {
43
+ alerts: alerts,
44
+ total: total,
45
+ current_page: filter[:page].to_i,
46
+ page_size: filter[:limit].to_i
47
+ },
48
+ with: Entities::AlertsWithPagination
49
+ )
50
+ end
47
51
 
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
- end
56
- delete "/:id" do
57
- extend Dry::Monads[:result, :try]
52
+ desc "Delete an alert", {
53
+ success: Entities::Message,
54
+ failure: [{ code: 404, message: "Not found", model: Entities::Message }],
55
+ summary: "Delete an alert"
56
+ }
57
+ params do
58
+ requires :id, type: Integer
59
+ end
60
+ delete "/:id" do
61
+ extend Dry::Monads[:result, :try]
58
62
 
59
- id = params["id"].to_i
63
+ id = params["id"].to_i
60
64
 
61
- result = Try do
62
- alert = Mihari::Alert.find(id)
63
- alert.destroy
64
- end.to_result
65
+ result = Try do
66
+ alert = Mihari::Models::Alert.find(id)
67
+ alert.destroy
68
+ end.to_result
65
69
 
66
- if result.success?
67
- status 204
68
- return present({ message: "" }, with: Entities::Message)
69
- end
70
+ if result.success?
71
+ status 204
72
+ return present({ message: "" }, with: Entities::Message)
73
+ end
70
74
 
71
- failure = result.failure
72
- case failure
73
- when ActiveRecord::RecordNotFound
74
- error!({ message: "ID:#{id} is not found" }, 404)
75
- else
76
- raise failure
75
+ failure = result.failure
76
+ case failure
77
+ when ActiveRecord::RecordNotFound
78
+ error!({ message: "ID:#{id} is not found" }, 404)
79
+ else
80
+ raise failure
81
+ end
77
82
  end
78
- end
79
83
 
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]
84
+ desc "Create an alert", {
85
+ success: Entities::Alert,
86
+ summary: "Create an alert"
87
+ }
88
+ params do
89
+ requires :ruleId, type: String, documentation: { param_type: "body" }
90
+ requires :artifacts, type: Array, documentation: { type: String, is_array: true, param_type: "body" }
91
+ end
92
+ post "/" do
93
+ extend Dry::Monads[:result, :try]
90
94
 
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
95
+ result = Try do
96
+ proxy = Services::AlertProxy.new(**params.to_snake_keys)
97
+ runner = Services::AlertRunner.new(proxy)
98
+ runner.call
99
+ end.to_result
96
100
 
97
- if result.success?
98
- status 201
99
- return present(result.value!, with: Entities::Alert)
100
- end
101
+ if result.success?
102
+ status 201
103
+ return present(result.value!, with: Entities::Alert)
104
+ end
101
105
 
102
- failure = result.failure
103
- case failure
104
- when ActiveRecord::RecordNotFound
105
- error!({ message: "Rule:#{params["ruleId"]} is not found" }, 404)
106
- else
107
- raise failure
106
+ failure = result.failure
107
+ case failure
108
+ when ActiveRecord::RecordNotFound
109
+ error!({ message: "Rule:#{params["ruleId"]} is not found" }, 404)
110
+ else
111
+ raise failure
112
+ end
108
113
  end
109
114
  end
110
115
  end
@@ -1,122 +1,127 @@
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
21
- artifact = Mihari::Artifact.includes(
22
- :autonomous_system,
23
- :geolocation,
24
- :whois_record,
25
- :dns_records,
26
- :reverse_dns_names
27
- ).find(id)
28
- # TODO: improve queries
29
- alert_ids = Mihari::Artifact.where(data: artifact.data).pluck(:alert_id)
30
- tag_ids = Mihari::Tagging.where(alert_id: alert_ids).pluck(:tag_id)
31
- tag_names = Mihari::Tag.where(id: tag_ids).distinct.pluck(:name)
32
-
33
- artifact.tags = tag_names
34
-
35
- 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
4
+ module Web
5
+ module Endpoints
6
+ #
7
+ # Artifact API endpoint
8
+ #
9
+ class Artifacts < Grape::API
10
+ namespace :artifacts do
11
+ desc "Get an artifact", {
12
+ success: Entities::Artifact,
13
+ failure: [{ code: 404, message: "Not found", model: Entities::Message }],
14
+ summary: "Get an artifact"
15
+ }
16
+ params do
17
+ requires :id, type: Integer
46
18
  end
47
- end
48
-
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
63
- artifact = Mihari::Artifact.includes(
64
- :autonomous_system,
65
- :geolocation,
66
- :whois_record,
67
- :dns_records,
68
- :reverse_dns_names,
69
- :cpes,
70
- :ports
71
- ).find(id)
72
-
73
- artifact.enrich_all
74
- artifact.save
75
- end.to_result
76
-
77
- if result.success?
78
- status 201
79
- return present({ message: "" }, with: Entities::Message)
19
+ get "/:id" do
20
+ extend Dry::Monads[:result, :try]
21
+
22
+ id = params[:id].to_i
23
+
24
+ result = Try do
25
+ artifact = Mihari::Models::Artifact.includes(
26
+ :autonomous_system,
27
+ :geolocation,
28
+ :whois_record,
29
+ :dns_records,
30
+ :reverse_dns_names
31
+ ).find(id)
32
+ # TODO: improve queries
33
+ alert_ids = Mihari::Models::Artifact.where(data: artifact.data).pluck(:alert_id)
34
+ tag_ids = Mihari::Models::Tagging.where(alert_id: alert_ids).pluck(:tag_id)
35
+ tag_names = Mihari::Models::Tag.where(id: tag_ids).distinct.pluck(:name)
36
+
37
+ artifact.tags = tag_names
38
+
39
+ artifact
40
+ end.to_result
41
+
42
+ return present(result.value!, with: Entities::Artifact) if result.success?
43
+
44
+ failure = result.failure
45
+ case failure
46
+ when ActiveRecord::RecordNotFound
47
+ error!({ message: "ID:#{id} is not found" }, 404)
48
+ else
49
+ raise failure
50
+ end
80
51
  end
81
52
 
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
53
+ desc "Enrich an artifact", {
54
+ success: Entities::Message,
55
+ failure: [{ code: 404, message: "Not found", model: Entities::Message }],
56
+ summary: "Enrich an artifact"
57
+ }
58
+ params do
59
+ requires :id, type: Integer
88
60
  end
89
- end
90
-
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
98
- end
99
- delete "/:id" do
100
- extend Dry::Monads[:result, :try]
101
-
102
- id = params["id"].to_i
103
-
104
- result = Try do
105
- alert = Mihari::Artifact.find(id)
106
- alert.destroy
107
- end.to_result
108
-
109
- if result.success?
110
- status 204
111
- return present({ message: "" }, with: Entities::Message)
61
+ get "/:id/enrich" do
62
+ extend Dry::Monads[:result, :try]
63
+
64
+ id = params["id"].to_i
65
+
66
+ result = Try do
67
+ artifact = Mihari::Models::Artifact.includes(
68
+ :autonomous_system,
69
+ :geolocation,
70
+ :whois_record,
71
+ :dns_records,
72
+ :reverse_dns_names,
73
+ :cpes,
74
+ :ports
75
+ ).find(id)
76
+
77
+ artifact.enrich_all
78
+ artifact.save
79
+ end.to_result
80
+
81
+ if result.success?
82
+ status 201
83
+ return present({ message: "" }, with: Entities::Message)
84
+ end
85
+
86
+ failure = result.failure
87
+ case failure
88
+ when ActiveRecord::RecordNotFound
89
+ error!({ message: "ID:#{id} is not found" }, 404)
90
+ else
91
+ raise failure
92
+ end
112
93
  end
113
94
 
114
- failure = result.failure
115
- case failure
116
- when ActiveRecord::RecordNotFound
117
- error!({ message: "ID:#{id} is not found" }, 404)
118
- else
119
- raise failure
95
+ desc "Delete an artifact", {
96
+ success: Entities::Message,
97
+ failure: [{ code: 404, message: "Not found", model: Entities::Message }],
98
+ summary: "Delete an artifact"
99
+ }
100
+ params do
101
+ requires :id, type: Integer
102
+ end
103
+ delete "/:id" do
104
+ extend Dry::Monads[:result, :try]
105
+
106
+ id = params["id"].to_i
107
+
108
+ result = Try do
109
+ alert = Mihari::Models::Artifact.find(id)
110
+ alert.destroy
111
+ end.to_result
112
+
113
+ if result.success?
114
+ status 204
115
+ return present({ message: "" }, with: Entities::Message)
116
+ end
117
+
118
+ failure = result.failure
119
+ case failure
120
+ when ActiveRecord::RecordNotFound
121
+ error!({ message: "ID:#{id} is not found" }, 404)
122
+ else
123
+ raise failure
124
+ end
120
125
  end
121
126
  end
122
127
  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