@@ -19,11 +19,21 @@ package secret
1919import (
2020 . "github.com/onsi/gomega" // nolint:revive
2121 "regexp"
22+ "slices"
2223 "testing"
2324)
2425
2526const 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)
2737var 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+
53258func 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