miteru 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7428f741cd791e37e5f201c978f4ee6b5f6fe239d350b69d3377a6af4604305d
4
- data.tar.gz: 0057c9ee7ebe6dd5ee039939b30ff897d3298605c3843c980f2b30081ad8bc2d
3
+ metadata.gz: 62e5976eed670c3d2f2b0309e7723434c1e5c04ad337d4db519a58362618d566
4
+ data.tar.gz: fe54d9316505b380ac50ba9ab8cf520f2fd5b0e76cf88f0e520d7fb0c18a3b56
5
5
  SHA512:
6
- metadata.gz: b4f69cf18c8db256f035466f669835810e53329f7af7df5fd3d58689002c2e7175abb9456b31e21c0b23369e31a1559040c82f92c662202661c50cc8cc702334
7
- data.tar.gz: d7188fe6bc2372560031fb964254f5ea6df56672edd0f112a6ebe7b41d5ad7efa2a52e6c1deb666891573990f0cf75e8b4ea768f5240cd5d25127a544e06e26c
6
+ metadata.gz: 122f011baecb0a38639610ffaf68645c48f454ef7aa6ba79d5e812f71129f303a4386308cf639e233ff43473907f9567f90625b3651e027d18d6324e694ba00e
7
+ data.tar.gz: 0f21e5a5b6882fb9705d9988647de16aad44693ddfbbd9983a01f16c029923629322c40165a28dcb33e4a49482bcddbc1d272825beae7b0017400c560e16a57d
@@ -0,0 +1,53 @@
1
+ require "redis"
2
+
3
+ module Miteru
4
+ class Cache < Service
5
+ # @return [String]
6
+ attr_reader :url
7
+
8
+ #
9
+ # @param [String] url
10
+ #
11
+ def initialize(url)
12
+ super()
13
+ @url = url
14
+ end
15
+
16
+ #
17
+ # @param [String] key
18
+ # @param [String] value
19
+ # @param [Integer. nil] ex
20
+ #
21
+ def set(key, value, ex:)
22
+ value = redis.set("#{prefix}:#{key}", value, ex:)
23
+ Miteru.logger.info("Cache:#{key} is set.") if verbose?
24
+ value
25
+ end
26
+
27
+ #
28
+ # @param [String] key
29
+ #
30
+ def cached?(key)
31
+ value = redis.exists?("#{prefix}:#{key}")
32
+ Miteru.logger.info("Cache:#{key} found.") if verbose?
33
+ value
34
+ end
35
+
36
+ private
37
+
38
+ def verbose?
39
+ Miteru.config.verbose
40
+ end
41
+
42
+ def prefix
43
+ Miteru.config.cache_prefix
44
+ end
45
+
46
+ #
47
+ # @return [Redis]
48
+ #
49
+ def redis
50
+ @redis ||= Redis.new(url:)
51
+ end
52
+ end
53
+ end
data/lib/miteru/config.rb CHANGED
@@ -22,6 +22,9 @@ module Miteru
22
22
  sentry_dsn: nil,
23
23
  sentry_trace_sample_rate: 0.25,
24
24
  sidekiq_redis_url: nil,
25
+ cache_redis_url: nil,
26
+ cache_ex: nil,
27
+ cache_prefix: "miteru:cache",
25
28
  slack_channel: "#general",
26
29
  slack_webhook_url: nil,
27
30
  threads: Parallel.processor_count,
@@ -39,6 +42,15 @@ module Miteru
39
42
  # @return [Float]
40
43
 
41
44
  # @!attribute [r] sidekiq_redis_url
45
+ # @return [String, nil]
46
+
47
+ # @!attribute [r] cache_redis_url
48
+ # @return [String, nil]
49
+
50
+ # @!attribute [r] cache_ex
51
+ # @return [Integer, nil]
52
+
53
+ # @!attribute [r] cache_prefix
42
54
  # @return [String]
43
55
 
44
56
  # @!attribute [r] http_timeout
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "colorize"
4
+
3
5
  module Miteru
4
6
  class Crawler < Service
5
7
  #
@@ -7,27 +9,46 @@ module Miteru
7
9
  #
8
10
  def call(website)
9
11
  Try[OpenSSL::SSL::SSLError, ::HTTP::Error, Addressable::URI::InvalidURIError] do
10
- Miteru.logger.info("Website:#{website.truncated_url} has #{website.kits.length} kit(s).")
11
- return unless website.kits?
12
-
13
- notify website
14
-
15
- return unless auto_download?
12
+ info = "Website:#{website.info}."
13
+ info = info.colorize(:red) if website.kits?
14
+ Miteru.logger.info(info)
16
15
 
17
16
  website.kits.each do |kit|
