mihari 5.6.1 → 5.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (153) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +5 -1
  3. data/README.md +1 -0
  4. data/config.ru +1 -1
  5. data/docs/analyzers/fofa.md +31 -0
  6. data/docs/analyzers/index.md +1 -0
  7. data/frontend/package-lock.json +183 -186
  8. data/frontend/package.json +10 -10
  9. data/frontend/src/components/alert/Form.vue +1 -14
  10. data/frontend/src/components/artifact/AS.vue +2 -8
  11. data/frontend/src/components/artifact/DnsRecords.vue +2 -8
  12. data/frontend/src/components/artifact/ReverseDnsNames.vue +2 -10
  13. data/frontend/src/components/artifact/WhoisRecord.vue +1 -1
  14. data/lib/mihari/{base.rb → actor.rb} +27 -3
  15. data/lib/mihari/analyzers/base.rb +16 -20
  16. data/lib/mihari/analyzers/binaryedge.rb +4 -1
  17. data/lib/mihari/analyzers/censys.rb +5 -3
  18. data/lib/mihari/analyzers/circl.rb +4 -1
  19. data/lib/mihari/analyzers/crtsh.rb +4 -1
  20. data/lib/mihari/analyzers/dnstwister.rb +4 -1
  21. data/lib/mihari/analyzers/feed.rb +3 -0
  22. data/lib/mihari/analyzers/fofa.rb +65 -0
  23. data/lib/mihari/analyzers/greynoise.rb +4 -1
  24. data/lib/mihari/analyzers/hunterhow.rb +7 -2
  25. data/lib/mihari/analyzers/onyphe.rb +4 -1
  26. data/lib/mihari/analyzers/otx.rb +4 -1
  27. data/lib/mihari/analyzers/passivetotal.rb +5 -2
  28. data/lib/mihari/analyzers/pulsedive.rb +4 -1
  29. data/lib/mihari/analyzers/securitytrails.rb +5 -2
  30. data/lib/mihari/analyzers/shodan.rb +4 -1
  31. data/lib/mihari/analyzers/urlscan.rb +5 -2
  32. data/lib/mihari/analyzers/virustotal.rb +9 -6
  33. data/lib/mihari/analyzers/virustotal_intelligence.rb +4 -1
  34. data/lib/mihari/analyzers/zoomeye.rb +8 -5
  35. data/lib/mihari/cli/alert.rb +3 -0
  36. data/lib/mihari/cli/base.rb +3 -0
  37. data/lib/mihari/cli/database.rb +3 -0
  38. data/lib/mihari/cli/main.rb +3 -0
  39. data/lib/mihari/cli/rule.rb +3 -0
  40. data/lib/mihari/clients/base.rb +3 -0
  41. data/lib/mihari/clients/binaryedge.rb +5 -2
  42. data/lib/mihari/clients/censys.rb +7 -4
  43. data/lib/mihari/clients/circl.rb +3 -0
  44. data/lib/mihari/clients/crtsh.rb +5 -2
  45. data/lib/mihari/clients/dnstwister.rb +3 -0
  46. data/lib/mihari/clients/fofa.rb +83 -0
  47. data/lib/mihari/clients/greynoise.rb +5 -2
  48. data/lib/mihari/clients/hunterhow.rb +5 -2
  49. data/lib/mihari/clients/misp.rb +3 -0
  50. data/lib/mihari/clients/onyphe.rb +5 -2
  51. data/lib/mihari/clients/otx.rb +3 -0
  52. data/lib/mihari/clients/passivetotal.rb +7 -4
  53. data/lib/mihari/clients/publsedive.rb +4 -1
  54. data/lib/mihari/clients/securitytrails.rb +6 -3
  55. data/lib/mihari/clients/shodan.rb +5 -2
  56. data/lib/mihari/clients/the_hive.rb +3 -0
  57. data/lib/mihari/clients/urlscan.rb +7 -4
  58. data/lib/mihari/clients/virustotal.rb +5 -2
  59. data/lib/mihari/clients/zoomeye.rb +3 -0
  60. data/lib/mihari/commands/alert.rb +5 -14
  61. data/lib/mihari/commands/database.rb +3 -0
  62. data/lib/mihari/commands/rule.rb +11 -11
  63. data/lib/mihari/commands/search.rb +9 -6
  64. data/lib/mihari/commands/version.rb +3 -0
  65. data/lib/mihari/commands/web.rb +4 -1
  66. data/lib/mihari/config.rb +139 -150
  67. data/lib/mihari/constants.rb +1 -1
  68. data/lib/mihari/database.rb +6 -0
  69. data/lib/mihari/emitters/base.rb +16 -25
  70. data/lib/mihari/emitters/database.rb +10 -9
  71. data/lib/mihari/emitters/misp.rb +20 -41
  72. data/lib/mihari/emitters/slack.rb +16 -13
  73. data/lib/mihari/emitters/the_hive.rb +18 -46
  74. data/lib/mihari/emitters/webhook.rb +34 -23
  75. data/lib/mihari/enrichers/base.rb +16 -15
  76. data/lib/mihari/enrichers/google_public_dns.rb +6 -5
  77. data/lib/mihari/enrichers/ipinfo.rb +10 -8
  78. data/lib/mihari/enrichers/shodan.rb +4 -6
  79. data/lib/mihari/enrichers/whois.rb +13 -10
  80. data/lib/mihari/errors.rb +6 -0
  81. data/lib/mihari/feed/parser.rb +3 -0
  82. data/lib/mihari/feed/reader.rb +3 -0
  83. data/lib/mihari/http.rb +6 -0
  84. data/lib/mihari/mixins/autonomous_system.rb +3 -0
  85. data/lib/mihari/mixins/configurable.rb +3 -0
  86. data/lib/mihari/mixins/error_notification.rb +3 -0
  87. data/lib/mihari/mixins/falsepositive.rb +3 -0
  88. data/lib/mihari/mixins/refang.rb +3 -0
  89. data/lib/mihari/mixins/retriable.rb +6 -2
  90. data/lib/mihari/models/alert.rb +78 -73
  91. data/lib/mihari/models/artifact.rb +186 -178
  92. data/lib/mihari/models/autonomous_system.rb +25 -20
  93. data/lib/mihari/models/cpe.rb +24 -19
  94. data/lib/mihari/models/dns.rb +27 -22
  95. data/lib/mihari/models/geolocation.rb +25 -20
  96. data/lib/mihari/models/port.rb +24 -19
  97. data/lib/mihari/models/reverse_dns.rb +24 -19
  98. data/lib/mihari/models/rule.rb +71 -66
  99. data/lib/mihari/models/tag.rb +8 -3
  100. data/lib/mihari/models/tagging.rb +8 -3
  101. data/lib/mihari/models/whois.rb +20 -17
  102. data/lib/mihari/rule.rb +357 -0
  103. data/lib/mihari/schemas/alert.rb +3 -0
  104. data/lib/mihari/schemas/analyzer.rb +105 -87
  105. data/lib/mihari/schemas/emitter.rb +12 -5
  106. data/lib/mihari/schemas/enricher.rb +11 -4
  107. data/lib/mihari/schemas/macros.rb +4 -0
  108. data/lib/mihari/schemas/mixins.rb +20 -0
  109. data/lib/mihari/schemas/rule.rb +6 -10
  110. data/lib/mihari/service.rb +16 -0
  111. data/lib/mihari/services/alert_builder.rb +8 -5
  112. data/lib/mihari/services/alert_proxy.rb +16 -7
  113. data/lib/mihari/services/alert_runner.rb +10 -14
  114. data/lib/mihari/services/rule_builder.rb +10 -7
  115. data/lib/mihari/services/rule_runner.rb +11 -13
  116. data/lib/mihari/structs/binaryedge.rb +14 -29
  117. data/lib/mihari/structs/censys.rb +54 -133
  118. data/lib/mihari/structs/config.rb +20 -31
  119. data/lib/mihari/structs/filters.rb +38 -0
  120. data/lib/mihari/structs/fofa.rb +44 -0
  121. data/lib/mihari/structs/google_public_dns.rb +10 -28
  122. data/lib/mihari/structs/greynoise.rb +38 -89
  123. data/lib/mihari/structs/hunterhow.rb +27 -25
  124. data/lib/mihari/structs/ipinfo.rb +14 -35
  125. data/lib/mihari/structs/onyphe.rb +36 -81
  126. data/lib/mihari/structs/shodan.rb +53 -118
  127. data/lib/mihari/structs/urlscan.rb +27 -66
  128. data/lib/mihari/structs/virustotal_intelligence.rb +23 -59
  129. data/lib/mihari/type_checker.rb +4 -0
  130. data/lib/mihari/types.rb +3 -0
  131. data/lib/mihari/version.rb +1 -1
  132. data/lib/mihari/web/api.rb +15 -10
  133. data/lib/mihari/web/app.rb +59 -54
  134. data/lib/mihari/web/endpoints/alerts.rb +94 -89
  135. data/lib/mihari/web/endpoints/artifacts.rb +115 -110
  136. data/lib/mihari/web/endpoints/configs.rb +18 -13
  137. data/lib/mihari/web/endpoints/ip_addresses.rb +21 -16
  138. data/lib/mihari/web/endpoints/rules.rb +202 -204
  139. data/lib/mihari/web/endpoints/tags.rb +41 -36
  140. data/lib/mihari/web/middleware/connection_adapter.rb +16 -9
  141. data/lib/mihari/web/middleware/error_notification_adapter.rb +17 -10
  142. data/lib/mihari/web/public/assets/{index-9cc489e6.js → index-821134e2.js} +54 -54
  143. data/lib/mihari/web/public/assets/mode-yaml-24faa242.js +8 -0
  144. data/lib/mihari/web/public/index.html +1 -1
  145. data/lib/mihari.rb +30 -13
  146. data/mihari.gemspec +9 -3
  147. data/mkdocs.yml +3 -2
  148. data/requirements.txt +1 -1
  149. metadata +44 -26
  150. data/lib/mihari/analyzers/rule.rb +0 -232
  151. data/lib/mihari/services/rule_proxy.rb +0 -182
  152. data/lib/mihari/templates/rule.yml.erb +0 -5
  153. data/lib/mihari/web/public/assets/mode-yaml-a21faa53.js +0 -8
