mihari 5.2.3 → 5.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -1
  3. data/README.md +0 -10
  4. data/Rakefile +7 -1
  5. data/build_frontend.sh +2 -10
  6. data/frontend/.eslintrc.cjs +22 -0
  7. data/frontend/.gitignore +31 -0
  8. data/frontend/.prettierrc.json +8 -0
  9. data/frontend/README.md +3 -0
  10. data/frontend/env.d.ts +5 -0
  11. data/frontend/index.html +21 -0
  12. data/frontend/package-lock.json +8650 -0
  13. data/frontend/package.json +64 -0
  14. data/frontend/public/favicon.ico +0 -0
  15. data/frontend/scripts/swagger_doc_to_yaml.rb +23 -0
  16. data/frontend/src/App.vue +27 -0
  17. data/frontend/src/api-helper.ts +111 -0
  18. data/frontend/src/api.ts +105 -0
  19. data/frontend/src/components/ErrorMessage.vue +32 -0
  20. data/frontend/src/components/Loading.vue +15 -0
  21. data/frontend/src/components/Navbar.vue +42 -0
  22. data/frontend/src/components/Pagination.vue +119 -0
  23. data/frontend/src/components/alert/Alert.vue +87 -0
  24. data/frontend/src/components/alert/Alerts.vue +64 -0
  25. data/frontend/src/components/alert/AlertsWithPagination.vue +91 -0
  26. data/frontend/src/components/alert/AlertsWrapper.vue +134 -0
  27. data/frontend/src/components/alert/Form.vue +184 -0
  28. data/frontend/src/components/artifact/AS.vue +29 -0
  29. data/frontend/src/components/artifact/Artifact.vue +304 -0
  30. data/frontend/src/components/artifact/ArtifactTag.vue +64 -0
  31. data/frontend/src/components/artifact/ArtifactTags.vue +29 -0
  32. data/frontend/src/components/artifact/ArtifactWrapper.vue +59 -0
  33. data/frontend/src/components/artifact/CPEs.vue +23 -0
  34. data/frontend/src/components/artifact/DnsRecords.vue +38 -0
  35. data/frontend/src/components/artifact/Ports.vue +23 -0
  36. data/frontend/src/components/artifact/ReverseDnsNames.vue +31 -0
  37. data/frontend/src/components/artifact/Tags.vue +29 -0
  38. data/frontend/src/components/artifact/WhoisRecord.vue +47 -0
  39. data/frontend/src/components/config/Configs.vue +65 -0
  40. data/frontend/src/components/config/ConfigsWrapper.vue +34 -0
  41. data/frontend/src/components/link/Link.vue +32 -0
  42. data/frontend/src/components/link/Links.vue +42 -0
  43. data/frontend/src/components/rule/EditRule.vue +74 -0
  44. data/frontend/src/components/rule/EditRuleWrapper.vue +50 -0
  45. data/frontend/src/components/rule/Form.vue +160 -0
  46. data/frontend/src/components/rule/InputForm.vue +86 -0
  47. data/frontend/src/components/rule/NewRule.vue +60 -0
  48. data/frontend/src/components/rule/Rule.vue +106 -0
  49. data/frontend/src/components/rule/RuleWrapper.vue +55 -0
  50. data/frontend/src/components/rule/Rules.vue +84 -0
  51. data/frontend/src/components/rule/RulesWrapper.vue +127 -0
  52. data/frontend/src/components/rule/YAML.vue +44 -0
  53. data/frontend/src/components/tag/Tag.vue +65 -0
  54. data/frontend/src/components/tag/Tags.vue +37 -0
  55. data/frontend/src/countries.ts +350 -0
  56. data/frontend/src/index.ts +20 -0
  57. data/frontend/src/links/anyrun.ts +19 -0
  58. data/frontend/src/links/base.ts +14 -0
  59. data/frontend/src/links/censys.ts +20 -0
  60. data/frontend/src/links/crtsh.ts +20 -0
  61. data/frontend/src/links/dnslytics.ts +38 -0
  62. data/frontend/src/links/greynoise.ts +20 -0
  63. data/frontend/src/links/index.ts +40 -0
  64. data/frontend/src/links/intezer.ts +20 -0
  65. data/frontend/src/links/otx.ts +33 -0
  66. data/frontend/src/links/securitytrails.ts +38 -0
  67. data/frontend/src/links/shodan.ts +20 -0
  68. data/frontend/src/links/urlscan.ts +50 -0
  69. data/frontend/src/links/virustotal.ts +72 -0
  70. data/frontend/src/main.ts +11 -0
  71. data/frontend/src/router/index.ts +57 -0
  72. data/frontend/src/rule.ts +14 -0
  73. data/frontend/src/shims-vue.d.ts +6 -0
  74. data/frontend/src/swagger.yaml +737 -0
  75. data/frontend/src/types.ts +188 -0
  76. data/frontend/src/utils.ts +54 -0
  77. data/frontend/src/views/Alerts.vue +20 -0
  78. data/frontend/src/views/Artifact.vue +44 -0
  79. data/frontend/src/views/Configs.vue +20 -0
  80. data/frontend/src/views/EditRule.vue +44 -0
  81. data/frontend/src/views/NewRule.vue +26 -0
  82. data/frontend/src/views/Rule.vue +44 -0
  83. data/frontend/src/views/Rules.vue +20 -0
  84. data/frontend/tests/utils.spec.ts +9 -0
  85. data/frontend/tsconfig.app.json +21 -0
  86. data/frontend/tsconfig.json +14 -0
  87. data/frontend/tsconfig.node.json +13 -0
  88. data/frontend/tsconfig.vitest.json +12 -0
  89. data/frontend/vite.config.ts +24 -0
  90. data/frontend/vitest.config.ts +21 -0
  91. data/lefthook.yml +12 -0
  92. data/lib/mihari/analyzers/base.rb +63 -12
  93. data/lib/mihari/analyzers/binaryedge.rb +10 -15
  94. data/lib/mihari/analyzers/censys.rb +12 -15
  95. data/lib/mihari/analyzers/circl.rb +10 -10
  96. data/lib/mihari/analyzers/crtsh.rb +10 -6
  97. data/lib/mihari/analyzers/dnstwister.rb +6 -8
  98. data/lib/mihari/analyzers/feed.rb +21 -10
  99. data/lib/mihari/analyzers/greynoise.rb +10 -20
  100. data/lib/mihari/analyzers/onyphe.rb +9 -14
  101. data/lib/mihari/analyzers/otx.rb +8 -9
  102. data/lib/mihari/analyzers/passivetotal.rb +10 -10
  103. data/lib/mihari/analyzers/pulsedive.rb +21 -31
  104. data/lib/mihari/analyzers/rule.rb +8 -29
  105. data/lib/mihari/analyzers/securitytrails.rb +8 -6
  106. data/lib/mihari/analyzers/shodan.rb +8 -13
  107. data/lib/mihari/analyzers/urlscan.rb +15 -20
  108. data/lib/mihari/analyzers/virustotal.rb +16 -26
  109. data/lib/mihari/analyzers/virustotal_intelligence.rb +11 -17
  110. data/lib/mihari/analyzers/zoomeye.rb +12 -17
  111. data/lib/mihari/commands/search.rb +16 -7
  112. data/lib/mihari/config.rb +133 -0
  113. data/lib/mihari/constants.rb +3 -0
  114. data/lib/mihari/emitters/slack.rb +13 -3
  115. data/lib/mihari/entities/rule.rb +1 -1
  116. data/lib/mihari/entities/tag.rb +1 -1
  117. data/lib/mihari/errors.rb +1 -1
  118. data/lib/mihari/http.rb +2 -3
  119. data/lib/mihari/schemas/analyzer.rb +4 -7
  120. data/lib/mihari/schemas/rule.rb +1 -1
  121. data/lib/mihari/structs/config.rb +39 -16
  122. data/lib/mihari/structs/rule.rb +1 -1
  123. data/lib/mihari/type_checker.rb +6 -6
  124. data/lib/mihari/version.rb +1 -1
  125. data/lib/mihari/web/endpoints/configs.rb +5 -1
  126. data/lib/mihari/web/public/assets/{index-eed1bcd8.css → index-2ba8f0a6.css} +1 -1
  127. data/lib/mihari/web/public/assets/index-71285b15.js +50 -0
  128. data/lib/mihari/web/public/index.html +2 -2
  129. data/lib/mihari/web/public/redoc-static.html +388 -2193
  130. data/lib/mihari.rb +9 -59
  131. data/mihari.gemspec +13 -13
  132. metadata +112 -69
  133. data/.gitmodules +0 -0
  134. data/.overcommit.yml +0 -12
  135. data/lib/mihari/web/public/assets/index-cbe1734c.js +0 -50
