mihari 5.2.2 → 5.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/build_frontend.sh +1 -9
  3. data/frontend/.browserslistrc +3 -0
  4. data/frontend/.eslintrc.js +33 -0
  5. data/frontend/.gitignore +25 -0
  6. data/frontend/README.md +3 -0
  7. data/frontend/babel.config.js +3 -0
  8. data/frontend/index.html +21 -0
  9. data/frontend/jest.config.js +9 -0
  10. data/frontend/package-lock.json +13216 -0
  11. data/frontend/package.json +54 -0
  12. data/frontend/public/favicon.ico +0 -0
  13. data/frontend/scripts/swagger_doc_to_yaml.rb +23 -0
  14. data/frontend/src/App.vue +27 -0
  15. data/frontend/src/api-helper.ts +113 -0
  16. data/frontend/src/api.ts +105 -0
  17. data/frontend/src/components/ErrorMessage.vue +32 -0
  18. data/frontend/src/components/Loading.vue +15 -0
  19. data/frontend/src/components/Navbar.vue +59 -0
  20. data/frontend/src/components/Pagination.vue +126 -0
  21. data/frontend/src/components/alert/Alert.vue +92 -0
  22. data/frontend/src/components/alert/Alerts.vue +66 -0
  23. data/frontend/src/components/alert/AlertsWithPagination.vue +91 -0
  24. data/frontend/src/components/alert/AlertsWrapper.vue +141 -0
  25. data/frontend/src/components/alert/Form.vue +185 -0
  26. data/frontend/src/components/artifact/AS.vue +29 -0
  27. data/frontend/src/components/artifact/Artifact.vue +321 -0
  28. data/frontend/src/components/artifact/ArtifactTag.vue +70 -0
  29. data/frontend/src/components/artifact/ArtifactTags.vue +29 -0
  30. data/frontend/src/components/artifact/ArtifactWrapper.vue +62 -0
  31. data/frontend/src/components/artifact/CPEs.vue +23 -0
  32. data/frontend/src/components/artifact/DnsRecords.vue +38 -0
  33. data/frontend/src/components/artifact/Ports.vue +23 -0
  34. data/frontend/src/components/artifact/ReverseDnsNames.vue +31 -0
  35. data/frontend/src/components/artifact/Tags.vue +29 -0
  36. data/frontend/src/components/artifact/WhoisRecord.vue +49 -0
  37. data/frontend/src/components/config/Configs.vue +68 -0
  38. data/frontend/src/components/config/ConfigsWrapper.vue +40 -0
  39. data/frontend/src/components/link/Link.vue +32 -0
  40. data/frontend/src/components/link/Links.vue +47 -0
  41. data/frontend/src/components/rule/EditRule.vue +74 -0
  42. data/frontend/src/components/rule/EditRuleWrapper.vue +56 -0
  43. data/frontend/src/components/rule/Form.vue +160 -0
  44. data/frontend/src/components/rule/InputForm.vue +80 -0
  45. data/frontend/src/components/rule/NewRule.vue +60 -0
  46. data/frontend/src/components/rule/Rule.vue +108 -0
  47. data/frontend/src/components/rule/RuleWrapper.vue +62 -0
  48. data/frontend/src/components/rule/Rules.vue +88 -0
  49. data/frontend/src/components/rule/RulesWrapper.vue +130 -0
  50. data/frontend/src/components/rule/YAML.vue +47 -0
  51. data/frontend/src/components/tag/Tag.vue +73 -0
  52. data/frontend/src/components/tag/Tags.vue +37 -0
  53. data/frontend/src/countries.ts +350 -0
  54. data/frontend/src/index.ts +23 -0
  55. data/frontend/src/links/anyrun.ts +19 -0
  56. data/frontend/src/links/base.ts +14 -0
  57. data/frontend/src/links/censys.ts +20 -0
  58. data/frontend/src/links/crtsh.ts +20 -0
  59. data/frontend/src/links/dnslytics.ts +38 -0
  60. data/frontend/src/links/greynoise.ts +20 -0
  61. data/frontend/src/links/index.ts +40 -0
  62. data/frontend/src/links/intezer.ts +20 -0
  63. data/frontend/src/links/otx.ts +33 -0
  64. data/frontend/src/links/securitytrails.ts +38 -0
  65. data/frontend/src/links/shodan.ts +20 -0
  66. data/frontend/src/links/urlscan.ts +50 -0
  67. data/frontend/src/links/virustotal.ts +72 -0
  68. data/frontend/src/main.ts +11 -0
  69. data/frontend/src/router/index.ts +57 -0
  70. data/frontend/src/rule.ts +14 -0
  71. data/frontend/src/shims-vue.d.ts +6 -0
  72. data/frontend/src/swagger.yaml +737 -0
  73. data/frontend/src/types.ts +188 -0
  74. data/frontend/src/utils.ts +60 -0
  75. data/frontend/src/views/Alerts.vue +20 -0
  76. data/frontend/src/views/Artifact.vue +44 -0
  77. data/frontend/src/views/Configs.vue +20 -0
  78. data/frontend/src/views/EditRule.vue +44 -0
  79. data/frontend/src/views/NewRule.vue +26 -0
  80. data/frontend/src/views/Rule.vue +44 -0
  81. data/frontend/src/views/Rules.vue +20 -0
  82. data/frontend/tests/unit/utils.spec.ts +7 -0
  83. data/frontend/tsconfig.json +40 -0
  84. data/frontend/vite.config.js +24 -0
  85. data/lefthook.yml +10 -0
  86. data/lib/mihari/analyzers/base.rb +22 -5
  87. data/lib/mihari/analyzers/binaryedge.rb +0 -1
  88. data/lib/mihari/analyzers/censys.rb +7 -2
  89. data/lib/mihari/analyzers/circl.rb +1 -1
  90. data/lib/mihari/analyzers/passivetotal.rb +1 -1
  91. data/lib/mihari/analyzers/rule.rb +43 -73
  92. data/lib/mihari/analyzers/virustotal_intelligence.rb +1 -2
  93. data/lib/mihari/clients/base.rb +1 -1
  94. data/lib/mihari/commands/database.rb +12 -11
  95. data/lib/mihari/commands/rule.rb +47 -45
  96. data/lib/mihari/commands/search.rb +73 -45
  97. data/lib/mihari/commands/version.rb +8 -6
  98. data/lib/mihari/commands/web.rb +26 -23
  99. data/lib/mihari/emitters/base.rb +14 -1
  100. data/lib/mihari/emitters/database.rb +3 -10
  101. data/lib/mihari/emitters/misp.rb +16 -5
  102. data/lib/mihari/emitters/slack.rb +13 -15
  103. data/lib/mihari/emitters/the_hive.rb +17 -19
  104. data/lib/mihari/emitters/webhook.rb +23 -23
  105. data/lib/mihari/enrichers/whois.rb +1 -0
  106. data/lib/mihari/entities/rule.rb +1 -1
  107. data/lib/mihari/entities/tag.rb +1 -1
  108. data/lib/mihari/feed/parser.rb +1 -0
  109. data/lib/mihari/feed/reader.rb +29 -14
  110. data/lib/mihari/mixins/configurable.rb +13 -4
  111. data/lib/mihari/schemas/analyzer.rb +2 -7
  112. data/lib/mihari/schemas/rule.rb +1 -1
  113. data/lib/mihari/structs/censys.rb +96 -82
  114. data/lib/mihari/structs/config.rb +46 -21
  115. data/lib/mihari/structs/google_public_dns.rb +27 -23
  116. data/lib/mihari/structs/greynoise.rb +44 -38
  117. data/lib/mihari/structs/onyphe.rb +34 -30
  118. data/lib/mihari/structs/rule.rb +1 -1
  119. data/lib/mihari/structs/shodan.rb +77 -69
  120. data/lib/mihari/structs/urlscan.rb +42 -36
  121. data/lib/mihari/structs/virustotal_intelligence.rb +57 -49
  122. data/lib/mihari/type_checker.rb +10 -8
  123. data/lib/mihari/version.rb +1 -1
  124. data/lib/mihari/web/public/assets/index-ac4e5ffa.js +50 -0
  125. data/lib/mihari/web/public/index.html +1 -1
  126. data/mihari.gemspec +8 -8
  127. metadata +103 -22
  128. data/.gitmodules +0 -0
  129. data/.overcommit.yml +0 -12
  130. data/lib/mihari/web/public/assets/index-cbe1734c.js +0 -50