18
17
  downloader = Downloader.new(kit)
19
18
  result = downloader.result
20
- if result.success?
21
- Miteru.logger.info("Kit:#{kit.truncated_url} downloaded as #{result.value!}.")
22
- else
19
+
20
+ unless result.success?
23
21
  Miteru.logger.warn("Kit:#{kit.truncated_url} failed to download - #{result.failure}.")
22
+ next
24
23
  end
24
+
25
+ destination = result.value!
26
+ Miteru.logger.info("Kit:#{kit.truncated_url} downloaded as #{destination}.")
27
+ # Remove downloaded file if auto_download is not allowed
28
+ FileUtils.rm(destination, force: true) unless auto_download?
29
+ # Notify the website
30
+ notify website
25
31
  end
32
+
33
+ # Cache the website
34
+ cache.set(website.url, website.source, ex: cache_ex) if cache?
26
35
  end.recover { nil }.value!
27
36
  end
28
37
 
29
38
  private
30
39
 
40
+ def cache?
41
+ Miteru.cache?
42
+ end
43
+
44
+ def cache
45
+ Miteru.cache
46
+ end
47
+
48
+ def cache_ex
49
+ Miteru.config.cache_ex
50
+ end
51
+
31
52
  def auto_download?
32
53
  Miteru.config.auto_download
33
54
  end
@@ -5,6 +5,7 @@ require "slack-notifier"
5
5
  module Miteru
6
6
  module Notifiers
7
7
  class SlackAttachment
8
+ # @return [String]
8
9
  attr_reader :url
9
10
 
10
11
  def initialize(url)
@@ -14,7 +15,7 @@ module Miteru
14
15
  def to_a
15
16
  [
16
17
  {
17
- text: defanged_url,
18
+ text:,
18
19
  fallback: "VT & urlscan.io links",
19
20
  actions:
20
21
  }
@@ -47,10 +48,6 @@ module Miteru
47
48
  }
48
49
  end
49
50
 
50
- def defanged_url
51
- @defanged_url ||= url.to_s.gsub(".", "[.]")
52
- end
53
-
54
51
  def domain
55
52
  @domain ||= [].tap do |out|
56
53
  out << URI(url).hostname
@@ -59,6 +56,10 @@ module Miteru
59
56
  end.first
60
57
  end
61
58
 
59
+ def text
60
+ domain.to_s.gsub(".", "[.]")
61
+ end
62
+
62
63
  def _urlscan_link
63
64
  return nil unless domain
64
65
 
@@ -82,12 +83,11 @@ module Miteru
82
83
  return unless callable?
83
84
 
84
85
  attachment = SlackAttachment.new(website.url)
85
- kits = website.kits.select(&:downloaded?)
86
- notifier.post(text: website.message.capitalize, attachments: attachment.to_a) if kits.any?
86
+ notifier.post(text: website.info, attachments: attachment.to_a) if website.kits?
87
87
  end
88
88
 
89
89
  def callable?
90
- !slack_webhook_url.nil?
90
+ !webhook_url.nil?
91
91
  end
92
92
 
93
93
  private
@@ -9,7 +9,7 @@ module Miteru
9
9
  def call(website)
10
10
  return unless callable?
11
11
 
12
- website.kits.each { |kit| submit(kit.url) }
12
+ website.kits.each { |kit| submit(kit.url, source: website.source) }
13
13
  end
14
14
 
15
15
  def callable?
@@ -41,8 +41,12 @@ module Miteru
41
41
  Miteru.config.urlscan_submit_visibility
42
42
  end
43
43
 
44
- def submit(url)
45
- http.post("https://urlscan.io/api/v1/scan/", json: {tags:, visibility:, url:})
44
+ #
45
+ # @param [String] url
46
+ # @param [String] source
47
+ #
48
+ def submit(url, source:)
49
+ http.post("https://urlscan.io/api/v1/scan/", json: {tags: tags + ["source:#{source}"], visibility:, url:})
46
50
  end
47
51
  end
48
52
  end
@@ -3,16 +3,16 @@
3
3
  module Miteru
4
4
  class Orchestrator < Service
5
5
  def call
6
- Miteru.logger.info("#{websites.length} websites loaded in total.") if verbose?
6
+ Miteru.logger.info("#{non_cached_websites.length} websites loaded in total.") if verbose?
7
7
 
8
8
  if Miteru.sidekiq?
9
- websites.each do |website|
9
+ non_cached_websites.each do |website|
10
10
  Jobs::CrawleJob.perform_async(website.url, website.source)
11
11
  Miteru.logger.info("Website:#{website.truncated_url} crawler job queued.") if verbose?
12
12
  end
13
13
  else
14
14
  Miteru.logger.info("Use #{threads} thread(s).") if verbose?
