robust_server_socket 0.3.3 → 0.4.3

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.
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RobustServerSocket
4
+ module Cacher # rubocop:disable Metrics/ModuleLength
5
+ class RedisConnectionError < StandardError; end
6
+
7
+ CLOCK_SKEW_MS = 30_000
8
+
9
+ class << self # rubocop:disable Metrics/ClassLength
10
+ def atomic_validate_and_log(key, ttl, timestamp_ms, expiration_time)
11
+ current_ms = Process.clock_gettime(Process::CLOCK_REALTIME, :millisecond)
12
+ redis.with do |conn|
13
+ conn.eval(lua_atomic_validate_and_log, keys: [key],
14
+ argv: [ttl, timestamp_ms, expiration_time, current_ms])
15
+ end
16
+ rescue ::Redis::BaseConnectionError => e
17
+ handle_redis_error(e, 'atomic_validate_and_log')
18
+ raise RedisConnectionError, "Failed to validate token: #{e.message}"
19
+ end
20
+
21
+ def incr_sliding_window_count(key, window_seconds)
22
+ now_ns = Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond)
23
+ redis.with do |conn|
24
+ conn.eval(lua_sliding_window, keys: [key],
25
+ argv: [now_ns, window_seconds * 1_000_000_000, window_seconds, now_ns.to_s])
26
+ end
27
+ rescue ::Redis::BaseConnectionError => e
28
+ handle_redis_error(e, 'incr_sliding_window_count')
29
+ raise RedisConnectionError, "Failed to count sliding window: #{e.message}"
30
+ end
31
+
32
+ def get(key)
33
+ redis.with do |conn|
34
+ conn.get(key)
35
+ end
36
+ rescue ::Redis::BaseConnectionError => e
37
+ handle_redis_error(e, 'get')
38
+ nil
39
+ end
40
+
41
+ def health_check
42
+ redis.with do |conn|
43
+ conn.ping == 'PONG'
44
+ end
45
+ rescue ::Redis::BaseConnectionError
46
+ false
47
+ end
48
+
49
+ def with_redis(&block)
50
+ redis.with(&block)
51
+ rescue ::Redis::BaseConnectionError => e
52
+ handle_redis_error(e, 'with_redis')
53
+ raise RedisConnectionError, "Redis operation failed: #{e.message}"
54
+ end
55
+
56
+ # Clear cached Redis connection pool (useful for hot reloading in development)
57
+ def clear_redis_pool_cache!
58
+ @redis = nil
59
+ end
60
+
61
+ private
62
+
63
+ def lua_sliding_window
64
+ <<~LUA
65
+ local key = KEYS[1]
66
+ local now_ns = tonumber(ARGV[1])
67
+ local window_ns = tonumber(ARGV[2])
68
+ local window_s = tonumber(ARGV[3])
69
+ local member = ARGV[4]
70
+
71
+ redis.call('ZREMRANGEBYSCORE', key, '-inf', now_ns - window_ns)
72
+ redis.call('ZADD', key, now_ns, member)
73
+ redis.call('EXPIRE', key, window_s)
74
+ return redis.call('ZCARD', key)
75
+ LUA
76
+ end
77
+
78
+ def lua_atomic_validate_and_log
79
+ <<~LUA
80
+ local key = KEYS[1]
81
+ local ttl = tonumber(ARGV[1])
82
+ local timestamp_ms = tonumber(ARGV[2])
83
+ local expiration_ms = tonumber(ARGV[3]) * 1000
84
+ local current_ms = tonumber(ARGV[4])
85
+
86
+ if timestamp_ms > current_ms + #{CLOCK_SKEW_MS} then
87
+ return 'stale'
88
+ end
89
+
90
+ if expiration_ms <= (current_ms - timestamp_ms) then
91
+ return 'stale'
92
+ end
93
+
94
+ -- Check if token was already used
95
+ local current = redis.call('GET', key)
96
+ if current and tonumber(current) > 0 then
97
+ return 'used'
98
+ end
99
+
100
+ -- Mark token as used
101
+ redis.call('INCRBY', key, 1)
102
+ redis.call('EXPIRE', key, ttl)
103
+
104
+ return 'ok'
105
+ LUA
106
+ end
107
+
108
+ # Cache Redis connection pool at module level for the lifetime of the Rails process
109
+ # This avoids recreating the connection pool on every Redis operation
110
+ def redis
111
+ @redis ||= ::ConnectionPool::Wrapper.new(**pool_config) do
112
+ ::Redis.new(redis_config)
113
+ end
114
+ end
115
+
116
+ def pool_config
117
+ {
118
+ size: ENV.fetch('REDIS_POOL_SIZE', 25).to_i,
119
+ timeout: ENV.fetch('REDIS_POOL_TIMEOUT', 1).to_f
120
+ }
121
+ end
122
+
123
+ def redis_config
124
+ config = {
125
+ url: ::RobustServerSocket.configuration.redis_url,
126
+ reconnect_attempts: 3,
127
+ timeout: 1.0,
128
+ connect_timeout: 2.0
129
+ }
130
+
131
+ password = ::RobustServerSocket.configuration.redis_pass
132
+ config[:password] = password if password && !password.empty?
133
+
134
+ config
135
+ end
136
+
137
+ def handle_redis_error(error, operation)
138
+ warn "Redis operation '#{operation}' failed: #{error.class} - #{error.message}"
139
+ end
140
+ end
141
+ end
142
+ end
@@ -1,20 +1,13 @@
1
- require_relative 'secure_token/cacher'
2
- require_relative 'secure_token/decrypt'
3
- require_relative 'rate_limiter'
1
+ # frozen_string_literal: true
4
2
 
