mihari 5.2.1 → 5.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -0
  3. data/lib/mihari/analyzers/base.rb +20 -115
  4. data/lib/mihari/analyzers/binaryedge.rb +0 -1
  5. data/lib/mihari/analyzers/censys.rb +26 -3
  6. data/lib/mihari/analyzers/circl.rb +1 -1
  7. data/lib/mihari/analyzers/onyphe.rb +1 -1
  8. data/lib/mihari/analyzers/passivetotal.rb +1 -1
  9. data/lib/mihari/analyzers/rule.rb +122 -75
  10. data/lib/mihari/analyzers/shodan.rb +1 -1
  11. data/lib/mihari/analyzers/urlscan.rb +6 -9
  12. data/lib/mihari/analyzers/virustotal_intelligence.rb +1 -6
  13. data/lib/mihari/cli/main.rb +2 -2
  14. data/lib/mihari/clients/base.rb +1 -1
  15. data/lib/mihari/commands/database.rb +12 -11
  16. data/lib/mihari/commands/rule.rb +47 -45
  17. data/lib/mihari/commands/search.rb +88 -0
  18. data/lib/mihari/commands/version.rb +8 -6
  19. data/lib/mihari/commands/web.rb +26 -23
  20. data/lib/mihari/emitters/base.rb +14 -1
  21. data/lib/mihari/emitters/database.rb +3 -10
  22. data/lib/mihari/emitters/misp.rb +16 -5
  23. data/lib/mihari/emitters/slack.rb +13 -15
  24. data/lib/mihari/emitters/the_hive.rb +17 -19
  25. data/lib/mihari/emitters/webhook.rb +23 -23
  26. data/lib/mihari/enrichers/whois.rb +1 -0
  27. data/lib/mihari/feed/parser.rb +1 -0
  28. data/lib/mihari/feed/reader.rb +29 -14
  29. data/lib/mihari/mixins/configurable.rb +13 -4
  30. data/lib/mihari/mixins/error_notification.rb +0 -2
  31. data/lib/mihari/models/artifact.rb +1 -1
  32. data/lib/mihari/schemas/rule.rb +2 -17
  33. data/lib/mihari/structs/censys.rb +226 -56
  34. data/lib/mihari/structs/config.rb +48 -18
  35. data/lib/mihari/structs/google_public_dns.rb +56 -14
  36. data/lib/mihari/structs/greynoise.rb +122 -29
  37. data/lib/mihari/structs/ipinfo.rb +40 -0
  38. data/lib/mihari/structs/onyphe.rb +112 -26
  39. data/lib/mihari/structs/rule.rb +4 -2
  40. data/lib/mihari/structs/shodan.rb +189 -47
  41. data/lib/mihari/structs/urlscan.rb +123 -20
  42. data/lib/mihari/structs/virustotal_intelligence.rb +129 -26
  43. data/lib/mihari/type_checker.rb +10 -8
  44. data/lib/mihari/version.rb +1 -1
  45. data/lib/mihari.rb +1 -0
  46. data/mihari.gemspec +11 -10
  47. metadata +35 -36
  48. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -43
  49. data/.github/ISSUE_TEMPLATE/feature_request.md +0 -15
  50. data/.github/workflows/test.yml +0 -90
  51. data/config/pre_commit.yml +0 -3
  52. data/docker/Dockerfile +0 -14
  53. data/examples/ipinfo_hosted_domains.rb +0 -45
  54. data/images/Tines-Full_Logo-Tines_Black.png +0 -0
  55. data/images/alert.png +0 -0
  56. data/images/logo.png +0 -0
  57. data/images/misp.png +0 -0
  58. data/images/overview.jpg +0 -0
  59. data/images/slack.png +0 -0
  60. data/images/tines.png +0 -0
  61. data/images/web_alerts.png +0 -0
  62. data/images/web_config.png +0 -0
  63. data/lib/mihari/commands/searcher.rb +0 -61