@@ -0,0 +1,8 @@
1
+ ace.define("ace/mode/yaml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment",regex:"#.*$"},{token:"list.markup",regex:/^(?:-{3}|\.{3})\s*(?=#|$)/},{token:"list.markup",regex:/^\s*[\-?](?:$|\s)/},{token:"constant",regex:"!![\\w//]+"},{token:"constant.language",regex:"[&\\*][a-zA-Z0-9-_]+"},{token:["meta.tag","keyword"],regex:/^(\s*\w[^\s:]*?)(:(?=\s|$))/},{token:["meta.tag","keyword"],regex:/(\w[^\s:]*?)(\s*:(?=\s|$))/},{token:"keyword.operator",regex:"<<\\w*:\\w*"},{token:"keyword.operator",regex:"-\\s*(?=[{])"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:/[|>][-+\d]*(?:$|\s+(?:$|#))/,onMatch:function(e,t,n,r){r=r.replace(/ #.*/,"");var i=/^ *((:\s*)?-(\s*[^|>])?)?/.exec(r)[0].replace(/\S\s*$/,"").length,s=parseInt(/\d+[\s+-]*$/.exec(r));return s?(i+=s-1,this.next="mlString"):this.next="mlStringPre",n.length?(n[0]=this.next,n[1]=i):(n.push(this.next),n.push(i)),this.token},next:"mlString"},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"constant.numeric",regex:/(\b|[+\-\.])[\d_]+(?:(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)(?=[^\d-\w]|$)$/},{token:"constant.numeric",regex:/[+\-]?\.inf\b|NaN\b|0x[\dA-Fa-f_]+|0b[10_]+/},{token:"constant.language.boolean",regex:"\\b(?:true|false|TRUE|FALSE|True|False|yes|no)\\b"},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"},{token:"text",regex:/[^\s,:\[\]\{\}]+/}],mlStringPre:[{token:"indent",regex:/^ *$/},{token:"indent",regex:/^ */,onMatch:function(e,t,n){var r=n[1];return r>=e.length?(this.next="start",n.shift(),n.shift()):(n[1]=e.length-1,this.next=n[0]="mlString"),this.token},next:"mlString"},{defaultToken:"string"}],mlString:[{token:"indent",regex:/^ *$/},{token:"indent",regex:/^ */,onMatch:function(e,t,n){var r=n[1];return r>=e.length?(this.next="start",n.splice(0)):this.next="mlString",this.token},next:"mlString"},{token:"string",regex:".+"}]},this.normalizeRules()};r.inherits(s,i),t.YamlHighlightRules=s}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),ace.define("ace/mode/folding/coffee",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode","ace/range"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=e("../../range").Range,o=t.FoldMode=function(){};r.inherits(o,i),function(){this.commentBlock=function(e,t){var n=/\S/,r=e.getLine(t),i=r.search(n);if(i==-1||r[i]!="#")return;var o=r.length,u=e.getLength(),a=t,f=t;while(++t<u){r=e.getLine(t);var l=r.search(n);if(l==-1)continue;if(r[l]!="#")break;f=t}if(f>a){var c=e.getLine(f).length;return new s(a,o,f,c)}},this.getFoldWidgetRange=function(e,t,n){var r=this.indentationBlock(e,n);if(r)return r;r=this.commentBlock(e,n);if(r)return r},this.getFoldWidget=function(e,t,n){var r=e.getLine(n),i=r.search(/\S/),s=e.getLine(n+1),o=e.getLine(n-1),u=o.search(/\S/),a=s.search(/\S/);if(i==-1)return e.foldWidgets[n-1]=u!=-1&&u<a?"start":"","";if(u==-1){if(i==a&&r[i]=="#"&&s[i]=="#")return e.foldWidgets[n-1]="",e.foldWidgets[n+1]="","start"}else if(u==i&&r[i]=="#"&&o[i]=="#"&&e.getLine(n-2).search(/\S/)==-1)return e.foldWidgets[n-1]="start",e.foldWidgets[n+1]="","";return u!=-1&&u<i?e.foldWidgets[n-1]="start":e.foldWidgets[n-1]="",i<a?"start":""}}.call(o.prototype)}),ace.define("ace/mode/folding/yaml",["require","exports","module","ace/lib/oop","ace/mode/folding/coffee","ace/range"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("./coffee").FoldMode,s=e("../../range").Range,o=t.FoldMode=function(){};r.inherits(o,i),function(){this.getFoldWidgetRange=function(e,t,n){var r=/\S/,i=e.getLine(n),o=i.search(r),u=i[o]==="#",a=i[o]==="-";if(o==-1)return;var f=i.length,l=e.getLength(),c=n,h=n;if(u){var p=this.commentBlock(e,n);if(p)return p}else if(a){var p=this.indentationBlock(e,n);if(p)return p}else while(++n<l){var i=e.getLine(n),d=i.search(r);if(d==-1)continue;if(d<=o&&i[o]!=="-"){var v=e.getTokenAt(n,0);if(!v||v.type!=="string")break}h=n}if(h>c){var m=e.getLine(h).length;return new s(c,f,h,m)}},this.getFoldWidget=function(e,t,n){var r=e.getLine(n),i=r.search(/\S/),s=e.getLine(n+1),o=e.getLine(n-1),u=o.search(/\S/),a=s.search(/\S/),f=r[i]==="-";if(i==-1)return e.foldWidgets[n-1]=u!=-1&&u<a?"start":"","";if(u==-1){if(i==a&&r[i]=="#"&&s[i]=="#")return e.foldWidgets[n-1]="",e.foldWidgets[n+1]="","start"}else if(u==i&&r[i]=="#"&&o[i]=="#"&&e.getLine(n-2).search(/\S/)==-1)return e.foldWidgets[n-1]="start",e.foldWidgets[n+1]="","";return u!=-1&&u<i?e.foldWidgets[n-1]="start":u!=-1&&u==i&&f?e.foldWidgets[n-1]="start":e.foldWidgets[n-1]="",i<a?"start":""}}.call(o.prototype)}),ace.define("ace/mode/yaml",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/yaml_highlight_rules","ace/mode/matching_brace_outdent","ace/mode/folding/yaml","ace/worker/worker_client"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./yaml_highlight_rules").YamlHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("./folding/yaml").FoldMode,a=e("../worker/worker_client").WorkerClient,f=function(){this.HighlightRules=s,this.$outdent=new o,this.foldingRules=new u,this.$behaviour=this.$defaultBehaviour};r.inherits(f,i),function(){this.lineCommentStart=["#"],this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t);if(e=="start"){var i=t.match(/^.*[\{\(\[]\s*$/);i&&(r+=n)}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){var t=new a(["ace"],"ace/mode/yaml_worker","YamlWorker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/yaml"}.call(f.prototype),t.Mode=f}); (function() {
2
+ ace.require(["ace/mode/yaml"], function(m) {
3
+ if (typeof module == "object" && typeof exports == "object" && module) {
4
+ module.exports = m;
5
+ }
6
+ });
7
+ })();
8
+
@@ -6,7 +6,7 @@
6
6
  <meta name="viewport" content="width=device-width,initial-scale=1.0" />
