From ee89ecbc8f2ece7b2441ee0edd8d664421a5688d Mon Sep 17 00:00:00 2001 From: Jeremy Evans <code@jeremyevans.net> Date: Mon, 23 May 2022 10:51:20 -0700 Subject: [PATCH] Work around implicit null byte check added in bcrypt 3.1.18 by checking password requirements before other password checks An undocumented change in bcrypt 3.1.18 causes it to raise ArgumentError for passwords with null bytes, due to a change to use StringValueCStr instead of StringValuePtr in the change to unlock the GVL while calculating the password hash. This works around the issue by performing Rodauth's checks on the password before passing the password to bcrypt. --- CHANGELOG | 2 ++ lib/rodauth/features/reset_password.rb | 8 ++++---- spec/reset_password_spec.rb | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 07658a3b..1ed56ed2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,7 @@ === master +* Work around implicit null byte check added in bcrypt 3.1.18 by checking password requirements before other password checks (jeremyevans) + * Fix invalid HTML on pages with OTP QR codes (jeremyevans) * Add recovery_codes_available? configuration method to the recovery_codes feature (janko) (#238) diff --git a/lib/rodauth/features/reset_password.rb b/lib/rodauth/features/reset_password.rb index 3f1ddc92..3b97bf08 100644 --- a/lib/rodauth/features/reset_password.rb +++ b/lib/rodauth/features/reset_password.rb @@ -130,6 +130,10 @@ module Rodauth password = param(password_param) catch_error do + unless password_meets_requirements?(password) + throw_error_status(invalid_field_error_status, password_param, password_does_not_meet_requirements_message) + end + if password_match?(password) throw_error_reason(:same_as_existing_password, invalid_field_error_status, password_param, same_as_existing_password_message) end @@ -138,10 +142,6 @@ module Rodauth throw_error_reason(:passwords_do_not_match, unmatched_field_error_status, password_param, passwords_do_not_match_message) end - unless password_meets_requirements?(password) - throw_error_status(invalid_field_error_status, password_param, password_does_not_meet_requirements_message) - end - transaction do before_reset_password set_password(password) diff --git a/spec/reset_password_spec.rb b/spec/reset_password_spec.rb index b1be24ef..866046c6 100644 --- a/spec/reset_password_spec.rb +++ b/spec/reset_password_spec.rb @@ -272,7 +272,7 @@ def rodauth.raised_uniqueness_violation(*) StandardError.new; end res = json_request('/reset-password', :key=>link[4...-1]) res.must_equal [401, {"reason"=>"invalid_reset_password_key", "error"=>"There was an error resetting your password"}] - res = json_request('/reset-password', :key=>link[4..-1], :password=>'1', "password-confirm"=>'2') + res = json_request('/reset-password', :key=>link[4..-1], :password=>'ab1234561', "password-confirm"=>'ab1234562') res.must_equal [422, {'reason'=>"passwords_do_not_match","error"=>"There was an error resetting your password", "field-error"=>["password", 'passwords do not match']}] res = json_request('/reset-password', :key=>link[4..-1], :password=>'0123456789', "password-confirm"=>'0123456789')