@@ -10,6 +10,27 @@ module Mihari
10
10
  attribute :country_code, Types::String
11
11
  attribute :asn, Types::String
12
12
 
13
+ #
14
+ # @return [String]
15
+ #
16
+ def country
17
+ attributes[:country]
18
+ end
19
+
20
+ #
21
+ # @return [String]
22
+ #
23
+ def country_code
24
+ attributes[:country_code]
25
+ end
26
+
27
+ #
28
+ # @return [String]
29
+ #
30
+ def asn
31
+ attributes[:asn]
32
+ end
33
+
13
34
  #
14
35
  # @return [Mihari::AutonomousSystem]
15
36
  #
@@ -27,13 +48,20 @@ module Mihari
27
48
  )
28
49
  end
29
50
 
30
- def self.from_dynamic!(d)
31
- d = Types::Hash[d]
32
- new(
33
- country: d.fetch("country"),
34
- country_code: d.fetch("country_code"),
35
- asn: d.fetch("asn")
36
- )
51
+ class << self
52
+ #
53
+ # @param [Hash] d
54
+ #
55
+ # @return [Metadata]
56
+ #
57
+ def from_dynamic!(d)
58
+ d = Types::Hash[d]
59
+ new(
60
+ country: d.fetch("country"),
61
+ country_code: d.fetch("country_code"),
62
+ asn: d.fetch("asn")
63
+ )
64
+ end
37
65
  end
38
66
  end
39
67
 
@@ -43,27 +71,52 @@ module Mihari
43
71
  attribute :metadata_, Types::Hash
44
72
 
45
73
  #
46
- # @param [String] source
74
+ # @return [String]
75
+ #
76
+ def ip
77
+ attributes[:ip]
78
+ end
79
+
80
+ #
81
+ # @return [Metadata]
82
+ #
83
+ def metadata
84
+ attributes[:metadata]
85
+ end
86
+
87
+ #
88
+ # @return [Hash]
89
+ #
90
+ def metadata_
91
+ attributes[:metadata_]
92
+ end
93
+
47
94
  #
48
95
  # @return [Mihari::Artifact]
49
96
  #
50
- def to_artifact(source = "GreyNoise")
97
+ def to_artifact
51
98
  Mihari::Artifact.new(
52
99
  data: ip,
53
- source: source,
54
100
  metadata: metadata_,
55
101
  autonomous_system: metadata.to_as,
56
102
  geolocation: metadata.to_geolocation
57
103
  )
58
104
  end
59
105
 
60
- def self.from_dynamic!(d)
61
- d = Types::Hash[d]
62
- new(
63
- ip: d.fetch("ip"),
64
- metadata: Metadata.from_dynamic!(d.fetch("metadata")),
65
- metadata_: d
66
- )
106
+ class << self
107
+ #
108
+ # @param [Hash] d
109
+ #
110
+ # @return [Datum]
111
+ #
112
+ def from_dynamic!(d)
113
+ d = Types::Hash[d]
114
+ new(
115
+ ip: d.fetch("ip"),
116
+ metadata: Metadata.from_dynamic!(d.fetch("metadata")),
117
+ metadata_: d
118
+ )
119
+ end
67
120
  end
68
121
  end
69
122
 
@@ -75,23 +128,63 @@ module Mihari
75
128
  attribute :query, Types::String
76
129
 
77
130
  #
78
- # @param [String] source
131
+ # @return [Boolean]
132
+ #
133
+ def complete
134
+ attributes[:complete]
135
+ end
136
+
137
+ #
138
+ # @return [Integer]
139
+ #
140
+ def count
141
+ attributes[:count]
142
+ end
143
+
144
+ #
145
+ # @return [Array<Datum>]
146
+ #
147
+ def data
148
+ attributes[:data]
149
+ end
150
+
151
+ #
152
+ # @return [String]
153
+ #
154
+ def message
155
+ attributes[:message]
156
+ end
157
+
158
+ #
159
+ # @return [String]
160
+ #
161
+ def query
162
+ attributes[:query]
163
+ end
164
+
79
165
  #
