Skip to content

Commit e1977c5

Browse files
authored
Correct bcrypt salt regex (#1279)
* Fix bcrypt salt regex checks The two-digit header of a bcrypt salt is a strength parameter. Valid values for the strength parameter range from 4 to 31 inclusive. Update the regex used to check the salt to only match against values from 04 to 31. * Add tests for invalid strength parameters * Add explanatory note to docstring for pw_hash mention the meaning of the first two characters as a strength parameter
1 parent 199e514 commit e1977c5

File tree

2 files changed

+18
-5
lines changed

2 files changed

+18
-5
lines changed

lib/puppet/parser/functions/pw_hash.rb

+7-5
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@
2525
|bcrypt-x |2x |bug compatible |
2626
|bcrypt-y |2y |historic alias for 2b|
2727
28-
The third argument to this function is the salt to use.
28+
The third argument to this function is the salt to use. For bcrypt-type hashes,
29+
the first two characters of the salt represent a strength parameter, with a value
30+
between 4 and 31 inclusive.
2931
3032
@return [String]
3133
Provides a crypt hash usable on most POSIX systems.
@@ -48,10 +50,10 @@
4850
'md5' => { prefix: '1' },
4951
'sha-256' => { prefix: '5' },
5052
'sha-512' => { prefix: '6' },
51-
'bcrypt' => { prefix: '2b', salt: %r{^[0-9]{2}\$[./A-Za-z0-9]{22}} },
52-
'bcrypt-a' => { prefix: '2a', salt: %r{^[0-9]{2}\$[./A-Za-z0-9]{22}} },
53-
'bcrypt-x' => { prefix: '2x', salt: %r{^[0-9]{2}\$[./A-Za-z0-9]{22}} },
54-
'bcrypt-y' => { prefix: '2y', salt: %r{^[0-9]{2}\$[./A-Za-z0-9]{22}} },
53+
'bcrypt' => { prefix: '2b', salt: %r{^(0[4-9]|[12][0-9]|3[01])\$[./A-Za-z0-9]{22}} },
54+
'bcrypt-a' => { prefix: '2a', salt: %r{^(0[4-9]|[12][0-9]|3[01])\$[./A-Za-z0-9]{22}} },
55+
'bcrypt-x' => { prefix: '2x', salt: %r{^(0[4-9]|[12][0-9]|3[01])\$[./A-Za-z0-9]{22}} },
56+
'bcrypt-y' => { prefix: '2y', salt: %r{^(0[4-9]|[12][0-9]|3[01])\$[./A-Za-z0-9]{22}} },
5557
}
5658

5759
raise ArgumentError, 'pw_hash(): first argument must be a string' unless args[0].is_a?(String) || args[0].nil?

spec/functions/pw_hash_spec.rb

+11
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,17 @@
5858
it { is_expected.to run.with_params('password', 'bcrypt-y', '1234').and_raise_error(ArgumentError, %r{characters in salt must match}) }
5959
end
6060

61+
context 'when the third argument has an invalid strength parameter for bcrypt' do
62+
it { is_expected.to run.with_params('password', 'bcrypt', '03$salt.salt.salt.salt.sa').and_raise_error(ArgumentError, %r{characters in salt must match}) }
63+
it { is_expected.to run.with_params('password', 'bcrypt-a', '03$salt.salt.salt.salt.sa').and_raise_error(ArgumentError, %r{characters in salt must match}) }
64+
it { is_expected.to run.with_params('password', 'bcrypt-x', '03$salt.salt.salt.salt.sa').and_raise_error(ArgumentError, %r{characters in salt must match}) }
65+
it { is_expected.to run.with_params('password', 'bcrypt-y', '03$salt.salt.salt.salt.sa').and_raise_error(ArgumentError, %r{characters in salt must match}) }
66+
it { is_expected.to run.with_params('password', 'bcrypt', '32$salt.salt.salt.salt.sa').and_raise_error(ArgumentError, %r{characters in salt must match}) }
67+
it { is_expected.to run.with_params('password', 'bcrypt-a', '32$salt.salt.salt.salt.sa').and_raise_error(ArgumentError, %r{characters in salt must match}) }
68+
it { is_expected.to run.with_params('password', 'bcrypt-x', '32$salt.salt.salt.salt.sa').and_raise_error(ArgumentError, %r{characters in salt must match}) }
69+
it { is_expected.to run.with_params('password', 'bcrypt-y', '32$salt.salt.salt.salt.sa').and_raise_error(ArgumentError, %r{characters in salt must match}) }
70+
end
71+
6172
context 'when running on a platform with a weak String#crypt implementation' do
6273
before(:each) { allow_any_instance_of(String).to receive(:crypt).with('$1$1').and_return('a bad hash') } # rubocop:disable RSpec/AnyInstance : Unable to find a viable replacement
6374

0 commit comments

Comments
 (0)