15
- Parallel.each(websites, in_threads: threads) do |website|
15
+ Parallel.each(non_cached_websites, in_threads: threads) do |website|
16
16
  Miteru.logger.info("Website:#{website.truncated_url} crawling started.") if verbose?
17
17
 
18
18
  result = Crawler.result(website)
@@ -26,7 +26,7 @@ module Miteru
26
26
  end
27
27
 
28
28
  #
29
- # @return [Array<Miteru::Websites>]
29
+ # @return [Array<Miteru::Website>]
30
30
  #
31
31
  def websites
32
32
  @websites ||= [].tap do |out|
@@ -43,6 +43,12 @@ module Miteru
43
43
  end.flatten.uniq(&:url)
44
44
  end
45
45
 
46
+ def non_cached_websites
47
+ return websites unless cache?
48
+
49
+ websites.reject { |website| cache.cached?(website.url) }
50
+ end
51
+
46
52
  #
47
53
  # @return [Array<Miteru::Feeds::Base>]
48
54
  #
@@ -52,6 +58,14 @@ module Miteru
52
58
 
53
59
  private
54
60
 
61
+ def cache?
62
+ Miteru.cache?
63
+ end
64
+
65
+ def cache
66
+ Miteru.cache
67
+ end
68
+
55
69
  def threads
56
70
  Miteru.config.threads
57
71
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Miteru
4
- VERSION = "2.1.0"
4
+ VERSION = "2.2.0"
5
5
  end
@@ -48,6 +48,14 @@ module Miteru
48
48
  url.truncate(64)
49
49
  end
50
50
 
51
+ def defanged_truncated_url
52
+ truncated_url.to_s.gsub(".", "[.]")
53
+ end
54
+
55
+ def info
56
+ "#{defanged_truncated_url} has #{kits.length} kit(s) (Source: #{source})"
57
+ end
58
+
51
59
  private
52
60
 
53
61
  def timeout
data/lib/miteru.rb CHANGED
@@ -33,6 +33,9 @@ require "miteru/concerns/database_connectable"
33
33
  require "miteru/concerns/error_unwrappable"
34
34
 
35
35
  # Core classes
36
+ require "miteru/service"
37
+
38
+ require "miteru/cache"
36
39
  require "miteru/config"
37
40
  require "miteru/http"
38
41
 
@@ -93,6 +96,14 @@ module Miteru
93
96
  env == "development"
94
97
  end
95
98
 
99
+ def cache?
100
+ !Miteru.config.cache_redis_url.nil?
101
+ end
102
+
103
+ def cache
104
+ @cache ||= Cache.new(Miteru.config.cache_redis_url)
105
+ end
106
+
96
107
  #
97
108
  # @return [Boolean]
98
109
  #
@@ -117,8 +128,6 @@ module Miteru
117
128
  end
118
129
 
119
130
  # Services
120
- require "miteru/service"
121
-
122
131
  require "miteru/crawler"
123
132
  require "miteru/downloader"
124
133
  require "miteru/kit"
