verikloak-rails 0.3.2 → 0.4.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f473cac2688d575650e3c483eac520fcb910ca7a9fd3c08620337386a705ca27
4
- data.tar.gz: d76e49d03d27b336e6c8727f0cf3b82bb783bbe5153051be4ad4d0c1af5cfe7f
3
+ metadata.gz: f2cf09e4cda410ff55efd34d8e2fb24fa78241de47f3c5cd8942deaca6e83db4
4
+ data.tar.gz: 8f8ee287a51d3a39e7807b70b4d11c42090de231b898e9eea07b221b518b8f63
5
5
  SHA512:
6
- metadata.gz: 6326a2ca5a86a50898ebe00094139733e3295203ec050e9c471093c1d3b21f2d161251d6ecf97ce265267718e83b570f1b9d8d82b9288855f732b557d22ac5a4
7
- data.tar.gz: 874c03abffe2470e4993b64e0afdcbe42f1b1f27e8b4bbd17aa457445deffce591907a46a62f0be24801786f3e339bfd43fd89deb3d2239defcd3ad37c8410ca
6
+ metadata.gz: dbb93e90c1408dce20360f186a6a59bb4f47a62db569656279c311c01d1623e6f894709d5ccd73b0df32e3a4e3351bb90a598074ae6ec3a8b9d31c2187a1b937
7
+ data.tar.gz: b51e2a247c8ca1a145b768e5d59762675669d5ab1b6556ac738197a611fd0c8e925d193d7308c8522f4b072c8645aa93f947a61993b1045f57d803a8903e6c78
data/CHANGELOG.md CHANGED
@@ -7,6 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ---
9
9
 
10
+ ## [0.4.0] - 2026-02-15
11
+
12
+ ### Security
13
+ - **Header sanitization**: `ErrorRenderer` now delegates to `Verikloak::ErrorResponse.sanitize_header_value` — strips all control characters (`[[:cntrl:]]`), not just CR/LF, consistent with core gem
14
+ - **Request-ID sanitization**: Log tag builder strengthened from `/[\r\n]+/` to `/[[:cntrl:]]+/` to block all control characters in tagged logging
15
+ - **BFF configurator hardening**: `public_send` in `BffConfigurator` is now guarded with a whitelist — rejects keys starting with `_` or containing `!` to prevent accidental invocation of non-accessor methods
16
+
17
+ ### Fixed
18
+ - **`with_required_audience!`**: Was passing two positional arguments to `Error.new` (`'forbidden', 'message'`), which raises `ArgumentError` with core gem 0.4.0's keyword-arg signature. Now uses `Error.new('Required audience not satisfied', code: 'forbidden')`
19
+ - **`authenticate_user!`**: Was passing `Error.new('unauthorized')` which set `code` to `nil` (message became `'unauthorized'` but code was unset). Now uses `Error.new('Unauthorized', code: 'unauthorized')` for correct error rendering
20
+ - Test stubs updated to match core gem's keyword-arg `Error.new(message, code:, http_status:)` signature
21
+
22
+ ### Added
23
+ - `allow_http` configuration option — forwarded to core `Verikloak::Middleware` for development/test environments where HTTPS is unavailable
24
+ - Logger cycle detection using `Set` guard in `_verikloak_base_logger` to prevent infinite loops with circular logger chains
25
+
26
+ ### Changed
27
+ - **BREAKING**: Minimum `verikloak` dependency raised to `>= 0.4.0` (keyword-arg `Error.new` signature)
28
+ - Dev dependency `rspec` pinned to `~> 3.13`, `rubocop-rspec` pinned to `~> 3.9`
29
+ - Extracted `auto_disable_rescue_pundit` method from `apply_configuration` in Railtie for clarity and testability
30
+ - Removed `sanitize_quoted` private method from `ErrorRenderer` in favor of shared `Verikloak::ErrorResponse.sanitize_header_value`
31
+
32
+ ---
33
+
10
34
  ## [0.3.2] - 2026-01-01
11
35
 
12
36
  ### Changed
