diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index 4fd2d00c..6aaa04a7 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -21,8 +21,8 @@ jobs: matrix: os: [ubuntu-22.04] # Read from .ruby-version instead of a second copy of the number here. CI used to be - # pinned to 3.0.3 while .ruby-version had been bumped to 3.4.9, so the suite stayed - # green on a Ruby the servers no longer ran and the breakage surfaced only on deploy. + # pinned to 3.0.3 while .ruby-version said 3.4.9, so the suite stayed green on a Ruby + # the servers no longer ran and the breakage surfaced only on deploy. ruby: ['.ruby-version'] runs-on: ${{ matrix.os }} continue-on-error: ${{ endsWith(matrix.ruby, 'head') || matrix.ruby == 'debug' }} @@ -53,6 +53,10 @@ jobs: cp config/application-example.yml config/application.yml cp config/database-travis.yml config/database.yml bundle exec rake db:setup + # Guards the autoloader: classic was removed in Rails 7.0, so a file whose + # constant does not match its path stops being a style question and becomes a + # boot failure. Cheaper to catch here than on deploy. + bundle exec rails zeitwerk:check bundle exec rails test test/* # ------------------------------ diff --git a/.gitignore b/.gitignore index 38904a12..49efcfc6 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,6 @@ .rubocop.yml .DS_Store Dockerfile.dev.v2 + +# repowise local index +.repowise/ diff --git a/.ruby-version b/.ruby-version index 75a22a26..7bcbb380 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.0.3 +3.4.9 diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ef75704..8160d0d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ + 27.08.2026 * Fixed stability issues on contact request https://github.com/internetee/rest-whois/pull/503 @@ -5,6 +6,9 @@ * Pinned Ruby back to 3.0.3 until Rails is upgraded https://github.com/internetee/rest-whois/pull/503 * Fixed contact request failures caused by registry replication delay https://github.com/internetee/rest-whois/pull/503 +21.08.2026 +* Rails 6.1 -> 8.1 and the zeitwerk autoloader on Ruby 3.4 https://github.com/internetee/rest-whois/pull/505 + 08.22.2025 * Replacing codeclimate https://github.com/internetee/rest-whois/pull/434 diff --git a/Gemfile b/Gemfile index 4a8f496f..02417932 100644 --- a/Gemfile +++ b/Gemfile @@ -4,21 +4,26 @@ gem 'aws-sdk-ses', '~> 1.40' gem 'bootsnap', '~> 1.18.0', require: false gem 'figaro', '~> 1.3.0' gem 'jbuilder' -gem 'mimemagic', '~> 0.4.3' gem 'passenger', '>= 5.3.2', require: 'phusion_passenger/rack_handler' -# Held on 1.5.x deliberately. 1.6.0 is the first release that publishes an x86_64-linux binary, -# and it is linked against glibc 2.29, which is newer than the deploy servers: the binary loads -# with "libm.so.6: version `GLIBC_2.29' not found". 1.5.x has no Linux binary at all, so it is -# always compiled from source there. The clean way back to 1.6.x is a newer distribution on the -# servers - or, as a stopgap, Bundler >= 2.3.18 on them plus force_ruby_platform on this line, -# which the Bundler currently installed there is too old to understand. -gem 'pg', '~> 1.5.9' -gem 'rails', '>= 6.0.3.1' + +# Both are built from source on purpose: the deploy servers run a glibc older than 2.29, and the +# precompiled x86_64-linux gems that pg ships since 1.6 and nokogiri since 1.18 are linked against +# 2.29, so they load with "libm.so.6: version `GLIBC_2.29' not found". force_ruby_platform makes +# Bundler ignore those binaries and compile from source there, which is how both were installed +# before they started publishing Linux binaries. Needs bundler >= 2.3.18. nokogiri is listed here +# only for that flag - it is pulled in by Rails, not used directly. Drop both once the servers get +# a newer distribution; anything below glibc 2.29 is an out-of-support release and these two will +# not be the last gems to ship binaries it cannot load. +gem 'nokogiri', force_ruby_platform: true +gem 'pg', '~> 1.6.3', force_ruby_platform: true +gem 'rails', '~> 8.1.3' gem 'recaptcha', '~> 5.21', require: 'recaptcha/rails' gem 'sassc', '~> 2.4' gem 'sassc-rails' gem 'simpleidn', '0.2.1' # For Punycode -gem 'uglifier' +# Ruby 3.4 demoted syslog from a default gem to a bundled one, so Bundler no longer puts it on the +# load path unless it is asked for. config/environments/production.rb logs through Syslog::Logger. +gem 'syslog' group :development do gem 'listen', '>= 3.0.5', '< 3.10.1' @@ -35,6 +40,9 @@ group :development, :test do end group :test do + # minitest 6 moved mocks and stubs out of the gem itself; test_helper requires + # minitest/mock and the mailer test stubs a method with it. + gem 'minitest-mock' gem 'selenium-webdriver' - gem 'simplecov', '0.17.1', require: false # CC last supported v0.17 + gem 'simplecov', require: false end diff --git a/Gemfile.lock b/Gemfile.lock index 1089865b..db578a3c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,194 +10,257 @@ GIT GEM remote: https://rubygems.org/ specs: - actioncable (6.1.4.1) - actionpack (= 6.1.4.1) - activesupport (= 6.1.4.1) + action_text-trix (2.1.19) + railties + actioncable (8.1.3.1) + actionpack (= 8.1.3.1) + activesupport (= 8.1.3.1) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (6.1.4.1) - actionpack (= 6.1.4.1) - activejob (= 6.1.4.1) - activerecord (= 6.1.4.1) - activestorage (= 6.1.4.1) - activesupport (= 6.1.4.1) - mail (>= 2.7.1) - actionmailer (6.1.4.1) - actionpack (= 6.1.4.1) - actionview (= 6.1.4.1) - activejob (= 6.1.4.1) - activesupport (= 6.1.4.1) - mail (~> 2.5, >= 2.5.4) - rails-dom-testing (~> 2.0) - actionpack (6.1.4.1) - actionview (= 6.1.4.1) - activesupport (= 6.1.4.1) - rack (~> 2.0, >= 2.0.9) + zeitwerk (~> 2.6) + actionmailbox (8.1.3.1) + actionpack (= 8.1.3.1) + activejob (= 8.1.3.1) + activerecord (= 8.1.3.1) + activestorage (= 8.1.3.1) + activesupport (= 8.1.3.1) + mail (>= 2.8.0) + actionmailer (8.1.3.1) + actionpack (= 8.1.3.1) + actionview (= 8.1.3.1) + activejob (= 8.1.3.1) + activesupport (= 8.1.3.1) + mail (>= 2.8.0) + rails-dom-testing (~> 2.2) + actionpack (8.1.3.1) + actionview (= 8.1.3.1) + activesupport (= 8.1.3.1) + nokogiri (>= 1.8.5) + rack (>= 2.2.4) + rack-session (>= 1.0.1) rack-test (>= 0.6.3) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (6.1.4.1) - actionpack (= 6.1.4.1) - activerecord (= 6.1.4.1) - activestorage (= 6.1.4.1) - activesupport (= 6.1.4.1) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + useragent (~> 0.16) + actiontext (8.1.3.1) + action_text-trix (~> 2.1.15) + actionpack (= 8.1.3.1) + activerecord (= 8.1.3.1) + activestorage (= 8.1.3.1) + activesupport (= 8.1.3.1) + globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (6.1.4.1) - activesupport (= 6.1.4.1) + actionview (8.1.3.1) + activesupport (= 8.1.3.1) builder (~> 3.1) - erubi (~> 1.4) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.1, >= 1.2.0) - activejob (6.1.4.1) - activesupport (= 6.1.4.1) + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + activejob (8.1.3.1) + activesupport (= 8.1.3.1) globalid (>= 0.3.6) - activemodel (6.1.4.1) - activesupport (= 6.1.4.1) - activerecord (6.1.4.1) - activemodel (= 6.1.4.1) - activesupport (= 6.1.4.1) - activestorage (6.1.4.1) - actionpack (= 6.1.4.1) - activejob (= 6.1.4.1) - activerecord (= 6.1.4.1) - activesupport (= 6.1.4.1) - marcel (~> 1.0.0) - mini_mime (>= 1.1.0) - activesupport (6.1.4.1) - concurrent-ruby (~> 1.0, >= 1.0.2) + activemodel (8.1.3.1) + activesupport (= 8.1.3.1) + activerecord (8.1.3.1) + activemodel (= 8.1.3.1) + activesupport (= 8.1.3.1) + timeout (>= 0.4.0) + activestorage (8.1.3.1) + actionpack (= 8.1.3.1) + activejob (= 8.1.3.1) + activerecord (= 8.1.3.1) + activesupport (= 8.1.3.1) + marcel (~> 1.0) + activesupport (8.1.3.1) + base64 + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb i18n (>= 1.6, < 2) + json + logger (>= 1.4.2) minitest (>= 5.1) - tzinfo (~> 2.0) - zeitwerk (~> 2.3) - addressable (2.8.0) - public_suffix (>= 2.0.2, < 5.0) - aws-eventstream (1.1.1) - aws-partitions (1.482.0) - aws-sdk-core (3.119.0) - aws-eventstream (~> 1, >= 1.0.2) - aws-partitions (~> 1, >= 1.239.0) - aws-sigv4 (~> 1.1) - jmespath (~> 1.0) - aws-sdk-ses (1.40.0) - aws-sdk-core (~> 3, >= 3.119.0) - aws-sigv4 (~> 1.1) - aws-sigv4 (1.2.4) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + uri (>= 0.13.1) + addressable (2.9.0) + public_suffix (>= 2.0.2, < 8.0) + aws-eventstream (1.4.0) + aws-partitions (1.1281.0) + aws-sdk-core (3.254.1) + aws-eventstream (~> 1, >= 1.3.0) + aws-partitions (~> 1, >= 1.992.0) + aws-sigv4 (~> 1.9) + base64 + bigdecimal + jmespath (~> 1, >= 1.6.1) + logger + aws-sdk-ses (1.102.0) + aws-sdk-core (~> 3, >= 3.254.0) + aws-sigv4 (~> 1.5) + aws-sigv4 (1.12.1) aws-eventstream (~> 1, >= 1.0.2) base64 (0.3.0) - bootsnap (1.18.3) + bigdecimal (4.1.2) + bootsnap (1.18.6) msgpack (~> 1.2) - builder (3.2.4) - capybara (3.35.3) + builder (3.3.0) + capybara (3.40.0) addressable + matrix mini_mime (>= 0.1.3) - nokogiri (~> 1.8) + nokogiri (~> 1.11) rack (>= 1.6.0) rack-test (>= 0.6.3) regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) - childprocess (3.0.0) coderay (1.1.3) - concurrent-ruby (1.1.9) - crack (0.4.5) + concurrent-ruby (1.3.8) + connection_pool (3.0.2) + crack (1.0.1) + bigdecimal rexml - crass (1.0.6) - docile (1.3.2) - erubi (1.10.0) - execjs (2.10.1) + crass (1.0.7) + date (3.5.1) + drb (2.2.3) + erb (6.0.7) + erubi (1.13.1) ffi (1.17.4) figaro (1.3.0) thor (>= 0.14.0, < 2) - globalid (0.5.2) - activesupport (>= 5.0) - hashdiff (1.0.1) - i18n (1.8.10) + globalid (1.4.0) + activesupport (>= 6.1) + hashdiff (1.2.1) + i18n (1.15.2) concurrent-ruby (~> 1.0) - jbuilder (2.11.2) - activesupport (>= 5.0.0) - jmespath (1.6.1) - json (2.19.9) + io-console (0.9.2) + irb (1.18.0) + pp (>= 0.6.0) + prism (>= 1.3.0) + rdoc (>= 4.0.0) + reline (>= 0.4.2) + jbuilder (2.15.1) + actionview (>= 7.0.0) + activesupport (>= 7.0.0) + jmespath (1.6.2) + json (2.21.2) kgio (2.11.4) listen (3.10.0) logger rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) logger (1.7.0) - loofah (2.19.1) + loofah (2.25.2) crass (~> 1.0.2) - nokogiri (>= 1.5.9) - mail (2.7.1) + nokogiri (>= 1.12.0) + mail (2.9.1) + logger mini_mime (>= 0.1.1) - marcel (1.0.1) - method_source (1.0.0) - mimemagic (0.4.3) - nokogiri (~> 1) - rake - mina (1.2.4) - open4 (~> 1.3.4) + net-imap + net-pop + net-smtp + marcel (1.2.1) + matrix (0.4.3) + method_source (1.1.0) + mina (1.2.5) rake - mini_mime (1.1.1) - mini_portile2 (2.8.0) - minitest (5.14.4) - msgpack (1.8.2) - nio4r (2.5.9) - nokogiri (1.13.10) - mini_portile2 (~> 2.8.0) + mini_mime (1.1.5) + mini_portile2 (2.8.9) + minitest (6.0.6) + drb (~> 2.0) + prism (~> 1.5) + minitest-mock (5.27.0) + msgpack (1.8.4) + net-imap (0.6.6) + date + net-protocol + net-pop (0.1.2) + net-protocol + net-protocol (0.2.2) + timeout + net-smtp (0.5.1) + net-protocol + nio4r (2.7.5) + nokogiri (1.19.4) + mini_portile2 (~> 2.8.2) racc (~> 1.4) - open4 (1.3.4) - passenger (6.1.2) + passenger (6.2.0) logger (>= 1.7.0) rack (>= 1.6.13) rackup (>= 1.0.1) rake (>= 12.3.3) - pg (1.5.9) - pry (0.14.1) + pg (1.6.3) + pp (0.6.4) + prettyprint + prettyprint (0.2.0) + prism (1.9.0) + pry (0.16.0) coderay (~> 1.1) method_source (~> 1.0) - public_suffix (4.0.6) - puma (5.6.8) + reline (>= 0.6.0) + public_suffix (7.0.5) + puma (8.0.2) nio4r (~> 2.0) - racc (1.6.1) - rack (2.2.23) - rack-test (1.1.0) - rack (>= 1.0, < 3) - rackup (1.0.1) - rack (< 3) - webrick - rails (6.1.4.1) - actioncable (= 6.1.4.1) - actionmailbox (= 6.1.4.1) - actionmailer (= 6.1.4.1) - actionpack (= 6.1.4.1) - actiontext (= 6.1.4.1) - actionview (= 6.1.4.1) - activejob (= 6.1.4.1) - activemodel (= 6.1.4.1) - activerecord (= 6.1.4.1) - activestorage (= 6.1.4.1) - activesupport (= 6.1.4.1) + racc (1.8.1) + rack (3.2.7) + rack-session (2.1.2) + base64 (>= 0.1.0) + rack (>= 3.0.0) + rack-test (2.2.0) + rack (>= 1.3) + rackup (2.3.1) + rack (>= 3) + rails (8.1.3.1) + actioncable (= 8.1.3.1) + actionmailbox (= 8.1.3.1) + actionmailer (= 8.1.3.1) + actionpack (= 8.1.3.1) + actiontext (= 8.1.3.1) + actionview (= 8.1.3.1) + activejob (= 8.1.3.1) + activemodel (= 8.1.3.1) + activerecord (= 8.1.3.1) + activestorage (= 8.1.3.1) + activesupport (= 8.1.3.1) bundler (>= 1.15.0) - railties (= 6.1.4.1) - sprockets-rails (>= 2.0.0) - rails-dom-testing (2.0.3) - activesupport (>= 4.2.0) + railties (= 8.1.3.1) + rails-dom-testing (2.3.0) + activesupport (>= 5.0.0) + minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.4.4) - loofah (~> 2.19, >= 2.19.1) - railties (6.1.4.1) - actionpack (= 6.1.4.1) - activesupport (= 6.1.4.1) - method_source - rake (>= 0.13) - thor (~> 1.0) - raindrops (0.19.2) + rails-html-sanitizer (1.7.1) + loofah (~> 2.25, >= 2.25.2) + nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) + railties (8.1.3.1) + actionpack (= 8.1.3.1) + activesupport (= 8.1.3.1) + irb (~> 1.13) + rackup (>= 1.0.0) + rake (>= 12.2) + thor (~> 1.0, >= 1.2.2) + tsort (>= 0.2) + zeitwerk (~> 2.6) + raindrops (0.20.1) rake (13.4.2) rb-fsevent (0.11.2) rb-inotify (0.11.1) ffi (~> 1.0) + rbs (4.1.3) + logger + prism (>= 1.6.0) + tsort + rdoc (8.0.0) + erb + prism (>= 1.6.0) + rbs (>= 4.0.0) + tsort recaptcha (5.21.2) - regexp_parser (2.1.1) - rexml (3.4.2) - rubyzip (2.3.0) + regexp_parser (2.12.0) + reline (0.7.0) + io-console (~> 0.5) + rexml (3.4.4) + rubyzip (3.5.0) sassc (2.4.0) ffi (~> 1.9) sassc-rails (2.1.2) @@ -206,47 +269,52 @@ GEM sprockets (> 3.0) sprockets-rails tilt - selenium-webdriver (3.142.7) - childprocess (>= 0.5, < 4.0) - rubyzip (>= 1.2.2) - simplecov (0.17.1) - docile (~> 1.1) - json (>= 1.8, < 3) - simplecov-html (~> 0.10.0) - simplecov-html (0.10.2) + securerandom (0.4.1) + selenium-webdriver (4.47.0) + base64 (~> 0.2) + logger (~> 1.4) + rexml (~> 3.2, >= 3.2.5) + rubyzip (>= 1.2.2, < 4.0) + websocket (~> 1.0) + simplecov (1.1.1) simpleidn (0.2.1) unf (~> 0.1.4) - sprockets (4.0.2) - concurrent-ruby (~> 1.0) - rack (> 1, < 3) - sprockets-rails (3.2.2) - actionpack (>= 4.0) - activesupport (>= 4.0) + sprockets (4.4.0) + concurrent-ruby (~> 1.1) + logger + rack (>= 2.2.4, < 4) + sprockets-rails (3.5.2) + actionpack (>= 6.1) + activesupport (>= 6.1) sprockets (>= 3.0.0) + syslog (0.4.0) + logger thor (1.5.0) - tilt (2.0.10) - tzinfo (2.0.4) + tilt (2.9.0) + timeout (0.6.1) + tsort (0.2.0) + tzinfo (2.0.6) concurrent-ruby (~> 1.0) - uglifier (4.2.1) - execjs (>= 0.3.0, < 3) unf (0.1.4) unf_ext - unf_ext (0.0.7.7) - unicorn (6.0.0) + unf_ext (0.0.9.1) + unicorn (6.1.0) kgio (~> 2.6) raindrops (~> 0.7) - webmock (3.14.0) + uri (1.1.1) + useragent (0.16.11) + webmock (3.26.2) addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) - webrick (1.9.2) + websocket (1.2.11) websocket-driver (0.8.2) base64 websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.4.2) + zeitwerk (2.8.3) PLATFORMS ruby @@ -259,22 +327,23 @@ DEPENDENCIES figaro (~> 1.3.0) jbuilder listen (>= 3.0.5, < 3.10.1) - mimemagic (~> 0.4.3) mina (~> 1.2.4) + minitest-mock + nokogiri passenger (>= 5.3.2) - pg (~> 1.5.9) + pg (~> 1.6.3) pry puma - rails (>= 6.0.3.1) + rails (~> 8.1.3) recaptcha (~> 5.21) sassc (~> 2.4) sassc-rails selenium-webdriver - simplecov (= 0.17.1) + simplecov simpleidn (= 0.2.1) - uglifier + syslog unicorn webmock BUNDLED WITH - 2.5.7 + 2.6.9 diff --git a/app/controllers/contact_requests_controller.rb b/app/controllers/contact_requests_controller.rb index 6da8e62a..988592ad 100644 --- a/app/controllers/contact_requests_controller.rb +++ b/app/controllers/contact_requests_controller.rb @@ -30,7 +30,10 @@ def create def redirect_to_main referer = ENV.fetch('main_page_url') { root_url } respond_to do |format| - format.html { redirect_to referer } + # Leaving our own host is the whole point of this button, and the address comes from the + # server configuration rather than from the request, so the open redirect protection that + # the 7.1 defaults turned on has nothing to protect here. + format.html { redirect_to referer, allow_other_host: true } end end diff --git a/app/mailers/contact_request_mailer.rb b/app/mailers/contact_request_mailer.rb index f5ec0949..46eb2ab4 100644 --- a/app/mailers/contact_request_mailer.rb +++ b/app/mailers/contact_request_mailer.rb @@ -14,7 +14,7 @@ def confirmation_email(contact_request) end def contact_email(contact_request:, recipients:, mail_body:, raise_error: false) - raise ::Net::SMTPFatalError if Rails.env.test? && raise_error + raise ::Net::SMTPFatalError, 'simulated delivery failure' if Rails.env.test? && raise_error if ApplicationMailer.ses_configured? ses_contact_email( diff --git a/app/models/concerns/bounceable.rb b/app/models/concerns/bounceable.rb new file mode 100644 index 00000000..9bd65bd3 --- /dev/null +++ b/app/models/concerns/bounceable.rb @@ -0,0 +1,37 @@ +module Bounceable + extend ActiveSupport::Concern + + def registrant_bounced?(bounced_recipients) + registrant_email = whois_record.json['email'] + bounced_recipients.as_json.find { |r| break true if r['emailAddress'] == registrant_email } + end + + class_methods do + def send_bounce_alert(json) + contact_request = find_by(message_id: json['mail']['messageId']) + log_to_registry(json) + return unless contact_request&.registrant_bounced?(json['bounce']['bouncedRecipients']) + + BounceBackMailer.bounce_alert( + contact_request.email, contact_request.whois_record['name'], contact_request.name + ).deliver_now + end + + def log_to_registry(json) + return unless ENV['bounces_api_url'] + + uri = URI.parse(ENV['bounces_api_url']) + secret = ENV['bounces_api_shared_key'] + + header = { 'Content-Type': 'application/json', 'Authorization': "Basic #{secret}" } + body = { data: json } + + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true if uri.instance_of?(URI::HTTPS) + request = Net::HTTP::Post.new(uri.request_uri, header) + request.body = body.to_json + + http.request(request) + end + end +end diff --git a/app/models/concerns/contact_request/bounceable.rb b/app/models/concerns/contact_request/bounceable.rb deleted file mode 100644 index 6ac968a2..00000000 --- a/app/models/concerns/contact_request/bounceable.rb +++ /dev/null @@ -1,41 +0,0 @@ -module Concerns - module ContactRequest - module Bounceable - extend ActiveSupport::Concern - - def registrant_bounced?(bounced_recipients) - registrant_email = whois_record.json['email'] - bounced_recipients.as_json.find { |r| break true if r['emailAddress'] == registrant_email } - end - - class_methods do - def send_bounce_alert(json) - contact_request = find_by(message_id: json['mail']['messageId']) - log_to_registry(json) - return unless contact_request&.registrant_bounced?(json['bounce']['bouncedRecipients']) - - BounceBackMailer.bounce_alert( - contact_request.email, contact_request.whois_record['name'], contact_request.name - ).deliver_now - end - - def log_to_registry(json) - return unless ENV['bounces_api_url'] - - uri = URI.parse(ENV['bounces_api_url']) - secret = ENV['bounces_api_shared_key'] - - header = { 'Content-Type': 'application/json', 'Authorization': "Basic #{secret}" } - body = { data: json } - - http = Net::HTTP.new(uri.host, uri.port) - http.use_ssl = true if uri.instance_of?(URI::HTTPS) - request = Net::HTTP::Post.new(uri.request_uri, header) - request.body = body.to_json - - http.request(request) - end - end - end - end -end diff --git a/app/models/contact_request.rb b/app/models/contact_request.rb index 9f830ffe..0cd58d38 100644 --- a/app/models/contact_request.rb +++ b/app/models/contact_request.rb @@ -1,5 +1,5 @@ class ContactRequest < ApplicationRecord - include Concerns::ContactRequest::Bounceable + include Bounceable def self.connect_to_write_database_if_defined return unless Rails.configuration.database_configuration["write_#{Rails.env}"] diff --git a/app/models/whois_record.rb b/app/models/whois_record.rb index 047aee84..cef7b099 100644 --- a/app/models/whois_record.rb +++ b/app/models/whois_record.rb @@ -50,7 +50,7 @@ def deserialize_domain delete: json['delete'], registration_deadline: json['registration_deadline'] .try(:to_datetime) - .try(:to_s, :iso8601)) + .try(:to_fs, :iso8601)) end # rubocop:enable Metrics/AbcSize diff --git a/config/application.rb b/config/application.rb index 1c13b0a0..f24d0fab 100644 --- a/config/application.rb +++ b/config/application.rb @@ -8,9 +8,7 @@ module RestWhois class Application < Rails::Application - # Initialize configuration defaults for originally generated Rails version. - config.load_defaults 6.0 - config.autoloader = :classic + config.load_defaults 8.1 # Authorize all hosts config.hosts.clear @@ -54,6 +52,12 @@ class Application < Rails::Application 'Referrer-Policy' => 'strict-origin-when-cross-origin' } - config.active_support.parse_json_times = true + # Deliberately off. The line used to say true, but it never took effect: on Rails 6.1 the + # active_support railtie skipped the setting because ActiveSupport did not respond to it + # yet at initializer time, so dates inside the whois json column have always been handed + # to the views and to the json API as plain strings. Rails 8 does apply it, and turning it + # on would silently change the date format of the public whois output - a product + # decision, not something an upgrade gets to do on the side. + config.active_support.parse_json_times = false end end diff --git a/config/environments/production.rb b/config/environments/production.rb index 9f81da8b..dbe72212 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -22,8 +22,8 @@ # Apache or NGINX already handles this. config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? - # Compress JavaScripts and CSS. - config.assets.js_compressor = Uglifier.new(harmony: true) + # No JavaScript compressor is configured: the application ships no JavaScript at all, so there is + # nothing to minify and no reason to require a JS runtime on the servers. # config.assets.css_compressor = :sass # Do not fallback to assets pipeline if a precompiled asset is missed. diff --git a/config/environments/test.rb b/config/environments/test.rb index 40f08b5b..f0617676 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -22,8 +22,9 @@ config.consider_all_requests_local = true config.action_controller.perform_caching = false - # Raise exceptions instead of rendering exception templates. - config.action_dispatch.show_exceptions = false + # Raise exceptions instead of rendering exception templates. Used to be false; since + # Rails 7.1 this setting takes a symbol, and a boolean no longer means anything here. + config.action_dispatch.show_exceptions = :none # Disable request forgery protection in test environment. config.action_controller.allow_forgery_protection = false diff --git a/config/initializers/new_framework_defaults_5_2.rb b/config/initializers/new_framework_defaults_5_2.rb deleted file mode 100644 index c383d072..00000000 --- a/config/initializers/new_framework_defaults_5_2.rb +++ /dev/null @@ -1,38 +0,0 @@ -# Be sure to restart your server when you modify this file. -# -# This file contains migration options to ease your Rails 5.2 upgrade. -# -# Once upgraded flip defaults one by one to migrate to the new default. -# -# Read the Guide for Upgrading Ruby on Rails for more info on each option. - -# Make Active Record use stable #cache_key alongside new #cache_version method. -# This is needed for recyclable cache keys. -# Rails.application.config.active_record.cache_versioning = true - -# Use AES-256-GCM authenticated encryption for encrypted cookies. -# Also, embed cookie expiry in signed or encrypted cookies for increased security. -# -# This option is not backwards compatible with earlier Rails versions. -# It's best enabled when your entire app is migrated and stable on 5.2. -# -# Existing cookies will be converted on read then written with the new scheme. -# Rails.application.config.action_dispatch.use_authenticated_cookie_encryption = true - -# Use AES-256-GCM authenticated encryption as default cipher for encrypting messages -# instead of AES-256-CBC, when use_authenticated_message_encryption is set to true. -# Rails.application.config.active_support.use_authenticated_message_encryption = true - -# Add default protection from forgery to ActionController::Base instead of in -# ApplicationController. -# Rails.application.config.action_controller.default_protect_from_forgery = true - -# Store boolean values are in sqlite3 databases as 1 and 0 instead of 't' and -# 'f' after migrating old data. -# Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true - -# Use SHA-1 instead of MD5 to generate non-sensitive digests, such as the ETag header. -# Rails.application.config.active_support.use_sha1_digests = true - -# Make `form_with` generate id attributes for any generated HTML tags. -# Rails.application.config.action_view.form_with_generates_ids = true diff --git a/lib/tasks/db.rake b/lib/tasks/db.rake index 7dd84102..fc100c9e 100644 --- a/lib/tasks/db.rake +++ b/lib/tasks/db.rake @@ -5,7 +5,9 @@ namespace :db do if Rails.env.production? %i[write_production production].each do |connection| - ActiveRecord::Base.clear_all_connections! + # ActiveRecord::Base delegated this to the connection handler until 6.1; the + # delegation is gone since 7.1, the handler method itself is not. + ActiveRecord::Base.connection_handler.clear_all_connections! ActiveRecord::Base.establish_connection(connection) Rake::Task['db:migrate'].invoke end diff --git a/test/integration/whois_records/details/private_person_registrant_json_test.rb b/test/integration/whois_records/details/private_person_registrant_json_test.rb index 4a1abd67..d099873b 100644 --- a/test/integration/whois_records/details/private_person_registrant_json_test.rb +++ b/test/integration/whois_records/details/private_person_registrant_json_test.rb @@ -5,7 +5,7 @@ class WhoisRecordDetailsPrivatePersonRegistrantJSONTest < ActionDispatch::Integr setup do @whois_record = whois_records(:privately_owned) - @whois_record.update!(json: @whois_record.json.merge({ registrant_kind: 'priv' })) + @whois_record.update!(json: @whois_record.json.merge({ registrant_kind: 'priv' }).deep_stringify_keys) @original_whitelist_ip = ENV['whitelist_ip'] ENV['whitelist_ip'] = '' @@ -21,7 +21,7 @@ def test_registrant_name_is_unmasked_when_disclosed @whois_record.update!(json: @whois_record.json .merge({ registrant: 'John', registrant_disclosed_attributes: %w[name], - registrant_publishable: 'true'})) + registrant_publishable: 'true'}).deep_stringify_keys) get whois_record_path(name: @whois_record.name), as: :json @@ -30,7 +30,7 @@ def test_registrant_name_is_unmasked_when_disclosed def test_registrant_email_is_masked_when_disclosed_and_captcha_is_unsolved @whois_record.update!(json: @whois_record.json - .merge({ registrant_disclosed_attributes: %w[email] })) + .merge({ registrant_disclosed_attributes: %w[email] }).deep_stringify_keys) get whois_record_path(name: @whois_record.name), as: :json @@ -41,7 +41,7 @@ def test_registrant_email_is_unmasked_when_disclosed_and_captcha_is_solved solve_captcha @whois_record.update!(json: @whois_record.json .merge({ email: 'john@inbox.test', - registrant_disclosed_attributes: %w[email] })) + registrant_disclosed_attributes: %w[email] }).deep_stringify_keys) get whois_record_path(name: @whois_record.name), as: :json @@ -50,7 +50,7 @@ def test_registrant_email_is_unmasked_when_disclosed_and_captcha_is_solved def test_registrant_phone_is_masked_when_disclosed_and_captcha_is_unsolved @whois_record.update!(json: @whois_record.json - .merge({ registrant_disclosed_attributes: %w[phone] })) + .merge({ registrant_disclosed_attributes: %w[phone] }).deep_stringify_keys) get whois_record_path(name: @whois_record.name), as: :json @@ -61,7 +61,7 @@ def test_registrant_phone_is_unmasked_when_disclosed_and_captcha_is_solved solve_captcha @whois_record.update!(json: @whois_record.json .merge({ phone: '1234', - registrant_disclosed_attributes: %w[phone] })) + registrant_disclosed_attributes: %w[phone] }).deep_stringify_keys) get whois_record_path(name: @whois_record.name), as: :json @@ -70,7 +70,7 @@ def test_registrant_phone_is_unmasked_when_disclosed_and_captcha_is_solved def test_registrant_sensitive_data_is_masked_when_not_publishable @whois_record.update!(json: @whois_record.json - .merge({ registrant_publishable: false, registrant_disclosed_attributes: %w[name] })) + .merge({ registrant_publishable: false, registrant_disclosed_attributes: %w[name] }).deep_stringify_keys) get whois_record_path(name: @whois_record.name), as: :json @@ -82,7 +82,7 @@ def test_registrant_sensitive_data_is_masked_when_not_publishable def test_registrant_sensitive_data_is_unmasked_when_publishable @whois_record.update!(json: @whois_record.json .merge({ registrant_publishable: true, - registrant_disclosed_attributes: %w[name email phone] })) + registrant_disclosed_attributes: %w[name email phone] }).deep_stringify_keys) get whois_record_path(name: @whois_record.name), as: :json @@ -95,7 +95,7 @@ def test_admin_contact_name_is_unmasked_when_disclosed @whois_record.update!(json: @whois_record.json .merge({ admin_contacts: [{ name: 'John', disclosed_attributes: %w[name], - contact_publishable: 'true' }] })) + contact_publishable: 'true' }] }).deep_stringify_keys) get whois_record_path(name: @whois_record.name), as: :json @@ -104,7 +104,7 @@ def test_admin_contact_name_is_unmasked_when_disclosed def test_admin_contact_email_is_masked_when_disclosed_and_captcha_is_unsolved @whois_record.update!(json: @whois_record.json - .merge({ admin_contacts: [{ disclosed_attributes: %w[email] }] })) + .merge({ admin_contacts: [{ disclosed_attributes: %w[email] }] }).deep_stringify_keys) get whois_record_path(name: @whois_record.name), as: :json @@ -115,7 +115,7 @@ def test_admin_contact_email_is_unmasked_when_disclosed_and_captcha_is_solved solve_captcha @whois_record.update!(json: @whois_record.json .merge({ admin_contacts: [{ email: 'john@inbox.test', - disclosed_attributes: %w[email] }] })) + disclosed_attributes: %w[email] }] }).deep_stringify_keys) get whois_record_path(name: @whois_record.name), as: :json @@ -126,7 +126,7 @@ def test_tech_contact_name_is_unmasked_when_disclosed @whois_record.update!(json: @whois_record.json .merge({ tech_contacts: [{ name: 'John', disclosed_attributes: %w[name], - contact_publishable: 'true'}] })) + contact_publishable: 'true'}] }).deep_stringify_keys) get whois_record_path(name: @whois_record.name), as: :json @@ -135,7 +135,7 @@ def test_tech_contact_name_is_unmasked_when_disclosed def test_tech_contact_email_is_masked_when_disclosed_and_captcha_is_unsolved @whois_record.update!(json: @whois_record.json - .merge({ tech_contacts: [{ disclosed_attributes: %w[email] }] })) + .merge({ tech_contacts: [{ disclosed_attributes: %w[email] }] }).deep_stringify_keys) get whois_record_path(name: @whois_record.name), as: :json @@ -146,7 +146,7 @@ def test_tech_contact_email_is_unmasked_when_disclosed_and_captcha_is_solved solve_captcha @whois_record.update!(json: @whois_record.json .merge({ tech_contacts: [{ email: 'john@inbox.test', - disclosed_attributes: %w[email] }] })) + disclosed_attributes: %w[email] }] }).deep_stringify_keys) get whois_record_path(name: @whois_record.name), as: :json diff --git a/test/integration/whois_records/json_test.rb b/test/integration/whois_records/json_test.rb index 4b5fd314..c2291e60 100644 --- a/test/integration/whois_records/json_test.rb +++ b/test/integration/whois_records/json_test.rb @@ -257,7 +257,7 @@ def test_hide_sensitive_data_of_legal_entity_when_ip_is_not_in_whitelist def test_show_sensitive_data_when_registrant_is_publishable whois_record = whois_records(:legally_owned) whois_record.update!(json: whois_record.json.merge({ registrant_publishable: true, - registrant_disclosed_attributes: %w[name email phone] })) + registrant_disclosed_attributes: %w[name email phone] }).deep_stringify_keys) get '/v1/company-domain.test', params: { format: :json } response_json = JSON.parse(response.body, symbolize_names: true) @@ -269,7 +269,7 @@ def test_show_sensitive_data_when_registrant_is_publishable def test_hide_sensitive_data_when_registrant_is_not_publishable whois_record = whois_records(:privately_owned) - whois_record.update!(json: whois_record.json.merge({ registrant_publishable: false })) + whois_record.update!(json: whois_record.json.merge({ registrant_publishable: false }).deep_stringify_keys) whois_record.reload get '/v1/privatedomain.test', params: { format: :json } diff --git a/test/models/contact_request_test.rb b/test/models/contact_request_test.rb index a9cf06a1..f9ee70d7 100644 --- a/test/models/contact_request_test.rb +++ b/test/models/contact_request_test.rb @@ -52,19 +52,21 @@ def test_new_request_generates_random_124_character_secret_on_creation def test_secret_cannot_be_changed @contact_request.save - @contact_request.update!(secret: 'foo') - @contact_request.reload - refute_equal('foo', @contact_request.secret) + assert_raises(ActiveRecord::ReadonlyAttributeError) do + @contact_request.update!(secret: 'foo') + end + refute_equal('foo', @contact_request.reload.secret) end def test_valid_to_cannot_be_changed @contact_request.save new_date = Time.parse('2018-01-01 00:00 UTC') - @contact_request.update!(valid_to: new_date) - @contact_request.reload - refute_equal(new_date, @contact_request.valid_to) + assert_raises(ActiveRecord::ReadonlyAttributeError) do + @contact_request.update!(valid_to: new_date) + end + refute_equal(new_date, @contact_request.reload.valid_to) end def test_can_be_confirmed_only_once diff --git a/test/models/whois_record_test.rb b/test/models/whois_record_test.rb index 9cc88d48..d56c0dba 100644 --- a/test/models/whois_record_test.rb +++ b/test/models/whois_record_test.rb @@ -72,7 +72,7 @@ def test_deserializes_domain_with_old_reg_deadline_format assert_equal 'shop.test', whois_record.domain.name assert_equal %w[active], whois_record.domain.statuses - assert_equal Time.parse('2020-06-09 23:59:59 +0300').try(:to_datetime).try(:to_s, :iso8601), + assert_equal Time.parse('2020-06-09 23:59:59 +0300').try(:to_datetime).try(:to_fs, :iso8601), whois_record.domain.registration_deadline end diff --git a/test/system/whois_records/details/base_test.rb b/test/system/whois_records/details/base_test.rb index 6c8f4072..14ec2de7 100644 --- a/test/system/whois_records/details/base_test.rb +++ b/test/system/whois_records/details/base_test.rb @@ -25,7 +25,7 @@ def test_domain_details changed: '2010-07-06T00:00:00+00:00', expire: '2010-07-07T00:00:00+00:00', outzone: '2010-07-08T00:00:00+00:00', - delete: '2010-07-09T00:00:00+00:00' })) + delete: '2010-07-09T00:00:00+00:00' }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -46,7 +46,7 @@ def test_registrant_details .merge({ registrant: 'John', registrant_kind: 'priv', email: 'john@inbox.test', - registrant_changed: '2010-07-05T00:00:00+00:00' })) + registrant_changed: '2010-07-05T00:00:00+00:00' }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -64,7 +64,7 @@ def test_admin_contacts_details .merge({ admin_contacts: [{ name: 'John', email: 'john@inbox.test', changed: '2010-07-05T00:00:00+00:00', - }] })) + }] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -81,7 +81,7 @@ def test_tech_contacts_details .merge({ tech_contacts: [{ name: 'John', email: 'john@inbox.test', changed: '2010-07-05T00:00:00+00:00', - }] })) + }] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -97,7 +97,7 @@ def test_registrar_details .merge({ registrar: 'Bestnames', registrar_website: 'http://bestnames.test', registrar_phone: '1234', - registrar_changed: '2010-07-05T00:00:00+00:00' })) + registrar_changed: '2010-07-05T00:00:00+00:00' }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -114,7 +114,7 @@ def test_nameservers .merge({ nameservers: %w[ns1.bestnames.test ns2.bestnames.test], nameservers_changed: '2010-07-05T00:00:00+00:00', - })) + }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -129,7 +129,7 @@ def test_dnssec_keys @whois_record.update!(json: @whois_record.json .merge({ dnssec_keys: %w[one two], dnssec_changed: '2010-07-05T00:00:00+00:00', - })) + }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) diff --git a/test/system/whois_records/details/legal_person_registrant_test.rb b/test/system/whois_records/details/legal_person_registrant_test.rb index f6676bcc..de4758ac 100644 --- a/test/system/whois_records/details/legal_person_registrant_test.rb +++ b/test/system/whois_records/details/legal_person_registrant_test.rb @@ -3,7 +3,7 @@ class WhoisRecordDetailsLegalPersonRegistrantTest < ApplicationSystemTestCase setup do @whois_record = whois_records(:privately_owned) - @whois_record.update!(json: @whois_record.json.merge({ registrant_kind: 'org' })) + @whois_record.update!(json: @whois_record.json.merge({ registrant_kind: 'org' }).deep_stringify_keys) @original_whitelist_ip = ENV['whitelist_ip'] ENV['whitelist_ip'] = '' @@ -16,7 +16,7 @@ class WhoisRecordDetailsLegalPersonRegistrantTest < ApplicationSystemTestCase def test_legal_person_specific_registrant_details_are_visible @whois_record.update!(json: @whois_record.json .merge({ registrant_reg_no: '1234', - registrant_ident_country_code: 'US' })) + registrant_ident_country_code: 'US' }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -27,7 +27,7 @@ def test_legal_person_specific_registrant_details_are_visible end def test_registrant_name_is_unmasked - @whois_record.update!(json: @whois_record.json.merge({ registrant: 'Acme', registrant_publishable: 'true' })) + @whois_record.update!(json: @whois_record.json.merge({ registrant: 'Acme', registrant_publishable: 'true' }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -55,7 +55,7 @@ def test_registrant_phone_is_masked_when_not_disclosed def test_registrant_phone_is_masked_when_disclosed_and_unsolved_captcha @whois_record.update!(json: @whois_record.json - .merge({ registrant_disclosed_attributes: %w[phone] })) + .merge({ registrant_disclosed_attributes: %w[phone] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) within '.registrant' do @@ -67,7 +67,7 @@ def test_registrant_email_and_last_update_are_unmasked_when_captcha_is_solved solve_captcha @whois_record.update!(json: @whois_record.json .merge({ email: 'john@inbox.test', - registrant_changed: '2010-07-05T00:00:00+00:00' })) + registrant_changed: '2010-07-05T00:00:00+00:00' }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -80,7 +80,7 @@ def test_registrant_email_and_last_update_are_unmasked_when_captcha_is_solved def test_registrant_phone_is_unmasked_when_disclosed_and_solved_captcha solve_captcha @whois_record.update!(json: @whois_record.json - .merge({ registrant_disclosed_attributes: %w[phone] })) + .merge({ registrant_disclosed_attributes: %w[phone] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) within '.registrant' do @@ -122,7 +122,7 @@ def test_admin_and_tech_contact_data_is_unmasked_when_captcha_is_solved changed: '2010-07-07T00:00:00+00:00', contact_publishable: true, disclosed_attributes: %w[name email phone changed] - }] })) + }] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -142,7 +142,7 @@ def test_admin_and_tech_contact_data_is_unmasked_when_captcha_is_solved end def test_registrant_sensitive_data_is_masked_when_registrant_is_not_publishable - @whois_record.update!(json: @whois_record.json.merge({ registrant_publishable: false })) + @whois_record.update!(json: @whois_record.json.merge({ registrant_publishable: false }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) within '.registrant' do @@ -155,7 +155,7 @@ def test_registrant_sensitive_data_is_masked_when_registrant_is_not_publishable def test_registrant_sensitive_data_is_unmasked_when_registrant_is_publishable @whois_record.update!(json: @whois_record.json.merge({ registrant_publishable: true, - registrant_disclosed_attributes: %w[name email phone] })) + registrant_disclosed_attributes: %w[name email phone] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) within '.registrant' do diff --git a/test/system/whois_records/details/private_person_registrant_test.rb b/test/system/whois_records/details/private_person_registrant_test.rb index d08c553a..409ddde5 100644 --- a/test/system/whois_records/details/private_person_registrant_test.rb +++ b/test/system/whois_records/details/private_person_registrant_test.rb @@ -3,7 +3,7 @@ class WhoisRecordDetailsPrivatePersonRegistrantTest < ApplicationSystemTestCase setup do @whois_record = whois_records(:privately_owned) - @whois_record.update!(json: @whois_record.json.merge({ registrant_kind: 'priv' })) + @whois_record.update!(json: @whois_record.json.merge({ registrant_kind: 'priv' }).deep_stringify_keys) @original_whitelist_ip = ENV['whitelist_ip'] ENV['whitelist_ip'] = '' @@ -16,7 +16,7 @@ class WhoisRecordDetailsPrivatePersonRegistrantTest < ApplicationSystemTestCase def test_legal_person_specific_registrant_details_are_hidden @whois_record.update!(json: @whois_record.json .merge({ registrant_reg_no: 'legal-person-reg-number', - registrant_ident_country_code: 'legal-person-country' })) + registrant_ident_country_code: 'legal-person-country' }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -36,7 +36,7 @@ def test_sensitive_data_is_masked_when_captcha_is_unsolved end def test_sensitive_data_is_masked_when_registrant_is_not_publishable - @whois_record.update!(json: @whois_record.json.merge({ registrant_publishable: false })) + @whois_record.update!(json: @whois_record.json.merge({ registrant_publishable: false }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) within '.registrant' do @@ -62,7 +62,7 @@ def test_registrant_name_is_unmasked_when_disclosed @whois_record.update!(json: @whois_record.json .merge({ registrant: 'John', registrant_disclosed_attributes: %w[name], - registrant_publishable: true})) + registrant_publishable: true}).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -73,7 +73,7 @@ def test_registrant_name_is_unmasked_when_disclosed def test_registrant_email_is_masked_when_disclosed_and_captcha_is_unsolved @whois_record.update!(json: @whois_record.json - .merge({ registrant_disclosed_attributes: %w[email] })) + .merge({ registrant_disclosed_attributes: %w[email] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -86,7 +86,7 @@ def test_registrant_email_is_unmasked_when_disclosed_and_captcha_is_solved solve_captcha @whois_record.update!(json: @whois_record.json .merge({ email: 'john@inbox.test', - registrant_disclosed_attributes: %w[email] })) + registrant_disclosed_attributes: %w[email] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -97,7 +97,7 @@ def test_registrant_email_is_unmasked_when_disclosed_and_captcha_is_solved def test_registrant_phone_is_masked_when_disclosed_and_captcha_is_unsolved @whois_record.update!(json: @whois_record.json - .merge({ registrant_disclosed_attributes: %w[phone] })) + .merge({ registrant_disclosed_attributes: %w[phone] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -110,7 +110,7 @@ def test_registrant_phone_is_unmasked_when_disclosed_and_captcha_is_solved solve_captcha @whois_record.update!(json: @whois_record.json .merge({ phone: '1234', - registrant_disclosed_attributes: %w[phone] })) + registrant_disclosed_attributes: %w[phone] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -121,7 +121,7 @@ def test_registrant_phone_is_unmasked_when_disclosed_and_captcha_is_solved def test_registrant_sensitive_data_is_unmasked_when_registrant_is_publishable @whois_record.update!(json: @whois_record.json.merge({ registrant_publishable: true, - registrant_disclosed_attributes: %w[name email phone] })) + registrant_disclosed_attributes: %w[name email phone] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) within '.registrant' do @@ -135,7 +135,7 @@ def test_admin_contact_name_is_unmasked_when_disclosed @whois_record.update!(json: @whois_record.json .merge({ admin_contacts: [{ name: 'John', disclosed_attributes: %w[name], - contact_publishable: 'true'}] })) + contact_publishable: 'true'}] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -146,7 +146,7 @@ def test_admin_contact_name_is_unmasked_when_disclosed def test_admin_contact_email_is_masked_when_disclosed_and_captcha_is_unsolved @whois_record.update!(json: @whois_record.json - .merge({ admin_contacts: [{ disclosed_attributes: %w[email] }] })) + .merge({ admin_contacts: [{ disclosed_attributes: %w[email] }] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -159,7 +159,7 @@ def test_admin_contact_email_is_unmasked_when_disclosed_and_captcha_is_solved solve_captcha @whois_record.update!(json: @whois_record.json .merge({ admin_contacts: [{ email: 'john@inbox.test', - disclosed_attributes: %w[email] }] })) + disclosed_attributes: %w[email] }] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -172,7 +172,7 @@ def test_tech_contact_name_is_unmasked_when_disclosed @whois_record.update!(json: @whois_record.json .merge({ tech_contacts: [{ name: 'John', disclosed_attributes: %w[name], - contact_publishable: 'true' }] })) + contact_publishable: 'true' }] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -183,7 +183,7 @@ def test_tech_contact_name_is_unmasked_when_disclosed def test_tech_contact_email_is_masked_when_disclosed_and_captcha_is_unsolved @whois_record.update!(json: @whois_record.json - .merge({ tech_contacts: [{ disclosed_attributes: %w[email] }] })) + .merge({ tech_contacts: [{ disclosed_attributes: %w[email] }] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -196,7 +196,7 @@ def test_tech_contact_email_is_unmasked_when_disclosed_and_captcha_is_solved solve_captcha @whois_record.update!(json: @whois_record.json .merge({ tech_contacts: [{ email: 'john@inbox.test', - disclosed_attributes: %w[email] }] })) + disclosed_attributes: %w[email] }] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name)