@@ -3,23 +3,18 @@
3
3
  module Mihari
4
4
  module Analyzers
5
5
  class Shodan < Base
6
- param :query
7
-
8
- option :interval, default: proc { 0 }
9
-
10
6
  # @return [String, nil]
11
7
  attr_reader :api_key
12
8
 
13
- # @return [Integer]
14
- attr_reader :interval
15
-
16
- # @return [String]
17
- attr_reader :query
18
-
19
- def initialize(*args, **kwargs)
20
- super(*args, **kwargs)
9
+ #
10
+ # @param [String] query
11
+ # @param [Hash, nil] options
12
+ # @param [String, nil] api_key
13
+ #
14
+ def initialize(query, options: nil, api_key: nil)
15
+ super(query, options: options)
21
16
 
22
- @api_key = kwargs[:api_key] || Mihari.config.shodan_api_key
17
+ @api_key = api_key || Mihari.config.shodan_api_key
23
18
  end
24
19
 
25
20
  def artifacts
@@ -3,35 +3,30 @@
3
3
  module Mihari
4
4
  module Analyzers
5
5
  class Urlscan < Base
6
- param :query
7
-
8
- option :allowed_data_types, default: proc { SUPPORTED_DATA_TYPES }
9
-
10
- option :interval, default: proc { 0 }
11
-
12
6
  SUPPORTED_DATA_TYPES = %w[url domain ip].freeze