7
7
  <link rel="icon" href="/favicon.ico" />
8
8
  <title>Mihari</title>
9
- <script type="module" crossorigin src="/assets/index-9cc489e6.js"></script>
9
+ <script type="module" crossorigin src="/assets/index-821134e2.js"></script>
10
10
  <link rel="stylesheet" href="/assets/index-56fc2187.css">
11
11
  </head>
12
12
  <body>
data/lib/mihari.rb CHANGED
@@ -3,8 +3,6 @@
3
3
  # standard libs
4
4
  require "ipaddr"
5
5
  require "json"
6
- require "net/http"
7
- require "net/https"
8
6
  require "resolv"
9
7
  require "yaml"
10
8
 
@@ -35,7 +33,6 @@ require "addressable/uri"
35
33
  require "awrence"
36
34
  require "email_address"
37
35
  require "memist"
38
- require "net/ping"
39
36
  require "parallel"
40
37
  require "plissken"
41
38
  require "public_suffix"
@@ -60,52 +57,67 @@ require "mihari/mixins/error_notification"
60
57
  require "mihari/mixins/refang"
61
58
  require "mihari/mixins/retriable"
62
59
 
60
+ #
61
+ # Mihari module
62
+ #
63
63
  module Mihari
64
64
  class << self
65
65
  include Memist::Memoizable
66
66
 
67
+ #
68
+ # @return [Array<Mihari::Emitters::Base>]
69
+ #
67
70
  def emitters
68
71
  []
69
72
  end
70
73
  memoize :emitters
71
74
 
72
75
  #
73
- # @return [Hash<String, Mihari::Enrichers::Base>]
76
+ # @return [Hash{String => Mihari::Enrichers::Base}]
74
77
  #
75
78
  def emitter_to_class
76
79
  @emitter_to_class ||= emitters.flat_map do |klass|
77
- klass.class_keys.map { |key| [key.downcase, klass] }
80
+ klass.class_keys.map { |key| [key, klass] }
78
81
  end.to_h
79
82
  end
80
83
 
84
+ #
85
+ # @return [Array<Mihari::Analyzers::Base>]
86
+ #
81
87
  def analyzers
82
88
  []
83
89
  end
84
90
  memoize :analyzers
85
91
 
86
92
  #
87
- # @return [Hash<String, Mihari::Analyzers::Base>]
93
+ # @return [Hash{String => Mihari::Analyzers::Base}]
88
94
  #
89
95
  def analyzer_to_class
90
96
  @analyzer_to_class ||= analyzers.flat_map do |klass|
91
- klass.class_keys.map { |key| [key.downcase, klass] }
97
+ klass.class_keys.map { |key| [key, klass] }
92
98
  end.to_h
93
99
  end
94
100
 
101
+ #
102
+ # @return [Array<Mihari::Enrichers::Base>]
103
+ #
95
104
  def enrichers
96
105
  []
97
106
  end
98
107
  memoize :enrichers
99
108
 
100
109
  #
101
- # @return [Hash<String, Mihari::Enrichers::Base>]
110
+ # @return [Hash{String => Mihari::Enrichers::Base}]
102
111
  #
103
112
  def enricher_to_class
104
113
  @enricher_to_class ||= enrichers.flat_map do |klass|
105
- klass.class_keys.map { |key| [key.downcase, klass] }
114
+ klass.class_keys.map { |key| [key, klass] }
106
115
  end.to_h
107
116
  end
108
117
 
118
+ #
119
+ # @return [Mihari::Config]
120
+ #
109
121
  def config
110
122
  @config ||= Config.new
111
123
  end
@@ -131,10 +143,14 @@ module Mihari
131
143
  end
132
144
 
133
145
  # Core classes
134
- require "mihari/base"
146
+ require "mihari/service"
147
+
148
+ require "mihari/actor"
149
+
135
150
  require "mihari/database"
136
151
  require "mihari/http"
137
152
  require "mihari/type_checker"
153
+ require "mihari/rule"
138
154
 
139
155
  # Enrichers
140
156
  require "mihari/enrichers/base"
@@ -174,6 +190,7 @@ require "mihari/clients/censys"
174
190
  require "mihari/clients/circl"
175
191
  require "mihari/clients/crtsh"
176
192
  require "mihari/clients/dnstwister"
193
+ require "mihari/clients/fofa"
177
194
  require "mihari/clients/greynoise"
178
195
  require "mihari/clients/hunterhow"
179
196
  require "mihari/clients/misp"
@@ -197,6 +214,7 @@ require "mihari/analyzers/circl"
197
214
  require "mihari/analyzers/crtsh"
198
215
  require "mihari/analyzers/dnstwister"
199
216
  require "mihari/analyzers/feed"
217
+ require "mihari/analyzers/fofa"
200
218
  require "mihari/analyzers/greynoise"