@@ -36,32 +36,57 @@ module Mihari
36
36
  attributes[:values]
37
37
  end
38
38
 
39
- #
40
- # @param [Class<Mihari::Analyzers::Base>, Class<Mihari::Emitters::Base>] klass
41
- #
42
- # @return [Mihari::Structs::Config, nil] config
43
- #
44
- def self.from_class(klass)
45
- return nil if klass == Mihari::Analyzers::Rule
39
+ class << self
40
+ #
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)
46
51
 
47
- name = klass.to_s.split("::").last.to_s
52
+ nil
53
+ end
48
54
 
49
- is_analyzer = klass.ancestors.include?(Mihari::Analyzers::Base)
50
- is_emitter = klass.ancestors.include?(Mihari::Emitters::Base)
51
- is_enricher = klass.ancestors.include?(Mihari::Enrichers::Base)
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
52
67
 
53
- type = "Analyzer"
54
- type = "Emitter" if is_emitter
55
- type = "Enricher" if is_enricher
68
+ #
69
+ # @param [Class<Mihari::Analyzers::Base>, Class<Mihari::Emitters::Base>, Class<Mihari::Enrichers::Base>] klass
70
+ #
71
+ # @return [Mihari::Structs::Config, nil] config
72
+ #
73
+ def from_class(klass)
74
+ return nil if klass == Mihari::Analyzers::Rule
56
75
 
