rack-protection 2.1.0 → 4.1.1

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.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/protection'
2
4
 
3
5
  module Rack
@@ -17,7 +19,7 @@ module Rack
17
19
  # frame. Use :deny to forbid any embedding, :sameorigin
18
20
  # to allow embedding from the same origin (default).
19
21
  class FrameOptions < Base
20
- default_options :frame_options => :sameorigin
22
+ default_options frame_options: :sameorigin
21
23
 
22
24
  def frame_options
23
25
  @frame_options ||= begin
@@ -29,7 +31,7 @@ module Rack
29
31
 
30
32
  def call(env)
31
33
  status, headers, body = @app.call(env)
32
- headers['X-Frame-Options'] ||= frame_options if html? headers
34
+ headers['x-frame-options'] ||= frame_options if html? headers
33
35
  [status, headers, body]
34
36
  end
35
37
  end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rack/protection'
4
+ require 'ipaddr'
5
+
6
+ module Rack
7
+ module Protection
8
+ ##
9
+ # Prevented attack:: DNS rebinding and other Host header attacks
10
+ # Supported browsers:: all
11
+ # More infos:: https://en.wikipedia.org/wiki/DNS_rebinding
12
+ # https://portswigger.net/web-security/host-header
13
+ #
14
+ # Blocks HTTP requests with an unrecognized hostname in any of the following
15
+ # HTTP headers: Host, X-Forwarded-Host, Forwarded
16
+ #
17
+ # If you want to permit a specific hostname, you can pass in as the `:permitted_hosts` option:
18
+ #
19
+ # use Rack::Protection::HostAuthorization, permitted_hosts: ["www.example.org", "sinatrarb.com"]
20
+ #
21
+ # The `:allow_if` option can also be set to a proc to use custom allow/deny logic.
22
+ class HostAuthorization < Base
23
+ DOT = '.'
24
+ PORT_REGEXP = /:\d+\z/.freeze
25
+ SUBDOMAINS = /[a-z0-9\-.]+/.freeze
26
+ private_constant :DOT,
27
+ :PORT_REGEXP,
28
+ :SUBDOMAINS
29
+ default_reaction :deny
30
+ default_options allow_if: nil,
31
+ message: 'Host not permitted'
32
+
33
+ def initialize(*)
34
+ super
35
+ @permitted_hosts = []
36
+ @domain_hosts = []
37
+ @ip_hosts = []
38
+ @all_permitted_hosts = Array(options[:permitted_hosts])
39
+
40
+ @all_permitted_hosts.each do |host|
41
+ case host
42
+ when String
43
+ if host.start_with?(DOT)
44
+ domain = host[1..-1]
45
+ @permitted_hosts << domain.downcase
46
+ @domain_hosts << /\A#{SUBDOMAINS}#{Regexp.escape(domain)}\z/i
47
+ else
48
+ @permitted_hosts << host.downcase
49
+ end
50
+ when IPAddr then @ip_hosts << host
51
+ end
52
+ end
53
+ end
54
+
55
+ def accepts?(env)
56
+ return true if options[:allow_if]&.call(env)
57
+ return true if @all_permitted_hosts.empty?
58
+
59
+ request = Request.new(env)
60
+ origin_host = extract_host(request.host_authority)
61
+ forwarded_host = extract_host(request.forwarded_authority)
62
+
63
+ debug env, "#{self.class} " \
64
+ "@all_permitted_hosts=#{@all_permitted_hosts.inspect} " \
65
+ "@permitted_hosts=#{@permitted_hosts.inspect} " \
66
+ "@domain_hosts=#{@domain_hosts.inspect} " \
67
+ "@ip_hosts=#{@ip_hosts.inspect} " \
68
+ "origin_host=#{origin_host.inspect} " \
69
+ "forwarded_host=#{forwarded_host.inspect}"
70
+
71
+ if host_permitted?(origin_host)
72
+ if forwarded_host.nil?
73
+ true
74
+ else
75
+ host_permitted?(forwarded_host)
76
+ end
77
+ else
78
+ false
79
+ end
80
+ end
81
+
82
+ private
83
+
84
+ def extract_host(authority)
85
+ authority.to_s.split(PORT_REGEXP).first&.downcase
86
+ end
87
+
88
+ def host_permitted?(host)
89
+ exact_match?(host) || domain_match?(host) || ip_match?(host)
90
+ end
91
+
92
+ def exact_match?(host)
93
+ @permitted_hosts.include?(host)
94
+ end
95
+
96
+ def domain_match?(host)
97
+ return false if host.nil?
98
+ return false if host.start_with?(DOT)
99
+
100
+ @domain_hosts.any? { |domain_host| host.match?(domain_host) }
101
+ end
102
+
103
+ def ip_match?(host)
104
+ @ip_hosts.any? { |ip_host| ip_host.include?(host) }
105
+ rescue IPAddr::InvalidAddressError
106
+ false
107
+ end
108
+ end
109
+ end
110
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/protection'
2
4
 