data/miteru.gemspec CHANGED
@@ -1,67 +1,68 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- lib = File.expand_path('lib', __dir__)
3
+ lib = File.expand_path("lib", __dir__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
- require 'miteru/version'
5
+ require "miteru/version"
6
6
 
7
7
  Gem::Specification.new do |spec|
8
- spec.name = 'miteru'
8
+ spec.name = "miteru"
9
9
  spec.version = Miteru::VERSION
10
- spec.authors = ['Manabu Niseki']
11
- spec.email = ['manabu.niseki@gmail.com']
12
- spec.metadata['rubygems_mfa_required'] = 'true'
10
+ spec.authors = ["Manabu Niseki"]
11
+ spec.email = ["manabu.niseki@gmail.com"]
12
+ spec.metadata["rubygems_mfa_required"] = "true"
13
13
 
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'
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 = 'exe'
24
+ spec.bindir = "exe"
25
25
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
- spec.require_paths = ['lib']
26
+ spec.require_paths = ["lib"]
27
27
 
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.13'
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'
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.13"
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"
41
41
 
42
- spec.add_dependency 'activerecord', '7.1.3'
43
- spec.add_dependency 'addressable', '2.8.6'
44
- spec.add_dependency 'anyway_config', '2.6.2'
45
- spec.add_dependency 'colorize', '1.1.0'
46
- spec.add_dependency 'dotenv', '2.8.1'
47
- spec.add_dependency 'down', '5.4.1'
48
- spec.add_dependency 'dry-files', '1.1.0'
49
- spec.add_dependency 'dry-monads', '1.6.0'
50
- spec.add_dependency 'http', '5.2.0'
51
- spec.add_dependency 'memo_wise', '1.8.0'
52
- spec.add_dependency 'oga', '3.4'
53
- spec.add_dependency 'parallel', '1.24.0'
54
- spec.add_dependency 'puma', '6.4.2'
55
- spec.add_dependency 'rack', '3.0.9'
56
- spec.add_dependency 'rack-session', '2.0.0'
57
- spec.add_dependency 'rackup', '2.1.0'
58
- spec.add_dependency 'semantic_logger', '4.15.0'
59
- spec.add_dependency 'sentry-ruby', '5.16.1'
60
- spec.add_dependency 'sentry-sidekiq', '5.16.1'
61
- spec.add_dependency 'sidekiq', '7.2.1'
62
- spec.add_dependency 'slack-notifier', '2.4.0'
63
- spec.add_dependency 'sqlite3', '1.7.2'
64
- spec.add_dependency 'thor', '1.3.0'
65
- spec.add_dependency 'thor-hollaback', '0.2.1'
66
- spec.add_dependency 'uuidtools', '2.2.0'
42
+ spec.add_dependency "activerecord", "7.1.3"
43
+ spec.add_dependency "addressable", "2.8.6"
44
+ spec.add_dependency "anyway_config", "2.6.3"
45
+ spec.add_dependency "colorize", "1.1.0"
46
+ spec.add_dependency "dotenv", "2.8.1"
47
+ spec.add_dependency "down", "5.4.1"
48
+ spec.add_dependency "dry-files", "1.1.0"
49
+ spec.add_dependency "dry-monads", "1.6.0"
50
+ spec.add_dependency "http", "5.2.0"
51
+ spec.add_dependency "memo_wise", "1.8.0"
52
+ spec.add_dependency "oga", "3.4"
53
+ spec.add_dependency "parallel", "1.24.0"
54
+ spec.add_dependency "puma", "6.4.2"
55
+ spec.add_dependency "rack", "3.0.9"
56
+ spec.add_dependency "rack-session", "2.0.0"
57
+ spec.add_dependency "rackup", "2.1.0"
58
+ spec.add_dependency "redis", "5.0.8"
59
+ spec.add_dependency "semantic_logger", "4.15.0"
60
+ spec.add_dependency "sentry-ruby", "5.16.1"
61
+ spec.add_dependency "sentry-sidekiq", "5.16.1"
62
+ spec.add_dependency "sidekiq", "7.2.1"
63
+ spec.add_dependency "slack-notifier", "2.4.0"
64
+ spec.add_dependency "sqlite3", "1.7.2"
65
+ spec.add_dependency "thor", "1.3.0"
66
+ spec.add_dependency "thor-hollaback", "0.2.1"
67
+ spec.add_dependency "uuidtools", "2.2.0"
67
68
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: miteru
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.2.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: 2024-02-06 00:00:00.000000000 Z
11
+ date: 2024-02-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -226,14 +226,14 @@ dependencies:
226
226
  requirements:
227
227
  - - '='
228
228
  - !ruby/object:Gem::Version
229
- version: 2.6.2
229
+ version: 2.6.3
230
230
  type: :runtime
231
231
  prerelease: false
232
232
  version_requirements: !ruby/object:Gem::Requirement
233
233
  requirements:
234
234
  - - '='
235
235
  - !ruby/object:Gem::Version
236
- version: 2.6.2
236
+ version: 2.6.3
237
237
  - !ruby/object:Gem::Dependency
238
238
  name: colorize
239
239
  requirement: !ruby/object:Gem::Requirement
@@ -416,6 +416,20 @@ dependencies:
416
416
  - - '='
417
417
  - !ruby/object:Gem::Version
418
418
  version: 2.1.0
419
+ - !ruby/object:Gem::Dependency
420
+ name: redis
421
+ requirement: !ruby/object:Gem::Requirement
422
+ requirements:
423
+ - - '='
424
+ - !ruby/object:Gem::Version
425
+ version: 5.0.8
426
+ type: :runtime
427
+ prerelease: false
428
+ version_requirements: !ruby/object:Gem::Requirement
429
+ requirements:
430
+ - - '='
431
+ - !ruby/object:Gem::Version
432
+ version: 5.0.8
419
433
  - !ruby/object:Gem::Dependency
420
434
  name: semantic_logger
421
435
  requirement: !ruby/object:Gem::Requirement
@@ -566,6 +580,7 @@ files:
566
580
  - exe/miteru
567
581
  - lefthook.yml
568
582
  - lib/miteru.rb
583
+ - lib/miteru/cache.rb
569
584
  - lib/miteru/cli/application.rb
570
585
  - lib/miteru/cli/base.rb
571
586
  - lib/miteru/cli/database.rb