mihari 3.10.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (176) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/docker/Dockerfile +1 -1
  4. data/lib/mihari/analyzers/base.rb +7 -7
  5. data/lib/mihari/analyzers/binaryedge.rb +8 -5
  6. data/lib/mihari/analyzers/censys.rb +6 -3
  7. data/lib/mihari/analyzers/circl.rb +0 -3
  8. data/lib/mihari/analyzers/crtsh.rb +7 -5
  9. data/lib/mihari/analyzers/dnpedia.rb +4 -4
  10. data/lib/mihari/analyzers/dnstwister.rb +1 -4
  11. data/lib/mihari/analyzers/feed.rb +36 -0
  12. data/lib/mihari/analyzers/greynoise.rb +1 -3
  13. data/lib/mihari/analyzers/onyphe.rb +6 -3
  14. data/lib/mihari/analyzers/otx.rb +0 -3
  15. data/lib/mihari/analyzers/passivetotal.rb +8 -9
  16. data/lib/mihari/analyzers/pulsedive.rb +7 -5
  17. data/lib/mihari/analyzers/rule.rb +11 -5
  18. data/lib/mihari/analyzers/securitytrails.rb +10 -7
  19. data/lib/mihari/analyzers/shodan.rb +15 -8
  20. data/lib/mihari/analyzers/spyse.rb +10 -11
  21. data/lib/mihari/analyzers/urlscan.rb +39 -16
  22. data/lib/mihari/analyzers/virustotal.rb +8 -9
  23. data/lib/mihari/analyzers/virustotal_intelligence.rb +9 -5
  24. data/lib/mihari/analyzers/zoomeye.rb +12 -5
  25. data/lib/mihari/cli/base.rb +0 -5
  26. data/lib/mihari/cli/init.rb +0 -2
  27. data/lib/mihari/cli/main.rb +4 -6
  28. data/lib/mihari/cli/mixins/utils.rb +2 -18
  29. data/lib/mihari/commands/init.rb +0 -18
  30. data/lib/mihari/commands/search.rb +20 -15
  31. data/lib/mihari/commands/validator.rb +7 -19
  32. data/lib/mihari/commands/web.rb +0 -3
  33. data/lib/mihari/database.rb +71 -19
  34. data/lib/mihari/emitters/misp.rb +0 -1
  35. data/lib/mihari/emitters/slack.rb +3 -4
  36. data/lib/mihari/emitters/stdout.rb +0 -2
  37. data/lib/mihari/emitters/the_hive.rb +0 -1
  38. data/lib/mihari/emitters/webhook.rb +1 -5
  39. data/lib/mihari/enrichers/ipinfo.rb +2 -2
  40. data/lib/mihari/errors.rb +6 -0
  41. data/lib/mihari/feed/parser.rb +34 -0
  42. data/lib/mihari/feed/reader.rb +127 -0
  43. data/lib/mihari/mixins/autonomous_system.rb +2 -0
  44. data/lib/mihari/mixins/database.rb +14 -0
  45. data/lib/mihari/mixins/disallowed_data_value.rb +2 -3
  46. data/lib/mihari/mixins/rule.rb +36 -31
  47. data/lib/mihari/models/alert.rb +7 -8
  48. data/lib/mihari/models/artifact.rb +1 -7
  49. data/lib/mihari/models/autonomous_system.rb +0 -2
  50. data/lib/mihari/models/dns.rb +2 -5
  51. data/lib/mihari/models/geolocation.rb +0 -1
  52. data/lib/mihari/models/reverse_dns.rb +0 -3
  53. data/lib/mihari/models/rule.rb +73 -0
  54. data/lib/mihari/models/tag.rb +0 -2
  55. data/lib/mihari/models/tagging.rb +0 -2
  56. data/lib/mihari/models/whois.rb +0 -2
  57. data/lib/mihari/notifiers/exception_notifier.rb +0 -2
  58. data/lib/mihari/schemas/analyzer.rb +0 -5
  59. data/lib/mihari/schemas/macros.rb +0 -2
  60. data/lib/mihari/schemas/rule.rb +21 -7
  61. data/lib/mihari/status.rb +2 -2
  62. data/lib/mihari/structs/alert.rb +2 -3
  63. data/lib/mihari/structs/censys.rb +4 -3
  64. data/lib/mihari/structs/greynoise.rb +4 -3
  65. data/lib/mihari/structs/ipinfo.rb +1 -2
  66. data/lib/mihari/structs/onyphe.rb +6 -5
  67. data/lib/mihari/structs/rule.rb +121 -0
  68. data/lib/mihari/structs/shodan.rb +8 -7
  69. data/lib/mihari/structs/urlscan.rb +50 -0
  70. data/lib/mihari/structs/virustotal_intelligence.rb +4 -3
  71. data/lib/mihari/type_checker.rb +2 -6
  72. data/lib/mihari/types.rb +8 -1
  73. data/lib/mihari/version.rb +1 -1
  74. data/lib/mihari/web/api.rb +6 -0
  75. data/lib/mihari/web/app.rb +5 -7
  76. data/lib/mihari/web/endpoints/alerts.rb +7 -3
  77. data/lib/mihari/web/endpoints/artifacts.rb +6 -3
  78. data/lib/mihari/web/endpoints/command.rb +2 -1
  79. data/lib/mihari/web/endpoints/configs.rb +2 -1
  80. data/lib/mihari/web/endpoints/ip_addresses.rb +2 -1
  81. data/lib/mihari/web/endpoints/rules.rb +140 -0
  82. data/lib/mihari/web/endpoints/sources.rb +2 -1
  83. data/lib/mihari/web/endpoints/tags.rb +4 -2
  84. data/lib/mihari/web/entities/artifact.rb +4 -2
  85. data/lib/mihari/web/entities/rule.rb +35 -0
  86. data/lib/mihari/web/entities/whois.rb +1 -1
  87. data/lib/mihari/web/middleware/connection_adapter.rb +19 -0
  88. data/lib/mihari/web/public/index.html +1 -1
  89. data/lib/mihari/web/public/redoc-static.html +196 -184
  90. data/lib/mihari/web/public/static/js/app.49ab738a.js +21 -0
  91. data/lib/mihari/web/public/static/js/app.49ab738a.js.map +1 -0
  92. data/lib/mihari/web/public/static/js/app.5dc97aae.js +21 -0
  93. data/lib/mihari/web/public/static/js/app.5dc97aae.js.map +1 -0
  94. data/lib/mihari/web/public/static/js/app.f2b8890f.js +21 -0
  95. data/lib/mihari/web/public/static/js/app.f2b8890f.js.map +1 -0
  96. data/lib/mihari/web/public/static/js/app.fbc19869.js +21 -0
  97. data/lib/mihari/web/public/static/js/app.fbc19869.js.map +1 -0
  98. data/lib/mihari.rb +42 -34
  99. data/mihari.gemspec +21 -23
  100. data/sig/lib/mihari/analyzers/binaryedge.rbs +2 -3
  101. data/sig/lib/mihari/analyzers/censys.rbs +2 -3
  102. data/sig/lib/mihari/analyzers/circl.rbs +1 -3
  103. data/sig/lib/mihari/analyzers/crtsh.rbs +1 -3
  104. data/sig/lib/mihari/analyzers/dnpedia.rbs +1 -4
  105. data/sig/lib/mihari/analyzers/dnstwister.rbs +1 -3
  106. data/sig/lib/mihari/analyzers/feed.rbs +20 -0
  107. data/sig/lib/mihari/analyzers/onyphe.rbs +2 -3
  108. data/sig/lib/mihari/analyzers/otx.rbs +1 -3
  109. data/sig/lib/mihari/analyzers/passivetotal.rbs +3 -5
  110. data/sig/lib/mihari/analyzers/pulsedive.rbs +2 -4
  111. data/sig/lib/mihari/analyzers/securitytrails.rbs +3 -5
  112. data/sig/lib/mihari/analyzers/shodan.rbs +2 -3
  113. data/sig/lib/mihari/analyzers/spyse.rbs +4 -6
  114. data/sig/lib/mihari/analyzers/urlscan.rbs +6 -5
  115. data/sig/lib/mihari/analyzers/virustotal.rbs +4 -6
  116. data/sig/lib/mihari/analyzers/virustotal_intelligence.rbs +2 -3
  117. data/sig/lib/mihari/analyzers/zoomeye.rbs +4 -4
  118. data/sig/lib/mihari/commands/init.rbs +0 -2
  119. data/sig/lib/mihari/commands/validator.rbs +0 -2
  120. data/sig/lib/mihari/emitters/slack.rbs +0 -1
  121. data/sig/lib/mihari/feed/parser.rbs +11 -0
  122. data/sig/lib/mihari/feed/reader.rbs +56 -0
  123. data/sig/lib/mihari/mixins/disallowed_data_value.rbs +0 -2
  124. data/sig/lib/mihari/mixins/rule.rbs +5 -12
  125. data/sig/lib/mihari/models/alert.rbs +1 -1
  126. data/sig/lib/mihari/models/artifact.rbs +2 -0
  127. data/sig/lib/mihari/models/rule.rbs +14 -0
  128. data/sig/lib/mihari/structs/alert.rbs +1 -1
  129. data/sig/lib/mihari/structs/greynoise.rbs +3 -3
  130. data/sig/lib/mihari/structs/rule.rbs +56 -0
  131. data/sig/lib/mihari/structs/shodan.rbs +2 -2
  132. data/sig/lib/mihari/structs/urlscan.rbs +28 -0
  133. data/sig/lib/mihari/types.rbs +4 -0
  134. data/sig/lib/mihari.rbs +0 -2
  135. metadata +102 -147
  136. data/lib/mihari/cli/analyzer.rb +0 -52
  137. data/lib/mihari/commands/binaryedge.rb +0 -21
  138. data/lib/mihari/commands/censys.rb +0 -22
  139. data/lib/mihari/commands/circl.rb +0 -21
  140. data/lib/mihari/commands/crtsh.rb +0 -22
  141. data/lib/mihari/commands/dnpedia.rb +0 -21
  142. data/lib/mihari/commands/dnstwister.rb +0 -21
  143. data/lib/mihari/commands/greynoise.rb +0 -21
  144. data/lib/mihari/commands/json.rb +0 -42
  145. data/lib/mihari/commands/onyphe.rb +0 -21
  146. data/lib/mihari/commands/otx.rb +0 -21
  147. data/lib/mihari/commands/passivetotal.rb +0 -22
  148. data/lib/mihari/commands/pulsedive.rb +0 -21
  149. data/lib/mihari/commands/securitytrails.rb +0 -22
  150. data/lib/mihari/commands/shodan.rb +0 -21
  151. data/lib/mihari/commands/spyse.rb +0 -22
  152. data/lib/mihari/commands/urlscan.rb +0 -23
  153. data/lib/mihari/commands/virustotal.rb +0 -22
  154. data/lib/mihari/commands/virustotal_intelligence.rb +0 -22
  155. data/lib/mihari/commands/zoomeye.rb +0 -22
  156. data/lib/mihari/mixins/configuration.rb +0 -100
  157. data/lib/mihari/mixins/hash.rb +0 -20
  158. data/lib/mihari/schemas/configuration.rb +0 -44
  159. data/lib/mihari/web/public/grape.rb +0 -73
  160. data/sig/lib/mihari/cli/analyzer.rbs +0 -39
  161. data/sig/lib/mihari/commands/binaryedge.rbs +0 -7
  162. data/sig/lib/mihari/commands/censys.rbs +0 -7
  163. data/sig/lib/mihari/commands/circl.rbs +0 -7
  164. data/sig/lib/mihari/commands/crtsh.rbs +0 -7
  165. data/sig/lib/mihari/commands/dnpedia.rbs +0 -7
  166. data/sig/lib/mihari/commands/dnstwister.rbs +0 -7
  167. data/sig/lib/mihari/commands/onyphe.rbs +0 -7
  168. data/sig/lib/mihari/commands/otx.rbs +0 -7
  169. data/sig/lib/mihari/commands/passivetotal.rbs +0 -7
  170. data/sig/lib/mihari/commands/pulsedive.rbs +0 -7
  171. data/sig/lib/mihari/commands/securitytrails.rbs +0 -7
  172. data/sig/lib/mihari/commands/shodan.rbs +0 -7
  173. data/sig/lib/mihari/commands/spyse.rbs +0 -7
  174. data/sig/lib/mihari/commands/urlscan.rbs +0 -7
  175. data/sig/lib/mihari/commands/virustotal.rbs +0 -7
  176. data/sig/lib/mihari/commands/zoomeye.rbs +0 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c4d53239fe8be60cdc674dc22c14f801147a71144649cec83f4fd3caaf2c99fa