3
5
  module Rack
@@ -19,7 +21,7 @@ module Rack
19
21
  class HttpOrigin < Base
20
22
  DEFAULT_PORTS = { 'http' => 80, 'https' => 443, 'coffee' => 80 }
21
23
  default_reaction :deny
22
- default_options :allow_if => nil
24
+ default_options allow_if: nil
23
25
 
24
26
  def base_url(env)
25
27
  request = Rack::Request.new(env)
@@ -29,19 +31,13 @@ module Rack
29
31
 
30
32
  def accepts?(env)
31
33
  return true if safe? env
32
- return true unless origin = env['HTTP_ORIGIN']
34
+ return true unless (origin = env['HTTP_ORIGIN'])
33
35
  return true if base_url(env) == origin
34
- return true if options[:allow_if] && options[:allow_if].call(env)
35
-
36
- if options.key? :origin_whitelist
37
- warn "Rack::Protection origin_whitelist option is deprecated and will be removed, " \
38
- "use permitted_origins instead.\n"
39
- end
36
+ return true if options[:allow_if]&.call(env)
40
37
 
41
- permitted_origins = options[:permitted_origins] || options[:origin_whitelist]
38
+ permitted_origins = options[:permitted_origins]
42
39
  Array(permitted_origins).include? origin
43
40
  end
44
-
45
41
  end
46
42
  end
47
43
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/protection'
2
4
 
3
5
  module Rack
@@ -13,9 +15,11 @@ module Rack
13
15
 
14
16
  def accepts?(env)
15
17
  return true unless env.include? 'HTTP_X_FORWARDED_FOR'
16
- ips = env['HTTP_X_FORWARDED_FOR'].split(/\s*,\s*/)
17
- return false if env.include? 'HTTP_CLIENT_IP' and not ips.include? env['HTTP_CLIENT_IP']
18
- return false if env.include? 'HTTP_X_REAL_IP' and not ips.include? env['HTTP_X_REAL_IP']
18
+
19
+ ips = env['HTTP_X_FORWARDED_FOR'].split(',').map(&:strip)
20
+ return false if env.include?('HTTP_CLIENT_IP') && (!ips.include? env['HTTP_CLIENT_IP'])
21
+ return false if env.include?('HTTP_X_REAL_IP') && (!ips.include? env['HTTP_X_REAL_IP'])
22
+
19
23
  true
20
24
  end
21
25
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/protection'
2
4
 
3
5
  module Rack
@@ -17,7 +19,7 @@ module Rack
17
19
  #
18
20
  # The `:allow_if` option can be set to a proc to use custom allow/deny logic.
19
21
  class JsonCsrf < Base
20
- default_options :allow_if => nil
22
+ default_options allow_if: nil
21
23
 
22
24
  alias react deny
23
25
 
@@ -36,8 +38,9 @@ module Rack
36
38
 