57
- begin
58
- instance = is_analyzer ? klass.new("dummy") : klass.new
59
- is_configured = instance.configured?
60
- values = instance.configuration_values
76
+ type = get_type(klass)
77
+ return nil if type.nil?
61
78
 
62
- new(name: name, values: values, is_configured: is_configured, type: type)
63
- rescue ArgumentError => _e
64
- nil
79
+ begin
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
88
+ nil
89
+ end
65
90
  end
66
91
  end
67
92
  end
@@ -37,19 +37,21 @@ module Mihari
37
37
  attributes[:resource_type]
38
38
  end
39
39
 
40
- #
41
- # @param [Hash] d
42
- #
43
- # @return [Answer]
44
- #
45
- def self.from_dynamic!(d)
46
- d = Types::Hash[d]
47
- resource_type = INT_TYPE_TO_TYPE[d.fetch("type")]
48
- new(
49
- name: d.fetch("name"),
50
- data: d.fetch("data"),
51
- resource_type: resource_type
52
- )
40
+ class << self
41
+ #
42
+ # @param [Hash] d
43
+ #
44
+ # @return [Answer]
45
+ #
46
+ def from_dynamic!(d)
47
+ d = Types::Hash[d]
48
+ resource_type = INT_TYPE_TO_TYPE[d.fetch("type")]
49
+ new(
50
+ name: d.fetch("name"),
51
+ data: d.fetch("data"),
52
+ resource_type: resource_type
53
+ )
54
+ end
53
55
  end
54
56
  end
55
57
 
@@ -63,16 +65,18 @@ module Mihari
63
65
  attributes[:answers]
64
66
  end
65
67
 
