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.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +6 -7
- data/config.ru +1 -1
- data/lib/mihari/errors.rb +2 -0
- data/lib/mihari/mixins/configuration.rb +12 -2
- data/lib/mihari/models/alert.rb +1 -8
- data/lib/mihari/models/artifact.rb +3 -0
- data/lib/mihari/structs/ipinfo.rb +3 -4
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/api.rb +43 -0
- data/lib/mihari/web/app.rb +47 -29
- data/lib/mihari/web/endpoints/alerts.rb +74 -0
- data/lib/mihari/web/endpoints/artifacts.rb +92 -0
- data/lib/mihari/web/endpoints/command.rb +32 -0
- data/lib/mihari/web/endpoints/configs.rb +22 -0
- data/lib/mihari/web/endpoints/ip_addresses.rb +27 -0
- data/lib/mihari/web/endpoints/sources.rb +18 -0
- data/lib/mihari/web/endpoints/tags.rb +38 -0
- data/lib/mihari/web/entities/alert.rb +23 -0
- data/lib/mihari/web/entities/artifact.rb +24 -0
- data/lib/mihari/web/entities/autonomous_system.rb +9 -0
- data/lib/mihari/web/entities/command.rb +14 -0
- data/lib/mihari/web/entities/config.rb +16 -0
- data/lib/mihari/web/entities/dns.rb +10 -0
- data/lib/mihari/web/entities/geolocation.rb +10 -0
- data/lib/mihari/web/entities/ip_address.rb +13 -0
- data/lib/mihari/web/entities/message.rb +9 -0
- data/lib/mihari/web/entities/reverse_dns.rb +9 -0
- data/lib/mihari/web/entities/source.rb +9 -0
- data/lib/mihari/web/entities/tag.rb +13 -0
- data/lib/mihari/web/entities/whois.rb +16 -0
- data/lib/mihari/web/public/grape.rb +73 -0
- data/lib/mihari/web/public/index.html +1 -1
- data/lib/mihari/web/public/redoc-static.html +53 -27
- data/lib/mihari/web/public/static/js/app.6b636b62.js +50 -0
- data/lib/mihari/web/public/static/js/app.6b636b62.js.map +1 -0
- data/lib/mihari.rb +2 -12
- data/mihari.gemspec +7 -5
- data/sig/lib/mihari/web/app.rbs +1 -1
- metadata +108 -74
- data/lib/mihari/serializers/alert.rb +0 -14
- data/lib/mihari/serializers/artifact.rb +0 -18
- data/lib/mihari/serializers/autonomous_system.rb +0 -9
- data/lib/mihari/serializers/dns.rb +0 -11
- data/lib/mihari/serializers/geolocation.rb +0 -11
- data/lib/mihari/serializers/reverse_dns.rb +0 -11
- data/lib/mihari/serializers/tag.rb +0 -11
- data/lib/mihari/serializers/whois.rb +0 -11
- data/lib/mihari/web/controllers/alerts_controller.rb +0 -67
- data/lib/mihari/web/controllers/analyzers_controller.rb +0 -38
- data/lib/mihari/web/controllers/artifacts_controller.rb +0 -94
- data/lib/mihari/web/controllers/base_controller.rb +0 -22
- data/lib/mihari/web/controllers/command_controller.rb +0 -26
- data/lib/mihari/web/controllers/config_controller.rb +0 -13
- data/lib/mihari/web/controllers/ip_address_controller.rb +0 -21
- data/lib/mihari/web/controllers/sources_controller.rb +0 -12
- data/lib/mihari/web/controllers/tags_controller.rb +0 -30
- data/lib/mihari/web/helpers/json.rb +0 -53
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d2e67ff3e1ae2bf328a9a77ef7c9a88dce779749c422490a97106d3529a9a3b1
|
4
|
+
data.tar.gz: a71ee49c8fcb0b06e180739a588930783dabbc40078d234f791314dd3f8af9b4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 41c30a97d80e6d96f425230401b7f0ee947979dff6b2a8c458bb72c38ed34c2577bed1b007f25e2607cacc1c70f6d7de183722bac0b604f0c54f582758db1e53
|
7
|
+
data.tar.gz: 6cb1d47e4efec3fb54bd93ddacf5e8f55e423d3b28c1e3c6ae2ea852898b88d3995d8eb767e89535ba50bfffb6b533ce946068eb348cfbd33afe41ace587a5df
|
data/.github/workflows/test.yml
CHANGED
@@ -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
|
-
|
56
|
-
|
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
data/lib/mihari/errors.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/mihari/models/alert.rb
CHANGED
@@ -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
|
-
|
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
|
#
|
data/lib/mihari/version.rb
CHANGED
@@ -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
|
data/lib/mihari/web/app.rb
CHANGED
@@ -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 "
|
7
|
+
require "rack/cors"
|
7
8
|
|
8
|
-
require "
|
9
|
+
require "grape"
|
10
|
+
require "grape-entity"
|
11
|
+
require "grape-swagger"
|
12
|
+
require "grape-swagger-entity"
|
9
13
|
|
10
|
-
require "mihari/web/
|
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
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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(
|
44
|
-
Launchy.open
|
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,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
|