37
39
  def has_vector?(request, headers)
38
40
  return false if request.xhr?
39
- return false if options[:allow_if] && options[:allow_if].call(request.env)
40
- return false unless headers['Content-Type'].to_s.split(';', 2).first =~ /^\s*application\/json\s*$/
41
+ return false if options[:allow_if]&.call(request.env)
42
+ return false unless headers['content-type'].to_s.split(';', 2).first =~ %r{^\s*application/json\s*$}
43
+
41
44
  origin(request.env).nil? and referrer(request.env) != request.host
42
45
  end
43
46
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/protection'
2
4
 
3
5
  module Rack
@@ -11,11 +13,11 @@ module Rack
11
13
  # Thus <tt>GET /foo/%2e%2e%2fbar</tt> becomes <tt>GET /bar</tt>.
12
14
  class PathTraversal < Base
13
15
  def call(env)
14
- path_was = env["PATH_INFO"]
15
- env["PATH_INFO"] = cleanup path_was if path_was && !path_was.empty?
16
+ path_was = env['PATH_INFO']
17
+ env['PATH_INFO'] = cleanup path_was if path_was && !path_was.empty?
16
18
  app.call env
17
19
  ensure
18
- env["PATH_INFO"] = path_was
20
+ env['PATH_INFO'] = path_was
19
21
  end
20
22
 
21
23
  def cleanup(path)
@@ -29,12 +31,13 @@ module Rack
29
31
  unescaped = unescaped.gsub(backslash, slash)
30
32
 
31
33
  unescaped.split(slash).each do |part|
32
- next if part.empty? or part == dot
34
+ next if part.empty? || (part == dot)
35
+
33
36
  part == '..' ? parts.pop : parts << part
34
37
  end
35
38
 
36
39
  cleaned = slash + parts.join(slash)
37
- cleaned << slash if parts.any? and unescaped =~ %r{/\.{0,2}$}
40
+ cleaned << slash if parts.any? && unescaped =~ (%r{/\.{0,2}$})
38
41
  cleaned
39
42
  end
40
43
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/protection'
2
4
 
3
5
  module Rack
@@ -13,11 +15,11 @@ module Rack
13
15
  # Options:
14
16
  # referrer_policy:: The policy to use (default: 'strict-origin-when-cross-origin')
15
17
  class ReferrerPolicy < Base
16
- default_options :referrer_policy => 'strict-origin-when-cross-origin'
18
+ default_options referrer_policy: 'strict-origin-when-cross-origin'
17
19
 
18
20
  def call(env)
19
21
  status, headers, body = @app.call(env)
20
- headers['Referrer-Policy'] ||= options[:referrer_policy]
22
+ headers['referrer-policy'] ||= options[:referrer_policy]
21
23
  [status, headers, body]
22
24
  end
23
25
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/protection'
2
4
 
3
5
  module Rack
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/protection'
2
4
 
3
5
  module Rack
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/protection'
2
4
 
3
5
  module Rack
@@ -13,23 +15,22 @@ module Rack
13
15
  # spoofed, too, this will not prevent determined hijacking attempts.
14
16
  class SessionHijacking < Base
15
17
  default_reaction :drop_session
16
- default_options :tracking_key => :tracking, :encrypt_tracking => true,
17
- :track => %w[HTTP_USER_AGENT]
18
+ default_options tracking_key: :tracking,
19
+ track: %w[HTTP_USER_AGENT]
18
20
 
19
21
  def accepts?(env)
20
22
  session = session env
21
23
  key = options[:tracking_key]
22
24
  if session.include? key
23
- session[key].all? { |k,v| v == encrypt(env[k]) }
25
+ session[key].all? { |k, v| v == encode(env[k]) }
24
26
  else
25
27
  session[key] = {}
26
- options[:track].each { |k| session[key][k] = encrypt(env[k]) }
28
+ options[:track].each { |k| session[key][k] = encode(env[k]) }
27
29
  end