5
3
  module RobustServerSocket
6
4
  class ClientToken
7
5
  TOKEN_REGEXP = /\A(.+)_(\d{10,})\z/.freeze
8
6
 
9
7
  InvalidToken = Class.new(StandardError)
10
- UnauthorizedClient = Class.new(StandardError)
11
- UsedToken = Class.new(StandardError)
12
- StaleToken = Class.new(StandardError)
13
8
 
14
9
  def self.validate!(secure_token)
15
- new(secure_token).tap do |instance|
16
- instance.validate!
17
- end
10
+ new(secure_token).tap(&:validate!)
18
11
  end
19
12
 
20
13
  def initialize(secure_token)
@@ -23,34 +16,25 @@ module RobustServerSocket
23
16
  end
24
17
 
25
18
  def validate!
26
- raise InvalidToken unless decrypted_token
27
- raise UnauthorizedClient unless client
28
-
29
- RateLimiter.check!(client)
30
-
31
- result = atomic_validate_and_log_token
32
-
33
- case result
34
- when 'stale'
35
- raise StaleToken
36
- when 'used'
37
- raise UsedToken
38
- when 'ok'
39
- true
40
- else
41
- raise InvalidToken, "Unexpected validation result: #{result}"
42
- end
19
+ modules_checks!
20
+ rescue SecureToken::InvalidToken => e
21
+ raise InvalidToken, e.message
43
22
  end
44
23
 
45
24
  def valid?
46
- !!(decrypted_token &&
47
- client &&
48
- RateLimiter.check(client) &&
49
- atomic_validate_and_log_token == 'ok')
25
+ modules_checks
50
26
  rescue StandardError
51
27
  false
52
28
  end
53
29
 
30
+ def modules_checks
31
+ true
32
+ end
33
+
34
+ def modules_checks!
35
+ true
36
+ end
37
+
54
38
  def client
55
39
  @client ||= begin
56
40
  target = client_name.strip
@@ -58,19 +42,6 @@ module RobustServerSocket
58
42
  end
59
43
  end
60
44
 
61
- def token_not_expired?
62
- token_expiration_time > Time.now.utc.to_i - timestamp
63
- end
64
-
65
- def atomic_validate_and_log_token
66
- SecureToken::Cacher.atomic_validate_and_log(
67
- decrypted_token,
68
- token_expiration_time + 300,
69
- timestamp,
70
- token_expiration_time
71
- )
72
- end
73
-
74
45
  def decrypted_token
75
46
  @decrypted_token ||= SecureToken::Decrypt.call(@secure_token)
76
47
  end
