mihari 3.9.0 → 3.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +6 -7
  3. data/config.ru +1 -1
  4. data/lib/mihari/errors.rb +2 -0
  5. data/lib/mihari/mixins/configuration.rb +12 -2
  6. data/lib/mihari/models/alert.rb +1 -8
  7. data/lib/mihari/models/artifact.rb +3 -0
  8. data/lib/mihari/structs/ipinfo.rb +3 -4
  9. data/lib/mihari/version.rb +1 -1
  10. data/lib/mihari/web/api.rb +43 -0
  11. data/lib/mihari/web/app.rb +47 -29
  12. data/lib/mihari/web/endpoints/alerts.rb +74 -0
  13. data/lib/mihari/web/endpoints/artifacts.rb +92 -0
  14. data/lib/mihari/web/endpoints/command.rb +32 -0
  15. data/lib/mihari/web/endpoints/configs.rb +22 -0
  16. data/lib/mihari/web/endpoints/ip_addresses.rb +27 -0
  17. data/lib/mihari/web/endpoints/sources.rb +18 -0
  18. data/lib/mihari/web/endpoints/tags.rb +38 -0
  19. data/lib/mihari/web/entities/alert.rb +23 -0
  20. data/lib/mihari/web/entities/artifact.rb +24 -0
  21. data/lib/mihari/web/entities/autonomous_system.rb +9 -0
  22. data/lib/mihari/web/entities/command.rb +14 -0
  23. data/lib/mihari/web/entities/config.rb +16 -0
  24. data/lib/mihari/web/entities/dns.rb +10 -0
  25. data/lib/mihari/web/entities/geolocation.rb +10 -0
  26. data/lib/mihari/web/entities/ip_address.rb +13 -0
  27. data/lib/mihari/web/entities/message.rb +9 -0
  28. data/lib/mihari/web/entities/reverse_dns.rb +9 -0
  29. data/lib/mihari/web/entities/source.rb +9 -0
  30. data/lib/mihari/web/entities/tag.rb +13 -0
  31. data/lib/mihari/web/entities/whois.rb +16 -0
  32. data/lib/mihari/web/public/grape.rb +73 -0
  33. data/lib/mihari/web/public/index.html +1 -1
  34. data/lib/mihari/web/public/redoc-static.html +53 -27
  35. data/lib/mihari/web/public/static/js/app.6b636b62.js +50 -0
  36. data/lib/mihari/web/public/static/js/app.6b636b62.js.map +1 -0
  37. data/lib/mihari.rb +2 -12
  38. data/mihari.gemspec +7 -5
  39. data/sig/lib/mihari/web/app.rbs +1 -1
  40. metadata +108 -74
  41. data/lib/mihari/serializers/alert.rb +0 -14
  42. data/lib/mihari/serializers/artifact.rb +0 -18
  43. data/lib/mihari/serializers/autonomous_system.rb +0 -9
  44. data/lib/mihari/serializers/dns.rb +0 -11
  45. data/lib/mihari/serializers/geolocation.rb +0 -11
  46. data/lib/mihari/serializers/reverse_dns.rb +0 -11
  47. data/lib/mihari/serializers/tag.rb +0 -11
  48. data/lib/mihari/serializers/whois.rb +0 -11
  49. data/lib/mihari/web/controllers/alerts_controller.rb +0 -67
  50. data/lib/mihari/web/controllers/analyzers_controller.rb +0 -38
  51. data/lib/mihari/web/controllers/artifacts_controller.rb +0 -94
  52. data/lib/mihari/web/controllers/base_controller.rb +0 -22
  53. data/lib/mihari/web/controllers/command_controller.rb +0 -26
  54. data/lib/mihari/web/controllers/config_controller.rb +0 -13
  55. data/lib/mihari/web/controllers/ip_address_controller.rb +0 -21
  56. data/lib/mihari/web/controllers/sources_controller.rb +0 -12
  57. data/lib/mihari/web/controllers/tags_controller.rb +0 -30
  58. data/lib/mihari/web/helpers/json.rb +0 -53
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 47f7a6231abe8eee1a1a4e4bc6f905d5f08fba2b68b1435d1621ab85e9e583c0
4
- data.tar.gz: 591a8c71130095d6ac963fb333179d087f00727f45ab545e4a674a3c42a2af46
3
+ metadata.gz: d2e67ff3e1ae2bf328a9a77ef7c9a88dce779749c422490a97106d3529a9a3b1
4
+ data.tar.gz: a71ee49c8fcb0b06e180739a588930783dabbc40078d234f791314dd3f8af9b4
5
5
  SHA512:
6
- metadata.gz: da1c99c406086a9d8d029f8421a3316a5a6976c5b8fbc839a910bd0d61f04a3297614582974dbda925722308d206db9d2889fe44ae213df2650f1482cc15e918
7
- data.tar.gz: edf130cb5027bbb6abfea72b150266b8768494b627162da814c93b38d54fe2597a870d78cd9f8f33238512e5a3205cb84a67b9da0589b71393b67fef062ad9b1
6
+ metadata.gz: 41c30a97d80e6d96f425230401b7f0ee947979dff6b2a8c458bb72c38ed34c2577bed1b007f25e2607cacc1c70f6d7de183722bac0b604f0c54f582758db1e53
7
+ data.tar.gz: 6cb1d47e4efec3fb54bd93ddacf5e8f55e423d3b28c1e3c6ae2ea852898b88d3995d8eb767e89535ba50bfffb6b533ce946068eb348cfbd33afe41ace587a5df
@@ -43,17 +43,16 @@ jobs:
43
43
 
44
44
  steps:
45
45
  - uses: actions/checkout@v2
46
- - name: Set up Ruby 2.7
47
- uses: ruby/setup-ruby@v1
48
- with:
49
- ruby-version: ${{ matrix.ruby }}
50
- bundler-cache: true
51
46
 
52
47
  - name: Install dependencies
53
48
  run: |
54
49
  sudo apt-get -yqq install libpq-dev libmysqlclient-dev
55
- gem install bundler
56
- bundle install
50
+
51
+ - name: Set up Ruby
52
+ uses: ruby/setup-ruby@v1
53
+ with:
54
+ ruby-version: ${{ matrix.ruby }}
55
+ bundler-cache: true
57
56
 
58
57
  - name: Test with PostgreSQL
59
58
  env:
data/config.ru CHANGED
@@ -4,4 +4,4 @@ require "./lib/mihari"
4
4
  # set rack env as development
5
5
  ENV["RACK_ENV"] ||= "development"
6
6
 
7
- run Mihari::App
7
+ run Mihari::App.instance
data/lib/mihari/errors.rb CHANGED
@@ -6,4 +6,6 @@ module Mihari
6
6
  class InvalidInputError < Error; end
7
7
 
8
8
  class RetryableError < Error; end
9
+
10
+ class FileNotFoundError < Error; end
9
11
  end
@@ -80,10 +80,20 @@ module Mihari
80
80
  end
81
81
  end
82
82
 
83
+ #
84
+ # Load configuration file
85
+ #
86
+ # @param [String] path
87
+ #
88
+ # @return [Hash]
89
+ #
83
90
  def _load_config(path)
84
- return YAML.safe_load(File.read(path), symbolize_names: true) if Pathname(path).exist?
91
+ unless Pathname(path).exist?
92
+ puts "#{path} does not exist".colorize(:red)
93
+ raise FileNotFoundError
94
+ end
85
95
 
86
- YAML.safe_load(path, symbolize_names: true)
96
+ YAML.safe_load(File.read(path), symbolize_names: true)
87
97
  end
88
98
  end
89
99
  end
@@ -30,14 +30,7 @@ module Mihari
30
30
 
31
31
  # TODO: improve queires
32
32
  alert_ids = relation.limit(limit).offset(offset).order(id: :desc).pluck(:id).uniq
33
- alerts = includes(:artifacts, :tags).where(id: [alert_ids]).order(id: :desc)
34
-
35
- alerts.map do |alert|
36
- json = Serializers::AlertSerializer.new(alert).as_json
37
- json[:artifacts] = json[:artifacts] || []
38
- json[:tags] = json[:tags] || []
39
- json
40
- end
33
+ includes(:artifacts, :tags).where(id: [alert_ids]).order(id: :desc)
41
34
  end
42
35
 
43
36
  #
@@ -29,10 +29,13 @@ module Mihari
29
29
 
30
30
  validates_with ArtifactValidator
31
31
 
32
+ attr_accessor :tags
33
+
32
34
  def initialize(attributes)
33
35
  super
34
36
 