66
- #
67
- # @param [Hash] d
68
- #
69
- # @return [Response]
70
- #
71
- def self.from_dynamic!(d)
72
- d = Types::Hash[d]
73
- new(
74
- answers: d.fetch("Answer", []).map { |x| Answer.from_dynamic!(x) }
75
- )
68
+ class << self
69
+ #
70
+ # @param [Hash] d
71
+ #
72
+ # @return [Response]
73
+ #
74
+ def from_dynamic!(d)
75
+ d = Types::Hash[d]
76
+ new(
77
+ answers: d.fetch("Answer", []).map { |x| Answer.from_dynamic!(x) }
78
+ )
79
+ end
76
80
  end
77
81
  end
78
82
  end
@@ -48,18 +48,20 @@ module Mihari
48
48
  )
49
49
  end
50
50
 
51
- #
52
- # @param [Hash] d
53
- #
54
- # @return [Metadata]
55
- #
56
- def self.from_dynamic!(d)
57
- d = Types::Hash[d]
58
- new(
59
- country: d.fetch("country"),
60
- country_code: d.fetch("country_code"),
61
- asn: d.fetch("asn")
62
- )
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
63
65
  end
64
66
  end
65
67
 
@@ -101,18 +103,20 @@ module Mihari
101
103
  )
102
104
  end
103
105
 
104
- #
105
- # @param [Hash] d
106
- #
107
- # @return [Datum]
108
- #
109
- def self.from_dynamic!(d)
110
- d = Types::Hash[d]
111
- new(
112
- ip: d.fetch("ip"),
113
- metadata: Metadata.from_dynamic!(d.fetch("metadata")),
114
- metadata_: d
115
- )
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
116
120
  end
117
121
  end
118
122
 
@@ -165,20 +169,22 @@ module Mihari
165
169
  data.map { |datum| datum.to_artifact }
166
170
  end
167
171
 
168
- #
169
- # @param [Hash] d
170
- #
171
- # @return [Response]
172
- #
173
- def self.from_dynamic!(d)
174
- d = Types::Hash[d]
175
- new(
176
- complete: d.fetch("complete"),
177
- count: d.fetch("count"),
178
- data: d.fetch("data").map { |x| Datum.from_dynamic!(x) },
179
- message: d.fetch("message"),
180
- query: d.fetch("query")
181
- )
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
182
188
  end
183
189
  end
184
190
  end
@@ -70,20 +70,22 @@ module Mihari
70
70
  Mihari::AutonomousSystem.new(asn: normalize_asn(asn))
71
71
  end
72
72
 
73
- #
74
- # @param [Hash] d
75
- #
76
- # @return [Result]
77
- #
78
- def self.from_dynamic!(d)
79
- d = Types::Hash[d]
80
- new(
81
- asn: d.fetch("asn"),
82
- ip: d.fetch("ip"),
83
- # Onyphe's country = 2-letter country code
84
- country_code: d["country"],
85
- metadata: d
86
- )
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
87
89
  end
88
90
  end
89
91
 
@@ -152,22 +154,24 @@ module Mihari
152
154
  results.map(&:to_artifact)
153
155
  end
154
156
 
155
- #
156
- # @param [Hash] d
157
- #
158
- # @return [Response]
159
- #
160
- def self.from_dynamic!(d)
161
- d = Types::Hash[d]
162
- new(
163
- count: d.fetch("count"),
164
- error: d.fetch("error"),
165
- max_page: d.fetch("max_page"),
166
- page: d.fetch("page").to_i,
167
- results: d.fetch("results").map { |x| Result.from_dynamic!(x) },
168
- status: d.fetch("status"),
169
- total: d.fetch("total")
170
- )
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
171
175
  end
172
176
  end
173
177
  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
@@ -33,17 +33,19 @@ module Mihari
33
33
  )
34
34
  end
35
35
 
