github.com/blend/go-sdk@v1.20220411.3/envoyutil/xfcc_test.go (about) 1 /* 2 3 Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file. 5 6 */ 7 8 package envoyutil_test 9 10 import ( 11 "crypto/x509" 12 "fmt" 13 "net/url" 14 "regexp" 15 "testing" 16 17 sdkAssert "github.com/blend/go-sdk/assert" 18 "github.com/blend/go-sdk/certutil" 19 "github.com/blend/go-sdk/ex" 20 21 "github.com/blend/go-sdk/envoyutil" 22 ) 23 24 const ( 25 fullXFCCTest = `By=spiffe://cluster.local/ns/blend/sa/yule;Hash=468ed33be74eee6556d90c0149c1309e9ba61d6425303443c0748a02dd8de688;Subject="/C=US/ST=CA/L=San Francisco/OU=Lyft/CN=Test Client";URI=spiffe://cluster.local/ns/blend/sa/cheer` 26 xfccElementByTest = `By=spiffe://cluster.local/ns/blend/sa/tide` 27 xfccElementHashTest = `Hash=468ed33be74eee6556d90c0149c1309e9ba61d6425303443c0748a02dd8de688` 28 xfccElementCertTest = `Cert=` + xfccElementTestCertEncoded 29 xfccElementChainTest = `CHAIN=` + xfccElementTestCertEncoded 30 xfccElementSubjectTest = `SUBJECT="/C=US/ST=CA/L=San Francisco/OU=Lyft/CN=Test Client"` 31 xfccElementURITest = `URI=spiffe://cluster.local/ns/blend/sa/quasar` 32 xfccElementDNSTest = `dns=http://frontend.lyft.com` 33 xfccElementEndTest = `dns=http://frontend.lyft.com;` 34 xfccElementNoneTest = `key=value;dns=http://frontend.lyft.com` 35 xfccElementMultiTest = `By=spiffe://cluster.local/ns/blend/sa/laser;Hash=468ed33be74eee6556d90c0149c1309e9ba61d6425303443c0748a02dd8de688` 36 xfccElementMalformedKeyTest = `=value` 37 xfccElementMultiMalformedKeyTest = `=value;dns=http://frontend.lyft.com` 38 xfccElementMultiCertTest = `cert=` + xfccElementTestCertEncoded + xfccElementTestCert 39 40 xfccElementMalformedEncoding = "%" 41 42 xfccElementTestCertEncoded = `-----BEGIN%20CERTIFICATE-----%0AMIIFKjCCAxICCQCA5%2FOCxg%2FqiDANBgkqhkiG9w0BAQsFADBXMQswCQYDVQQGEwJV%0AUzELMAkGA1UECAwCQ0ExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTALBgNVBAoM%0ABEx5ZnQxFDASBgNVBAMMC1Rlc3QgQ2xpZW50MB4XDTIwMDYwNDE3NDkzNVoXDTIx%0AMDYwNDE3NDkzNVowVzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRYwFAYDVQQH%0ADA1TYW4gRnJhbmNpc2NvMQ0wCwYDVQQKDARMeWZ0MRQwEgYDVQQDDAtUZXN0IENs%0AaWVudDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKs6T8vcb8rIIkC4%0Aiz9h%2FOj6Iv%2BfazTLwNLK%2Fk58Ape5ZL0IdW6h8pWDlGnGz4X%2FTaJ5TwlamFo1h62v%0AsR8HPNOoLY0wmC2qHVquPF6eR9Lt5ejJiakr%2BYvf%2BU6LHXlOpOoot5rcTGoGBCf0%0AH3zmjdOE0o6hwJxMf54XQEVwNXqRrIDbY27mYS8eAVcSMrPUQVZ%2B3Vk1S56Imybz%0Adegi79IIoc6TzE5M7ChfJZBNNNZT08haJe6Oi%2FIgZhK3IexssY%2BQyD5uBSc7Mpas%0A6TstzeevIbeFy3Od2GhUy2Hz98qW%2FoO5iuerEArkNs4lB0J%2F0ARPHUDnmmH%2BqWYF%0APKealq2yEyXHHXrhDcSK%2FN5R64pp%2FVrxEas1qG20%2FCG4rixv36UJuEz5oUKNWyaR%0A268EI5Vecw%2BpK%2F0XC2%2Bhra9T%2FeP9JH0Fp43x7bdpQoxph8ZJZBsjbgCFMonf3ku1%0A9n74%2FxwvV6B0wp5C8jpwbGa85n%2BT8hogtO78mnpvxhTVJ7TOy596tI2apJ02edtD%0AJgsJV9MfZ%2FfGu3QZ6yN3rKVMPkZfC18cK04xy%2BroPo756CHkUHP5cz%2BKtJ7%2B8COR%0ArPDPxKBLOqwaSFcanQNONFIrffnZciiisCxjMHGoM4%2Fuix5gStlDC9%2FM5yyHt9He%0AldC8xL%2FyIalsa9Df7SL59Fd7T2JrAgMBAAEwDQYJKoZIhvcNAQELBQADggIBAGTb%0AOTddb6Yr37JigDYGKxSiidWfPgxTFr3DWTTE2aBhTi8K1Gr8Br1FAGRg4P114kxm%0AQBx3TxuCZcssMX2Z3GbL20DrHNExCl7hM%2FZVA2OCVhwfXGOHRQ6lcpmeQISWDNsN%0Atanlap%2FAgqKN%2F6At6JEYmuTSJnKc4Bfgk2GP5LPa63yJOlyvFb8ovKsCgb1ppVyw%0ARE%2B7AmB2DfDdVql4nHsDh5UBZRgVxMZ6xGnkYKaAUDKl4slejvKwXuzu2Xf%2BAd74%0AgjdLHzP0WmHlAggR5LIv%2F9xlvrsKCrNDDxWwOGeYk2WZl%2Fybud0RFKhLIqbbeMy7%0ADcdy04cJcqa9qRHYySgaWtM6Ab%2Fx9CJqdzR2NQZNnLgk6Vc3%2BoDjXMUuyM17WJAS%0ArenwJvanXvF9P1yPMByJQlXxkUehkCa%2FPs7E1O%2F%2BE2FJnvrtGVdYVR8Otbec1osS%0AmtJC6k7rgMhgvk63sCqQqaZwRWwLl2R5XcDZknUiqDKjuVHHA01II7jtGB1oyEIH%0Asp%2FrQlLNeyYlyhAlc3MhF5hu6nUjH%2B2%2BDuIHJsM0mEF0rjlbnp4bKJ%2FgF1COAIAL%0APzu2qAC%2BaOFldCmRonqUluayv6fQaQCeeh8sW2IjNVjA2ynKn2ybGIXH4mrH0KVa%0AJmUY%2B1YGMn7qbeHTma33N28Ec7hK%2BWByul746Nro%0A-----END%20CERTIFICATE-----` 43 xfccElementTestCert = `-----BEGIN CERTIFICATE----- 44 MIIFKjCCAxICCQCA5/OCxg/qiDANBgkqhkiG9w0BAQsFADBXMQswCQYDVQQGEwJV 45 UzELMAkGA1UECAwCQ0ExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTALBgNVBAoM 46 BEx5ZnQxFDASBgNVBAMMC1Rlc3QgQ2xpZW50MB4XDTIwMDYwNDE3NDkzNVoXDTIx 47 MDYwNDE3NDkzNVowVzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRYwFAYDVQQH 48 DA1TYW4gRnJhbmNpc2NvMQ0wCwYDVQQKDARMeWZ0MRQwEgYDVQQDDAtUZXN0IENs 49 aWVudDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKs6T8vcb8rIIkC4 50 iz9h/Oj6Iv+fazTLwNLK/k58Ape5ZL0IdW6h8pWDlGnGz4X/TaJ5TwlamFo1h62v 51 sR8HPNOoLY0wmC2qHVquPF6eR9Lt5ejJiakr+Yvf+U6LHXlOpOoot5rcTGoGBCf0 52 H3zmjdOE0o6hwJxMf54XQEVwNXqRrIDbY27mYS8eAVcSMrPUQVZ+3Vk1S56Imybz 53 degi79IIoc6TzE5M7ChfJZBNNNZT08haJe6Oi/IgZhK3IexssY+QyD5uBSc7Mpas 54 6TstzeevIbeFy3Od2GhUy2Hz98qW/oO5iuerEArkNs4lB0J/0ARPHUDnmmH+qWYF 55 PKealq2yEyXHHXrhDcSK/N5R64pp/VrxEas1qG20/CG4rixv36UJuEz5oUKNWyaR 56 268EI5Vecw+pK/0XC2+hra9T/eP9JH0Fp43x7bdpQoxph8ZJZBsjbgCFMonf3ku1 57 9n74/xwvV6B0wp5C8jpwbGa85n+T8hogtO78mnpvxhTVJ7TOy596tI2apJ02edtD 58 JgsJV9MfZ/fGu3QZ6yN3rKVMPkZfC18cK04xy+roPo756CHkUHP5cz+KtJ7+8COR 59 rPDPxKBLOqwaSFcanQNONFIrffnZciiisCxjMHGoM4/uix5gStlDC9/M5yyHt9He 60 ldC8xL/yIalsa9Df7SL59Fd7T2JrAgMBAAEwDQYJKoZIhvcNAQELBQADggIBAGTb 61 OTddb6Yr37JigDYGKxSiidWfPgxTFr3DWTTE2aBhTi8K1Gr8Br1FAGRg4P114kxm 62 QBx3TxuCZcssMX2Z3GbL20DrHNExCl7hM/ZVA2OCVhwfXGOHRQ6lcpmeQISWDNsN 63 tanlap/AgqKN/6At6JEYmuTSJnKc4Bfgk2GP5LPa63yJOlyvFb8ovKsCgb1ppVyw 64 RE+7AmB2DfDdVql4nHsDh5UBZRgVxMZ6xGnkYKaAUDKl4slejvKwXuzu2Xf+Ad74 65 gjdLHzP0WmHlAggR5LIv/9xlvrsKCrNDDxWwOGeYk2WZl/ybud0RFKhLIqbbeMy7 66 Dcdy04cJcqa9qRHYySgaWtM6Ab/x9CJqdzR2NQZNnLgk6Vc3+oDjXMUuyM17WJAS 67 renwJvanXvF9P1yPMByJQlXxkUehkCa/Ps7E1O/+E2FJnvrtGVdYVR8Otbec1osS 68 mtJC6k7rgMhgvk63sCqQqaZwRWwLl2R5XcDZknUiqDKjuVHHA01II7jtGB1oyEIH 69 sp/rQlLNeyYlyhAlc3MhF5hu6nUjH+2+DuIHJsM0mEF0rjlbnp4bKJ/gF1COAIAL 70 Pzu2qAC+aOFldCmRonqUluayv6fQaQCeeh8sW2IjNVjA2ynKn2ybGIXH4mrH0KVa 71 JmUY+1YGMn7qbeHTma33N28Ec7hK+WByul746Nro 72 -----END CERTIFICATE-----` 73 ) 74 75 func TestXFCCElementDecodeBy(t *testing.T) { 76 assert := sdkAssert.New(t) 77 78 type testCase struct { 79 By string 80 Expected *url.URL 81 ErrorRegexp *regexp.Regexp 82 } 83 testCases := []testCase{ 84 {By: "", Expected: &url.URL{}}, 85 // NOTE: Regex needed to support error format changes from go1.13 to go1.14 86 {By: "\n", ErrorRegexp: regexp.MustCompile(`(?m)^parse ("\\n"|\n): net/url: invalid control character in URL$`)}, 87 { 88 By: "spiffe://cluster.local/ns/blend/sa/yule", 89 Expected: &url.URL{ 90 Scheme: "spiffe", 91 Host: "cluster.local", 92 Path: "/ns/blend/sa/yule", 93 }, 94 }, 95 } 96 for _, tc := range testCases { 97 xe := envoyutil.XFCCElement{By: tc.By} 98 uri, err := xe.DecodeBy() 99 assert.Equal(tc.Expected, uri) 100 if tc.ErrorRegexp != nil { 101 asEx, ok := err.(*ex.Ex) 102 assert.True(ok) 103 assert.Equal(envoyutil.ErrXFCCParsing, asEx.Class) 104 assert.True(tc.ErrorRegexp.MatchString(asEx.Inner.Error())) 105 } else { 106 assert.Nil(err) 107 } 108 } 109 } 110 111 func TestXFCCElementDecodeHash(t *testing.T) { 112 assert := sdkAssert.New(t) 113 114 type testCase struct { 115 Hash string 116 Expected []byte 117 Error string 118 } 119 testCases := []testCase{ 120 {Hash: "", Expected: []byte("")}, 121 {Hash: "41434944", Expected: []byte("ACID")}, 122 {Hash: "x", Expected: nil, Error: "Error Parsing X-Forwarded-Client-Cert\nencoding/hex: invalid byte: U+0078 'x'"}, 123 } 124 for _, tc := range testCases { 125 xe := envoyutil.XFCCElement{Hash: tc.Hash} 126 decoded, err := xe.DecodeHash() 127 assert.Equal(tc.Expected, decoded) 128 if tc.Error != "" { 129 assert.Equal(tc.Error, fmt.Sprintf("%v", err)) 130 } else { 131 assert.Nil(err) 132 } 133 } 134 } 135 136 func TestXFCCElementDecodeCert(t *testing.T) { 137 assert := sdkAssert.New(t) 138 139 parsedCert, err := certutil.ParseCertPEM([]byte(xfccElementTestCert)) 140 assert.Nil(err) 141 142 type testCase struct { 143 Cert string 144 Parsed *x509.Certificate 145 Error string 146 } 147 testCases := []testCase{ 148 {Cert: ""}, 149 {Cert: xfccElementTestCertEncoded, Parsed: parsedCert[0]}, 150 {Cert: "%", Error: "Error Parsing X-Forwarded-Client-Cert\ninvalid URL escape \"%\""}, 151 { 152 Cert: "-----BEGIN CERTIFICATE-----\nnope\n-----END CERTIFICATE-----\n", 153 Error: "Error Parsing X-Forwarded-Client-Cert\nx509: malformed certificate", 154 }, 155 { 156 Cert: url.QueryEscape(xfccElementTestCert + "\n" + xfccElementTestCert), 157 Error: "Error Parsing X-Forwarded-Client-Cert; Incorrect number of certificates; expected 1 got 2", 158 }, 159 { 160 Cert: xfccElementMultiCertTest, 161 Error: "Error Parsing X-Forwarded-Client-Cert; Incorrect number of certificates; expected 1 got 0", 162 }, 163 } 164 165 for _, tc := range testCases { 166 xe := envoyutil.XFCCElement{Cert: tc.Cert} 167 cert, err := xe.DecodeCert() 168 if tc.Error != "" { 169 assert.Equal(tc.Error, fmt.Sprintf("%v", err)) 170 } else { 171 assert.Nil(err) 172 } 173 assert.Equal(tc.Parsed, cert) 174 } 175 } 176 177 func TestXFCCElementDecodeChain(t *testing.T) { 178 assert := sdkAssert.New(t) 179 180 parsedCerts, err := certutil.ParseCertPEM([]byte(xfccElementTestCert + "\n" + xfccElementTestCert)) 181 assert.Nil(err) 182 183 type testCase struct { 184 Chain string 185 Parsed []*x509.Certificate 186 Error string 187 } 188 testCases := []testCase{ 189 {Chain: ""}, 190 { 191 Chain: url.QueryEscape(xfccElementTestCert + "\n" + xfccElementTestCert), 192 Parsed: parsedCerts, 193 }, 194 {Chain: "%", Error: "Error Parsing X-Forwarded-Client-Cert\ninvalid URL escape \"%\""}, 195 { 196 Chain: "-----BEGIN CERTIFICATE-----\nnope\n-----END CERTIFICATE-----\n", 197 Error: "Error Parsing X-Forwarded-Client-Cert\nx509: malformed certificate", 198 }, 199 } 200 201 for _, tc := range testCases { 202 xe := envoyutil.XFCCElement{Chain: tc.Chain} 203 chain, err := xe.DecodeChain() 204 if tc.Error != "" { 205 assert.Equal(tc.Error, fmt.Sprintf("%v", err)) 206 } else { 207 assert.Nil(err) 208 } 209 assert.Equal(tc.Parsed, chain) 210 } 211 } 212 213 func TestXFCCElementDecodeURI(t *testing.T) { 214 assert := sdkAssert.New(t) 215 216 type testCase struct { 217 URI string 218 Expected *url.URL 219 ErrorRegexp *regexp.Regexp 220 } 221 testCases := []testCase{ 222 {URI: "", Expected: &url.URL{}}, 223 // NOTE: Regex needed to support error format changes from go1.13 to go1.14 224 {URI: "\r", ErrorRegexp: regexp.MustCompile(`(?m)^parse ("\\r"|\r): net/url: invalid control character in URL$`)}, 225 { 226 URI: "spiffe://cluster.local/ns/first/sa/furst", 227 Expected: &url.URL{ 228 Scheme: "spiffe", 229 Host: "cluster.local", 230 Path: "/ns/first/sa/furst", 231 }, 232 }, 233 } 234 for _, tc := range testCases { 235 xe := envoyutil.XFCCElement{URI: tc.URI} 236 uri, err := xe.DecodeURI() 237 assert.Equal(tc.Expected, uri) 238 if tc.ErrorRegexp != nil { 239 asEx, ok := err.(*ex.Ex) 240 assert.True(ok) 241 assert.Equal(envoyutil.ErrXFCCParsing, asEx.Class) 242 assert.True(tc.ErrorRegexp.MatchString(asEx.Inner.Error())) 243 } else { 244 assert.Nil(err) 245 } 246 } 247 } 248 249 func TestXFCCElementString(t *testing.T) { 250 assert := sdkAssert.New(t) 251 252 type testCase struct { 253 Element envoyutil.XFCCElement 254 Expected string 255 } 256 testCases := []testCase{ 257 {Element: envoyutil.XFCCElement{By: "hi"}, Expected: "By=hi"}, 258 {Element: envoyutil.XFCCElement{Hash: "1389ab1"}, Expected: "Hash=1389ab1"}, 259 {Element: envoyutil.XFCCElement{Cert: "anything-goes"}, Expected: "Cert=anything-goes"}, 260 {Element: envoyutil.XFCCElement{Chain: "anything-goes"}, Expected: "Chain=anything-goes"}, 261 { 262 Element: envoyutil.XFCCElement{Subject: "OU=Blent/CN=Test Client"}, 263 Expected: `Subject="OU=Blent/CN=Test Client"`, 264 }, 265 {Element: envoyutil.XFCCElement{URI: "bye"}, Expected: "URI=bye"}, 266 { 267 Element: envoyutil.XFCCElement{DNS: []string{"web.invalid", "bye.invalid"}}, 268 Expected: "DNS=web.invalid;DNS=bye.invalid", 269 }, 270 { 271 Element: envoyutil.XFCCElement{By: "a,b=10", URI: `c; "then" again`}, 272 Expected: "By=\"a,b=10\";URI=\"c; \\\"then\\\" again\"", 273 }, 274 } 275 for _, tc := range testCases { 276 asString := tc.Element.String() 277 assert.Equal(tc.Expected, asString) 278 parsed, err := envoyutil.ParseXFCC(asString) 279 assert.Nil(err) 280 assert.Equal(envoyutil.XFCC{tc.Element}, parsed) 281 } 282 283 element := envoyutil.XFCCElement{} 284 asString := element.String() 285 assert.Equal("", asString) 286 } 287 288 func TestParseXFCC(t *testing.T) { 289 assert := sdkAssert.New(t) 290 291 ele, err := envoyutil.ParseXFCC(xfccElementByTest) 292 assert.Nil(err) 293 expected := envoyutil.XFCC{ 294 envoyutil.XFCCElement{By: "spiffe://cluster.local/ns/blend/sa/tide"}, 295 } 296 assert.Equal(expected, ele) 297 298 ele, err = envoyutil.ParseXFCC(xfccElementByTest + ";" + xfccElementByTest) 299 except, ok := err.(*ex.Ex) 300 assert.True(ok) 301 assert.NotNil(except) 302 expectedErr := &ex.Ex{ 303 Class: envoyutil.ErrXFCCParsing, 304 Message: `Key already encountered "by"`, 305 StackTrace: except.StackTrace, 306 } 307 assert.Equal(expectedErr, except) 308 assert.Equal(envoyutil.XFCC{}, ele) 309 310 ele, err = envoyutil.ParseXFCC(xfccElementHashTest) 311 assert.Nil(err) 312 expected = envoyutil.XFCC{ 313 envoyutil.XFCCElement{ 314 Hash: "468ed33be74eee6556d90c0149c1309e9ba61d6425303443c0748a02dd8de688", 315 }, 316 } 317 assert.Equal(expected, ele) 318 319 ele, err = envoyutil.ParseXFCC(xfccElementHashTest + ";" + xfccElementHashTest) 320 except, ok = err.(*ex.Ex) 321 assert.True(ok) 322 assert.NotNil(except) 323 expectedErr = &ex.Ex{ 324 Class: envoyutil.ErrXFCCParsing, 325 Message: `Key already encountered "hash"`, 326 StackTrace: except.StackTrace, 327 } 328 assert.Equal(expectedErr, except) 329 assert.Equal(envoyutil.XFCC{}, ele) 330 331 ele, err = envoyutil.ParseXFCC(xfccElementCertTest) 332 assert.Nil(err) 333 expected = envoyutil.XFCC{ 334 envoyutil.XFCCElement{ 335 Cert: xfccElementTestCertEncoded, 336 }, 337 } 338 assert.Equal(expected, ele) 339 340 ele, err = envoyutil.ParseXFCC(xfccElementCertTest + ";" + xfccElementCertTest) 341 except, ok = err.(*ex.Ex) 342 assert.True(ok) 343 assert.NotNil(except) 344 expectedErr = &ex.Ex{ 345 Class: envoyutil.ErrXFCCParsing, 346 Message: `Key already encountered "cert"`, 347 StackTrace: except.StackTrace, 348 } 349 assert.Equal(expectedErr, except) 350 assert.Equal(envoyutil.XFCC{}, ele) 351 352 ele, err = envoyutil.ParseXFCC(xfccElementChainTest) 353 assert.Nil(err) 354 expected = envoyutil.XFCC{ 355 envoyutil.XFCCElement{ 356 Chain: xfccElementTestCertEncoded, 357 }, 358 } 359 assert.Equal(expected, ele) 360 361 ele, err = envoyutil.ParseXFCC(xfccElementChainTest + ";" + xfccElementChainTest) 362 except, ok = err.(*ex.Ex) 363 assert.True(ok) 364 assert.NotNil(except) 365 expectedErr = &ex.Ex{ 366 Class: envoyutil.ErrXFCCParsing, 367 Message: `Key already encountered "chain"`, 368 StackTrace: except.StackTrace, 369 } 370 assert.Equal(expectedErr, except) 371 assert.Equal(envoyutil.XFCC{}, ele) 372 373 ele, err = envoyutil.ParseXFCC(xfccElementSubjectTest) 374 assert.Nil(err) 375 expected = envoyutil.XFCC{ 376 envoyutil.XFCCElement{ 377 Subject: "/C=US/ST=CA/L=San Francisco/OU=Lyft/CN=Test Client", 378 }, 379 } 380 assert.Equal(expected, ele) 381 382 ele, err = envoyutil.ParseXFCC(xfccElementSubjectTest + ";" + xfccElementSubjectTest) 383 except, ok = err.(*ex.Ex) 384 assert.True(ok) 385 assert.NotNil(except) 386 expectedErr = &ex.Ex{ 387 Class: envoyutil.ErrXFCCParsing, 388 Message: `Key already encountered "subject"`, 389 StackTrace: except.StackTrace, 390 } 391 assert.Equal(expectedErr, except) 392 assert.Equal(envoyutil.XFCC{}, ele) 393 394 ele, err = envoyutil.ParseXFCC(xfccElementURITest) 395 assert.Nil(err) 396 expected = envoyutil.XFCC{ 397 envoyutil.XFCCElement{ 398 URI: "spiffe://cluster.local/ns/blend/sa/quasar", 399 }, 400 } 401 assert.Equal(expected, ele) 402 403 ele, err = envoyutil.ParseXFCC(xfccElementURITest + ";" + xfccElementURITest) 404 except, ok = err.(*ex.Ex) 405 assert.True(ok) 406 assert.NotNil(except) 407 expectedErr = &ex.Ex{ 408 Class: envoyutil.ErrXFCCParsing, 409 Message: `Key already encountered "uri"`, 410 StackTrace: except.StackTrace, 411 } 412 assert.Equal(expectedErr, except) 413 assert.Equal(envoyutil.XFCC{}, ele) 414 415 ele, err = envoyutil.ParseXFCC(xfccElementDNSTest) 416 assert.Nil(err) 417 expected = envoyutil.XFCC{ 418 envoyutil.XFCCElement{ 419 DNS: []string{"http://frontend.lyft.com"}, 420 }, 421 } 422 assert.Equal(expected, ele) 423 424 ele, err = envoyutil.ParseXFCC("dns=web.invalid;dns=blend.local.invalid") 425 assert.Nil(err) 426 expected = envoyutil.XFCC{ 427 envoyutil.XFCCElement{ 428 DNS: []string{"web.invalid", "blend.local.invalid"}, 429 }, 430 } 431 assert.Equal(expected, ele) 432 433 _, err = envoyutil.ParseXFCC(xfccElementNoneTest) 434 assert.NotNil(err) 435 except, ok = err.(*ex.Ex) 436 assert.True(ok) 437 assert.NotNil(except) 438 assert.Equal(envoyutil.ErrXFCCParsing, except.Class) 439 440 ele, err = envoyutil.ParseXFCC(xfccElementMultiTest) 441 assert.Nil(err) 442 expected = envoyutil.XFCC{ 443 envoyutil.XFCCElement{ 444 By: "spiffe://cluster.local/ns/blend/sa/laser", 445 Hash: "468ed33be74eee6556d90c0149c1309e9ba61d6425303443c0748a02dd8de688", 446 }, 447 } 448 assert.Equal(expected, ele) 449 450 _, err = envoyutil.ParseXFCC(xfccElementMalformedKeyTest) 451 assert.NotNil(err) 452 except, ok = err.(*ex.Ex) 453 assert.True(ok) 454 assert.NotNil(except) 455 assert.Equal(envoyutil.ErrXFCCParsing, except.Class) 456 457 _, err = envoyutil.ParseXFCC(xfccElementMultiMalformedKeyTest) 458 assert.NotNil(err) 459 except, ok = err.(*ex.Ex) 460 assert.True(ok) 461 assert.NotNil(except) 462 assert.Equal(envoyutil.ErrXFCCParsing, except.Class) 463 464 ele, err = envoyutil.ParseXFCC(xfccElementEndTest) 465 except, ok = err.(*ex.Ex) 466 assert.True(ok) 467 assert.NotNil(except) 468 expectedErr = &ex.Ex{ 469 Class: envoyutil.ErrXFCCParsing, 470 Message: "Ends with separator character", 471 StackTrace: except.StackTrace, 472 } 473 assert.Equal(expectedErr, except) 474 assert.Equal(envoyutil.XFCC{}, ele) 475 476 ele, err = envoyutil.ParseXFCC("cert=" + xfccElementMalformedEncoding) 477 assert.Nil(err) 478 expected = envoyutil.XFCC{ 479 envoyutil.XFCCElement{ 480 Cert: "%", 481 }, 482 } 483 assert.Equal(expected, ele) 484 485 ele, err = envoyutil.ParseXFCC("chain=" + xfccElementMalformedEncoding) 486 assert.Nil(err) 487 expected = envoyutil.XFCC{ 488 envoyutil.XFCCElement{ 489 Chain: "%", 490 }, 491 } 492 assert.Equal(expected, ele) 493 494 ele, err = envoyutil.ParseXFCC("=;") 495 assert.NotNil(err) 496 except, ok = err.(*ex.Ex) 497 assert.True(ok) 498 assert.NotNil(except) 499 assert.Equal(envoyutil.ErrXFCCParsing, except.Class) 500 assert.Equal(envoyutil.XFCC{}, ele) 501 502 // Test empty subject 503 ele, err = envoyutil.ParseXFCC(`By=spiffe://cluster.local/ns/blend/sa/protocol;Hash=52114972613efb0820c5e32bfee0f0ee2a84859f7169da6c222300ef852a1129;Subject="";URI=spiffe://cluster.local/ns/blend/sa/world`) 504 assert.Nil(err) 505 expected = envoyutil.XFCC{ 506 envoyutil.XFCCElement{ 507 By: "spiffe://cluster.local/ns/blend/sa/protocol", 508 Hash: "52114972613efb0820c5e32bfee0f0ee2a84859f7169da6c222300ef852a1129", 509 Subject: "", 510 URI: "spiffe://cluster.local/ns/blend/sa/world", 511 }, 512 } 513 assert.Equal(expected, ele) 514 515 // Quoted value with empty key. 516 ele, err = envoyutil.ParseXFCC(`="a";b=20`) 517 assert.Equal(envoyutil.XFCC{}, ele) 518 except, ok = err.(*ex.Ex) 519 assert.True(ok) 520 assert.NotNil(except) 521 expectedErr = &ex.Ex{ 522 Class: envoyutil.ErrXFCCParsing, 523 Message: "Key missing", 524 StackTrace: except.StackTrace, 525 } 526 assert.Equal(expectedErr, except) 527 528 // Quoted value with invalid key. 529 ele, err = envoyutil.ParseXFCC(`wrong="quoted";by=next`) 530 assert.Equal(envoyutil.XFCC{}, ele) 531 except, ok = err.(*ex.Ex) 532 assert.True(ok) 533 assert.NotNil(except) 534 expectedErr = &ex.Ex{ 535 Class: envoyutil.ErrXFCCParsing, 536 Message: `Unknown key "wrong"`, 537 StackTrace: except.StackTrace, 538 } 539 assert.Equal(expectedErr, except) 540 541 // Closing quoted not following by `;` 542 ele, err = envoyutil.ParseXFCC(`a="b"---`) 543 assert.Equal(envoyutil.XFCC{}, ele) 544 except, ok = err.(*ex.Ex) 545 assert.True(ok) 546 assert.NotNil(except) 547 expectedErr = &ex.Ex{ 548 Class: envoyutil.ErrXFCCParsing, 549 Message: "Closing quote not followed by `;`.", 550 StackTrace: except.StackTrace, 551 } 552 assert.Equal(expectedErr, except) 553 554 // Escaped quotes and other characters work as expected. 555 ele, err = envoyutil.ParseXFCC(`By="a,b=10";URI="c; \"then\" again"`) 556 assert.Nil(err) 557 expected = envoyutil.XFCC{ 558 envoyutil.XFCCElement{ 559 By: "a,b=10", 560 URI: `c; "then" again`, 561 }, 562 } 563 assert.Equal(expected, ele) 564 565 // Bare escape character works fine (when not followed by a quote). 566 ele, err = envoyutil.ParseXFCC(`By="first\tsecond"`) 567 assert.Nil(err) 568 expected = envoyutil.XFCC{ 569 envoyutil.XFCCElement{ 570 By: "first\\tsecond", 571 }, 572 } 573 assert.Equal(expected, ele) 574 575 xfcc, err := envoyutil.ParseXFCC(fullXFCCTest + "," + fullXFCCTest) 576 assert.Nil(err) 577 expected = envoyutil.XFCC{ 578 envoyutil.XFCCElement{ 579 By: "spiffe://cluster.local/ns/blend/sa/yule", 580 Hash: "468ed33be74eee6556d90c0149c1309e9ba61d6425303443c0748a02dd8de688", 581 Subject: "/C=US/ST=CA/L=San Francisco/OU=Lyft/CN=Test Client", 582 URI: "spiffe://cluster.local/ns/blend/sa/cheer", 583 }, 584 envoyutil.XFCCElement{ 585 By: "spiffe://cluster.local/ns/blend/sa/yule", 586 Hash: "468ed33be74eee6556d90c0149c1309e9ba61d6425303443c0748a02dd8de688", 587 Subject: "/C=US/ST=CA/L=San Francisco/OU=Lyft/CN=Test Client", 588 URI: "spiffe://cluster.local/ns/blend/sa/cheer", 589 }, 590 } 591 assert.Equal(expected, xfcc) 592 593 ele, err = envoyutil.ParseXFCC(xfccElementMalformedKeyTest) 594 except, ok = err.(*ex.Ex) 595 assert.True(ok) 596 assert.NotNil(except) 597 expectedErr = &ex.Ex{ 598 Class: envoyutil.ErrXFCCParsing, 599 Message: "Key or value found but not both", 600 StackTrace: except.StackTrace, 601 } 602 assert.Equal(expectedErr, except) 603 assert.Equal(envoyutil.XFCC{}, ele) 604 605 // Quoted value at element boundary. 606 xfcc, err = envoyutil.ParseXFCC(`by="me",uri=you`) 607 assert.Nil(err) 608 expected = envoyutil.XFCC{ 609 envoyutil.XFCCElement{By: "me"}, 610 envoyutil.XFCCElement{URI: "you"}, 611 } 612 assert.Equal(expected, xfcc) 613 614 // KV separator is the last character 615 xfcc, err = envoyutil.ParseXFCC("by=cliffhanger;") 616 assert.Equal(envoyutil.XFCC{}, xfcc) 617 except, ok = err.(*ex.Ex) 618 assert.True(ok) 619 assert.NotNil(except) 620 expectedErr = &ex.Ex{ 621 Class: envoyutil.ErrXFCCParsing, 622 Message: "Ends with separator character", 623 StackTrace: except.StackTrace, 624 } 625 assert.Equal(expectedErr, except) 626 assert.Equal(envoyutil.XFCC{}, ele) 627 628 // Element separator is the last character 629 xfcc, err = envoyutil.ParseXFCC("uri=cliffhanger,") 630 assert.Equal(envoyutil.XFCC{}, xfcc) 631 except, ok = err.(*ex.Ex) 632 assert.True(ok) 633 assert.NotNil(except) 634 expectedErr = &ex.Ex{ 635 Class: envoyutil.ErrXFCCParsing, 636 Message: "Ends with separator character", 637 StackTrace: except.StackTrace, 638 } 639 assert.Equal(expectedErr, except) 640 assert.Equal(envoyutil.XFCC{}, ele) 641 642 // Empty header 643 xfcc, err = envoyutil.ParseXFCC("") 644 assert.Equal(envoyutil.XFCC{}, xfcc) 645 assert.Nil(err) 646 assert.Equal(envoyutil.XFCC{}, ele) 647 }