Skip to content

Commit fb83fe1

Browse files
committed
Enhance password validation tests with more test vectors
This patch reorganizes the way we test the new PasswordValidator method due to the increased patterns that we want to test to validate the existing components. In particular: - Add TestVector struct to support structured test case definitions - Add comprehensive test coverage for various password patterns: * Fernet encryption key format validation * Alphanumeric-only password patterns (tr command generated) * Shell expansion attack vector detection Signed-off-by: Francesco Pantano <fpantano@redhat.com>
1 parent 9818242 commit fb83fe1

1 file changed

Lines changed: 215 additions & 133 deletions

File tree

modules/common/secret/password_test.go

Lines changed: 215 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,21 @@ package secret
1919
import (
2020
. "github.com/onsi/gomega" // nolint:revive
2121
"regexp"
22+
"slices"
2223
"testing"
2324
)
2425

2526
const ErrMsg string = "password does not meet the requirements"
2627

28+
// TestVector -
29+
type TestVector struct {
30+
name string
31+
password string
32+
wantErr bool
33+
errMsg string
34+
}
35+
36+
// Pattern definition (requirements and reject rules)
2737
var testRequirements []Rule = []Rule{
2838
{
2939
description: "Must contain at least one digit",
@@ -50,6 +60,201 @@ var testRejects []Rule = []Rule{
5060
},
5161
}
5262

63+
// Input TestVectors
64+
// 1. Fernet pattern
65+
// 2. alphaNumPattern
66+
// 3. validPattern
67+
// 4. Invalid patterns
68+
var fernetPattern []TestVector = []TestVector{
69+
// Fernet pattern:
70+
// python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode(' UTF-8'))";
71+
{
72+
name: "Fernet Pattern Test 1",
73+
password: "dzUSgMefMDi3Mrq-PF8p4lHoOdF-ps_oDQB9-KzS-j0=",
74+
wantErr: false,
75+
},
76+
{
77+
name: "Fernet Pattern Test 2",
78+
password: "7Q0cfVtxqzZMKWhY5LJQF4sImYyOvC_BYYd-yg2GvUg=",
79+
wantErr: false,
80+
},
81+
{
82+
name: "Fernet Pattern Test 3",
83+
password: "UjQSpLh2WGEIFZ1Y-QCSiUr4aE76Wu3YdYLTStyEK1c=",
84+
wantErr: false,
85+
},
86+
{
87+
name: "Fernet Pattern Test 4",
88+
password: "UWtR_BCTgszn2kDhz_yxBoxxiHytMB1IR0t200uRD2s=",
89+
wantErr: false,
90+
},
91+
{
92+
name: "Fernet Pattern Test 5",
93+
password: "7cXj_fYimZ1WNu_87kNj4WM6JaI2KqCL3In2WZhAD7I=",
94+
wantErr: false,
95+
},
96+
}
97+
98+
var alphaNumPattern []TestVector = []TestVector{
99+
// $(tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 32 | base64)
100+
{
101+
name: "Default pattern 1",
102+
password: "QTFkZ1U2VnF3RWdXWjZuQW9teUo2dHlEUk43UWtaOHM=",
103+
wantErr: false,
104+
},
105+
{
106+
name: "Default pattern 2",
107+
password: "SlBVUm9OUVhEOWpjOHJsNkJmTENNbnlENjJvMU5yc1A=",
108+
wantErr: false,
109+
},
110+
{
111+
name: "Default pattern 3",
112+
password: "QzA5ejh5a1ZoWVIxbk1LUmZ4elJNMVBQNFNVdzdhaG0=",
113+
wantErr: false,
114+
},
115+
{
116+
name: "Default pattern 4",
117+
password: "ckIwWk1MUnRsSmtqdGFseThmRkYyUlhUUDdXUXhOcmc=",
118+
wantErr: false,
119+
},
120+
{
121+
name: "Default pattern 5",
122+
password: "aUxHdDRDcFR4amM2MXg4bVBoaGU2blBJRHFER3hoMFU=",
123+
wantErr: false,
124+
},
125+
}
126+
127+
var validPattern []TestVector = []TestVector{
128+
// Valid password scenarios
129+
{
130+
name: "valid password with all requirements",
131+
password: "Password123",
132+
wantErr: false,
133+
},
134+
{
135+
name: "valid password with minimum length",
136+
password: "Abcdef12",
137+
wantErr: false,
138+
},
139+
{
140+
name: "valid password with longer length",
141+
password: "MySecurePassword123",
142+
wantErr: false,
143+
},
144+
{
145+
name: "valid password with allowed special characters",
146+
password: "Password123!@+=._-",
147+
wantErr: false,
148+
},
149+
{
150+
name: "valid password with semicolon",
151+
password: "Password123;allowed",
152+
wantErr: false,
153+
},
154+
{
155+
name: "valid password with angle brackets",
156+
password: "Password123<valid>",
157+
wantErr: false,
158+
},
159+
{
160+
name: "valid password with caret character",
161+
password: "Password123^valid",
162+
wantErr: false,
163+
},
164+
{
165+
name: "valid password with percent character",
166+
password: "Password123%valid",
167+
wantErr: false,
168+
},
169+
{
170+
name: "valid password with safe metacharacters",
171+
password: "Password123*?{}[]|&~#'\"",
172+
wantErr: false,
173+
},
174+
{
175+
name: "valid password with isolated dollar and number",
176+
password: "Password123$123",
177+
wantErr: false,
178+
},
179+
}
180+
181+
var invalidPattern []TestVector = []TestVector{
182+
// Invalid password scenarios - shell expansion patterns
183+
{
184+
name: "password made by all numbers",
185+
password: "12345678",
186+
wantErr: true,
187+
},
188+
{
189+
name: "empty password",
190+
password: "",
191+
wantErr: true,
192+
errMsg: "empty password not allowed",
193+
},
194+
{
195+
name: "password without uppercase",
196+
password: "password123",
197+
wantErr: true,
198+
},
199+
{
200+
name: "password without lowercase",
201+
password: "PASSWORD123",
202+
wantErr: true,
203+
},
204+
{
205+
name: "password without digit",
206+
password: "PasswordABC",
207+
wantErr: true,
208+
},
209+
{
210+
name: "password too short",
211+
password: "Pass12",
212+
wantErr: true,
213+
},
214+
{
215+
name: "password with variable expansion",
216+
password: "Password123$HOME",
217+
wantErr: true,
218+
errMsg: ErrMsg,
219+
},
220+
{
221+
name: "password with variable expansion underscore",
222+
password: "Password123$USER_NAME",
223+
wantErr: true,
224+
errMsg: ErrMsg,
225+
},
226+
{
227+
name: "password with braced variable expansion",
228+
password: "Password123${HOME}",
229+
wantErr: true,
230+
errMsg: ErrMsg,
231+
},
232+
{
233+
name: "password with command substitution",
234+
password: "Password123$(echo bad)",
235+
wantErr: true,
236+
errMsg: ErrMsg,
237+
},
238+
{
239+
name: "password with backtick command substitution",
240+
password: "Password123`echo bad`",
241+
wantErr: true,
242+
errMsg: ErrMsg,
243+
},
244+
{
245+
name: "password with complex variable expansion",
246+
password: "Password123${VARIABLE_NAME}",
247+
wantErr: true,
248+
errMsg: ErrMsg,
249+
},
250+
{
251+
name: "shell expansion attack example",
252+
password: "c^sometext02%text%text02$someText&",
253+
wantErr: true,
254+
errMsg: ErrMsg,
255+
},
256+
}
257+
53258
func TestValidatePassword(t *testing.T) {
54259
// Save original values
55260
originalRequirements := requirements
@@ -65,139 +270,16 @@ func TestValidatePassword(t *testing.T) {
65270
rejects = originalRejects
66271
}()
67272

68-
tests := []struct {
69-
name string
70-
password string
71-
wantErr bool
72-
errMsg string
73-
}{
74-
// Valid password scenarios
75-
{
76-
name: "valid password with all requirements",
77-
password: "Password123",
78-
wantErr: false,
79-
},
80-
{
81-
name: "valid password with minimum length",
82-
password: "Abcdef12",
83-
wantErr: false,
84-
},
85-
{
86-
name: "valid password with longer length",
87-
password: "MySecurePassword123",
88-
wantErr: false,
89-
},
90-
{
91-
name: "valid password with allowed special characters",
92-
password: "Password123!@+=._-",
93-
wantErr: false,
94-
},
95-
{
96-
name: "valid password with semicolon",
97-
password: "Password123;allowed",
98-
wantErr: false,
99-
},
100-
{
101-
name: "valid password with angle brackets",
102-
password: "Password123<valid>",
103-
wantErr: false,
104-
},
105-
{
106-
name: "valid password with caret character",
107-
password: "Password123^valid",
108-
wantErr: false,
109-
},
110-
{
111-
name: "valid password with percent character",
112-
password: "Password123%valid",
113-
wantErr: false,
114-
},
115-
{
116-
name: "valid password with safe metacharacters",
117-
password: "Password123*?{}[]|&~#'\"",
118-
wantErr: false,
119-
},
120-
{
121-
name: "valid password with isolated dollar and number",
122-
password: "Password123$123",
123-
wantErr: false,
124-
},
125-
126-
// Invalid password scenarios - shell expansion patterns
127-
{
128-
name: "password made by all numbers",
129-
password: "12345678",
130-
wantErr: true,
131-
},
132-
{
133-
name: "empty password",
134-
password: "",
135-
wantErr: true,
136-
errMsg: "empty password not allowed",
137-
},
138-
{
139-
name: "password without uppercase",
140-
password: "password123",
141-
wantErr: true,
142-
},
143-
{
144-
name: "password without lowercase",
145-
password: "PASSWORD123",
146-
wantErr: true,
147-
},
148-
{
149-
name: "password without digit",
150-
password: "PasswordABC",
151-
wantErr: true,
152-
},
153-
{
154-
name: "password too short",
155-
password: "Pass12",
156-
wantErr: true,
157-
},
158-
{
159-
name: "password with variable expansion",
160-
password: "Password123$HOME",
161-
wantErr: true,
162-
errMsg: ErrMsg,
163-
},
164-
{
165-
name: "password with variable expansion underscore",
166-
password: "Password123$USER_NAME",
167-
wantErr: true,
168-
errMsg: ErrMsg,
169-
},
170-
{
171-
name: "password with braced variable expansion",
172-
password: "Password123${HOME}",
173-
wantErr: true,
174-
errMsg: ErrMsg,
175-
},
176-
{
177-
name: "password with command substitution",
178-
password: "Password123$(echo bad)",
179-
wantErr: true,
180-
errMsg: ErrMsg,
181-
},
182-
{
183-
name: "password with backtick command substitution",
184-
password: "Password123`echo bad`",
185-
wantErr: true,
186-
errMsg: ErrMsg,
187-
},
188-
{
189-
name: "password with complex variable expansion",
190-
password: "Password123${VARIABLE_NAME}",
191-
wantErr: true,
192-
errMsg: ErrMsg,
193-
},
194-
{
195-
name: "shell expansion attack example",
196-
password: "c^sometext02%text%text02$someText&",
197-
wantErr: true,
198-
errMsg: ErrMsg,
199-
},
200-
}
273+
// Build TestVector
274+
var tests []TestVector
275+
tests = slices.Concat(
276+
tests,
277+
validPattern,
278+
invalidPattern,
279+
alphaNumPattern,
280+
fernetPattern,
281+
)
282+
// Execute ValidatePassword against the generated TestVector
201283
for _, tt := range tests {
202284
t.Run(tt.name, func(t *testing.T) {
203285
g := NewWithT(t)

0 commit comments

Comments
 (0)