@@ -93,6 +64,7 @@ module RobustServerSocket
93
64
  @split_token ||= begin
94
65
  match_data = decrypted_token.to_s.match(TOKEN_REGEXP)
95
66
  raise InvalidToken, 'Invalid token format' unless match_data
67
+
96
68
  match_data.captures
97
69
  end
98
70
  end
@@ -114,6 +86,9 @@ module RobustServerSocket
114
86
  raise InvalidToken, 'Token cannot be empty' if token.empty?
115
87
  raise InvalidToken, 'Token too long' if token.length > 2048
116
88
 
89
+ # Check for null-byte injection
90
+ raise InvalidToken, 'Token contains invalid characters' if token.include?("\x00")
91
+
117
92
  token
118
93
  end
119
94
  end
@@ -1,12 +1,25 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RobustServerSocket
2
4
  module Configuration
3
5
  MIN_KEY_SIZE = 2048
4
6
 
7
+ ConfigurationError = Class.new(StandardError)
8
+
5
9
  attr_reader :configuration, :configured
6
10
 
11
+ def _push_modules_check_code(code)
12
+ configuration._modules_check_rows.push(code)
13
+ end
14
+
15
+ def _push_bang_modules_check_code(code)
16
+ configuration._bang_modules_check_rows.push(code)
17
+ end
18
+
7
19
  def configure
8
20
  @configuration ||= ConfigStore.new
9
21
  yield(configuration)
22
+ validate_configuration!
10
23
  validate_key_security!
11
24
 
12
25
  @configured = true
@@ -29,13 +42,25 @@ module RobustServerSocket
29
42
 
30
43
  private
31
44
 
45
+ def validate_configuration!
46
+ validate_positive!(:rate_limit_max_requests)
47
+ validate_positive!(:rate_limit_window_seconds)
48
+ validate_positive!(:token_expiration_time)
49
+ end
50
+
51
+ def validate_positive!(attr)
52
+ return if configuration.public_send(attr).to_i.positive?
53
+
54
+ raise ConfigurationError, "#{attr} must be positive"
55
+ end
56
+
32
57
  def validate_key_security!
33
58
  key = ::OpenSSL::PKey::RSA.new(configuration.private_key)
34
59
  key_bits = key.n.num_bits
35
60
 
36
61
  if key_bits < MIN_KEY_SIZE
37
62
  raise SecurityError,
38
- "RSA key size (#{key_bits} bits) below minimum (#{MIN_KEY_SIZE} bits)"
63
+ "RSA key size (#{key_bits} bits) below minimum (#{MIN_KEY_SIZE} bits)"
39
64
  end
40
65
  rescue ::OpenSSL::PKey::RSAError => e
41
66
  raise SecurityError, "Invalid private key: #{e.message}"
@@ -43,13 +68,19 @@ module RobustServerSocket
43
68
  end
44
69
 
45
70
  class ConfigStore
46
- attr_accessor :allowed_services, :private_key, :token_expiration_time, :redis_url, :redis_pass,
47
- :rate_limit_enabled, :rate_limit_max_requests, :rate_limit_window_seconds
71
+ attr_accessor :allowed_services, :private_key, :token_expiration_time,
72
+ :redis_url, :redis_pass,
73
+ :rate_limit_max_requests, :rate_limit_window_seconds, :using_modules
74
+
75
+ attr_reader :_modules_check_rows, :_bang_modules_check_rows
48
76
 
49
77
  def initialize
50
- @rate_limit_enabled = false
51
78
  @rate_limit_max_requests = 100
52
79
  @rate_limit_window_seconds = 60
80
+ @token_expiration_time = 10
81
+ @using_modules = %i[client_auth_protection rate_limit_protection replay_attack_protection]
82
+ @_modules_check_rows = []
83
+ @_bang_modules_check_rows = []
53
84
  end
54
85
  end