13
7
  SIZE = 1000
14
8
 
15
9
  # @return [String, nil]
16
10
  attr_reader :api_key
17
11
 
18
- # @return [String]
19
- attr_reader :query
20
-
21
- # @return [Integer]
22
- attr_reader :interval
23
-
24
- # @return [String]
12
+ # @return [Array<String>]
25
13
  attr_reader :allowed_data_types
26
14
 
27
- def initialize(*args, **kwargs)
28
- super
15
+ #
16
+ # @param [String] query
17
+ # @param [Hash, nil] options
18
+ # @param [String, nil] api_key
19
+ # @param [Array<String>] allowed_data_types
20
+ #
21
+ def initialize(query, options: nil, api_key: nil, allowed_data_types: SUPPORTED_DATA_TYPES)
22
+ super(query, options: options)
29
23
 
30
- unless valid_allowed_data_types?
31
- raise InvalidInputError, "allowed_data_types should be any of url, domain and ip."
32
- end
24
+ @api_key = api_key || Mihari.config.urlscan_api_key
25
+ @allowed_data_types = allowed_data_types
26
+
27
+ return if valid_allowed_data_types?
33
28
 
34
- @api_key = kwargs[:api_key] || Mihari.config.urlscan_api_key
29
+ raise InvalidInputError, "allowed_data_types should be any of url, domain and ip."
35
30
  end
36
31
 
37
32
  def artifacts
@@ -82,7 +77,7 @@ module Mihari
82
77
  search_after = res.results.last.sort.join(",")
83
78
 
84
79
  # sleep #{interval} seconds to avoid the rate limitation (if it is set)
85
- sleep interval
80
+ sleep(interval) if interval
86
81
  end
87
82
 
88
83
  responses
@@ -5,28 +5,34 @@ module Mihari
5
5
  class VirusTotal < Base
6
6
  include Mixins::Refang
7
7
 
8
- param :query
9
-
10
8
  # @return [String]
11
9
  attr_reader :type
12
10
 
13
11
  # @return [String, nil]
14
12
  attr_reader :api_key
15
13
 
16
- # @return [String]
17
- attr_reader :query
18
-
19
- def initialize(*args, **kwargs)
20
- super(*args, **kwargs)
14
+ #
15
+ # @param [String] query
16
+ # @param [Hash, nil] options
17
+ # @param [String, nil] api_key
18
+ #
19
+ def initialize(query, options: nil, api_key: nil)
20
+ super(refang(query), options: options)
21
21
 