28
30
  end
29
31
 
30
- def encrypt(value)
31
- value = value.to_s.downcase
32
- options[:encrypt_tracking] ? super(value) : value
32
+ def encode(value)
33
+ value.to_s.downcase
33
34
  end
34
35
  end
35
36
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/protection'
2
4
 
3
5
  module Rack
@@ -18,11 +20,11 @@ module Rack
18
20
  # preload:: Allow this domain to be included in browsers HSTS preload list. See https://hstspreload.appspot.com/
19
21
 
20
22
  class StrictTransport < Base
21
- default_options :max_age => 31_536_000, :include_subdomains => false, :preload => false
23
+ default_options max_age: 31_536_000, include_subdomains: false, preload: false
22
24
 
23
25
  def strict_transport
24
26
  @strict_transport ||= begin
25
- strict_transport = 'max-age=' + options[:max_age].to_s
27
+ strict_transport = "max-age=#{options[:max_age]}"
26
28
  strict_transport += '; includeSubDomains' if options[:include_subdomains]
27
29
  strict_transport += '; preload' if options[:preload]
28
30
  strict_transport.to_str
@@ -31,7 +33,7 @@ module Rack
31
33
 
32
34
  def call(env)
33
35
  status, headers, body = @app.call(env)
34
- headers['Strict-Transport-Security'] ||= strict_transport
36
+ headers['strict-transport-security'] ||= strict_transport
35
37
  [status, headers, body]
36
38
  end
37
39
  end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  module Protection
3
- VERSION = '2.1.0'
5
+ VERSION = '4.1.1'
4
6
  end
5
7
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/protection'
2
4
 
3
5
  module Rack
@@ -12,12 +14,12 @@ module Rack
12
14
  # Options:
13
15
  # xss_mode:: How the browser should prevent the attack (default: :block)
14
16
  class XSSHeader < Base
15
- default_options :xss_mode => :block, :nosniff => true
17
+ default_options xss_mode: :block, nosniff: true
16
18
 
17
19
  def call(env)
18
20
  status, headers, body = @app.call(env)
19
- headers['X-XSS-Protection'] ||= "1; mode=#{options[:xss_mode]}" if html? headers
20
- headers['X-Content-Type-Options'] ||= 'nosniff' if options[:nosniff]
21
+ headers['x-xss-protection'] ||= "1; mode=#{options[:xss_mode]}" if html? headers
22
+ headers['x-content-type-options'] ||= 'nosniff' if options[:nosniff]
21
23
  [status, headers, body]
22
24
  end
23
25
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/protection/version'
2
4
  require 'rack'
3
5
 
@@ -10,6 +12,7 @@ module Rack
10
12
  autoload :EscapedParams, 'rack/protection/escaped_params'
11
13
  autoload :FormToken, 'rack/protection/form_token'
12
14
  autoload :FrameOptions, 'rack/protection/frame_options'
15
+ autoload :HostAuthorization, 'rack/protection/host_authorization'
13
16
  autoload :HttpOrigin, 'rack/protection/http_origin'
14
17
  autoload :IPSpoofing, 'rack/protection/ip_spoofing'
15
18
  autoload :JsonCsrf, 'rack/protection/json_csrf'
@@ -22,12 +25,11 @@ module Rack
22
25
  autoload :XSSHeader, 'rack/protection/xss_header'
23
26
 
24
27
  def self.new(app, options = {})
25
- # does not include: RemoteReferrer, AuthenticityToken and FormToken
26
28
  except = Array options[:except]
27
29
  use_these = Array options[:use]
28
30
 
29
31
  if options.fetch(:without_session, false)
30
- except += [:session_hijacking, :remote_token]
32
+ except += %i[remote_token]
31
33
  end
32
34
 
33
35
  Rack::Builder.new do
@@ -39,6 +41,7 @@ module Rack
39
41
  use ::Rack::Protection::FormToken, options if use_these.include? :form_token