55
86
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RobustServerSocket
4
+ module Modules
5
+ module ClientAuthProtection
6
+ UnauthorizedClient = Class.new(StandardError)
7
+
8
+ def self.included(_base)
9
+ RobustServerSocket._push_modules_check_code('validate_client')
10
+ RobustServerSocket._push_bang_modules_check_code("validate_client!\n")
11
+ end
12
+
13
+ def validate_client
14
+ !!client
15
+ end
16
+
17
+ def validate_client!
18
+ raise UnauthorizedClient unless validate_client
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../cacher'
4
+ require_relative '../rate_limiter'
5
+
6
+ module RobustServerSocket
7
+ module Modules
8
+ module RateLimitProtection
9
+ def self.included(_base)
10
+ RobustServerSocket._push_modules_check_code('validate_rate_limit')
11
+ RobustServerSocket._push_bang_modules_check_code("validate_rate_limit!\n")
12
+ end
13
+
14
+ def validate_rate_limit
15
+ RateLimiter.check(client)
16
+ end
17
+
18
+ def validate_rate_limit!
19
+ RateLimiter.check!(client)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../cacher'
4
+
5
+ module RobustServerSocket
6
+ module Modules
7
+ module ReplayAttackProtection
8
+ UsedToken = Class.new(StandardError)
9
+ StaleToken = Class.new(StandardError)
10
+
11
+ def self.included(_base)
12
+ RobustServerSocket._push_modules_check_code('atomic_validate_and_log_token')
13
+ RobustServerSocket._push_bang_modules_check_code("atomic_validate_and_log_token!\n")
14
+ end
15
+
16
+ def atomic_validate_and_log_token!
17
+ result = Cacher.atomic_validate_and_log(
18
+ decrypted_token, store_used_token_time, timestamp, token_expiration_time
19
+ )
20
+ handle_validation_result!(result)
21
+ end
22
+
23
+ def atomic_validate_and_log_token
24
+ Cacher.atomic_validate_and_log(
25
+ decrypted_token,
26
+ store_used_token_time,
27
+ timestamp,
28
+ token_expiration_time
29
+ ) == 'ok'
30
+ end
31
+
32
+ private
33
+
34
+ def handle_validation_result!(result)
35
+ case result
36
+ when 'ok' then true
37
+ when 'stale' then raise StaleToken
38
+ when 'used' then raise UsedToken
39
+ else raise StandardError, "Unexpected result: #{result}"
40
+ end
41
+ end
42
+
43
+ def store_used_token_time
44
+ RobustServerSocket.configuration.token_expiration_time + Cacher::CLOCK_SKEW_MS / 1000
45
+ end
46
+ end
47
+ end
48
+ end
@@ -1,4 +1,4 @@
1
- require_relative 'secure_token/cacher'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module RobustServerSocket
4
4
  class RateLimiter
@@ -6,64 +6,40 @@ module RobustServerSocket
6
6
 
7
7
  class << self
8
8
  def check!(client_name)
9
- unless (attempts = check(client_name))
10
- actual_attempts = current_attempts(client_name)
11
- raise RateLimitExceeded, "Rate limit exceeded for #{client_name}: #{actual_attempts}/#{max_requests} requests per #{window_seconds}s"
12
- end
9
+ return if check(client_name)
13
10
 
14
- attempts
11
+ raise RateLimitExceeded,
12
+ "Rate limit exceeded for #{client_name}: max #{max_requests} per #{window_seconds}s"
15
13
  end
16
14
 
17
15
  def check(client_name)
18
- return 0 unless rate_limit_enabled?
19
-
20
- key = rate_limit_key(client_name)
21
- attempts = increment_attempts(key)
22
-
23
- return false if attempts > max_requests
24
-
25
- attempts
26
- end
27
-
28
- def current_attempts(client_name)
29
- return 0 unless rate_limit_enabled?
30
-
31
- key = rate_limit_key(client_name)
32
- SecureToken::Cacher.get(key).to_i
16
+ attempts = record_attempt(client_name)
17
+ attempts <= max_requests
33
18
  end
34
19
 
35
20
  def reset!(client_name)
36
21
  key = rate_limit_key(client_name)
37
- SecureToken::Cacher.with_redis do |conn|
22
+ Cacher.with_redis do |conn|
38
23
  conn.del(key)
39
24
  end
40
- rescue SecureToken::Cacher::RedisConnectionError => e
25
+ rescue Cacher::RedisConnectionError => e
41
26
  handle_redis_error(e, 'reset')
