git.iko.soy/typeable/oauth2-proxy
on master help sign in
Add tests for cookie validation

This also removes the check for the decoded from the valid secret size check. The code was unreachable because encryption.SecretBytes will only return the decoded secret if it was the right length after decoding.

commit285c65a2d4e9117a25361db2010e15a66b808233 authorJoel Speed <joel.speed@hotmail.co.uk> dateMon 25 May 2020, 12:01 (6 years ago) parent900061b
3 files changed+253−25
pkg/validation/cookie.go+3−10 pkg/validation/cookie_test.go+250 pkg/validation/options_test.go−15
pkg/validation/cookie.go+3−10
@@ -55,15 +55,8 @@ func validateCookieSecret(secret string) []string {
5555 return []string{}
5656 }
5757 // Invalid secret size found, return a message
58−
59− // If the secretBytes is different to the raw secret, it was decoded from Base64
60− // Add a note to the error message
61− var decodedSuffix string
62− if string(secretBytes) != secret {
63− decodedSuffix = " note: cookie secret was base64 decoded"
64− }
65−
6658 return []string{fmt.Sprintf(
67− "cookie_secret must be 16, 24, or 32 bytes to create an AES cipher, but is %d bytes.%s",
68− len(secretBytes), decodedSuffix)}
59+ "cookie_secret must be 16, 24, or 32 bytes to create an AES cipher, but is %d bytes",
60+ len(secretBytes)),
61+ }
6962}
pkg/validation/cookie_test.gonew file+250
@@ -0,0 +1,250 @@
1+package validation
2+
3+import (
4+ "testing"
5+ "time"
6+
7+ "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options"
8+ . "github.com/onsi/gomega"
9+)
10+
11+func TestValidateCookie(t *testing.T) {
12+ validName := "_oauth2_proxy"
13+ invalidName := "_oauth2;proxy" // Separater character not allowed
14+ validSecret := "secretthirtytwobytes+abcdefghijk"
15+ invalidSecret := "abcdef" // 6 bytes is not a valid size
16+ validBase64Secret := "c2VjcmV0dGhpcnR5dHdvYnl0ZXMrYWJjZGVmZ2hpams" // Base64 encoding of "secretthirtytwobytes+abcdefghijk"
17+ invalidBase64Secret := "YWJjZGVmCg" // Base64 encoding of "abcdef"
18+ emptyDomains := []string{}
19+ domains := []string{
20+ "a.localhost",
21+ "ba.localhost",
22+ "ca.localhost",
23+ "cba.localhost",
24+ "a.cba.localhost",
25+ }
26+
27+ invalidNameMsg := "invalid cookie name: \"_oauth2;proxy\""
28+ missingSecretMsg := "missing setting: cookie-secret"
29+ invalidSecretMsg := "cookie_secret must be 16, 24, or 32 bytes to create an AES cipher, but is 6 bytes"
30+ invalidBase64SecretMsg := "cookie_secret must be 16, 24, or 32 bytes to create an AES cipher, but is 10 bytes"
31+ refreshLongerThanExpireMsg := "cookie_refresh (\"1h0m0s\") must be less than cookie_expire (\"15m0s\")"
32+ invalidSameSiteMsg := "cookie_samesite (\"invalid\") must be one of ['', 'lax', 'strict', 'none']"
33+
34+ testCases := []struct {
35+ name string
36+ cookie options.CookieOptions
37+ errStrings []string
38+ }{
39+ {
40+ name: "with valid configuration",
41+ cookie: options.CookieOptions{
42+ Name: validName,
43+ Secret: validSecret,
44+ Domains: domains,
45+ Path: "",
46+ Expire: time.Hour,
47+ Refresh: 15 * time.Minute,
48+ Secure: true,
49+ HTTPOnly: false,
50+ SameSite: "",
51+ },
52+ errStrings: []string{},
53+ },
54+ {
55+ name: "with no cookie secret",
56+ cookie: options.CookieOptions{
57+ Name: validName,
58+ Secret: "",
59+ Domains: emptyDomains,
60+ Path: "",
61+ Expire: time.Hour,
62+ Refresh: 15 * time.Minute,
63+ Secure: true,
64+ HTTPOnly: false,
65+ SameSite: "",
66+ },
67+ errStrings: []string{
68+ missingSecretMsg,
69+ },
70+ },
71+ {
72+ name: "with an invalid cookie secret",
73+ cookie: options.CookieOptions{
74+ Name: validName,
75+ Secret: invalidSecret,
76+ Domains: emptyDomains,
77+ Path: "",
78+ Expire: time.Hour,
79+ Refresh: 15 * time.Minute,
80+ Secure: true,
81+ HTTPOnly: false,
82+ SameSite: "",
83+ },
84+ errStrings: []string{
85+ invalidSecretMsg,
86+ },
87+ },
88+ {
89+ name: "with a valid Base64 secret",
90+ cookie: options.CookieOptions{
91+ Name: validName,
92+ Secret: validBase64Secret,
93+ Domains: emptyDomains,
94+ Path: "",
95+ Expire: time.Hour,
96+ Refresh: 15 * time.Minute,
97+ Secure: true,
98+ HTTPOnly: false,
99+ SameSite: "",
100+ },
101+ errStrings: []string{},
102+ },
103+ {
104+ name: "with an invalid Base64 secret",
105+ cookie: options.CookieOptions{
106+ Name: validName,
107+ Secret: invalidBase64Secret,
108+ Domains: emptyDomains,
109+ Path: "",
110+ Expire: time.Hour,
111+ Refresh: 15 * time.Minute,
112+ Secure: true,
113+ HTTPOnly: false,
114+ SameSite: "",
115+ },
116+ errStrings: []string{
117+ invalidBase64SecretMsg,
118+ },
119+ },
120+ {
121+ name: "with an invalid name",
122+ cookie: options.CookieOptions{
123+ Name: invalidName,
124+ Secret: validSecret,
125+ Domains: emptyDomains,
126+ Path: "",
127+ Expire: time.Hour,
128+ Refresh: 15 * time.Minute,
129+ Secure: true,
130+ HTTPOnly: false,
131+ SameSite: "",
132+ },
133+ errStrings: []string{
134+ invalidNameMsg,
135+ },
136+ },
137+ {
138+ name: "with refresh longer than expire",
139+ cookie: options.CookieOptions{
140+ Name: validName,
141+ Secret: validSecret,
142+ Domains: emptyDomains,
143+ Path: "",
144+ Expire: 15 * time.Minute,
145+ Refresh: time.Hour,
146+ Secure: true,
147+ HTTPOnly: false,
148+ SameSite: "",
149+ },
150+ errStrings: []string{
151+ refreshLongerThanExpireMsg,
152+ },
153+ },
154+ {
155+ name: "with samesite \"none\"",
156+ cookie: options.CookieOptions{
157+ Name: validName,
158+ Secret: validSecret,
159+ Domains: emptyDomains,
160+ Path: "",
161+ Expire: time.Hour,
162+ Refresh: 15 * time.Minute,
163+ Secure: true,
164+ HTTPOnly: false,
165+ SameSite: "none",
166+ },
167+ errStrings: []string{},
168+ },
169+ {
170+ name: "with samesite \"lax\"",
171+ cookie: options.CookieOptions{
172+ Name: validName,
173+ Secret: validSecret,
174+ Domains: emptyDomains,
175+ Path: "",
176+ Expire: time.Hour,
177+ Refresh: 15 * time.Minute,
178+ Secure: true,
179+ HTTPOnly: false,
180+ SameSite: "none",
181+ },
182+ errStrings: []string{},
183+ },
184+ {
185+ name: "with samesite \"strict\"",
186+ cookie: options.CookieOptions{
187+ Name: validName,
188+ Secret: validSecret,
189+ Domains: emptyDomains,
190+ Path: "",
191+ Expire: time.Hour,
192+ Refresh: 15 * time.Minute,
193+ Secure: true,
194+ HTTPOnly: false,
195+ SameSite: "none",
196+ },
197+ errStrings: []string{},
198+ },
199+ {
200+ name: "with samesite \"invalid\"",
201+ cookie: options.CookieOptions{
202+ Name: validName,
203+ Secret: validSecret,
204+ Domains: emptyDomains,
205+ Path: "",
206+ Expire: time.Hour,
207+ Refresh: 15 * time.Minute,
208+ Secure: true,
209+ HTTPOnly: false,
210+ SameSite: "invalid",
211+ },
212+ errStrings: []string{
213+ invalidSameSiteMsg,
214+ },
215+ },
216+ {
217+ name: "with a combination of configuration errors",
218+ cookie: options.CookieOptions{
219+ Name: invalidName,
220+ Secret: invalidSecret,
221+ Domains: domains,
222+ Path: "",
223+ Expire: 15 * time.Minute,
224+ Refresh: time.Hour,
225+ Secure: true,
226+ HTTPOnly: false,
227+ SameSite: "invalid",
228+ },
229+ errStrings: []string{
230+ invalidNameMsg,
231+ invalidSecretMsg,
232+ refreshLongerThanExpireMsg,
233+ invalidSameSiteMsg,
234+ },
235+ },
236+ }
237+
238+ for _, tc := range testCases {
239+ t.Run(tc.name, func(t *testing.T) {
240+ errStrings := validateCookieOptions(tc.cookie)
241+ g := NewWithT(t)
242+
243+ g.Expect(errStrings).To(ConsistOf(tc.errStrings))
244+ // Check domains were sorted to the right lengths
245+ for i := 0; i < len(tc.cookie.Domains)-1; i++ {
246+ g.Expect(len(tc.cookie.Domains[i])).To(BeNumerically(">=", len(tc.cookie.Domains[i+1])))
247+ }
248+ })
249+ }
250+}
pkg/validation/options_test.go−15
@@ -2,7 +2,6 @@ package validation
22
33import (
44 "crypto"
5− "fmt"
65 "io/ioutil"
76 "net/url"
87 "os"
@@ -291,20 +290,6 @@ func TestValidateSignatureKeyUnsupportedAlgorithm(t *testing.T) {
291290 " unsupported signature hash algorithm: "+o.SignatureKey)
292291}
293292
294−func TestValidateCookie(t *testing.T) {
295− o := testOptions()
296− o.Cookie.Name = "_valid_cookie_name"
297− assert.Equal(t, nil, Validate(o))
298−}
299−
300−func TestValidateCookieBadName(t *testing.T) {
301− o := testOptions()
302− o.Cookie.Name = "_bad_cookie_name{}"
303− err := Validate(o)
304− assert.Equal(t, err.Error(), "invalid configuration:\n"+
305− fmt.Sprintf(" invalid cookie name: %q", o.Cookie.Name))
306−}
307−
308293func TestSkipOIDCDiscovery(t *testing.T) {
309294 o := testOptions()
310295 o.ProviderType = "oidc"