80
166
  # @return [Array<Mihari::Artifact>]
81
167
  #
82
- def to_artifacts(source = "GreyNoise")
83
- data.map { |datum| datum.to_artifact(source) }
168
+ def to_artifacts
169
+ data.map { |datum| datum.to_artifact }
84
170
  end
85
171
 
86
- def self.from_dynamic!(d)
87
- d = Types::Hash[d]
88
- new(
89
- complete: d.fetch("complete"),
90
- count: d.fetch("count"),
91
- data: d.fetch("data").map { |x| Datum.from_dynamic!(x) },
92
- message: d.fetch("message"),
93
- query: d.fetch("query")
94
- )
172
+ class << self
173
+ #
174
+ # @param [Hash] d
175
+ #
176
+ # @return [Response]
177
+ #
178
+ def from_dynamic!(d)
179
+ d = Types::Hash[d]
180
+ new(
181
+ complete: d.fetch("complete"),
182
+ count: d.fetch("count"),
183
+ data: d.fetch("data").map { |x| Datum.from_dynamic!(x) },
184
+ message: d.fetch("message"),
185
+ query: d.fetch("query")
186
+ )
187
+ end
95
188
  end
96
189
  end
97
190
  end
@@ -10,9 +10,49 @@ module Mihari
10
10
  attribute :country_code, Types::String.optional
11
11
  attribute :asn, Types::Integer.optional
12
12
 
13
+ #
14
+ # @return [String]
15
+ #
16
+ def ip
17
+ attributes[:ip]
18
+ end
19
+
20
+ #
21
+ # @return [String, nil]
22
+ #
23
+ def hostname
24
+ attributes[:hostname]
25
+ end
26
+
27
+ #
28
+ # @return [String, nil]
29
+ #
30
+ def loc
31
+ attributes[:loc]
32
+ end
33
+
34
+ #
35
+ # @return [String, nil]
36
+ #
37
+ def country_code
38
+ attributes[:country_code]
39
+ end
40
+
41
+ #
42
+ # @return [Integer, nil]
43
+ #
44
+ def asn
45
+ attributes[:asn]
46
+ end
47
+
13
48
  class << self
14
49
  include Mixins::AutonomousSystem
15
50
 
51
+ #
52
+ # @param [Hash] d
53
+ #
54
+ # @return [Response]
55
+ #
16
56
  def from_dynamic!(d)
17
57
  d = d.deep_stringify_keys
18
58
  d = Types::Hash[d]
@@ -12,14 +12,39 @@ module Mihari
12
12
  attribute :metadata, Types::Hash
13
13
 
14
14
  #
15
- # @param [String] source
15
+ # @return [String]
16
+ #
17
+ def asn
18
+ attributes[:asn]
19
+ end
20
+
21
+ #
22
+ # @return [String, nil]
23
+ #
24
+ def country_code
25
+ attributes[:country_code]
26
+ end
27
+
28
+ #
29
+ # @return [String]
30
+ #
31
+ def ip
32
+ attributes[:ip]
33
+ end
34
+
35
+ #
36
+ # @return [Hash]
37
+ #
38
+ def metadata
39
+ attributes[:metadata]
40
+ end
41
+
16
42
  #
17
43
  # @return [Mihari::Artifact]
18
44
  #