40
42
  use ::Rack::Protection::ReferrerPolicy, options if use_these.include? :referrer_policy
41
43
  use ::Rack::Protection::RemoteReferrer, options if use_these.include? :remote_referrer
44
+ use ::Rack::Protection::SessionHijacking, options if use_these.include? :session_hijacking
42
45
  use ::Rack::Protection::StrictTransport, options if use_these.include? :strict_transport
43
46
 
44
47
  # On by default, unless skipped
@@ -48,7 +51,6 @@ module Rack
48
51
  use ::Rack::Protection::JsonCsrf, options unless except.include? :json_csrf
49
52
  use ::Rack::Protection::PathTraversal, options unless except.include? :path_traversal
50
53
  use ::Rack::Protection::RemoteToken, options unless except.include? :remote_token
51
- use ::Rack::Protection::SessionHijacking, options unless except.include? :session_hijacking
52
54
  use ::Rack::Protection::XSSHeader, options unless except.include? :xss_header
53
55
  run app
54
56
  end.to_app
@@ -1 +1 @@
1
- require "rack/protection"
1
+ require 'rack/protection'
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rack/protection'
@@ -1,40 +1,45 @@
1
- version = File.read(File.expand_path("../../VERSION", __FILE__)).strip
1
+ # frozen_string_literal: true
2
+
3
+ version = File.read(File.expand_path('../VERSION', __dir__)).strip
2
4
 
3
5
  Gem::Specification.new do |s|
4
6
  # general infos
5
- s.name = "rack-protection"
7
+ s.name = 'rack-protection'
6
8
  s.version = version
7
- s.description = "Protect against typical web attacks, works with all Rack apps, including Rails."
8
- s.homepage = "http://sinatrarb.com/protection/"
9
- s.summary = s.description
9
+ s.description = 'Protect against typical web attacks, works with all Rack apps, including Rails'
10
+ s.homepage = 'https://sinatrarb.com/protection/'
11
+ s.summary = "#{s.description}."
10
12
  s.license = 'MIT'