22
- @query = refang(query)
23
22
  @type = TypeChecker.type(query)
24
23
 
25
- @api_key = kwargs[:api_key] || Mihari.config.virustotal_api_key
24
+ @api_key = api_key || Mihari.config.virustotal_api_key
26
25
  end
27
26
 
28
27
  def artifacts
29
- search || []
28
+ case type
29
+ when "domain"
30
+ domain_search
31
+ when "ip"
32
+ ip_search
33
+ else
34
+ raise InvalidInputError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
35
+ end
30
36
  end
31
37
 
32
38
  private
@@ -48,22 +54,6 @@ module Mihari
48
54
  %w[ip domain].include? type
49
55
  end
50
56
 
51
- #
52
- # Search
53
- #
54
- # @return [Array<Mihari::Artifact>]
55
- #
56
- def search
57
- case type
58
- when "domain"
59
- domain_search
60
- when "ip"
61
- ip_search
62
- else
63
- raise InvalidInputError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
64
- end
65
- end
66
-
67
57
  #
68
58
  # Domain search
69
59
  #
@@ -3,25 +3,18 @@
3
3
  module Mihari
4
4
  module Analyzers
5
5
  class VirusTotalIntelligence < Base
6
- param :query
7
-
8
- option :interval, default: proc { 0 }
9
-
10
6
  # @return [String, nil]
11
7
  attr_reader :api_key
12
8
 
13
- # @return [String]
14
- attr_reader :query
15
-
16
- # @return [Integer]
17
- attr_reader :interval
18
-
19
- def initialize(*args, **kwargs)
20
- super
21
-
22
- @query = query
9
+ #
10
+ # @param [String] query
11
+ # @param [Hash, nll] options
12
+ # @param [String, nil] api_key
13
+ #
14
+ def initialize(query, options: nil, api_key: nil)
15
+ super(query, options: options)
23
16
 
24
- @api_key = kwargs[:api_key] || Mihari.config.virustotal_api_key
17
+ @api_key = api_key || Mihari.config.virustotal_api_key
25
18
  end
26
19
 
27
20
  def artifacts
@@ -53,13 +46,14 @@ module Mihari
53
46
  responses = []
54
47
 
55
48
  loop do
56
- response = Structs::VirusTotalIntelligence::Response.from_dynamic!(client.intel_search(query, cursor: cursor))
49
+ response = Structs::VirusTotalIntelligence::Response.from_dynamic!(client.intel_search(query,
50
+ cursor: cursor))
57
51
  responses << response
58
52
  break if response.meta.cursor.nil?
59
53
 
60
54
  cursor = response.meta.cursor
61
55
  # sleep #{interval} seconds to avoid the rate limitation (if it is set)
62
- sleep interval
56
+ sleep(interval) if interval
63
57
  end
64
58
 
65
59
  responses
@@ -3,28 +3,23 @@
3
3
  module Mihari
4
4
  module Analyzers
5
5
  class ZoomEye < Base
6
- param :query
7
-
8
- option :type, default: proc { "host" }
9
-
10
- option :interval, default: proc { 0 }
11
-
12
6
  # @return [String, nil]
13
7
  attr_reader :api_key
14
8
 
15
- # @return [String]
16
- attr_reader :query
17
-
18
9
  # @return [String]
19
10
  attr_reader :type
20
11
 
21
- # @return [Integer]
22
- attr_reader :interval
23
-
24
- def initialize(*args, **kwargs)
25
- super(*args, **kwargs)
12
+ #
13
+ # @param [String] query
14
+ # @param [Hash, nil] options
15
+ # @param [String, nil] api_key
16
+ # @param [String] type
17
+ #
18
+ def initialize(query, options: nil, api_key: nil, type: "host")
19
+ super(query, options: options)
26
20
 
27
- @api_key = kwargs[:api_key] || Mihari.config.zoomeye_api_key
21
+ @type = type
22
+ @api_key = api_key || Mihari.config.zoomeye_api_key
28
23
  end
29
24
 
30
25
  def artifacts
@@ -109,7 +104,7 @@ module Mihari
109
104
  break if total <= page * PAGE_SIZE
110
105
 
111
106
  # sleep #{interval} seconds to avoid the rate limitation (if it is set)
112
- sleep interval
107
+ sleep(interval) if interval
113
108
  end
114
109
  convert_responses responses.compact
115
110
  end
@@ -142,7 +137,7 @@ module Mihari
142
137
  break if total <= page * PAGE_SIZE
143
138
 
144
139
  # sleep #{interval} seconds to avoid the rate limitation (if it is set)
145
- sleep interval
140
+ sleep(interval) if interval
146
141
  end
147
142
  convert_responses responses.compact
148
143
  end
@@ -13,7 +13,7 @@ module Mihari
13
13
  # @return [Boolean]
14
14
  attr_reader :force_overwrite
15
15
 
16
- def initialize(rule:, force_overwrite:)
16
+ def initialize(rule, force_overwrite:)
17
17
  @rule = rule
18
18
  @force_overwrite = force_overwrite
19
19
  end
@@ -37,14 +37,23 @@ module Mihari
37
37
  end
38
38
 
39
39
  def run
40
+ begin
41
+ analyzer = rule.analyzer
42
+ rescue ConfigurationError => e
43
+ # if there is a configuration error, output that error without the stack trace
44
+ Mihari.logger.error e.to_s
45
+ return
46
+ end
47
+
40
48
  with_error_notification do
41
- alert = rule.analyzer.run
42
- if alert
43
- data = Mihari::Entities::Alert.represent(alert)
44
- puts JSON.pretty_generate(data.as_json)
45
- else
49
+ alert = analyzer.run
50
+ if alert.nil?
46
51
  Mihari.logger.info "There is no new artifact found"
52
+ return
47
53
  end
54
+
55
+ data = Mihari::Entities::Alert.represent(alert)
56
+ puts JSON.pretty_generate(data.as_json)
48
57
  end
49
58
  end
50
59
  end
@@ -69,7 +78,7 @@ module Mihari
69
78
  end
70
79
 
71
80
  force_overwrite = options["force_overwrite"] || false
72
- wrapper = RuleWrapper.new(rule: rule, force_overwrite: force_overwrite)
81
+ wrapper = RuleWrapper.new(rule, force_overwrite: force_overwrite)
73
82
 
74
83
  if wrapper.diff? && !force_overwrite
75
84
  message = "There is diff in the rule (#{rule.id}). Are you sure you want to overwrite the rule? (y/n)"
@@ -0,0 +1,133 @@
1
+ module Mihari
2
+ class Config
3
+ # @return [String, nil]
4
+ attr_accessor :binaryedge_api_key
5
+
6
+ # @return [String, nil]
7
+ attr_accessor :censys_id
8
+
9
+ # @return [String, nil]
10
+ attr_accessor :censys_secret
11
+
12
+ # @return [String, nil]
13
+ attr_accessor :circl_passive_password
14
+
15
+ # @return [String, nil]
16
+ attr_accessor :circl_passive_username
17
+
18
+ # @return [URI]
19
+ attr_accessor :database_url
20
+
21
+ # @return [String, nil]
22
+ attr_accessor :greynoise_api_key
23
+
24
+ # @return [String, nil]
25
+ attr_accessor :ipinfo_api_key
26
+
27
+ # @return [String, nil]
28
+ attr_accessor :misp_url
29
+
30
+ # @return [String, nil]
31
+ attr_accessor :misp_api_key
32
+
33
+ # @return [String, nil]
34
+ attr_accessor :onyphe_api_key
35
+
36
+ # @return [String, nil]
37
+ attr_accessor :otx_api_key
38
+
39
+ # @return [String, nil]
40
+ attr_accessor :passivetotal_api_key
41
+
42
+ # @return [String, nil]
43
+ attr_accessor :passivetotal_username
44
+
45
+ # @return [String, nil]
46
+ attr_accessor :pulsedive_api_key
47
+
48
+ # @return [String, nil]
49
+ attr_accessor :securitytrails_api_key
50
+
51
+ # @return [String, nil]
52
+ attr_accessor :shodan_api_key
53
+
54
+ # @return [String, nil]
55
+ attr_accessor :slack_channel
56
+
57
+ # @return [String, nil]
58
+ attr_accessor :slack_webhook_url
59
+
60
+ # @return [String, nil]
61
+ attr_accessor :thehive_url
62
+
63
+ # @return [String, nil]
64
+ attr_accessor :thehive_api_key
65
+
66
+ # @return [String, nil]
67
+ attr_accessor :thehive_api_version
68
+
69
+ # @return [String, nil]
70
+ attr_accessor :urlscan_api_key
71
+
72
+ # @return [String, nil]
73
+ attr_accessor :virustotal_api_key
74
+
75
+ # @return [String, nil]
76
+ attr_accessor :zoomeye_api_key
77
+
78
+ # @return [String, nil]
79
+ attr_accessor :sentry_dsn
80
+
81
+ # @return [String, nil]
82
+ attr_accessor :hide_config_values
83
+
84
+ def initialize
85
+ @binaryedge_api_key = ENV.fetch("BINARYEDGE_API_KEY", nil)
86
+
87
+ @censys_id = ENV.fetch("CENSYS_ID", nil)
88
+ @censys_secret = ENV.fetch("CENSYS_SECRET", nil)
89
+
90
+ @circl_passive_password = ENV.fetch("CIRCL_PASSIVE_PASSWORD", nil)
91
+ @circl_passive_username = ENV.fetch("CIRCL_PASSIVE_USERNAME", nil)
92
+
93
+ @database_url = URI(ENV.fetch("DATABASE_URL", "sqlite3:///mihari.db"))
94
+
95
+ @greynoise_api_key = ENV.fetch("GREYNOISE_API_KEY", nil)
96
+
97
+ @ipinfo_api_key = ENV.fetch("IPINFO_API_KEY", nil)
98
+
99
+ @misp_url = ENV.fetch("MISP_URL", nil)
100
+ @misp_api_key = ENV.fetch("MISP_API_KEY", nil)
101
+
102
+ @onyphe_api_key = ENV.fetch("ONYPHE_API_KEY", nil)
103
+
104
+ @otx_api_key = ENV.fetch("OTX_API_KEY", nil)
105
+
106
+ @passivetotal_api_key = ENV.fetch("PASSIVETOTAL_API_KEY", nil)
107
+ @passivetotal_username = ENV.fetch("PASSIVETOTAL_USERNAME", nil)
108
+
109
+ @pulsedive_api_key = ENV.fetch("PULSEDIVE_API_KEY", nil)
110
+
111
+ @securitytrails_api_key = ENV.fetch("SECURITYTRAILS_API_KEY", nil)
112
+
113
+ @shodan_api_key = ENV.fetch("SHODAN_API_KEY", nil)
114
+
115
+ @slack_channel = ENV.fetch("SLACK_CHANNEL", nil)
116
+ @slack_webhook_url = ENV.fetch("SLACK_WEBHOOK_URL", nil)
117
+
118
+ @thehive_url = ENV.fetch("THEHIVE_URL", nil)
119
+ @thehive_api_key = ENV.fetch("THEHIVE_API_KEY", nil)
120
+ @thehive_api_version = ENV.fetch("THEHIVE_API_VERSION", nil)
121
+
122
+ @urlscan_api_key = ENV.fetch("URLSCAN_API_KEY", nil)
123
+
124
+ @virustotal_api_key = ENV.fetch("VIRUSTOTAL_API_KEY", nil)
125
+
126
+ @zoomeye_api_key = ENV.fetch("ZOOMEYE_API_KEY", nil)
127
+
128
+ @sentry_dsn = ENV.fetch("SENTRY_DSN", nil)
129
+
130
+ @hide_config_values = ENV.fetch("HIDE_CONFIG_VALUES", false)
131
+ end
132
+ end
133
+ end
@@ -6,4 +6,7 @@ module Mihari
6
6
  DEFAULT_EMITTERS = %w[database misp slack the_hive].map { |name| { emitter: name } }.freeze
7
7
 
8
8
  DEFAULT_ENRICHERS = %w[whois ipinfo shodan google_public_dns].map { |name| { enricher: name } }.freeze
9
+
10
+ DEFAULT_RETRY_TIMES = 3
11
+ DEFAULT_RETRY_INTERVAL = 5
9
12
  end
@@ -8,10 +8,20 @@ module Mihari
8
8
  class Attachment
9
9
  include Memist::Memoizable
10
10
 
11
- extend Dry::Initializer
11
+ # @return [String]
12
+ attr_reader :data
13
+
14
+ # @return [String]
15
+ attr_reader :data_type
12
16
 