42
27
  nil
43
28
  end
44
29
 
45
30
  private
46
31
 
47
- def increment_attempts(key)
48
- SecureToken::Cacher.with_redis do |conn|
49
- attempts = conn.incr(key)
50
- # Set expiration only on first attempt to ensure atomic window
51
- conn.expire(key, window_seconds) if attempts == 1
52
- attempts
53
- end
54
- rescue SecureToken::Cacher::RedisConnectionError => e
55
- handle_redis_error(e, 'increment_attempts')
56
- 0 # Fail open: allow request if Redis is down
32
+ def record_attempt(client_name)
33
+ Cacher.incr_sliding_window_count(rate_limit_key(client_name), window_seconds)
34
+ rescue Cacher::RedisConnectionError => e
35
+ handle_redis_error(e, 'record_attempt')
36
+ 0
57
37
  end
58
38
 
59
39
  def rate_limit_key(client_name)
60
40
  "rate_limit:#{client_name}"
61
41
  end
62
42
 
63
- def rate_limit_enabled?
64
- RobustServerSocket.configuration.rate_limit_enabled
65
- end
66
-
67
43
  def max_requests
68
44
  RobustServerSocket.configuration.rate_limit_max_requests
69
45
  end
@@ -1,22 +1,18 @@
1
- require 'openssl'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module RobustServerSocket
4
4
  module SecureToken
5
- BASE64_REGEXP = /\A[A-Za-z0-9+\/]*={0,2}\z/.freeze
5
+ BASE64_REGEXP = %r{\A[A-Za-z0-9+/]+={0,2}\z}.freeze
6
6
  InvalidToken = Class.new(StandardError)
7
7
 
8
8
  module Decrypt
9
9
  class << self
10
10
  def call(token)
11
- unless token.is_a?(String) && token.match?(BASE64_REGEXP)
12
- raise InvalidToken, 'Invalid token format'
13
- end
11
+ raise InvalidToken, 'Invalid token format' unless token.is_a?(String) && token.match?(BASE64_REGEXP)
14
12
 
15
13
  decoded_token = ::Base64.strict_decode64(token)
16
14
 
17
- if decoded_token.bytesize > 1024
18
- raise InvalidToken, 'Token too large'
19
- end
15
+ raise InvalidToken, 'Token too large' if decoded_token.bytesize > 1024
20
16
 
21
17
  private_key.private_decrypt(decoded_token, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING).force_encoding('UTF-8')
22
18
  rescue ::OpenSSL::PKey::RSAError, ArgumentError
@@ -1,21 +1,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'base64'
4
+ require 'openssl'
5
+ require 'redis'
6
+ require 'connection_pool'
7
+
3
8
  require_relative 'robust_server_socket/configuration'
9
+ require_relative 'robust_server_socket/secure_token/decrypt'
10
+ require_relative 'robust_server_socket/client_token'
4
11
 
5
12
  module RobustServerSocket
6
13
  extend RobustServerSocket::Configuration
7
14
 
8
15
  module_function
9
16
 
10
- def load!
17
+ def load! # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
11
18
  raise 'You must correctly configure RobustServerSocket first!' unless configured?
12
19
 
13
- require 'openssl'
14
- require 'base64'
15
- require 'redis'
16
- require 'connection_pool'
20
+ configuration.using_modules.each do |mod|
21
+ raise ArgumentError, 'Module must be a Symbol!' unless mod.is_a?(Symbol)
22
+
23
+ require_relative "robust_server_socket/modules/#{mod}"
24
+ ClientToken.include const_get(mod.to_s.split('_').map(&:capitalize).unshift('Modules::').join)
25
+ end
26
+
27
+ ClientToken.class_eval(<<~METHOD, __FILE__, __LINE__ + 1)
28
+ def modules_checks
29
+ #{(RobustServerSocket.configuration._modules_check_rows.empty? ? ['true'] : RobustServerSocket.configuration._modules_check_rows.map(&:strip)).join(' && ')}
30
+ end
17
31
 
