miteru 1.2.2 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/gem.yml +36 -0
- data/.github/workflows/{test.yml → ruby.yml} +4 -13
- data/.gitignore +7 -2
- data/.rspec +1 -1
- data/README.md +7 -17
- data/docker-compose.yml +12 -0
- data/exe/miteru +3 -3
- data/lefthook.yml +9 -0
- data/lib/miteru/cli/application.rb +27 -0
- data/lib/miteru/cli/base.rb +16 -0
- data/lib/miteru/cli/database.rb +11 -0
- data/lib/miteru/commands/database.rb +23 -0
- data/lib/miteru/commands/main.rb +37 -0
- data/lib/miteru/commands/sidekiq.rb +35 -0
- data/lib/miteru/commands/web.rb +37 -0
- data/lib/miteru/concerns/database_connectable.rb +16 -0
- data/lib/miteru/concerns/error_unwrappable.rb +30 -0
- data/lib/miteru/config.rb +98 -0
- data/lib/miteru/crawler.rb +28 -44
- data/lib/miteru/database.rb +50 -38
- data/lib/miteru/downloader.rb +52 -41
- data/lib/miteru/errors.rb +37 -0
- data/lib/miteru/feeds/ayashige.rb +9 -20
- data/lib/miteru/feeds/base.rb +141 -0
- data/lib/miteru/feeds/phishing_database.rb +11 -10
- data/lib/miteru/feeds/urlscan.rb +47 -19
- data/lib/miteru/feeds/urlscan_pro.rb +20 -18
- data/lib/miteru/http.rb +51 -0
- data/lib/miteru/kit.rb +28 -20
- data/lib/miteru/mixin.rb +2 -29
- data/lib/miteru/notifiers/base.rb +10 -3
- data/lib/miteru/notifiers/slack.rb +85 -10
- data/lib/miteru/notifiers/urlscan.rb +29 -14
- data/lib/miteru/orchestrator.rb +58 -0
- data/lib/miteru/record.rb +8 -15
- data/lib/miteru/service.rb +28 -0
- data/lib/miteru/sidekiq/application.rb +13 -0
- data/lib/miteru/sidekiq/jobs.rb +21 -0
- data/lib/miteru/version.rb +1 -1
- data/lib/miteru/web/application.rb +42 -0
- data/lib/miteru/website.rb +48 -48
- data/lib/miteru.rb +130 -22
- data/miteru.gemspec +49 -38
- metadata +262 -97
- data/.overcommit.yml +0 -12
- data/.standard.yml +0 -4
- data/lib/miteru/attachement.rb +0 -74
- data/lib/miteru/cli.rb +0 -41
- data/lib/miteru/configuration.rb +0 -122
- data/lib/miteru/error.rb +0 -7
- data/lib/miteru/feeds/feed.rb +0 -53
- data/lib/miteru/feeds/phishstats.rb +0 -28
- data/lib/miteru/feeds.rb +0 -45
- data/lib/miteru/http_client.rb +0 -85
data/lib/miteru/website.rb
CHANGED
@@ -3,16 +3,20 @@
|
|
3
3
|
require "oga"
|
4
4
|
|
5
5
|
module Miteru
|
6
|
-
class Website
|
7
|
-
VALID_EXTENSIONS = Miteru.configuration.valid_extensions
|
8
|
-
|
6
|
+
class Website < Service
|
9
7
|
# @return [String]
|
10
8
|
attr_reader :url
|
11
9
|
|
12
10
|
# @return [String]
|
13
11
|
attr_reader :source
|
14
12
|
|
15
|
-
|
13
|
+
#
|
14
|
+
# @param [String] url
|
15
|
+
# @param [String] source
|
16
|
+
#
|
17
|
+
def initialize(url, source:)
|
18
|
+
super()
|
19
|
+
|
16
20
|
@url = url
|
17
21
|
@source = source
|
18
22
|
end
|
@@ -22,86 +26,82 @@ module Miteru
|
|
22
26
|
end
|
23
27
|
|
24
28
|
def kits
|
25
|
-
@kits ||= links.
|
26
|
-
kit = Kit.new(link, source)
|
27
|
-
kit.valid? ? kit : nil
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def ok?
|
32
|
-
response.code == 200
|
29
|
+
@kits ||= links.map { |link| Kit.new(link, source:) }.select(&:valid?)
|
33
30
|
end
|
34
31
|
|
35
32
|
def index?
|
36
33
|
title.to_s.start_with? "Index of"
|
37
34
|
end
|
38
35
|
|
39
|
-
def kits?
|
40
|
-
!kits.empty?
|
41
|
-
end
|
42
|
-
|
43
36
|
def has_kits?
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
filename_with_sizes = kits.map(&:filename_with_size).join(", ")
|
53
|
-
noun = kits.length == 1 ? "a phishing kit" : "phishing kits"
|
54
|
-
"it might contain #{noun}: #{filename_with_sizes}."
|
37
|
+
@has_kits ||= lambda do
|
38
|
+
Try[Addressable::URI::InvalidURIError, Encoding::CompatibilityError, ::HTTP::Error, LL::ParserError,
|
39
|
+
OpenSSL::SSL::SSLError, StatusError, ArgumentError] do
|
40
|
+
!kits.empty?
|
41
|
+
end.recover do
|
42
|
+
false
|
43
|
+
end.value!
|
44
|
+
end.call
|
55
45
|
end
|
56
46
|
|
57
47
|
def links
|
58
48
|
(href_links + possible_file_links).compact.uniq
|
59
49
|
end
|
60
50
|
|
51
|
+
#
|
52
|
+
# @return [String]
|
53
|
+
#
|
54
|
+
def truncated_url
|
55
|
+
url.truncate(64)
|
56
|
+
end
|
57
|
+
|
61
58
|
private
|
62
59
|
|
63
|
-
def
|
64
|
-
|
60
|
+
def timeout
|
61
|
+
Miteru.config.http_timeout
|
62
|
+
end
|
63
|
+
|
64
|
+
def http
|
65
|
+
@http ||= HTTP::Factory.build(timeout:)
|
65
66
|
end
|
66
67
|
|
67
68
|
def get
|
68
|
-
|
69
|
+
http.get url
|
69
70
|
end
|
70
71
|
|
71
|
-
def
|
72
|
-
@
|
72
|
+
def response
|
73
|
+
@response ||= get
|
73
74
|
end
|
74
75
|
|
75
|
-
def
|
76
|
-
Oga.parse_html
|
77
|
-
rescue ArgumentError, Encoding::CompatibilityError, LL::ParserError => _e
|
78
|
-
nil
|
76
|
+
def doc
|
77
|
+
Oga.parse_html response.body.to_s
|
79
78
|
end
|
80
79
|
|
81
80
|
def href_links
|
82
|
-
|
81
|
+
Try[Addressable::URI::InvalidURIError, Encoding::CompatibilityError, ::HTTP::Error, LL::ParserError,
|
82
|
+
OpenSSL::SSL::SSLError, StatusError, ArgumentError] do
|
83
83
|
doc.css("a").filter_map { |a| a.get("href") }.map do |href|
|
84
84
|
href = href.start_with?("/") ? href : "/#{href}"
|
85
85
|
url + href
|
86
86
|
end
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
87
|
+
end.recover { [] }.value!
|
88
|
+
end
|
89
|
+
|
90
|
+
def file_extensions
|
91
|
+
Miteru.config.file_extensions
|
92
92
|
end
|
93
93
|
|
94
94
|
def possible_file_links
|
95
|
-
|
95
|
+
parsed = URI.parse(url)
|
96
96
|
|
97
|
-
segments =
|
98
|
-
return [] if segments.
|
97
|
+
segments = parsed.path.split("/")
|
98
|
+
return [] if segments.empty?
|
99
99
|
|
100
100
|
last = segments.last
|
101
|
-
|
101
|
+
file_extensions.map do |ext|
|
102
102
|
new_segments = segments[0..-2] + ["#{last}#{ext}"]
|
103
|
-
|
104
|
-
|
103
|
+
parsed.path = new_segments.join("/")
|
104
|
+
parsed.to_s
|
105
105
|
end
|
106
106
|
end
|
107
107
|
end
|
data/lib/miteru.rb
CHANGED
@@ -1,42 +1,150 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
# Core standard libraries
|
4
|
+
require "cgi"
|
5
|
+
require "json"
|
6
|
+
require "uri"
|
7
|
+
require "uuidtools"
|
8
|
+
|
9
|
+
# Core 3rd party libraries
|
10
|
+
require "colorize"
|
11
|
+
require "memo_wise"
|
12
|
+
require "parallel"
|
4
13
|
require "semantic_logger"
|
14
|
+
require "sentry-ruby"
|
5
15
|
|
6
|
-
require "
|
16
|
+
require "dry/files"
|
17
|
+
require "dry/monads"
|
7
18
|
|
8
|
-
|
9
|
-
require "
|
19
|
+
# Load .env
|
20
|
+
require "dotenv/load"
|
10
21
|
|
11
|
-
|
22
|
+
# Active Support & Active Record
|
23
|
+
require "active_support"
|
24
|
+
require "active_record"
|
12
25
|
|
13
|
-
|
26
|
+
# Version
|
27
|
+
require "miteru/version"
|
28
|
+
# Errors
|
29
|
+
require "miteru/errors"
|
14
30
|
|
15
|
-
|
16
|
-
require "miteru/
|
17
|
-
require "miteru/
|
31
|
+
# Concerns
|
32
|
+
require "miteru/concerns/database_connectable"
|
33
|
+
require "miteru/concerns/error_unwrappable"
|
18
34
|
|
19
|
-
|
20
|
-
require "miteru/
|
21
|
-
require "miteru/
|
22
|
-
require "miteru/website"
|
23
|
-
require "miteru/downloader"
|
24
|
-
require "miteru/feeds"
|
25
|
-
require "miteru/attachement"
|
26
|
-
require "miteru/crawler"
|
27
|
-
require "miteru/cli"
|
35
|
+
# Core classes
|
36
|
+
require "miteru/config"
|
37
|
+
require "miteru/http"
|
28
38
|
|
29
|
-
#
|
30
|
-
require "
|
39
|
+
# Database + ActiveRecord
|
40
|
+
require "miteru/database"
|
41
|
+
require "miteru/record"
|
31
42
|
|
32
43
|
module Miteru
|
33
44
|
class << self
|
34
|
-
|
45
|
+
prepend MemoWise
|
46
|
+
|
47
|
+
#
|
48
|
+
# @return [SematicLogger]
|
49
|
+
#
|
35
50
|
def logger
|
51
|
+
SemanticLogger.sync! unless sidekiq?
|
52
|
+
|
36
53
|
SemanticLogger.default_level = :info
|
37
54
|
SemanticLogger.add_appender(io: $stderr, formatter: :color)
|
38
55
|
SemanticLogger["Miteru"]
|
39
56
|
end
|
40
|
-
|
57
|
+
memo_wise :logger
|
58
|
+
|
59
|
+
#
|
60
|
+
# @return [Array<Miteru::Feeds::Base>]
|
61
|
+
#
|
62
|
+
def feeds
|
63
|
+
[]
|
64
|
+
end
|
65
|
+
memo_wise :feeds
|
66
|
+
|
67
|
+
#
|
68
|
+
# @return [Array<Miteru::Notifiers::Base>]
|
69
|
+
#
|
70
|
+
def notifiers
|
71
|
+
[]
|
72
|
+
end
|
73
|
+
memo_wise :notifiers
|
74
|
+
|
75
|
+
#
|
76
|
+
# @return [Miteru::Config]
|
77
|
+
#
|
78
|
+
def config
|
79
|
+
@config ||= Config.new
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
# @return [String]
|
84
|
+
#
|
85
|
+
def env
|
86
|
+
ENV["APP_ENV"] || ENV["RACK_ENV"]
|
87
|
+
end
|
88
|
+
|
89
|
+
#
|
90
|
+
# @return [Boolean]
|
91
|
+
#
|
92
|
+
def development?
|
93
|
+
env == "development"
|
94
|
+
end
|
95
|
+
|
96
|
+
#
|
97
|
+
# @return [Boolean]
|
98
|
+
#
|
99
|
+
def sidekiq?
|
100
|
+
!Miteru.config.sidekiq_redis_url.nil?
|
101
|
+
end
|
102
|
+
|
103
|
+
def sentry?
|
104
|
+
!Miteru.config.sentry_dsn.nil?
|
105
|
+
end
|
106
|
+
|
107
|
+
def initialize_sentry
|
108
|
+
return if Sentry.initialized?
|
109
|
+
|
110
|
+
Sentry.init do |config|
|
111
|
+
config.dsn = Miteru.config.sentry_dsn
|
112
|
+
config.traces_sample_rate = Miteru.config.sentry_trace_sample_rate
|
113
|
+
config.breadcrumbs_logger = %i[sentry_logger http_logger]
|
114
|
+
end
|
115
|
+
end
|
41
116
|
end
|
42
117
|
end
|
118
|
+
|
119
|
+
# Services
|
120
|
+
require "miteru/service"
|
121
|
+
|
122
|
+
require "miteru/crawler"
|
123
|
+
require "miteru/downloader"
|
124
|
+
require "miteru/kit"
|
125
|
+
require "miteru/orchestrator"
|
126
|
+
require "miteru/website"
|
127
|
+
|
128
|
+
# Notifiers
|
129
|
+
require "miteru/notifiers/base"
|
130
|
+
require "miteru/notifiers/slack"
|
131
|
+
require "miteru/notifiers/urlscan"
|
132
|
+
|
133
|
+
# Feeds
|
134
|
+
require "miteru/feeds/base"
|
135
|
+
|
136
|
+
require "miteru/feeds/ayashige"
|
137
|
+
require "miteru/feeds/phishing_database"
|
138
|
+
require "miteru/feeds/urlscan_pro"
|
139
|
+
require "miteru/feeds/urlscan"
|
140
|
+
|
141
|
+
# CLI
|
142
|
+
require "miteru/cli/application"
|
143
|
+
|
144
|
+
# Sidekiq
|
145
|
+
require "sidekiq"
|
146
|
+
|
147
|
+
require "miteru/sidekiq/application"
|
148
|
+
require "miteru/sidekiq/jobs"
|
149
|
+
|
150
|
+
Miteru.initialize_sentry if Miteru.sentry?
|
data/miteru.gemspec
CHANGED
@@ -1,55 +1,66 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
lib = File.expand_path(
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
-
require
|
5
|
+
require 'miteru/version'
|
6
6
|
|
7
7
|
Gem::Specification.new do |spec|
|
8
|
-
spec.name =
|
8
|
+
spec.name = 'miteru'
|
9
9
|
spec.version = Miteru::VERSION
|
10
|
-
spec.authors = [
|
11
|
-
spec.email = [
|
12
|
-
spec.metadata[
|
10
|
+
spec.authors = ['Manabu Niseki']
|
11
|
+
spec.email = ['manabu.niseki@gmail.com']
|
12
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
13
13
|
|
14
|
-
spec.summary =
|
15
|
-
spec.description =
|
16
|
-
spec.homepage =
|
17
|
-
spec.license =
|
14
|
+
spec.summary = 'A phishing kit collector for scavengers'
|
15
|
+
spec.description = 'A phishing kit collector for scavengers'
|
16
|
+
spec.homepage = 'https://github.com/ninoseki/miteru'
|
17
|
+
spec.license = 'MIT'
|
18
18
|
|
19
19
|
# Specify which files should be added to the gem when it is released.
|
20
20
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
21
21
|
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
22
22
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
23
23
|
end
|
24
|
-
spec.bindir =
|
24
|
+
spec.bindir = 'exe'
|
25
25
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
26
|
-
spec.require_paths = [
|
26
|
+
spec.require_paths = ['lib']
|
27
27
|
|
28
|
-
spec.add_development_dependency
|
29
|
-
spec.add_development_dependency
|
30
|
-
spec.add_development_dependency
|
31
|
-
spec.add_development_dependency
|
32
|
-
spec.add_development_dependency
|
33
|
-
spec.add_development_dependency
|
34
|
-
spec.add_development_dependency
|
35
|
-
spec.add_development_dependency
|
36
|
-
spec.add_development_dependency
|
37
|
-
spec.add_development_dependency
|
38
|
-
spec.add_development_dependency
|
39
|
-
spec.add_development_dependency
|
28
|
+
spec.add_development_dependency 'bundler', '~> 2.5'
|
29
|
+
spec.add_development_dependency 'capybara', '~> 3.40'
|
30
|
+
spec.add_development_dependency 'coveralls_reborn', '~> 0.28'
|
31
|
+
spec.add_development_dependency 'fuubar', '~> 2.5'
|
32
|
+
spec.add_development_dependency 'mysql2', '~> 0.5'
|
33
|
+
spec.add_development_dependency 'pg', '~> 1.5'
|
34
|
+
spec.add_development_dependency 'rake', '~> 13.1'
|
35
|
+
spec.add_development_dependency 'rspec', '~> 3.12'
|
36
|
+
spec.add_development_dependency 'simplecov-lcov', '~> 0.8'
|
37
|
+
spec.add_development_dependency 'standard', '~> 1.33'
|
38
|
+
spec.add_development_dependency 'test-prof', '~> 1.3'
|
39
|
+
spec.add_development_dependency 'vcr', '~> 6.2'
|
40
|
+
spec.add_development_dependency 'webmock', '~> 3.19'
|
40
41
|
|
41
|
-
spec.add_dependency
|
42
|
-
spec.add_dependency
|
43
|
-
spec.add_dependency
|
44
|
-
spec.add_dependency
|
45
|
-
spec.add_dependency
|
46
|
-
spec.add_dependency
|
47
|
-
spec.add_dependency
|
48
|
-
spec.add_dependency
|
49
|
-
spec.add_dependency
|
50
|
-
spec.add_dependency
|
51
|
-
spec.add_dependency
|
52
|
-
spec.add_dependency
|
53
|
-
spec.add_dependency
|
54
|
-
spec.add_dependency
|
42
|
+
spec.add_dependency 'activerecord', '7.1.3'
|
43
|
+
spec.add_dependency 'anyway_config', '2.6.2'
|
44
|
+
spec.add_dependency 'colorize', '1.1.0'
|
45
|
+
spec.add_dependency 'dotenv', '2.8.1'
|
46
|
+
spec.add_dependency 'down', '5.4.1'
|
47
|
+
spec.add_dependency 'dry-files', '1.1.0'
|
48
|
+
spec.add_dependency 'dry-monads', '1.6.0'
|
49
|
+
spec.add_dependency 'http', '5.1.1'
|
50
|
+
spec.add_dependency 'memo_wise', '1.8.0'
|
51
|
+
spec.add_dependency 'oga', '3.4'
|
52
|
+
spec.add_dependency 'parallel', '1.24.0'
|
53
|
+
spec.add_dependency 'puma', '6.4.2'
|
54
|
+
spec.add_dependency 'rack', '3.0.8'
|
55
|
+
spec.add_dependency 'rack-session', '2.0.0'
|
56
|
+
spec.add_dependency 'rackup', '2.1.0'
|
57
|
+
spec.add_dependency 'semantic_logger', '4.15.0'
|
58
|
+
spec.add_dependency 'sentry-ruby', '5.16.1'
|
59
|
+
spec.add_dependency 'sentry-sidekiq', '5.16.1'
|
60
|
+
spec.add_dependency 'sidekiq', '7.2.1'
|
61
|
+
spec.add_dependency 'slack-notifier', '2.4.0'
|
62
|
+
spec.add_dependency 'sqlite3', '1.7.1'
|
63
|
+
spec.add_dependency 'thor', '1.3.0'
|
64
|
+
spec.add_dependency 'thor-hollaback', '0.2.1'
|
65
|
+
spec.add_dependency 'uuidtools', '2.2.0'
|
55
66
|
end
|