mihari 3.9.0 → 3.10.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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +6 -7
  3. data/README.md +1 -0
  4. data/config.ru +1 -1
  5. data/lib/mihari/analyzers/greynoise.rb +65 -0
  6. data/lib/mihari/analyzers/rule.rb +1 -0
  7. data/lib/mihari/analyzers/shodan.rb +11 -5
  8. data/lib/mihari/cli/analyzer.rb +2 -0
  9. data/lib/mihari/commands/greynoise.rb +21 -0
  10. data/lib/mihari/commands/search.rb +3 -2
  11. data/lib/mihari/errors.rb +2 -0
  12. data/lib/mihari/mixins/configuration.rb +12 -2
  13. data/lib/mihari/models/alert.rb +1 -8
  14. data/lib/mihari/models/artifact.rb +3 -0
  15. data/lib/mihari/schemas/configuration.rb +3 -2
  16. data/lib/mihari/structs/greynoise.rb +55 -0
  17. data/lib/mihari/structs/ipinfo.rb +3 -4
  18. data/lib/mihari/structs/shodan.rb +6 -6
  19. data/lib/mihari/types.rb +1 -0
  20. data/lib/mihari/version.rb +1 -1
  21. data/lib/mihari/web/api.rb +43 -0
  22. data/lib/mihari/web/app.rb +47 -29
  23. data/lib/mihari/web/endpoints/alerts.rb +74 -0
  24. data/lib/mihari/web/endpoints/artifacts.rb +92 -0
  25. data/lib/mihari/web/endpoints/command.rb +32 -0
  26. data/lib/mihari/web/endpoints/configs.rb +22 -0
  27. data/lib/mihari/web/endpoints/ip_addresses.rb +27 -0
  28. data/lib/mihari/web/endpoints/sources.rb +18 -0
  29. data/lib/mihari/web/endpoints/tags.rb +38 -0
  30. data/lib/mihari/web/entities/alert.rb +23 -0
  31. data/lib/mihari/web/entities/artifact.rb +24 -0
  32. data/lib/mihari/web/entities/autonomous_system.rb +9 -0
  33. data/lib/mihari/web/entities/command.rb +14 -0
  34. data/lib/mihari/web/entities/config.rb +16 -0
  35. data/lib/mihari/web/entities/dns.rb +10 -0
  36. data/lib/mihari/web/entities/geolocation.rb +10 -0
  37. data/lib/mihari/web/entities/ip_address.rb +13 -0
  38. data/lib/mihari/web/entities/message.rb +9 -0
  39. data/lib/mihari/web/entities/reverse_dns.rb +9 -0
  40. data/lib/mihari/web/entities/source.rb +9 -0
  41. data/lib/mihari/web/entities/tag.rb +13 -0
  42. data/lib/mihari/web/entities/whois.rb +16 -0
  43. data/lib/mihari/web/public/grape.rb +73 -0
  44. data/lib/mihari/web/public/index.html +1 -1
  45. data/lib/mihari/web/public/redoc-static.html +54 -28
  46. data/lib/mihari/web/public/static/js/app.0a0cc502.js +21 -0
  47. data/lib/mihari/web/public/static/js/app.0a0cc502.js.map +1 -0
  48. data/lib/mihari/web/public/static/js/app.14008741.js +21 -0
  49. data/lib/mihari/web/public/static/js/app.14008741.js.map +1 -0
  50. data/lib/mihari/web/public/static/js/app.6b636b62.js +50 -0
  51. data/lib/mihari/web/public/static/js/app.6b636b62.js.map +1 -0
  52. data/lib/mihari/web/public/static/js/app.fbc19869.js +21 -0
  53. data/lib/mihari/web/public/static/js/app.fbc19869.js.map +1 -0
  54. data/lib/mihari.rb +7 -14
  55. data/mihari.gemspec +9 -5
  56. data/sig/lib/mihari/structs/greynoise.rbs +30 -0
  57. data/sig/lib/mihari/structs/shodan.rbs +3 -3
  58. data/sig/lib/mihari/web/app.rbs +1 -1
  59. metadata +146 -74
  60. data/lib/mihari/serializers/alert.rb +0 -14
  61. data/lib/mihari/serializers/artifact.rb +0 -18
  62. data/lib/mihari/serializers/autonomous_system.rb +0 -9
  63. data/lib/mihari/serializers/dns.rb +0 -11
  64. data/lib/mihari/serializers/geolocation.rb +0 -11
  65. data/lib/mihari/serializers/reverse_dns.rb +0 -11
  66. data/lib/mihari/serializers/tag.rb +0 -11
  67. data/lib/mihari/serializers/whois.rb +0 -11
  68. data/lib/mihari/web/controllers/alerts_controller.rb +0 -67
  69. data/lib/mihari/web/controllers/analyzers_controller.rb +0 -38
  70. data/lib/mihari/web/controllers/artifacts_controller.rb +0 -94
  71. data/lib/mihari/web/controllers/base_controller.rb +0 -22
  72. data/lib/mihari/web/controllers/command_controller.rb +0 -26
  73. data/lib/mihari/web/controllers/config_controller.rb +0 -13
  74. data/lib/mihari/web/controllers/ip_address_controller.rb +0 -21
  75. data/lib/mihari/web/controllers/sources_controller.rb +0 -12
  76. data/lib/mihari/web/controllers/tags_controller.rb +0 -30
  77. 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: 0fcea779559f641125a388f63f0aa2d39979842f9b66f116194c36fe6410c56c
