github.com/nginxinc/kubernetes-ingress@v1.12.5/pkg/apis/configuration/validation/policy_test.go (about)

     1  package validation
     2  
     3  import (
     4  	"testing"
     5  
     6  	v1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1"
     7  	"k8s.io/apimachinery/pkg/util/validation/field"
     8  )
     9  
    10  func TestValidatePolicy(t *testing.T) {
    11  	tests := []struct {
    12  		policy                *v1.Policy
    13  		isPlus                bool
    14  		enablePreviewPolicies bool
    15  		enableAppProtect      bool
    16  		msg                   string
    17  	}{
    18  		{
    19  			policy: &v1.Policy{
    20  				Spec: v1.PolicySpec{
    21  					AccessControl: &v1.AccessControl{
    22  						Allow: []string{"127.0.0.1"},
    23  					},
    24  				},
    25  			},
    26  			isPlus:                false,
    27  			enablePreviewPolicies: false,
    28  			enableAppProtect:      false,
    29  		},
    30  		{
    31  			policy: &v1.Policy{
    32  				Spec: v1.PolicySpec{
    33  					JWTAuth: &v1.JWTAuth{
    34  						Realm:  "My Product API",
    35  						Secret: "my-jwk",
    36  					},
    37  				},
    38  			},
    39  			isPlus:                true,
    40  			enablePreviewPolicies: true,
    41  			enableAppProtect:      false,
    42  			msg:                   "use jwt(plus only) policy",
    43  		},
    44  		{
    45  			policy: &v1.Policy{
    46  				Spec: v1.PolicySpec{
    47  					OIDC: &v1.OIDC{
    48  						AuthEndpoint:  "https://foo.bar/auth",
    49  						TokenEndpoint: "https://foo.bar/token",
    50  						JWKSURI:       "https://foo.bar/certs",
    51  						ClientID:      "random-string",
    52  						ClientSecret:  "random-secret",
    53  						Scope:         "openid",
    54  					},
    55  				},
    56  			},
    57  			isPlus:                true,
    58  			enablePreviewPolicies: true,
    59  			msg:                   "use OIDC (plus only)",
    60  		},
    61  		{
    62  			policy: &v1.Policy{
    63  				Spec: v1.PolicySpec{
    64  					WAF: &v1.WAF{
    65  						Enable: true,
    66  					},
    67  				},
    68  			},
    69  			isPlus:                true,
    70  			enablePreviewPolicies: true,
    71  			enableAppProtect:      true,
    72  			msg:                   "use WAF(plus only) policy",
    73  		},
    74  	}
    75  	for _, test := range tests {
    76  		err := ValidatePolicy(test.policy, test.isPlus, test.enablePreviewPolicies, test.enableAppProtect)
    77  		if err != nil {
    78  			t.Errorf("ValidatePolicy() returned error %v for valid input for the case of %v", err, test.msg)
    79  		}
    80  	}
    81  }
    82  
    83  func TestValidatePolicyFails(t *testing.T) {
    84  	tests := []struct {
    85  		policy                *v1.Policy
    86  		isPlus                bool
    87  		enablePreviewPolicies bool
    88  		enableAppProtect      bool
    89  		msg                   string
    90  	}{
    91  		{
    92  			policy: &v1.Policy{
    93  				Spec: v1.PolicySpec{},
    94  			},
    95  			isPlus:                false,
    96  			enablePreviewPolicies: false,
    97  			enableAppProtect:      false,
    98  			msg:                   "empty policy spec",
    99  		},
   100  		{
   101  			policy: &v1.Policy{
   102  				Spec: v1.PolicySpec{
   103  					AccessControl: &v1.AccessControl{
   104  						Allow: []string{"127.0.0.1"},
   105  					},
   106  					RateLimit: &v1.RateLimit{
   107  						Key:      "${uri}",
   108  						ZoneSize: "10M",
   109  						Rate:     "10r/s",
   110  					},
   111  				},
   112  			},
   113  			isPlus:                true,
   114  			enablePreviewPolicies: true,
   115  			enableAppProtect:      false,
   116  			msg:                   "multiple policies in spec",
   117  		},
   118  		{
   119  			policy: &v1.Policy{
   120  				Spec: v1.PolicySpec{
   121  					JWTAuth: &v1.JWTAuth{
   122  						Realm:  "My Product API",
   123  						Secret: "my-jwk",
   124  					},
   125  				},
   126  			},
   127  			isPlus:                false,
   128  			enablePreviewPolicies: true,
   129  			enableAppProtect:      false,
   130  			msg:                   "jwt(plus only) policy on OSS",
   131  		},
   132  		{
   133  			policy: &v1.Policy{
   134  				Spec: v1.PolicySpec{
   135  					WAF: &v1.WAF{
   136  						Enable: true,
   137  					},
   138  				},
   139  			},
   140  			isPlus:                false,
   141  			enablePreviewPolicies: true,
   142  			enableAppProtect:      false,
   143  			msg:                   "WAF(plus only) policy on OSS",
   144  		},
   145  		{
   146  			policy: &v1.Policy{
   147  				Spec: v1.PolicySpec{
   148  					RateLimit: &v1.RateLimit{
   149  						Rate:     "10r/s",
   150  						ZoneSize: "10M",
   151  						Key:      "${request_uri}",
   152  					},
   153  				},
   154  			},
   155  			isPlus:                false,
   156  			enablePreviewPolicies: false,
   157  			enableAppProtect:      false,
   158  			msg:                   "rateLimit policy with preview policies disabled",
   159  		},
   160  		{
   161  			policy: &v1.Policy{
   162  				Spec: v1.PolicySpec{
   163  					JWTAuth: &v1.JWTAuth{
   164  						Realm:  "My Product API",
   165  						Secret: "my-jwk",
   166  					},
   167  				},
   168  			},
   169  			isPlus:                true,
   170  			enablePreviewPolicies: false,
   171  			enableAppProtect:      false,
   172  			msg:                   "jwt policy with preview policies disabled",
   173  		},
   174  		{
   175  			policy: &v1.Policy{
   176  				Spec: v1.PolicySpec{
   177  					IngressMTLS: &v1.IngressMTLS{
   178  						ClientCertSecret: "mtls-secret",
   179  					},
   180  				},
   181  			},
   182  			isPlus:                false,
   183  			enablePreviewPolicies: false,
   184  			enableAppProtect:      false,
   185  			msg:                   "ingressMTLS policy with preview policies disabled",
   186  		},
   187  		{
   188  			policy: &v1.Policy{
   189  				Spec: v1.PolicySpec{
   190  					EgressMTLS: &v1.EgressMTLS{
   191  						TLSSecret: "mtls-secret",
   192  					},
   193  				},
   194  			},
   195  			isPlus:                false,
   196  			enablePreviewPolicies: false,
   197  			enableAppProtect:      false,
   198  			msg:                   "egressMTLS policy with preview policies disabled",
   199  		},
   200  		{
   201  			policy: &v1.Policy{
   202  				Spec: v1.PolicySpec{
   203  					OIDC: &v1.OIDC{
   204  						AuthEndpoint:  "https://foo.bar/auth",
   205  						TokenEndpoint: "https://foo.bar/token",
   206  						JWKSURI:       "https://foo.bar/certs",
   207  						ClientID:      "random-string",
   208  						ClientSecret:  "random-secret",
   209  						Scope:         "openid",
   210  					},
   211  				},
   212  			},
   213  			isPlus:                true,
   214  			enablePreviewPolicies: false,
   215  			msg:                   "OIDC policy with preview policies disabled",
   216  		},
   217  		{
   218  			policy: &v1.Policy{
   219  				Spec: v1.PolicySpec{
   220  					OIDC: &v1.OIDC{
   221  						AuthEndpoint:  "https://foo.bar/auth",
   222  						TokenEndpoint: "https://foo.bar/token",
   223  						JWKSURI:       "https://foo.bar/certs",
   224  						ClientID:      "random-string",
   225  						ClientSecret:  "random-secret",
   226  						Scope:         "openid",
   227  					},
   228  				},
   229  			},
   230  			isPlus:                false,
   231  			enablePreviewPolicies: true,
   232  			msg:                   "OIDC policy in OSS",
   233  		},
   234  		{
   235  			policy: &v1.Policy{
   236  				Spec: v1.PolicySpec{
   237  					WAF: &v1.WAF{
   238  						Enable: true,
   239  					},
   240  				},
   241  			},
   242  			isPlus:                true,
   243  			enablePreviewPolicies: false,
   244  			enableAppProtect:      true,
   245  			msg:                   "WAF policy with preview policies disabled",
   246  		},
   247  		{
   248  			policy: &v1.Policy{
   249  				Spec: v1.PolicySpec{
   250  					WAF: &v1.WAF{
   251  						Enable: true,
   252  					},
   253  				},
   254  			},
   255  			isPlus:                true,
   256  			enablePreviewPolicies: true,
   257  			enableAppProtect:      false,
   258  			msg:                   "WAF policy with AP disabled",
   259  		},
   260  	}
   261  	for _, test := range tests {
   262  		err := ValidatePolicy(test.policy, test.isPlus, test.enablePreviewPolicies, test.enableAppProtect)
   263  		if err == nil {
   264  			t.Errorf("ValidatePolicy() returned no error for invalid input")
   265  		}
   266  	}
   267  }
   268  
   269  func TestValidateAccessControl(t *testing.T) {
   270  	validInput := []*v1.AccessControl{
   271  		{
   272  			Allow: []string{},
   273  		},
   274  		{
   275  			Allow: []string{"127.0.0.1"},
   276  		},
   277  		{
   278  			Deny: []string{},
   279  		},
   280  		{
   281  			Deny: []string{"127.0.0.1"},
   282  		},
   283  	}
   284  
   285  	for _, input := range validInput {
   286  		allErrs := validateAccessControl(input, field.NewPath("accessControl"))
   287  		if len(allErrs) > 0 {
   288  			t.Errorf("validateAccessControl(%+v) returned errors %v for valid input", input, allErrs)
   289  		}
   290  	}
   291  }
   292  
   293  func TestValidateAccessControlFails(t *testing.T) {
   294  	tests := []struct {
   295  		accessControl *v1.AccessControl
   296  		msg           string
   297  	}{
   298  		{
   299  			accessControl: &v1.AccessControl{
   300  				Allow: nil,
   301  				Deny:  nil,
   302  			},
   303  			msg: "neither allow nor deny is defined",
   304  		},
   305  		{
   306  			accessControl: &v1.AccessControl{
   307  				Allow: []string{},
   308  				Deny:  []string{},
   309  			},
   310  			msg: "both allow and deny are defined",
   311  		},
   312  		{
   313  			accessControl: &v1.AccessControl{
   314  				Allow: []string{"invalid"},
   315  			},
   316  			msg: "invalid allow",
   317  		},
   318  		{
   319  			accessControl: &v1.AccessControl{
   320  				Deny: []string{"invalid"},
   321  			},
   322  			msg: "invalid deny",
   323  		},
   324  	}
   325  
   326  	for _, test := range tests {
   327  		allErrs := validateAccessControl(test.accessControl, field.NewPath("accessControl"))
   328  		if len(allErrs) == 0 {
   329  			t.Errorf("validateAccessControl() returned no errors for invalid input for the case of %s", test.msg)
   330  		}
   331  	}
   332  }
   333  
   334  func TestValidateRateLimit(t *testing.T) {
   335  	dryRun := true
   336  	noDelay := false
   337  
   338  	tests := []struct {
   339  		rateLimit *v1.RateLimit
   340  		msg       string
   341  	}{
   342  		{
   343  			rateLimit: &v1.RateLimit{
   344  				Rate:     "10r/s",
   345  				ZoneSize: "10M",
   346  				Key:      "${request_uri}",
   347  			},
   348  			msg: "only required fields are set",
   349  		},
   350  		{
   351  			rateLimit: &v1.RateLimit{
   352  				Rate:       "30r/m",
   353  				Key:        "${request_uri}",
   354  				Delay:      createPointerFromInt(5),
   355  				NoDelay:    &noDelay,
   356  				Burst:      createPointerFromInt(10),
   357  				ZoneSize:   "10M",
   358  				DryRun:     &dryRun,
   359  				LogLevel:   "info",
   360  				RejectCode: createPointerFromInt(505),
   361  			},
   362  			msg: "ratelimit all fields set",
   363  		},
   364  	}
   365  
   366  	isPlus := false
   367  
   368  	for _, test := range tests {
   369  		allErrs := validateRateLimit(test.rateLimit, field.NewPath("rateLimit"), isPlus)
   370  		if len(allErrs) > 0 {
   371  			t.Errorf("validateRateLimit() returned errors %v for valid input for the case of %v", allErrs, test.msg)
   372  		}
   373  	}
   374  }
   375  
   376  func createInvalidRateLimit(f func(r *v1.RateLimit)) *v1.RateLimit {
   377  	validRateLimit := &v1.RateLimit{
   378  		Rate:     "10r/s",
   379  		ZoneSize: "10M",
   380  		Key:      "${request_uri}",
   381  	}
   382  	f(validRateLimit)
   383  	return validRateLimit
   384  }
   385  
   386  func TestValidateRateLimitFails(t *testing.T) {
   387  	tests := []struct {
   388  		rateLimit *v1.RateLimit
   389  		msg       string
   390  	}{
   391  		{
   392  			rateLimit: createInvalidRateLimit(func(r *v1.RateLimit) {
   393  				r.Rate = "0r/s"
   394  			}),
   395  			msg: "invalid rateLimit rate",
   396  		},
   397  		{
   398  			rateLimit: createInvalidRateLimit(func(r *v1.RateLimit) {
   399  				r.Key = "${fail}"
   400  			}),
   401  			msg: "invalid rateLimit key variable use",
   402  		},
   403  		{
   404  			rateLimit: createInvalidRateLimit(func(r *v1.RateLimit) {
   405  				r.Delay = createPointerFromInt(0)
   406  			}),
   407  			msg: "invalid rateLimit delay",
   408  		},
   409  		{
   410  			rateLimit: createInvalidRateLimit(func(r *v1.RateLimit) {
   411  				r.Burst = createPointerFromInt(0)
   412  			}),
   413  			msg: "invalid rateLimit burst",
   414  		},
   415  		{
   416  			rateLimit: createInvalidRateLimit(func(r *v1.RateLimit) {
   417  				r.ZoneSize = "31k"
   418  			}),
   419  			msg: "invalid rateLimit zoneSize",
   420  		},
   421  		{
   422  			rateLimit: createInvalidRateLimit(func(r *v1.RateLimit) {
   423  				r.RejectCode = createPointerFromInt(600)
   424  			}),
   425  			msg: "invalid rateLimit rejectCode",
   426  		},
   427  		{
   428  			rateLimit: createInvalidRateLimit(func(r *v1.RateLimit) {
   429  				r.LogLevel = "invalid"
   430  			}),
   431  			msg: "invalid rateLimit logLevel",
   432  		},
   433  	}
   434  
   435  	isPlus := false
   436  
   437  	for _, test := range tests {
   438  		allErrs := validateRateLimit(test.rateLimit, field.NewPath("rateLimit"), isPlus)
   439  		if len(allErrs) == 0 {
   440  			t.Errorf("validateRateLimit() returned no errors for invalid input for the case of %v", test.msg)
   441  		}
   442  	}
   443  }
   444  
   445  func TestValidateJWT(t *testing.T) {
   446  	tests := []struct {
   447  		jwt *v1.JWTAuth
   448  		msg string
   449  	}{
   450  		{
   451  			jwt: &v1.JWTAuth{
   452  				Realm:  "My Product API",
   453  				Secret: "my-jwk",
   454  			},
   455  			msg: "basic",
   456  		},
   457  		{
   458  			jwt: &v1.JWTAuth{
   459  				Realm:  "My Product API",
   460  				Secret: "my-jwk",
   461  				Token:  "$cookie_auth_token",
   462  			},
   463  			msg: "jwt with token",
   464  		},
   465  	}
   466  	for _, test := range tests {
   467  		allErrs := validateJWT(test.jwt, field.NewPath("jwt"))
   468  		if len(allErrs) != 0 {
   469  			t.Errorf("validateJWT() returned errors %v for valid input for the case of %v", allErrs, test.msg)
   470  		}
   471  	}
   472  }
   473  
   474  func TestValidateJWTFails(t *testing.T) {
   475  	tests := []struct {
   476  		msg string
   477  		jwt *v1.JWTAuth
   478  	}{
   479  		{
   480  			jwt: &v1.JWTAuth{
   481  				Realm: "My Product API",
   482  			},
   483  			msg: "missing secret",
   484  		},
   485  		{
   486  			jwt: &v1.JWTAuth{
   487  				Secret: "my-jwk",
   488  			},
   489  			msg: "missing realm",
   490  		},
   491  		{
   492  			jwt: &v1.JWTAuth{
   493  				Realm:  "My Product API",
   494  				Secret: "my-jwk",
   495  				Token:  "$uri",
   496  			},
   497  			msg: "invalid variable use in token",
   498  		},
   499  		{
   500  			jwt: &v1.JWTAuth{
   501  				Realm:  "My Product API",
   502  				Secret: "my-\"jwk",
   503  			},
   504  			msg: "invalid secret name",
   505  		},
   506  		{
   507  			jwt: &v1.JWTAuth{
   508  				Realm:  "My \"Product API",
   509  				Secret: "my-jwk",
   510  			},
   511  			msg: "invalid realm due to escaped string",
   512  		},
   513  		{
   514  			jwt: &v1.JWTAuth{
   515  				Realm:  "My Product ${api}",
   516  				Secret: "my-jwk",
   517  			},
   518  			msg: "invalid variable use in realm with curly braces",
   519  		},
   520  		{
   521  			jwt: &v1.JWTAuth{
   522  				Realm:  "My Product $api",
   523  				Secret: "my-jwk",
   524  			},
   525  			msg: "invalid variable use in realm without curly braces",
   526  		},
   527  	}
   528  	for _, test := range tests {
   529  		allErrs := validateJWT(test.jwt, field.NewPath("jwt"))
   530  		if len(allErrs) == 0 {
   531  			t.Errorf("validateJWT() returned no errors for invalid input for the case of %v", test.msg)
   532  		}
   533  	}
   534  }
   535  
   536  func TestValidateIPorCIDR(t *testing.T) {
   537  	validInput := []string{
   538  		"192.168.1.1",
   539  		"192.168.1.0/24",
   540  		"2001:0db8::1",
   541  		"2001:0db8::/32",
   542  	}
   543  
   544  	for _, input := range validInput {
   545  		allErrs := validateIPorCIDR(input, field.NewPath("ipOrCIDR"))
   546  		if len(allErrs) > 0 {
   547  			t.Errorf("validateIPorCIDR(%q) returned errors %v for valid input", input, allErrs)
   548  		}
   549  	}
   550  
   551  	invalidInput := []string{
   552  		"localhost",
   553  		"192.168.1.0/",
   554  		"2001:0db8:::1",
   555  		"2001:0db8::/",
   556  	}
   557  
   558  	for _, input := range invalidInput {
   559  		allErrs := validateIPorCIDR(input, field.NewPath("ipOrCIDR"))
   560  		if len(allErrs) == 0 {
   561  			t.Errorf("validateIPorCIDR(%q) returned no errors for invalid input", input)
   562  		}
   563  	}
   564  }
   565  
   566  func TestValidateRate(t *testing.T) {
   567  	validInput := []string{
   568  		"10r/s",
   569  		"100r/m",
   570  		"1r/s",
   571  	}
   572  
   573  	for _, input := range validInput {
   574  		allErrs := validateRate(input, field.NewPath("rate"))
   575  		if len(allErrs) > 0 {
   576  			t.Errorf("validateRate(%q) returned errors %v for valid input", input, allErrs)
   577  		}
   578  	}
   579  
   580  	invalidInput := []string{
   581  		"10s",
   582  		"10r/",
   583  		"10r/ms",
   584  		"0r/s",
   585  	}
   586  
   587  	for _, input := range invalidInput {
   588  		allErrs := validateRate(input, field.NewPath("rate"))
   589  		if len(allErrs) == 0 {
   590  			t.Errorf("validateRate(%q) returned no errors for invalid input", input)
   591  		}
   592  	}
   593  }
   594  
   595  func TestValidatePositiveInt(t *testing.T) {
   596  	validInput := []int{1, 2}
   597  
   598  	for _, input := range validInput {
   599  		allErrs := validatePositiveInt(input, field.NewPath("int"))
   600  		if len(allErrs) > 0 {
   601  			t.Errorf("validatePositiveInt(%q) returned errors %v for valid input", input, allErrs)
   602  		}
   603  	}
   604  
   605  	invalidInput := []int{-1, 0}
   606  
   607  	for _, input := range invalidInput {
   608  		allErrs := validatePositiveInt(input, field.NewPath("int"))
   609  		if len(allErrs) == 0 {
   610  			t.Errorf("validatePositiveInt(%q) returned no errors for invalid input", input)
   611  		}
   612  	}
   613  }
   614  
   615  func TestValidateRateLimitZoneSize(t *testing.T) {
   616  	validInput := []string{"32", "32k", "32K", "10m"}
   617  
   618  	for _, test := range validInput {
   619  		allErrs := validateRateLimitZoneSize(test, field.NewPath("size"))
   620  		if len(allErrs) != 0 {
   621  			t.Errorf("validateRateLimitZoneSize(%q) returned an error for valid input", test)
   622  		}
   623  	}
   624  
   625  	invalidInput := []string{"", "31", "31k", "0", "0M"}
   626  
   627  	for _, test := range invalidInput {
   628  		allErrs := validateRateLimitZoneSize(test, field.NewPath("size"))
   629  		if len(allErrs) == 0 {
   630  			t.Errorf("validateRateLimitZoneSize(%q) didn't return error for invalid input", test)
   631  		}
   632  	}
   633  }
   634  
   635  func TestValidateRateLimitLogLevel(t *testing.T) {
   636  	validInput := []string{"error", "info", "warn", "notice"}
   637  
   638  	for _, test := range validInput {
   639  		allErrs := validateRateLimitLogLevel(test, field.NewPath("logLevel"))
   640  		if len(allErrs) != 0 {
   641  			t.Errorf("validateRateLimitLogLevel(%q) returned an error for valid input", test)
   642  		}
   643  	}
   644  
   645  	invalidInput := []string{"warn ", "info error", ""}
   646  
   647  	for _, test := range invalidInput {
   648  		allErrs := validateRateLimitLogLevel(test, field.NewPath("logLevel"))
   649  		if len(allErrs) == 0 {
   650  			t.Errorf("validateRateLimitLogLevel(%q) didn't return error for invalid input", test)
   651  		}
   652  	}
   653  }
   654  
   655  func TestValidateJWTToken(t *testing.T) {
   656  	validTests := []struct {
   657  		token string
   658  		msg   string
   659  	}{
   660  		{
   661  			token: "",
   662  			msg:   "no token set",
   663  		},
   664  		{
   665  			token: "$http_token",
   666  			msg:   "http special variable usage",
   667  		},
   668  		{
   669  			token: "$arg_token",
   670  			msg:   "arg special variable usage",
   671  		},
   672  		{
   673  			token: "$cookie_token",
   674  			msg:   "cookie special variable usage",
   675  		},
   676  	}
   677  	for _, test := range validTests {
   678  		allErrs := validateJWTToken(test.token, field.NewPath("token"))
   679  		if len(allErrs) != 0 {
   680  			t.Errorf("validateJWTToken(%v) returned an error for valid input for the case of %v", test.token, test.msg)
   681  		}
   682  	}
   683  
   684  	invalidTests := []struct {
   685  		token string
   686  		msg   string
   687  	}{
   688  		{
   689  			token: "http_token",
   690  			msg:   "missing $ prefix",
   691  		},
   692  		{
   693  			token: "${http_token}",
   694  			msg:   "usage of $ and curly braces",
   695  		},
   696  		{
   697  			token: "$http_token$http_token",
   698  			msg:   "multi variable usage",
   699  		},
   700  		{
   701  			token: "something$http_token",
   702  			msg:   "non variable usage",
   703  		},
   704  		{
   705  			token: "$uri",
   706  			msg:   "non special variable usage",
   707  		},
   708  	}
   709  	for _, test := range invalidTests {
   710  		allErrs := validateJWTToken(test.token, field.NewPath("token"))
   711  		if len(allErrs) == 0 {
   712  			t.Errorf("validateJWTToken(%v) didn't return error for invalid input for the case of %v", test.token, test.msg)
   713  		}
   714  	}
   715  }
   716  
   717  func TestValidateIngressMTLS(t *testing.T) {
   718  	tests := []struct {
   719  		ing *v1.IngressMTLS
   720  		msg string
   721  	}{
   722  		{
   723  			ing: &v1.IngressMTLS{
   724  				ClientCertSecret: "mtls-secret",
   725  			},
   726  			msg: "default",
   727  		},
   728  		{
   729  			ing: &v1.IngressMTLS{
   730  				ClientCertSecret: "mtls-secret",
   731  				VerifyClient:     "on",
   732  				VerifyDepth:      createPointerFromInt(1),
   733  			},
   734  			msg: "all parameters with default value",
   735  		},
   736  		{
   737  			ing: &v1.IngressMTLS{
   738  				ClientCertSecret: "ingress-mtls-secret",
   739  				VerifyClient:     "optional",
   740  				VerifyDepth:      createPointerFromInt(2),
   741  			},
   742  			msg: "optional parameters",
   743  		},
   744  	}
   745  	for _, test := range tests {
   746  		allErrs := validateIngressMTLS(test.ing, field.NewPath("ingressMTLS"))
   747  		if len(allErrs) != 0 {
   748  			t.Errorf("validateIngressMTLS() returned errors %v for valid input for the case of %v", allErrs, test.msg)
   749  		}
   750  	}
   751  }
   752  
   753  func TestValidateIngressMTLSInvalid(t *testing.T) {
   754  	tests := []struct {
   755  		ing *v1.IngressMTLS
   756  		msg string
   757  	}{
   758  		{
   759  			ing: &v1.IngressMTLS{
   760  				VerifyClient: "on",
   761  			},
   762  			msg: "no secret",
   763  		},
   764  		{
   765  			ing: &v1.IngressMTLS{
   766  				ClientCertSecret: "-foo-",
   767  			},
   768  			msg: "invalid secret name",
   769  		},
   770  		{
   771  			ing: &v1.IngressMTLS{
   772  				ClientCertSecret: "mtls-secret",
   773  				VerifyClient:     "foo",
   774  			},
   775  			msg: "invalid verify client",
   776  		},
   777  		{
   778  			ing: &v1.IngressMTLS{
   779  				ClientCertSecret: "ingress-mtls-secret",
   780  				VerifyClient:     "on",
   781  				VerifyDepth:      createPointerFromInt(-1),
   782  			},
   783  			msg: "invalid depth",
   784  		},
   785  	}
   786  	for _, test := range tests {
   787  		allErrs := validateIngressMTLS(test.ing, field.NewPath("ingressMTLS"))
   788  		if len(allErrs) == 0 {
   789  			t.Errorf("validateIngressMTLS() returned no errors for invalid input for the case of %v", test.msg)
   790  		}
   791  	}
   792  }
   793  
   794  func TestValidateIngressMTLSVerifyClient(t *testing.T) {
   795  	validInput := []string{"on", "off", "optional", "optional_no_ca"}
   796  
   797  	for _, test := range validInput {
   798  		allErrs := validateIngressMTLSVerifyClient(test, field.NewPath("verifyClient"))
   799  		if len(allErrs) != 0 {
   800  			t.Errorf("validateIngressMTLSVerifyClient(%q) returned errors %v for valid input", allErrs, test)
   801  		}
   802  	}
   803  
   804  	invalidInput := []string{"true", "false"}
   805  
   806  	for _, test := range invalidInput {
   807  		allErrs := validateIngressMTLSVerifyClient(test, field.NewPath("verifyClient"))
   808  		if len(allErrs) == 0 {
   809  			t.Errorf("validateIngressMTLSVerifyClient(%q) didn't return error for invalid input", test)
   810  		}
   811  	}
   812  }
   813  
   814  func TestValidateEgressMTLS(t *testing.T) {
   815  	tests := []struct {
   816  		eg  *v1.EgressMTLS
   817  		msg string
   818  	}{
   819  		{
   820  			eg: &v1.EgressMTLS{
   821  				TLSSecret: "mtls-secret",
   822  			},
   823  			msg: "tls secret",
   824  		},
   825  		{
   826  			eg: &v1.EgressMTLS{
   827  				TrustedCertSecret: "tls-secret",
   828  				VerifyServer:      true,
   829  				VerifyDepth:       createPointerFromInt(2),
   830  				ServerName:        false,
   831  			},
   832  			msg: "verify server set to true",
   833  		},
   834  		{
   835  			eg: &v1.EgressMTLS{
   836  				VerifyServer: false,
   837  			},
   838  			msg: "verify server set to false",
   839  		},
   840  		{
   841  			eg: &v1.EgressMTLS{
   842  				SSLName: "foo.com",
   843  			},
   844  			msg: "ssl name",
   845  		},
   846  	}
   847  	for _, test := range tests {
   848  		allErrs := validateEgressMTLS(test.eg, field.NewPath("egressMTLS"))
   849  		if len(allErrs) != 0 {
   850  			t.Errorf("validateEgressMTLS() returned errors %v for valid input for the case of %v", allErrs, test.msg)
   851  		}
   852  	}
   853  }
   854  
   855  func TestValidateEgressMTLSInvalid(t *testing.T) {
   856  	tests := []struct {
   857  		eg  *v1.EgressMTLS
   858  		msg string
   859  	}{
   860  		{
   861  			eg: &v1.EgressMTLS{
   862  				VerifyServer: true,
   863  			},
   864  			msg: "verify server set to true",
   865  		},
   866  		{
   867  			eg: &v1.EgressMTLS{
   868  				TrustedCertSecret: "-foo-",
   869  			},
   870  			msg: "invalid secret name",
   871  		},
   872  		{
   873  			eg: &v1.EgressMTLS{
   874  				TrustedCertSecret: "ingress-mtls-secret",
   875  				VerifyServer:      true,
   876  				VerifyDepth:       createPointerFromInt(-1),
   877  			},
   878  			msg: "invalid depth",
   879  		},
   880  		{
   881  			eg: &v1.EgressMTLS{
   882  				SSLName: "foo.com;",
   883  			},
   884  			msg: "invalid name",
   885  		},
   886  	}
   887  
   888  	for _, test := range tests {
   889  		allErrs := validateEgressMTLS(test.eg, field.NewPath("egressMTLS"))
   890  		if len(allErrs) == 0 {
   891  			t.Errorf("validateEgressMTLS() returned no errors for invalid input for the case of %v", test.msg)
   892  		}
   893  	}
   894  }
   895  
   896  func TestValidateOIDCValid(t *testing.T) {
   897  	tests := []struct {
   898  		oidc *v1.OIDC
   899  		msg  string
   900  	}{
   901  		{
   902  			oidc: &v1.OIDC{
   903  				AuthEndpoint:  "https://accounts.google.com/o/oauth2/v2/auth",
   904  				TokenEndpoint: "https://oauth2.googleapis.com/token",
   905  				JWKSURI:       "https://www.googleapis.com/oauth2/v3/certs",
   906  				ClientID:      "random-string",
   907  				ClientSecret:  "random-secret",
   908  				Scope:         "openid",
   909  				RedirectURI:   "/foo",
   910  			},
   911  			msg: "verify full oidc",
   912  		},
   913  		{
   914  			oidc: &v1.OIDC{
   915  				AuthEndpoint:  "https://login.microsoftonline.com/dd-fff-eee-1234-9be/oauth2/v2.0/authorize",
   916  				TokenEndpoint: "https://login.microsoftonline.com/dd-fff-eee-1234-9be/oauth2/v2.0/token",
   917  				JWKSURI:       "https://login.microsoftonline.com/dd-fff-eee-1234-9be/discovery/v2.0/keys",
   918  				ClientID:      "ff",
   919  				ClientSecret:  "ff",
   920  				Scope:         "openid+profile",
   921  				RedirectURI:   "/_codexe",
   922  			},
   923  			msg: "verify azure endpoint",
   924  		},
   925  		{
   926  			oidc: &v1.OIDC{
   927  				AuthEndpoint:  "http://keycloak.default.svc.cluster.local:8080/auth/realms/master/protocol/openid-connect/auth",
   928  				TokenEndpoint: "http://keycloak.default.svc.cluster.local:8080/auth/realms/master/protocol/openid-connect/token",
   929  				JWKSURI:       "http://keycloak.default.svc.cluster.local:8080/auth/realms/master/protocol/openid-connect/certs",
   930  				ClientID:      "bar",
   931  				ClientSecret:  "foo",
   932  				Scope:         "openid",
   933  			},
   934  			msg: "domain with port number",
   935  		},
   936  		{
   937  			oidc: &v1.OIDC{
   938  				AuthEndpoint:  "http://127.0.0.1:8080/auth/realms/master/protocol/openid-connect/auth",
   939  				TokenEndpoint: "http://127.0.0.1:8080/auth/realms/master/protocol/openid-connect/token",
   940  				JWKSURI:       "http://127.0.0.1:8080/auth/realms/master/protocol/openid-connect/certs",
   941  				ClientID:      "client",
   942  				ClientSecret:  "secret",
   943  				Scope:         "openid",
   944  			},
   945  			msg: "ip address",
   946  		},
   947  	}
   948  
   949  	for _, test := range tests {
   950  		allErrs := validateOIDC(test.oidc, field.NewPath("oidc"))
   951  		if len(allErrs) != 0 {
   952  			t.Errorf("validateOIDC() returned errors %v for valid input for the case of %v", allErrs, test.msg)
   953  		}
   954  	}
   955  }
   956  
   957  func TestValidateOIDCInvalid(t *testing.T) {
   958  	tests := []struct {
   959  		oidc *v1.OIDC
   960  		msg  string
   961  	}{
   962  		{
   963  			oidc: &v1.OIDC{
   964  				RedirectURI: "/foo",
   965  			},
   966  			msg: "missing required field auth",
   967  		},
   968  		{
   969  			oidc: &v1.OIDC{
   970  				AuthEndpoint: "https://login.microsoftonline.com/dd-fff-eee-1234-9be/oauth2/v2.0/authorize",
   971  				JWKSURI:      "https://login.microsoftonline.com/dd-fff-eee-1234-9be/discovery/v2.0/keys",
   972  				ClientID:     "ff",
   973  				ClientSecret: "ff",
   974  				Scope:        "openid+profile",
   975  			},
   976  			msg: "missing required field token",
   977  		},
   978  		{
   979  			oidc: &v1.OIDC{
   980  				AuthEndpoint:  "https://login.microsoftonline.com/dd-fff-eee-1234-9be/oauth2/v2.0/authorize",
   981  				TokenEndpoint: "https://login.microsoftonline.com/dd-fff-eee-1234-9be/oauth2/v2.0/token",
   982  				ClientID:      "ff",
   983  				ClientSecret:  "ff",
   984  				Scope:         "openid+profile",
   985  			},
   986  			msg: "missing required field jwk",
   987  		},
   988  		{
   989  			oidc: &v1.OIDC{
   990  				AuthEndpoint:  "https://login.microsoftonline.com/dd-fff-eee-1234-9be/oauth2/v2.0/authorize",
   991  				TokenEndpoint: "https://login.microsoftonline.com/dd-fff-eee-1234-9be/oauth2/v2.0/token",
   992  				JWKSURI:       "https://login.microsoftonline.com/dd-fff-eee-1234-9be/discovery/v2.0/keys",
   993  				ClientSecret:  "ff",
   994  				Scope:         "openid+profile",
   995  			},
   996  			msg: "missing required field clientid",
   997  		},
   998  		{
   999  			oidc: &v1.OIDC{
  1000  				AuthEndpoint:  "https://login.microsoftonline.com/dd-fff-eee-1234-9be/oauth2/v2.0/authorize",
  1001  				TokenEndpoint: "https://login.microsoftonline.com/dd-fff-eee-1234-9be/oauth2/v2.0/token",
  1002  				JWKSURI:       "https://login.microsoftonline.com/dd-fff-eee-1234-9be/discovery/v2.0/keys",
  1003  				ClientID:      "ff",
  1004  				Scope:         "openid+profile",
  1005  			},
  1006  			msg: "missing required field client secret",
  1007  		},
  1008  
  1009  		{
  1010  			oidc: &v1.OIDC{
  1011  				AuthEndpoint:  "https://login.microsoftonline.com/dd-fff-eee-1234-9be/oauth2/v2.0/authorize",
  1012  				TokenEndpoint: "https://login.microsoftonline.com/dd-fff-eee-1234-9be/oauth2/v2.0/token",
  1013  				JWKSURI:       "https://login.microsoftonline.com/dd-fff-eee-1234-9be/discovery/v2.0/keys",
  1014  				ClientID:      "ff",
  1015  				ClientSecret:  "-ff-",
  1016  				Scope:         "openid+profile",
  1017  			},
  1018  			msg: "invalid secret name",
  1019  		},
  1020  		{
  1021  			oidc: &v1.OIDC{
  1022  				AuthEndpoint:  "http://foo.\bar.com",
  1023  				TokenEndpoint: "http://keycloak.default",
  1024  				JWKSURI:       "http://keycloak.default",
  1025  				ClientID:      "bar",
  1026  				ClientSecret:  "foo",
  1027  				Scope:         "openid",
  1028  			},
  1029  			msg: "invalid URL",
  1030  		},
  1031  		{
  1032  			oidc: &v1.OIDC{
  1033  				AuthEndpoint:  "http://127.0.0.1:8080/auth/realms/master/protocol/openid-connect/auth",
  1034  				TokenEndpoint: "http://127.0.0.1:8080/auth/realms/master/protocol/openid-connect/token",
  1035  				JWKSURI:       "http://127.0.0.1:8080/auth/realms/master/protocol/openid-connect/certs",
  1036  				ClientID:      "$foo$bar",
  1037  				ClientSecret:  "secret",
  1038  				Scope:         "openid",
  1039  			},
  1040  			msg: "invalid chars in clientID",
  1041  		},
  1042  	}
  1043  
  1044  	for _, test := range tests {
  1045  		allErrs := validateOIDC(test.oidc, field.NewPath("oidc"))
  1046  		if len(allErrs) == 0 {
  1047  			t.Errorf("validateOIDC() returned no errors for invalid input for the case of %v", test.msg)
  1048  		}
  1049  	}
  1050  }
  1051  
  1052  func TestValidateClientID(t *testing.T) {
  1053  	validInput := []string{"myid", "your.id", "id-sf-sjfdj.com", "foo_bar~vni"}
  1054  
  1055  	for _, test := range validInput {
  1056  		allErrs := validateClientID(test, field.NewPath("clientID"))
  1057  		if len(allErrs) != 0 {
  1058  			t.Errorf("validateClientID(%q) returned errors %v for valid input", allErrs, test)
  1059  		}
  1060  	}
  1061  
  1062  	invalidInput := []string{"$boo", "foo$bar", `foo_bar"vni`, `client\`}
  1063  
  1064  	for _, test := range invalidInput {
  1065  		allErrs := validateClientID(test, field.NewPath("clientID"))
  1066  		if len(allErrs) == 0 {
  1067  			t.Errorf("validateClientID(%q) didn't return error for invalid input", test)
  1068  		}
  1069  	}
  1070  }
  1071  
  1072  func TestValidateOIDCScope(t *testing.T) {
  1073  	validInput := []string{"openid", "openid+profile", "openid+email", "openid+phone"}
  1074  
  1075  	for _, test := range validInput {
  1076  		allErrs := validateOIDCScope(test, field.NewPath("scope"))
  1077  		if len(allErrs) != 0 {
  1078  			t.Errorf("validateOIDCScope(%q) returned errors %v for valid input", allErrs, test)
  1079  		}
  1080  	}
  1081  
  1082  	invalidInput := []string{"profile", "openid+web", `openid+foobar.com`}
  1083  
  1084  	for _, test := range invalidInput {
  1085  		allErrs := validateOIDCScope(test, field.NewPath("scope"))
  1086  		if len(allErrs) == 0 {
  1087  			t.Errorf("validateOIDCScope(%q) didn't return error for invalid input", test)
  1088  		}
  1089  	}
  1090  }
  1091  
  1092  func TestValidateURL(t *testing.T) {
  1093  	validInput := []string{"http://google.com/auth", "https://foo.bar/baz", "http://127.0.0.1/bar", "http://openid.connect.com:8080/foo"}
  1094  
  1095  	for _, test := range validInput {
  1096  		allErrs := validateURL(test, field.NewPath("authEndpoint"))
  1097  		if len(allErrs) != 0 {
  1098  			t.Errorf("validateURL(%q) returned errors %v for valid input", allErrs, test)
  1099  		}
  1100  	}
  1101  
  1102  	invalidInput := []string{"www.google..foo.com", "http://{foo.bar", `https://google.foo\bar`, "http://foo.bar:8080", "http://foo.bar:812345/fooo"}
  1103  
  1104  	for _, test := range invalidInput {
  1105  		allErrs := validateURL(test, field.NewPath("authEndpoint"))
  1106  		if len(allErrs) == 0 {
  1107  			t.Errorf("validateURL(%q) didn't return error for invalid input", test)
  1108  		}
  1109  	}
  1110  }
  1111  
  1112  func TestValidateWAF(t *testing.T) {
  1113  	tests := []struct {
  1114  		waf *v1.WAF
  1115  		msg string
  1116  	}{
  1117  		{
  1118  			waf: &v1.WAF{
  1119  				Enable: true,
  1120  			},
  1121  			msg: "waf enabled",
  1122  		},
  1123  		{
  1124  			waf: &v1.WAF{
  1125  				Enable:   true,
  1126  				ApPolicy: "ns1/waf-pol",
  1127  			},
  1128  			msg: "cross ns reference",
  1129  		},
  1130  		{
  1131  			waf: &v1.WAF{
  1132  				Enable: true,
  1133  				SecurityLog: &v1.SecurityLog{
  1134  					Enable:  true,
  1135  					LogDest: "syslog:server=8.7.7.7:517",
  1136  				},
  1137  			},
  1138  			msg: "custom logdest",
  1139  		},
  1140  	}
  1141  
  1142  	for _, test := range tests {
  1143  		allErrs := validateWAF(test.waf, field.NewPath("waf"))
  1144  		if len(allErrs) != 0 {
  1145  			t.Errorf("validateWAF() returned errors %v for valid input for the case of %v", allErrs, test.msg)
  1146  		}
  1147  	}
  1148  }
  1149  
  1150  func TestValidateWAFInvalid(t *testing.T) {
  1151  	tests := []struct {
  1152  		waf *v1.WAF
  1153  		msg string
  1154  	}{
  1155  		{
  1156  			waf: &v1.WAF{
  1157  				Enable:   true,
  1158  				ApPolicy: "ns1/ap-pol/ns2",
  1159  			},
  1160  			msg: "invalid apPolicy format",
  1161  		},
  1162  		{
  1163  			waf: &v1.WAF{
  1164  				Enable: true,
  1165  				SecurityLog: &v1.SecurityLog{
  1166  					Enable:  true,
  1167  					LogDest: "stdout",
  1168  				},
  1169  			},
  1170  			msg: "invalid logdest",
  1171  		},
  1172  		{
  1173  			waf: &v1.WAF{
  1174  				Enable: true,
  1175  				SecurityLog: &v1.SecurityLog{
  1176  					Enable:    true,
  1177  					ApLogConf: "ns1/log-conf/ns2",
  1178  				},
  1179  			},
  1180  			msg: "invalid logConf format",
  1181  		},
  1182  	}
  1183  
  1184  	for _, test := range tests {
  1185  		allErrs := validateWAF(test.waf, field.NewPath("waf"))
  1186  		if len(allErrs) == 0 {
  1187  			t.Errorf("validateWAF() returned no errors for invalid input for the case of %v", test.msg)
  1188  		}
  1189  	}
  1190  }