Skip to content

Fix skip_env? to fall back to Rails.env when default_env is nil - #476

Merged
grosser merged 1 commit into
ambethia:masterfrom
duncantmiller:fix/skip-env-reliability-in-multithreaded-test-servers
Apr 9, 2026
Merged

grosser merged 1 commit into
ambethia:masterfrom
duncantmiller:fix/skip-env-reliability-in-multithreaded-test-servers

Conversation

@duncantmiller

Copy link
Copy Markdown
Contributor

Problem

In Rails apps using a multi-threaded server (e.g. Puma) for system tests with Capybara, verify_recaptcha intermittently skips the skip_verify_env bypass and makes real HTTP verification calls to Google's reCAPTCHA API. These calls fail (timeout or invalid token), causing forms to re-render as if reCAPTCHA verification failed — despite the app running in RAILS_ENV=test.

Root cause: Recaptcha::Configuration#default_env is set once at initialization:

@default_env = ENV['RAILS_ENV'] || ENV['RACK_ENV'] || (Rails.env if defined? Rails.env)

When the singleton initializes before RAILS_ENV is fully available in a multi-threaded context, default_env is nil. Then skip_env?(nil) evaluates:

configuration.skip_verify_env.include?(nil || nil)  # => false

...bypassing the test-mode skip entirely, even though Rails.env would correctly return "test" at call time.

This is also demonstrated by the gem's own test helper, which explicitly deletes RAILS_ENV and RACK_ENV (test/helper.rb lines 2–3), meaning default_env is nil throughout the test suite.

Fix

Fall back to Rails.env at call time if default_env is nil:

def self.skip_env?(env)
  resolved = env || configuration.default_env
  resolved ||= Rails.env.to_s if defined?(Rails.env)
  configuration.skip_verify_env.include?(resolved)
end

This ensures the skip check always reflects the current Rails environment, even if the configuration was initialized before RAILS_ENV was set.

Tests

Added four tests for Recaptcha.skip_env?:

  • Returns true for an explicit env matching skip_verify_env
  • Returns false for an explicit env not in skip_verify_env
  • Returns false when both env argument and default_env are nil (existing behavior, correctly documented)
  • Returns true when default_env is nil but Rails.env is in skip_verify_env (the bug this fixes)

All 160 tests pass. RuboCop reports no offenses.

When a Rails app uses a multi-threaded server (e.g. Puma) for system
tests, the Recaptcha::Configuration singleton can be initialised before
RAILS_ENV is visible, leaving default_env as nil. In that state,
skip_env?(nil) calls skip_verify_env.include?(nil) which always
returns false, bypassing the test-mode skip and causing real HTTP
verification calls that intermittently fail.

Fix: resolve the env to check as `env || default_env || Rails.env`
so that a live Rails.env is always used as a final fallback.

Adds four focused tests for Recaptcha.skip_env? covering the explicit
env argument, default_env, and the Rails.env fallback path.

@grosser grosser left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kinda hate it because env being setup correctly should not be the gems problem ... but seems like the easiest fix 🤦

@grosser
grosser merged commit feb2b6d into ambethia:master Apr 9, 2026
7 of 8 checks passed
@grosser

grosser commented Apr 9, 2026

Copy link
Copy Markdown
Collaborator

5.21.2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants