mihari 2.4.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +7 -0
- data/.overcommit.yml +12 -0
- data/README.md +1 -9
- data/exe/mihari +1 -1
- data/lib/mihari.rb +88 -15
- data/lib/mihari/analyzers/base.rb +49 -8
- data/lib/mihari/analyzers/basic.rb +1 -2
- data/lib/mihari/analyzers/binaryedge.rb +7 -13
- data/lib/mihari/analyzers/censys.rb +26 -63
- data/lib/mihari/analyzers/circl.rb +20 -17
- data/lib/mihari/analyzers/crtsh.rb +6 -13
- data/lib/mihari/analyzers/dnpedia.rb +6 -12
- data/lib/mihari/analyzers/dnstwister.rb +13 -10
- data/lib/mihari/analyzers/onyphe.rb +6 -12
- data/lib/mihari/analyzers/otx.rb +22 -19
- data/lib/mihari/analyzers/passivetotal.rb +22 -21
- data/lib/mihari/analyzers/pulsedive.rb +16 -13
- data/lib/mihari/analyzers/rule.rb +99 -0
- data/lib/mihari/analyzers/securitytrails.rb +22 -19
- data/lib/mihari/analyzers/shodan.rb +7 -13
- data/lib/mihari/analyzers/spyse.rb +12 -19
- data/lib/mihari/analyzers/urlscan.rb +22 -27
- data/lib/mihari/analyzers/virustotal.rb +25 -22
- data/lib/mihari/analyzers/zoomeye.rb +14 -20
- data/lib/mihari/cli/analyzer.rb +44 -0
- data/lib/mihari/cli/base.rb +27 -0
- data/lib/mihari/cli/init.rb +13 -0
- data/lib/mihari/cli/main.rb +30 -0
- data/lib/mihari/cli/mixins/utils.rb +88 -0
- data/lib/mihari/cli/validator.rb +11 -0
- data/lib/mihari/commands/binaryedge.rb +1 -1
- data/lib/mihari/commands/censys.rb +1 -1
- data/lib/mihari/commands/circl.rb +2 -2
- data/lib/mihari/commands/crtsh.rb +1 -1
- data/lib/mihari/commands/dnpedia.rb +1 -1
- data/lib/mihari/commands/dnstwister.rb +2 -2
- data/lib/mihari/commands/init.rb +46 -0
- data/lib/mihari/commands/json.rb +1 -1
- data/lib/mihari/commands/onyphe.rb +1 -1
- data/lib/mihari/commands/otx.rb +2 -2
- data/lib/mihari/commands/passivetotal.rb +2 -2
- data/lib/mihari/commands/pulsedive.rb +2 -2
- data/lib/mihari/commands/search.rb +77 -0
- data/lib/mihari/commands/securitytrails.rb +2 -2
- data/lib/mihari/commands/shodan.rb +1 -1
- data/lib/mihari/commands/spyse.rb +1 -1
- data/lib/mihari/commands/urlscan.rb +2 -2
- data/lib/mihari/commands/validator.rb +38 -0
- data/lib/mihari/commands/virustotal.rb +2 -2
- data/lib/mihari/commands/zoomeye.rb +1 -1
- data/lib/mihari/constraints.rb +5 -0
- data/lib/mihari/database.rb +13 -2
- data/lib/mihari/emitters/base.rb +2 -2
- data/lib/mihari/emitters/database.rb +1 -1
- data/lib/mihari/emitters/misp.rb +1 -1
- data/lib/mihari/emitters/slack.rb +5 -6
- data/lib/mihari/emitters/the_hive.rb +1 -1
- data/lib/mihari/emitters/webhook.rb +2 -9
- data/lib/mihari/mixins/configurable.rb +38 -0
- data/lib/mihari/mixins/configuration.rb +85 -0
- data/lib/mihari/mixins/hash.rb +20 -0
- data/lib/mihari/mixins/refang.rb +21 -0
- data/lib/mihari/mixins/retriable.rb +27 -0
- data/lib/mihari/mixins/rule.rb +79 -0
- data/lib/mihari/models/alert.rb +28 -1
- data/lib/mihari/models/artifact.rb +10 -0
- data/lib/mihari/notifiers/base.rb +9 -1
- data/lib/mihari/notifiers/exception_notifier.rb +50 -0
- data/lib/mihari/notifiers/slack.rb +29 -0
- data/lib/mihari/schemas/configuration.rb +42 -0
- data/lib/mihari/schemas/macros.rb +17 -0
- data/lib/mihari/schemas/rule.rb +72 -0
- data/lib/mihari/serializers/artifact.rb +1 -1
- data/lib/mihari/status.rb +14 -0
- data/lib/mihari/templates/rule.yml.erb +19 -0
- data/lib/mihari/type_checker.rb +8 -3
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/controllers/base_controller.rb +1 -1
- data/lib/mihari/web/public/index.html +1 -21
- data/lib/mihari/web/public/redoc-static.html +2 -2
- data/lib/mihari/web/public/static/js/app.ab213f7c.js +12 -0
- data/lib/mihari/web/public/static/js/app.ab213f7c.js.map +1 -0
- data/mihari.gemspec +12 -5
- metadata +123 -50
- data/.rubocop.yml +0 -161
- data/lib/mihari/analyzers/free_text.rb +0 -48
- data/lib/mihari/analyzers/http_hash.rb +0 -100
- data/lib/mihari/analyzers/passive_dns.rb +0 -59
- data/lib/mihari/analyzers/passive_ssl.rb +0 -55
- data/lib/mihari/analyzers/reverse_whois.rb +0 -55
- data/lib/mihari/analyzers/securitytrails_domain_feed.rb +0 -59
- data/lib/mihari/analyzers/ssh_fingerprint.rb +0 -58
- data/lib/mihari/cli.rb +0 -126
- data/lib/mihari/commands/config.rb +0 -27
- data/lib/mihari/commands/free_text.rb +0 -21
- data/lib/mihari/commands/http_hash.rb +0 -25
- data/lib/mihari/commands/passive_dns.rb +0 -21
- data/lib/mihari/commands/passive_ssl.rb +0 -21
- data/lib/mihari/commands/reverse_whois.rb +0 -21
- data/lib/mihari/commands/securitytrails_domain_feed.rb +0 -23
- data/lib/mihari/commands/ssh_fingerprint.rb +0 -21
- data/lib/mihari/config.rb +0 -85
- data/lib/mihari/configurable.rb +0 -21
- data/lib/mihari/html.rb +0 -43
- data/lib/mihari/retriable.rb +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 513324945758cadacf94345cb8a2a410cab99a08abc0ad02148c7737660d7348
|
4
|
+
data.tar.gz: 5c031467f3a6da288c5fceb137c17102654de072b6b34b2374dd15c523d09a03
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf28354ab901254085d6e055e9c7a4989ce3d75ede0a6e978c009e69eb8f8a60a198e3fe505b82b9c5eddfa29b1866cc51da4bb8b41cc1727352d5bacd04e4d1
|
7
|
+
data.tar.gz: fa111a82851f3e695ce1b5c7a582b030459e3dac3598cff1b4d943f03fea8bdd000d21f17434e080f80109d2f90a19d154e4291efced316bf9e3c2d59880f747
|
data/.gitignore
CHANGED
data/.overcommit.yml
ADDED
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
|
-
- [
|
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/exe/mihari
CHANGED
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,52 @@ 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/configuration"
|
98
|
+
require "mihari/schemas/rule"
|
99
|
+
|
100
|
+
# Models
|
29
101
|
require "mihari/models/alert"
|
30
102
|
require "mihari/models/artifact"
|
31
103
|
require "mihari/models/tag"
|
32
104
|
require "mihari/models/tagging"
|
33
105
|
|
106
|
+
# Serializers
|
34
107
|
require "mihari/serializers/alert"
|
35
108
|
require "mihari/serializers/artifact"
|
36
109
|
require "mihari/serializers/tag"
|
37
110
|
|
38
|
-
|
39
|
-
|
40
|
-
require "mihari/configurable"
|
41
|
-
require "mihari/retriable"
|
42
|
-
|
111
|
+
# Analyzers
|
43
112
|
require "mihari/analyzers/base"
|
44
113
|
require "mihari/analyzers/basic"
|
45
114
|
|
@@ -53,7 +122,6 @@ require "mihari/analyzers/onyphe"
|
|
53
122
|
require "mihari/analyzers/otx"
|
54
123
|
require "mihari/analyzers/passivetotal"
|
55
124
|
require "mihari/analyzers/pulsedive"
|
56
|
-
require "mihari/analyzers/securitytrails_domain_feed"
|
57
125
|
require "mihari/analyzers/securitytrails"
|
58
126
|
require "mihari/analyzers/shodan"
|
59
127
|
require "mihari/analyzers/spyse"
|
@@ -61,17 +129,14 @@ require "mihari/analyzers/urlscan"
|
|
61
129
|
require "mihari/analyzers/virustotal"
|
62
130
|
require "mihari/analyzers/zoomeye"
|
63
131
|
|
64
|
-
require "mihari/analyzers/
|
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"
|
132
|
+
require "mihari/analyzers/rule"
|
70
133
|
|
134
|
+
# Notifiers
|
71
135
|
require "mihari/notifiers/base"
|
72
136
|
require "mihari/notifiers/slack"
|
73
137
|
require "mihari/notifiers/exception_notifier"
|
74
138
|
|
139
|
+
# Emitters
|
75
140
|
require "mihari/emitters/base"
|
76
141
|
require "mihari/emitters/database"
|
77
142
|
require "mihari/emitters/misp"
|
@@ -80,8 +145,16 @@ require "mihari/emitters/stdout"
|
|
80
145
|
require "mihari/emitters/the_hive"
|
81
146
|
require "mihari/emitters/webhook"
|
82
147
|
|
148
|
+
# Status checker
|
83
149
|
require "mihari/status"
|
84
150
|
|
151
|
+
# Web app
|
85
152
|
require "mihari/web/app"
|
86
153
|
|
87
|
-
|
154
|
+
# CLIs
|
155
|
+
require "mihari/cli/base"
|
156
|
+
|
157
|
+
require "mihari/cli/analyzer"
|
158
|
+
require "mihari/cli/init"
|
159
|
+
|
160
|
+
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
|
-
|
9
|
-
|
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
|
-
|
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
|
-
|
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.
|
125
|
+
@valid_emitters ||= Mihari.emitters.filter_map do |klass|
|
85
126
|
emitter = klass.new
|
86
127
|
emitter.valid? ? emitter : nil
|
87
|
-
end
|
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
|
-
|
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
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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.
|
19
|
+
events.filter_map do |event|
|
26
20
|
event.dig "target", "ip"
|
27
|
-
end
|
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
|
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 "
|
3
|
+
require "censysx"
|
4
4
|
|
5
5
|
module Mihari
|
6
6
|
module Analyzers
|
7
7
|
class Censys < Base
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|
-
|
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
|
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
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
22
|
+
cursor = nil
|
23
|
+
loop do
|
24
|
+
response = api.search(query, cursor: cursor)
|
25
|
+
ipv4s << response_to_ipv4s(response)
|
52
26
|
|
53
|
-
|
54
|
-
|
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
|
-
|
32
|
+
ipv4s.flatten
|
65
33
|
end
|
66
34
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
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
|
47
|
+
def configuration_keys
|
85
48
|
%w[censys_id censys_secret]
|
86
49
|
end
|
87
50
|
|