35
37
  self.data_type = TypeChecker.type(data)
38
+ self.tags = []
36
39
  end
37
40
 
38
41
  #
@@ -18,10 +18,9 @@ module Mihari
18
18
  d = Types::Hash[d]
19
19
 
20
20
  asn = nil
21
- org = d["org"]
22
- unless org.nil?
23
- asn = org.split.first
24
- asn = normalize_asn(asn)
21
+ asn_ = d.dig("asn", "asn")
22
+ unless asn_.nil?
23
+ asn = normalize_asn(asn_)
25
24
  end
26
25
 
27
26
  new(
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- VERSION = "3.9.0"
4
+ VERSION = "3.9.1"
5
5
  end
@@ -0,0 +1,43 @@
1
+ # Entities
2
+ require "mihari/web/entities/message"
3
+
4
+ require "mihari/web/entities/autonomous_system"
5
+ require "mihari/web/entities/command"
6
+ require "mihari/web/entities/config"
7
+ require "mihari/web/entities/dns"
8
+ require "mihari/web/entities/geolocation"
9
+ require "mihari/web/entities/ip_address"
10
+ require "mihari/web/entities/reverse_dns"
11
+ require "mihari/web/entities/source"
12
+ require "mihari/web/entities/tag"
13
+ require "mihari/web/entities/whois"
14
+
15
+ require "mihari/web/entities/artifact"
16
+
17
+ require "mihari/web/entities/alert"
18
+
19
+ # Endpoints
20
+ require "mihari/web/endpoints/alerts"
21
+ require "mihari/web/endpoints/artifacts"
22
+ require "mihari/web/endpoints/command"
23
+ require "mihari/web/endpoints/configs"
24
+ require "mihari/web/endpoints/ip_addresses"
25
+ require "mihari/web/endpoints/sources"
26
+ require "mihari/web/endpoints/tags"
27
+
28
+ module Mihari
29
+ class API < Grape::API
30
+ prefix "api"
31
+ format :json
32
+
33
+ mount Endpoints::Alerts
34
+ mount Endpoints::Artifacts
35
+ mount Endpoints::Command
36
+ mount Endpoints::Configs
37
+ mount Endpoints::IPAddresses
38
+ mount Endpoints::Sources
39
+ mount Endpoints::Tags
40
+
41
+ add_swagger_documentation(api_version: "v1", info: { title: "Mihari API" })
42
+ end
43
+ end
@@ -2,46 +2,47 @@
2
2
 
3
3
  require "launchy"
4
4
  require "rack"
5
+ require "rack/contrib"
5
6
  require "rack/handler/puma"
6
- require "sinatra"
7
+ require "rack/cors"
7
8
 
8
- require "mihari/web/helpers/json"
9
+ require "grape"
10
+ require "grape-entity"
11
+ require "grape-swagger"
12
+ require "grape-swagger-entity"
9
13
 
10
- require "mihari/web/controllers/base_controller"
11
-
12
- require "mihari/web/controllers/alerts_controller"
13
- require "mihari/web/controllers/analyzers_controller"
14
- require "mihari/web/controllers/artifacts_controller"
15
- require "mihari/web/controllers/command_controller"
16
- require "mihari/web/controllers/config_controller"
17
- require "mihari/web/controllers/ip_address_controller"
18
- require "mihari/web/controllers/sources_controller"
19
- require "mihari/web/controllers/tags_controller"
14
+ require "mihari/web/api"
20
15
 
21
16
  module Mihari
22
- class App < Sinatra::Base
23
- set :root, File.dirname(__FILE__)
24
- set :public_folder, File.join(root, "public")
25
-
26
- get "/" do
27
- send_file File.join(settings.public_folder, "index.html")
17
+ class App
18
+ def initialize
19
+ @filenames = ["", ".html", "index.html", "/index.html"]
20
+ @rack_static = ::Rack::Static.new(
21
+ -> { [404, {}, []] },
22
+ root: File.expand_path("./public", __dir__),
23
+ urls: ["/"]
24
+ )
28
25
  end
29
26
 
30
- use Mihari::Controllers::AlertsController
31
- use Mihari::Controllers::AnalyzersController
32
- use Mihari::Controllers::ArtifactsController
33
- use Mihari::Controllers::CommandController
34
- use Mihari::Controllers::ConfigController
35
- use Mihari::Controllers::IPAddressController
36
- use Mihari::Controllers::SourcesController
37
- use Mihari::Controllers::TagsController
38
-
39
27
  class << self
28
+ def instance
29
+ @instance ||= Rack::Builder.new do
30
+ use Rack::Cors do
31
+ allow do
32
+ origins "*"
33
+ resource "*", headers: :any, methods: [:get, :post, :put, :delete, :options]
34
+ end
35
+ end
36
+
37
+ run App.new
38
+ end.to_app
39
+ end
40
+
40
41
  def run!(port: 9292, host: "localhost", threads: "0:16", verbose: false)
41
42
  url = "http://#{host}:#{port}"
42
43
 
43
- Rack::Handler::Puma.run(self, Port: port, Host: host, Threads: threads, Verbose: verbose) do |server|
44
- Launchy.open url
44
+ Rack::Handler::Puma.run(instance, Port: port, Host: host, Threads: threads, Verbose: verbose) do |server|
45
+ Launchy.open(url) if ENV["RACK_ENV"] != "development"
45
46
 
46
47
  [:INT, :TERM].each do |sig|
47
48
  trap(sig) do
@@ -51,5 +52,22 @@ module Mihari
51
52
  end
52
53
  end
53
54
  end
55
+
56
+ def call(env)
57
+ # api
58
+ api_response = API.call(env)
59
+
60
+ # Check if the App wants us to pass the response along to others
61
+ if api_response[1]["X-Cascade"] == "pass"
62
+ # static files
63
+ request_path = env["PATH_INFO"]
64
+ @filenames.each do |path|
65
+ response = @rack_static.call(env.merge("PATH_INFO" => request_path + path))
66
+ return response if response[0] != 404
67
+ end
68
+ end
69
+
70
+ api_response
71
+ end
54
72
  end
55
73
  end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
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::Alert,
10
+ failure: [{ code: 404, message: "Not found", model: Entities::Message }]
11
+ }
12
+ params do
13
+ optional :page, type: Integer
14
+ optional :artifact, type: String
15
+ optional :description, type: String
16
+ optional :source, type: String
17
+ optional :tag, type: String
18
+
19
+ optional :fromAt, type: DateTime
20
+ optional :toAt, type: DateTime
21
+
22
+ optional :asn, type: Integer
23
+ optional :dnsRecord, type: String
24
+ optional :reverseDnsName, type: String
25
+ end
26
+ get "/" do
27
+ filter = params.to_h.to_snake_keys
28
+
29
+ # set page & limit
30
+ page = filter["page"] || 1
31
+ filter["page"] = page.to_i
32
+
33
+ limit = 10
34
+ filter["limit"] = 10
35
+
36
+ # normalize keys
37
+ filter["artifact_data"] = filter["artifact"]
38
+ filter["tag_name"] = filter["tag"]
39
+
40
+ # symbolize hash keys
41
+ filter = filter.to_h.transform_keys(&:to_sym)
42
+
43
+ search_filter_with_pagenation = Structs::Alert::SearchFilterWithPagination.new(**filter)
44
+ alerts = Mihari::Alert.search(search_filter_with_pagenation)
45
+ total = Mihari::Alert.count(search_filter_with_pagenation.without_pagination)
46
+
47
+ present({ alerts: alerts, total: total, current_page: page, page_size: limit }, with: Entities::AlertsWithPagination)
48
+ end
49
+
50
+ desc "Delete an alert", {
51
+ success: Entities::Message,
52
+ failure: [{ code: 404, message: "Not found", model: Entities::Message }]
53
+ }
54
+ params do
55
+ requires :id, type: Integer
56
+ end
57
+ delete "/:id" do
58
+ id = params["id"].to_i
59
+
60
+ begin
61
+ alert = Mihari::Alert.find(id)
62
+ rescue ActiveRecord::RecordNotFound
63
+ error!({ message: "ID:#{id} is not found" }, 404)
64
+ end
65
+
66
+ alert.destroy
67
+
68
+ status 204
69
+ present({ message: "" }, with: Entities::Message)
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
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
+ }
11
+ params do
12
+ requires :id, type: Integer
13
+ end
14
+ get "/:id" do
15
+ id = params[:id].to_i
16
+
17
+ begin
18
+ artifact = Mihari::Artifact.includes(
19
+ :autonomous_system,
20
+ :geolocation,
21
+ :whois_record,
22
+ :dns_records,
23
+ :reverse_dns_names
24
+ ).find(id)
25
+ rescue ActiveRecord::RecordNotFound
26
+ error!({ message: "ID:#{id} is not found" }, 404)
27
+ end
28
+
29
+ # TODO: improve queries
30
+ alert_ids = Mihari::Artifact.where(data: artifact.data).pluck(:alert_id)
31
+ tag_ids = Mihari::Tagging.where(alert_id: alert_ids).pluck(:tag_id)
32
+ tag_names = Mihari::Tag.where(id: tag_ids).distinct.pluck(:name)
33
+
34
+ artifact.tags = tag_names
35
+
36
+ present artifact, with: Entities::Artifact
37
+ end
38
+
39
+ desc "Enrich an artifact", {
40
+ success: Entities::Message,
41
+ failure: [{ code: 404, message: "Not found", model: Entities::Message }]
42
+ }
43
+ params do
44
+ requires :id, type: Integer
45
+ end
46
+ get "/:id/enrich" do
47
+ id = params["id"].to_i
48
+
49
+ begin
50
+ artifact = Mihari::Artifact.includes(
51
+ :autonomous_system,
52
+ :geolocation,
53
+ :whois_record,
54
+ :dns_records,
55
+ :reverse_dns_names
56
+ ).find(id)
57
+ rescue ActiveRecord::RecordNotFound
58
+ error!({ message: "ID:#{id} is not found" }, 404)
59
+ end
60
+
61
+ artifact.enrich_all
62
+ artifact.save
63
+
64
+ status 201
65
+ present({ message: "" }, with: Entities::Message)
66
+ end
67
+
68
+ desc "Delete an artifact", {
69
+ success: Entities::Message,
70
+ failure: [{ code: 404, message: "Not found", model: Entities::Message }]
71
+ }
72
+ params do
73
+ requires :id, type: Integer
74
+ end
75
+ delete "/:id" do
76
+ id = params["id"].to_i
77
+
78
+ begin
79
+ alert = Mihari::Artifact.find(id)
80
+ rescue ActiveRecord::RecordNotFound
81
+ error!({ message: "ID:#{id} is not found" }, 404)
82
+ end
83
+
84
+ alert.destroy
85
+
86
+ status 204
87
+ present({ message: "" }, with: Entities::Message)
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "safe_shell"
4
+
5
+ module Mihari
6
+ module Endpoints
7
+ class Command < Grape::API
8
+ namespace :command do
9
+ desc "Run a command", {
10
+ success: Entities::CommandResult,
11
+ failure: [{ code: 400, message: "Bad request", model: Entities::Message }]
12
+ }
13
+ params do
14
+ requires :command, type: String, documentation: { param_type: "body" }
15
+ end
16
+ post "/" do
17
+ command = params[:command]
18
+ if command.nil?
19
+ error!({ message: "command is required" }, 400)
20
+ end
21
+
22
+ command = command.split
23
+
24
+ output = SafeShell.execute("mihari", *command)
25
+ success = $?.success?
26
+
27
+ present({ output: output, success: success }, with: Entities::CommandResult)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
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
+ }
11
+ get "/" do
12
+ statuses = Status.check
13
+
14
+ configs = statuses.map do |key, value|
15
+ { name: key, status: value }
16
+ end
17
+ present(configs, with: Entities::Config)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
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
+ }
11
+ params do
12
+ requires :ip, type: String, regexp: /\A[0-9.]+\z/
13
+ end
14
+ get "/:ip", requirements: { ip: %r{[^/]+} } do
15
+ ip = params[:ip].to_s
16
+
17
+ data = Enrichers::IPInfo.query(ip)
18
+ if data.nil?
19
+ error!({ message: "IP:#{ip} is not found" }, 404)
20
+ else
21
+ present data, with: Entities::IPAddress
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mihari
4
+ module Endpoints
5
+ class Sources < Grape::API
6
+ namespace :sources do
7
+ desc "Get sources", {
8
+ is_array: true,
9
+ success: Entities::Sources
10
+ }
11
+ get "/" do
12
+ sources = Mihari::Alert.distinct.pluck(:source)
13
+ present({ sources: sources }, with: Entities::Sources)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
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
+ }
11
+ get "/" do
12
+ tags = Mihari::Tag.distinct.pluck(:name)
13
+ present({ tags: tags }, with: Entities::Tags)
14
+ end
15
+
16
+ desc "Delete a tag", {
17
+ success: Entities::Message,
18
+ failure: [{ code: 404, message: "Not found", model: Entities::Message }]
19
+ }
20
+ params do
21
+ requires :name, type: String
22
+ end
23
+ delete "/:name" do
24
+ name = params[:name].to_s
25
+
26
+ begin
27
+ Mihari::Tag.where(name: name).destroy_all
28
+ rescue ActiveRecord::RecordNotFound
29
+ error!({ message: "Name:#{name} is not found" }, 404)
30
+ end
31
+
32
+ status 204
33
+ present({ message: "" }, with: Entities::Message)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mihari
4
+ module Entities
5
+ class Alert < Grape::Entity
6
+ expose :id, documentation: { type: Integer, required: true }
7
+ expose :title, documentation: { type: String, required: true }
8
+ expose :description, documentation: { type: String, required: true }
9
+ expose :source, documentation: { type: String, required: true }
10
+ expose :created_at, documentation: { type: DateTime, required: true }, as: :createdAt
11
+
12
+ expose :artifacts, using: Entities::Artifact, documentation: { type: Entities::Artifact, is_array: true }
13
+ expose :tags, using: Entities::Tag, documentation: { type: Entities::Tag, is_array: true, required: true }
14
+ end
15
+
16
+ class AlertsWithPagination < Grape::Entity
17
+ expose :alerts, using: Entities::Alert, documentation: { type: Entities::Alert, is_array: true, required: true }
18
+ expose :total, documentation: { type: Integer, required: true }
19
+ expose :current_page, documentation: { type: Integer, required: true }, as: :currentPage
20
+ expose :page_size, documentation: { type: Integer, required: true }, as: :pageSize
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mihari
4
+ module Entities
5
+ class Artifact < Grape::Entity
6
+ expose :id, documentation: { type: Integer, required: true }
7
+ expose :data, documentation: { type: String, required: true }
8
+ expose :data_type, documentation: { type: String, required: true }, as: :dataType
9
+ expose :source, documentation: { type: String, required: true }
10
+ expose :tags, documentation: { type: String, is_array: true }
11
+
12
+ expose :autonomous_system, using: Entities::AutonomousSystem, documentation: { type: Entities::AutonomousSystem, required: false }, as: :autonomousSystem
13
+ expose :geolocation, using: Entities::Geolocation, documentation: { type: Entities::Geolocation, required: false }
14
+ expose :whois_record, using: Entities::WhoisRecord, documentation: { type: Entities::WhoisRecord, required: false }, as: :whoisRecord
15
+
16
+ expose :reverse_dns_names, using: Entities::ReverseDnsName, documentation: { type: Entities::ReverseDnsName, is_array: true, required: false }, as: :reverseDnsNames do |status, _options|
17
+ status.reverse_dns_names.length > 0 ? status.reverse_dns_names : nil
18
+ end
19
+ expose :dns_records, using: Entities::DnsRecord, documentation: { type: Entities::DnsRecord, is_array: true, required: false }, as: :dnsRecords do |status, _options|
20
+ status.dns_records.length > 0 ? status.dns_records : nil
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mihari
4
+ module Entities
5
+ class AutonomousSystem < Grape::Entity
6
+ expose :asn, documentation: { type: Integer, required: true }
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mihari
4
+ module Entities
5
+ class CommandInput < Grape::Entity
6
+ expose :command, documentation: { type: String, required: true }
7
+ end
8
+
9
+ class CommandResult < Grape::Entity
10
+ expose :output, documentation: { type: String, required: true }
11
+ expose :success, documentation: { type: Grape::API::Boolean, required: true }
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mihari
4
+ module Entities
5
+ class ConfigStatus < Grape::Entity
6
+ expose :type, documentation: { type: String, required: true }
7
+ expose :values, documentation: { type: String, is_array: true, required: true }
8
+ expose :is_configured, documentation: { type: Grape::API::Boolean, required: true }, as: :isConfigured
9
+ end
10
+
11
+ class Config < Grape::Entity
12
+ expose :name, documentation: { type: String, required: true }
13
+ expose :status, using: Entities::ConfigStatus, documentation: { type: ConfigStatus, required: true }
14
+ end
15
+ end
16
+ end