11
- s.authors = ["https://github.com/sinatra/sinatra/graphs/contributors"]
12
- s.email = "sinatrarb@googlegroups.com"
13
- s.files = Dir["lib/**/*.rb"] + [
14
- "License",
15
- "README.md",
16
- "Rakefile",
17
- "Gemfile",
18
- "rack-protection.gemspec"
13
+ s.authors = ['https://github.com/sinatra/sinatra/graphs/contributors']
14
+ s.email = 'sinatrarb@googlegroups.com'
15
+ s.files = Dir['lib/**/*.rb'] + [
16
+ 'License',
17
+ 'README.md',
18
+ 'Rakefile',
19
+ 'Gemfile',
20
+ 'rack-protection.gemspec'
19
21
  ]
20
22
 
21
- if s.respond_to?(:metadata)
22
- s.metadata = {
23
- 'source_code_uri' => 'https://github.com/sinatra/sinatra/tree/master/rack-protection',
24
- 'homepage_uri' => 'http://sinatrarb.com/protection/',
25
- 'documentation_uri' => 'https://www.rubydoc.info/gems/rack-protection'
26
- }
27
- else
28
- raise <<-EOF
23
+ unless s.respond_to?(:metadata)
24
+ raise <<-WARN
29
25
  RubyGems 2.0 or newer is required to protect against public gem pushes. You can update your rubygems version by running:
30
26
  gem install rubygems-update
31
27
  update_rubygems:
32
28
  gem update --system
33
- EOF
29
+ WARN
34
30
  end
35
31
 
32
+ s.metadata = {
33
+ 'source_code_uri' => 'https://github.com/sinatra/sinatra/tree/main/rack-protection',
34
+ 'homepage_uri' => 'http://sinatrarb.com/protection/',
35
+ 'documentation_uri' => 'https://www.rubydoc.info/gems/rack-protection',
36
+ 'rubygems_mfa_required' => 'true'
37
+ }
38
+
39
+ s.required_ruby_version = '>= 2.7.8'
40
+
36
41
  # dependencies
37
- s.add_dependency "rack"
38
- s.add_development_dependency "rack-test"
39
- s.add_development_dependency "rspec", "~> 3.6"
42
+ s.add_dependency 'base64', '>= 0.1.0'
43
+ s.add_dependency 'logger', '>= 1.6.0'
44
+ s.add_dependency 'rack', '>= 3.0.0', '< 4'
40
45
  end
metadata CHANGED
@@ -1,59 +1,65 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-protection
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 4.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - https://github.com/sinatra/sinatra/graphs/contributors
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-04 00:00:00.000000000 Z
11
+ date: 2024-11-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: rack
14
+ name: base64
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 0.1.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: 0.1.0
27
27
  - !ruby/object:Gem::Dependency
28
- name: rack-test
28
+ name: logger
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
33
+ version: 1.6.0
34
+ type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: 1.6.0
41
41
  - !ruby/object:Gem::Dependency
42
- name: rspec
42
+ name: rack
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 3.0.0
48
+ - - "<"
46
49
  - !ruby/object:Gem::Version
47
- version: '3.6'
48
- type: :development
50
+ version: '4'
51
+ type: :runtime
49
52
  prerelease: false
50
53
  version_requirements: !ruby/object:Gem::Requirement
51
54
  requirements:
52
- - - "~>"
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: 3.0.0
58
+ - - "<"
53
59
  - !ruby/object:Gem::Version
54
- version: '3.6'
60
+ version: '4'
55
61
  description: Protect against typical web attacks, works with all Rack apps, including
56
- Rails.
62
+ Rails
57
63
  email: sinatrarb@googlegroups.com
58
64
  executables: []
59
65
  extensions: []
@@ -72,6 +78,7 @@ files:
72
78
  - lib/rack/protection/escaped_params.rb
73
79
  - lib/rack/protection/form_token.rb
74
80
  - lib/rack/protection/frame_options.rb
81
+ - lib/rack/protection/host_authorization.rb
75
82
  - lib/rack/protection/http_origin.rb
76
83
  - lib/rack/protection/ip_spoofing.rb
77
84
  - lib/rack/protection/json_csrf.rb
@@ -83,14 +90,16 @@ files:
83
90
  - lib/rack/protection/strict_transport.rb
84
91
  - lib/rack/protection/version.rb
85
92
  - lib/rack/protection/xss_header.rb
93
+ - lib/rack_protection.rb
86
94
  - rack-protection.gemspec
87
- homepage: http://sinatrarb.com/protection/
95
+ homepage: https://sinatrarb.com/protection/
88
96
  licenses:
89
97
  - MIT
90
98
  metadata:
91
- source_code_uri: https://github.com/sinatra/sinatra/tree/master/rack-protection
99
+ source_code_uri: https://github.com/sinatra/sinatra/tree/main/rack-protection
92
100
  homepage_uri: http://sinatrarb.com/protection/
93
101
  documentation_uri: https://www.rubydoc.info/gems/rack-protection
102
+ rubygems_mfa_required: 'true'
94
103
  post_install_message:
95
104
  rdoc_options: []
96
105
  require_paths:
@@ -99,14 +108,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
99
108
  requirements:
100
109
  - - ">="
101
110
  - !ruby/object:Gem::Version
102
- version: '0'
111
+ version: 2.7.8
103
112
  required_rubygems_version: !ruby/object:Gem::Requirement
104
113
  requirements:
105
114
  - - ">="
106
115
  - !ruby/object:Gem::Version
107
116
  version: '0'
108
117
  requirements: []
109
- rubygems_version: 3.1.2
118
+ rubygems_version: 3.5.22
110
119
  signing_key:
111
120
  specification_version: 4
112
121
  summary: Protect against typical web attacks, works with all Rack apps, including