19
- def to_artifact(source = "Onyphe")
45
+ def to_artifact
20
46
  Mihari::Artifact.new(
21
47
  data: ip,
22
- source: source,
23
48
  metadata: metadata,
24
49
  autonomous_system: to_as,
25
50
  geolocation: to_geolocation
@@ -45,15 +70,22 @@ module Mihari
45
70
  Mihari::AutonomousSystem.new(asn: normalize_asn(asn))
46
71
  end
47
72
 
48
- def self.from_dynamic!(d)
49
- d = Types::Hash[d]
50
- new(
51
- asn: d.fetch("asn"),
52
- ip: d.fetch("ip"),
53
- # Onyphe's country = 2-letter country code
54
- country_code: d["country"],
55
- metadata: d
56
- )
73
+ class << self
74
+ #
75
+ # @param [Hash] d
76
+ #
77
+ # @return [Result]
78
+ #
79
+ def from_dynamic!(d)
80
+ d = Types::Hash[d]
81
+ new(
82
+ asn: d.fetch("asn"),
83
+ ip: d.fetch("ip"),
84
+ # Onyphe's country = 2-letter country code
85
+ country_code: d["country"],
86
+ metadata: d
87
+ )
88
+ end
57
89
  end
58
90
  end
59
91
 
@@ -67,25 +99,79 @@ module Mihari
67
99
  attribute :total, Types::Int
68
100
 
69
101
  #
70
- # @param [String] source
102
+ # @return [Integer]
103
+ #
104
+ def count
105
+ attributes[:count]
106
+ end
107
+
108
+ #
109
+ # @return [Integer]
110
+ #
111
+ def error
112
+ attributes[:error]
113
+ end
114
+
115
+ #
116
+ # @return [Integer]
117
+ #
118
+ def max_page
119
+ attributes[:max_page]
120
+ end
121
+
122
+ #
123
+ # @return [Integer]
124
+ #
125
+ def page
126
+ attributes[:page]
127
+ end
128
+
129
+ #
130
+ # @return [Array<Result>]
131
+ #
132
+ def results
133
+ attributes[:results]
134
+ end
135
+
136
+ #
137
+ # @return [String]
138
+ #
139
+ def status
140
+ attributes[:status]
141
+ end
142
+
143
+ #
144
+ # @return [Integer]
145
+ #
146
+ def total
147
+ attributes[:total]
148
+ end
149
+
71
150
  #
72
151
  # @return [Array<Mihari::Artifact>]
73
152
  #
74
- def to_artifacts(source = "Onyphe")
75
- results.map { |result| result.to_artifact(source) }
153
+ def to_artifacts
154
+ results.map(&:to_artifact)
76
155
  end
77
156
 
78
- def self.from_dynamic!(d)
79
- d = Types::Hash[d]
80
- new(
81
- count: d.fetch("count"),
82
- error: d.fetch("error"),
83
- max_page: d.fetch("max_page"),
84
- page: d.fetch("page").to_i,
85
- results: d.fetch("results").map { |x| Result.from_dynamic!(x) },
86
- status: d.fetch("status"),
87
- total: d.fetch("total")
88
- )
157
+ class << self
158
+ #
159
+ # @param [Hash] d
160
+ #
161
+ # @return [Response]
162
+ #
163
+ def from_dynamic!(d)
164
+ d = Types::Hash[d]
165
+ new(
166
+ count: d.fetch("count"),
167
+ error: d.fetch("error"),
168
+ max_page: d.fetch("max_page"),
169
+ page: d.fetch("page").to_i,
170
+ results: d.fetch("results").map { |x| Result.from_dynamic!(x) },
171
+ status: d.fetch("status"),
172
+ total: d.fetch("total")
173
+ )
174
+ end
89
175
  end
90
176
  end
91
177
  end
@@ -10,6 +10,8 @@ require "yaml"
10
10
  module Mihari
11
11
  module Structs
12
12
  class Rule
13
+ include Mixins::FalsePositive
14
+
13
15
  # @return [Hash]
14
16
  attr_reader :data
15
17
 
@@ -109,10 +111,10 @@ module Mihari
109
111
  end
110
112
 
111
113
  #
112
- # @return [Array<String>]
114
+ # @return [Array<String, RegExp>]
113
115
  #
114
116
  def falsepositives
115
- @falsepositives ||= data[:falsepositives]
117
+ @falsepositives ||= data[:falsepositives].map { |fp| normalize_falsepositive fp }
116
118
  end
117
119
 
118
120
  #