201
219
  require "mihari/analyzers/hunterhow"
202
220
  require "mihari/analyzers/onyphe"
@@ -210,8 +228,6 @@ require "mihari/analyzers/virustotal_intelligence"
210
228
  require "mihari/analyzers/virustotal"
211
229
  require "mihari/analyzers/zoomeye"
212
230
 
213
- require "mihari/analyzers/rule"
214
-
215
231
  # Types
216
232
  require "mihari/types"
217
233
 
@@ -223,6 +239,7 @@ require "mihari/structs/binaryedge"
223
239
  require "mihari/structs/censys"
224
240
  require "mihari/structs/config"
225
241
  require "mihari/structs/filters"
242
+ require "mihari/structs/fofa"
226
243
  require "mihari/structs/google_public_dns"
227
244
  require "mihari/structs/greynoise"
228
245
  require "mihari/structs/ipinfo"
@@ -234,6 +251,7 @@ require "mihari/structs/virustotal_intelligence"
234
251
 
235
252
  # Schemas
236
253
  require "mihari/schemas/macros"
254
+ require "mihari/schemas/mixins"
237
255
 
238
256
  require "mihari/schemas/options"
239
257
 
@@ -243,7 +261,6 @@ require "mihari/schemas/rule"
243
261
 
244
262
  # Services
245
263
  require "mihari/services/rule_builder"
246
- require "mihari/services/rule_proxy"
247
264
  require "mihari/services/rule_runner"
248
265
 
249
266
  require "mihari/services/alert_builder"
data/mihari.gemspec CHANGED
@@ -10,6 +10,10 @@ def ci_env?
10
10
  ENV["CI"]
11
11
  end
12
12
 
13
+ def is_ruby2?
14
+ RUBY_VERSION.to_s.start_with?("2.")
15
+ end
16
+
13
17
  Gem::Specification.new do |spec|
14
18
  spec.name = "mihari"
15
19
  spec.version = Mihari::VERSION
@@ -48,7 +52,7 @@ Gem::Specification.new do |spec|
48
52
  spec.add_development_dependency "rb-fsevent", "~> 0.11"
49
53
  spec.add_development_dependency "rerun", "~> 0.14"
50
54
  spec.add_development_dependency "rspec", "~> 3.12"
51
- spec.add_development_dependency "rubocop-rspec", "~> 2.24"
55
+ spec.add_development_dependency "rubocop-rspec", "~> 2.25"
52
56
  spec.add_development_dependency "simplecov-lcov", "~> 0.8"
53
57
  spec.add_development_dependency "standard", "~> 1.31"
54
58
  spec.add_development_dependency "test-prof", "~> 1.2"
@@ -56,6 +60,8 @@ Gem::Specification.new do |spec|
56
60
  spec.add_development_dependency "vcr", "~> 6.2"
57
61
  spec.add_development_dependency "webmock", "~> 3.19"
58
62
 
63
+ spec.add_development_dependency "rubocop-yard", "~> 0.7" unless is_ruby2?
64
+
59
65
  unless ci_env?
60
66
  spec.add_development_dependency "lefthook", "~> 1.5"
61
67
  spec.add_development_dependency "solargraph", "~> 0.49"
@@ -63,6 +69,7 @@ Gem::Specification.new do |spec|
63
69
 
64
70
  spec.add_dependency "activerecord", "7.1.1"
65
71
  spec.add_dependency "addressable", "2.8.5"
72
+ spec.add_dependency "anyway_config", "2.5.4"
66
73
  spec.add_dependency "awrence", "2.0.1"
67
74
  spec.add_dependency "dotenv", "2.8.1"
68
75
  spec.add_dependency "dry-container", "0.11.0"
@@ -80,7 +87,6 @@ Gem::Specification.new do |spec|
80
87
  spec.add_dependency "jr-cli", "0.6.0"
81
88
  spec.add_dependency "launchy", "2.5.2"
82
89
  spec.add_dependency "memist", "2.0.2"
83
- spec.add_dependency "net-ping", "2.0.8"
84
90
  spec.add_dependency "normalize_country", "0.3.2"
85
91
  spec.add_dependency "parallel", "1.23.0"
86
92
  spec.add_dependency "plissken", "2.0.1"
@@ -92,7 +98,7 @@ Gem::Specification.new do |spec|
92
98
  spec.add_dependency "semantic_logger", "4.14.0"
93
99
  spec.add_dependency "sentry-ruby", "5.12.0"
94
100
  spec.add_dependency "slack-notifier", "2.4.0"
95
- spec.add_dependency "sqlite3", "1.6.7"
101
+ spec.add_dependency "sqlite3", "1.6.8"
96
102
  spec.add_dependency "thor", "1.3.0"
97
103
  spec.add_dependency "thor-hollaback", "0.2.1"
98
104
  spec.add_dependency "uuidtools", "2.2.0"
data/mkdocs.yml CHANGED
@@ -29,6 +29,7 @@ markdown_extensions:
29
29
  permalink: true
30
30
  - admonition
31
31
  - pymdownx.details
32
- - pymdownx.superfences
33
- - pymdownx.highlight
34
32
  - pymdownx.extra
33
+ - pymdownx.highlight
34
+ - pymdownx.magiclink
35
+ - pymdownx.superfences
data/requirements.txt CHANGED
@@ -1,2 +1,2 @@
1
1
  mkdocs==1.5.3
2
- mkdocs-material==9.4.6
2
+ mkdocs-material==9.4.7
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mihari
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.6.1
4
+ version: 5.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Manabu Niseki
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-10-23 00:00:00.000000000 Z
11
+ date: 2023-11-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -184,14 +184,14 @@ dependencies:
184
184
  requirements:
185
185
  - - "~>"
186
186
  - !ruby/object:Gem::Version
187
- version: '2.24'
187
+ version: '2.25'
188
188
  type: :development
189
189
  prerelease: false
190
190
  version_requirements: !ruby/object:Gem::Requirement
191
191
  requirements:
192
192
  - - "~>"
193
193
  - !ruby/object:Gem::Version
194
- version: '2.24'
194
+ version: '2.25'
195
195
  - !ruby/object:Gem::Dependency
196
196
  name: simplecov-lcov
197
197
  requirement: !ruby/object:Gem::Requirement
@@ -276,6 +276,20 @@ dependencies:
276
276
  - - "~>"
277
277
  - !ruby/object:Gem::Version
278
278
  version: '3.19'
279
+ - !ruby/object:Gem::Dependency
280
+ name: rubocop-yard
281
+ requirement: !ruby/object:Gem::Requirement
282
+ requirements:
283
+ - - "~>"
284
+ - !ruby/object:Gem::Version
285
+ version: '0.7'
286
+ type: :development
287
+ prerelease: false
288
+ version_requirements: !ruby/object:Gem::Requirement
289
+ requirements:
290
+ - - "~>"
291
+ - !ruby/object:Gem::Version
292
+ version: '0.7'
279
293
  - !ruby/object:Gem::Dependency
280
294
  name: lefthook
281
295
  requirement: !ruby/object:Gem::Requirement
@@ -332,6 +346,20 @@ dependencies:
332
346
  - - '='
333
347
  - !ruby/object:Gem::Version
334
348
  version: 2.8.5
349
+ - !ruby/object:Gem::Dependency
350
+ name: anyway_config
351
+ requirement: !ruby/object:Gem::Requirement
352
+ requirements:
353
+ - - '='
354
+ - !ruby/object:Gem::Version
355
+ version: 2.5.4
356
+ type: :runtime
357
+ prerelease: false
358
+ version_requirements: !ruby/object:Gem::Requirement
359
+ requirements:
360
+ - - '='
361
+ - !ruby/object:Gem::Version
362
+ version: 2.5.4
335
363
  - !ruby/object:Gem::Dependency
336
364
  name: awrence
337
365
  requirement: !ruby/object:Gem::Requirement
@@ -570,20 +598,6 @@ dependencies:
570
598
  - - '='
571
599
  - !ruby/object:Gem::Version
572
600
  version: 2.0.2
573
- - !ruby/object:Gem::Dependency
574
- name: net-ping
575
- requirement: !ruby/object:Gem::Requirement
576
- requirements:
577
- - - '='
578
- - !ruby/object:Gem::Version
579
- version: 2.0.8
580
- type: :runtime
581
- prerelease: false
582
- version_requirements: !ruby/object:Gem::Requirement
583
- requirements:
584
- - - '='
585
- - !ruby/object:Gem::Version
586
- version: 2.0.8
587
601
  - !ruby/object:Gem::Dependency
588
602
  name: normalize_country
589
603
  requirement: !ruby/object:Gem::Requirement
@@ -744,14 +758,14 @@ dependencies:
744
758
  requirements:
745
759
  - - '='
746
760
  - !ruby/object:Gem::Version
747
- version: 1.6.7
761
+ version: 1.6.8
748
762
  type: :runtime
749
763
  prerelease: false
750
764
  version_requirements: !ruby/object:Gem::Requirement
751
765
  requirements:
752
766
  - - '='
753
767
  - !ruby/object:Gem::Version
754
- version: 1.6.7
768
+ version: 1.6.8
755
769
  - !ruby/object:Gem::Dependency
756
770
  name: thor
757
771
  requirement: !ruby/object:Gem::Requirement
@@ -849,6 +863,7 @@ files:
849
863
  - docs/analyzers/crtsh.md
850
864
  - docs/analyzers/dnstwister.md
851
865
  - docs/analyzers/feed.md
866
+ - docs/analyzers/fofa.md
852
867
  - docs/analyzers/greynoise.md
853
868
  - docs/analyzers/hunterhow.md
854
869
  - docs/analyzers/index.md
@@ -970,6 +985,7 @@ files:
970
985
  - frontend/vitest.config.ts
971
986
  - lefthook.yml
972
987
  - lib/mihari.rb
988
+ - lib/mihari/actor.rb
973
989
  - lib/mihari/analyzers/base.rb
974
990
  - lib/mihari/analyzers/binaryedge.rb
975
991
  - lib/mihari/analyzers/censys.rb
@@ -977,20 +993,19 @@ files:
977
993
  - lib/mihari/analyzers/crtsh.rb
978
994
  - lib/mihari/analyzers/dnstwister.rb
979
995
  - lib/mihari/analyzers/feed.rb
996
+ - lib/mihari/analyzers/fofa.rb
980
997
  - lib/mihari/analyzers/greynoise.rb
981
998
  - lib/mihari/analyzers/hunterhow.rb
982
999
  - lib/mihari/analyzers/onyphe.rb
983
1000
  - lib/mihari/analyzers/otx.rb
984
1001
  - lib/mihari/analyzers/passivetotal.rb
985
1002
  - lib/mihari/analyzers/pulsedive.rb
986
- - lib/mihari/analyzers/rule.rb
987
1003
  - lib/mihari/analyzers/securitytrails.rb
988
1004
  - lib/mihari/analyzers/shodan.rb
989
1005
  - lib/mihari/analyzers/urlscan.rb
990
1006
  - lib/mihari/analyzers/virustotal.rb