4
- data.tar.gz: 71b9b3746164b1f3eec9b98a807a6940e85a9184991efd7dd9abe5bd9b3f3578
3
+ metadata.gz: 1a42a8897840d2f2268f88c6734c1633642256ef08667c39f45f5d03ac13874f
4
+ data.tar.gz: 89bdd5aa38f833e3158464915ec46c05e8c0bb6f17d34fe397d1590c59a57d7c
5
5
  SHA512:
6
- metadata.gz: 6bee10443bd55e0ac483e8495c508bcce95dc56073aeb99f6c1566f38fb73707140baa838c07212c39795017d109aef8e9478f560bc3d04a21cbb949a48c9b61
7
- data.tar.gz: c35f757168a737c37dcbf9a54d0408eabdfbf807a7a57db77c59c10f9a5b803d6731c615f3360db93b05647ee5e6cdcd35518baf4a7a7efe8d73e198d7f2664e
6
+ metadata.gz: f62927fdb96eabffd99106bf03178e6eb573ee5e58b8fa2bb94b6b6b2ea898324fd65656bbb128aab67196ad03da959603dd73108c8840e883560d673c8afa5f
7
+ data.tar.gz: 2e752fa7ab93691ad6c38ec534af3bab14e27f2b66127dc00fc016f26554ea29fc2c743d2bdaef93d85e816d8a47e54d2d41bb6dafaabf711cfb2c833c3ff0ff
data/README.md CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/mihari.svg)](https://badge.fury.io/rb/mihari)
4
4
  [![Ruby CI](https://github.com/ninoseki/mihari/actions/workflows/test.yml/badge.svg)](https://github.com/ninoseki/mihari/actions/workflows/test.yml)
5
- [![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/ninoseki/mihari)](https://hub.docker.com/r/ninoseki/mihari)
6
5
  [![Coverage Status](https://coveralls.io/repos/github/ninoseki/mihari/badge.svg?branch=master)](https://coveralls.io/github/ninoseki/mihari?branch=master)
7
6
  [![CodeFactor](https://www.codefactor.io/repository/github/ninoseki/mihari/badge)](https://www.codefactor.io/repository/github/ninoseki/mihari)
8
7
 
@@ -38,6 +37,7 @@ Mihari supports the following services by default.
38
37
  - [crt.sh](https://crt.sh/)
39
38
  - [DN Pedia](https://dnpedia.com/)
40
39
  - [dnstwister](https://dnstwister.report/)
40
+ - [GreyNoise](https://www.greynoise.io/)
41
41
  - [Onyphe](https://onyphe.io)
42
42
  - [OTX](https://otx.alienvault.com/)
43
43
  - [PassiveTotal](https://community.riskiq.com/)
data/docker/Dockerfile CHANGED
@@ -1,4 +1,4 @@
1
- FROM ruby:3.0.2-alpine3.13
1
+ FROM ruby:3.0.3-alpine3.13
2
2
 
3
3
  RUN apk --no-cache add git build-base ruby-dev sqlite-dev postgresql-dev mysql-client mysql-dev \
4
4
  && gem install pg mysql2 \
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry-initializer"
4
- require "parallel"
5
-
6
3
  module Mihari
7
4
  module Analyzers
8
5
  class Base
@@ -10,6 +7,7 @@ module Mihari
10
7
 
11
8
  include Mixins::AutonomousSystem
12
9
  include Mixins::Configurable
10
+ include Mixins::Database
13
11
  include Mixins::Retriable
14
12
 
15
13
  attr_accessor :ignore_old_artifacts, :ignore_threshold
@@ -52,10 +50,12 @@ module Mihari
52
50
  # @return [nil]
53
51
  #
54
52
  def run
55
- set_enriched_artifacts
53
+ with_db_connection do
54
+ set_enriched_artifacts
56
55
 
57
- Parallel.each(valid_emitters) do |emitter|
58
- run_emitter emitter
56
+ Parallel.each(valid_emitters) do |emitter|
57
+ run_emitter emitter
58
+ end
59
59
  end
60
60
  end
61
61
 
@@ -111,7 +111,7 @@ module Mihari
111
111
  # @return [Array<Mihari::Artifact>]
112
112
  #
113
113
  def enriched_artifacts
114
- @enriched_artifacts ||= unique_artifacts.map do |artifact|
114
+ @enriched_artifacts ||= Parallel.map(unique_artifacts) do |artifact|
115
115
  artifact.enrich_all
116
116
  artifact
117
117
  end
@@ -6,9 +6,8 @@ module Mihari
6
6
  module Analyzers
7
7
  class BinaryEdge < Base
8
8
  param :query
9
- option :title, default: proc { "BinaryEdge search" }
10
- option :description, default: proc { "query = #{query}" }
11
- option :tags, default: proc { [] }
9
+
10
+ option :interval, default: proc { 0 }
12
11
 
13
12
  def artifacts
14
13
  results = search
@@ -17,9 +16,10 @@ module Mihari
17
16
  results.map do |result|
18
17
  events = result["events"] || []
19
18
  events.filter_map do |event|
20
- event.dig "target", "ip"
19
+ data = event.dig("target", "ip")
20
+ data.nil? ? nil : Artifact.new(data: data, source: source, metadata: event)
21
21
  end
22
- end.flatten.compact.uniq
22
+ end.flatten
23
23
  end
24
24
 
25
25
  private
@@ -55,6 +55,9 @@ module Mihari
55
55
 
56
56
  responses << res
57
57
  break if total <= page * PAGE_SIZE
58
+
59
+ # sleep #{interval} seconds to avoid the rate limitation (if it is set)
60
+ sleep interval
58
61
  end
59
62
  responses
60
63
  end
@@ -6,9 +6,8 @@ module Mihari
6
6
  module Analyzers
7
7
  class Censys < Base
8
8
  param :query
9
- option :title, default: proc { "Censys search" }
10
- option :description, default: proc { "query = #{query}" }
11
- option :tags, default: proc { [] }
9
+
10
+ option :interval, default: proc { 0 }
12
11
 
13
12
  def artifacts
14
13
  search
@@ -33,6 +32,9 @@ module Mihari
33
32
 
34
33
  cursor = response.result.links.next
35
34
  break if cursor == ""
35
+
36
+ # sleep #{interval} seconds to avoid the rate limitation (if it is set)
37
+ sleep interval
36
38
  end
37
39
 
38
40
  artifacts.flatten.uniq(&:data)
@@ -72,6 +74,7 @@ module Mihari
72
74
  Artifact.new(
73
75
  data: hit.ip,
74
76
  source: source,
77
+ metadata: hit.metadata,
75
78
  autonomous_system: as,
76
79
  geolocation: geolocation
77
80
  )
@@ -8,9 +8,6 @@ module Mihari
8
8
  include Mixins::Refang
9
9
 
10
10
  param :query
11
- option :title, default: proc { "CIRCL passive DNS/SSL search" }
12
- option :description, default: proc { "query = #{query}" }
13
- option :tags, default: proc { [] }
14
11
 
15
12
  attr_reader :type
16
13
 
@@ -6,15 +6,17 @@ module Mihari
6
6
  module Analyzers
7
7
  class Crtsh < Base
8
8
  param :query
9
- option :title, default: proc { "crt.sh search" }
10
- option :description, default: proc { "query = #{query}" }
11
- option :tags, default: proc { [] }
9
+
12
10
  option :exclude_expired, default: proc { true }
13
11
 
14
12
  def artifacts
15
13
  results = search
16
- name_values = results.filter_map { |result| result["name_value"] }
17
- name_values.map(&:lines).flatten.uniq.map(&:chomp)
14
+ results.map do |result|
15
+ values = result["name_value"].to_s.lines.map(&:chomp)
16
+ values.map do |value|
17
+ Artifact.new(data: value, source: source, metadata: result)
18
+ end
19
+ end.flatten
18
20
  end
19
21
 
20
22
  private
@@ -6,8 +6,7 @@ module Mihari
6
6
  module Analyzers
7
7
  class DNPedia < Base
8
8
  param :query
9
- option :title, default: proc { "DNPedia domain search" }
10
- option :description, default: proc { "query = #{query}" }
9
+
11
10
  option :tags, default: proc { [] }
12
11
 
13
12
  def artifacts
@@ -23,13 +22,14 @@ module Mihari
23
22
  #
24
23
  # Search
25
24
  #
26
- # @return [Array<String>]
25
+ # @return [Array<Mihari::Artifact>]
27
26
  #
28
27
  def search
29
28
  res = api.search(query)
30
29
  rows = res["rows"] || []
31
30
  rows.map do |row|
32
- [row["name"], row["zoneid"]].join(".")
31
+ data = [row["name"], row["zoneid"]].join(".")
32
+ Artifact.new(data: data, source: source, metadata: row)
33
33
  end
34
34
  end
35
35
  end
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "dnstwister"
4
- require "resolv"
5
- require "parallel"
6
4
 
7
5
  module Mihari
8
6
  module Analyzers
@@ -10,8 +8,7 @@ module Mihari
10
8
  include Mixins::Refang
11
9
 
12
10
  param :query
13
- option :title, default: proc { "dnstwister domain search" }
14
- option :description, default: proc { "query = #{query}" }
11
+
15
12
  option :tags, default: proc { [] }
16
13
 
17
14
  attr_reader :type
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mihari/feed/reader"
4
+ require "mihari/feed/parser"
5
+
6
+ module Mihari
7
+ module Analyzers
8
+ class Feed < Base
9
+ param :query
10
+
11
+ option :http_request_method, default: proc { "GET" }
12
+ option :http_request_headers, default: proc { {} }
13
+ option :http_request_payload, default: proc { {} }
14
+ option :http_request_payload_type, default: proc {}
15
+
16
+ option :selector, default: proc { "" }
17
+
18
+ def artifacts
19
+ Mihari::Feed::Parser.new(data).parse selector
20
+ end
21
+
22
+ private
23
+
24
+ def data
25
+ reader = Mihari::Feed::Reader.new(
26
+ query,
27
+ http_request_method: http_request_method,
28
+ http_request_headers: http_request_headers,
29
+ http_request_payload: http_request_payload,
30
+ http_request_payload_type: http_request_payload_type
31
+ )
32
+ reader.read
33
+ end
34
+ end
35
+ end
36
+ end
@@ -6,9 +6,6 @@ module Mihari
6
6
  module Analyzers
7
7
  class GreyNoise < Base
8
8
  param :query
9
- option :title, default: proc { "GreyNoise search" }
10
- option :description, default: proc { "query = #{query}" }
11
- option :tags, default: proc { [] }
12
9
 
13
10
  def artifacts
14
11
  res = Structs::GreyNoise::Response.from_dynamic!(search)
@@ -56,6 +53,7 @@ module Mihari
56
53
  Artifact.new(
57
54
  data: datum.ip,
58
55
  source: source,
56
+ metadata: datum.metadata_,
59
57
  autonomous_system: as,
60
58
  geolocation: geolocation
61
59
  )
@@ -7,9 +7,8 @@ module Mihari
7
7
  module Analyzers
8
8
  class Onyphe < Base
9
9
  param :query
10
- option :title, default: proc { "Onyphe search" }
11
- option :description, default: proc { "query = #{query}" }
12
- option :tags, default: proc { [] }
10
+
11
+ option :interval, default: proc { 0 }
13
12
 
14
13
  def artifacts
15
14
  responses = search
@@ -59,6 +58,9 @@ module Mihari
59
58
 
60
59
  total = res.total
61
60
  break if total <= page * PAGE_SIZE
61
+
62
+ # sleep #{interval} seconds to avoid the rate limitation (if it is set)
63
+ sleep interval
62
64
  end
63
65
  responses
64
66
  end
@@ -84,6 +86,7 @@ module Mihari
84
86
  Artifact.new(
85
87
  data: result.ip,
86
88
  source: source,
89
+ metadata: result.metadata,
87
90
  autonomous_system: as,
88
91
  geolocation: geolocation
89
92
  )
@@ -8,9 +8,6 @@ module Mihari
8
8
  include Mixins::Refang
9
9
 
10
10
  param :query
11
- option :title, default: proc { "OTX search" }
12
- option :description, default: proc { "query = #{query}" }
13
- option :tags, default: proc { [] }
14
11
 
15
12
  attr_reader :type
16
13
 
@@ -8,9 +8,6 @@ module Mihari
8
8
  include Mixins::Refang
9
9
 
10
10
  param :query
11
- option :title, default: proc { "PassiveTotal search" }
12
- option :description, default: proc { "query = #{query}" }
13
- option :tags, default: proc { [] }
14
11
 
15
12
  attr_reader :type
16
13
 
@@ -75,27 +72,29 @@ module Mihari
75
72
  #
76
73
  # Reverse whois search
77
74
  #
78
- # @return [Array<String>]
75
+ # @return [Array<Mihari::Artifact>]
79
76
  #
80
77
  def reverse_whois_search
81
78
  res = api.whois.search(query: query, field: "email")
82
79
  results = res["results"] || []
83
80
  results.map do |result|
84
- result["domain"]
85
- end.flatten.compact.uniq
81
+ data = result["domain"]
82
+ Artifact.new(data: data, source: source, metadata: result)
83
+ end.flatten
86
84
  end
87
85
 
88
86
  #
89
87
  # Passive SSL search
90
88
  #
91
- # @return [Array<String>]
89
+ # @return [Array<Mihari::Artifact>]
92
90
  #
93
91
  def ssl_search
94
92
  res = api.ssl.history(query)
95
93
  results = res["results"] || []
96
94
  results.map do |result|
97
- result["ipAddresses"]
98
- end.flatten.compact.uniq
95
+ data = result["ipAddresses"]
96
+ Artifact.new(data: data, source: source, metadata: result)
97
+ end.flatten
99
98
  end
100
99
  end
101
100
  end
@@ -8,9 +8,6 @@ module Mihari
8
8
  include Mixins::Refang
9
9
 
10
10
  param :query
11
- option :title, default: proc { "Pulsedive search" }
12
- option :description, default: proc { "query = #{query}" }
13
- option :tags, default: proc { [] }
14
11
 
15
12
  attr_reader :type
16
13
 
@@ -47,7 +44,7 @@ module Mihari
47
44
  #
48
45
  # Search
49
46
  #
50
- # @return [Array<String>]
47
+ # @return [Array<Mihari::Artifact>]
51
48
  #
52
49
  def search
53
50
  raise InvalidInputError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
@@ -57,7 +54,12 @@ module Mihari
57
54
 
58
55
  properties = api.indicator.get_properties_by_id(iid)
59
56
  (properties["dns"] || []).filter_map do |property|
60
- property["value"] if ["A", "PTR"].include?(property["name"])
57
+ if ["A", "PTR"].include?(property["name"])
58
+ nil
59
+ else
60
+ data = property["value"]
61
+ Artifact.new(data: data, source: source, metadata: property)
62
+ end
61
63
  end
62
64
  end
63
65
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "uuidtools"
4
-
5
3
  module Mihari
6
4
  module Analyzers
7
5
  ANALYZER_TO_CLASS = {
@@ -11,6 +9,7 @@ module Mihari
11
9
  "crtsh" => Crtsh,
12
10
  "dnpedia" => DNPedia,
13
11
  "dnstwister" => DNSTwister,
12
+ "feed" => Feed,
14
13
  "greynoise" => GreyNoise,
15
14
  "onyphe" => Onyphe,
16
15
  "otx" => OTX,
@@ -30,13 +29,14 @@ module Mihari
30
29
  }.freeze
31
30
 
32
31
  class Rule < Base
33
- include Mihari::Mixins::DisallowedDataValue
32
+ include Mixins::DisallowedDataValue
33
+ include Mixins::Rule
34
34
 
35
35
  option :title
36
36
  option :description
37
37
  option :queries
38
38
 
39
- option :id, default: proc {}
39
+ option :id, default: proc { "" }
40
40
  option :tags, default: proc { [] }
41
41
  option :allowed_data_types, default: proc { ALLOWED_DATA_TYPES }
42
42
  option :disallowed_data_values, default: proc { [] }
@@ -46,7 +46,7 @@ module Mihari
46
46
  def initialize(**kwargs)
47
47
  super(**kwargs)
48
48
 
49
- @source = id || UUIDTools::UUID.md5_create(UUIDTools::UUID_URL_NAMESPACE, title + description).to_s
49
+ @source = id
50
50
 
51
51
  validate_analyzer_configurations
52
52
  end
@@ -64,6 +64,12 @@ module Mihari
64
64
  klass = get_analyzer_class(analyzer_name)
65
65
 
66
66
  query = params[:query]
67
+
68
+ # set interval in the top level
69
+ options = params[:options] || {}
70
+ interval = options[:interval]
71
+ params[:interval] = interval if interval
72
+
67
73
  analyzer = klass.new(query, **params)
68
74
 
69
75
  # Use #normalized_artifacts method to get atrifacts as Array<Mihari::Artifact>
@@ -8,9 +8,6 @@ module Mihari
8
8
  include Mixins::Refang
9
9
 
10
10
  param :query
11
- option :title, default: proc { "SecurityTrails search" }
12
- option :description, default: proc { "query = #{query}" }
13
- option :tags, default: proc { [] }
14
11
 
15
12
  attr_reader :type
16
13
 
@@ -47,7 +44,7 @@ module Mihari
47
44
  #
48
45
  # IP/domain/mail search
49
46
  #
50
- # @return [Array<String>]
47
+ # @return [Array<String>, Array<Mihari::Artifact>]
51
48
  #
52
49
  def search
53
50
  case type
@@ -78,12 +75,15 @@ module Mihari
78
75
  #
79
76
  # IP search
80
77
  #
81
- # @return [Array<String>]
78
+ # @return [Array<Mihari::Artifact>]
82
79
  #
83
80
  def ip_search
84
81
  result = api.domains.search(filter: { ipv4: query })
85
82
  records = result["records"] || []
86
- records.filter_map { |record| record["hostname"] }.uniq
83
+ records.filter_map do |record|
84
+ data = record["hostname"]
85
+ Artifact.new(data: data, source: source, metadata: record)
86
+ end
87
87
  end
88
88
 
89
89
  #
@@ -94,7 +94,10 @@ module Mihari
94
94
  def mail_search
95
95
  result = api.domains.search(filter: { whois_email: query })
96
96
  records = result["records"] || []
97
- records.filter_map { |record| record["hostname"] }.uniq
97
+ records.filter_map do |record|
98
+ data = record["hostname"]
99
+ Artifact.new(data: data, source: source, metadata: record)
100
+ end
98
101
  end
99
102
  end
100
103
  end
@@ -6,9 +6,8 @@ module Mihari
6
6
  module Analyzers
7
7
  class Shodan < Base
8
8
  param :query
9
- option :title, default: proc { "Shodan search" }
10
- option :description, default: proc { "query = #{query}" }
11
- option :tags, default: proc { [] }
9
+
10
+ option :interval, default: proc { 0 }
12
11
 
13
12
  def artifacts
14
13
  results = search
@@ -18,7 +17,7 @@ module Mihari
18
17
  results.map do |result|
19
18
  matches = result.matches || []
20
19
  matches.map { |match| build_artifact match }
21
- end.flatten.compact.uniq(&:data)
20
+ end.flatten.uniq(&:data)
22
21
  end
23
22
 
24
23
  private
@@ -58,10 +57,14 @@ module Mihari
58
57
  responses = []
59
58
  (1..Float::INFINITY).each do |page|
60
59
  res = search_with_page(query, page: page)
60
+
61
61
  break unless res
62
62
 
63
63
  responses << res
64
64
  break if res["total"].to_i <= page * PAGE_SIZE
65
+
66
+ # sleep #{interval} seconds to avoid the rate limitation (if it is set)
67
+ sleep interval
65
68
  rescue JSON::ParserError
66
69
  # ignore JSON::ParserError
67
70
  # ref. https://github.com/ninoseki/mihari/issues/197
@@ -81,14 +84,18 @@ module Mihari
81
84
  as = nil
82
85
  as = AutonomousSystem.new(asn: normalize_asn(match.asn)) unless match.asn.nil?
83
86
 
84
- geolocation = Geolocation.new(
85
- country: match.location.country_name,
86
- country_code: match.location.country_code
87
- )
87
+ geolocation = nil
88
+ if !match.location.country_name.nil? && !match.location.country_code.nil?
89
+ geolocation = Geolocation.new(
90
+ country: match.location.country_name,
91
+ country_code: match.location.country_code
92
+ )
93
+ end
88
94
 
89
95
  Artifact.new(
90
96
  data: match.ip_str,
91
97
  source: source,
98
+ metadata: match.metadata,
92
99
  autonomous_system: as,
93
100
  geolocation: geolocation
94
101
  )
@@ -1,16 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "spyse"
4
- require "json"
5
4
 
6
5
  module Mihari
7
6
  module Analyzers
8
7
  class Spyse < Base
9
8
  param :query
10
- option :title, default: proc { "Spyse search" }
11
- option :description, default: proc { "query = #{query}" }
9
+
12
10
  option :type, default: proc { "domain" }
13
- option :tags, default: proc { [] }
14
11
 
15
12
  def artifacts
16
13
  search || []
@@ -42,33 +39,35 @@ module Mihari
42
39
  #
43
40
  # Domain search
44
41
  #
45
- # @return [Array<String>]
42
+ # @return [Array<Mihari::Artifact>]
46
43
  #
47
44
  def domain_search
48
45
  res = api.domain.search(search_params, limit: 100)
49
46
  items = res.dig("data", "items") || []
50
47
  items.map do |item|
51
- item["name"]
52
- end.uniq.compact
48
+ data = item["name"]
49
+ Artifact.new(data: data, source: source, metadata: item)
50
+ end
53
51
  end
54
52
 
55
53
  #
56
54
  # IP search
57
55
  #
58
- # @return [Array<String>]
56
+ # @return [Array<Mihari::Artifact>]
59
57
  #
60
58
  def ip_search
61
59
  res = api.ip.search(search_params, limit: 100)
62
60
  items = res.dig("data", "items") || []
63
61
  items.map do |item|
64
- item["ip"]
65
- end.uniq.compact
62
+ data = item["ip"]
63
+ Artifact.new(data: data, source: source, metadata: item)
64
+ end
66
65
  end
67
66
 
68
67
  #
69
68
  # IP/domain search
70
69
  #
71
- # @return [Array<String>]
70
+ # @return [Array<Mihari::Artifact>]
72
71
  #
73
72
  def search
74
73
  case type