mihari 2.4.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +7 -0
  3. data/.overcommit.yml +12 -0
  4. data/README.md +1 -9
  5. data/build_frontend.sh +5 -0
  6. data/docker/Dockerfile +1 -1
  7. data/exe/mihari +1 -1
  8. data/lib/mihari.rb +89 -15
  9. data/lib/mihari/analyzers/base.rb +49 -8
  10. data/lib/mihari/analyzers/basic.rb +1 -2
  11. data/lib/mihari/analyzers/binaryedge.rb +7 -13
  12. data/lib/mihari/analyzers/censys.rb +26 -63
  13. data/lib/mihari/analyzers/circl.rb +20 -17
  14. data/lib/mihari/analyzers/crtsh.rb +6 -13
  15. data/lib/mihari/analyzers/dnpedia.rb +6 -12
  16. data/lib/mihari/analyzers/dnstwister.rb +13 -10
  17. data/lib/mihari/analyzers/onyphe.rb +6 -12
  18. data/lib/mihari/analyzers/otx.rb +22 -19
  19. data/lib/mihari/analyzers/passivetotal.rb +22 -21
  20. data/lib/mihari/analyzers/pulsedive.rb +16 -13
  21. data/lib/mihari/analyzers/rule.rb +97 -0
  22. data/lib/mihari/analyzers/securitytrails.rb +22 -19
  23. data/lib/mihari/analyzers/shodan.rb +7 -13
  24. data/lib/mihari/analyzers/spyse.rb +12 -19
  25. data/lib/mihari/analyzers/urlscan.rb +22 -27
  26. data/lib/mihari/analyzers/virustotal.rb +25 -22
  27. data/lib/mihari/analyzers/zoomeye.rb +14 -20
  28. data/lib/mihari/cli/analyzer.rb +44 -0
  29. data/lib/mihari/cli/base.rb +27 -0
  30. data/lib/mihari/cli/init.rb +13 -0
  31. data/lib/mihari/cli/main.rb +30 -0
  32. data/lib/mihari/cli/mixins/utils.rb +88 -0
  33. data/lib/mihari/cli/validator.rb +11 -0
  34. data/lib/mihari/commands/binaryedge.rb +1 -1
  35. data/lib/mihari/commands/censys.rb +1 -1
  36. data/lib/mihari/commands/circl.rb +2 -2
  37. data/lib/mihari/commands/crtsh.rb +1 -1
  38. data/lib/mihari/commands/dnpedia.rb +1 -1
  39. data/lib/mihari/commands/dnstwister.rb +2 -2
  40. data/lib/mihari/commands/init.rb +46 -0
  41. data/lib/mihari/commands/json.rb +1 -1
  42. data/lib/mihari/commands/onyphe.rb +1 -1
  43. data/lib/mihari/commands/otx.rb +2 -2
  44. data/lib/mihari/commands/passivetotal.rb +2 -2
  45. data/lib/mihari/commands/pulsedive.rb +2 -2
  46. data/lib/mihari/commands/search.rb +77 -0
  47. data/lib/mihari/commands/securitytrails.rb +2 -2
  48. data/lib/mihari/commands/shodan.rb +1 -1
  49. data/lib/mihari/commands/spyse.rb +1 -1
  50. data/lib/mihari/commands/urlscan.rb +2 -2
  51. data/lib/mihari/commands/validator.rb +38 -0
  52. data/lib/mihari/commands/virustotal.rb +2 -2
  53. data/lib/mihari/commands/zoomeye.rb +1 -1
  54. data/lib/mihari/constraints.rb +5 -0
  55. data/lib/mihari/database.rb +13 -2
  56. data/lib/mihari/emitters/base.rb +2 -2
  57. data/lib/mihari/emitters/database.rb +1 -1
  58. data/lib/mihari/emitters/misp.rb +3 -1
  59. data/lib/mihari/emitters/slack.rb +6 -7
  60. data/lib/mihari/emitters/the_hive.rb +1 -1
  61. data/lib/mihari/emitters/webhook.rb +2 -9
  62. data/lib/mihari/mixins/configurable.rb +38 -0
  63. data/lib/mihari/mixins/configuration.rb +90 -0
  64. data/lib/mihari/mixins/hash.rb +20 -0
  65. data/lib/mihari/mixins/refang.rb +21 -0
  66. data/lib/mihari/mixins/retriable.rb +27 -0
  67. data/lib/mihari/mixins/rule.rb +79 -0
  68. data/lib/mihari/models/alert.rb +28 -1
  69. data/lib/mihari/models/artifact.rb +11 -1
  70. data/lib/mihari/notifiers/base.rb +9 -1
  71. data/lib/mihari/notifiers/exception_notifier.rb +50 -0
  72. data/lib/mihari/notifiers/slack.rb +29 -0
  73. data/lib/mihari/schemas/analyzer.rb +25 -0
  74. data/lib/mihari/schemas/configuration.rb +42 -0
  75. data/lib/mihari/schemas/macros.rb +17 -0
  76. data/lib/mihari/schemas/rule.rb +72 -0
  77. data/lib/mihari/serializers/artifact.rb +1 -1
  78. data/lib/mihari/status.rb +14 -0
  79. data/lib/mihari/templates/rule.yml.erb +19 -0
  80. data/lib/mihari/type_checker.rb +8 -3
  81. data/lib/mihari/version.rb +1 -1
  82. data/lib/mihari/web/app.rb +2 -1
  83. data/lib/mihari/web/controllers/analyzers_controller.rb +38 -0
  84. data/lib/mihari/web/controllers/base_controller.rb +1 -1
  85. data/lib/mihari/web/public/index.html +1 -21
  86. data/lib/mihari/web/public/redoc-static.html +338 -461
  87. data/lib/mihari/web/public/static/js/app.365f1907.js +13 -0
  88. data/lib/mihari/web/public/static/js/app.365f1907.js.map +1 -0
  89. data/lib/mihari/web/public/static/js/app.ab213f7c.js +12 -0
  90. data/lib/mihari/web/public/static/js/app.ab213f7c.js.map +1 -0
  91. data/mihari.gemspec +16 -9
  92. metadata +135 -58
  93. data/.rubocop.yml +0 -161
  94. data/lib/mihari/analyzers/free_text.rb +0 -48
  95. data/lib/mihari/analyzers/http_hash.rb +0 -100
  96. data/lib/mihari/analyzers/passive_dns.rb +0 -59
  97. data/lib/mihari/analyzers/passive_ssl.rb +0 -55
  98. data/lib/mihari/analyzers/reverse_whois.rb +0 -55
  99. data/lib/mihari/analyzers/securitytrails_domain_feed.rb +0 -59
  100. data/lib/mihari/analyzers/ssh_fingerprint.rb +0 -58
  101. data/lib/mihari/cli.rb +0 -126
  102. data/lib/mihari/commands/config.rb +0 -27
  103. data/lib/mihari/commands/free_text.rb +0 -21
  104. data/lib/mihari/commands/http_hash.rb +0 -25
  105. data/lib/mihari/commands/passive_dns.rb +0 -21
  106. data/lib/mihari/commands/passive_ssl.rb +0 -21
  107. data/lib/mihari/commands/reverse_whois.rb +0 -21
  108. data/lib/mihari/commands/securitytrails_domain_feed.rb +0 -23
  109. data/lib/mihari/commands/ssh_fingerprint.rb +0 -21
  110. data/lib/mihari/config.rb +0 -85
  111. data/lib/mihari/configurable.rb +0 -21
  112. data/lib/mihari/html.rb +0 -43
  113. data/lib/mihari/retriable.rb +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 388f48a9001d38fd83f4a6d527d7c5826a490be1d339596d174a9f619a78cb0c
4
- data.tar.gz: 7e0a6e2fcbe9ad1792c21472b8be7706a86b09fbbce951edb1829a76493aaedc
3
+ metadata.gz: eeee7ec511b59cc1e6d07f47df8b2edc29860dbeeeafebc79ba876efe17f2954
4
+ data.tar.gz: e3c4fe0b49b20efa5ebdafaecdadbfb35135cbbdf3ff41dd36c68d2eaae644f8
5
5
  SHA512:
6
- metadata.gz: dfcde6c4fa80ae12c56606157c6800c7e321cef71ed3e4aa9250805ea51126c74a19b3f73040630d169966fb17d834d8c45b37cc6f7baa808d7eea3e7c585fb9
7
- data.tar.gz: d477cdcc4b4075e7671263f32ed5e81daad42e499eded5dffcecfba2d7568b779e99010a64a271a652db3f928800506cb6a4c7cf4060816742d4bb8d5bbec86a
6
+ metadata.gz: eff2de53ad20576849a81a5ebe96155c6b8747ab4b1775b26816ae6eff7eeb8324f2ad360755e867741a1b6cc778f1dec63d80bbb5439b9e8894536988971183
7
+ data.tar.gz: 2b1effec63ad4119f7cb521db866e0e9deb2dca7971036530d4cb16ea4a8669d591981ad5517784da13b98b7a3d3aa732538d040b53590b371d8f51acde849e0
data/.gitignore CHANGED
@@ -57,3 +57,10 @@ Gemfile.lock
57
57
 
58
58
  # SQLite
59
59
  *.db
60
+
61
+ # Config
62
+ mihari.yml
63
+
64
+ # Rule
65
+ rule.yml
66
+ !lib/mihari/templates/rule.yml
data/.overcommit.yml ADDED
@@ -0,0 +1,12 @@
1
+ PreCommit:
2
+ BundleCheck:
3
+ enabled: true
4
+
5
+ RuboCop:
6
+ enabled: true
7
+ required_executable: bundle
8
+ command: ["bundle", "exec", "standardrb"]
9
+ on_warn: fail
10
+
11
+ YamlSyntax:
12
+ enabled: true
data/README.md CHANGED
@@ -48,17 +48,9 @@ Mihari supports the following services by default.
48
48
  - [VirusTotal](http://virustotal.com)
49
49
  - [ZoomEye](https://zoomeye.org)
50
50
 
51
- See [Usage](https://github.com/ninoseki/mihari/wiki/Usage) for more information.
52
-
53
51
  ## Docs
54
52
 
55
- - [Requirements & Installation](https://github.com/ninoseki/mihari/wiki/Requirements-&-Installation)
56
- - [Usage](https://github.com/ninoseki/mihari/wiki/Usage)
57
- - [Built-in Web App](https://github.com/ninoseki/mihari/wiki/Built-in-Web-App)
58
- - [Configuration](https://github.com/ninoseki/mihari/wiki/Configuration)
59
- - [Custom Script](https://github.com/ninoseki/mihari/wiki/Custom-Script)
60
- - [Docker](https://github.com/ninoseki/mihari/wiki/Docker)
61
- - [GitHub Actions](https://github.com/ninoseki/mihari/wiki/GitHub-Actions)
53
+ - [Mihari Knowledge Base](https://www.notion.so/Mihari-Knowledge-Base-266994ff61204428ba6cfcebe40b0bd1)
62
54
 
63
55
  ## License
64
56
 
data/build_frontend.sh CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  CURRENT_DIR=${PWD}
4
4
 
5
+ # build the frontend app
5
6
  mkdir -p tmp
6
7
  cd tmp
7
8
  git clone https://github.com/ninoseki/mihari-frontend.git
@@ -11,4 +12,8 @@ npm run build
11
12
 
12
13
  cp -r dist/* ${CURRENT_DIR}/lib/mihari/web/public
13
14
 
15
+ # replace favicon path
16
+ sed -i "" 's/href="\/favicon.ico"/href="\/static\/favicon.ico"/' ${CURRENT_DIR}/lib/mihari/web/public/index.html
17
+
18
+ # remove tmp dir
14
19
  rm -rf ${CURRENT_DIR}/tmp/mihari-frontend
data/docker/Dockerfile CHANGED
@@ -1,4 +1,4 @@
1
- FROM ruby:3.0.1-alpine3.13
1
+ FROM ruby:3.0.2-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 \
data/exe/mihari CHANGED
@@ -5,4 +5,4 @@ $LOAD_PATH.unshift("#{__dir__}/../lib")
5
5
 
6
6
  require "mihari"
7
7
 
8
- Mihari::CLI.start
8
+ Mihari::CLI::Main.start
data/lib/mihari.rb CHANGED
@@ -1,8 +1,56 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "colorize"
4
+ require "dry/configurable"
5
+ require "dry/files"
3
6
  require "mem"
7
+ require "yaml"
8
+
9
+ # Mixins
10
+ require "mihari/mixins/configurable"
11
+ require "mihari/mixins/configuration"
12
+ require "mihari/mixins/hash"
13
+ require "mihari/mixins/refang"
14
+ require "mihari/mixins/retriable"
15
+ require "mihari/mixins/rule"
16
+
17
+ def truthy?(value)
18
+ return true if value == "true"
19
+ return true if value == true
20
+
21
+ false
22
+ end
4
23
 
5
24
  module Mihari
25
+ extend Dry::Configurable
26
+ extend Mixins::Configuration
27
+
28
+ setting :binaryedge_api_key, ENV["BINARYEDGE_API_KEY"]
29
+ setting :censys_id, ENV["CENSYS_ID"]
30
+ setting :censys_secret, ENV["CENSYS_SECRET"]
31
+ setting :circl_passive_password, ENV["CIRCL_PASSIVE_PASSWORD"]
32
+ setting :circl_passive_username, ENV["CIRCL_PASSIVE_USERNAME"]
33
+ setting :misp_api_endpoint, ENV["MISP_API_ENDPOINT"]
34
+ setting :misp_api_key, ENV["MISP_API_KEY"]
35
+ setting :onyphe_api_key, ENV["ONYPHE_API_KEY"]
36
+ setting :otx_api_key, ENV["OTX_API_KEY"]
37
+ setting :passivetotal_api_key, ENV["PASSIVETOTAL_API_KEY"]
38
+ setting :passivetotal_username, ENV["PASSIVETOTAL_USERNAME"]
39
+ setting :pulsedive_api_key, ENV["PULSEDIVE_API_KEY"]
40
+ setting :securitytrails_api_key, ENV["SECURITYTRAILS_API_KEY"]
41
+ setting :shodan_api_key, ENV["SHODAN_API_KEY"]
42
+ setting :slack_channel, ENV["SLACK_CHANNEL"]
43
+ setting :slack_webhook_url, ENV["SLACK_WEBHOOK_URL"]
44
+ setting :spyse_api_key, ENV["SPYSE_API_KEY"]
45
+ setting :thehive_api_endpoint, ENV["THEHIVE_API_ENDPOINT"]
46
+ setting :thehive_api_key, ENV["THEHIVE_API_KEY"]
47
+ setting :urlscan_api_key, ENV["URLSCAN_API_KEY"]
48
+ setting :virustotal_api_key, ENV["VIRUSTOTAL_API_KEY"]
49
+ setting :zoomeye_api_key, ENV["ZOOMEYE_API_KEY"]
50
+ setting :webhook_url, ENV["WEBHOOK_URL"]
51
+ setting(:webhook_use_json_body, ENV["WEBHOOK_USE_JSON_BODY"]) { |value| truthy?(value) }
52
+ setting :database, ENV["DATABASE"] || "mihari.db"
53
+
6
54
  class << self
7
55
  include Mem
8
56
 
@@ -15,31 +63,53 @@ module Mihari
15
63
  []
16
64
  end
17
65
  memoize :analyzers
66
+
67
+ #
68
+ # Load configuration from YAML file
69
+ #
70
+ # @param [String] path Path to YAML file
71
+ #
72
+ # @return [nil]
73
+ #
74
+ def load_config_from_yaml(path)
75
+ config = load_config(path)
76
+
77
+ # validate loaded yaml data
78
+ validate_config config
79
+
80
+ config.each do |key, value|
81
+ Mihari.config.send("#{key.downcase}=".to_sym, value)
82
+ end
83
+ end
18
84
  end
19
85
  end
20
86
 
21
87
  require "mihari/version"
22
88
  require "mihari/errors"
23
89
 
24
- require "mihari/config"
25
-
26
90
  require "mihari/database"
27
91
  require "mihari/type_checker"
28
92
 
93
+ # Constraints
94
+ require "mihari/constraints"
95
+
96
+ # Schemas
97
+ require "mihari/schemas/analyzer"
98
+ require "mihari/schemas/configuration"
99
+ require "mihari/schemas/rule"
100
+
101
+ # Models
29
102
  require "mihari/models/alert"
30
103
  require "mihari/models/artifact"
31
104
  require "mihari/models/tag"
32
105
  require "mihari/models/tagging"
33
106
 
107
+ # Serializers
34
108
  require "mihari/serializers/alert"
35
109
  require "mihari/serializers/artifact"
36
110
  require "mihari/serializers/tag"
37
111
 
38
- require "mihari/html"
39
-
40
- require "mihari/configurable"
41
- require "mihari/retriable"
42
-
112
+ # Analyzers
43
113
  require "mihari/analyzers/base"
44
114
  require "mihari/analyzers/basic"
45
115
 
@@ -53,7 +123,6 @@ require "mihari/analyzers/onyphe"
53
123
  require "mihari/analyzers/otx"
54
124
  require "mihari/analyzers/passivetotal"
55
125
  require "mihari/analyzers/pulsedive"
56
- require "mihari/analyzers/securitytrails_domain_feed"
57
126
  require "mihari/analyzers/securitytrails"
58
127
  require "mihari/analyzers/shodan"
59
128
  require "mihari/analyzers/spyse"
@@ -61,17 +130,14 @@ require "mihari/analyzers/urlscan"
61
130
  require "mihari/analyzers/virustotal"
62
131
  require "mihari/analyzers/zoomeye"
63
132
 
64
- require "mihari/analyzers/free_text"
65
- require "mihari/analyzers/http_hash"
66
- require "mihari/analyzers/passive_dns"
67
- require "mihari/analyzers/passive_ssl"
68
- require "mihari/analyzers/reverse_whois"
69
- require "mihari/analyzers/ssh_fingerprint"
133
+ require "mihari/analyzers/rule"
70
134
 
135
+ # Notifiers
71
136
  require "mihari/notifiers/base"
72
137
  require "mihari/notifiers/slack"
73
138
  require "mihari/notifiers/exception_notifier"
74
139
 
140
+ # Emitters
75
141
  require "mihari/emitters/base"
76
142
  require "mihari/emitters/database"
77
143
  require "mihari/emitters/misp"
@@ -80,8 +146,16 @@ require "mihari/emitters/stdout"
80
146
  require "mihari/emitters/the_hive"
81
147
  require "mihari/emitters/webhook"
82
148
 
149
+ # Status checker
83
150
  require "mihari/status"
84
151
 
152
+ # Web app
85
153
  require "mihari/web/app"
86
154
 
87
- require "mihari/cli"
155
+ # CLIs
156
+ require "mihari/cli/base"
157
+
158
+ require "mihari/cli/analyzer"
159
+ require "mihari/cli/init"
160
+
161
+ require "mihari/cli/main"
@@ -1,16 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "dry-initializer"
3
4
  require "parallel"
4
5
 
5
6
  module Mihari
6
7
  module Analyzers
7
8
  class Base
8
- include Configurable
9
- include Retriable
9
+ extend Dry::Initializer
10
+
11
+ include Mixins::Configurable
12
+ include Mixins::Retriable
10
13
 
11
14
  attr_accessor :ignore_old_artifacts, :ignore_threshold
12
15
 
13
- def initialize
16
+ def initialize(*args, **kwargs)
17
+ super
18
+
14
19
  @ignore_old_artifacts = false
15
20
  @ignore_threshold = 0
16
21
  end
@@ -30,6 +35,7 @@ module Mihari
30
35
  raise NotImplementedError, "You must implement #{self.class}##{__method__}"
31
36
  end
32
37
 
38
+ # @return [String]
33
39
  def source
34
40
  self.class.to_s.split("::").last
35
41
  end
@@ -39,6 +45,11 @@ module Mihari
39
45
  []
40
46
  end
41
47
 
48
+ #
49
+ # Set artifacts & run emitters in parallel
50
+ #
51
+ # @return [nil]
52
+ #
42
53
  def run
43
54
  set_unique_artifacts
44
55
 
@@ -47,6 +58,13 @@ module Mihari
47
58
  end
48
59
  end
49
60
 
61
+ #
62
+ # Run emitter
63
+ #
64
+ # @param [Mihari::Emitters::Base] emitter
65
+ #
66
+ # @return [nil]
67
+ #
50
68
  def run_emitter(emitter)
51
69
  emitter.run(title: title, description: description, artifacts: unique_artifacts, source: source, tags: tags)
52
70
  rescue StandardError => e
@@ -57,22 +75,40 @@ module Mihari
57
75
  Mihari.analyzers << child
58
76
  end
59
77
 
60
- private
61
-
78
+ #
79
+ # Normalize artifacts
80
+ # - Uniquefy artifacts by native #uniq
81
+ # - Convert data (string) into an artifact
82
+ # - Reject an invalid artifact
83
+ #
62
84
  # @return [Array<Mihari::Artifact>]
85
+ #
63
86
  def normalized_artifacts
64
87
  @normalized_artifacts ||= artifacts.compact.uniq.sort.map do |artifact|
65
- artifact.is_a?(Artifact) ? artifact : Artifact.new(data: artifact)
88
+ # No need to set data_type manually
89
+ # It is set automatically in #initialize
90
+ artifact.is_a?(Artifact) ? artifact : Artifact.new(data: artifact, source: source)
66
91
  end.select(&:valid?)
67
92
  end
68
93
 
94
+ private
95
+
96
+ #
97
+ # Uniquefy artifacts
98
+ #
69
99
  # @return [Array<Mihari::Artifact>]
100
+ #
70
101
  def unique_artifacts
71
102
  @unique_artifacts ||= normalized_artifacts.select do |artifact|
72
103
  artifact.unique?(ignore_old_artifacts: ignore_old_artifacts, ignore_threshold: ignore_threshold)
73
104
  end
74
105
  end
75
106
 
107
+ #
108
+ # Set unique artifacts
109
+ #
110
+ # @return [nil]
111
+ #
76
112
  def set_unique_artifacts
77
113
  retry_on_error { unique_artifacts }
78
114
  rescue ArgumentError => _e
@@ -80,11 +116,16 @@ module Mihari
80
116
  raise Error, "Please configure #{klass} API settings properly"
81
117
  end
82
118
 
119
+ #
120
+ # Select valid emitters
121
+ #
122
+ # @return [Array<Mihari::Emitters::Base>]
123
+ #
83
124
  def valid_emitters
84
- @valid_emitters ||= Mihari.emitters.map do |klass|
125
+ @valid_emitters ||= Mihari.emitters.filter_map do |klass|
85
126
  emitter = klass.new
86
127
  emitter.valid? ? emitter : nil
87
- end.compact
128
+ end
88
129
  end
89
130
  end
90
131
  end
@@ -3,8 +3,7 @@
3
3
  module Mihari
4
4
  module Analyzers
5
5
  class Basic < Base
6
- attr_accessor :title
7
- attr_reader :description, :artifacts, :source, :tags
6
+ attr_reader :title, :description, :artifacts, :source, :tags
8
7
 
9
8
  def initialize(title:, description:, artifacts:, source:, tags: [])
10
9
  super()
@@ -5,16 +5,10 @@ require "binaryedge"
5
5
  module Mihari
6
6
  module Analyzers
7
7
  class BinaryEdge < Base
8
- attr_reader :title, :description, :query, :tags
9
-
10
- def initialize(query, title: nil, description: nil, tags: [])
11
- super()
12
-
13
- @query = query
14
- @title = title || "BinaryEdge lookup"
15
- @description = description || "query = #{query}"
16
- @tags = tags
17
- end
8
+ param :query
9
+ option :title, default: proc { "BinaryEdge search" }
10
+ option :description, default: proc { "query = #{query}" }
11
+ option :tags, default: proc { [] }
18
12
 
19
13
  def artifacts
20
14
  results = search
@@ -22,9 +16,9 @@ module Mihari
22
16
 
23
17
  results.map do |result|
24
18
  events = result["events"] || []
25
- events.map do |event|
19
+ events.filter_map do |event|
26
20
  event.dig "target", "ip"
27
- end.compact
21
+ end
28
22
  end.flatten.compact.uniq
29
23
  end
30
24
 
@@ -52,7 +46,7 @@ module Mihari
52
46
  responses
53
47
  end
54
48
 
55
- def config_keys
49
+ def configuration_keys
56
50
  %w[binaryedge_api_key]
57
51
  end
58
52
 
@@ -1,87 +1,50 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "censu"
3
+ require "censysx"
4
4
 
5
5
  module Mihari
6
6
  module Analyzers
7
7
  class Censys < Base
8
- attr_reader :title, :description, :query, :tags, :type
9
-
10
- def initialize(query, title: nil, description: nil, tags: [], type: "ipv4")
11
- super()
12
-
13
- @query = query
14
- @title = title || "Censys lookup"
15
- @description = description || "query = #{query}"
16
- @tags = tags
17
- @type = type
18
- end
8
+ param :query
9
+ option :title, default: proc { "Censys search" }
10
+ option :description, default: proc { "query = #{query}" }
11
+ option :tags, default: proc { [] }
19
12
 
20
13
  def artifacts
21
- case type
22
- when "ipv4"
23
- ipv4_lookup
24
- when "websites"
25
- websites_lookup
26
- when "certificates"
27
- certificates_lookup
28
- else
29
- raise InvalidInputError, "#{type} type is not supported." unless valid_type?
30
- end
14
+ search
31
15
  end
32
16
 
33
17
  private
34
18
 
35
- def valid_type?
36
- %w[ipv4 websites certificates].include? type
37
- end
38
-
39
- def normalize(domain)
40
- return domain unless domain.start_with?("*.")
41
-
42
- domain.sub("*.", "")
43
- end
44
-
45
- def ipv4_lookup
19
+ def search
46
20
  ipv4s = []
47
21
 
48
- res = api.ipv4.search(query: query)
49
- res.each_page do |page|
50
- ipv4s << page.map(&:ip)
51
- end
22
+ cursor = nil
23
+ loop do
24
+ response = api.search(query, cursor: cursor)
25
+ ipv4s << response_to_ipv4s(response)
52
26
 
53
- ipv4s.flatten
54
- end
55
-
56
- def websites_lookup
57
- domains = []
58
-
59
- res = api.websites.search(query: query)
60
- res.each_page do |page|
61
- domains << page.map(&:domain)
27
+ links = response.dig("result", "links")
28
+ cursor = links["next"]
29
+ break if cursor == ""
62
30
  end
63
31
 
64
- domains.flatten
32
+ ipv4s.flatten
65
33
  end
66
34
 
67
- def certificates_lookup
68
- domains = []
69
-
70
- res = api.certificates.search(query: query)
71
- res.each_page do |page|
72
- page.each do |result|
73
- subject_dn = result.subject_dn
74
- names = subject_dn.scan(/CN=(.+)/).flatten.first
75
- next unless names
76
-
77
- domains << names.split(",").map { |domain| normalize(domain) }
78
- end
79
- end
80
-
81
- domains.flatten
35
+ #
36
+ # Extract IPv4s from Censys search API response
37
+ #
38
+ # @param [Hash] response
39
+ #
40
+ # @return [Array<String>]
41
+ #
42
+ def response_to_ipv4s(response)
43
+ hits = response.dig("result", "hits") || []
44
+ hits.map { |hit| hit["ip"] }
82
45
  end
83
46
 
84
- def config_keys
47
+ def configuration_keys
85
48
  %w[censys_id censys_secret]
86
49
  end
87
50