36
- #
37
- # @param [Hash] d
38
- #
39
- # @return [Location]
40
- #
41
- def self.from_dynamic!(d)
42
- d = Types::Hash[d]
43
- new(
44
- country_code: d["country_code"],
45
- country_name: d["country_name"]
46
- )
36
+ class << self
37
+ #
38
+ # @param [Hash] d
39
+ #
40
+ # @return [Location]
41
+ #
42
+ def from_dynamic!(d)
43
+ d = Types::Hash[d]
44
+ new(
45
+ country_code: d["country_code"],
46
+ country_name: d["country_name"]
47
+ )
48
+ end
47
49
  end
48
50
  end
49
51
 
@@ -109,30 +111,32 @@ module Mihari
109
111
  Mihari::AutonomousSystem.new(asn: normalize_asn(asn))
110
112
  end
111
113
 
112
- #
113
- # @param [Hash] d
114
- #
115
- # @return [Match]
116
- #
117
- def self.from_dynamic!(d)
118
- d = Types::Hash[d]
119
-
120
- # hostnames should be an array of string but sometimes Shodan returns a string
121
- # e.g. "hostnames": "set(['149.28.146.131.vultr.com', 'rebs.ga'])",
122
- # https://github.com/ninoseki/mihari/issues/424
123
- # so use an empty array if hostnames is a string
124
- hostnames = d.fetch("hostnames")
125
- hostnames = [] if hostnames.is_a?(String)
126
-
127
- new(
128
- asn: d["asn"],
129
- hostnames: hostnames,
130
- location: Location.from_dynamic!(d.fetch("location")),
131
- domains: d.fetch("domains"),
132
- ip_str: d.fetch("ip_str"),
133
- port: d.fetch("port"),
134
- metadata: d
135
- )
114
+ class << self
115
+ #
116
+ # @param [Hash] d
117
+ #
118
+ # @return [Match]
119
+ #
120
+ def from_dynamic!(d)
121
+ d = Types::Hash[d]
122
+
123
+ # hostnames should be an array of string but sometimes Shodan returns a string
124
+ # e.g. "hostnames": "set(['149.28.146.131.vultr.com', 'rebs.ga'])",
125
+ # https://github.com/ninoseki/mihari/issues/424
126
+ # so use an empty array if hostnames is a string
127
+ hostnames = d.fetch("hostnames")
128
+ hostnames = [] if hostnames.is_a?(String)
129
+
130
+ new(
131
+ asn: d["asn"],
132
+ hostnames: hostnames,
133
+ location: Location.from_dynamic!(d.fetch("location")),
134
+ domains: d.fetch("domains"),
135
+ ip_str: d.fetch("ip_str"),
136
+ port: d.fetch("port"),
137
+ metadata: d
138
+ )
139
+ end
136
140
  end
137
141
  end
138
142
 
@@ -211,17 +215,19 @@ module Mihari
211
215
  end
212
216
  end
213
217
 
214
- #
215
- # @param [Hash] d
216
- #
217
- # @return [Result]
218
- #
219
- def self.from_dynamic!(d)
220
- d = Types::Hash[d]
221
- new(
222
- matches: d.fetch("matches", []).map { |x| Match.from_dynamic!(x) },
223
- total: d.fetch("total")
224
- )
218
+ class << self
219
+ #
220
+ # @param [Hash] d
221
+ #
222
+ # @return [Result]
223
+ #
224
+ def from_dynamic!(d)
225
+ d = Types::Hash[d]
226
+ new(
227
+ matches: d.fetch("matches", []).map { |x| Match.from_dynamic!(x) },
228
+ total: d.fetch("total")
229
+ )
230
+ end
225
231
  end
226
232
  end
227
233
 
@@ -275,30 +281,32 @@ module Mihari
275
281
  attributes[:vulns]
276
282
  end
277
283
 