991
1007
  - lib/mihari/analyzers/virustotal_intelligence.rb
992
1008
  - lib/mihari/analyzers/zoomeye.rb
993
- - lib/mihari/base.rb
994
1009
  - lib/mihari/cli/alert.rb
995
1010
  - lib/mihari/cli/base.rb
996
1011
  - lib/mihari/cli/database.rb
@@ -1002,6 +1017,7 @@ files:
1002
1017
  - lib/mihari/clients/circl.rb
1003
1018
  - lib/mihari/clients/crtsh.rb
1004
1019
  - lib/mihari/clients/dnstwister.rb
1020
+ - lib/mihari/clients/fofa.rb
1005
1021
  - lib/mihari/clients/greynoise.rb
1006
1022
  - lib/mihari/clients/hunterhow.rb
1007
1023
  - lib/mihari/clients/misp.rb
@@ -1071,23 +1087,26 @@ files:
1071
1087
  - lib/mihari/models/tag.rb
1072
1088
  - lib/mihari/models/tagging.rb
1073
1089
  - lib/mihari/models/whois.rb
1090
+ - lib/mihari/rule.rb
1074
1091
  - lib/mihari/schemas/alert.rb
1075
1092
  - lib/mihari/schemas/analyzer.rb
1076
1093
  - lib/mihari/schemas/emitter.rb
1077
1094
  - lib/mihari/schemas/enricher.rb
1078
1095
  - lib/mihari/schemas/macros.rb
1096
+ - lib/mihari/schemas/mixins.rb
1079
1097
  - lib/mihari/schemas/options.rb
1080
1098
  - lib/mihari/schemas/rule.rb
1099
+ - lib/mihari/service.rb
1081
1100
  - lib/mihari/services/alert_builder.rb
1082
1101
  - lib/mihari/services/alert_proxy.rb
1083
1102
  - lib/mihari/services/alert_runner.rb
1084
1103
  - lib/mihari/services/rule_builder.rb
1085
- - lib/mihari/services/rule_proxy.rb
1086
1104
  - lib/mihari/services/rule_runner.rb
1087
1105
  - lib/mihari/structs/binaryedge.rb
1088
1106
  - lib/mihari/structs/censys.rb
1089
1107
  - lib/mihari/structs/config.rb
1090
1108
  - lib/mihari/structs/filters.rb
1109
+ - lib/mihari/structs/fofa.rb
1091
1110
  - lib/mihari/structs/google_public_dns.rb
1092
1111
  - lib/mihari/structs/greynoise.rb
1093
1112
  - lib/mihari/structs/hunterhow.rb
@@ -1096,7 +1115,6 @@ files:
1096
1115
  - lib/mihari/structs/shodan.rb
1097
1116
  - lib/mihari/structs/urlscan.rb
1098
1117
  - lib/mihari/structs/virustotal_intelligence.rb
1099
- - lib/mihari/templates/rule.yml.erb
1100
1118
  - lib/mihari/type_checker.rb
1101
1119
  - lib/mihari/types.rb
1102
1120
  - lib/mihari/version.rb
@@ -1111,8 +1129,8 @@ files:
1111
1129
  - lib/mihari/web/middleware/connection_adapter.rb
1112
1130
  - lib/mihari/web/middleware/error_notification_adapter.rb
1113
1131
  - lib/mihari/web/public/assets/index-56fc2187.css
1114
- - lib/mihari/web/public/assets/index-9cc489e6.js
1115
- - lib/mihari/web/public/assets/mode-yaml-a21faa53.js
1132
+ - lib/mihari/web/public/assets/index-821134e2.js
1133
+ - lib/mihari/web/public/assets/mode-yaml-24faa242.js
1116
1134
  - lib/mihari/web/public/favicon.ico
1117
1135
  - lib/mihari/web/public/index.html
1118
1136
  - lib/mihari/web/public/redoc-static.html