13
- option :data
14
- option :data_type
17
+ #
18
+ # @param [String] data
19
+ # @param [String] data_type
20
+ #
21
+ def initialize(data:, data_type:)
22
+ @data = data
23
+ @data_type = data_type
24
+ end
15
25
 
16
26
  def actions
17
27
  [vt_link, urlscan_link, censys_link, shodan_link].compact
@@ -31,7 +31,7 @@ module Mihari
31
31
  end
32
32
 
33
33
  class RuleIDs < Grape::Entity
34
- expose :rule_ids, documentation: { type: Array[String], required: true }, as: :ruleIds
34
+ expose :rule_ids, documentation: { type: [String], required: true }, as: :ruleIds
35
35
  end
36
36
  end
37
37
  end
@@ -7,7 +7,7 @@ module Mihari
7
7
  end
8
8
 
9
9
  class Tags < Grape::Entity
10
- expose :tags, documentation: { type: Array[String], required: true }
10
+ expose :tags, documentation: { type: [String], required: true }
11
11
  end
12
12
  end
13
13
  end
data/lib/mihari/errors.rb CHANGED
@@ -21,7 +21,7 @@ module Mihari
21
21
 
22
22
  class HTTPError < Error; end
23
23
 
24
- class UnsuccessfulStatusCodeError < HTTPError; end
24
+ class StatusCodeError < HTTPError; end
25
25
 
26
26
  class NetworkError < HTTPError; end
27
27
 
data/lib/mihari/http.rb CHANGED
@@ -93,9 +93,8 @@ module Mihari
93
93
  def request(req)
94
94
  Net::HTTP.start(url.host, url.port, https_options) do |http|
95
95
  res = http.request(req)
96
- unless res.is_a?(Net::HTTPSuccess)
97
- raise UnsuccessfulStatusCodeError, "Unsuccessful response code returned: #{res.code}"
98
- end
96
+
97
+ raise StatusCodeError, "Unsuccessful response code returned: #{res.code}" unless res.is_a?(Net::HTTPSuccess)
99
98
 
100
99
  res
101
100
  end
@@ -4,10 +4,12 @@ module Mihari
4
4
  module Schemas
5
5
  AnalyzerOptions = Dry::Schema.Params do
6
6
  optional(:interval).value(:integer)
7
+ optional(:retry_times).value(:integer).default(DEFAULT_RETRY_TIMES)
8
+ optional(:retry_interval).value(:integer).default(DEFAULT_RETRY_INTERVAL)
7
9
  end
8
10
 
9
11
  AnalyzerWithoutAPIKey = Dry::Schema.Params do
10
- required(:analyzer).value(Types::String.enum("crtsh", "dnpedia", "dnstwister"))
12
+ required(:analyzer).value(Types::String.enum("dnstwister"))
11
13
  required(:query).value(:string)
12
14
  optional(:options).hash(AnalyzerOptions)
13
15
  end
@@ -23,6 +25,7 @@ module Mihari
23
25
  "securitytrails",
24
26
  "shodan",
25
27
  "st",
28
+ "urlscan",
26
29
  "virustotal_intelligence",
27
30
  "virustotal",
28
31
  "vt_intel",
@@ -72,12 +75,6 @@ module Mihari
72
75
  optional(:options).hash(AnalyzerOptions)
73
76
  end
74
77
 
75
- Urlscan = Dry::Schema.Params do
76
- required(:analyzer).value(Types::String.enum("urlscan"))
77
- required(:query).value(:string)
78
- optional(:options).hash(AnalyzerOptions)
79
- end
80
-
81
78
  Feed = Dry::Schema.Params do
82
79
  required(:analyzer).value(Types::String.enum("feed"))
83
80
  required(:query).value(:string)
@@ -22,7 +22,7 @@ module Mihari
22
22
  optional(:updated_on).value(:date)
23
23
 
24
24
  required(:queries).value(:array).each do
25
- AnalyzerWithoutAPIKey | AnalyzerWithAPIKey | Censys | CIRCL | PassiveTotal | ZoomEye | Urlscan | Crtsh | Feed
25
+ AnalyzerWithoutAPIKey | AnalyzerWithAPIKey | Censys | CIRCL | PassiveTotal | ZoomEye | Crtsh | Feed
26
26
  end