4
+ data.tar.gz: ceefe0fa86e5d3c8066bab202f7dc1dfb2b21017554a2b63c5a18f567e45aadb
5
5
  SHA512:
6
- metadata.gz: da1c99c406086a9d8d029f8421a3316a5a6976c5b8fbc839a910bd0d61f04a3297614582974dbda925722308d206db9d2889fe44ae213df2650f1482cc15e918
7
- data.tar.gz: edf130cb5027bbb6abfea72b150266b8768494b627162da814c93b38d54fe2597a870d78cd9f8f33238512e5a3205cb84a67b9da0589b71393b67fef062ad9b1
6
+ metadata.gz: 834c4c815ddf9c14b9047c9a8d9ed8ce56e3f115c16b797c4ebf40205529f7c94fe7ddda8f9c7f5cc0675c65123ac0957d28a0bfe51108d2492efc230f509dd8
7
+ data.tar.gz: 03ed05a672e17bfce72706de94aa2a339053f9b6545907c1e71cf60987be047e1af6bc6d4e95a16f6b6bb371de2ff3b65602ae4c3c9eb2eab3f70c2ace0735cf
@@ -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/README.md CHANGED
@@ -38,6 +38,7 @@ Mihari supports the following services by default.
38
38
  - [crt.sh](https://crt.sh/)
39
39
  - [DN Pedia](https://dnpedia.com/)
40
40
  - [dnstwister](https://dnstwister.report/)
41
+ - [GreyNoise](https://www.greynoise.io/)
41
42
  - [Onyphe](https://onyphe.io)
42
43
  - [OTX](https://otx.alienvault.com/)
43
44
  - [PassiveTotal](https://community.riskiq.com/)
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
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "greynoise"
4
+
5
+ module Mihari
6
+ module Analyzers
7
+ class GreyNoise < Base
8
+ param :query
9
+ option :title, default: proc { "GreyNoise search" }
10
+ option :description, default: proc { "query = #{query}" }
11
+ option :tags, default: proc { [] }
12
+
13
+ def artifacts
14
+ res = Structs::GreyNoise::Response.from_dynamic!(search)
15
+ res.data.map do |datum|
16
+ build_artifact datum
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ PAGE_SIZE = 10_000
23
+
24
+ def configuration_keys
25
+ %w[greynoise_api_key]
26
+ end
27
+
28
+ def api
29
+ @api ||= ::GreyNoise::API.new(key: Mihari.config.greynoise_api_key)
30
+ end
31
+
32
+ #
33
+ # Search
34
+ #
35
+ # @return [Hash]
36
+ #
37
+ def search
38
+ api.experimental.gnql(query, size: PAGE_SIZE)
39
+ end
40
+
41
+ #
42
+ # Build an artifact from a GreyNoise search API response
43
+ #
44
+ # @param [Structs::GreyNoise::Datum] datum
45
+ #
46
+ # @return [Artifact]
47
+ #
48
+ def build_artifact(datum)
49
+ as = AutonomousSystem.new(asn: normalize_asn(datum.metadata.asn))
50
+
51
+ geolocation = Geolocation.new(
52
+ country: datum.metadata.country,
53
+ country_code: datum.metadata.country_code
54
+ )
55
+
56
+ Artifact.new(
57
+ data: datum.ip,
58
+ source: source,
59
+ autonomous_system: as,
60
+ geolocation: geolocation
61
+ )
62
+ end
63
+ end
64
+ end
65
+ end
@@ -11,6 +11,7 @@ module Mihari
11
11
  "crtsh" => Crtsh,
12
12
  "dnpedia" => DNPedia,
13
13
  "dnstwister" => DNSTwister,
14
+ "greynoise" => GreyNoise,
14
15
  "onyphe" => Onyphe,
15
16
  "otx" => OTX,
16
17
  "passivetotal" => PassiveTotal,
@@ -58,6 +58,7 @@ module Mihari
58
58
  responses = []
59
59
  (1..Float::INFINITY).each do |page|
60
60
  res = search_with_page(query, page: page)
61
+
61
62
  break unless res
62
63
 
63
64
  responses << res
@@ -78,11 +79,16 @@ module Mihari
78
79
  # @return [Artifact]
79
80
  #
80
81
  def build_artifact(match)
81
- as = AutonomousSystem.new(asn: normalize_asn(match.asn))
82
- geolocation = Geolocation.new(
83
- country: match.location.country_name,
84
- country_code: match.location.country_code
85
- )
82
+ as = nil
83
+ as = AutonomousSystem.new(asn: normalize_asn(match.asn)) unless match.asn.nil?
84
+
85
+ geolocation = nil
86
+ if !match.location.country_name.nil? && !match.location.country_code.nil?
87
+ geolocation = Geolocation.new(
88
+ country: match.location.country_name,
89
+ country_code: match.location.country_code
90
+ )
91
+ end
86
92
 
87
93
  Artifact.new(
88
94
  data: match.ip_str,
@@ -6,6 +6,7 @@ require "mihari/commands/circl"
6
6
  require "mihari/commands/crtsh"
7
7
  require "mihari/commands/dnpedia"
8
8
  require "mihari/commands/dnstwister"
9
+ require "mihari/commands/greynoise"
9
10
  require "mihari/commands/onyphe"
10
11
  require "mihari/commands/otx"
11
12
  require "mihari/commands/passivetotal"
@@ -33,6 +34,7 @@ module Mihari
33
34
  include Mihari::Commands::Crtsh
34
35
  include Mihari::Commands::DNPedia
35
36
  include Mihari::Commands::DNSTwister
37
+ include Mihari::Commands::GreyNoise
36
38
  include Mihari::Commands::JSON
37
39
  include Mihari::Commands::Onyphe
38
40
  include Mihari::Commands::OTX
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mihari
4
+ module Commands
5
+ module GreyNoise
6
+ def self.included(thor)
7
+ thor.class_eval do
8
+ desc "greynoise [QUERY]", "GreyNoise search"
9
+ method_option :title, type: :string, desc: "title"
10
+ method_option :description, type: :string, desc: "description"
11
+ method_option :tags, type: :array, desc: "tags"
12
+ def greynoise(query)
13
+ with_error_handling do
14
+ run_analyzer Analyzers::GreyNoise, query: query, options: options
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -10,6 +10,9 @@ module Mihari
10
10
  desc "search [RULE]", "Search by a rule"
11
11
  method_option :config, type: :string, desc: "Path to the config file"
12
12
  def search_by_rule(rule)
13
+ # load configuration
14
+ load_configuration
15
+
13
16
  # convert str(YAML) to hash or str(path/YAML file) to hash
14
17
  rule = load_rule(rule)
15
18
 
@@ -77,8 +80,6 @@ module Mihari
77
80
  # @return [nil]
78
81
  #
79
82
  def run_rule_analyzer(analyzer, ignore_old_artifacts: false, ignore_threshold: 0)
80
- load_configuration
81
-
82
83
  analyzer.ignore_old_artifacts = ignore_old_artifacts
83
84
  analyzer.ignore_threshold = ignore_threshold
84
85
 
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
  #
@@ -13,6 +13,8 @@ module Mihari
13
13
  optional(:censys_secret).value(:string)
14
14
  optional(:circl_passive_password).value(:string)
15
15
  optional(:circl_passive_username).value(:string)
16
+ optional(:database).value(:string)
17
+ optional(:greynoise_api_key).value(:string)
16
18
  optional(:ipinfo_api_key).value(:string)
17
19
  optional(:misp_api_endpoint).value(:string)
18
20
  optional(:misp_api_key).value(:string)
@@ -30,10 +32,9 @@ module Mihari
30
32
  optional(:thehive_api_key).value(:string)
31
33
  optional(:urlscan_api_key).value(:string)
32
34
  optional(:virustotal_api_key).value(:string)
33
- optional(:zoomeye_api_key).value(:string)
34
35
  optional(:webhook_url).value(:string)
35
36
  optional(:webhook_use_json_body).value(:bool)
36
- optional(:database).value(:string)
37
+ optional(:zoomeye_api_key).value(:string)
37
38
  end
38
39
 
39
40
  class ConfigurationContract < Dry::Validation::Contract
@@ -0,0 +1,55 @@
1
+ require "json"
2
+ require "dry/struct"
3
+
4
+ module Mihari
5
+ module Structs
6
+ module GreyNoise
7
+ class Metadata < Dry::Struct
8
+ attribute :country, Types::String
9
+ attribute :country_code, Types::String
10
+ attribute :asn, Types::String
11
+
12
+ def self.from_dynamic!(d)
13
+ d = Types::Hash[d]
14
+ new(
15
+ country: d.fetch("country"),
16
+ country_code: d.fetch("country_code"),
17
+ asn: d.fetch("asn")
18
+ )
19
+ end
20
+ end
21
+
22
+ class Datum < Dry::Struct
23
+ attribute :ip, Types::String
24
+ attribute :metadata, Metadata
25
+
26
+ def self.from_dynamic!(d)
27
+ d = Types::Hash[d]
28
+ new(
29
+ ip: d.fetch("ip"),
30
+ metadata: Metadata.from_dynamic!(d.fetch("metadata"))
31
+ )
32
+ end
33
+ end
34
+
35
+ class Response < Dry::Struct
36
+ attribute :complete, Types::Bool
37
+ attribute :count, Types::Int
38
+ attribute :data, Types.Array(Datum)
39
+ attribute :message, Types::String
40
+ attribute :query, Types::String
41
+
42
+ def self.from_dynamic!(d)
43
+ d = Types::Hash[d]
44
+ new(
45
+ complete: d.fetch("complete"),
46
+ count: d.fetch("count"),
47
+ data: d.fetch("data").map { |x| Datum.from_dynamic!(x) },
48
+ message: d.fetch("message"),
49
+ query: d.fetch("query")
50
+ )
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -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(
@@ -5,20 +5,20 @@ module Mihari
5
5
  module Structs
6
6
  module Shodan
7
7
  class Location < Dry::Struct
8
- attribute :country_code, Types::String
9
- attribute :country_name, Types::String
8
+ attribute :country_code, Types::String.optional
9
+ attribute :country_name, Types::String.optional
10
10
 
11
11
  def self.from_dynamic!(d)
12
12
  d = Types::Hash[d]
13
13
  new(
14
- country_code: d.fetch("country_code"),
15
- country_name: d.fetch("country_name")
14
+ country_code: d["country_code"],
15
+ country_name: d["country_name"]
16
16
  )
17
17
  end
18
18
  end
19
19
 
20
20
  class Match < Dry::Struct
21
- attribute :asn, Types::String
21
+ attribute :asn, Types::String.optional
22
22
  attribute :hostnames, Types.Array(Types::String)
23
23
  attribute :location, Location
24
24
  attribute :domains, Types.Array(Types::String)
@@ -27,7 +27,7 @@ module Mihari
27
27
  def self.from_dynamic!(d)
28
28
  d = Types::Hash[d]
29
29
  new(
30
- asn: d.fetch("asn"),
30
+ asn: d["asn"],
31
31
  hostnames: d.fetch("hostnames"),
32
32
  location: Location.from_dynamic!(d.fetch("location")),
33
33
  domains: d.fetch("domains"),
data/lib/mihari/types.rb CHANGED
@@ -19,6 +19,7 @@ module Mihari
19
19
  "circl",
20
20
  "dnpedia",
21
21
  "dnstwister",
22
+ "greynoise",
22
23
  "onyphe",
23
24
  "otx",
24
25
  "passivetotal",
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- VERSION = "3.9.0"
4
+ VERSION = "3.10.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