278
- #
279
- # @param [Hash] d
280
- #
281
- # @return [InternetDBResponse]
282
- #
283
- def self.from_dynamic!(d)
284
- d = Types::Hash[d]
285
- new(
286
- ip: d.fetch("ip"),
287
- ports: d.fetch("ports"),
288
- cpes: d.fetch("cpes"),
289
- hostnames: d.fetch("hostnames"),
290
- tags: d.fetch("tags"),
291
- vulns: d.fetch("vulns")
292
- )
293
- end
284
+ class << self
285
+ #
286
+ # @param [Hash] d
287
+ #
288
+ # @return [InternetDBResponse]
289
+ #
290
+ def from_dynamic!(d)
291
+ d = Types::Hash[d]
292
+ new(
293
+ ip: d.fetch("ip"),
294
+ ports: d.fetch("ports"),
295
+ cpes: d.fetch("cpes"),
296
+ hostnames: d.fetch("hostnames"),
297
+ tags: d.fetch("tags"),
298
+ vulns: d.fetch("vulns")
299
+ )
300
+ end
294
301
 
295
- #
296
- # @param [String] json
297
- #
298
- # @return [InternetDBResponse]
299
- #
300
- def self.from_json!(json)
301
- from_dynamic!(JSON.parse(json))
302
+ #
303
+ # @param [String] json
304
+ #
305
+ # @return [InternetDBResponse]
306
+ #
307
+ def from_json!(json)
308
+ from_dynamic!(JSON.parse(json))
309
+ end
302
310
  end
303
311
  end
304
312
  end
@@ -29,18 +29,20 @@ module Mihari
29
29
  attributes[:url]
30
30
  end
31
31
 
32
- #
33
- # @param [Hash] d
34
- #
35
- # @return [Page]
36
- #
37
- def self.from_dynamic!(d)
38
- d = Types::Hash[d]
39
- new(
40
- domain: d["domain"],
41
- ip: d["ip"],
42
- url: d.fetch("url")
43
- )
32
+ class << self
33
+ #
34
+ # @param [Hash] d
35
+ #
36
+ # @return [Page]
37
+ #
38
+ def from_dynamic!(d)
39
+ d = Types::Hash[d]
40
+ new(
41
+ domain: d["domain"],
42
+ ip: d["ip"],
43
+ url: d.fetch("url")
44
+ )
45
+ end
44
46
  end
45
47
  end
46
48
 
@@ -88,19 +90,21 @@ module Mihari
88
90
  end
89
91
  end
90
92
 
91
- #
92
- # @param [Hash] d
93
- #
94
- # @return [Result]
95
- #
96
- def self.from_dynamic!(d)
97
- d = Types::Hash[d]
98
- new(
99
- page: Page.from_dynamic!(d.fetch("page")),
100
- id: d.fetch("_id"),
101
- sort: d.fetch("sort"),
102
- metadata: d
103
- )
93
+ class << self
94
+ #
95
+ # @param [Hash] d
96
+ #
97
+ # @return [Result]
98
+ #
99
+ def from_dynamic!(d)
100
+ d = Types::Hash[d]
101
+ new(
102
+ page: Page.from_dynamic!(d.fetch("page")),
103
+ id: d.fetch("_id"),
104
+ sort: d.fetch("sort"),
105
+ metadata: d
106
+ )
107
+ end
104
108
  end
105
109
  end
106
110
 
@@ -129,17 +133,19 @@ module Mihari
129
133
  results.map(&:to_artifacts).flatten
130
134
  end
131
135
 
132
- #
133
- # @param [Hash] d
134
- #
135
- # @return [Response]
136
- #
137
- def self.from_dynamic!(d)
138
- d = Types::Hash[d]
139
- new(
140
- results: d.fetch("results").map { |x| Result.from_dynamic!(x) },
141
- has_more: d.fetch("has_more")
142
- )
136
+ class << self
137
+ #
138
+ # @param [Hash] d
139
+ #
140
+ # @return [Response]
141
+ #
142
+ def from_dynamic!(d)
143
+ d = Types::Hash[d]
144
+ new(
145
+ results: d.fetch("results").map { |x| Result.from_dynamic!(x) },
146
+ has_more: d.fetch("has_more")
147
+ )
148
+ end
143
149
  end
144
150
  end
145
151
  end