@@ -1,232 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Mihari
4
- module Analyzers
5
- class Rule
6
- include Mixins::FalsePositive
7
-
8
- # @return [Mihari::Services::RuleProxy]
9
- attr_reader :rule
10
-
11
- # @return [Time]
12
- attr_reader :base_time
13
-
14
- #
15
- # @param [Mihari::Services::RuleProxy] rule
16
- #
17
- def initialize(rule)
18
- @rule = rule
19
- @base_time = Time.now.utc
20
-
21
- validate_analyzer_configurations
22
- end
23
-
24
- #
25
- # Returns a list of artifacts matched with queries/analyzers (with the rule ID)
26
- #
27
- # @return [Array<Mihari::Artifact>]
28
- #
29
- def artifacts
30
- analyzers.flat_map do |analyzer|
31
- result = analyzer.result
32
-
33
- raise result.failure if result.failure? && !analyzer.ignore_error?
34
-
35
- artifacts = result.value!
36
- artifacts.map do |artifact|
37
- artifact.rule_id = rule.id
38
- artifact
39
- end
40
- end
41
- end
42
-
43
- #
44
- # Normalize artifacts
45
- # - Reject invalid artifacts (for just in case)
46
- # - Select artifacts with allowed data types
47
- # - Reject artifacts with false positive values
48
- # - Set rule ID
49
- #
50
- # @return [Array<Mihari::Artifact>]
51
- #
52
- def normalized_artifacts
53
- valid_artifacts = artifacts.uniq(&:data).select(&:valid?)
54
- date_type_allowed_artifacts = valid_artifacts.select { |artifact| rule.data_types.include? artifact.data_type }
55
- date_type_allowed_artifacts.reject { |artifact| falsepositive? artifact.data }
56
- end
57
-
58
- #
59
- # Uniquify artifacts (assure rule level uniqueness)
60
- #
61
- # @return [Array<Mihari::Artifact>]
62
- #
63
- def unique_artifacts
64
- normalized_artifacts.select do |artifact|
65
- artifact.unique?(base_time: base_time, artifact_lifetime: rule.artifact_lifetime)
66
- end
67
- end
68
-
69
- #
70
- # Enriched artifacts
71
- #
72
- # @return [Array<Mihari::Artifact>]
73
- #
74
- def enriched_artifacts
75
- @enriched_artifacts ||= Parallel.map(unique_artifacts) do |artifact|
76
- enrichers.each { |enricher| artifact.enrich_by_enricher enricher }
77
- artifact
78
- end
79
- end
80
-
81
- #
82
- # Bulk emit
83
- #
84
- # @return [Array<Mihari::Alert>]
85
- #
86
- def bulk_emit
87
- return [] if enriched_artifacts.empty?
88
-
89
- # NOTE: separate parallel execution and logging
90
- # because the logger does not work along with Parallel
91
- results = Parallel.map(valid_emitters) do |emitter|
92
- emitter.result
93
- end
94
-
95
- results.zip(valid_emitters).map do |result_and_emitter|
96
- result, emitter = result_and_emitter
97
-
98
- Mihari.logger.info "Emission by #{emitter.class} is failed: #{result.failure}" if result.failure?
99
- Mihari.logger.info "Emission by #{emitter.class} is succeeded" if result.success?
100
-
101
- result.value_or nil
102
- end.compact
103
- end
104
-
105
- #
106
- # Set artifacts & run emitters in parallel
107
- #
108
- # @return [Mihari::Alert, nil]
109
- #
110
- def run
111
- alert_or_something = bulk_emit
112
- # returns Mihari::Alert created by the database emitter
113
- alert_or_something.find { |res| res.is_a?(Mihari::Alert) }
114
- end
115
-
116
- private
117
-
118
- #
119
- # Check whether a value is a falsepositive value or not
120
- #
121
- # @return [Boolean]
122
- #
123
- def falsepositive?(value)
124
- return true if rule.falsepositives.include?(value)
125
-
126
- regexps = rule.falsepositives.select { |fp| fp.is_a?(Regexp) }
127
- regexps.any? { |fp| fp.match?(value) }
128
- end
129
-
130
- #
131
- # Get analyzer class
132
- #
133
- # @param [String] key
134
- #
135
- # @return [Class<Mihari::Analyzers::Base>] analyzer class
136
- #
137
- def get_analyzer_class(key)
138
- raise ArgumentError, "#{key} is not supported" unless Mihari.analyzer_to_class.key?(key)
139
-
140
- Mihari.analyzer_to_class[key]
141
- end
142
-
143
- #
144
- # @return [Array<Mihari::Analyzers::Base>]
145
- #
146
- def analyzers
147
- rule.queries.map do |query_params|
148
- analyzer_name = query_params[:analyzer]
149
- klass = get_analyzer_class(analyzer_name)
150
- klass.from_query(query_params)
151
- end
152
- end
153
-
154
- #
155
- # Get emitter class
156
- #
157
- # @param [String] key
158
- #
159
- # @return [Class<Mihari::Emitters::Base>] emitter class
160
- #
161
- def get_emitter_class(key)
162
- raise ArgumentError, "#{key} is not supported" unless Mihari.emitter_to_class.key?(key)
163
-
164
- Mihari.emitter_to_class[key]
165
- end
166
-
167
- #
168
- # @return [Array<Mihari::Emitters::Base>]
169
- #
170
- def emitters
171
- rule.emitters.map(&:deep_dup).map do |params|
172
- name = params[:emitter]
173
- options = params[:options]
174
-
175
- %i[emitter options].each { |key| params.delete key }
176
-
177
- klass = get_emitter_class(name)
178
- klass.new(artifacts: enriched_artifacts, rule: rule, options: options, **params)
179
- end
180
- end
181
-
182
- #
183
- # @return [Array<Mihari::Emitters::Base>]
184
- #
185
- def valid_emitters
186
- @valid_emitters ||= emitters.select(&:valid?)
187
- end
188
-
189
- #
190
- # Get enricher class
191
- #
192
- # @param [String] key
193
- #
194
- # @return [Class<Mihari::Enrichers::Base>] enricher class
195
- #
196
- def get_enricher_class(key)
197
- raise ArgumentError, "#{key} is not supported" unless Mihari.enricher_to_class.key?(key)
198
-
199
- Mihari.enricher_to_class[key]
200
- end
201
-
202
- #
203
- # @return [Array<Mihari::Enrichers::Base>] enrichers
204
- #
205
- def enrichers
206
- @enrichers ||= rule.enrichers.map(&:deep_dup).map do |params|
207
- name = params[:enricher]
208
- options = params[:options]
209
-
210
- %i[enricher options].each { |key| params.delete key }
211
-
212
- klass = get_enricher_class(name)
213
- klass.new(options: options, **params)
214
- end
215
- end
216
-
217
- #
218
- # Validate configuration of analyzers
219
- #
220
- def validate_analyzer_configurations
221
- analyzers.map do |analyzer|
222
- next if analyzer.configured?
223
-
224
- joined = analyzer.configuration_keys.join(", ")
225
- be = (analyzer.configuration_keys.length > 1) ? "are" : "is"
226
- message = "#{analyzer.class.class_key} is not configured correctly. #{joined} #{be} missing."
227
- raise ConfigurationError, message
228
- end
229
- end
230
- end
231
- end
232
- end