18
- require_relative 'robust_server_socket/rate_limiter'
19
- require_relative 'robust_server_socket/client_token'
32
+ def modules_checks!
33
+ #{(RobustServerSocket.configuration._bang_modules_check_rows.empty? ? ['true'] : RobustServerSocket.configuration._bang_modules_check_rows).join}
34
+ end
35
+ METHOD
20
36
  end
21
37
  end
data/lib/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RobustServerSocket
4
- VERSION = '0.3.3'
4
+ VERSION = '0.4.3'
5
5
  end
@@ -1,16 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "./lib/version"
3
+ require './lib/version'
4
4
 
5
5
  Gem::Specification.new do |spec|
6
- spec.name = "robust_server_socket"
6
+ spec.name = 'robust_server_socket'
7
7
  spec.version = RobustServerSocket::VERSION
8
- spec.authors = ["tee_zed"]
9
- spec.email = ["tee0zed@gmail.com"]
10
- spec.description = "Robust Server Socket"
11
- spec.summary = "Robust Server Socket gem for RobustPro"
12
- spec.license = "MIT"
13
- spec.required_ruby_version = ">= 2.7.0"
8
+ spec.authors = ['tee_zed']
9
+ spec.email = ['tee0zed@gmail.com']
10
+ spec.description = 'Robust Server Socket'
11
+ spec.summary = 'Robust Server Socket gem for RobustPro'
12
+ spec.license = 'MIT'
13
+ spec.required_ruby_version = '>= 2.7.0'
14
14
 
15
15
  # Specify which files should be added to the gem when it is released.
16
16
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
@@ -20,13 +20,13 @@ Gem::Specification.new do |spec|
20
20
  f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile])
21
21
  end
22
22
  end
23
- spec.bindir = "exe"
23
+ spec.bindir = 'exe'
24
24
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
25
- spec.require_paths = ["lib"]
26
- spec.add_dependency 'rspec'
25
+ spec.require_paths = ['lib']
26
+ spec.add_dependency 'hiredis'
27
27
  spec.add_dependency 'rake'
28
28
  spec.add_dependency 'redis'
29
- spec.add_dependency 'hiredis'
29
+ spec.add_dependency 'rspec'
30
30
 
31
31
  # Uncomment to register a new dependency of your gem
32
32
  # spec.add_dependency "example-gem", "~> 1.0"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: robust_server_socket
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.3
4
+ version: 0.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - tee_zed
@@ -10,7 +10,7 @@ cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
- name: rspec
13
+ name: hiredis
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
16
  - - ">="
@@ -52,7 +52,7 @@ dependencies:
52
52
  - !ruby/object:Gem::Version
53
53
  version: '0'
54
54
  - !ruby/object:Gem::Dependency
55
- name: hiredis
55
+ name: rspec
56
56
  requirement: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - ">="
@@ -73,16 +73,21 @@ extensions: []
73
73
  extra_rdoc_files: []
74
74
  files:
75
75
  - ".rspec"
76
+ - ".rubocop.yml"
77
+ - ".ruby-version"
76
78
  - CODE_OF_CONDUCT.md
77
79
  - LICENSE.txt
78
80
  - README.en.md
79
81
  - README.md
80
82
  - Rakefile
81
83
  - lib/robust_server_socket.rb
84
+ - lib/robust_server_socket/cacher.rb
82
85
  - lib/robust_server_socket/client_token.rb
83
86
  - lib/robust_server_socket/configuration.rb
87
+ - lib/robust_server_socket/modules/client_auth_protection.rb
88
+ - lib/robust_server_socket/modules/rate_limit_protection.rb
89
+ - lib/robust_server_socket/modules/replay_attack_protection.rb
84
90
  - lib/robust_server_socket/rate_limiter.rb
85
- - lib/robust_server_socket/secure_token/cacher.rb
86
91
  - lib/robust_server_socket/secure_token/decrypt.rb
87
92
  - lib/version.rb
88
93
  - robust_server_socket.gemspec
@@ -103,7 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
103
108
  - !ruby/object:Gem::Version
104
109
  version: '0'
105
110
  requirements: []
106
- rubygems_version: 3.6.9
111
+ rubygems_version: 4.0.6
107
112
  specification_version: 4
108
113
  summary: Robust Server Socket gem for RobustPro
109
114
  test_files: []