27
27
 
28
28
  optional(:emitters).value(:array).each { Database | MISP | TheHive | Slack | Webhook }.default(DEFAULT_EMITTERS)
@@ -38,30 +38,53 @@ module Mihari
38
38
 
39
39
  class << self
40
40
  #
41
- # @param [Class<Mihari::Analyzers::Base>, Class<Mihari::Emitters::Base>] klass
41
+ # Get a type of a class
42
+ #
43
+ # @param [Class<Mihari::Analyzers::Base>, Class<Mihari::Emitters::Base>, Class<Mihari::Enrichers::Base>] klass
44
+ #
45
+ # @return [String, nil]
46
+ #
47
+ def get_type(klass)
48
+ return "Analyzer" if klass.ancestors.include?(Mihari::Analyzers::Base)
49
+ return "Emitter" if klass.ancestors.include?(Mihari::Emitters::Base)
50
+ return "Enricher" if klass.ancestors.include?(Mihari::Enrichers::Base)
51
+
52
+ nil
53
+ end
54
+
55
+ #
56
+ # Get a dummy instance
57
+ #
58
+ # @param [Class<Mihari::Analyzers::Base>, Class<Mihari::Emitters::Base>, Class<Mihari::Enrichers::Base>] klass
59
+ #
60
+ # @return [Mihari::Analyzers::Base, Mihari::Emitter::Base, Mihari::Enricher::Base] dummy
61
+ #
62
+ def get_dummy(klass)
63
+ type = get_type(klass)
64
+ is_analyzer = type == "Analyzer"
65
+ is_analyzer ? klass.new("dummy") : klass.new(artifacts: [], rule: nil)
66
+ end
67
+
68
+ #
69
+ # @param [Class<Mihari::Analyzers::Base>, Class<Mihari::Emitters::Base>, Class<Mihari::Enrichers::Base>] klass
42
70
  #
43
71
  # @return [Mihari::Structs::Config, nil] config
44
72
  #
45
73
  def from_class(klass)
46
74
  return nil if klass == Mihari::Analyzers::Rule
47
75
 
48
- name = klass.to_s.split("::").last.to_s
49
-
50
- is_analyzer = klass.ancestors.include?(Mihari::Analyzers::Base)
51
- is_emitter = klass.ancestors.include?(Mihari::Emitters::Base)
52
- is_enricher = klass.ancestors.include?(Mihari::Enrichers::Base)
53
-
54
- type = "Analyzer"
55
- type = "Emitter" if is_emitter
56
- type = "Enricher" if is_enricher
76
+ type = get_type(klass)
77
+ return nil if type.nil?
57
78
 
58
79
  begin
59
- instance = is_analyzer ? klass.new("dummy") : klass.new(artifacts: [], rule: nil)
60
- is_configured = instance.configured?
61
- values = instance.configuration_values
62
-
63
- new(name: name, values: values, is_configured: is_configured, type: type)
64
- rescue ArgumentError => _e
80
+ instance = get_dummy(klass)
81
+ new(
82
+ name: klass.to_s.split("::").last.to_s,
83
+ values: instance.configuration_values,
84
+ is_configured: instance.configured?,
85
+ type: type
86
+ )
87
+ rescue ArgumentError
65
88
  nil
66
89
  end
67
90
  end
@@ -162,7 +162,7 @@ module Mihari
162
162
  # @return [Mihari::Analyzers::Rule]
163
163
  #
164
164
  def analyzer
165
- Mihari::Analyzers::Rule.new(rule: self)
165
+ Mihari::Analyzers::Rule.new(self)
166
166
  end
167
167
 
168
168
  class << self
@@ -2,13 +2,13 @@
2
2
 
3
3
  module Mihari
4
4
  class TypeChecker
5
- extend Dry::Initializer
6
-
7
- param :data
8
-
9
- def initialize(*args, **kwargs)
10
- super
5
+ # @return [String]
6
+ attr_reader :data
11
7
 
8
+ #
9
+ # @param [String] data
10
+ #
11
+ def initialize(data)
12
12
  raise ArgumentError if data.is_a?(Hash)
13
13
 
14
14
  @data = data.to_s
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- VERSION = "5.2.3"
4
+ VERSION = "5.3.0"
5
5
  end