data/README.md CHANGED
@@ -21,7 +21,7 @@ Provide drop-in, token-based authentication for Rails APIs via Verikloak (OIDC d
21
21
  ## Compatibility
22
22
  - Ruby: >= 3.1
23
23
  - Rails: 6.1 – 8.x
24
- - verikloak: >= 0.3.0, < 1.0.0
24
+ - verikloak: >= 0.4.0, < 1.0.0
25
25
 
26
26
  ## Quick Start
27
27
  ```bash
@@ -168,6 +168,7 @@ Keys under `config.verikloak`:
168
168
  | `token_env_key` | String | Custom Rack env key that stores the Bearer token | `nil` (middleware default `verikloak.token`) |
169
169
  | `user_env_key` | String | Custom Rack env key that stores decoded claims | `nil` (middleware default `verikloak.user`) |
170
170
  | `bff_header_guard_options` | Hash or Proc | Forwarded to `Verikloak::BFF.configure` prior to middleware insertion | `{}` |
171
+ | `allow_http` | Boolean | Allow `http://` discovery URLs (forwarded to core middleware). **Only for development/test.** | `false` |
171
172
 
172
173
  Environment variable examples are in the generated initializer.
173
174
 
@@ -266,14 +267,15 @@ Rails.application.configure do
266
267
  end
267
268
  ```
268
269
 
269
- Note: Always sanitize values placed into `WWW-Authenticate` header parameters to avoid header injection. For example:
270
+ Note: Always sanitize values placed into `WWW-Authenticate` header parameters to avoid header injection. You can use the shared helper from the core gem:
270
271
 
271
272
  ```ruby
272
273
  class CompactErrorRenderer
273
274
  private
274
- def sanitize_quoted(val)
275
- # Escape quotes/backslashes and strip CR/LF (collapse runs to a single space)
276
- val.to_s.gsub(/(["\\])/) { |m| "\\#{m}" }.gsub(/[\r\n]+/, ' ')
275
+ def sanitize(val)
276
+ # Delegates to core gem's sanitizer escapes quotes/backslashes,
277
+ # truncates at CRLF, and strips all control characters.
278
+ Verikloak::ErrorResponse.sanitize_header_value(val)
277
279
  end
278
280
  end
279
281
  ```
@@ -26,7 +26,7 @@ module Verikloak
26
26
  ✅ verikloak: initializer created.
27
27
 
28
28
  Next steps:
29
- 1) Ensure the base gem is installed: gem 'verikloak', '>= 0.3.0', '< 1.0.0'
29
+ 1) Ensure the base gem is installed: gem 'verikloak', '>= 0.4.0', '< 1.0.0'
30
30
  2) Set discovery_url / audience in config/initializers/verikloak.rb
31
31
  3) (Optional) If you disable auto-include, add this line to ApplicationController:
32
32
  include Verikloak::Rails::Controller
@@ -103,7 +103,12 @@ module Verikloak
103
103
  target.configure do |config|
104
104
  entries.each do |key, value|
105
105
  writer = "#{key}="
106
- config.public_send(writer, value) if config.respond_to?(writer)
106
+ # Guard: only call known attr_accessor writers to prevent
107
+ # accidental invocation of arbitrary public methods.
108
+ next unless config.respond_to?(writer)
109
+ next if key.to_s.start_with?('_') || key.to_s.include?('!')
110
+
111
+ config.public_send(writer, value)
107
112
  end
108
113
  end
109
114
  end
@@ -61,7 +61,8 @@ module Verikloak
61
61
  :auto_insert_bff_header_guard,
62
62
  :bff_header_guard_insert_before, :bff_header_guard_insert_after,
63
63
  :token_verify_options, :decoder_cache_limit,
64
- :token_env_key, :user_env_key, :bff_header_guard_options
64
+ :token_env_key, :user_env_key, :bff_header_guard_options,
65
+ :allow_http
65
66
 
66
67
  # Initialize configuration with sensible defaults for Rails apps.
67
68
  # @return [void]
@@ -86,6 +87,7 @@ module Verikloak
86
87
  @token_env_key = nil
87
88
  @user_env_key = nil
88
89
  @bff_header_guard_options = {}
90
+ @allow_http = false
89
91
  end
90
92
 
91
93
  # Options forwarded to the base Verikloak Rack middleware.
@@ -103,7 +105,8 @@ module Verikloak
103
105
  token_verify_options: token_verify_options,
104
106
  decoder_cache_limit: decoder_cache_limit,
105
107
  token_env_key: token_env_key,
106
- user_env_key: user_env_key
108
+ user_env_key: user_env_key,
109
+ allow_http: allow_http
107
110
  }.compact
108
111
  end
109
112
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'active_support/concern'
4
+ require 'set'
4
5
 
5
6
  module Verikloak
6
7
  module Rails
@@ -43,11 +44,7 @@ module Verikloak
43
44
  def authenticate_user!
44
45
  return if authenticated?
45
46
 
46
- e = begin
47
- ::Verikloak::Error.new('unauthorized')
48
- rescue StandardError
49
- StandardError.new('Unauthorized')
50
- end
47
+ e = ::Verikloak::Error.new('Unauthorized', code: 'unauthorized')
51
48
  Verikloak::Rails.config.error_renderer.render(self, e)
52
49
  end
53
50
 
@@ -84,7 +81,7 @@ module Verikloak
84
81
  aud = Array(current_user_claims&.dig('aud'))
85
82
  return if required.flatten.all? { |r| aud.include?(r) }
86
83
 
87
- raise ::Verikloak::Error.new('forbidden', 'Required audience not satisfied')
84
+ raise ::Verikloak::Error.new('Required audience not satisfied', code: 'forbidden')
88
85
  end
89
86
 
90
87
  private
@@ -108,7 +105,7 @@ module Verikloak
108
105
  tags = []
109
106
  if config.logger_tags.include?(:request_id)
110
107
  rid = request.request_id || request.headers['X-Request-Id']
111
- rid = rid.to_s.gsub(/[\r\n]+/, ' ')
108
+ rid = rid.to_s.gsub(/[[:cntrl:]]+/, ' ').strip
112
109
  tags << "req:#{rid}" unless rid.empty?
113
110
  end
114
111
  if config.logger_tags.include?(:sub)
@@ -164,7 +161,10 @@ module Verikloak
164
161
  logger
165
162
  end
166
163
  current = root_logger
164
+ seen = Set.new
167
165
  while current.respond_to?(:logger)
166
+ break unless seen.add?(current.object_id)
167
+
168
168
  next_logger = current.logger
169
169
  break if next_logger.nil? || next_logger.equal?(current)
170
170
 
@@ -1,11 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'verikloak/error_response'
4
+
3
5
  module Verikloak
4
6
  module Rails
5
7
  # Renders JSON errors for authentication/authorization failures.
6
8
  #
7
9
  # When status is 401, adds a `WWW-Authenticate: Bearer` header including
8
10
  # `error` and `error_description` fields when available.
11
+ #
12
+ # Header sanitization is delegated to {Verikloak::ErrorResponse} to ensure
13
+ # consistent control-character stripping across all Verikloak gems.
9
14
  class ErrorRenderer
10
15
  DEFAULT_STATUS_MAP = {
11
16
  'invalid_token' => 401,
@@ -67,6 +72,8 @@ module Verikloak
67
72
  end
68
73
 
69
74
  # Build WWW-Authenticate headers when returning 401 responses.
75
+ # Delegates sanitization to {Verikloak::ErrorResponse.sanitize_header_value}
76
+ # to ensure control-character stripping is consistent with the core gem.
70
77
  # @param status [Integer]
71
78
  # @param code [String, nil]
72
79
  # @param message [String]
@@ -74,23 +81,12 @@ module Verikloak
74
81
  def auth_headers(status, code, message)
75
82
  return {} unless status == 401
76
83
 
84
+ sanitize = ->(v) { Verikloak::ErrorResponse.sanitize_header_value(v) }
77
85
  header = +'Bearer'
78
- header << %( error="#{sanitize_quoted(code)}") if code
79
- header << %( error_description="#{sanitize_quoted(message)}") if message
86
+ header << %( error="#{sanitize.call(code)}") if code
87
+ header << %( error_description="#{sanitize.call(message)}") if message
80
88
  { 'WWW-Authenticate' => header }
81
89
  end
82
-
83
- # Sanitize a value for inclusion inside a quoted HTTP header parameter.
84
- # Escapes quotes and backslashes, and strips CR/LF to prevent header injection.
85
- # Why block replacement? String replacements like '\\1' are parsed as
86
- # backreferences/escapes in Ruby, making precise escaping error‑prone.
87
- # The block receives the literal match and we return it prefixed with a
88
- # backslash, guaranteeing predictable escaping for both " and \\.
89
- # @param val [String]
90
- # @return [String]
91
- def sanitize_quoted(val)
92
- val.to_s.gsub(/(["\\])/) { |m| "\\#{m}" }.gsub(/[\r\n]+/, ' ')
93
- end
94
90
  end
95
91
  end
96
92
  end
@@ -20,7 +20,7 @@ module Verikloak
20
20
  middleware_insert_after auto_insert_bff_header_guard
21
21
  bff_header_guard_insert_before bff_header_guard_insert_after
22
22
  token_verify_options decoder_cache_limit token_env_key user_env_key
23
- bff_header_guard_options
23
+ bff_header_guard_options allow_http
24
24
  ].freeze
25
25
 
26
26
  config.verikloak = ActiveSupport::OrderedOptions.new
@@ -100,10 +100,24 @@ module Verikloak
100
100
  CONFIG_KEYS.each do |key|
101
101
  c.send("#{key}=", rails_cfg[key]) if rails_cfg.key?(key)
102
102
  end
103
- c.rescue_pundit = false if !rails_cfg.key?(:rescue_pundit) && defined?(::Verikloak::Pundit)
103
+ auto_disable_rescue_pundit(c, rails_cfg)
104
104
  end
105
105
  end
106
106
 
107
+ # When verikloak-pundit is loaded and the user has not explicitly set
108
+ # rescue_pundit, disable the built-in Pundit rescue to avoid
109
+ # double-handling errors.
110
+ #
111
+ # @param config [Verikloak::Rails::Configuration]
112
+ # @param rails_cfg [ActiveSupport::OrderedOptions]
113
+ # @return [void]
114
+ def auto_disable_rescue_pundit(config, rails_cfg)
115
+ return if rails_cfg.key?(:rescue_pundit)
116
+ return unless defined?(::Verikloak::Pundit)
117
+
118
+ config.rescue_pundit = false
119
+ end
120
+
107
121
  # Insert the base Verikloak::Middleware into the application middleware stack.
108
122
  # Respects the configured insertion point (before or after specified middleware).
109
123
  #
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Verikloak
4
4
  module Rails
5
- VERSION = '0.3.2'
5
+ VERSION = '0.4.0'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: verikloak-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - taiyaky
@@ -55,7 +55,7 @@ dependencies:
55
55
  requirements:
56
56
  - - ">="
57
57
  - !ruby/object:Gem::Version
58
- version: 0.3.0
58
+ version: 0.4.0
59
59
  - - "<"
60
60
  - !ruby/object:Gem::Version
61
61
  version: 1.0.0
@@ -65,7 +65,7 @@ dependencies:
65
65
  requirements:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: 0.3.0
68
+ version: 0.4.0
69
69
  - - "<"
70
70
  - !ruby/object:Gem::Version
71
71
  version: 1.0.0
@@ -96,7 +96,7 @@ metadata:
96
96
  source_code_uri: https://github.com/taiyaky/verikloak-rails
97
97
  changelog_uri: https://github.com/taiyaky/verikloak-rails/blob/main/CHANGELOG.md
98
98
  bug_tracker_uri: https://github.com/taiyaky/verikloak-rails/issues
99
- documentation_uri: https://rubydoc.info/gems/verikloak-rails/0.3.2
99
+ documentation_uri: https://rubydoc.info/gems/verikloak-rails/0.4.0
100
100
  rubygems_mfa_required: 'true'
101
101
  rdoc_options: []
102
102
  require_paths: