istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/config/validation/validation_test.go (about)

     1  // Copyright Istio Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package validation
    16  
    17  import (
    18  	"errors"
    19  	"fmt"
    20  	"strings"
    21  	"testing"
    22  	"time"
    23  
    24  	"google.golang.org/protobuf/proto"
    25  	"google.golang.org/protobuf/types/known/durationpb"
    26  	"google.golang.org/protobuf/types/known/wrapperspb"
    27  
    28  	extensions "istio.io/api/extensions/v1alpha1"
    29  	networking "istio.io/api/networking/v1alpha3"
    30  	networkingv1beta1 "istio.io/api/networking/v1beta1"
    31  	security_beta "istio.io/api/security/v1beta1"
    32  	telemetry "istio.io/api/telemetry/v1alpha1"
    33  	api "istio.io/api/type/v1beta1"
    34  	"istio.io/istio/pkg/config"
    35  	"istio.io/istio/pkg/config/constants"
    36  	"istio.io/istio/pkg/config/schema/gvk"
    37  	"istio.io/istio/pkg/test/util/assert"
    38  )
    39  
    40  const (
    41  	// Config name for testing
    42  	someName = "foo"
    43  	// Config namespace for testing.
    44  	someNamespace = "bar"
    45  )
    46  
    47  func TestValidateMaxServerConnectionAge(t *testing.T) {
    48  	type durationCheck struct {
    49  		duration time.Duration
    50  		isValid  bool
    51  	}
    52  	durMin, _ := time.ParseDuration("-30m")
    53  	durHr, _ := time.ParseDuration("-1.5h")
    54  	checks := []durationCheck{
    55  		{
    56  			duration: 30 * time.Minute,
    57  			isValid:  true,
    58  		},
    59  		{
    60  			duration: durMin,
    61  			isValid:  false,
    62  		},
    63  		{
    64  			duration: durHr,
    65  			isValid:  false,
    66  		},
    67  	}
    68  
    69  	for _, check := range checks {
    70  		if got := ValidateMaxServerConnectionAge(check.duration); (got == nil) != check.isValid {
    71  			t.Errorf("Failed: got valid=%t but wanted valid=%t: %v for %v", got == nil, check.isValid, got, check.duration)
    72  		}
    73  	}
    74  }
    75  
    76  func TestValidateGateway(t *testing.T) {
    77  	tests := []struct {
    78  		name    string
    79  		in      proto.Message
    80  		out     string
    81  		warning string
    82  	}{
    83  		{"empty", &networking.Gateway{}, "server", ""},
    84  		{"invalid message", &networking.Server{}, "cannot cast", ""},
    85  		{
    86  			"happy domain",
    87  			&networking.Gateway{
    88  				Servers: []*networking.Server{{
    89  					Hosts: []string{"foo.bar.com"},
    90  					Port:  &networking.Port{Name: "name1", Number: 7, Protocol: "http"},
    91  				}},
    92  			},
    93  			"", "",
    94  		},
    95  		{
    96  			"happy multiple servers",
    97  			&networking.Gateway{
    98  				Servers: []*networking.Server{
    99  					{
   100  						Hosts: []string{"foo.bar.com"},
   101  						Port:  &networking.Port{Name: "name1", Number: 7, Protocol: "http"},
   102  					},
   103  				},
   104  			},
   105  			"", "",
   106  		},
   107  		{
   108  			"happy k8s gateway-api server with no attached routes",
   109  			&networking.Gateway{
   110  				Servers: []*networking.Server{{
   111  					Hosts: []string{"~/foo.bar.com"},
   112  					Port:  &networking.Port{Name: "name1", Number: 7, Protocol: "http"},
   113  				}},
   114  			},
   115  			"invalid namespace value", "",
   116  		},
   117  		{
   118  			"invalid port",
   119  			&networking.Gateway{
   120  				Servers: []*networking.Server{
   121  					{
   122  						Hosts: []string{"foo.bar.com"},
   123  						Port:  &networking.Port{Name: "name1", Number: 66000, Protocol: "http"},
   124  					},
   125  				},
   126  			},
   127  			"port", "",
   128  		},
   129  		{
   130  			"duplicate port names",
   131  			&networking.Gateway{
   132  				Servers: []*networking.Server{
   133  					{
   134  						Hosts: []string{"foo.bar.com"},
   135  						Port:  &networking.Port{Name: "foo", Number: 80, Protocol: "http"},
   136  					},
   137  					{
   138  						Hosts: []string{"scooby.doo.com"},
   139  						Port:  &networking.Port{Name: "foo", Number: 8080, Protocol: "http"},
   140  					},
   141  				},
   142  			},
   143  			"port names", "",
   144  		},
   145  		{
   146  			"invalid domain",
   147  			&networking.Gateway{
   148  				Servers: []*networking.Server{
   149  					{
   150  						Hosts: []string{"foo.*.bar.com"},
   151  						Port:  &networking.Port{Number: 7, Protocol: "http"},
   152  					},
   153  				},
   154  			},
   155  			"domain", "",
   156  		},
   157  		{
   158  			"valid httpsRedirect",
   159  			&networking.Gateway{
   160  				Servers: []*networking.Server{
   161  					{
   162  						Hosts: []string{"bar.com"},
   163  						Port:  &networking.Port{Name: "http", Number: 80, Protocol: "http"},
   164  						Tls:   &networking.ServerTLSSettings{HttpsRedirect: true},
   165  					},
   166  				},
   167  			},
   168  			"", "",
   169  		},
   170  		{
   171  			"invalid https httpsRedirect",
   172  			&networking.Gateway{
   173  				Servers: []*networking.Server{
   174  					{
   175  						Hosts: []string{"bar.com"},
   176  						Port:  &networking.Port{Name: "https", Number: 80, Protocol: "https"},
   177  						Tls:   &networking.ServerTLSSettings{HttpsRedirect: true},
   178  					},
   179  				},
   180  			},
   181  			"", "tls.httpsRedirect should only be used with http servers",
   182  		},
   183  		{
   184  			"invalid partial wildcard",
   185  			&networking.Gateway{
   186  				Servers: []*networking.Server{
   187  					{
   188  						Hosts: []string{"*bar.com"},
   189  						Port:  &networking.Port{Name: "tls", Number: 443, Protocol: "tls"},
   190  						Tls:   &networking.ServerTLSSettings{Mode: networking.ServerTLSSettings_ISTIO_MUTUAL},
   191  					},
   192  				},
   193  			},
   194  			"partial wildcard \"*bar.com\" not allowed", "",
   195  		},
   196  	}
   197  	for _, tt := range tests {
   198  		t.Run(tt.name, func(t *testing.T) {
   199  			warn, err := ValidateGateway(config.Config{
   200  				Meta: config.Meta{
   201  					Name:      someName,
   202  					Namespace: someNamespace,
   203  				},
   204  				Spec: tt.in,
   205  			})
   206  			checkValidationMessage(t, warn, err, tt.warning, tt.out)
   207  		})
   208  	}
   209  }
   210  
   211  func TestValidateK8sGateway(t *testing.T) {
   212  	tests := []struct {
   213  		name    string
   214  		in      proto.Message
   215  		out     string
   216  		warning string
   217  	}{
   218  		{
   219  			"happy k8s gateway-api server with no attached routes",
   220  			&networking.Gateway{
   221  				Servers: []*networking.Server{{
   222  					Hosts: []string{"~/foo.bar.com"},
   223  					Port:  &networking.Port{Name: "name1", Number: 7, Protocol: "http"},
   224  				}},
   225  			},
   226  			"", "",
   227  		},
   228  	}
   229  	for _, tt := range tests {
   230  		t.Run(tt.name, func(t *testing.T) {
   231  			annotations := map[string]string{}
   232  			annotations[constants.InternalGatewaySemantics] = constants.GatewaySemanticsGateway
   233  
   234  			warn, err := ValidateGateway(config.Config{
   235  				Meta: config.Meta{
   236  					Name:        someName,
   237  					Namespace:   someNamespace,
   238  					Annotations: annotations,
   239  				},
   240  				Spec: tt.in,
   241  			})
   242  			checkValidationMessage(t, warn, err, tt.warning, tt.out)
   243  		})
   244  	}
   245  }
   246  
   247  func TestValidateServer(t *testing.T) {
   248  	tests := []struct {
   249  		name string
   250  		in   *networking.Server
   251  		out  string
   252  	}{
   253  		{"empty", &networking.Server{}, "host"},
   254  		{"empty", &networking.Server{}, "port"},
   255  		{
   256  			"happy",
   257  			&networking.Server{
   258  				Hosts: []string{"foo.bar.com"},
   259  				Port:  &networking.Port{Number: 7, Name: "http", Protocol: "http"},
   260  			},
   261  			"",
   262  		},
   263  		{
   264  			"happy ip",
   265  			&networking.Server{
   266  				Hosts: []string{"1.1.1.1"},
   267  				Port:  &networking.Port{Number: 7, Name: "http", Protocol: "http"},
   268  			},
   269  			"",
   270  		},
   271  		{
   272  			"happy ns/name",
   273  			&networking.Server{
   274  				Hosts: []string{"ns1/foo.bar.com"},
   275  				Port:  &networking.Port{Number: 7, Name: "http", Protocol: "http"},
   276  			},
   277  			"",
   278  		},
   279  		{
   280  			"happy */name",
   281  			&networking.Server{
   282  				Hosts: []string{"*/foo.bar.com"},
   283  				Port:  &networking.Port{Number: 7, Name: "http", Protocol: "http"},
   284  			},
   285  			"",
   286  		},
   287  		{
   288  			"happy ./name",
   289  			&networking.Server{
   290  				Hosts: []string{"./foo.bar.com"},
   291  				Port:  &networking.Port{Number: 7, Name: "http", Protocol: "http"},
   292  			},
   293  			"",
   294  		},
   295  		{
   296  			"invalid ~/name",
   297  			&networking.Server{
   298  				Hosts: []string{"~/foo.bar.com"},
   299  				Port:  &networking.Port{Number: 7, Name: "http", Protocol: "http"},
   300  			},
   301  			"namespace",
   302  		},
   303  		{
   304  			"invalid domain ns/name format",
   305  			&networking.Server{
   306  				Hosts: []string{"ns1/foo.*.bar.com"},
   307  				Port:  &networking.Port{Number: 7, Name: "http", Protocol: "http"},
   308  			},
   309  			"domain",
   310  		},
   311  		{
   312  			"invalid domain",
   313  			&networking.Server{
   314  				Hosts: []string{"foo.*.bar.com"},
   315  				Port:  &networking.Port{Number: 7, Name: "http", Protocol: "http"},
   316  			},
   317  			"domain",
   318  		},
   319  		{
   320  			"invalid short name host",
   321  			&networking.Server{
   322  				Hosts: []string{"foo"},
   323  				Port:  &networking.Port{Number: 7, Name: "http", Protocol: "http"},
   324  			},
   325  			"short names",
   326  		},
   327  		{
   328  			"invalid port",
   329  			&networking.Server{
   330  				Hosts: []string{"foo.bar.com"},
   331  				Port:  &networking.Port{Number: 66000, Name: "http", Protocol: "http"},
   332  			},
   333  			"port",
   334  		},
   335  		{
   336  			"invalid tls options",
   337  			&networking.Server{
   338  				Hosts: []string{"foo.bar.com"},
   339  				Port:  &networking.Port{Number: 1, Name: "http", Protocol: "http"},
   340  				Tls:   &networking.ServerTLSSettings{Mode: networking.ServerTLSSettings_SIMPLE},
   341  			},
   342  			"TLS",
   343  		},
   344  		{
   345  			"no tls on HTTPS",
   346  			&networking.Server{
   347  				Hosts: []string{"foo.bar.com"},
   348  				Port:  &networking.Port{Number: 10000, Name: "https", Protocol: "https"},
   349  			},
   350  			"must have TLS",
   351  		},
   352  		{
   353  			"tls on HTTP",
   354  			&networking.Server{
   355  				Hosts: []string{"foo.bar.com"},
   356  				Port:  &networking.Port{Number: 10000, Name: "http", Protocol: "http"},
   357  				Tls:   &networking.ServerTLSSettings{Mode: networking.ServerTLSSettings_SIMPLE},
   358  			},
   359  			"cannot have TLS",
   360  		},
   361  		{
   362  			"tls redirect on HTTP",
   363  			&networking.Server{
   364  				Hosts: []string{"foo.bar.com"},
   365  				Port:  &networking.Port{Number: 10000, Name: "http", Protocol: "http"},
   366  				Tls: &networking.ServerTLSSettings{
   367  					HttpsRedirect: true,
   368  				},
   369  			},
   370  			"",
   371  		},
   372  		{
   373  			"bind ip",
   374  			&networking.Server{
   375  				Hosts: []string{"foo.bar.com"},
   376  				Port:  &networking.Port{Number: 7, Name: "http", Protocol: "http"},
   377  				Bind:  "127.0.0.1",
   378  			},
   379  			"",
   380  		},
   381  		{
   382  			"bind unix path with invalid port",
   383  			&networking.Server{
   384  				Hosts: []string{"foo.bar.com"},
   385  				Port:  &networking.Port{Number: 7, Name: "http", Protocol: "http"},
   386  				Bind:  "unix://@foobar",
   387  			},
   388  			"port number must be 0 for unix domain socket",
   389  		},
   390  		{
   391  			"bind unix path",
   392  			&networking.Server{
   393  				Hosts: []string{"foo.bar.com"},
   394  				Port:  &networking.Port{Number: 0, Name: "http", Protocol: "http"},
   395  				Bind:  "unix://@foobar",
   396  			},
   397  			"",
   398  		},
   399  		{
   400  			"bind bad ip",
   401  			&networking.Server{
   402  				Hosts: []string{"foo.bar.com"},
   403  				Port:  &networking.Port{Number: 0, Name: "http", Protocol: "http"},
   404  				Bind:  "foo.bar",
   405  			},
   406  			"foo.bar is not a valid IP",
   407  		},
   408  	}
   409  	for _, tt := range tests {
   410  		t.Run(tt.name, func(t *testing.T) {
   411  			v := validateServer(tt.in, false)
   412  			warn, err := v.Unwrap()
   413  			checkValidationMessage(t, warn, err, "", tt.out)
   414  		})
   415  	}
   416  }
   417  
   418  func TestValidateServerPort(t *testing.T) {
   419  	tests := []struct {
   420  		name string
   421  		in   *networking.Port
   422  		bind string
   423  		out  string
   424  	}{
   425  		{"empty", &networking.Port{}, "", "invalid protocol"},
   426  		{"empty", &networking.Port{}, "", "port name"},
   427  		{
   428  			"happy",
   429  			&networking.Port{
   430  				Protocol: "http",
   431  				Number:   1,
   432  				Name:     "Henry",
   433  			},
   434  			"",
   435  			"",
   436  		},
   437  		{
   438  			"invalid protocol",
   439  			&networking.Port{
   440  				Protocol: "kafka",
   441  				Number:   1,
   442  				Name:     "Henry",
   443  			},
   444  			"",
   445  			"invalid protocol",
   446  		},
   447  		{
   448  			"invalid number",
   449  			&networking.Port{
   450  				Protocol: "http",
   451  				Number:   uint32(1 << 30),
   452  				Name:     "http",
   453  			},
   454  			"",
   455  			"port number",
   456  		},
   457  		{
   458  			"name, no number",
   459  			&networking.Port{
   460  				Protocol: "http",
   461  				Number:   0,
   462  				Name:     "Henry",
   463  			},
   464  			"",
   465  			"port number",
   466  		},
   467  		{
   468  			"name, no number, uds",
   469  			&networking.Port{
   470  				Protocol: "http",
   471  				Number:   0,
   472  				Name:     "Henry",
   473  			},
   474  			"uds:///tmp",
   475  			"port number",
   476  		},
   477  	}
   478  	for _, tt := range tests {
   479  		t.Run(tt.name, func(t *testing.T) {
   480  			v := validateServerPort(tt.in, tt.bind)
   481  			_, err := v.Unwrap()
   482  			if err == nil && tt.out != "" {
   483  				t.Fatalf("validateServerPort(%v) = nil, wanted %q", tt.in, tt.out)
   484  			} else if err != nil && tt.out == "" {
   485  				t.Fatalf("validateServerPort(%v) = %v, wanted nil", tt.in, err)
   486  			} else if err != nil && !strings.Contains(err.Error(), tt.out) {
   487  				t.Fatalf("validateServerPort(%v) = %v, wanted %q", tt.in, err, tt.out)
   488  			}
   489  		})
   490  	}
   491  }
   492  
   493  func TestValidateTlsOptions(t *testing.T) {
   494  	tests := []struct {
   495  		name    string
   496  		in      *networking.ServerTLSSettings
   497  		out     string
   498  		warning string
   499  	}{
   500  		{"empty", &networking.ServerTLSSettings{}, "", ""},
   501  		{
   502  			"simple",
   503  			&networking.ServerTLSSettings{
   504  				Mode:              networking.ServerTLSSettings_SIMPLE,
   505  				ServerCertificate: "Captain Jean-Luc Picard",
   506  				PrivateKey:        "Khan Noonien Singh",
   507  			},
   508  			"", "",
   509  		},
   510  		{
   511  			"simple with client bundle",
   512  			&networking.ServerTLSSettings{
   513  				Mode:              networking.ServerTLSSettings_SIMPLE,
   514  				ServerCertificate: "Captain Jean-Luc Picard",
   515  				PrivateKey:        "Khan Noonien Singh",
   516  				CaCertificates:    "Commander William T. Riker",
   517  			},
   518  			"", "",
   519  		},
   520  		{
   521  			"simple sds with client bundle",
   522  			&networking.ServerTLSSettings{
   523  				Mode:              networking.ServerTLSSettings_SIMPLE,
   524  				ServerCertificate: "Captain Jean-Luc Picard",
   525  				PrivateKey:        "Khan Noonien Singh",
   526  				CaCertificates:    "Commander William T. Riker",
   527  				CredentialName:    "sds-name",
   528  			},
   529  			"", "",
   530  		},
   531  		{
   532  			"simple no server cert",
   533  			&networking.ServerTLSSettings{
   534  				Mode:              networking.ServerTLSSettings_SIMPLE,
   535  				ServerCertificate: "",
   536  				PrivateKey:        "Khan Noonien Singh",
   537  			},
   538  			"server certificate", "",
   539  		},
   540  		{
   541  			"simple no private key",
   542  			&networking.ServerTLSSettings{
   543  				Mode:              networking.ServerTLSSettings_SIMPLE,
   544  				ServerCertificate: "Captain Jean-Luc Picard",
   545  				PrivateKey:        "",
   546  			},
   547  			"private key", "",
   548  		},
   549  		{
   550  			"simple sds no server cert",
   551  			&networking.ServerTLSSettings{
   552  				Mode:              networking.ServerTLSSettings_SIMPLE,
   553  				ServerCertificate: "",
   554  				PrivateKey:        "Khan Noonien Singh",
   555  				CredentialName:    "sds-name",
   556  			},
   557  			"", "",
   558  		},
   559  		{
   560  			"simple sds no private key",
   561  			&networking.ServerTLSSettings{
   562  				Mode:              networking.ServerTLSSettings_SIMPLE,
   563  				ServerCertificate: "Captain Jean-Luc Picard",
   564  				PrivateKey:        "",
   565  				CredentialName:    "sds-name",
   566  			},
   567  			"", "",
   568  		},
   569  		{
   570  			"mutual",
   571  			&networking.ServerTLSSettings{
   572  				Mode:              networking.ServerTLSSettings_MUTUAL,
   573  				ServerCertificate: "Captain Jean-Luc Picard",
   574  				PrivateKey:        "Khan Noonien Singh",
   575  				CaCertificates:    "Commander William T. Riker",
   576  			},
   577  			"", "",
   578  		},
   579  		{
   580  			"mutual sds",
   581  			&networking.ServerTLSSettings{
   582  				Mode:              networking.ServerTLSSettings_MUTUAL,
   583  				ServerCertificate: "Captain Jean-Luc Picard",
   584  				PrivateKey:        "Khan Noonien Singh",
   585  				CaCertificates:    "Commander William T. Riker",
   586  				CredentialName:    "sds-name",
   587  			},
   588  			"", "",
   589  		},
   590  		{
   591  			"mutual no server cert",
   592  			&networking.ServerTLSSettings{
   593  				Mode:              networking.ServerTLSSettings_MUTUAL,
   594  				ServerCertificate: "",
   595  				PrivateKey:        "Khan Noonien Singh",
   596  				CaCertificates:    "Commander William T. Riker",
   597  			},
   598  			"server certificate", "",
   599  		},
   600  		{
   601  			"mutual sds no server cert",
   602  			&networking.ServerTLSSettings{
   603  				Mode:              networking.ServerTLSSettings_MUTUAL,
   604  				ServerCertificate: "",
   605  				PrivateKey:        "Khan Noonien Singh",
   606  				CaCertificates:    "Commander William T. Riker",
   607  				CredentialName:    "sds-name",
   608  			},
   609  			"", "",
   610  		},
   611  		{
   612  			"mutual no client CA bundle",
   613  			&networking.ServerTLSSettings{
   614  				Mode:              networking.ServerTLSSettings_MUTUAL,
   615  				ServerCertificate: "Captain Jean-Luc Picard",
   616  				PrivateKey:        "Khan Noonien Singh",
   617  				CaCertificates:    "",
   618  			},
   619  			"client CA bundle", "",
   620  		},
   621  		// this pair asserts we get errors about both client and server certs missing when in mutual mode
   622  		// and both are absent, but requires less rewriting of the testing harness than merging the cases
   623  		{
   624  			"mutual no certs",
   625  			&networking.ServerTLSSettings{
   626  				Mode:              networking.ServerTLSSettings_MUTUAL,
   627  				ServerCertificate: "",
   628  				PrivateKey:        "",
   629  				CaCertificates:    "",
   630  			},
   631  			"server certificate", "",
   632  		},
   633  		{
   634  			"mutual no certs",
   635  			&networking.ServerTLSSettings{
   636  				Mode:              networking.ServerTLSSettings_MUTUAL,
   637  				ServerCertificate: "",
   638  				PrivateKey:        "",
   639  				CaCertificates:    "",
   640  			},
   641  			"private key", "",
   642  		},
   643  		{
   644  			"mutual no certs",
   645  			&networking.ServerTLSSettings{
   646  				Mode:              networking.ServerTLSSettings_MUTUAL,
   647  				ServerCertificate: "",
   648  				PrivateKey:        "",
   649  				CaCertificates:    "",
   650  			},
   651  			"client CA bundle", "",
   652  		},
   653  		{
   654  			"optional mutual no certs",
   655  			&networking.ServerTLSSettings{
   656  				Mode:              networking.ServerTLSSettings_OPTIONAL_MUTUAL,
   657  				ServerCertificate: "",
   658  				PrivateKey:        "",
   659  				CaCertificates:    "",
   660  			},
   661  			"server certificate", "",
   662  		},
   663  		{
   664  			"optional mutual no certs",
   665  			&networking.ServerTLSSettings{
   666  				Mode:              networking.ServerTLSSettings_OPTIONAL_MUTUAL,
   667  				ServerCertificate: "",
   668  				PrivateKey:        "",
   669  				CaCertificates:    "",
   670  			},
   671  			"private key", "",
   672  		},
   673  		{
   674  			"optional mutual no certs",
   675  			&networking.ServerTLSSettings{
   676  				Mode:              networking.ServerTLSSettings_OPTIONAL_MUTUAL,
   677  				ServerCertificate: "",
   678  				PrivateKey:        "",
   679  				CaCertificates:    "",
   680  			},
   681  			"client CA bundle", "",
   682  		},
   683  		{
   684  			"pass through sds no certs",
   685  			&networking.ServerTLSSettings{
   686  				Mode:              networking.ServerTLSSettings_PASSTHROUGH,
   687  				ServerCertificate: "",
   688  				CaCertificates:    "",
   689  				CredentialName:    "sds-name",
   690  			},
   691  			"", "PASSTHROUGH mode does not use certificates",
   692  		},
   693  		{
   694  			"pass through sds crl",
   695  			&networking.ServerTLSSettings{
   696  				Mode:              networking.ServerTLSSettings_PASSTHROUGH,
   697  				ServerCertificate: "",
   698  				CaCertificates:    "",
   699  				CaCrl:             "scrl",
   700  			},
   701  			"", "PASSTHROUGH mode does not use certificates",
   702  		},
   703  		{
   704  			"istio_mutual no certs",
   705  			&networking.ServerTLSSettings{
   706  				Mode:              networking.ServerTLSSettings_ISTIO_MUTUAL,
   707  				ServerCertificate: "",
   708  				PrivateKey:        "",
   709  				CaCertificates:    "",
   710  			},
   711  			"", "",
   712  		},
   713  		{
   714  			"istio_mutual with server cert",
   715  			&networking.ServerTLSSettings{
   716  				Mode:              networking.ServerTLSSettings_ISTIO_MUTUAL,
   717  				ServerCertificate: "Captain Jean-Luc Picard",
   718  			},
   719  			"cannot have associated server cert", "",
   720  		},
   721  		{
   722  			"istio_mutual with client bundle",
   723  			&networking.ServerTLSSettings{
   724  				Mode:              networking.ServerTLSSettings_ISTIO_MUTUAL,
   725  				ServerCertificate: "Captain Jean-Luc Picard",
   726  				PrivateKey:        "Khan Noonien Singh",
   727  				CaCertificates:    "Commander William T. Riker",
   728  			},
   729  			"cannot have associated", "",
   730  		},
   731  		{
   732  			"istio_mutual with private key",
   733  			&networking.ServerTLSSettings{
   734  				Mode:       networking.ServerTLSSettings_ISTIO_MUTUAL,
   735  				PrivateKey: "Khan Noonien Singh",
   736  			},
   737  			"cannot have associated private key", "",
   738  		},
   739  		{
   740  			"istio_mutual with credential name",
   741  			&networking.ServerTLSSettings{
   742  				Mode:           networking.ServerTLSSettings_ISTIO_MUTUAL,
   743  				CredentialName: "some-cred",
   744  			},
   745  			"cannot have associated credentialName", "",
   746  		},
   747  		{
   748  			"invalid cipher suites",
   749  			&networking.ServerTLSSettings{
   750  				Mode:           networking.ServerTLSSettings_SIMPLE,
   751  				CredentialName: "sds-name",
   752  				CipherSuites:   []string{"not-a-cipher-suite"},
   753  			},
   754  			"", "not-a-cipher-suite",
   755  		},
   756  		{
   757  			"valid cipher suites",
   758  			&networking.ServerTLSSettings{
   759  				Mode:           networking.ServerTLSSettings_SIMPLE,
   760  				CredentialName: "sds-name",
   761  				CipherSuites:   []string{"ECDHE-ECDSA-AES128-SHA"},
   762  			},
   763  			"", "",
   764  		},
   765  		{
   766  			"cipher suites operations",
   767  			&networking.ServerTLSSettings{
   768  				Mode:           networking.ServerTLSSettings_SIMPLE,
   769  				CredentialName: "sds-name",
   770  				CipherSuites:   []string{"-ECDHE-ECDSA-AES128-SHA"},
   771  			},
   772  			"", "",
   773  		},
   774  		{
   775  			"duplicate cipher suites",
   776  			&networking.ServerTLSSettings{
   777  				Mode:           networking.ServerTLSSettings_SIMPLE,
   778  				CredentialName: "sds-name",
   779  				CipherSuites:   []string{"ECDHE-ECDSA-AES128-SHA", "ECDHE-ECDSA-AES128-SHA"},
   780  			},
   781  			"", "ECDHE-ECDSA-AES128-SHA",
   782  		},
   783  		{
   784  			"invalid cipher suites with invalid config",
   785  			&networking.ServerTLSSettings{
   786  				Mode:         networking.ServerTLSSettings_SIMPLE,
   787  				CipherSuites: []string{"not-a-cipher-suite"},
   788  			},
   789  			"requires a private key", "not-a-cipher-suite",
   790  		},
   791  		{
   792  			"crl specified for SIMPLE TLS",
   793  			&networking.ServerTLSSettings{
   794  				Mode:  networking.ServerTLSSettings_SIMPLE,
   795  				CaCrl: "crl",
   796  			},
   797  			"CRL is not supported with SIMPLE TLS", "",
   798  		},
   799  		{
   800  			"crl specified for CredentialName",
   801  			&networking.ServerTLSSettings{
   802  				Mode:           networking.ServerTLSSettings_SIMPLE,
   803  				CaCrl:          "crl",
   804  				CredentialName: "credential",
   805  			},
   806  			"", "",
   807  		},
   808  	}
   809  	for _, tt := range tests {
   810  		t.Run(tt.name, func(t *testing.T) {
   811  			v := validateTLSOptions(tt.in)
   812  			warn, err := v.Unwrap()
   813  			checkValidationMessage(t, warn, err, tt.warning, tt.out)
   814  		})
   815  	}
   816  }
   817  
   818  func TestValidateHTTPHeaderName(t *testing.T) {
   819  	testCases := []struct {
   820  		name  string
   821  		valid bool
   822  	}{
   823  		{name: "header1", valid: true},
   824  		{name: "X-Requested-With", valid: true},
   825  		{name: "", valid: false},
   826  	}
   827  
   828  	for _, tc := range testCases {
   829  		if got := ValidateHTTPHeaderName(tc.name); (got == nil) != tc.valid {
   830  			t.Errorf("ValidateHTTPHeaderName(%q) => got valid=%v, want valid=%v",
   831  				tc.name, got == nil, tc.valid)
   832  		}
   833  	}
   834  }
   835  
   836  func TestValidateCORSPolicy(t *testing.T) {
   837  	testCases := []struct {
   838  		name  string
   839  		in    *networking.CorsPolicy
   840  		valid bool
   841  	}{
   842  		{name: "valid", in: &networking.CorsPolicy{
   843  			AllowMethods:  []string{"GET", "POST"},
   844  			AllowHeaders:  []string{"header1", "header2"},
   845  			ExposeHeaders: []string{"header3"},
   846  			MaxAge:        &durationpb.Duration{Seconds: 2},
   847  		}, valid: true},
   848  		{name: "bad method", in: &networking.CorsPolicy{
   849  			AllowMethods:  []string{"GET", "PUTT"},
   850  			AllowHeaders:  []string{"header1", "header2"},
   851  			ExposeHeaders: []string{"header3"},
   852  			MaxAge:        &durationpb.Duration{Seconds: 2},
   853  		}, valid: false},
   854  		{name: "bad header", in: &networking.CorsPolicy{
   855  			AllowMethods:  []string{"GET", "POST"},
   856  			AllowHeaders:  []string{"header1", "header2"},
   857  			ExposeHeaders: []string{""},
   858  			MaxAge:        &durationpb.Duration{Seconds: 2},
   859  		}, valid: false},
   860  		{name: "bad max age", in: &networking.CorsPolicy{
   861  			AllowMethods:  []string{"GET", "POST"},
   862  			AllowHeaders:  []string{"header1", "header2"},
   863  			ExposeHeaders: []string{"header3"},
   864  			MaxAge:        &durationpb.Duration{Seconds: 2, Nanos: 42},
   865  		}, valid: false},
   866  		{name: "empty matchType AllowOrigins", in: &networking.CorsPolicy{
   867  			AllowOrigins: []*networking.StringMatch{
   868  				{MatchType: &networking.StringMatch_Exact{Exact: ""}},
   869  				{MatchType: &networking.StringMatch_Prefix{Prefix: ""}},
   870  				{MatchType: &networking.StringMatch_Regex{Regex: ""}},
   871  			},
   872  			AllowMethods:  []string{"GET", "POST"},
   873  			AllowHeaders:  []string{"header1", "header2"},
   874  			ExposeHeaders: []string{"header3"},
   875  			MaxAge:        &durationpb.Duration{Seconds: 2},
   876  		}, valid: false},
   877  		{name: "non empty matchType AllowOrigins", in: &networking.CorsPolicy{
   878  			AllowOrigins: []*networking.StringMatch{
   879  				{MatchType: &networking.StringMatch_Exact{Exact: "exact"}},
   880  				{MatchType: &networking.StringMatch_Prefix{Prefix: "prefix"}},
   881  				{MatchType: &networking.StringMatch_Regex{Regex: "regex"}},
   882  			},
   883  			AllowMethods:  []string{"GET", "POST"},
   884  			AllowHeaders:  []string{"header1", "header2"},
   885  			ExposeHeaders: []string{"header3"},
   886  			MaxAge:        &durationpb.Duration{Seconds: 2},
   887  		}, valid: true},
   888  	}
   889  
   890  	for _, tc := range testCases {
   891  		t.Run(tc.name, func(t *testing.T) {
   892  			if got := validateCORSPolicy(tc.in); (got == nil) != tc.valid {
   893  				t.Errorf("got valid=%v, want valid=%v: %v",
   894  					got == nil, tc.valid, got)
   895  			}
   896  		})
   897  	}
   898  }
   899  
   900  func TestValidateHTTPStatus(t *testing.T) {
   901  	testCases := []struct {
   902  		in    int32
   903  		valid bool
   904  	}{
   905  		{-100, false},
   906  		{0, false},
   907  		{200, true},
   908  		{600, true},
   909  		{601, false},
   910  	}
   911  
   912  	for _, tc := range testCases {
   913  		if got := validateHTTPStatus(tc.in); (got == nil) != tc.valid {
   914  			t.Errorf("validateHTTPStatus(%d) => got valid=%v, want valid=%v",
   915  				tc.in, got, tc.valid)
   916  		}
   917  	}
   918  }
   919  
   920  func TestValidateHTTPFaultInjectionAbort(t *testing.T) {
   921  	testCases := []struct {
   922  		name  string
   923  		in    *networking.HTTPFaultInjection_Abort
   924  		valid bool
   925  	}{
   926  		{name: "nil", in: nil, valid: true},
   927  		{name: "valid", in: &networking.HTTPFaultInjection_Abort{
   928  			Percentage: &networking.Percent{
   929  				Value: 20,
   930  			},
   931  			ErrorType: &networking.HTTPFaultInjection_Abort_HttpStatus{
   932  				HttpStatus: 200,
   933  			},
   934  		}, valid: true},
   935  		{name: "valid default", in: &networking.HTTPFaultInjection_Abort{
   936  			ErrorType: &networking.HTTPFaultInjection_Abort_HttpStatus{
   937  				HttpStatus: 200,
   938  			},
   939  		}, valid: true},
   940  		{name: "invalid http status", in: &networking.HTTPFaultInjection_Abort{
   941  			Percentage: &networking.Percent{
   942  				Value: 20,
   943  			},
   944  			ErrorType: &networking.HTTPFaultInjection_Abort_HttpStatus{
   945  				HttpStatus: 9000,
   946  			},
   947  		}, valid: false},
   948  		{name: "invalid low http status", in: &networking.HTTPFaultInjection_Abort{
   949  			Percentage: &networking.Percent{
   950  				Value: 20,
   951  			},
   952  			ErrorType: &networking.HTTPFaultInjection_Abort_HttpStatus{
   953  				HttpStatus: 100,
   954  			},
   955  		}, valid: false},
   956  		{name: "valid percentage", in: &networking.HTTPFaultInjection_Abort{
   957  			Percentage: &networking.Percent{
   958  				Value: 0.001,
   959  			},
   960  			ErrorType: &networking.HTTPFaultInjection_Abort_HttpStatus{
   961  				HttpStatus: 200,
   962  			},
   963  		}, valid: true},
   964  		{name: "invalid fractional percent", in: &networking.HTTPFaultInjection_Abort{
   965  			Percentage: &networking.Percent{
   966  				Value: -10.0,
   967  			},
   968  			ErrorType: &networking.HTTPFaultInjection_Abort_HttpStatus{
   969  				HttpStatus: 200,
   970  			},
   971  		}, valid: false},
   972  		{name: "grpc: nil", in: nil, valid: true},
   973  		{name: "grpc: valid", in: &networking.HTTPFaultInjection_Abort{
   974  			Percentage: &networking.Percent{
   975  				Value: 20,
   976  			},
   977  			ErrorType: &networking.HTTPFaultInjection_Abort_GrpcStatus{
   978  				GrpcStatus: "DEADLINE_EXCEEDED",
   979  			},
   980  		}, valid: true},
   981  		{name: "grpc: valid default percentage", in: &networking.HTTPFaultInjection_Abort{
   982  			ErrorType: &networking.HTTPFaultInjection_Abort_GrpcStatus{
   983  				GrpcStatus: "DEADLINE_EXCEEDED",
   984  			},
   985  		}, valid: true},
   986  		{name: "grpc: invalid status", in: &networking.HTTPFaultInjection_Abort{
   987  			Percentage: &networking.Percent{
   988  				Value: 20,
   989  			},
   990  			ErrorType: &networking.HTTPFaultInjection_Abort_GrpcStatus{
   991  				GrpcStatus: "BAD_STATUS",
   992  			},
   993  		}, valid: false},
   994  		{name: "grpc: valid percentage", in: &networking.HTTPFaultInjection_Abort{
   995  			Percentage: &networking.Percent{
   996  				Value: 0.001,
   997  			},
   998  			ErrorType: &networking.HTTPFaultInjection_Abort_GrpcStatus{
   999  				GrpcStatus: "INTERNAL",
  1000  			},
  1001  		}, valid: true},
  1002  		{name: "grpc: invalid fractional percent", in: &networking.HTTPFaultInjection_Abort{
  1003  			Percentage: &networking.Percent{
  1004  				Value: -10.0,
  1005  			},
  1006  			ErrorType: &networking.HTTPFaultInjection_Abort_GrpcStatus{
  1007  				GrpcStatus: "DEADLINE_EXCEEDED",
  1008  			},
  1009  		}, valid: false},
  1010  	}
  1011  
  1012  	for _, tc := range testCases {
  1013  		t.Run(tc.name, func(t *testing.T) {
  1014  			if got := validateHTTPFaultInjectionAbort(tc.in); (got.Err == nil) != tc.valid {
  1015  				t.Errorf("got valid=%v, want valid=%v: %v",
  1016  					got.Err == nil, tc.valid, got)
  1017  			}
  1018  		})
  1019  	}
  1020  }
  1021  
  1022  func TestValidateHTTPFaultInjectionDelay(t *testing.T) {
  1023  	testCases := []struct {
  1024  		name  string
  1025  		in    *networking.HTTPFaultInjection_Delay
  1026  		valid bool
  1027  	}{
  1028  		{name: "nil", in: nil, valid: true},
  1029  		{name: "valid fixed", in: &networking.HTTPFaultInjection_Delay{
  1030  			Percentage: &networking.Percent{
  1031  				Value: 20,
  1032  			},
  1033  			HttpDelayType: &networking.HTTPFaultInjection_Delay_FixedDelay{
  1034  				FixedDelay: &durationpb.Duration{Seconds: 3},
  1035  			},
  1036  		}, valid: true},
  1037  		{name: "valid default", in: &networking.HTTPFaultInjection_Delay{
  1038  			HttpDelayType: &networking.HTTPFaultInjection_Delay_FixedDelay{
  1039  				FixedDelay: &durationpb.Duration{Seconds: 3},
  1040  			},
  1041  		}, valid: true},
  1042  		{name: "invalid percent", in: &networking.HTTPFaultInjection_Delay{
  1043  			Percentage: &networking.Percent{
  1044  				Value: 101,
  1045  			},
  1046  			HttpDelayType: &networking.HTTPFaultInjection_Delay_FixedDelay{
  1047  				FixedDelay: &durationpb.Duration{Seconds: 3},
  1048  			},
  1049  		}, valid: false},
  1050  		{name: "invalid delay", in: &networking.HTTPFaultInjection_Delay{
  1051  			Percentage: &networking.Percent{
  1052  				Value: 20,
  1053  			},
  1054  			HttpDelayType: &networking.HTTPFaultInjection_Delay_FixedDelay{
  1055  				FixedDelay: &durationpb.Duration{Seconds: 3, Nanos: 42},
  1056  			},
  1057  		}, valid: false},
  1058  		{name: "valid fractional percentage", in: &networking.HTTPFaultInjection_Delay{
  1059  			Percentage: &networking.Percent{
  1060  				Value: 0.001,
  1061  			},
  1062  			HttpDelayType: &networking.HTTPFaultInjection_Delay_FixedDelay{
  1063  				FixedDelay: &durationpb.Duration{Seconds: 3},
  1064  			},
  1065  		}, valid: true},
  1066  		{name: "invalid fractional percentage", in: &networking.HTTPFaultInjection_Delay{
  1067  			Percentage: &networking.Percent{
  1068  				Value: -10.0,
  1069  			},
  1070  			HttpDelayType: &networking.HTTPFaultInjection_Delay_FixedDelay{
  1071  				FixedDelay: &durationpb.Duration{Seconds: 3},
  1072  			},
  1073  		}, valid: false},
  1074  	}
  1075  
  1076  	for _, tc := range testCases {
  1077  		t.Run(tc.name, func(t *testing.T) {
  1078  			if got := validateHTTPFaultInjectionDelay(tc.in); (got == nil) != tc.valid {
  1079  				t.Errorf("got valid=%v, want valid=%v: %v",
  1080  					got == nil, tc.valid, got)
  1081  			}
  1082  		})
  1083  	}
  1084  }
  1085  
  1086  func TestValidateHTTPRetry(t *testing.T) {
  1087  	testCases := []struct {
  1088  		name  string
  1089  		in    *networking.HTTPRetry
  1090  		valid bool
  1091  	}{
  1092  		{name: "valid", in: &networking.HTTPRetry{
  1093  			Attempts:      10,
  1094  			PerTryTimeout: &durationpb.Duration{Seconds: 2},
  1095  			RetryOn:       "5xx,gateway-error",
  1096  		}, valid: true},
  1097  		{name: "disable retries", in: &networking.HTTPRetry{
  1098  			Attempts: 0,
  1099  		}, valid: true},
  1100  		{name: "invalid, retry policy configured but attempts set to zero", in: &networking.HTTPRetry{
  1101  			Attempts:      0,
  1102  			PerTryTimeout: &durationpb.Duration{Seconds: 2},
  1103  			RetryOn:       "5xx,gateway-error",
  1104  		}, valid: false},
  1105  		{name: "valid default", in: &networking.HTTPRetry{
  1106  			Attempts: 10,
  1107  		}, valid: true},
  1108  		{name: "valid http status retryOn", in: &networking.HTTPRetry{
  1109  			Attempts:      10,
  1110  			PerTryTimeout: &durationpb.Duration{Seconds: 2},
  1111  			RetryOn:       "503,connect-failure",
  1112  		}, valid: true},
  1113  		{name: "invalid attempts", in: &networking.HTTPRetry{
  1114  			Attempts:      -1,
  1115  			PerTryTimeout: &durationpb.Duration{Seconds: 2},
  1116  		}, valid: false},
  1117  		{name: "invalid timeout", in: &networking.HTTPRetry{
  1118  			Attempts:      10,
  1119  			PerTryTimeout: &durationpb.Duration{Seconds: 2, Nanos: 1},
  1120  		}, valid: false},
  1121  		{name: "timeout too small", in: &networking.HTTPRetry{
  1122  			Attempts:      10,
  1123  			PerTryTimeout: &durationpb.Duration{Nanos: 999},
  1124  		}, valid: false},
  1125  		{name: "invalid policy retryOn", in: &networking.HTTPRetry{
  1126  			Attempts:      10,
  1127  			PerTryTimeout: &durationpb.Duration{Seconds: 2},
  1128  			RetryOn:       "5xx,invalid policy",
  1129  		}, valid: false},
  1130  		{name: "invalid http status retryOn", in: &networking.HTTPRetry{
  1131  			Attempts:      10,
  1132  			PerTryTimeout: &durationpb.Duration{Seconds: 2},
  1133  			RetryOn:       "600,connect-failure",
  1134  		}, valid: false},
  1135  		{name: "invalid, retryRemoteLocalities configured but attempts set to zero", in: &networking.HTTPRetry{
  1136  			Attempts:              0,
  1137  			RetryRemoteLocalities: &wrapperspb.BoolValue{Value: false},
  1138  		}, valid: false},
  1139  	}
  1140  
  1141  	for _, tc := range testCases {
  1142  		t.Run(tc.name, func(t *testing.T) {
  1143  			if got := validateHTTPRetry(tc.in); (got == nil) != tc.valid {
  1144  				t.Errorf("got valid=%v, want valid=%v: %v",
  1145  					got == nil, tc.valid, got)
  1146  			}
  1147  		})
  1148  	}
  1149  }
  1150  
  1151  func TestValidateHTTPRewrite(t *testing.T) {
  1152  	testCases := []struct {
  1153  		name  string
  1154  		in    *networking.HTTPRewrite
  1155  		valid bool
  1156  	}{
  1157  		{
  1158  			name:  "nil in",
  1159  			in:    nil,
  1160  			valid: true,
  1161  		},
  1162  		{
  1163  			name: "uri and authority",
  1164  			in: &networking.HTTPRewrite{
  1165  				Uri:       "/path/to/resource",
  1166  				Authority: "foobar.org",
  1167  			},
  1168  			valid: true,
  1169  		},
  1170  		{
  1171  			name: "uri",
  1172  			in: &networking.HTTPRewrite{
  1173  				Uri: "/path/to/resource",
  1174  			},
  1175  			valid: true,
  1176  		},
  1177  		{
  1178  			name: "authority",
  1179  			in: &networking.HTTPRewrite{
  1180  				Authority: "foobar.org",
  1181  			},
  1182  			valid: true,
  1183  		},
  1184  		{
  1185  			name: "uriRegexRewrite",
  1186  			in: &networking.HTTPRewrite{
  1187  				UriRegexRewrite: &networking.RegexRewrite{
  1188  					Match:   "^/service/([^/]+)(/.*)$",
  1189  					Rewrite: `\2/instance/\1`,
  1190  				},
  1191  			},
  1192  			valid: true,
  1193  		},
  1194  		{
  1195  			name: "uriRegexRewrite and authority",
  1196  			in: &networking.HTTPRewrite{
  1197  				Authority: "foobar.org",
  1198  				UriRegexRewrite: &networking.RegexRewrite{
  1199  					Match:   "^/service/([^/]+)(/.*)$",
  1200  					Rewrite: `\2/instance/\1`,
  1201  				},
  1202  			},
  1203  			valid: true,
  1204  		},
  1205  		{
  1206  			name: "uriRegexRewrite and uri",
  1207  			in: &networking.HTTPRewrite{
  1208  				Uri: "/path/to/resource",
  1209  				UriRegexRewrite: &networking.RegexRewrite{
  1210  					Match:   "^/service/([^/]+)(/.*)$",
  1211  					Rewrite: `\2/instance/\1`,
  1212  				},
  1213  			},
  1214  			valid: false,
  1215  		},
  1216  		{
  1217  			name:  "no uri, uriRegexRewrite, or authority",
  1218  			in:    &networking.HTTPRewrite{},
  1219  			valid: false,
  1220  		},
  1221  	}
  1222  
  1223  	for _, tc := range testCases {
  1224  		t.Run(tc.name, func(t *testing.T) {
  1225  			if got := validateHTTPRewrite(tc.in); (got == nil) != tc.valid {
  1226  				t.Errorf("got valid=%v, want valid=%v: %v",
  1227  					got == nil, tc.valid, got)
  1228  			}
  1229  		})
  1230  	}
  1231  }
  1232  
  1233  func TestValidateUriRegexRewrite(t *testing.T) {
  1234  	testCases := []struct {
  1235  		name  string
  1236  		in    *networking.RegexRewrite
  1237  		valid bool
  1238  	}{
  1239  		{
  1240  			name:  "uriRegexRewrite nil",
  1241  			in:    nil,
  1242  			valid: true,
  1243  		},
  1244  		{
  1245  			name: "uriRegexRewrite happy path",
  1246  			in: &networking.RegexRewrite{
  1247  				Match:   "^/service/([^/]+)(/.*)$",
  1248  				Rewrite: `\2/instance/\1`,
  1249  			},
  1250  			valid: true,
  1251  		},
  1252  		{
  1253  			name: "uriRegexRewrite missing match",
  1254  			in: &networking.RegexRewrite{
  1255  				Rewrite: `\2/instance/\1`,
  1256  			},
  1257  			valid: false,
  1258  		},
  1259  		{
  1260  			name: "uriRegexRewrite missing rewrite",
  1261  			in: &networking.RegexRewrite{
  1262  				Match: "^/service/([^/]+)(/.*)$",
  1263  			},
  1264  			valid: false,
  1265  		},
  1266  		{
  1267  			name: "uriRegexRewrite invalid regex patterns",
  1268  			in: &networking.RegexRewrite{
  1269  				Match:   "[",
  1270  				Rewrite: "[",
  1271  			},
  1272  			valid: false,
  1273  		},
  1274  	}
  1275  
  1276  	for _, tc := range testCases {
  1277  		t.Run(tc.name, func(t *testing.T) {
  1278  			if got := validateURIRegexRewrite(tc.in); (got == nil) != tc.valid {
  1279  				t.Errorf("got valid=%v, want valid=%v: %v",
  1280  					got == nil, tc.valid, got)
  1281  			}
  1282  		})
  1283  	}
  1284  }
  1285  
  1286  func TestValidatePortName(t *testing.T) {
  1287  	testCases := []struct {
  1288  		name  string
  1289  		valid bool
  1290  	}{
  1291  		{
  1292  			name:  "",
  1293  			valid: false,
  1294  		},
  1295  		{
  1296  			name:  "simple",
  1297  			valid: true,
  1298  		},
  1299  		{
  1300  			name:  "full",
  1301  			valid: true,
  1302  		},
  1303  		{
  1304  			name:  "toolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolong",
  1305  			valid: false,
  1306  		},
  1307  	}
  1308  
  1309  	for _, tc := range testCases {
  1310  		t.Run(tc.name, func(t *testing.T) {
  1311  			if err := ValidatePortName(tc.name); (err == nil) != tc.valid {
  1312  				t.Fatalf("got valid=%v but wanted valid=%v: %v", err == nil, tc.valid, err)
  1313  			}
  1314  		})
  1315  	}
  1316  }
  1317  
  1318  func TestValidateHTTPRedirect(t *testing.T) {
  1319  	testCases := []struct {
  1320  		name     string
  1321  		redirect *networking.HTTPRedirect
  1322  		valid    bool
  1323  	}{
  1324  		{
  1325  			name:     "nil redirect",
  1326  			redirect: nil,
  1327  			valid:    true,
  1328  		},
  1329  		{
  1330  			name: "empty uri and authority",
  1331  			redirect: &networking.HTTPRedirect{
  1332  				Uri:       "",
  1333  				Authority: "",
  1334  			},
  1335  			valid: false,
  1336  		},
  1337  		{
  1338  			name: "too small redirect code",
  1339  			redirect: &networking.HTTPRedirect{
  1340  				Uri:          "t",
  1341  				Authority:    "",
  1342  				RedirectCode: 299,
  1343  			},
  1344  			valid: false,
  1345  		},
  1346  		{
  1347  			name: "too large redirect code",
  1348  			redirect: &networking.HTTPRedirect{
  1349  				Uri:          "t",
  1350  				Authority:    "",
  1351  				RedirectCode: 400,
  1352  			},
  1353  			valid: false,
  1354  		},
  1355  		{
  1356  			name: "empty authority",
  1357  			redirect: &networking.HTTPRedirect{
  1358  				Uri:       "t",
  1359  				Authority: "",
  1360  			},
  1361  			valid: true,
  1362  		},
  1363  		{
  1364  			name: "empty uri",
  1365  			redirect: &networking.HTTPRedirect{
  1366  				Uri:       "",
  1367  				Authority: "t",
  1368  			},
  1369  			valid: true,
  1370  		},
  1371  		{
  1372  			name: "empty redirect code",
  1373  			redirect: &networking.HTTPRedirect{
  1374  				Uri:          "t",
  1375  				Authority:    "t",
  1376  				RedirectCode: 0,
  1377  			},
  1378  			valid: true,
  1379  		},
  1380  		{
  1381  			name: "normal redirect",
  1382  			redirect: &networking.HTTPRedirect{
  1383  				Uri:          "t",
  1384  				Authority:    "t",
  1385  				RedirectCode: 308,
  1386  			},
  1387  			valid: true,
  1388  		},
  1389  	}
  1390  
  1391  	for _, tc := range testCases {
  1392  		t.Run(tc.name, func(t *testing.T) {
  1393  			if err := validateHTTPRedirect(tc.redirect); (err == nil) != tc.valid {
  1394  				t.Fatalf("got valid=%v but wanted valid=%v: %v", err == nil, tc.valid, err)
  1395  			}
  1396  		})
  1397  	}
  1398  }
  1399  
  1400  func TestValidateHTTPDirectResponse(t *testing.T) {
  1401  	testCases := []struct {
  1402  		name           string
  1403  		directResponse *networking.HTTPDirectResponse
  1404  		valid          bool
  1405  		warning        bool
  1406  	}{
  1407  		{
  1408  			name:           "nil redirect",
  1409  			directResponse: nil,
  1410  			valid:          true,
  1411  		},
  1412  		{
  1413  			name: "status 200",
  1414  			directResponse: &networking.HTTPDirectResponse{
  1415  				Status: 200,
  1416  			},
  1417  			valid: true,
  1418  		},
  1419  		{
  1420  			name: "status 100",
  1421  			directResponse: &networking.HTTPDirectResponse{
  1422  				Status: 199,
  1423  			},
  1424  			valid: false,
  1425  		},
  1426  		{
  1427  			name: "status 600",
  1428  			directResponse: &networking.HTTPDirectResponse{
  1429  				Status: 601,
  1430  			},
  1431  			valid: false,
  1432  		},
  1433  		{
  1434  			name: "with string body",
  1435  			directResponse: &networking.HTTPDirectResponse{
  1436  				Status: 200,
  1437  				Body: &networking.HTTPBody{
  1438  					Specifier: &networking.HTTPBody_String_{String_: "hello"},
  1439  				},
  1440  			},
  1441  			valid: true,
  1442  		},
  1443  		{
  1444  			name: "with string body over 100kb",
  1445  			directResponse: &networking.HTTPDirectResponse{
  1446  				Status: 200,
  1447  				Body: &networking.HTTPBody{
  1448  					Specifier: &networking.HTTPBody_String_{String_: strings.Repeat("a", 101*kb)},
  1449  				},
  1450  			},
  1451  			valid:   true,
  1452  			warning: true,
  1453  		},
  1454  		{
  1455  			name: "with string body over 1mb",
  1456  			directResponse: &networking.HTTPDirectResponse{
  1457  				Status: 200,
  1458  				Body: &networking.HTTPBody{
  1459  					Specifier: &networking.HTTPBody_String_{String_: strings.Repeat("a", 2*mb)},
  1460  				},
  1461  			},
  1462  			valid: false,
  1463  		},
  1464  		{
  1465  			name: "with bytes body",
  1466  			directResponse: &networking.HTTPDirectResponse{
  1467  				Status: 200,
  1468  				Body: &networking.HTTPBody{
  1469  					Specifier: &networking.HTTPBody_Bytes{Bytes: []byte("hello")},
  1470  				},
  1471  			},
  1472  			valid: true,
  1473  		},
  1474  		{
  1475  			name: "with bytes body over 100kb",
  1476  			directResponse: &networking.HTTPDirectResponse{
  1477  				Status: 200,
  1478  				Body: &networking.HTTPBody{
  1479  					Specifier: &networking.HTTPBody_Bytes{Bytes: []byte(strings.Repeat("a", (100*kb)+1))},
  1480  				},
  1481  			},
  1482  			valid:   true,
  1483  			warning: true,
  1484  		},
  1485  		{
  1486  			name: "with bytes body over 1mb",
  1487  			directResponse: &networking.HTTPDirectResponse{
  1488  				Status: 200,
  1489  				Body: &networking.HTTPBody{
  1490  					Specifier: &networking.HTTPBody_Bytes{Bytes: []byte(strings.Repeat("a", (1*mb)+1))},
  1491  				},
  1492  			},
  1493  			valid: false,
  1494  		},
  1495  	}
  1496  
  1497  	for _, tc := range testCases {
  1498  		t.Run(tc.name, func(t *testing.T) {
  1499  			if err := validateHTTPDirectResponse(tc.directResponse); (err.Err == nil) != tc.valid {
  1500  				t.Fatalf("got valid=%v but wanted valid=%v: %v", err.Err == nil, tc.valid, err)
  1501  			}
  1502  			if err := validateHTTPDirectResponse(tc.directResponse); (err.Warning != nil) != tc.warning {
  1503  				t.Fatalf("got valid=%v but wanted valid=%v: %v", err.Warning != nil, tc.warning, err)
  1504  			}
  1505  		})
  1506  	}
  1507  }
  1508  
  1509  func TestValidateDestination(t *testing.T) {
  1510  	testCases := []struct {
  1511  		name        string
  1512  		destination *networking.Destination
  1513  		valid       bool
  1514  	}{
  1515  		{
  1516  			name:        "empty",
  1517  			destination: &networking.Destination{}, // nothing
  1518  			valid:       false,
  1519  		},
  1520  		{
  1521  			name: "simple",
  1522  			destination: &networking.Destination{
  1523  				Host: "foo.bar",
  1524  			},
  1525  			valid: true,
  1526  		},
  1527  		{
  1528  			name: "full",
  1529  			destination: &networking.Destination{
  1530  				Host:   "foo.bar",
  1531  				Subset: "shiny",
  1532  				Port: &networking.PortSelector{
  1533  					Number: 5000,
  1534  				},
  1535  			},
  1536  			valid: true,
  1537  		},
  1538  		{
  1539  			name: "unnumbered-selector",
  1540  			destination: &networking.Destination{
  1541  				Host:   "foo.bar",
  1542  				Subset: "shiny",
  1543  				Port:   &networking.PortSelector{},
  1544  			},
  1545  			valid: false,
  1546  		},
  1547  	}
  1548  
  1549  	for _, tc := range testCases {
  1550  		t.Run(tc.name, func(t *testing.T) {
  1551  			if err := validateDestination(tc.destination); (err == nil) != tc.valid {
  1552  				t.Fatalf("got valid=%v but wanted valid=%v: %v", err == nil, tc.valid, err)
  1553  			}
  1554  		})
  1555  	}
  1556  }
  1557  
  1558  func TestValidateHTTPRoute(t *testing.T) {
  1559  	testCases := []struct {
  1560  		name  string
  1561  		route *networking.HTTPRoute
  1562  		valid bool
  1563  	}{
  1564  		{name: "empty", route: &networking.HTTPRoute{ // nothing
  1565  		}, valid:                                     false},
  1566  		{name: "simple", route: &networking.HTTPRoute{
  1567  			Route: []*networking.HTTPRouteDestination{{
  1568  				Destination: &networking.Destination{Host: "foo.baz"},
  1569  			}},
  1570  		}, valid: true},
  1571  		{name: "no destination", route: &networking.HTTPRoute{
  1572  			Route: []*networking.HTTPRouteDestination{{
  1573  				Destination: nil,
  1574  			}},
  1575  		}, valid: false},
  1576  		{name: "weighted", route: &networking.HTTPRoute{
  1577  			Route: []*networking.HTTPRouteDestination{{
  1578  				Destination: &networking.Destination{Host: "foo.baz.south"},
  1579  				Weight:      25,
  1580  			}, {
  1581  				Destination: &networking.Destination{Host: "foo.baz.east"},
  1582  				Weight:      75,
  1583  			}},
  1584  		}, valid: true},
  1585  		{name: "total weight > 100", route: &networking.HTTPRoute{
  1586  			Route: []*networking.HTTPRouteDestination{{
  1587  				Destination: &networking.Destination{Host: "foo.baz.south"},
  1588  				Weight:      550,
  1589  			}, {
  1590  				Destination: &networking.Destination{Host: "foo.baz.east"},
  1591  				Weight:      500,
  1592  			}},
  1593  		}, valid: true},
  1594  		{name: "total weight < 100", route: &networking.HTTPRoute{
  1595  			Route: []*networking.HTTPRouteDestination{{
  1596  				Destination: &networking.Destination{Host: "foo.baz.south"},
  1597  				Weight:      49,
  1598  			}, {
  1599  				Destination: &networking.Destination{Host: "foo.baz.east"},
  1600  				Weight:      50,
  1601  			}},
  1602  		}, valid: true},
  1603  		{name: "simple redirect", route: &networking.HTTPRoute{
  1604  			Redirect: &networking.HTTPRedirect{
  1605  				Uri:       "/lerp",
  1606  				Authority: "foo.biz",
  1607  			},
  1608  		}, valid: true},
  1609  		{name: "conflicting redirect and route", route: &networking.HTTPRoute{
  1610  			Route: []*networking.HTTPRouteDestination{{
  1611  				Destination: &networking.Destination{Host: "foo.baz"},
  1612  			}},
  1613  			Redirect: &networking.HTTPRedirect{
  1614  				Uri:       "/lerp",
  1615  				Authority: "foo.biz",
  1616  			},
  1617  		}, valid: false},
  1618  		{name: "request response headers", route: &networking.HTTPRoute{
  1619  			Route: []*networking.HTTPRouteDestination{{
  1620  				Destination: &networking.Destination{Host: "foo.baz"},
  1621  			}},
  1622  		}, valid: true},
  1623  		{name: "valid headers", route: &networking.HTTPRoute{
  1624  			Route: []*networking.HTTPRouteDestination{{
  1625  				Destination: &networking.Destination{Host: "foo.baz"},
  1626  				Headers: &networking.Headers{
  1627  					Request: &networking.Headers_HeaderOperations{
  1628  						Add: map[string]string{
  1629  							"name": "",
  1630  						},
  1631  						Set: map[string]string{
  1632  							"name": "",
  1633  						},
  1634  						Remove: []string{
  1635  							"name",
  1636  						},
  1637  					},
  1638  					Response: &networking.Headers_HeaderOperations{
  1639  						Add: map[string]string{
  1640  							"name": "",
  1641  						},
  1642  						Set: map[string]string{
  1643  							"name": "",
  1644  						},
  1645  						Remove: []string{
  1646  							"name",
  1647  						},
  1648  					},
  1649  				},
  1650  			}},
  1651  		}, valid: true},
  1652  		{name: "empty header name - request add", route: &networking.HTTPRoute{
  1653  			Route: []*networking.HTTPRouteDestination{{
  1654  				Destination: &networking.Destination{Host: "foo.baz"},
  1655  				Headers: &networking.Headers{
  1656  					Request: &networking.Headers_HeaderOperations{
  1657  						Add: map[string]string{
  1658  							"": "value",
  1659  						},
  1660  					},
  1661  				},
  1662  			}},
  1663  		}, valid: false},
  1664  		{name: "empty header name - request set", route: &networking.HTTPRoute{
  1665  			Route: []*networking.HTTPRouteDestination{{
  1666  				Destination: &networking.Destination{Host: "foo.baz"},
  1667  				Headers: &networking.Headers{
  1668  					Request: &networking.Headers_HeaderOperations{
  1669  						Set: map[string]string{
  1670  							"": "value",
  1671  						},
  1672  					},
  1673  				},
  1674  			}},
  1675  		}, valid: false},
  1676  		{name: "empty header name - request remove", route: &networking.HTTPRoute{
  1677  			Route: []*networking.HTTPRouteDestination{{
  1678  				Destination: &networking.Destination{Host: "foo.baz"},
  1679  				Headers: &networking.Headers{
  1680  					Request: &networking.Headers_HeaderOperations{
  1681  						Remove: []string{
  1682  							"",
  1683  						},
  1684  					},
  1685  				},
  1686  			}},
  1687  		}, valid: false},
  1688  		{name: "empty header name - response add", route: &networking.HTTPRoute{
  1689  			Route: []*networking.HTTPRouteDestination{{
  1690  				Destination: &networking.Destination{Host: "foo.baz"},
  1691  				Headers: &networking.Headers{
  1692  					Response: &networking.Headers_HeaderOperations{
  1693  						Add: map[string]string{
  1694  							"": "value",
  1695  						},
  1696  					},
  1697  				},
  1698  			}},
  1699  		}, valid: false},
  1700  		{name: "empty header name - response set", route: &networking.HTTPRoute{
  1701  			Route: []*networking.HTTPRouteDestination{{
  1702  				Destination: &networking.Destination{Host: "foo.baz"},
  1703  				Headers: &networking.Headers{
  1704  					Response: &networking.Headers_HeaderOperations{
  1705  						Set: map[string]string{
  1706  							"": "value",
  1707  						},
  1708  					},
  1709  				},
  1710  			}},
  1711  		}, valid: false},
  1712  		{name: "empty header name - response remove", route: &networking.HTTPRoute{
  1713  			Route: []*networking.HTTPRouteDestination{{
  1714  				Destination: &networking.Destination{Host: "foo.baz"},
  1715  				Headers: &networking.Headers{
  1716  					Response: &networking.Headers_HeaderOperations{
  1717  						Remove: []string{
  1718  							"",
  1719  						},
  1720  					},
  1721  				},
  1722  			}},
  1723  		}, valid: false},
  1724  		{name: "envoy escaped % set", route: &networking.HTTPRoute{
  1725  			Route: []*networking.HTTPRouteDestination{{
  1726  				Destination: &networking.Destination{Host: "foo.baz"},
  1727  				Headers: &networking.Headers{
  1728  					Response: &networking.Headers_HeaderOperations{
  1729  						Set: map[string]string{
  1730  							"i-love-istio": "100%%",
  1731  						},
  1732  					},
  1733  				},
  1734  			}},
  1735  		}, valid: true},
  1736  		{name: "envoy variable set", route: &networking.HTTPRoute{
  1737  			Route: []*networking.HTTPRouteDestination{{
  1738  				Destination: &networking.Destination{Host: "foo.baz"},
  1739  				Headers: &networking.Headers{
  1740  					Response: &networking.Headers_HeaderOperations{
  1741  						Set: map[string]string{
  1742  							"name": "%HOSTNAME%",
  1743  						},
  1744  					},
  1745  				},
  1746  			}},
  1747  		}, valid: true},
  1748  		{name: "envoy unescaped % set", route: &networking.HTTPRoute{
  1749  			Route: []*networking.HTTPRouteDestination{{
  1750  				Destination: &networking.Destination{Host: "foo.baz"},
  1751  				Headers: &networking.Headers{
  1752  					Response: &networking.Headers_HeaderOperations{
  1753  						Set: map[string]string{
  1754  							"name": "abcd%oijasodifj",
  1755  						},
  1756  					},
  1757  				},
  1758  			}},
  1759  		}, valid: false},
  1760  		{name: "envoy escaped % add", route: &networking.HTTPRoute{
  1761  			Route: []*networking.HTTPRouteDestination{{
  1762  				Destination: &networking.Destination{Host: "foo.baz"},
  1763  				Headers: &networking.Headers{
  1764  					Response: &networking.Headers_HeaderOperations{
  1765  						Add: map[string]string{
  1766  							"i-love-istio": "100%% and more",
  1767  						},
  1768  					},
  1769  				},
  1770  			}},
  1771  		}, valid: true},
  1772  		{name: "envoy variable add", route: &networking.HTTPRoute{
  1773  			Route: []*networking.HTTPRouteDestination{{
  1774  				Destination: &networking.Destination{Host: "foo.baz"},
  1775  				Headers: &networking.Headers{
  1776  					Response: &networking.Headers_HeaderOperations{
  1777  						Add: map[string]string{
  1778  							"name": "hello %HOSTNAME%",
  1779  						},
  1780  					},
  1781  				},
  1782  			}},
  1783  		}, valid: true},
  1784  		{name: "envoy unescaped % add", route: &networking.HTTPRoute{
  1785  			Route: []*networking.HTTPRouteDestination{{
  1786  				Destination: &networking.Destination{Host: "foo.baz"},
  1787  				Headers: &networking.Headers{
  1788  					Response: &networking.Headers_HeaderOperations{
  1789  						Add: map[string]string{
  1790  							"name": "abcd%oijasodifj",
  1791  						},
  1792  					},
  1793  				},
  1794  			}},
  1795  		}, valid: false},
  1796  		{name: "null header match", route: &networking.HTTPRoute{
  1797  			Route: []*networking.HTTPRouteDestination{{
  1798  				Destination: &networking.Destination{Host: "foo.bar"},
  1799  			}},
  1800  			Match: []*networking.HTTPMatchRequest{{
  1801  				Headers: map[string]*networking.StringMatch{
  1802  					"header": nil,
  1803  				},
  1804  			}},
  1805  		}, valid: false},
  1806  		{name: "empty prefix header match", route: &networking.HTTPRoute{
  1807  			Route: []*networking.HTTPRouteDestination{{
  1808  				Destination: &networking.Destination{Host: "foo.bar"},
  1809  			}},
  1810  			Match: []*networking.HTTPMatchRequest{{
  1811  				Headers: map[string]*networking.StringMatch{
  1812  					"emptyprefix": {MatchType: &networking.StringMatch_Prefix{Prefix: ""}},
  1813  				},
  1814  			}},
  1815  		}, valid: false},
  1816  		{name: "nil match", route: &networking.HTTPRoute{
  1817  			Route: []*networking.HTTPRouteDestination{{
  1818  				Destination: &networking.Destination{Host: "foo.bar"},
  1819  			}},
  1820  			Match: nil,
  1821  		}, valid: true},
  1822  		{name: "match with nil element", route: &networking.HTTPRoute{
  1823  			Route: []*networking.HTTPRouteDestination{{
  1824  				Destination: &networking.Destination{Host: "foo.bar"},
  1825  			}},
  1826  			Match: []*networking.HTTPMatchRequest{nil},
  1827  		}, valid: true},
  1828  		{name: "invalid mirror percent", route: &networking.HTTPRoute{
  1829  			MirrorPercent: &wrapperspb.UInt32Value{Value: 101},
  1830  			Route: []*networking.HTTPRouteDestination{{
  1831  				Destination: &networking.Destination{Host: "foo.bar"},
  1832  			}},
  1833  			Match: []*networking.HTTPMatchRequest{nil},
  1834  		}, valid: false},
  1835  		{name: "invalid mirror percentage", route: &networking.HTTPRoute{
  1836  			MirrorPercentage: &networking.Percent{
  1837  				Value: 101,
  1838  			},
  1839  			Route: []*networking.HTTPRouteDestination{{
  1840  				Destination: &networking.Destination{Host: "foo.bar"},
  1841  			}},
  1842  			Match: []*networking.HTTPMatchRequest{nil},
  1843  		}, valid: false},
  1844  		{name: "valid mirror percentage", route: &networking.HTTPRoute{
  1845  			MirrorPercentage: &networking.Percent{
  1846  				Value: 1,
  1847  			},
  1848  			Route: []*networking.HTTPRouteDestination{{
  1849  				Destination: &networking.Destination{Host: "foo.bar"},
  1850  			}},
  1851  			Match: []*networking.HTTPMatchRequest{nil},
  1852  		}, valid: true},
  1853  		{name: "negative mirror percentage", route: &networking.HTTPRoute{
  1854  			MirrorPercentage: &networking.Percent{
  1855  				Value: -1,
  1856  			},
  1857  			Route: []*networking.HTTPRouteDestination{{
  1858  				Destination: &networking.Destination{Host: "foo.bar"},
  1859  			}},
  1860  			Match: []*networking.HTTPMatchRequest{nil},
  1861  		}, valid: false},
  1862  		{name: "mirrors without destination", route: &networking.HTTPRoute{
  1863  			Mirrors: []*networking.HTTPMirrorPolicy{{
  1864  				Destination: nil,
  1865  			}},
  1866  			Route: []*networking.HTTPRouteDestination{{
  1867  				Destination: &networking.Destination{Host: "foo.bar"},
  1868  			}},
  1869  			Match: []*networking.HTTPMatchRequest{nil},
  1870  		}, valid: false},
  1871  		{name: "mirrors invalid mirror percentage", route: &networking.HTTPRoute{
  1872  			Mirrors: []*networking.HTTPMirrorPolicy{{
  1873  				Destination: &networking.Destination{Host: "foo.baz"},
  1874  			}, {
  1875  				Destination: &networking.Destination{Host: "foo.baz"},
  1876  				Percentage:  &networking.Percent{Value: 101},
  1877  			}},
  1878  			Route: []*networking.HTTPRouteDestination{{
  1879  				Destination: &networking.Destination{Host: "foo.bar"},
  1880  			}},
  1881  			Match: []*networking.HTTPMatchRequest{nil},
  1882  		}, valid: false},
  1883  		{name: "mirrors valid mirror percentage", route: &networking.HTTPRoute{
  1884  			Mirrors: []*networking.HTTPMirrorPolicy{{
  1885  				Destination: &networking.Destination{Host: "foo.baz"},
  1886  				Percentage:  &networking.Percent{Value: 1},
  1887  			}, {
  1888  				Destination: &networking.Destination{Host: "foo.baz"},
  1889  				Percentage:  &networking.Percent{Value: 50},
  1890  			}},
  1891  			Route: []*networking.HTTPRouteDestination{{
  1892  				Destination: &networking.Destination{Host: "foo.bar"},
  1893  			}},
  1894  			Match: []*networking.HTTPMatchRequest{nil},
  1895  		}, valid: true},
  1896  		{name: "mirrors negative mirror percentage", route: &networking.HTTPRoute{
  1897  			Mirrors: []*networking.HTTPMirrorPolicy{{
  1898  				Destination: &networking.Destination{Host: "foo.baz"},
  1899  				Percentage:  &networking.Percent{Value: -1},
  1900  			}},
  1901  			Route: []*networking.HTTPRouteDestination{{
  1902  				Destination: &networking.Destination{Host: "foo.bar"},
  1903  			}},
  1904  			Match: []*networking.HTTPMatchRequest{nil},
  1905  		}, valid: false},
  1906  		{name: "conflicting mirror and mirrors", route: &networking.HTTPRoute{
  1907  			Mirror: &networking.Destination{Host: "foo.baz"},
  1908  			Mirrors: []*networking.HTTPMirrorPolicy{{
  1909  				Destination: &networking.Destination{Host: "foo.bar"},
  1910  			}},
  1911  			Route: []*networking.HTTPRouteDestination{{
  1912  				Destination: &networking.Destination{Host: "foo.baz"},
  1913  			}},
  1914  		}, valid: false},
  1915  	}
  1916  
  1917  	for _, tc := range testCases {
  1918  		t.Run(tc.name, func(t *testing.T) {
  1919  			if err := validateHTTPRoute(tc.route, false, false); (err.Err == nil) != tc.valid {
  1920  				t.Fatalf("got valid=%v but wanted valid=%v: %v", err.Err == nil, tc.valid, err)
  1921  			}
  1922  		})
  1923  	}
  1924  }
  1925  
  1926  func TestValidateRouteDestination(t *testing.T) {
  1927  	testCases := []struct {
  1928  		name   string
  1929  		routes []*networking.RouteDestination
  1930  		valid  bool
  1931  	}{
  1932  		{name: "simple", routes: []*networking.RouteDestination{{
  1933  			Destination: &networking.Destination{Host: "foo.baz"},
  1934  		}}, valid: true},
  1935  		{name: "wildcard dash", routes: []*networking.RouteDestination{{
  1936  			Destination: &networking.Destination{Host: "*-foo.baz"},
  1937  		}}, valid: true},
  1938  		{name: "wildcard prefix", routes: []*networking.RouteDestination{{
  1939  			Destination: &networking.Destination{Host: "*foo.baz"},
  1940  		}}, valid: true},
  1941  		{name: "wildcard", routes: []*networking.RouteDestination{{
  1942  			Destination: &networking.Destination{Host: "*"},
  1943  		}}, valid: false},
  1944  		{name: "bad wildcard", routes: []*networking.RouteDestination{{
  1945  			Destination: &networking.Destination{Host: "foo.*"},
  1946  		}}, valid: false},
  1947  		{name: "bad fqdn", routes: []*networking.RouteDestination{{
  1948  			Destination: &networking.Destination{Host: "default/baz"},
  1949  		}}, valid: false},
  1950  		{name: "no destination", routes: []*networking.RouteDestination{{
  1951  			Destination: nil,
  1952  		}}, valid: false},
  1953  		{name: "weighted", routes: []*networking.RouteDestination{{
  1954  			Destination: &networking.Destination{Host: "foo.baz.south"},
  1955  			Weight:      25,
  1956  		}, {
  1957  			Destination: &networking.Destination{Host: "foo.baz.east"},
  1958  			Weight:      75,
  1959  		}}, valid: true},
  1960  		{name: "weight < 0", routes: []*networking.RouteDestination{{
  1961  			Destination: &networking.Destination{Host: "foo.baz.south"},
  1962  			Weight:      5,
  1963  		}, {
  1964  			Destination: &networking.Destination{Host: "foo.baz.east"},
  1965  			Weight:      -1,
  1966  		}}, valid: false},
  1967  		{name: "total weight > 100", routes: []*networking.RouteDestination{{
  1968  			Destination: &networking.Destination{Host: "foo.baz.south"},
  1969  			Weight:      550,
  1970  		}, {
  1971  			Destination: &networking.Destination{Host: "foo.baz.east"},
  1972  			Weight:      500,
  1973  		}}, valid: true},
  1974  		{name: "total weight < 100", routes: []*networking.RouteDestination{{
  1975  			Destination: &networking.Destination{Host: "foo.baz.south"},
  1976  			Weight:      49,
  1977  		}, {
  1978  			Destination: &networking.Destination{Host: "foo.baz.east"},
  1979  			Weight:      50,
  1980  		}}, valid: true},
  1981  		{name: "total weight = 100", routes: []*networking.RouteDestination{{
  1982  			Destination: &networking.Destination{Host: "foo.baz.south"},
  1983  			Weight:      100,
  1984  		}, {
  1985  			Destination: &networking.Destination{Host: "foo.baz.east"},
  1986  			Weight:      0,
  1987  		}}, valid: true},
  1988  		{name: "weight = 0", routes: []*networking.RouteDestination{{
  1989  			Destination: &networking.Destination{Host: "foo.baz.south"},
  1990  			Weight:      0,
  1991  		}}, valid: true},
  1992  		{name: "total weight = 0 with multi RouteDestination", routes: []*networking.RouteDestination{{
  1993  			Destination: &networking.Destination{Host: "foo.baz.south"},
  1994  			Weight:      0,
  1995  		}, {
  1996  			Destination: &networking.Destination{Host: "foo.baz.east"},
  1997  			Weight:      0,
  1998  		}}, valid: false},
  1999  	}
  2000  
  2001  	for _, tc := range testCases {
  2002  		t.Run(tc.name, func(t *testing.T) {
  2003  			if err := validateRouteDestinations(tc.routes, false); (err == nil) != tc.valid {
  2004  				t.Fatalf("got valid=%v but wanted valid=%v: %v", err == nil, tc.valid, err)
  2005  			}
  2006  		})
  2007  	}
  2008  }
  2009  
  2010  // TODO: add TCP test cases once it is implemented
  2011  func TestValidateVirtualService(t *testing.T) {
  2012  	testCases := []struct {
  2013  		name    string
  2014  		in      proto.Message
  2015  		valid   bool
  2016  		warning bool
  2017  	}{
  2018  		{name: "simple", in: &networking.VirtualService{
  2019  			Hosts: []string{"foo.bar"},
  2020  			Http: []*networking.HTTPRoute{{
  2021  				Route: []*networking.HTTPRouteDestination{{
  2022  					Destination: &networking.Destination{Host: "foo.baz"},
  2023  				}},
  2024  			}},
  2025  		}, valid: true},
  2026  		{name: "duplicate hosts", in: &networking.VirtualService{
  2027  			Hosts: []string{"*.foo.bar", "*.bar"},
  2028  			Http: []*networking.HTTPRoute{{
  2029  				Route: []*networking.HTTPRouteDestination{{
  2030  					Destination: &networking.Destination{Host: "foo.baz"},
  2031  				}},
  2032  			}},
  2033  		}, valid: false},
  2034  		{name: "with no destination", in: &networking.VirtualService{
  2035  			Hosts: []string{"*.foo.bar", "*.bar"},
  2036  			Http: []*networking.HTTPRoute{{
  2037  				Route: []*networking.HTTPRouteDestination{{}},
  2038  			}},
  2039  		}, valid: false},
  2040  		{name: "destination with out hosts", in: &networking.VirtualService{
  2041  			Hosts: []string{"*.foo.bar", "*.bar"},
  2042  			Http: []*networking.HTTPRoute{{
  2043  				Route: []*networking.HTTPRouteDestination{{
  2044  					Destination: &networking.Destination{},
  2045  				}},
  2046  			}},
  2047  		}, valid: false},
  2048  		{name: "delegate with no hosts", in: &networking.VirtualService{
  2049  			Hosts: nil,
  2050  			Http: []*networking.HTTPRoute{{
  2051  				Route: []*networking.HTTPRouteDestination{{
  2052  					Destination: &networking.Destination{Host: "foo.baz"},
  2053  				}},
  2054  			}},
  2055  		}, valid: true},
  2056  		{name: "bad host", in: &networking.VirtualService{
  2057  			Hosts: []string{"foo.ba!r"},
  2058  			Http: []*networking.HTTPRoute{{
  2059  				Route: []*networking.HTTPRouteDestination{{
  2060  					Destination: &networking.Destination{Host: "foo.baz"},
  2061  				}},
  2062  			}},
  2063  		}, valid: false},
  2064  		{name: "no tcp or http routing", in: &networking.VirtualService{
  2065  			Hosts: []string{"foo.bar"},
  2066  		}, valid: false},
  2067  		{name: "bad gateway", in: &networking.VirtualService{
  2068  			Hosts:    []string{"foo.bar"},
  2069  			Gateways: []string{"b@dgateway"},
  2070  			Http: []*networking.HTTPRoute{{
  2071  				Route: []*networking.HTTPRouteDestination{{
  2072  					Destination: &networking.Destination{Host: "foo.baz"},
  2073  				}},
  2074  			}},
  2075  		}, valid: false},
  2076  		{name: "FQDN for gateway", in: &networking.VirtualService{
  2077  			Hosts:    []string{"foo.bar"},
  2078  			Gateways: []string{"gateway.example.com"},
  2079  			Http: []*networking.HTTPRoute{{
  2080  				Route: []*networking.HTTPRouteDestination{{
  2081  					Destination: &networking.Destination{Host: "foo.baz"},
  2082  				}},
  2083  			}},
  2084  		}, valid: true, warning: true},
  2085  		{name: "namespace/name for gateway", in: &networking.VirtualService{
  2086  			Hosts:    []string{"foo.bar"},
  2087  			Gateways: []string{"ns1/gateway"},
  2088  			Http: []*networking.HTTPRoute{{
  2089  				Route: []*networking.HTTPRouteDestination{{
  2090  					Destination: &networking.Destination{Host: "foo.baz"},
  2091  				}},
  2092  			}},
  2093  		}, valid: true},
  2094  		{name: "namespace/* for gateway", in: &networking.VirtualService{
  2095  			Hosts:    []string{"foo.bar"},
  2096  			Gateways: []string{"ns1/*"},
  2097  			Http: []*networking.HTTPRoute{{
  2098  				Route: []*networking.HTTPRouteDestination{{
  2099  					Destination: &networking.Destination{Host: "foo.baz"},
  2100  				}},
  2101  			}},
  2102  		}, valid: false},
  2103  		{name: "*/name for gateway", in: &networking.VirtualService{
  2104  			Hosts:    []string{"foo.bar"},
  2105  			Gateways: []string{"*/gateway"},
  2106  			Http: []*networking.HTTPRoute{{
  2107  				Route: []*networking.HTTPRouteDestination{{
  2108  					Destination: &networking.Destination{Host: "foo.baz"},
  2109  				}},
  2110  			}},
  2111  		}, valid: false},
  2112  		{name: "wildcard for mesh gateway", in: &networking.VirtualService{
  2113  			Hosts: []string{"*"},
  2114  			Http: []*networking.HTTPRoute{{
  2115  				Route: []*networking.HTTPRouteDestination{{
  2116  					Destination: &networking.Destination{Host: "foo.baz"},
  2117  				}},
  2118  			}},
  2119  		}, valid: false},
  2120  		{name: "wildcard for non-mesh gateway", in: &networking.VirtualService{
  2121  			Hosts:    []string{"*"},
  2122  			Gateways: []string{"somegateway"},
  2123  			Http: []*networking.HTTPRoute{{
  2124  				Route: []*networking.HTTPRouteDestination{{
  2125  					Destination: &networking.Destination{Host: "foo.baz"},
  2126  				}},
  2127  			}},
  2128  		}, valid: true},
  2129  		{name: "missing tcp route", in: &networking.VirtualService{
  2130  			Hosts: []string{"foo.bar"},
  2131  			Tcp: []*networking.TCPRoute{{
  2132  				Match: []*networking.L4MatchAttributes{
  2133  					{Port: 999},
  2134  				},
  2135  			}},
  2136  		}, valid: false},
  2137  		{name: "missing tls route", in: &networking.VirtualService{
  2138  			Hosts: []string{"foo.bar"},
  2139  			Tls: []*networking.TLSRoute{{
  2140  				Match: []*networking.TLSMatchAttributes{
  2141  					{
  2142  						Port:     999,
  2143  						SniHosts: []string{"foo.bar"},
  2144  					},
  2145  				},
  2146  			}},
  2147  		}, valid: false},
  2148  		{name: "deprecated mirror", in: &networking.VirtualService{
  2149  			Hosts:    []string{"foo.bar"},
  2150  			Gateways: []string{"ns1/gateway"},
  2151  			Http: []*networking.HTTPRoute{{
  2152  				MirrorPercent: &wrapperspb.UInt32Value{Value: 5},
  2153  				Route: []*networking.HTTPRouteDestination{{
  2154  					Destination: &networking.Destination{Host: "foo.baz"},
  2155  				}},
  2156  			}},
  2157  		}, valid: true, warning: true},
  2158  		{name: "set authority", in: &networking.VirtualService{
  2159  			Hosts: []string{"foo.bar"},
  2160  			Http: []*networking.HTTPRoute{{
  2161  				Headers: &networking.Headers{
  2162  					Request: &networking.Headers_HeaderOperations{Set: map[string]string{":authority": "foo"}},
  2163  				},
  2164  				Route: []*networking.HTTPRouteDestination{{
  2165  					Destination: &networking.Destination{Host: "foo.baz"},
  2166  				}},
  2167  			}},
  2168  		}, valid: true, warning: false},
  2169  		{name: "set authority in destination", in: &networking.VirtualService{
  2170  			Hosts: []string{"foo.bar"},
  2171  			Http: []*networking.HTTPRoute{{
  2172  				Route: []*networking.HTTPRouteDestination{{
  2173  					Destination: &networking.Destination{Host: "foo.baz"},
  2174  					Headers: &networking.Headers{
  2175  						Request: &networking.Headers_HeaderOperations{Set: map[string]string{":authority": "foo"}},
  2176  					},
  2177  				}},
  2178  			}},
  2179  		}, valid: true, warning: false},
  2180  		{name: "set authority in rewrite and header", in: &networking.VirtualService{
  2181  			Hosts: []string{"foo.bar"},
  2182  			Http: []*networking.HTTPRoute{{
  2183  				Headers: &networking.Headers{
  2184  					Request: &networking.Headers_HeaderOperations{Set: map[string]string{":authority": "foo"}},
  2185  				},
  2186  				Rewrite: &networking.HTTPRewrite{Authority: "bar"},
  2187  				Route: []*networking.HTTPRouteDestination{{
  2188  					Destination: &networking.Destination{Host: "foo.baz"},
  2189  				}},
  2190  			}},
  2191  		}, valid: false, warning: false},
  2192  		{name: "non-method-get", in: &networking.VirtualService{
  2193  			Hosts: []string{"foo.bar"},
  2194  			Http: []*networking.HTTPRoute{{
  2195  				Route: []*networking.HTTPRouteDestination{{
  2196  					Destination: &networking.Destination{Host: "foo.baz"},
  2197  				}},
  2198  				Match: []*networking.HTTPMatchRequest{
  2199  					{
  2200  						Uri: &networking.StringMatch{
  2201  							MatchType: &networking.StringMatch_Prefix{Prefix: "/api/v1/product"},
  2202  						},
  2203  					},
  2204  					{
  2205  						Uri: &networking.StringMatch{
  2206  							MatchType: &networking.StringMatch_Prefix{Prefix: "/api/v1/products"},
  2207  						},
  2208  						Method: &networking.StringMatch{
  2209  							MatchType: &networking.StringMatch_Exact{Exact: "GET"},
  2210  						},
  2211  					},
  2212  				},
  2213  			}},
  2214  		}, valid: true, warning: true},
  2215  		{name: "uri-with-prefix-exact", in: &networking.VirtualService{
  2216  			Hosts: []string{"foo.bar"},
  2217  			Http: []*networking.HTTPRoute{{
  2218  				Route: []*networking.HTTPRouteDestination{{
  2219  					Destination: &networking.Destination{Host: "foo.baz"},
  2220  				}},
  2221  				Match: []*networking.HTTPMatchRequest{
  2222  					{
  2223  						Uri: &networking.StringMatch{
  2224  							MatchType: &networking.StringMatch_Prefix{Prefix: "/"},
  2225  						},
  2226  					},
  2227  					{
  2228  						Uri: &networking.StringMatch{
  2229  							MatchType: &networking.StringMatch_Exact{Exact: "/"},
  2230  						},
  2231  						Method: &networking.StringMatch{
  2232  							MatchType: &networking.StringMatch_Exact{Exact: "GET"},
  2233  						},
  2234  					},
  2235  				},
  2236  			}},
  2237  		}, valid: true, warning: false},
  2238  		{name: "jwt claim route without gateway", in: &networking.VirtualService{
  2239  			Hosts:    []string{"foo.bar"},
  2240  			Gateways: []string{"mesh"},
  2241  			Http: []*networking.HTTPRoute{{
  2242  				Route: []*networking.HTTPRouteDestination{{
  2243  					Destination: &networking.Destination{Host: "foo.baz"},
  2244  				}},
  2245  				Match: []*networking.HTTPMatchRequest{
  2246  					{
  2247  						Uri: &networking.StringMatch{
  2248  							MatchType: &networking.StringMatch_Prefix{Prefix: "/"},
  2249  						},
  2250  						Headers: map[string]*networking.StringMatch{
  2251  							"@request.auth.claims.foo": {
  2252  								MatchType: &networking.StringMatch_Exact{Exact: "bar"},
  2253  							},
  2254  						},
  2255  					},
  2256  				},
  2257  			}},
  2258  		}, valid: false, warning: false},
  2259  		{name: "ip address as sni host", in: &networking.VirtualService{
  2260  			Hosts: []string{"foo.bar"},
  2261  			Tls: []*networking.TLSRoute{{
  2262  				Route: []*networking.RouteDestination{{
  2263  					Destination: &networking.Destination{Host: "foo.baz"},
  2264  				}},
  2265  				Match: []*networking.TLSMatchAttributes{
  2266  					{
  2267  						Port:     999,
  2268  						SniHosts: []string{"1.1.1.1"},
  2269  					},
  2270  				},
  2271  			}},
  2272  		}, valid: true, warning: true},
  2273  		{name: "invalid wildcard as sni host", in: &networking.VirtualService{
  2274  			Hosts: []string{"foo.bar"},
  2275  			Tls: []*networking.TLSRoute{{
  2276  				Route: []*networking.RouteDestination{{
  2277  					Destination: &networking.Destination{Host: "foo.baz"},
  2278  				}},
  2279  				Match: []*networking.TLSMatchAttributes{
  2280  					{
  2281  						Port:     999,
  2282  						SniHosts: []string{"foo.*.com"},
  2283  					},
  2284  				},
  2285  			}},
  2286  		}, valid: false, warning: false},
  2287  	}
  2288  
  2289  	for _, tc := range testCases {
  2290  		t.Run(tc.name, func(t *testing.T) {
  2291  			warn, err := ValidateVirtualService(config.Config{Spec: tc.in})
  2292  			checkValidation(t, warn, err, tc.valid, tc.warning)
  2293  		})
  2294  	}
  2295  }
  2296  
  2297  func TestValidateWorkloadEntry(t *testing.T) {
  2298  	testCases := []struct {
  2299  		name    string
  2300  		in      proto.Message
  2301  		valid   bool
  2302  		warning bool
  2303  	}{
  2304  		{
  2305  			name:  "valid",
  2306  			in:    &networking.WorkloadEntry{Address: "1.2.3.4"},
  2307  			valid: true,
  2308  		},
  2309  		{
  2310  			name:  "missing address",
  2311  			in:    &networking.WorkloadEntry{},
  2312  			valid: false,
  2313  		},
  2314  		{
  2315  			name:    "missing address with network",
  2316  			in:      &networking.WorkloadEntry{Network: "network-2"},
  2317  			valid:   true,
  2318  			warning: true,
  2319  		},
  2320  		{
  2321  			name:  "valid unix endpoint",
  2322  			in:    &networking.WorkloadEntry{Address: "unix:///lon/google/com"},
  2323  			valid: true,
  2324  		},
  2325  		{
  2326  			name:  "invalid unix endpoint",
  2327  			in:    &networking.WorkloadEntry{Address: "unix:///lon/google/com", Ports: map[string]uint32{"7777": 7777}},
  2328  			valid: false,
  2329  		},
  2330  		{
  2331  			name:  "valid FQDN",
  2332  			in:    &networking.WorkloadEntry{Address: "validdns.com", Ports: map[string]uint32{"7777": 7777}},
  2333  			valid: true,
  2334  		},
  2335  		{
  2336  			name:  "invalid FQDN",
  2337  			in:    &networking.WorkloadEntry{Address: "invaliddns.com:9443", Ports: map[string]uint32{"7777": 7777}},
  2338  			valid: false,
  2339  		},
  2340  		{
  2341  			name:  "valid IP",
  2342  			in:    &networking.WorkloadEntry{Address: "172.16.1.1", Ports: map[string]uint32{"7777": 7777}},
  2343  			valid: true,
  2344  		},
  2345  	}
  2346  	for _, tc := range testCases {
  2347  		t.Run(tc.name, func(t *testing.T) {
  2348  			warn, err := ValidateWorkloadEntry(config.Config{Spec: tc.in})
  2349  			checkValidation(t, warn, err, tc.valid, tc.warning)
  2350  		})
  2351  	}
  2352  }
  2353  
  2354  func TestValidateWorkloadGroup(t *testing.T) {
  2355  	testCases := []struct {
  2356  		name    string
  2357  		in      proto.Message
  2358  		valid   bool
  2359  		warning bool
  2360  	}{
  2361  		{
  2362  			name:  "valid",
  2363  			in:    &networking.WorkloadGroup{Template: &networking.WorkloadEntry{}},
  2364  			valid: true,
  2365  		},
  2366  		{
  2367  			name: "invalid",
  2368  			in: &networking.WorkloadGroup{Template: &networking.WorkloadEntry{}, Metadata: &networking.WorkloadGroup_ObjectMeta{Labels: map[string]string{
  2369  				".": "~",
  2370  			}}},
  2371  			valid: false,
  2372  		},
  2373  		{
  2374  			name: "probe missing method",
  2375  			in: &networking.WorkloadGroup{
  2376  				Template: &networking.WorkloadEntry{},
  2377  				Probe:    &networking.ReadinessProbe{},
  2378  			},
  2379  			valid: false,
  2380  		},
  2381  		{
  2382  			name: "probe nil",
  2383  			in: &networking.WorkloadGroup{
  2384  				Template: &networking.WorkloadEntry{},
  2385  				Probe: &networking.ReadinessProbe{
  2386  					HealthCheckMethod: &networking.ReadinessProbe_HttpGet{},
  2387  				},
  2388  			},
  2389  			valid: false,
  2390  		},
  2391  		{
  2392  			name: "probe http empty",
  2393  			in: &networking.WorkloadGroup{
  2394  				Template: &networking.WorkloadEntry{},
  2395  				Probe: &networking.ReadinessProbe{
  2396  					HealthCheckMethod: &networking.ReadinessProbe_HttpGet{
  2397  						HttpGet: &networking.HTTPHealthCheckConfig{},
  2398  					},
  2399  				},
  2400  			},
  2401  			valid: false,
  2402  		},
  2403  		{
  2404  			name: "probe http valid",
  2405  			in: &networking.WorkloadGroup{
  2406  				Template: &networking.WorkloadEntry{},
  2407  				Probe: &networking.ReadinessProbe{
  2408  					HealthCheckMethod: &networking.ReadinessProbe_HttpGet{
  2409  						HttpGet: &networking.HTTPHealthCheckConfig{
  2410  							Port: 5,
  2411  						},
  2412  					},
  2413  				},
  2414  			},
  2415  			valid: true,
  2416  		},
  2417  		{
  2418  			name: "probe tcp invalid",
  2419  			in: &networking.WorkloadGroup{
  2420  				Template: &networking.WorkloadEntry{},
  2421  				Probe: &networking.ReadinessProbe{
  2422  					HealthCheckMethod: &networking.ReadinessProbe_TcpSocket{
  2423  						TcpSocket: &networking.TCPHealthCheckConfig{},
  2424  					},
  2425  				},
  2426  			},
  2427  			valid: false,
  2428  		},
  2429  		{
  2430  			name: "probe tcp valid",
  2431  			in: &networking.WorkloadGroup{
  2432  				Template: &networking.WorkloadEntry{},
  2433  				Probe: &networking.ReadinessProbe{
  2434  					HealthCheckMethod: &networking.ReadinessProbe_TcpSocket{
  2435  						TcpSocket: &networking.TCPHealthCheckConfig{
  2436  							Port: 5,
  2437  						},
  2438  					},
  2439  				},
  2440  			},
  2441  			valid: true,
  2442  		},
  2443  		{
  2444  			name: "probe exec invalid",
  2445  			in: &networking.WorkloadGroup{
  2446  				Template: &networking.WorkloadEntry{},
  2447  				Probe: &networking.ReadinessProbe{
  2448  					HealthCheckMethod: &networking.ReadinessProbe_Exec{
  2449  						Exec: &networking.ExecHealthCheckConfig{},
  2450  					},
  2451  				},
  2452  			},
  2453  			valid: false,
  2454  		},
  2455  		{
  2456  			name: "probe exec valid",
  2457  			in: &networking.WorkloadGroup{
  2458  				Template: &networking.WorkloadEntry{},
  2459  				Probe: &networking.ReadinessProbe{
  2460  					HealthCheckMethod: &networking.ReadinessProbe_Exec{
  2461  						Exec: &networking.ExecHealthCheckConfig{
  2462  							Command: []string{"foo", "bar"},
  2463  						},
  2464  					},
  2465  				},
  2466  			},
  2467  			valid: true,
  2468  		},
  2469  	}
  2470  	for _, tc := range testCases {
  2471  		t.Run(tc.name, func(t *testing.T) {
  2472  			warn, err := ValidateWorkloadGroup(config.Config{Spec: tc.in})
  2473  			checkValidation(t, warn, err, tc.valid, tc.warning)
  2474  		})
  2475  	}
  2476  }
  2477  
  2478  func checkValidation(t *testing.T, gotWarning Warning, gotError error, valid bool, warning bool) {
  2479  	t.Helper()
  2480  	if (gotError == nil) != valid {
  2481  		t.Fatalf("got valid=%v but wanted valid=%v: %v", gotError == nil, valid, gotError)
  2482  	}
  2483  	if (gotWarning == nil) == warning {
  2484  		t.Fatalf("got warning=%v but wanted warning=%v", gotWarning, warning)
  2485  	}
  2486  }
  2487  
  2488  func stringOrEmpty(v error) string {
  2489  	if v == nil {
  2490  		return ""
  2491  	}
  2492  	return v.Error()
  2493  }
  2494  
  2495  func checkValidationMessage(t *testing.T, gotWarning Warning, gotError error, wantWarning string, wantError string) {
  2496  	t.Helper()
  2497  	if (gotError == nil) != (wantError == "") {
  2498  		t.Fatalf("got err=%v but wanted err=%v", gotError, wantError)
  2499  	}
  2500  	if !strings.Contains(stringOrEmpty(gotError), wantError) {
  2501  		t.Fatalf("got err=%v but wanted err=%v", gotError, wantError)
  2502  	}
  2503  
  2504  	if (gotWarning == nil) != (wantWarning == "") {
  2505  		t.Fatalf("got warning=%v but wanted warning=%v", gotWarning, wantWarning)
  2506  	}
  2507  	if !strings.Contains(stringOrEmpty(gotWarning), wantWarning) {
  2508  		t.Fatalf("got warning=%v but wanted warning=%v", gotWarning, wantWarning)
  2509  	}
  2510  }
  2511  
  2512  func TestValidateDestinationRule(t *testing.T) {
  2513  	cases := []struct {
  2514  		name    string
  2515  		in      proto.Message
  2516  		valid   bool
  2517  		warning bool
  2518  	}{
  2519  		{name: "simple destination rule", in: &networking.DestinationRule{
  2520  			Host: "reviews",
  2521  			Subsets: []*networking.Subset{
  2522  				{Name: "v1", Labels: map[string]string{"version": "v1"}},
  2523  				{Name: "v2", Labels: map[string]string{"version": "v2"}},
  2524  			},
  2525  		}, valid: true},
  2526  
  2527  		{name: "simple destination rule with empty selector labels", in: &networking.DestinationRule{
  2528  			Host: "reviews",
  2529  			Subsets: []*networking.Subset{
  2530  				{Name: "v1", Labels: map[string]string{"version": "v1"}},
  2531  				{Name: "v2", Labels: map[string]string{"version": "v2"}},
  2532  			},
  2533  			WorkloadSelector: &api.WorkloadSelector{},
  2534  		}, valid: true, warning: true},
  2535  
  2536  		{name: "missing destination name", in: &networking.DestinationRule{
  2537  			Host: "",
  2538  			Subsets: []*networking.Subset{
  2539  				{Name: "v1", Labels: map[string]string{"version": "v1"}},
  2540  				{Name: "v2", Labels: map[string]string{"version": "v2"}},
  2541  			},
  2542  		}, valid: false},
  2543  
  2544  		{name: "missing subset name", in: &networking.DestinationRule{
  2545  			Host: "reviews",
  2546  			Subsets: []*networking.Subset{
  2547  				{Name: "", Labels: map[string]string{"version": "v1"}},
  2548  				{Name: "v2", Labels: map[string]string{"version": "v2"}},
  2549  			},
  2550  		}, valid: false},
  2551  
  2552  		{name: "duplicate subset names", in: &networking.DestinationRule{
  2553  			Host: "reviews",
  2554  			Subsets: []*networking.Subset{
  2555  				{Name: "foo", Labels: map[string]string{"version": "v1"}},
  2556  				{Name: "foo", Labels: map[string]string{"version": "v2"}},
  2557  			},
  2558  		}, valid: false},
  2559  
  2560  		{name: "valid traffic policy, top level", in: &networking.DestinationRule{
  2561  			Host: "reviews",
  2562  			TrafficPolicy: &networking.TrafficPolicy{
  2563  				LoadBalancer: &networking.LoadBalancerSettings{
  2564  					LbPolicy: &networking.LoadBalancerSettings_Simple{
  2565  						Simple: networking.LoadBalancerSettings_ROUND_ROBIN,
  2566  					},
  2567  				},
  2568  				ConnectionPool: &networking.ConnectionPoolSettings{
  2569  					Tcp:  &networking.ConnectionPoolSettings_TCPSettings{MaxConnections: 7},
  2570  					Http: &networking.ConnectionPoolSettings_HTTPSettings{Http2MaxRequests: 11},
  2571  				},
  2572  				OutlierDetection: &networking.OutlierDetection{
  2573  					MinHealthPercent: 20,
  2574  				},
  2575  			},
  2576  			Subsets: []*networking.Subset{
  2577  				{Name: "v1", Labels: map[string]string{"version": "v1"}},
  2578  				{Name: "v2", Labels: map[string]string{"version": "v2"}},
  2579  			},
  2580  		}, valid: true},
  2581  
  2582  		{name: "invalid traffic policy, top level", in: &networking.DestinationRule{
  2583  			Host: "reviews",
  2584  			TrafficPolicy: &networking.TrafficPolicy{
  2585  				LoadBalancer: &networking.LoadBalancerSettings{
  2586  					LbPolicy: &networking.LoadBalancerSettings_Simple{
  2587  						Simple: networking.LoadBalancerSettings_ROUND_ROBIN,
  2588  					},
  2589  				},
  2590  				ConnectionPool: &networking.ConnectionPoolSettings{},
  2591  				OutlierDetection: &networking.OutlierDetection{
  2592  					MinHealthPercent: 20,
  2593  				},
  2594  			},
  2595  			Subsets: []*networking.Subset{
  2596  				{Name: "v1", Labels: map[string]string{"version": "v1"}},
  2597  				{Name: "v2", Labels: map[string]string{"version": "v2"}},
  2598  			},
  2599  		}, valid: false},
  2600  
  2601  		{name: "valid traffic policy, subset level", in: &networking.DestinationRule{
  2602  			Host: "reviews",
  2603  			Subsets: []*networking.Subset{
  2604  				{
  2605  					Name: "v1", Labels: map[string]string{"version": "v1"},
  2606  					TrafficPolicy: &networking.TrafficPolicy{
  2607  						LoadBalancer: &networking.LoadBalancerSettings{
  2608  							LbPolicy: &networking.LoadBalancerSettings_Simple{
  2609  								Simple: networking.LoadBalancerSettings_ROUND_ROBIN,
  2610  							},
  2611  						},
  2612  						ConnectionPool: &networking.ConnectionPoolSettings{
  2613  							Tcp:  &networking.ConnectionPoolSettings_TCPSettings{MaxConnections: 7},
  2614  							Http: &networking.ConnectionPoolSettings_HTTPSettings{Http2MaxRequests: 11},
  2615  						},
  2616  						OutlierDetection: &networking.OutlierDetection{
  2617  							MinHealthPercent: 20,
  2618  						},
  2619  					},
  2620  				},
  2621  				{Name: "v2", Labels: map[string]string{"version": "v2"}},
  2622  			},
  2623  		}, valid: true},
  2624  
  2625  		{name: "invalid traffic policy, subset level", in: &networking.DestinationRule{
  2626  			Host: "reviews",
  2627  			Subsets: []*networking.Subset{
  2628  				{
  2629  					Name: "v1", Labels: map[string]string{"version": "v1"},
  2630  					TrafficPolicy: &networking.TrafficPolicy{
  2631  						LoadBalancer: &networking.LoadBalancerSettings{
  2632  							LbPolicy: &networking.LoadBalancerSettings_Simple{
  2633  								Simple: networking.LoadBalancerSettings_ROUND_ROBIN,
  2634  							},
  2635  						},
  2636  						ConnectionPool: &networking.ConnectionPoolSettings{},
  2637  						OutlierDetection: &networking.OutlierDetection{
  2638  							MinHealthPercent: 20,
  2639  						},
  2640  					},
  2641  				},
  2642  				{Name: "v2", Labels: map[string]string{"version": "v2"}},
  2643  			},
  2644  		}, valid: false},
  2645  
  2646  		{name: "valid traffic policy, both levels", in: &networking.DestinationRule{
  2647  			Host: "reviews",
  2648  			TrafficPolicy: &networking.TrafficPolicy{
  2649  				LoadBalancer: &networking.LoadBalancerSettings{
  2650  					LbPolicy: &networking.LoadBalancerSettings_Simple{
  2651  						Simple: networking.LoadBalancerSettings_ROUND_ROBIN,
  2652  					},
  2653  				},
  2654  				ConnectionPool: &networking.ConnectionPoolSettings{
  2655  					Tcp:  &networking.ConnectionPoolSettings_TCPSettings{MaxConnections: 7},
  2656  					Http: &networking.ConnectionPoolSettings_HTTPSettings{Http2MaxRequests: 11},
  2657  				},
  2658  				OutlierDetection: &networking.OutlierDetection{
  2659  					MinHealthPercent: 20,
  2660  				},
  2661  			},
  2662  			Subsets: []*networking.Subset{
  2663  				{
  2664  					Name: "v1", Labels: map[string]string{"version": "v1"},
  2665  					TrafficPolicy: &networking.TrafficPolicy{
  2666  						LoadBalancer: &networking.LoadBalancerSettings{
  2667  							LbPolicy: &networking.LoadBalancerSettings_Simple{
  2668  								Simple: networking.LoadBalancerSettings_ROUND_ROBIN,
  2669  							},
  2670  						},
  2671  						ConnectionPool: &networking.ConnectionPoolSettings{
  2672  							Tcp:  &networking.ConnectionPoolSettings_TCPSettings{MaxConnections: 7},
  2673  							Http: &networking.ConnectionPoolSettings_HTTPSettings{Http2MaxRequests: 11},
  2674  						},
  2675  						OutlierDetection: &networking.OutlierDetection{
  2676  							MinHealthPercent: 30,
  2677  						},
  2678  					},
  2679  				},
  2680  				{Name: "v2", Labels: map[string]string{"version": "v2"}},
  2681  			},
  2682  		}, valid: true},
  2683  
  2684  		{name: "InsecureSkipVerify is not specified with tls mode simple, and the ca cert is specified by CaCertificates", in: &networking.DestinationRule{
  2685  			Host: "reviews",
  2686  			TrafficPolicy: &networking.TrafficPolicy{
  2687  				Tls: &networking.ClientTLSSettings{
  2688  					Mode:            networking.ClientTLSSettings_SIMPLE,
  2689  					CaCertificates:  "test",
  2690  					SubjectAltNames: []string{"reviews.default.svc"},
  2691  				},
  2692  			},
  2693  		}, valid: true},
  2694  
  2695  		{name: "InsecureSkipVerify is not specified with tls mode simple, and the ca cert is specified by CredentialName", in: &networking.DestinationRule{
  2696  			Host: "reviews",
  2697  			TrafficPolicy: &networking.TrafficPolicy{
  2698  				Tls: &networking.ClientTLSSettings{
  2699  					Mode:            networking.ClientTLSSettings_SIMPLE,
  2700  					CredentialName:  "test",
  2701  					SubjectAltNames: []string{"reviews.default.svc"},
  2702  				},
  2703  			},
  2704  		}, valid: true},
  2705  
  2706  		{name: "InsecureSkipVerify is set false with tls mode simple, and the ca cert is specified by CaCertificates", in: &networking.DestinationRule{
  2707  			Host: "reviews",
  2708  			TrafficPolicy: &networking.TrafficPolicy{
  2709  				Tls: &networking.ClientTLSSettings{
  2710  					Mode:            networking.ClientTLSSettings_SIMPLE,
  2711  					CaCertificates:  "test",
  2712  					SubjectAltNames: []string{"reviews.default.svc"},
  2713  					InsecureSkipVerify: &wrapperspb.BoolValue{
  2714  						Value: false,
  2715  					},
  2716  				},
  2717  			},
  2718  		}, valid: true},
  2719  
  2720  		{name: "InsecureSkipVerify is set false with tls mode simple, and the ca cert is specified by CredentialName", in: &networking.DestinationRule{
  2721  			Host: "reviews",
  2722  			TrafficPolicy: &networking.TrafficPolicy{
  2723  				Tls: &networking.ClientTLSSettings{
  2724  					Mode:            networking.ClientTLSSettings_SIMPLE,
  2725  					CredentialName:  "test",
  2726  					SubjectAltNames: []string{"reviews.default.svc"},
  2727  					InsecureSkipVerify: &wrapperspb.BoolValue{
  2728  						Value: false,
  2729  					},
  2730  				},
  2731  			},
  2732  		}, valid: true},
  2733  
  2734  		{name: "InsecureSkipVerify is set true with tls mode simple, and the ca cert is specified by CaCertificates", in: &networking.DestinationRule{
  2735  			Host: "reviews",
  2736  			TrafficPolicy: &networking.TrafficPolicy{
  2737  				Tls: &networking.ClientTLSSettings{
  2738  					Mode:           networking.ClientTLSSettings_SIMPLE,
  2739  					CredentialName: "test",
  2740  					InsecureSkipVerify: &wrapperspb.BoolValue{
  2741  						Value: true,
  2742  					},
  2743  				},
  2744  			},
  2745  		}, valid: false},
  2746  
  2747  		{name: "InsecureSkipVerify is set true with tls mode simple, and the ca cert is specified by CredentialName", in: &networking.DestinationRule{
  2748  			Host: "reviews",
  2749  			TrafficPolicy: &networking.TrafficPolicy{
  2750  				Tls: &networking.ClientTLSSettings{
  2751  					Mode:           networking.ClientTLSSettings_SIMPLE,
  2752  					CaCertificates: "test",
  2753  					InsecureSkipVerify: &wrapperspb.BoolValue{
  2754  						Value: true,
  2755  					},
  2756  				},
  2757  			},
  2758  		}, valid: false},
  2759  
  2760  		{name: "InsecureSkipVerify is set true with tls mode simple, and the san is specified", in: &networking.DestinationRule{
  2761  			Host: "reviews",
  2762  			TrafficPolicy: &networking.TrafficPolicy{
  2763  				Tls: &networking.ClientTLSSettings{
  2764  					Mode:            networking.ClientTLSSettings_SIMPLE,
  2765  					SubjectAltNames: []string{"reviews.default.svc"},
  2766  					InsecureSkipVerify: &wrapperspb.BoolValue{
  2767  						Value: true,
  2768  					},
  2769  				},
  2770  			},
  2771  		}, valid: false},
  2772  
  2773  		{name: "InsecureSkipVerify is not specified with tls mode mutual, and the ca cert is specified by CaCertificates", in: &networking.DestinationRule{
  2774  			Host: "reviews",
  2775  			TrafficPolicy: &networking.TrafficPolicy{
  2776  				Tls: &networking.ClientTLSSettings{
  2777  					Mode:              networking.ClientTLSSettings_MUTUAL,
  2778  					CaCertificates:    "test",
  2779  					PrivateKey:        "key",
  2780  					ClientCertificate: "cert",
  2781  					SubjectAltNames:   []string{"reviews.default.svc"},
  2782  				},
  2783  			},
  2784  		}, valid: true},
  2785  
  2786  		{name: "InsecureSkipVerify is not specified with tls mode mutual, and the ca cert is specified by CredentialName", in: &networking.DestinationRule{
  2787  			Host: "reviews",
  2788  			TrafficPolicy: &networking.TrafficPolicy{
  2789  				Tls: &networking.ClientTLSSettings{
  2790  					Mode:            networking.ClientTLSSettings_MUTUAL,
  2791  					CredentialName:  "test",
  2792  					SubjectAltNames: []string{"reviews.default.svc"},
  2793  				},
  2794  			},
  2795  		}, valid: true},
  2796  
  2797  		{name: "InsecureSkipVerify is set false with tls mode mutual, and the ca cert is specified by CaCertificates", in: &networking.DestinationRule{
  2798  			Host: "reviews",
  2799  			TrafficPolicy: &networking.TrafficPolicy{
  2800  				Tls: &networking.ClientTLSSettings{
  2801  					Mode:              networking.ClientTLSSettings_MUTUAL,
  2802  					CaCertificates:    "test",
  2803  					PrivateKey:        "key",
  2804  					ClientCertificate: "cert",
  2805  					SubjectAltNames:   []string{"reviews.default.svc"},
  2806  					InsecureSkipVerify: &wrapperspb.BoolValue{
  2807  						Value: false,
  2808  					},
  2809  				},
  2810  			},
  2811  		}, valid: true},
  2812  
  2813  		{name: "InsecureSkipVerify is set false with tls mode mutual, and the ca cert is specified by CredentialName", in: &networking.DestinationRule{
  2814  			Host: "reviews",
  2815  			TrafficPolicy: &networking.TrafficPolicy{
  2816  				Tls: &networking.ClientTLSSettings{
  2817  					Mode:            networking.ClientTLSSettings_MUTUAL,
  2818  					CredentialName:  "test",
  2819  					SubjectAltNames: []string{"reviews.default.svc"},
  2820  					InsecureSkipVerify: &wrapperspb.BoolValue{
  2821  						Value: false,
  2822  					},
  2823  				},
  2824  			},
  2825  		}, valid: true},
  2826  
  2827  		{name: "InsecureSkipVerify is set true with tls mode mutual, and the ca cert is specified by CaCertificates", in: &networking.DestinationRule{
  2828  			Host: "reviews",
  2829  			TrafficPolicy: &networking.TrafficPolicy{
  2830  				Tls: &networking.ClientTLSSettings{
  2831  					Mode:              networking.ClientTLSSettings_MUTUAL,
  2832  					CaCertificates:    "test",
  2833  					PrivateKey:        "key",
  2834  					ClientCertificate: "cert",
  2835  					InsecureSkipVerify: &wrapperspb.BoolValue{
  2836  						Value: true,
  2837  					},
  2838  				},
  2839  			},
  2840  		}, valid: false},
  2841  
  2842  		{name: "InsecureSkipVerify is set true with tls mode mutual, and the ca cert is specified by CaCertificates", in: &networking.DestinationRule{
  2843  			Host: "reviews",
  2844  			TrafficPolicy: &networking.TrafficPolicy{
  2845  				Tls: &networking.ClientTLSSettings{
  2846  					Mode:           networking.ClientTLSSettings_MUTUAL,
  2847  					CredentialName: "test",
  2848  					InsecureSkipVerify: &wrapperspb.BoolValue{
  2849  						Value: true,
  2850  					},
  2851  				},
  2852  			},
  2853  		}, valid: true},
  2854  
  2855  		{name: "InsecureSkipVerify is set true with tls mode mutual, and the ca cert is not specified", in: &networking.DestinationRule{
  2856  			Host: "reviews",
  2857  			TrafficPolicy: &networking.TrafficPolicy{
  2858  				Tls: &networking.ClientTLSSettings{
  2859  					Mode:              networking.ClientTLSSettings_MUTUAL,
  2860  					PrivateKey:        "key",
  2861  					ClientCertificate: "cert",
  2862  					InsecureSkipVerify: &wrapperspb.BoolValue{
  2863  						Value: true,
  2864  					},
  2865  				},
  2866  			},
  2867  		}, valid: true},
  2868  
  2869  		{name: "InsecureSkipVerify is set true with tls mode mutual, and the san is specified", in: &networking.DestinationRule{
  2870  			Host: "reviews",
  2871  			TrafficPolicy: &networking.TrafficPolicy{
  2872  				Tls: &networking.ClientTLSSettings{
  2873  					Mode:              networking.ClientTLSSettings_MUTUAL,
  2874  					PrivateKey:        "key",
  2875  					ClientCertificate: "cert",
  2876  					SubjectAltNames:   []string{"reviews.default.svc"},
  2877  					InsecureSkipVerify: &wrapperspb.BoolValue{
  2878  						Value: true,
  2879  					},
  2880  				},
  2881  			},
  2882  		}, valid: false},
  2883  	}
  2884  	for _, c := range cases {
  2885  		t.Run(c.name, func(t *testing.T) {
  2886  			warn, got := ValidateDestinationRule(config.Config{
  2887  				Meta: config.Meta{
  2888  					Name:      someName,
  2889  					Namespace: someNamespace,
  2890  				},
  2891  				Spec: c.in,
  2892  			})
  2893  			if (got == nil) != c.valid {
  2894  				t.Errorf("ValidateDestinationRule failed on %v: got valid=%v but wanted valid=%v: %v",
  2895  					c.name, got == nil, c.valid, got)
  2896  			}
  2897  			if (warn == nil) == c.warning {
  2898  				t.Errorf("ValidateDestinationRule failed on %v: got warn=%v but wanted warn=%v: %v",
  2899  					c.name, warn == nil, c.warning, warn)
  2900  			}
  2901  		})
  2902  	}
  2903  }
  2904  
  2905  func TestValidateTrafficPolicy(t *testing.T) {
  2906  	cases := []struct {
  2907  		name  string
  2908  		in    *networking.TrafficPolicy
  2909  		valid bool
  2910  	}{
  2911  		{
  2912  			name: "valid traffic policy", in: &networking.TrafficPolicy{
  2913  				LoadBalancer: &networking.LoadBalancerSettings{
  2914  					LbPolicy: &networking.LoadBalancerSettings_Simple{
  2915  						Simple: networking.LoadBalancerSettings_ROUND_ROBIN,
  2916  					},
  2917  				},
  2918  				ConnectionPool: &networking.ConnectionPoolSettings{
  2919  					Tcp:  &networking.ConnectionPoolSettings_TCPSettings{MaxConnections: 7},
  2920  					Http: &networking.ConnectionPoolSettings_HTTPSettings{Http2MaxRequests: 11},
  2921  				},
  2922  				OutlierDetection: &networking.OutlierDetection{
  2923  					MinHealthPercent: 20,
  2924  				},
  2925  			},
  2926  			valid: true,
  2927  		},
  2928  		{
  2929  			name: "invalid traffic policy, nil entries", in: &networking.TrafficPolicy{},
  2930  			valid: false,
  2931  		},
  2932  
  2933  		{
  2934  			name: "invalid traffic policy, missing port in port level settings", in: &networking.TrafficPolicy{
  2935  				PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{
  2936  					{
  2937  						LoadBalancer: &networking.LoadBalancerSettings{
  2938  							LbPolicy: &networking.LoadBalancerSettings_Simple{
  2939  								Simple: networking.LoadBalancerSettings_ROUND_ROBIN,
  2940  							},
  2941  						},
  2942  						ConnectionPool: &networking.ConnectionPoolSettings{
  2943  							Tcp:  &networking.ConnectionPoolSettings_TCPSettings{MaxConnections: 7},
  2944  							Http: &networking.ConnectionPoolSettings_HTTPSettings{Http2MaxRequests: 11},
  2945  						},
  2946  						OutlierDetection: &networking.OutlierDetection{
  2947  							MinHealthPercent: 20,
  2948  						},
  2949  					},
  2950  				},
  2951  			},
  2952  			valid: false,
  2953  		},
  2954  		{
  2955  			name: "invalid traffic policy, bad connection pool", in: &networking.TrafficPolicy{
  2956  				LoadBalancer: &networking.LoadBalancerSettings{
  2957  					LbPolicy: &networking.LoadBalancerSettings_Simple{
  2958  						Simple: networking.LoadBalancerSettings_ROUND_ROBIN,
  2959  					},
  2960  				},
  2961  				ConnectionPool: &networking.ConnectionPoolSettings{},
  2962  				OutlierDetection: &networking.OutlierDetection{
  2963  					MinHealthPercent: 20,
  2964  				},
  2965  			},
  2966  			valid: false,
  2967  		},
  2968  		{
  2969  			name: "invalid traffic policy, bad max connection duration", in: &networking.TrafficPolicy{
  2970  				LoadBalancer: &networking.LoadBalancerSettings{
  2971  					LbPolicy: &networking.LoadBalancerSettings_Simple{
  2972  						Simple: networking.LoadBalancerSettings_ROUND_ROBIN,
  2973  					},
  2974  				},
  2975  				ConnectionPool: &networking.ConnectionPoolSettings{
  2976  					Tcp: &networking.ConnectionPoolSettings_TCPSettings{
  2977  						MaxConnectionDuration: &durationpb.Duration{Nanos: 500},
  2978  					},
  2979  				},
  2980  			},
  2981  			valid: false,
  2982  		},
  2983  		{
  2984  			name: "invalid traffic policy, panic threshold too low", in: &networking.TrafficPolicy{
  2985  				LoadBalancer: &networking.LoadBalancerSettings{
  2986  					LbPolicy: &networking.LoadBalancerSettings_Simple{
  2987  						Simple: networking.LoadBalancerSettings_ROUND_ROBIN,
  2988  					},
  2989  				},
  2990  				ConnectionPool: &networking.ConnectionPoolSettings{
  2991  					Tcp:  &networking.ConnectionPoolSettings_TCPSettings{MaxConnections: 7},
  2992  					Http: &networking.ConnectionPoolSettings_HTTPSettings{Http2MaxRequests: 11},
  2993  				},
  2994  				OutlierDetection: &networking.OutlierDetection{
  2995  					MinHealthPercent: -1,
  2996  				},
  2997  			},
  2998  			valid: false,
  2999  		},
  3000  		{
  3001  			name: "invalid traffic policy, both upgrade and use client protocol set", in: &networking.TrafficPolicy{
  3002  				ConnectionPool: &networking.ConnectionPoolSettings{
  3003  					Http: &networking.ConnectionPoolSettings_HTTPSettings{
  3004  						H2UpgradePolicy:   networking.ConnectionPoolSettings_HTTPSettings_UPGRADE,
  3005  						UseClientProtocol: true,
  3006  					},
  3007  				},
  3008  			},
  3009  			valid: false,
  3010  		},
  3011  	}
  3012  	for _, c := range cases {
  3013  		if got := validateTrafficPolicy(c.in).Err; (got == nil) != c.valid {
  3014  			t.Errorf("ValidateTrafficPolicy failed on %v: got valid=%v but wanted valid=%v: %v",
  3015  				c.name, got == nil, c.valid, got)
  3016  		}
  3017  	}
  3018  }
  3019  
  3020  func TestValidateConnectionPool(t *testing.T) {
  3021  	cases := []struct {
  3022  		name  string
  3023  		in    *networking.ConnectionPoolSettings
  3024  		valid bool
  3025  	}{
  3026  		{
  3027  			name: "valid connection pool, tcp and http", in: &networking.ConnectionPoolSettings{
  3028  				Tcp: &networking.ConnectionPoolSettings_TCPSettings{
  3029  					MaxConnections: 7,
  3030  					ConnectTimeout: &durationpb.Duration{Seconds: 2},
  3031  				},
  3032  				Http: &networking.ConnectionPoolSettings_HTTPSettings{
  3033  					Http1MaxPendingRequests:  2,
  3034  					Http2MaxRequests:         11,
  3035  					MaxRequestsPerConnection: 5,
  3036  					MaxRetries:               4,
  3037  					IdleTimeout:              &durationpb.Duration{Seconds: 30},
  3038  					MaxConcurrentStreams:     5,
  3039  				},
  3040  			},
  3041  			valid: true,
  3042  		},
  3043  
  3044  		{
  3045  			name: "valid connection pool, tcp only", in: &networking.ConnectionPoolSettings{
  3046  				Tcp: &networking.ConnectionPoolSettings_TCPSettings{
  3047  					MaxConnections: 7,
  3048  					ConnectTimeout: &durationpb.Duration{Seconds: 2},
  3049  				},
  3050  			},
  3051  			valid: true,
  3052  		},
  3053  
  3054  		{
  3055  			name: "valid connection pool, http only", in: &networking.ConnectionPoolSettings{
  3056  				Http: &networking.ConnectionPoolSettings_HTTPSettings{
  3057  					Http1MaxPendingRequests:  2,
  3058  					Http2MaxRequests:         11,
  3059  					MaxRequestsPerConnection: 5,
  3060  					MaxRetries:               4,
  3061  					IdleTimeout:              &durationpb.Duration{Seconds: 30},
  3062  					MaxConcurrentStreams:     5,
  3063  				},
  3064  			},
  3065  			valid: true,
  3066  		},
  3067  
  3068  		{
  3069  			name: "valid connection pool, http only with empty idle timeout", in: &networking.ConnectionPoolSettings{
  3070  				Http: &networking.ConnectionPoolSettings_HTTPSettings{
  3071  					Http1MaxPendingRequests:  2,
  3072  					Http2MaxRequests:         11,
  3073  					MaxRequestsPerConnection: 5,
  3074  					MaxRetries:               4,
  3075  					MaxConcurrentStreams:     5,
  3076  				},
  3077  			},
  3078  			valid: true,
  3079  		},
  3080  
  3081  		{name: "invalid connection pool, empty", in: &networking.ConnectionPoolSettings{}, valid: false},
  3082  
  3083  		{
  3084  			name: "invalid connection pool, bad max connections", in: &networking.ConnectionPoolSettings{
  3085  				Tcp: &networking.ConnectionPoolSettings_TCPSettings{MaxConnections: -1},
  3086  			},
  3087  			valid: false,
  3088  		},
  3089  
  3090  		{
  3091  			name: "invalid connection pool, bad connect timeout", in: &networking.ConnectionPoolSettings{
  3092  				Tcp: &networking.ConnectionPoolSettings_TCPSettings{
  3093  					ConnectTimeout: &durationpb.Duration{Seconds: 2, Nanos: 5},
  3094  				},
  3095  			},
  3096  			valid: false,
  3097  		},
  3098  
  3099  		{
  3100  			name: "invalid connection pool, bad max pending requests", in: &networking.ConnectionPoolSettings{
  3101  				Http: &networking.ConnectionPoolSettings_HTTPSettings{Http1MaxPendingRequests: -1},
  3102  			},
  3103  			valid: false,
  3104  		},
  3105  
  3106  		{
  3107  			name: "invalid connection pool, bad max requests", in: &networking.ConnectionPoolSettings{
  3108  				Http: &networking.ConnectionPoolSettings_HTTPSettings{Http2MaxRequests: -1},
  3109  			},
  3110  			valid: false,
  3111  		},
  3112  
  3113  		{
  3114  			name: "invalid connection pool, bad max requests per connection", in: &networking.ConnectionPoolSettings{
  3115  				Http: &networking.ConnectionPoolSettings_HTTPSettings{MaxRequestsPerConnection: -1},
  3116  			},
  3117  			valid: false,
  3118  		},
  3119  
  3120  		{
  3121  			name: "invalid connection pool, bad max retries", in: &networking.ConnectionPoolSettings{
  3122  				Http: &networking.ConnectionPoolSettings_HTTPSettings{MaxRetries: -1},
  3123  			},
  3124  			valid: false,
  3125  		},
  3126  
  3127  		{
  3128  			name: "invalid connection pool, bad idle timeout", in: &networking.ConnectionPoolSettings{
  3129  				Http: &networking.ConnectionPoolSettings_HTTPSettings{IdleTimeout: &durationpb.Duration{Seconds: 30, Nanos: 5}},
  3130  			},
  3131  			valid: false,
  3132  		},
  3133  
  3134  		{
  3135  			name: "invalid connection pool, bad max concurrent streams", in: &networking.ConnectionPoolSettings{
  3136  				Http: &networking.ConnectionPoolSettings_HTTPSettings{MaxConcurrentStreams: -1},
  3137  			},
  3138  			valid: false,
  3139  		},
  3140  	}
  3141  
  3142  	for _, c := range cases {
  3143  		if got := validateConnectionPool(c.in); (got == nil) != c.valid {
  3144  			t.Errorf("ValidateConnectionSettings failed on %v: got valid=%v but wanted valid=%v: %v",
  3145  				c.name, got == nil, c.valid, got)
  3146  		}
  3147  	}
  3148  }
  3149  
  3150  func TestValidateLoadBalancer(t *testing.T) {
  3151  	duration := durationpb.Duration{Seconds: int64(time.Hour / time.Second)}
  3152  	cases := []struct {
  3153  		name  string
  3154  		in    *networking.LoadBalancerSettings
  3155  		valid bool
  3156  	}{
  3157  		{
  3158  			name: "valid load balancer with simple load balancing", in: &networking.LoadBalancerSettings{
  3159  				LbPolicy: &networking.LoadBalancerSettings_Simple{
  3160  					Simple: networking.LoadBalancerSettings_ROUND_ROBIN,
  3161  				},
  3162  			},
  3163  			valid: true,
  3164  		},
  3165  
  3166  		{
  3167  			name: "valid load balancer with consistentHash load balancing", in: &networking.LoadBalancerSettings{
  3168  				LbPolicy: &networking.LoadBalancerSettings_ConsistentHash{
  3169  					ConsistentHash: &networking.LoadBalancerSettings_ConsistentHashLB{
  3170  						MinimumRingSize: 1024,
  3171  						HashKey: &networking.LoadBalancerSettings_ConsistentHashLB_HttpCookie{
  3172  							HttpCookie: &networking.LoadBalancerSettings_ConsistentHashLB_HTTPCookie{
  3173  								Name: "test",
  3174  								Ttl:  &duration,
  3175  							},
  3176  						},
  3177  					},
  3178  				},
  3179  			},
  3180  			valid: true,
  3181  		},
  3182  
  3183  		{
  3184  			name: "invalid load balancer with consistentHash load balancing, missing name", in: &networking.LoadBalancerSettings{
  3185  				LbPolicy: &networking.LoadBalancerSettings_ConsistentHash{
  3186  					ConsistentHash: &networking.LoadBalancerSettings_ConsistentHashLB{
  3187  						MinimumRingSize: 1024,
  3188  						HashKey: &networking.LoadBalancerSettings_ConsistentHashLB_HttpCookie{
  3189  							HttpCookie: &networking.LoadBalancerSettings_ConsistentHashLB_HTTPCookie{
  3190  								Ttl: &duration,
  3191  							},
  3192  						},
  3193  					},
  3194  				},
  3195  			},
  3196  			valid: false,
  3197  		},
  3198  
  3199  		{
  3200  			name: "invalid load balancer with consistentHash load balancing, maglev not prime", in: &networking.LoadBalancerSettings{
  3201  				LbPolicy: &networking.LoadBalancerSettings_ConsistentHash{
  3202  					ConsistentHash: &networking.LoadBalancerSettings_ConsistentHashLB{
  3203  						HashAlgorithm: &networking.LoadBalancerSettings_ConsistentHashLB_Maglev{
  3204  							Maglev: &networking.LoadBalancerSettings_ConsistentHashLB_MagLev{TableSize: 1000},
  3205  						},
  3206  					},
  3207  				},
  3208  			},
  3209  			valid: false,
  3210  		},
  3211  	}
  3212  
  3213  	for _, c := range cases {
  3214  		if got := validateLoadBalancer(c.in, nil); (got.Err == nil) != c.valid {
  3215  			t.Errorf("validateLoadBalancer failed on %v: got valid=%v but wanted valid=%v: %v",
  3216  				c.name, got.Err == nil, c.valid, got)
  3217  		}
  3218  	}
  3219  }
  3220  
  3221  func TestValidateOutlierDetection(t *testing.T) {
  3222  	cases := []struct {
  3223  		name  string
  3224  		in    *networking.OutlierDetection
  3225  		valid bool
  3226  		warn  bool
  3227  	}{
  3228  		{name: "valid outlier detection", in: &networking.OutlierDetection{
  3229  			Interval:           &durationpb.Duration{Seconds: 2},
  3230  			BaseEjectionTime:   &durationpb.Duration{Seconds: 2},
  3231  			MaxEjectionPercent: 50,
  3232  		}, valid: true},
  3233  
  3234  		{
  3235  			name: "invalid outlier detection, bad interval", in: &networking.OutlierDetection{
  3236  				Interval: &durationpb.Duration{Seconds: 2, Nanos: 5},
  3237  			},
  3238  			valid: false,
  3239  		},
  3240  
  3241  		{
  3242  			name: "invalid outlier detection, bad base ejection time", in: &networking.OutlierDetection{
  3243  				BaseEjectionTime: &durationpb.Duration{Seconds: 2, Nanos: 5},
  3244  			},
  3245  			valid: false,
  3246  		},
  3247  
  3248  		{
  3249  			name: "invalid outlier detection, bad max ejection percent", in: &networking.OutlierDetection{
  3250  				MaxEjectionPercent: 105,
  3251  			},
  3252  			valid: false,
  3253  		},
  3254  		{
  3255  			name: "invalid outlier detection, panic threshold too low", in: &networking.OutlierDetection{
  3256  				MinHealthPercent: -1,
  3257  			},
  3258  			valid: false,
  3259  		},
  3260  		{
  3261  			name: "invalid outlier detection, panic threshold too high", in: &networking.OutlierDetection{
  3262  				MinHealthPercent: 101,
  3263  			},
  3264  			valid: false,
  3265  		},
  3266  		{
  3267  			name: "deprecated outlier detection, ConsecutiveErrors", in: &networking.OutlierDetection{
  3268  				ConsecutiveErrors: 101,
  3269  			},
  3270  			valid: true,
  3271  			warn:  true,
  3272  		},
  3273  		{
  3274  			name: "consecutive local origin errors is set but split local origin errors is not set", in: &networking.OutlierDetection{
  3275  				ConsecutiveLocalOriginFailures: &wrapperspb.UInt32Value{Value: 10},
  3276  			},
  3277  			valid: false,
  3278  		},
  3279  	}
  3280  
  3281  	for _, c := range cases {
  3282  		got := validateOutlierDetection(c.in)
  3283  		if (got.Err == nil) != c.valid {
  3284  			t.Errorf("ValidateOutlierDetection failed on %v: got valid=%v but wanted valid=%v: %v",
  3285  				c.name, got.Err == nil, c.valid, got.Err)
  3286  		}
  3287  		if (got.Warning == nil) == c.warn {
  3288  			t.Errorf("ValidateOutlierDetection failed on %v: got warn=%v but wanted warn=%v: %v",
  3289  				c.name, got.Warning == nil, c.warn, got.Warning)
  3290  		}
  3291  	}
  3292  }
  3293  
  3294  func TestValidateServiceEntries(t *testing.T) {
  3295  	cases := []struct {
  3296  		name    string
  3297  		in      *networking.ServiceEntry
  3298  		valid   bool
  3299  		warning bool
  3300  	}{
  3301  		{
  3302  			name: "discovery type DNS", in: &networking.ServiceEntry{
  3303  				Hosts: []string{"*.google.com"},
  3304  				Ports: []*networking.ServicePort{
  3305  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3306  					{Number: 8080, Protocol: "http", Name: "http-valid2"},
  3307  				},
  3308  				Endpoints: []*networking.WorkloadEntry{
  3309  					{Address: "lon.google.com", Ports: map[string]uint32{"http-valid1": 8080}},
  3310  					{Address: "in.google.com", Ports: map[string]uint32{"http-valid2": 9080}},
  3311  				},
  3312  				Resolution: networking.ServiceEntry_DNS,
  3313  			},
  3314  			valid: true,
  3315  		},
  3316  		{
  3317  			name: "discovery type DNS Round Robin", in: &networking.ServiceEntry{
  3318  				Hosts: []string{"*.istio.io"},
  3319  				Ports: []*networking.ServicePort{
  3320  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3321  					{Number: 8080, Protocol: "http", Name: "http-valid2"},
  3322  				},
  3323  				Endpoints: []*networking.WorkloadEntry{
  3324  					{Address: "api-v1.istio.io", Ports: map[string]uint32{"http-valid1": 8080}},
  3325  				},
  3326  				Resolution: networking.ServiceEntry_DNS_ROUND_ROBIN,
  3327  			},
  3328  			valid: true,
  3329  		},
  3330  		{
  3331  			name: "discovery type DNS, label tlsMode: istio", in: &networking.ServiceEntry{
  3332  				Hosts: []string{"*.google.com"},
  3333  				Ports: []*networking.ServicePort{
  3334  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3335  					{Number: 8080, Protocol: "http", Name: "http-valid2"},
  3336  				},
  3337  				Endpoints: []*networking.WorkloadEntry{
  3338  					{Address: "lon.google.com", Ports: map[string]uint32{"http-valid1": 8080}, Labels: map[string]string{"security.istio.io/tlsMode": "istio"}},
  3339  					{Address: "in.google.com", Ports: map[string]uint32{"http-valid2": 9080}, Labels: map[string]string{"security.istio.io/tlsMode": "istio"}},
  3340  				},
  3341  				Resolution: networking.ServiceEntry_DNS,
  3342  			},
  3343  			valid: true,
  3344  		},
  3345  		{
  3346  			name: "discovery type DNS, one host set with IP address and https port",
  3347  			in: &networking.ServiceEntry{
  3348  				Hosts:     []string{"httpbin.org"},
  3349  				Addresses: []string{"10.10.10.10"},
  3350  				Ports: []*networking.ServicePort{
  3351  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3352  					{Number: 8080, Protocol: "http", Name: "http-valid2"},
  3353  					{Number: 443, Protocol: "https", Name: "https"},
  3354  				},
  3355  				Resolution: networking.ServiceEntry_DNS,
  3356  			},
  3357  			valid:   true,
  3358  			warning: false,
  3359  		},
  3360  
  3361  		{
  3362  			name: "discovery type DNS, multi hosts set with IP address and https port",
  3363  			in: &networking.ServiceEntry{
  3364  				Hosts:     []string{"httpbin.org", "wikipedia.org"},
  3365  				Addresses: []string{"10.10.10.10"},
  3366  				Ports: []*networking.ServicePort{
  3367  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3368  					{Number: 8080, Protocol: "http", Name: "http-valid2"},
  3369  					{Number: 443, Protocol: "https", Name: "https"},
  3370  				},
  3371  				Resolution: networking.ServiceEntry_DNS,
  3372  			},
  3373  			valid:   true,
  3374  			warning: true,
  3375  		},
  3376  
  3377  		{
  3378  			name: "discovery type DNS, IP address set",
  3379  			in: &networking.ServiceEntry{
  3380  				Hosts:     []string{"*.google.com"},
  3381  				Addresses: []string{"10.10.10.10"},
  3382  				Ports: []*networking.ServicePort{
  3383  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3384  					{Number: 8080, Protocol: "http", Name: "http-valid2"},
  3385  				},
  3386  				Endpoints: []*networking.WorkloadEntry{
  3387  					{Address: "lon.google.com", Ports: map[string]uint32{"http-valid1": 8080}},
  3388  					{Address: "in.google.com", Ports: map[string]uint32{"http-valid2": 9080}},
  3389  				},
  3390  				Resolution: networking.ServiceEntry_DNS,
  3391  			},
  3392  			valid:   true,
  3393  			warning: false,
  3394  		},
  3395  
  3396  		{
  3397  			name: "discovery type DNS, IP in endpoints", in: &networking.ServiceEntry{
  3398  				Hosts: []string{"*.google.com"},
  3399  				Ports: []*networking.ServicePort{
  3400  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3401  					{Number: 8080, Protocol: "http", Name: "http-valid2"},
  3402  				},
  3403  				Endpoints: []*networking.WorkloadEntry{
  3404  					{Address: "1.1.1.1", Ports: map[string]uint32{"http-valid1": 8080}},
  3405  					{Address: "in.google.com", Ports: map[string]uint32{"http-valid2": 9080}},
  3406  				},
  3407  				Resolution: networking.ServiceEntry_DNS,
  3408  			},
  3409  			valid: true,
  3410  		},
  3411  
  3412  		{
  3413  			name: "empty hosts", in: &networking.ServiceEntry{
  3414  				Ports: []*networking.ServicePort{
  3415  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3416  				},
  3417  				Endpoints: []*networking.WorkloadEntry{
  3418  					{Address: "in.google.com", Ports: map[string]uint32{"http-valid2": 9080}},
  3419  				},
  3420  				Resolution: networking.ServiceEntry_DNS,
  3421  			},
  3422  			valid: false,
  3423  		},
  3424  		{
  3425  			name: "bad hosts", in: &networking.ServiceEntry{
  3426  				Hosts: []string{"-"},
  3427  				Ports: []*networking.ServicePort{
  3428  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3429  				},
  3430  				Endpoints: []*networking.WorkloadEntry{
  3431  					{Address: "in.google.com", Ports: map[string]uint32{"http-valid2": 9080}},
  3432  				},
  3433  				Resolution: networking.ServiceEntry_DNS,
  3434  			},
  3435  			valid: false,
  3436  		},
  3437  		{
  3438  			name: "full wildcard host", in: &networking.ServiceEntry{
  3439  				Hosts: []string{"foo.com", "*"},
  3440  				Ports: []*networking.ServicePort{
  3441  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3442  				},
  3443  				Endpoints: []*networking.WorkloadEntry{
  3444  					{Address: "in.google.com", Ports: map[string]uint32{"http-valid2": 9080}},
  3445  				},
  3446  				Resolution: networking.ServiceEntry_DNS,
  3447  			},
  3448  			valid: false,
  3449  		},
  3450  		{
  3451  			name: "short name host", in: &networking.ServiceEntry{
  3452  				Hosts: []string{"foo", "bar.com"},
  3453  				Ports: []*networking.ServicePort{
  3454  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3455  				},
  3456  				Endpoints: []*networking.WorkloadEntry{
  3457  					{Address: "in.google.com", Ports: map[string]uint32{"http-valid1": 9080}},
  3458  				},
  3459  				Resolution: networking.ServiceEntry_DNS,
  3460  			},
  3461  			valid: true,
  3462  		},
  3463  		{
  3464  			name: "undefined endpoint port", in: &networking.ServiceEntry{
  3465  				Hosts: []string{"google.com"},
  3466  				Ports: []*networking.ServicePort{
  3467  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3468  					{Number: 80, Protocol: "http", Name: "http-valid2"},
  3469  				},
  3470  				Endpoints: []*networking.WorkloadEntry{
  3471  					{Address: "lon.google.com", Ports: map[string]uint32{"http-valid1": 8080}},
  3472  					{Address: "in.google.com", Ports: map[string]uint32{"http-dne": 9080}},
  3473  				},
  3474  				Resolution: networking.ServiceEntry_DNS,
  3475  			},
  3476  			valid: false,
  3477  		},
  3478  
  3479  		{
  3480  			name: "discovery type DNS, non-FQDN endpoint", in: &networking.ServiceEntry{
  3481  				Hosts: []string{"*.google.com"},
  3482  				Ports: []*networking.ServicePort{
  3483  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3484  					{Number: 8080, Protocol: "http", Name: "http-valid2"},
  3485  				},
  3486  				Endpoints: []*networking.WorkloadEntry{
  3487  					{Address: "*.lon.google.com", Ports: map[string]uint32{"http-valid1": 8080}},
  3488  					{Address: "in.google.com", Ports: map[string]uint32{"http-dne": 9080}},
  3489  				},
  3490  				Resolution: networking.ServiceEntry_DNS,
  3491  			},
  3492  			valid: false,
  3493  		},
  3494  
  3495  		{
  3496  			name: "discovery type DNS, non-FQDN host", in: &networking.ServiceEntry{
  3497  				Hosts: []string{"*.google.com"},
  3498  				Ports: []*networking.ServicePort{
  3499  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3500  					{Number: 8080, Protocol: "http", Name: "http-valid2"},
  3501  				},
  3502  
  3503  				Resolution: networking.ServiceEntry_DNS,
  3504  			},
  3505  			valid: false,
  3506  		},
  3507  
  3508  		{
  3509  			name: "discovery type DNS, no endpoints", in: &networking.ServiceEntry{
  3510  				Hosts: []string{"google.com"},
  3511  				Ports: []*networking.ServicePort{
  3512  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3513  					{Number: 8080, Protocol: "http", Name: "http-valid2"},
  3514  				},
  3515  
  3516  				Resolution: networking.ServiceEntry_DNS,
  3517  			},
  3518  			valid: true,
  3519  		},
  3520  
  3521  		{
  3522  			name: "discovery type DNS, unix endpoint", in: &networking.ServiceEntry{
  3523  				Hosts: []string{"*.google.com"},
  3524  				Ports: []*networking.ServicePort{
  3525  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3526  				},
  3527  				Endpoints: []*networking.WorkloadEntry{
  3528  					{Address: "unix:///lon/google/com"},
  3529  				},
  3530  				Resolution: networking.ServiceEntry_DNS,
  3531  			},
  3532  			valid: false,
  3533  		},
  3534  
  3535  		{
  3536  			name: "discovery type none", in: &networking.ServiceEntry{
  3537  				Hosts: []string{"google.com"},
  3538  				Ports: []*networking.ServicePort{
  3539  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3540  					{Number: 8080, Protocol: "http", Name: "http-valid2"},
  3541  				},
  3542  				Resolution: networking.ServiceEntry_NONE,
  3543  			},
  3544  			valid: true,
  3545  		},
  3546  
  3547  		{
  3548  			name: "discovery type none, endpoints provided", in: &networking.ServiceEntry{
  3549  				Hosts: []string{"google.com"},
  3550  				Ports: []*networking.ServicePort{
  3551  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3552  					{Number: 8080, Protocol: "http", Name: "http-valid2"},
  3553  				},
  3554  				Endpoints: []*networking.WorkloadEntry{
  3555  					{Address: "lon.google.com", Ports: map[string]uint32{"http-valid1": 8080}},
  3556  				},
  3557  				Resolution: networking.ServiceEntry_NONE,
  3558  			},
  3559  			valid: false,
  3560  		},
  3561  
  3562  		{
  3563  			name: "discovery type none, cidr addresses", in: &networking.ServiceEntry{
  3564  				Hosts:     []string{"google.com"},
  3565  				Addresses: []string{"172.1.2.16/16"},
  3566  				Ports: []*networking.ServicePort{
  3567  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3568  					{Number: 8080, Protocol: "http", Name: "http-valid2"},
  3569  				},
  3570  				Resolution: networking.ServiceEntry_NONE,
  3571  			},
  3572  			valid: true,
  3573  		},
  3574  
  3575  		{
  3576  			name: "discovery type static, cidr addresses with endpoints", in: &networking.ServiceEntry{
  3577  				Hosts:     []string{"google.com"},
  3578  				Addresses: []string{"172.1.2.16/16"},
  3579  				Ports: []*networking.ServicePort{
  3580  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3581  					{Number: 8080, Protocol: "http", Name: "http-valid2"},
  3582  				},
  3583  				Endpoints: []*networking.WorkloadEntry{
  3584  					{Address: "1.1.1.1", Ports: map[string]uint32{"http-valid1": 8080}},
  3585  					{Address: "2.2.2.2", Ports: map[string]uint32{"http-valid2": 9080}},
  3586  				},
  3587  				Resolution: networking.ServiceEntry_STATIC,
  3588  			},
  3589  			valid: true,
  3590  		},
  3591  
  3592  		{
  3593  			name: "discovery type static", in: &networking.ServiceEntry{
  3594  				Hosts:     []string{"google.com"},
  3595  				Addresses: []string{"172.1.2.16"},
  3596  				Ports: []*networking.ServicePort{
  3597  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3598  					{Number: 8080, Protocol: "http", Name: "http-valid2"},
  3599  				},
  3600  				Endpoints: []*networking.WorkloadEntry{
  3601  					{Address: "1.1.1.1", Ports: map[string]uint32{"http-valid1": 8080}},
  3602  					{Address: "2.2.2.2", Ports: map[string]uint32{"http-valid2": 9080}},
  3603  				},
  3604  				Resolution: networking.ServiceEntry_STATIC,
  3605  			},
  3606  			valid: true,
  3607  		},
  3608  
  3609  		{
  3610  			name: "discovery type static, FQDN in endpoints", in: &networking.ServiceEntry{
  3611  				Hosts:     []string{"google.com"},
  3612  				Addresses: []string{"172.1.2.16"},
  3613  				Ports: []*networking.ServicePort{
  3614  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3615  					{Number: 8080, Protocol: "http", Name: "http-valid2"},
  3616  				},
  3617  				Endpoints: []*networking.WorkloadEntry{
  3618  					{Address: "google.com", Ports: map[string]uint32{"http-valid1": 8080}},
  3619  					{Address: "2.2.2.2", Ports: map[string]uint32{"http-valid2": 9080}},
  3620  				},
  3621  				Resolution: networking.ServiceEntry_STATIC,
  3622  			},
  3623  			valid: false,
  3624  		},
  3625  
  3626  		{
  3627  			name: "discovery type static, missing endpoints", in: &networking.ServiceEntry{
  3628  				Hosts:     []string{"google.com"},
  3629  				Addresses: []string{"172.1.2.16"},
  3630  				Ports: []*networking.ServicePort{
  3631  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3632  					{Number: 8080, Protocol: "http", Name: "http-valid2"},
  3633  				},
  3634  				Resolution: networking.ServiceEntry_STATIC,
  3635  			},
  3636  			valid: true,
  3637  		},
  3638  
  3639  		{
  3640  			name: "discovery type static, bad endpoint port name", in: &networking.ServiceEntry{
  3641  				Hosts:     []string{"google.com"},
  3642  				Addresses: []string{"172.1.2.16"},
  3643  				Ports: []*networking.ServicePort{
  3644  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3645  					{Number: 8080, Protocol: "http", Name: "http-valid2"},
  3646  				},
  3647  				Endpoints: []*networking.WorkloadEntry{
  3648  					{Address: "1.1.1.1", Ports: map[string]uint32{"http-valid1": 8080}},
  3649  					{Address: "2.2.2.2", Ports: map[string]uint32{"http-dne": 9080}},
  3650  				},
  3651  				Resolution: networking.ServiceEntry_STATIC,
  3652  			},
  3653  			valid: false,
  3654  		},
  3655  
  3656  		{
  3657  			name: "discovery type none, conflicting port names", in: &networking.ServiceEntry{
  3658  				Hosts: []string{"google.com"},
  3659  				Ports: []*networking.ServicePort{
  3660  					{Number: 80, Protocol: "http", Name: "http-conflict"},
  3661  					{Number: 8080, Protocol: "http", Name: "http-conflict"},
  3662  				},
  3663  				Resolution: networking.ServiceEntry_NONE,
  3664  			},
  3665  			valid: false,
  3666  		},
  3667  
  3668  		{
  3669  			name: "discovery type none, conflicting port numbers", in: &networking.ServiceEntry{
  3670  				Hosts: []string{"google.com"},
  3671  				Ports: []*networking.ServicePort{
  3672  					{Number: 80, Protocol: "http", Name: "http-conflict1"},
  3673  					{Number: 80, Protocol: "http", Name: "http-conflict2"},
  3674  				},
  3675  				Resolution: networking.ServiceEntry_NONE,
  3676  			},
  3677  			valid: false,
  3678  		},
  3679  
  3680  		{
  3681  			name: "unix socket", in: &networking.ServiceEntry{
  3682  				Hosts: []string{"uds.cluster.local"},
  3683  				Ports: []*networking.ServicePort{
  3684  					{Number: 6553, Protocol: "grpc", Name: "grpc-service1"},
  3685  				},
  3686  				Resolution: networking.ServiceEntry_STATIC,
  3687  				Endpoints: []*networking.WorkloadEntry{
  3688  					{Address: "unix:///path/to/socket"},
  3689  				},
  3690  			},
  3691  			valid: true,
  3692  		},
  3693  
  3694  		{
  3695  			name: "unix socket, relative path", in: &networking.ServiceEntry{
  3696  				Hosts: []string{"uds.cluster.local"},
  3697  				Ports: []*networking.ServicePort{
  3698  					{Number: 6553, Protocol: "grpc", Name: "grpc-service1"},
  3699  				},
  3700  				Resolution: networking.ServiceEntry_STATIC,
  3701  				Endpoints: []*networking.WorkloadEntry{
  3702  					{Address: "unix://./relative/path.sock"},
  3703  				},
  3704  			},
  3705  			valid: false,
  3706  		},
  3707  
  3708  		{
  3709  			name: "unix socket, endpoint ports", in: &networking.ServiceEntry{
  3710  				Hosts: []string{"uds.cluster.local"},
  3711  				Ports: []*networking.ServicePort{
  3712  					{Number: 6553, Protocol: "grpc", Name: "grpc-service1"},
  3713  				},
  3714  				Resolution: networking.ServiceEntry_STATIC,
  3715  				Endpoints: []*networking.WorkloadEntry{
  3716  					{Address: "unix:///path/to/socket", Ports: map[string]uint32{"grpc-service1": 6553}},
  3717  				},
  3718  			},
  3719  			valid: false,
  3720  		},
  3721  		{
  3722  			name: "unix socket, multiple service ports", in: &networking.ServiceEntry{
  3723  				Hosts: []string{"uds.cluster.local"},
  3724  				Ports: []*networking.ServicePort{
  3725  					{Number: 6553, Protocol: "grpc", Name: "grpc-service1"},
  3726  					{Number: 80, Protocol: "http", Name: "http-service2"},
  3727  				},
  3728  				Resolution: networking.ServiceEntry_STATIC,
  3729  				Endpoints: []*networking.WorkloadEntry{
  3730  					{Address: "unix:///path/to/socket"},
  3731  				},
  3732  			},
  3733  			valid: false,
  3734  		},
  3735  		{
  3736  			name: "empty protocol", in: &networking.ServiceEntry{
  3737  				Hosts:     []string{"google.com"},
  3738  				Addresses: []string{"172.1.2.16/16"},
  3739  				Ports: []*networking.ServicePort{
  3740  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3741  					{Number: 8080, Name: "http-valid2"},
  3742  				},
  3743  				Resolution: networking.ServiceEntry_NONE,
  3744  			},
  3745  			valid: true,
  3746  		},
  3747  		{
  3748  			name: "selector", in: &networking.ServiceEntry{
  3749  				Hosts:            []string{"google.com"},
  3750  				WorkloadSelector: &networking.WorkloadSelector{Labels: map[string]string{"foo": "bar"}},
  3751  				Ports: []*networking.ServicePort{
  3752  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3753  				},
  3754  			},
  3755  			valid:   true,
  3756  			warning: true,
  3757  		},
  3758  		{
  3759  			name: "workload selector without labels",
  3760  			in: &networking.ServiceEntry{
  3761  				Hosts: []string{"google.com"},
  3762  				Ports: []*networking.ServicePort{
  3763  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3764  					{Number: 8080, Protocol: "http", Name: "http-valid2"},
  3765  				},
  3766  				WorkloadSelector: &networking.WorkloadSelector{},
  3767  			},
  3768  			valid:   true,
  3769  			warning: true,
  3770  		},
  3771  		{
  3772  			name: "selector and endpoints", in: &networking.ServiceEntry{
  3773  				Hosts:            []string{"google.com"},
  3774  				WorkloadSelector: &networking.WorkloadSelector{Labels: map[string]string{"foo": "bar"}},
  3775  				Ports: []*networking.ServicePort{
  3776  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3777  				},
  3778  				Endpoints: []*networking.WorkloadEntry{
  3779  					{Address: "1.1.1.1"},
  3780  				},
  3781  			},
  3782  			valid:   false,
  3783  			warning: true,
  3784  		},
  3785  		{
  3786  			name: "selector and resolution NONE", in: &networking.ServiceEntry{
  3787  				Hosts:            []string{"google.com"},
  3788  				WorkloadSelector: &networking.WorkloadSelector{Labels: map[string]string{"foo": "bar"}},
  3789  				Ports: []*networking.ServicePort{
  3790  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3791  				},
  3792  				Resolution: networking.ServiceEntry_NONE,
  3793  			},
  3794  			valid:   true,
  3795  			warning: true,
  3796  		},
  3797  		{
  3798  			name: "selector and resolution DNS", in: &networking.ServiceEntry{
  3799  				Hosts:            []string{"google.com"},
  3800  				WorkloadSelector: &networking.WorkloadSelector{Labels: map[string]string{"foo": "bar"}},
  3801  				Ports: []*networking.ServicePort{
  3802  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3803  				},
  3804  				Resolution: networking.ServiceEntry_DNS,
  3805  			},
  3806  			valid:   true,
  3807  			warning: true,
  3808  		},
  3809  		{
  3810  			name: "bad selector key", in: &networking.ServiceEntry{
  3811  				Hosts:            []string{"google.com"},
  3812  				WorkloadSelector: &networking.WorkloadSelector{Labels: map[string]string{"": "bar"}},
  3813  				Ports: []*networking.ServicePort{
  3814  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3815  				},
  3816  			},
  3817  			valid: false,
  3818  		},
  3819  		{
  3820  			name: "repeat target port", in: &networking.ServiceEntry{
  3821  				Hosts:      []string{"google.com"},
  3822  				Resolution: networking.ServiceEntry_DNS,
  3823  				Ports: []*networking.ServicePort{
  3824  					{Number: 80, Protocol: "http", Name: "http-valid1", TargetPort: 80},
  3825  					{Number: 81, Protocol: "http", Name: "http-valid2", TargetPort: 80},
  3826  				},
  3827  			},
  3828  			valid: true,
  3829  		},
  3830  		{
  3831  			name: "valid target port", in: &networking.ServiceEntry{
  3832  				Hosts:      []string{"google.com"},
  3833  				Resolution: networking.ServiceEntry_DNS,
  3834  				Ports: []*networking.ServicePort{
  3835  					{Number: 80, Protocol: "http", Name: "http-valid1", TargetPort: 81},
  3836  				},
  3837  			},
  3838  			valid: true,
  3839  		},
  3840  		{
  3841  			name: "invalid target port", in: &networking.ServiceEntry{
  3842  				Hosts:      []string{"google.com"},
  3843  				Resolution: networking.ServiceEntry_DNS,
  3844  				Ports: []*networking.ServicePort{
  3845  					{Number: 80, Protocol: "http", Name: "http-valid1", TargetPort: 65536},
  3846  				},
  3847  			},
  3848  			valid: false,
  3849  		},
  3850  		{
  3851  			name: "warn target port", in: &networking.ServiceEntry{
  3852  				Hosts:            []string{"google.com"},
  3853  				WorkloadSelector: &networking.WorkloadSelector{Labels: map[string]string{"key": "bar"}},
  3854  				Ports: []*networking.ServicePort{
  3855  					{Number: 80, Protocol: "http", Name: "http-valid1", TargetPort: 1234},
  3856  				},
  3857  			},
  3858  			valid:   true,
  3859  			warning: true,
  3860  		},
  3861  		{
  3862  			name: "valid endpoint port", in: &networking.ServiceEntry{
  3863  				Hosts: []string{"google.com"},
  3864  				Ports: []*networking.ServicePort{
  3865  					{Number: 80, Protocol: "http", Name: "http-valid1", TargetPort: 81},
  3866  				},
  3867  				Resolution: networking.ServiceEntry_STATIC,
  3868  				Endpoints: []*networking.WorkloadEntry{
  3869  					{
  3870  						Address: "1.1.1.1",
  3871  						Ports: map[string]uint32{
  3872  							"http-valid1": 8081,
  3873  						},
  3874  					},
  3875  				},
  3876  			},
  3877  			valid: true,
  3878  		},
  3879  		{
  3880  			name: "invalid endpoint port", in: &networking.ServiceEntry{
  3881  				Hosts: []string{"google.com"},
  3882  				Ports: []*networking.ServicePort{
  3883  					{Number: 80, Protocol: "http", Name: "http-valid1", TargetPort: 81},
  3884  				},
  3885  				Resolution: networking.ServiceEntry_STATIC,
  3886  				Endpoints: []*networking.WorkloadEntry{
  3887  					{
  3888  						Address: "1.1.1.1",
  3889  						Ports: map[string]uint32{
  3890  							"http-valid1": 65536,
  3891  						},
  3892  					},
  3893  				},
  3894  			},
  3895  			valid: false,
  3896  		},
  3897  		{
  3898  			name: "protocol unset for addresses empty", in: &networking.ServiceEntry{
  3899  				Hosts:     []string{"google.com"},
  3900  				Addresses: []string{},
  3901  				Ports: []*networking.ServicePort{
  3902  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3903  					{Number: 8080, Name: "http-valid2"},
  3904  				},
  3905  				Resolution: networking.ServiceEntry_NONE,
  3906  			},
  3907  			valid:   true,
  3908  			warning: true,
  3909  		},
  3910  		{
  3911  			name: "protocol is TCP for addresses empty", in: &networking.ServiceEntry{
  3912  				Hosts:     []string{"google.com"},
  3913  				Addresses: []string{},
  3914  				Ports: []*networking.ServicePort{
  3915  					{Number: 80, Protocol: "http", Name: "http-valid1"},
  3916  					{Number: 81, Protocol: "TCP", Name: "tcp-valid1"},
  3917  				},
  3918  				Resolution: networking.ServiceEntry_NONE,
  3919  			},
  3920  			valid:   true,
  3921  			warning: true,
  3922  		},
  3923  		{
  3924  			name: "dns round robin with more than one endpoint", in: &networking.ServiceEntry{
  3925  				Hosts:     []string{"google.com"},
  3926  				Addresses: []string{},
  3927  				Ports: []*networking.ServicePort{
  3928  					{Number: 8081, Protocol: "http", Name: "http-valid1"},
  3929  				},
  3930  				Endpoints: []*networking.WorkloadEntry{
  3931  					{
  3932  						Address: "api-v1.istio.io",
  3933  						Ports: map[string]uint32{
  3934  							"http-valid1": 8081,
  3935  						},
  3936  					},
  3937  					{
  3938  						Address: "1.1.1.2",
  3939  						Ports: map[string]uint32{
  3940  							"http-valid1": 8081,
  3941  						},
  3942  					},
  3943  				},
  3944  				Resolution: networking.ServiceEntry_DNS_ROUND_ROBIN,
  3945  			},
  3946  			valid:   false,
  3947  			warning: false,
  3948  		},
  3949  		{
  3950  			name: "dns round robin with 0 endpoints", in: &networking.ServiceEntry{
  3951  				Hosts:     []string{"google.com"},
  3952  				Addresses: []string{},
  3953  				Ports: []*networking.ServicePort{
  3954  					{Number: 8081, Protocol: "http", Name: "http-valid1"},
  3955  				},
  3956  				Endpoints:  []*networking.WorkloadEntry{},
  3957  				Resolution: networking.ServiceEntry_DNS_ROUND_ROBIN,
  3958  			},
  3959  			valid:   true,
  3960  			warning: false,
  3961  		},
  3962  		{
  3963  			name: "partial wildcard hosts", in: &networking.ServiceEntry{
  3964  				Hosts:     []string{"*.nytimes.com", "*washingtonpost.com"},
  3965  				Addresses: []string{},
  3966  				Ports: []*networking.ServicePort{
  3967  					{Number: 443, Protocol: "HTTPS", Name: "https-nytimes"},
  3968  				},
  3969  				Endpoints:  []*networking.WorkloadEntry{},
  3970  				Resolution: networking.ServiceEntry_NONE,
  3971  			},
  3972  			valid:   true,
  3973  			warning: true,
  3974  		},
  3975  		{
  3976  			name: "network set with no address on endpoint", in: &networking.ServiceEntry{
  3977  				Hosts:     []string{"www.example.com"},
  3978  				Addresses: []string{},
  3979  				Ports: []*networking.ServicePort{
  3980  					{Number: 443, Protocol: "HTTPS", Name: "https-example"},
  3981  				},
  3982  				Endpoints: []*networking.WorkloadEntry{
  3983  					{Network: "cluster-1"},
  3984  				},
  3985  				Resolution: networking.ServiceEntry_STATIC,
  3986  			},
  3987  			valid:   true,
  3988  			warning: true,
  3989  		},
  3990  	}
  3991  
  3992  	for _, c := range cases {
  3993  		t.Run(c.name, func(t *testing.T) {
  3994  			warning, err := ValidateServiceEntry(config.Config{
  3995  				Meta: config.Meta{
  3996  					Name:      someName,
  3997  					Namespace: someNamespace,
  3998  				},
  3999  				Spec: c.in,
  4000  			})
  4001  			if (err == nil) != c.valid {
  4002  				t.Errorf("ValidateServiceEntry got valid=%v but wanted valid=%v: %v",
  4003  					err == nil, c.valid, err)
  4004  			}
  4005  			if (warning != nil) != c.warning {
  4006  				t.Errorf("ValidateServiceEntry got warning=%v but wanted warning=%v: %v",
  4007  					warning != nil, c.warning, warning)
  4008  			}
  4009  		})
  4010  	}
  4011  }
  4012  
  4013  func TestValidateAuthorizationPolicy(t *testing.T) {
  4014  	cases := []struct {
  4015  		name        string
  4016  		annotations map[string]string
  4017  		in          proto.Message
  4018  		valid       bool
  4019  		Warning     bool
  4020  	}{
  4021  		{
  4022  			name: "good",
  4023  			in: &security_beta.AuthorizationPolicy{
  4024  				Selector: &api.WorkloadSelector{
  4025  					MatchLabels: map[string]string{
  4026  						"app":     "httpbin",
  4027  						"version": "v1",
  4028  					},
  4029  				},
  4030  				Rules: []*security_beta.Rule{
  4031  					{
  4032  						From: []*security_beta.Rule_From{
  4033  							{
  4034  								Source: &security_beta.Source{
  4035  									Principals: []string{"sa1"},
  4036  								},
  4037  							},
  4038  							{
  4039  								Source: &security_beta.Source{
  4040  									Principals: []string{"sa2"},
  4041  								},
  4042  							},
  4043  						},
  4044  						To: []*security_beta.Rule_To{
  4045  							{
  4046  								Operation: &security_beta.Operation{
  4047  									Methods: []string{"GET"},
  4048  								},
  4049  							},
  4050  							{
  4051  								Operation: &security_beta.Operation{
  4052  									Methods: []string{"POST"},
  4053  								},
  4054  							},
  4055  						},
  4056  						When: []*security_beta.Condition{
  4057  							{
  4058  								Key:    "source.ip",
  4059  								Values: []string{"1.2.3.4", "5.6.7.0/24"},
  4060  							},
  4061  							{
  4062  								Key:    "request.headers[:authority]",
  4063  								Values: []string{"v1", "v2"},
  4064  							},
  4065  						},
  4066  					},
  4067  				},
  4068  			},
  4069  			valid: true,
  4070  		},
  4071  		{
  4072  			name: "custom-good",
  4073  			in: &security_beta.AuthorizationPolicy{
  4074  				Action: security_beta.AuthorizationPolicy_CUSTOM,
  4075  				ActionDetail: &security_beta.AuthorizationPolicy_Provider{
  4076  					Provider: &security_beta.AuthorizationPolicy_ExtensionProvider{
  4077  						Name: "my-custom-authz",
  4078  					},
  4079  				},
  4080  				Rules: []*security_beta.Rule{{}},
  4081  			},
  4082  			valid: true,
  4083  		},
  4084  		{
  4085  			name: "custom-empty-provider",
  4086  			in: &security_beta.AuthorizationPolicy{
  4087  				Action: security_beta.AuthorizationPolicy_CUSTOM,
  4088  				ActionDetail: &security_beta.AuthorizationPolicy_Provider{
  4089  					Provider: &security_beta.AuthorizationPolicy_ExtensionProvider{
  4090  						Name: "",
  4091  					},
  4092  				},
  4093  				Rules: []*security_beta.Rule{{}},
  4094  			},
  4095  			valid: false,
  4096  		},
  4097  		{
  4098  			name: "custom-nil-provider",
  4099  			in: &security_beta.AuthorizationPolicy{
  4100  				Action: security_beta.AuthorizationPolicy_CUSTOM,
  4101  				Rules:  []*security_beta.Rule{{}},
  4102  			},
  4103  			valid: false,
  4104  		},
  4105  		{
  4106  			name: "custom-invalid-rule",
  4107  			in: &security_beta.AuthorizationPolicy{
  4108  				Action: security_beta.AuthorizationPolicy_CUSTOM,
  4109  				ActionDetail: &security_beta.AuthorizationPolicy_Provider{
  4110  					Provider: &security_beta.AuthorizationPolicy_ExtensionProvider{
  4111  						Name: "my-custom-authz",
  4112  					},
  4113  				},
  4114  				Rules: []*security_beta.Rule{
  4115  					{
  4116  						From: []*security_beta.Rule_From{
  4117  							{
  4118  								Source: &security_beta.Source{
  4119  									Namespaces:           []string{"ns"},
  4120  									NotNamespaces:        []string{"ns"},
  4121  									Principals:           []string{"id"},
  4122  									NotPrincipals:        []string{"id"},
  4123  									RequestPrincipals:    []string{"req"},
  4124  									NotRequestPrincipals: []string{"req"},
  4125  								},
  4126  							},
  4127  						},
  4128  						When: []*security_beta.Condition{
  4129  							{
  4130  								Key:       "source.namespace",
  4131  								Values:    []string{"source.namespace1"},
  4132  								NotValues: []string{"source.namespace2"},
  4133  							},
  4134  							{
  4135  								Key:       "source.principal",
  4136  								Values:    []string{"source.principal1"},
  4137  								NotValues: []string{"source.principal2"},
  4138  							},
  4139  							{
  4140  								Key:       "request.auth.claims[a]",
  4141  								Values:    []string{"claims1"},
  4142  								NotValues: []string{"claims2"},
  4143  							},
  4144  						},
  4145  					},
  4146  				},
  4147  			},
  4148  			valid: false,
  4149  		},
  4150  		{
  4151  			name: "provider-wrong-action",
  4152  			in: &security_beta.AuthorizationPolicy{
  4153  				Action: security_beta.AuthorizationPolicy_ALLOW,
  4154  				ActionDetail: &security_beta.AuthorizationPolicy_Provider{
  4155  					Provider: &security_beta.AuthorizationPolicy_ExtensionProvider{
  4156  						Name: "",
  4157  					},
  4158  				},
  4159  				Rules: []*security_beta.Rule{{}},
  4160  			},
  4161  			valid: false,
  4162  		},
  4163  		{
  4164  			name: "allow-rules-nil",
  4165  			in: &security_beta.AuthorizationPolicy{
  4166  				Action: security_beta.AuthorizationPolicy_ALLOW,
  4167  			},
  4168  			valid: true,
  4169  		},
  4170  		{
  4171  			name:        "dry-run-valid-allow",
  4172  			annotations: map[string]string{"istio.io/dry-run": "true"},
  4173  			in: &security_beta.AuthorizationPolicy{
  4174  				Action: security_beta.AuthorizationPolicy_ALLOW,
  4175  			},
  4176  			valid: true,
  4177  		},
  4178  		{
  4179  			name:        "dry-run-valid-deny",
  4180  			annotations: map[string]string{"istio.io/dry-run": "false"},
  4181  			in: &security_beta.AuthorizationPolicy{
  4182  				Action: security_beta.AuthorizationPolicy_DENY,
  4183  				Rules:  []*security_beta.Rule{{}},
  4184  			},
  4185  			valid: true,
  4186  		},
  4187  		{
  4188  			name:        "dry-run-invalid-value",
  4189  			annotations: map[string]string{"istio.io/dry-run": "foo"},
  4190  			in: &security_beta.AuthorizationPolicy{
  4191  				Action: security_beta.AuthorizationPolicy_ALLOW,
  4192  			},
  4193  			valid: false,
  4194  		},
  4195  		{
  4196  			name:        "dry-run-invalid-action-custom",
  4197  			annotations: map[string]string{"istio.io/dry-run": "true"},
  4198  			in: &security_beta.AuthorizationPolicy{
  4199  				Action: security_beta.AuthorizationPolicy_CUSTOM,
  4200  			},
  4201  			valid: false,
  4202  		},
  4203  		{
  4204  			name:        "dry-run-invalid-action-audit",
  4205  			annotations: map[string]string{"istio.io/dry-run": "true"},
  4206  			in: &security_beta.AuthorizationPolicy{
  4207  				Action: security_beta.AuthorizationPolicy_AUDIT,
  4208  			},
  4209  			valid: false,
  4210  		},
  4211  		{
  4212  			name: "deny-rules-nil",
  4213  			in: &security_beta.AuthorizationPolicy{
  4214  				Action: security_beta.AuthorizationPolicy_DENY,
  4215  			},
  4216  			valid: false,
  4217  		},
  4218  		{
  4219  			name: "selector-empty-value",
  4220  			in: &security_beta.AuthorizationPolicy{
  4221  				Selector: &api.WorkloadSelector{
  4222  					MatchLabels: map[string]string{
  4223  						"app":     "",
  4224  						"version": "v1",
  4225  					},
  4226  				},
  4227  			},
  4228  			valid: true,
  4229  		},
  4230  		{
  4231  			name: "selector-empty-key",
  4232  			in: &security_beta.AuthorizationPolicy{
  4233  				Selector: &api.WorkloadSelector{
  4234  					MatchLabels: map[string]string{
  4235  						"app": "httpbin",
  4236  						"":    "v1",
  4237  					},
  4238  				},
  4239  			},
  4240  			valid: false,
  4241  		},
  4242  		{
  4243  			name: "selector-empty-labels",
  4244  			in: &security_beta.AuthorizationPolicy{
  4245  				Selector: &api.WorkloadSelector{},
  4246  			},
  4247  			valid:   true,
  4248  			Warning: true,
  4249  		},
  4250  		{
  4251  			name: "selector-wildcard-value",
  4252  			in: &security_beta.AuthorizationPolicy{
  4253  				Selector: &api.WorkloadSelector{
  4254  					MatchLabels: map[string]string{
  4255  						"app": "httpbin-*",
  4256  					},
  4257  				},
  4258  			},
  4259  			valid: false,
  4260  		},
  4261  		{
  4262  			name: "selector-wildcard-key",
  4263  			in: &security_beta.AuthorizationPolicy{
  4264  				Selector: &api.WorkloadSelector{
  4265  					MatchLabels: map[string]string{
  4266  						"app-*": "httpbin",
  4267  					},
  4268  				},
  4269  			},
  4270  			valid: false,
  4271  		},
  4272  		{
  4273  			name: "target-ref-good",
  4274  			in: &security_beta.AuthorizationPolicy{
  4275  				Action: security_beta.AuthorizationPolicy_DENY,
  4276  				TargetRef: &api.PolicyTargetReference{
  4277  					Group: gvk.KubernetesGateway.Group,
  4278  					Kind:  gvk.KubernetesGateway.Kind,
  4279  					Name:  "foo",
  4280  				},
  4281  				Rules: []*security_beta.Rule{
  4282  					{
  4283  						From: []*security_beta.Rule_From{
  4284  							{
  4285  								Source: &security_beta.Source{
  4286  									Principals: []string{"temp"},
  4287  								},
  4288  							},
  4289  						},
  4290  						To: []*security_beta.Rule_To{
  4291  							{
  4292  								Operation: &security_beta.Operation{
  4293  									Ports:   []string{"8080"},
  4294  									Methods: []string{"GET", "DELETE"},
  4295  								},
  4296  							},
  4297  						},
  4298  					},
  4299  				},
  4300  			},
  4301  			valid:   true,
  4302  			Warning: false,
  4303  		},
  4304  		{
  4305  			name: "target-refs-good",
  4306  			in: &security_beta.AuthorizationPolicy{
  4307  				Action: security_beta.AuthorizationPolicy_DENY,
  4308  				TargetRefs: []*api.PolicyTargetReference{{
  4309  					Group: gvk.KubernetesGateway.Group,
  4310  					Kind:  gvk.KubernetesGateway.Kind,
  4311  					Name:  "foo",
  4312  				}},
  4313  				Rules: []*security_beta.Rule{
  4314  					{
  4315  						From: []*security_beta.Rule_From{
  4316  							{
  4317  								Source: &security_beta.Source{
  4318  									Principals: []string{"temp"},
  4319  								},
  4320  							},
  4321  						},
  4322  						To: []*security_beta.Rule_To{
  4323  							{
  4324  								Operation: &security_beta.Operation{
  4325  									Ports:   []string{"8080"},
  4326  									Methods: []string{"GET", "DELETE"},
  4327  								},
  4328  							},
  4329  						},
  4330  					},
  4331  				},
  4332  			},
  4333  			valid:   true,
  4334  			Warning: false,
  4335  		},
  4336  		{
  4337  			name: "target-refs-good-service",
  4338  			in: &security_beta.AuthorizationPolicy{
  4339  				Action: security_beta.AuthorizationPolicy_DENY,
  4340  				TargetRefs: []*api.PolicyTargetReference{{
  4341  					Group: gvk.Service.Group,
  4342  					Kind:  gvk.Service.Kind,
  4343  					Name:  "foo",
  4344  				}},
  4345  				Rules: []*security_beta.Rule{
  4346  					{
  4347  						From: []*security_beta.Rule_From{
  4348  							{
  4349  								Source: &security_beta.Source{
  4350  									Principals: []string{"temp"},
  4351  								},
  4352  							},
  4353  						},
  4354  						To: []*security_beta.Rule_To{
  4355  							{
  4356  								Operation: &security_beta.Operation{
  4357  									Ports:   []string{"8080"},
  4358  									Methods: []string{"GET", "DELETE"},
  4359  								},
  4360  							},
  4361  						},
  4362  					},
  4363  				},
  4364  			},
  4365  			valid:   true,
  4366  			Warning: false,
  4367  		},
  4368  		{
  4369  			name: "target-ref-non-empty-namespace",
  4370  			in: &security_beta.AuthorizationPolicy{
  4371  				Action: security_beta.AuthorizationPolicy_DENY,
  4372  				TargetRef: &api.PolicyTargetReference{
  4373  					Group:     gvk.KubernetesGateway.Group,
  4374  					Kind:      gvk.KubernetesGateway.Kind,
  4375  					Name:      "foo",
  4376  					Namespace: "bar",
  4377  				},
  4378  				Rules: []*security_beta.Rule{
  4379  					{
  4380  						From: []*security_beta.Rule_From{
  4381  							{
  4382  								Source: &security_beta.Source{
  4383  									Principals: []string{"temp"},
  4384  								},
  4385  							},
  4386  						},
  4387  						To: []*security_beta.Rule_To{
  4388  							{
  4389  								Operation: &security_beta.Operation{
  4390  									Ports:   []string{"8080"},
  4391  									Methods: []string{"GET", "DELETE"},
  4392  								},
  4393  							},
  4394  						},
  4395  					},
  4396  				},
  4397  			},
  4398  			valid:   false,
  4399  			Warning: false,
  4400  		},
  4401  		{
  4402  			name: "target-ref-empty-name",
  4403  			in: &security_beta.AuthorizationPolicy{
  4404  				Action: security_beta.AuthorizationPolicy_DENY,
  4405  				TargetRef: &api.PolicyTargetReference{
  4406  					Group: gvk.KubernetesGateway.Group,
  4407  					Kind:  gvk.KubernetesGateway.Kind,
  4408  				},
  4409  				Rules: []*security_beta.Rule{
  4410  					{
  4411  						From: []*security_beta.Rule_From{
  4412  							{
  4413  								Source: &security_beta.Source{
  4414  									Principals: []string{"temp"},
  4415  								},
  4416  							},
  4417  						},
  4418  						To: []*security_beta.Rule_To{
  4419  							{
  4420  								Operation: &security_beta.Operation{
  4421  									Ports:   []string{"8080"},
  4422  									Methods: []string{"GET", "DELETE"},
  4423  								},
  4424  							},
  4425  						},
  4426  					},
  4427  				},
  4428  			},
  4429  			valid:   false,
  4430  			Warning: false,
  4431  		},
  4432  		{
  4433  			name: "target-ref-wrong-group",
  4434  			in: &security_beta.AuthorizationPolicy{
  4435  				Action: security_beta.AuthorizationPolicy_DENY,
  4436  				TargetRef: &api.PolicyTargetReference{
  4437  					Group: "wrong-group",
  4438  					Kind:  gvk.KubernetesGateway.Kind,
  4439  					Name:  "foo",
  4440  				},
  4441  				Rules: []*security_beta.Rule{
  4442  					{
  4443  						From: []*security_beta.Rule_From{
  4444  							{
  4445  								Source: &security_beta.Source{
  4446  									Principals: []string{"temp"},
  4447  								},
  4448  							},
  4449  						},
  4450  						To: []*security_beta.Rule_To{
  4451  							{
  4452  								Operation: &security_beta.Operation{
  4453  									Ports:   []string{"8080"},
  4454  									Methods: []string{"GET", "DELETE"},
  4455  								},
  4456  							},
  4457  						},
  4458  					},
  4459  				},
  4460  			},
  4461  			valid:   false,
  4462  			Warning: false,
  4463  		},
  4464  		{
  4465  			name: "target-ref-wrong-kind",
  4466  			in: &security_beta.AuthorizationPolicy{
  4467  				Action: security_beta.AuthorizationPolicy_DENY,
  4468  				TargetRef: &api.PolicyTargetReference{
  4469  					Group: gvk.KubernetesGateway.Group,
  4470  					Kind:  "wrong-kind",
  4471  					Name:  "foo",
  4472  				},
  4473  				Rules: []*security_beta.Rule{
  4474  					{
  4475  						From: []*security_beta.Rule_From{
  4476  							{
  4477  								Source: &security_beta.Source{
  4478  									Principals: []string{"temp"},
  4479  								},
  4480  							},
  4481  						},
  4482  						To: []*security_beta.Rule_To{
  4483  							{
  4484  								Operation: &security_beta.Operation{
  4485  									Ports:   []string{"8080"},
  4486  									Methods: []string{"GET", "DELETE"},
  4487  								},
  4488  							},
  4489  						},
  4490  					},
  4491  				},
  4492  			},
  4493  			valid:   false,
  4494  			Warning: false,
  4495  		},
  4496  		{
  4497  			name: "target-ref-and-selector-cannot-both-be-set",
  4498  			in: &security_beta.AuthorizationPolicy{
  4499  				Action: security_beta.AuthorizationPolicy_DENY,
  4500  				TargetRef: &api.PolicyTargetReference{
  4501  					Group: gvk.KubernetesGateway.Group,
  4502  					Kind:  gvk.KubernetesGateway.Kind,
  4503  					Name:  "foo",
  4504  				},
  4505  				Selector: &api.WorkloadSelector{
  4506  					MatchLabels: map[string]string{
  4507  						"app": "httpbin",
  4508  					},
  4509  				},
  4510  				Rules: []*security_beta.Rule{
  4511  					{
  4512  						From: []*security_beta.Rule_From{
  4513  							{
  4514  								Source: &security_beta.Source{
  4515  									Principals: []string{"temp"},
  4516  								},
  4517  							},
  4518  						},
  4519  						To: []*security_beta.Rule_To{
  4520  							{
  4521  								Operation: &security_beta.Operation{
  4522  									Ports:   []string{"8080"},
  4523  									Methods: []string{"GET", "DELETE"},
  4524  								},
  4525  							},
  4526  						},
  4527  					},
  4528  				},
  4529  			},
  4530  			valid:   false,
  4531  			Warning: false,
  4532  		},
  4533  		{
  4534  			name: "target-refs-and-selector-cannot-both-be-set",
  4535  			in: &security_beta.AuthorizationPolicy{
  4536  				Action: security_beta.AuthorizationPolicy_DENY,
  4537  				TargetRefs: []*api.PolicyTargetReference{{
  4538  					Group: gvk.KubernetesGateway.Group,
  4539  					Kind:  gvk.KubernetesGateway.Kind,
  4540  					Name:  "foo",
  4541  				}},
  4542  				Selector: &api.WorkloadSelector{
  4543  					MatchLabels: map[string]string{
  4544  						"app": "httpbin",
  4545  					},
  4546  				},
  4547  				Rules: []*security_beta.Rule{
  4548  					{
  4549  						From: []*security_beta.Rule_From{
  4550  							{
  4551  								Source: &security_beta.Source{
  4552  									Principals: []string{"temp"},
  4553  								},
  4554  							},
  4555  						},
  4556  						To: []*security_beta.Rule_To{
  4557  							{
  4558  								Operation: &security_beta.Operation{
  4559  									Ports:   []string{"8080"},
  4560  									Methods: []string{"GET", "DELETE"},
  4561  								},
  4562  							},
  4563  						},
  4564  					},
  4565  				},
  4566  			},
  4567  			valid:   false,
  4568  			Warning: false,
  4569  		},
  4570  		{
  4571  			name: "from-empty",
  4572  			in: &security_beta.AuthorizationPolicy{
  4573  				Rules: []*security_beta.Rule{
  4574  					{
  4575  						From: []*security_beta.Rule_From{},
  4576  					},
  4577  				},
  4578  			},
  4579  			valid: false,
  4580  		},
  4581  		{
  4582  			name: "source-nil",
  4583  			in: &security_beta.AuthorizationPolicy{
  4584  				Rules: []*security_beta.Rule{
  4585  					{
  4586  						From: []*security_beta.Rule_From{
  4587  							{},
  4588  						},
  4589  					},
  4590  				},
  4591  			},
  4592  			valid: false,
  4593  		},
  4594  		{
  4595  			name: "source-empty",
  4596  			in: &security_beta.AuthorizationPolicy{
  4597  				Rules: []*security_beta.Rule{
  4598  					{
  4599  						From: []*security_beta.Rule_From{
  4600  							{
  4601  								Source: &security_beta.Source{},
  4602  							},
  4603  						},
  4604  					},
  4605  				},
  4606  			},
  4607  			valid: false,
  4608  		},
  4609  		{
  4610  			name: "to-empty",
  4611  			in: &security_beta.AuthorizationPolicy{
  4612  				Rules: []*security_beta.Rule{
  4613  					{
  4614  						To: []*security_beta.Rule_To{},
  4615  					},
  4616  				},
  4617  			},
  4618  			valid: false,
  4619  		},
  4620  		{
  4621  			name: "operation-nil",
  4622  			in: &security_beta.AuthorizationPolicy{
  4623  				Rules: []*security_beta.Rule{
  4624  					{
  4625  						To: []*security_beta.Rule_To{
  4626  							{},
  4627  						},
  4628  					},
  4629  				},
  4630  			},
  4631  			valid: false,
  4632  		},
  4633  		{
  4634  			name: "operation-empty",
  4635  			in: &security_beta.AuthorizationPolicy{
  4636  				Rules: []*security_beta.Rule{
  4637  					{
  4638  						To: []*security_beta.Rule_To{
  4639  							{
  4640  								Operation: &security_beta.Operation{},
  4641  							},
  4642  						},
  4643  					},
  4644  				},
  4645  			},
  4646  			valid: false,
  4647  		},
  4648  		{
  4649  			name: "Principals-empty",
  4650  			in: &security_beta.AuthorizationPolicy{
  4651  				Rules: []*security_beta.Rule{
  4652  					{
  4653  						From: []*security_beta.Rule_From{
  4654  							{
  4655  								Source: &security_beta.Source{
  4656  									Principals: []string{"p1", ""},
  4657  								},
  4658  							},
  4659  						},
  4660  					},
  4661  				},
  4662  			},
  4663  			valid: false,
  4664  		},
  4665  		{
  4666  			name: "NotPrincipals-empty",
  4667  			in: &security_beta.AuthorizationPolicy{
  4668  				Rules: []*security_beta.Rule{
  4669  					{
  4670  						From: []*security_beta.Rule_From{
  4671  							{
  4672  								Source: &security_beta.Source{
  4673  									NotPrincipals: []string{"p1", ""},
  4674  								},
  4675  							},
  4676  						},
  4677  					},
  4678  				},
  4679  			},
  4680  			valid: false,
  4681  		},
  4682  		{
  4683  			name: "RequestPrincipals-empty",
  4684  			in: &security_beta.AuthorizationPolicy{
  4685  				Rules: []*security_beta.Rule{
  4686  					{
  4687  						From: []*security_beta.Rule_From{
  4688  							{
  4689  								Source: &security_beta.Source{
  4690  									RequestPrincipals: []string{"p1", ""},
  4691  								},
  4692  							},
  4693  						},
  4694  					},
  4695  				},
  4696  			},
  4697  			valid: false,
  4698  		},
  4699  		{
  4700  			name: "NotRequestPrincipals-empty",
  4701  			in: &security_beta.AuthorizationPolicy{
  4702  				Rules: []*security_beta.Rule{
  4703  					{
  4704  						From: []*security_beta.Rule_From{
  4705  							{
  4706  								Source: &security_beta.Source{
  4707  									NotRequestPrincipals: []string{"p1", ""},
  4708  								},
  4709  							},
  4710  						},
  4711  					},
  4712  				},
  4713  			},
  4714  			valid: false,
  4715  		},
  4716  		{
  4717  			name: "Namespaces-empty",
  4718  			in: &security_beta.AuthorizationPolicy{
  4719  				Rules: []*security_beta.Rule{
  4720  					{
  4721  						From: []*security_beta.Rule_From{
  4722  							{
  4723  								Source: &security_beta.Source{
  4724  									Namespaces: []string{"ns", ""},
  4725  								},
  4726  							},
  4727  						},
  4728  					},
  4729  				},
  4730  			},
  4731  			valid: false,
  4732  		},
  4733  		{
  4734  			name: "NotNamespaces-empty",
  4735  			in: &security_beta.AuthorizationPolicy{
  4736  				Rules: []*security_beta.Rule{
  4737  					{
  4738  						From: []*security_beta.Rule_From{
  4739  							{
  4740  								Source: &security_beta.Source{
  4741  									NotNamespaces: []string{"ns", ""},
  4742  								},
  4743  							},
  4744  						},
  4745  					},
  4746  				},
  4747  			},
  4748  			valid: false,
  4749  		},
  4750  		{
  4751  			name: "IpBlocks-empty",
  4752  			in: &security_beta.AuthorizationPolicy{
  4753  				Rules: []*security_beta.Rule{
  4754  					{
  4755  						From: []*security_beta.Rule_From{
  4756  							{
  4757  								Source: &security_beta.Source{
  4758  									IpBlocks: []string{"1.2.3.4", ""},
  4759  								},
  4760  							},
  4761  						},
  4762  					},
  4763  				},
  4764  			},
  4765  			valid: false,
  4766  		},
  4767  		{
  4768  			name: "NotIpBlocks-empty",
  4769  			in: &security_beta.AuthorizationPolicy{
  4770  				Rules: []*security_beta.Rule{
  4771  					{
  4772  						From: []*security_beta.Rule_From{
  4773  							{
  4774  								Source: &security_beta.Source{
  4775  									NotIpBlocks: []string{"1.2.3.4", ""},
  4776  								},
  4777  							},
  4778  						},
  4779  					},
  4780  				},
  4781  			},
  4782  			valid: false,
  4783  		},
  4784  		{
  4785  			name: "RemoteIpBlocks-empty",
  4786  			in: &security_beta.AuthorizationPolicy{
  4787  				Rules: []*security_beta.Rule{
  4788  					{
  4789  						From: []*security_beta.Rule_From{
  4790  							{
  4791  								Source: &security_beta.Source{
  4792  									RemoteIpBlocks: []string{"1.2.3.4", ""},
  4793  								},
  4794  							},
  4795  						},
  4796  					},
  4797  				},
  4798  			},
  4799  			valid: false,
  4800  		},
  4801  		{
  4802  			name: "NotRemoteIpBlocks-empty",
  4803  			in: &security_beta.AuthorizationPolicy{
  4804  				Rules: []*security_beta.Rule{
  4805  					{
  4806  						From: []*security_beta.Rule_From{
  4807  							{
  4808  								Source: &security_beta.Source{
  4809  									NotRemoteIpBlocks: []string{"1.2.3.4", ""},
  4810  								},
  4811  							},
  4812  						},
  4813  					},
  4814  				},
  4815  			},
  4816  			valid: false,
  4817  		},
  4818  		{
  4819  			name: "Hosts-empty",
  4820  			in: &security_beta.AuthorizationPolicy{
  4821  				Rules: []*security_beta.Rule{
  4822  					{
  4823  						To: []*security_beta.Rule_To{
  4824  							{
  4825  								Operation: &security_beta.Operation{
  4826  									Hosts: []string{"host", ""},
  4827  								},
  4828  							},
  4829  						},
  4830  					},
  4831  				},
  4832  			},
  4833  			valid: false,
  4834  		},
  4835  		{
  4836  			name: "NotHosts-empty",
  4837  			in: &security_beta.AuthorizationPolicy{
  4838  				Rules: []*security_beta.Rule{
  4839  					{
  4840  						To: []*security_beta.Rule_To{
  4841  							{
  4842  								Operation: &security_beta.Operation{
  4843  									NotHosts: []string{"host", ""},
  4844  								},
  4845  							},
  4846  						},
  4847  					},
  4848  				},
  4849  			},
  4850  			valid: false,
  4851  		},
  4852  		{
  4853  			name: "Ports-empty",
  4854  			in: &security_beta.AuthorizationPolicy{
  4855  				Rules: []*security_beta.Rule{
  4856  					{
  4857  						To: []*security_beta.Rule_To{
  4858  							{
  4859  								Operation: &security_beta.Operation{
  4860  									Ports: []string{"80", ""},
  4861  								},
  4862  							},
  4863  						},
  4864  					},
  4865  				},
  4866  			},
  4867  			valid: false,
  4868  		},
  4869  		{
  4870  			name: "NotPorts-empty",
  4871  			in: &security_beta.AuthorizationPolicy{
  4872  				Rules: []*security_beta.Rule{
  4873  					{
  4874  						To: []*security_beta.Rule_To{
  4875  							{
  4876  								Operation: &security_beta.Operation{
  4877  									NotPorts: []string{"80", ""},
  4878  								},
  4879  							},
  4880  						},
  4881  					},
  4882  				},
  4883  			},
  4884  			valid: false,
  4885  		},
  4886  		{
  4887  			name: "Methods-empty",
  4888  			in: &security_beta.AuthorizationPolicy{
  4889  				Rules: []*security_beta.Rule{
  4890  					{
  4891  						To: []*security_beta.Rule_To{
  4892  							{
  4893  								Operation: &security_beta.Operation{
  4894  									Methods: []string{"GET", ""},
  4895  								},
  4896  							},
  4897  						},
  4898  					},
  4899  				},
  4900  			},
  4901  			valid: false,
  4902  		},
  4903  		{
  4904  			name: "NotMethods-empty",
  4905  			in: &security_beta.AuthorizationPolicy{
  4906  				Rules: []*security_beta.Rule{
  4907  					{
  4908  						To: []*security_beta.Rule_To{
  4909  							{
  4910  								Operation: &security_beta.Operation{
  4911  									NotMethods: []string{"GET", ""},
  4912  								},
  4913  							},
  4914  						},
  4915  					},
  4916  				},
  4917  			},
  4918  			valid: false,
  4919  		},
  4920  		{
  4921  			name: "Paths-empty",
  4922  			in: &security_beta.AuthorizationPolicy{
  4923  				Rules: []*security_beta.Rule{
  4924  					{
  4925  						To: []*security_beta.Rule_To{
  4926  							{
  4927  								Operation: &security_beta.Operation{
  4928  									Paths: []string{"/path", ""},
  4929  								},
  4930  							},
  4931  						},
  4932  					},
  4933  				},
  4934  			},
  4935  			valid: false,
  4936  		},
  4937  		{
  4938  			name: "NotPaths-empty",
  4939  			in: &security_beta.AuthorizationPolicy{
  4940  				Rules: []*security_beta.Rule{
  4941  					{
  4942  						To: []*security_beta.Rule_To{
  4943  							{
  4944  								Operation: &security_beta.Operation{
  4945  									NotPaths: []string{"/path", ""},
  4946  								},
  4947  							},
  4948  						},
  4949  					},
  4950  				},
  4951  			},
  4952  			valid: false,
  4953  		},
  4954  		{
  4955  			name: "value-empty",
  4956  			in: &security_beta.AuthorizationPolicy{
  4957  				Rules: []*security_beta.Rule{
  4958  					{
  4959  						When: []*security_beta.Condition{
  4960  							{
  4961  								Key:    "request.headers[:authority]",
  4962  								Values: []string{"v1", ""},
  4963  							},
  4964  						},
  4965  					},
  4966  				},
  4967  			},
  4968  			valid: false,
  4969  		},
  4970  		{
  4971  			name: "invalid ip and port in ipBlocks",
  4972  			in: &security_beta.AuthorizationPolicy{
  4973  				Rules: []*security_beta.Rule{
  4974  					{
  4975  						From: []*security_beta.Rule_From{
  4976  							{
  4977  								Source: &security_beta.Source{
  4978  									IpBlocks:    []string{"1.2.3.4", "ip1"},
  4979  									NotIpBlocks: []string{"5.6.7.8", "ip2"},
  4980  								},
  4981  							},
  4982  						},
  4983  						To: []*security_beta.Rule_To{
  4984  							{
  4985  								Operation: &security_beta.Operation{
  4986  									Ports:    []string{"80", "port1"},
  4987  									NotPorts: []string{"90", "port2"},
  4988  								},
  4989  							},
  4990  						},
  4991  					},
  4992  				},
  4993  			},
  4994  			valid: false,
  4995  		},
  4996  		{
  4997  			name: "invalid ip and port in remoteIpBlocks",
  4998  			in: &security_beta.AuthorizationPolicy{
  4999  				Rules: []*security_beta.Rule{
  5000  					{
  5001  						From: []*security_beta.Rule_From{
  5002  							{
  5003  								Source: &security_beta.Source{
  5004  									RemoteIpBlocks:    []string{"1.2.3.4", "ip1"},
  5005  									NotRemoteIpBlocks: []string{"5.6.7.8", "ip2"},
  5006  								},
  5007  							},
  5008  						},
  5009  						To: []*security_beta.Rule_To{
  5010  							{
  5011  								Operation: &security_beta.Operation{
  5012  									Ports:    []string{"80", "port1"},
  5013  									NotPorts: []string{"90", "port2"},
  5014  								},
  5015  							},
  5016  						},
  5017  					},
  5018  				},
  5019  			},
  5020  			valid: false,
  5021  		},
  5022  		{
  5023  			name: "condition-key-missing",
  5024  			in: &security_beta.AuthorizationPolicy{
  5025  				Selector: &api.WorkloadSelector{
  5026  					MatchLabels: map[string]string{
  5027  						"app": "httpbin",
  5028  					},
  5029  				},
  5030  				Rules: []*security_beta.Rule{
  5031  					{
  5032  						When: []*security_beta.Condition{
  5033  							{
  5034  								Values: []string{"v1", "v2"},
  5035  							},
  5036  						},
  5037  					},
  5038  				},
  5039  			},
  5040  			valid: false,
  5041  		},
  5042  		{
  5043  			name: "condition-key-empty",
  5044  			in: &security_beta.AuthorizationPolicy{
  5045  				Selector: &api.WorkloadSelector{
  5046  					MatchLabels: map[string]string{
  5047  						"app": "httpbin",
  5048  					},
  5049  				},
  5050  				Rules: []*security_beta.Rule{
  5051  					{
  5052  						When: []*security_beta.Condition{
  5053  							{
  5054  								Key:    "",
  5055  								Values: []string{"v1", "v2"},
  5056  							},
  5057  						},
  5058  					},
  5059  				},
  5060  			},
  5061  			valid: false,
  5062  		},
  5063  		{
  5064  			name: "condition-value-missing",
  5065  			in: &security_beta.AuthorizationPolicy{
  5066  				Selector: &api.WorkloadSelector{
  5067  					MatchLabels: map[string]string{
  5068  						"app": "httpbin",
  5069  					},
  5070  				},
  5071  				Rules: []*security_beta.Rule{
  5072  					{
  5073  						When: []*security_beta.Condition{
  5074  							{
  5075  								Key: "source.principal",
  5076  							},
  5077  						},
  5078  					},
  5079  				},
  5080  			},
  5081  			valid: false,
  5082  		},
  5083  		{
  5084  			name: "condition-value-invalid",
  5085  			in: &security_beta.AuthorizationPolicy{
  5086  				Rules: []*security_beta.Rule{
  5087  					{
  5088  						When: []*security_beta.Condition{
  5089  							{
  5090  								Key:    "source.ip",
  5091  								Values: []string{"a.b.c.d"},
  5092  							},
  5093  						},
  5094  					},
  5095  				},
  5096  			},
  5097  			valid: false,
  5098  		},
  5099  		{
  5100  			name: "condition-notValue-invalid",
  5101  			in: &security_beta.AuthorizationPolicy{
  5102  				Rules: []*security_beta.Rule{
  5103  					{
  5104  						When: []*security_beta.Condition{
  5105  							{
  5106  								Key:       "source.ip",
  5107  								NotValues: []string{"a.b.c.d"},
  5108  							},
  5109  						},
  5110  					},
  5111  				},
  5112  			},
  5113  			valid: false,
  5114  		},
  5115  		{
  5116  			name: "condition-unknown",
  5117  			in: &security_beta.AuthorizationPolicy{
  5118  				Selector: &api.WorkloadSelector{
  5119  					MatchLabels: map[string]string{
  5120  						"app": "httpbin",
  5121  					},
  5122  				},
  5123  				Rules: []*security_beta.Rule{
  5124  					{
  5125  						When: []*security_beta.Condition{
  5126  							{
  5127  								Key:    "key1",
  5128  								Values: []string{"v1"},
  5129  							},
  5130  						},
  5131  					},
  5132  				},
  5133  			},
  5134  			valid: false,
  5135  		},
  5136  		{
  5137  			name: "L7DenyWithFrom",
  5138  			in: &security_beta.AuthorizationPolicy{
  5139  				Action: security_beta.AuthorizationPolicy_DENY,
  5140  				Selector: &api.WorkloadSelector{
  5141  					MatchLabels: map[string]string{
  5142  						"app": "httpbin",
  5143  					},
  5144  				},
  5145  				Rules: []*security_beta.Rule{
  5146  					{
  5147  						From: []*security_beta.Rule_From{
  5148  							{
  5149  								Source: &security_beta.Source{
  5150  									RequestPrincipals: []string{"example.com/sub-1"},
  5151  								},
  5152  							},
  5153  						},
  5154  					},
  5155  				},
  5156  			},
  5157  			valid:   true,
  5158  			Warning: true,
  5159  		},
  5160  		{
  5161  			name: "L7DenyWithTo",
  5162  			in: &security_beta.AuthorizationPolicy{
  5163  				Action: security_beta.AuthorizationPolicy_DENY,
  5164  				Selector: &api.WorkloadSelector{
  5165  					MatchLabels: map[string]string{
  5166  						"app": "httpbin",
  5167  					},
  5168  				},
  5169  				Rules: []*security_beta.Rule{
  5170  					{
  5171  						To: []*security_beta.Rule_To{
  5172  							{
  5173  								Operation: &security_beta.Operation{
  5174  									Methods: []string{"GET", "DELETE"},
  5175  								},
  5176  							},
  5177  						},
  5178  					},
  5179  				},
  5180  			},
  5181  			valid:   true,
  5182  			Warning: true,
  5183  		},
  5184  		{
  5185  			name: "L7DenyWithFromAndTo",
  5186  			in: &security_beta.AuthorizationPolicy{
  5187  				Action: security_beta.AuthorizationPolicy_DENY,
  5188  				Selector: &api.WorkloadSelector{
  5189  					MatchLabels: map[string]string{
  5190  						"app": "httpbin",
  5191  					},
  5192  				},
  5193  				Rules: []*security_beta.Rule{
  5194  					{
  5195  						From: []*security_beta.Rule_From{
  5196  							{
  5197  								Source: &security_beta.Source{
  5198  									Principals: []string{"temp"},
  5199  								},
  5200  							},
  5201  						},
  5202  						To: []*security_beta.Rule_To{
  5203  							{
  5204  								Operation: &security_beta.Operation{
  5205  									Methods: []string{"GET", "DELETE"},
  5206  								},
  5207  							},
  5208  						},
  5209  					},
  5210  				},
  5211  			},
  5212  			valid:   true,
  5213  			Warning: true,
  5214  		},
  5215  		{
  5216  			name: "L7DenyWithFromAndToWithPort",
  5217  			in: &security_beta.AuthorizationPolicy{
  5218  				Action: security_beta.AuthorizationPolicy_DENY,
  5219  				Selector: &api.WorkloadSelector{
  5220  					MatchLabels: map[string]string{
  5221  						"app": "httpbin",
  5222  					},
  5223  				},
  5224  				Rules: []*security_beta.Rule{
  5225  					{
  5226  						From: []*security_beta.Rule_From{
  5227  							{
  5228  								Source: &security_beta.Source{
  5229  									Principals: []string{"temp"},
  5230  								},
  5231  							},
  5232  						},
  5233  						To: []*security_beta.Rule_To{
  5234  							{
  5235  								Operation: &security_beta.Operation{
  5236  									Ports:   []string{"8080"},
  5237  									Methods: []string{"GET", "DELETE"},
  5238  								},
  5239  							},
  5240  						},
  5241  					},
  5242  				},
  5243  			},
  5244  			valid:   true,
  5245  			Warning: false,
  5246  		},
  5247  	}
  5248  
  5249  	for _, c := range cases {
  5250  		war, got := ValidateAuthorizationPolicy(config.Config{
  5251  			Meta: config.Meta{
  5252  				Name:        "name",
  5253  				Namespace:   "namespace",
  5254  				Annotations: c.annotations,
  5255  			},
  5256  			Spec: c.in,
  5257  		})
  5258  		if (got == nil) != c.valid {
  5259  			t.Errorf("test: %q error: got: %v\nwant: %v", c.name, got, c.valid)
  5260  		}
  5261  		if (war != nil) != c.Warning {
  5262  			t.Errorf("test: %q warning: got: %v\nwant: %v", c.name, war, c.valid)
  5263  		}
  5264  	}
  5265  }
  5266  
  5267  func TestValidateSidecar(t *testing.T) {
  5268  	tests := []struct {
  5269  		name  string
  5270  		in    *networking.Sidecar
  5271  		valid bool
  5272  		warn  bool
  5273  	}{
  5274  		{"empty ingress and egress", &networking.Sidecar{}, false, false},
  5275  		{"default", &networking.Sidecar{
  5276  			Egress: []*networking.IstioEgressListener{
  5277  				{
  5278  					Hosts: []string{"*/*"},
  5279  				},
  5280  			},
  5281  		}, true, false},
  5282  		{"workload selector without labels", &networking.Sidecar{
  5283  			Egress: []*networking.IstioEgressListener{
  5284  				{
  5285  					Hosts: []string{"*/*"},
  5286  				},
  5287  			},
  5288  			WorkloadSelector: &networking.WorkloadSelector{},
  5289  		}, true, true},
  5290  		{"import local namespace with wildcard", &networking.Sidecar{
  5291  			Egress: []*networking.IstioEgressListener{
  5292  				{
  5293  					Hosts: []string{"./*"},
  5294  				},
  5295  			},
  5296  		}, true, false},
  5297  		{"import local namespace with fqdn", &networking.Sidecar{
  5298  			Egress: []*networking.IstioEgressListener{
  5299  				{
  5300  					Hosts: []string{"./foo.com"},
  5301  				},
  5302  			},
  5303  		}, true, false},
  5304  		{"import nothing", &networking.Sidecar{
  5305  			Egress: []*networking.IstioEgressListener{
  5306  				{
  5307  					Hosts: []string{"~/*"},
  5308  				},
  5309  			},
  5310  		}, true, false},
  5311  		{"bad egress host 1", &networking.Sidecar{
  5312  			Egress: []*networking.IstioEgressListener{
  5313  				{
  5314  					Hosts: []string{"*"},
  5315  				},
  5316  			},
  5317  		}, false, false},
  5318  		{"bad egress host 2", &networking.Sidecar{
  5319  			Egress: []*networking.IstioEgressListener{
  5320  				{
  5321  					Hosts: []string{"/"},
  5322  				},
  5323  			},
  5324  		}, false, false},
  5325  		{"empty egress host", &networking.Sidecar{
  5326  			Egress: []*networking.IstioEgressListener{
  5327  				{
  5328  					Hosts: []string{},
  5329  				},
  5330  			},
  5331  		}, false, false},
  5332  		{"multiple wildcard egress", &networking.Sidecar{
  5333  			Egress: []*networking.IstioEgressListener{
  5334  				{
  5335  					Hosts: []string{
  5336  						"*/foo.com",
  5337  					},
  5338  				},
  5339  				{
  5340  					Hosts: []string{
  5341  						"ns1/bar.com",
  5342  					},
  5343  				},
  5344  			},
  5345  		}, false, false},
  5346  		{"wildcard egress not in end", &networking.Sidecar{
  5347  			Egress: []*networking.IstioEgressListener{
  5348  				{
  5349  					Hosts: []string{
  5350  						"*/foo.com",
  5351  					},
  5352  				},
  5353  				{
  5354  					Port: &networking.SidecarPort{
  5355  						Protocol: "http",
  5356  						Number:   8080,
  5357  						Name:     "h8080",
  5358  					},
  5359  					Hosts: []string{
  5360  						"ns1/bar.com",
  5361  					},
  5362  				},
  5363  			},
  5364  		}, false, false},
  5365  		{"invalid Port", &networking.Sidecar{
  5366  			Egress: []*networking.IstioEgressListener{
  5367  				{
  5368  					Port: &networking.SidecarPort{
  5369  						Protocol: "http1",
  5370  						Number:   1000000,
  5371  						Name:     "",
  5372  					},
  5373  					Hosts: []string{
  5374  						"ns1/bar.com",
  5375  					},
  5376  				},
  5377  			},
  5378  		}, false, false},
  5379  		{"Port without name", &networking.Sidecar{
  5380  			Egress: []*networking.IstioEgressListener{
  5381  				{
  5382  					Port: &networking.SidecarPort{
  5383  						Protocol: "http",
  5384  						Number:   8080,
  5385  					},
  5386  					Hosts: []string{
  5387  						"ns1/bar.com",
  5388  					},
  5389  				},
  5390  			},
  5391  		}, true, false},
  5392  		{"UDS bind in outbound", &networking.Sidecar{
  5393  			Egress: []*networking.IstioEgressListener{
  5394  				{
  5395  					Port: &networking.SidecarPort{
  5396  						Protocol: "http",
  5397  						Number:   0,
  5398  						Name:     "uds",
  5399  					},
  5400  					Hosts: []string{
  5401  						"ns1/bar.com",
  5402  					},
  5403  					Bind: "unix:///@foo/bar/com",
  5404  				},
  5405  			},
  5406  		}, true, false},
  5407  		{"UDS bind in inbound", &networking.Sidecar{
  5408  			Ingress: []*networking.IstioIngressListener{
  5409  				{
  5410  					Port: &networking.SidecarPort{
  5411  						Protocol: "http",
  5412  						Number:   0,
  5413  						Name:     "uds",
  5414  					},
  5415  					Bind:            "unix:///@foo/bar/com",
  5416  					DefaultEndpoint: "127.0.0.1:9999",
  5417  				},
  5418  			},
  5419  		}, false, false},
  5420  		{"UDS bind in outbound 2", &networking.Sidecar{
  5421  			Egress: []*networking.IstioEgressListener{
  5422  				{
  5423  					Port: &networking.SidecarPort{
  5424  						Protocol: "http",
  5425  						Number:   0,
  5426  						Name:     "uds",
  5427  					},
  5428  					Hosts: []string{
  5429  						"ns1/bar.com",
  5430  					},
  5431  					Bind: "unix:///foo/bar/com",
  5432  				},
  5433  			},
  5434  		}, true, false},
  5435  		{"invalid bind", &networking.Sidecar{
  5436  			Egress: []*networking.IstioEgressListener{
  5437  				{
  5438  					Port: &networking.SidecarPort{
  5439  						Protocol: "http",
  5440  						Number:   0,
  5441  						Name:     "uds",
  5442  					},
  5443  					Hosts: []string{
  5444  						"ns1/bar.com",
  5445  					},
  5446  					Bind: "foobar:///@foo/bar/com",
  5447  				},
  5448  			},
  5449  		}, false, false},
  5450  		{"invalid capture mode with uds bind", &networking.Sidecar{
  5451  			Egress: []*networking.IstioEgressListener{
  5452  				{
  5453  					Port: &networking.SidecarPort{
  5454  						Protocol: "http",
  5455  						Number:   0,
  5456  						Name:     "uds",
  5457  					},
  5458  					Hosts: []string{
  5459  						"ns1/bar.com",
  5460  					},
  5461  					Bind:        "unix:///@foo/bar/com",
  5462  					CaptureMode: networking.CaptureMode_IPTABLES,
  5463  				},
  5464  			},
  5465  		}, false, false},
  5466  		{"duplicate UDS bind", &networking.Sidecar{
  5467  			Egress: []*networking.IstioEgressListener{
  5468  				{
  5469  					Port: &networking.SidecarPort{
  5470  						Protocol: "http",
  5471  						Number:   0,
  5472  						Name:     "uds",
  5473  					},
  5474  					Hosts: []string{
  5475  						"ns1/bar.com",
  5476  					},
  5477  					Bind: "unix:///@foo/bar/com",
  5478  				},
  5479  				{
  5480  					Port: &networking.SidecarPort{
  5481  						Protocol: "http",
  5482  						Number:   0,
  5483  						Name:     "uds",
  5484  					},
  5485  					Hosts: []string{
  5486  						"ns1/bar.com",
  5487  					},
  5488  					Bind: "unix:///@foo/bar/com",
  5489  				},
  5490  			},
  5491  		}, false, false},
  5492  		{"duplicate ports", &networking.Sidecar{
  5493  			Egress: []*networking.IstioEgressListener{
  5494  				{
  5495  					Port: &networking.SidecarPort{
  5496  						Protocol: "http",
  5497  						Number:   90,
  5498  						Name:     "foo",
  5499  					},
  5500  					Hosts: []string{
  5501  						"ns1/bar.com",
  5502  					},
  5503  				},
  5504  				{
  5505  					Port: &networking.SidecarPort{
  5506  						Protocol: "tcp",
  5507  						Number:   90,
  5508  						Name:     "tcp",
  5509  					},
  5510  					Hosts: []string{
  5511  						"ns2/bar.com",
  5512  					},
  5513  				},
  5514  			},
  5515  		}, false, false},
  5516  		{"ingress without port", &networking.Sidecar{
  5517  			Ingress: []*networking.IstioIngressListener{
  5518  				{
  5519  					DefaultEndpoint: "127.0.0.1:110",
  5520  				},
  5521  			},
  5522  			Egress: []*networking.IstioEgressListener{
  5523  				{
  5524  					Hosts: []string{"*/*"},
  5525  				},
  5526  			},
  5527  		}, false, false},
  5528  		{"ingress without port and with IPv6 endpoint", &networking.Sidecar{
  5529  			Ingress: []*networking.IstioIngressListener{
  5530  				{
  5531  					DefaultEndpoint: "[::1]:110",
  5532  				},
  5533  			},
  5534  			Egress: []*networking.IstioEgressListener{
  5535  				{
  5536  					Hosts: []string{"*/*"},
  5537  				},
  5538  			},
  5539  		}, false, false},
  5540  		{"ingress with duplicate ports", &networking.Sidecar{
  5541  			Ingress: []*networking.IstioIngressListener{
  5542  				{
  5543  					Port: &networking.SidecarPort{
  5544  						Protocol: "http",
  5545  						Number:   90,
  5546  						Name:     "foo",
  5547  					},
  5548  					DefaultEndpoint: "127.0.0.1:110",
  5549  				},
  5550  				{
  5551  					Port: &networking.SidecarPort{
  5552  						Protocol: "tcp",
  5553  						Number:   90,
  5554  						Name:     "bar",
  5555  					},
  5556  					DefaultEndpoint: "127.0.0.1:110",
  5557  				},
  5558  			},
  5559  			Egress: []*networking.IstioEgressListener{
  5560  				{
  5561  					Hosts: []string{"*/*"},
  5562  				},
  5563  			},
  5564  		}, false, false},
  5565  		{"ingress with duplicate ports and IPv6 endpoint", &networking.Sidecar{
  5566  			Ingress: []*networking.IstioIngressListener{
  5567  				{
  5568  					Port: &networking.SidecarPort{
  5569  						Protocol: "http",
  5570  						Number:   90,
  5571  						Name:     "foo",
  5572  					},
  5573  					DefaultEndpoint: "[::1]:110",
  5574  				},
  5575  				{
  5576  					Port: &networking.SidecarPort{
  5577  						Protocol: "tcp",
  5578  						Number:   90,
  5579  						Name:     "bar",
  5580  					},
  5581  					DefaultEndpoint: "[::1]:110",
  5582  				},
  5583  			},
  5584  			Egress: []*networking.IstioEgressListener{
  5585  				{
  5586  					Hosts: []string{"*/*"},
  5587  				},
  5588  			},
  5589  		}, false, false},
  5590  		{"ingress without default endpoint", &networking.Sidecar{
  5591  			Ingress: []*networking.IstioIngressListener{
  5592  				{
  5593  					Port: &networking.SidecarPort{
  5594  						Protocol: "http",
  5595  						Number:   90,
  5596  						Name:     "foo",
  5597  					},
  5598  				},
  5599  			},
  5600  			Egress: []*networking.IstioEgressListener{
  5601  				{
  5602  					Hosts: []string{"*/*"},
  5603  				},
  5604  			},
  5605  		}, true, false},
  5606  		{"ingress with invalid default endpoint in IPv4", &networking.Sidecar{
  5607  			Ingress: []*networking.IstioIngressListener{
  5608  				{
  5609  					Port: &networking.SidecarPort{
  5610  						Protocol: "http",
  5611  						Number:   90,
  5612  						Name:     "foo",
  5613  					},
  5614  					DefaultEndpoint: "1.1.1.1:90",
  5615  				},
  5616  			},
  5617  		}, false, false},
  5618  		{"ingress with invalid default endpoint in IPv6", &networking.Sidecar{
  5619  			Ingress: []*networking.IstioIngressListener{
  5620  				{
  5621  					Port: &networking.SidecarPort{
  5622  						Protocol: "http",
  5623  						Number:   90,
  5624  						Name:     "foo",
  5625  					},
  5626  					DefaultEndpoint: "[1:1:1:1:1:1:1:1]:90",
  5627  				},
  5628  			},
  5629  		}, false, false},
  5630  		{"ingress with invalid default endpoint uds", &networking.Sidecar{
  5631  			Ingress: []*networking.IstioIngressListener{
  5632  				{
  5633  					Port: &networking.SidecarPort{
  5634  						Protocol: "http",
  5635  						Number:   90,
  5636  						Name:     "foo",
  5637  					},
  5638  					DefaultEndpoint: "unix:///",
  5639  				},
  5640  			},
  5641  			Egress: []*networking.IstioEgressListener{
  5642  				{
  5643  					Hosts: []string{"*/*"},
  5644  				},
  5645  			},
  5646  		}, false, false},
  5647  		{"ingress with invalid default endpoint port in IPv4", &networking.Sidecar{
  5648  			Ingress: []*networking.IstioIngressListener{
  5649  				{
  5650  					Port: &networking.SidecarPort{
  5651  						Protocol: "http",
  5652  						Number:   90,
  5653  						Name:     "foo",
  5654  					},
  5655  					DefaultEndpoint: "127.0.0.1:hi",
  5656  				},
  5657  			},
  5658  			Egress: []*networking.IstioEgressListener{
  5659  				{
  5660  					Hosts: []string{"*/*"},
  5661  				},
  5662  			},
  5663  		}, false, false},
  5664  		{"ingress with invalid default endpoint port in IPv6", &networking.Sidecar{
  5665  			Ingress: []*networking.IstioIngressListener{
  5666  				{
  5667  					Port: &networking.SidecarPort{
  5668  						Protocol: "http",
  5669  						Number:   90,
  5670  						Name:     "foo",
  5671  					},
  5672  					DefaultEndpoint: "[::1]:hi",
  5673  				},
  5674  			},
  5675  			Egress: []*networking.IstioEgressListener{
  5676  				{
  5677  					Hosts: []string{"*/*"},
  5678  				},
  5679  			},
  5680  		}, false, false},
  5681  		{"valid ingress and egress in IPv4", &networking.Sidecar{
  5682  			Ingress: []*networking.IstioIngressListener{
  5683  				{
  5684  					Port: &networking.SidecarPort{
  5685  						Protocol: "http",
  5686  						Number:   90,
  5687  						Name:     "foo",
  5688  					},
  5689  					DefaultEndpoint: "127.0.0.1:9999",
  5690  				},
  5691  			},
  5692  			Egress: []*networking.IstioEgressListener{
  5693  				{
  5694  					Hosts: []string{"*/*"},
  5695  				},
  5696  			},
  5697  		}, true, false},
  5698  		{"valid ingress and egress in IPv6", &networking.Sidecar{
  5699  			Ingress: []*networking.IstioIngressListener{
  5700  				{
  5701  					Port: &networking.SidecarPort{
  5702  						Protocol: "http",
  5703  						Number:   90,
  5704  						Name:     "foo",
  5705  					},
  5706  					DefaultEndpoint: "[::1]:9999",
  5707  				},
  5708  			},
  5709  			Egress: []*networking.IstioEgressListener{
  5710  				{
  5711  					Hosts: []string{"*/*"},
  5712  				},
  5713  			},
  5714  		}, true, false},
  5715  		{"valid ingress and empty egress in IPv4", &networking.Sidecar{
  5716  			Ingress: []*networking.IstioIngressListener{
  5717  				{
  5718  					Port: &networking.SidecarPort{
  5719  						Protocol: "http",
  5720  						Number:   90,
  5721  						Name:     "foo",
  5722  					},
  5723  					DefaultEndpoint: "127.0.0.1:9999",
  5724  				},
  5725  			},
  5726  		}, true, false},
  5727  		{"valid ingress and empty egress in IPv6", &networking.Sidecar{
  5728  			Ingress: []*networking.IstioIngressListener{
  5729  				{
  5730  					Port: &networking.SidecarPort{
  5731  						Protocol: "http",
  5732  						Number:   90,
  5733  						Name:     "foo",
  5734  					},
  5735  					DefaultEndpoint: "[::1]:9999",
  5736  				},
  5737  			},
  5738  		}, true, false},
  5739  		{"empty", &networking.Sidecar{}, false, false},
  5740  		{"just outbound traffic policy", &networking.Sidecar{OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{
  5741  			Mode: networking.OutboundTrafficPolicy_ALLOW_ANY,
  5742  		}}, true, false},
  5743  		{"empty protocol in IPv4", &networking.Sidecar{
  5744  			Ingress: []*networking.IstioIngressListener{
  5745  				{
  5746  					Port: &networking.SidecarPort{
  5747  						Number: 90,
  5748  						Name:   "foo",
  5749  					},
  5750  					DefaultEndpoint: "127.0.0.1:9999",
  5751  				},
  5752  			},
  5753  			Egress: []*networking.IstioEgressListener{
  5754  				{
  5755  					Hosts: []string{"*/*"},
  5756  				},
  5757  			},
  5758  		}, true, false},
  5759  		{"empty protocol in IPv6", &networking.Sidecar{
  5760  			Ingress: []*networking.IstioIngressListener{
  5761  				{
  5762  					Port: &networking.SidecarPort{
  5763  						Number: 90,
  5764  						Name:   "foo",
  5765  					},
  5766  					DefaultEndpoint: "[::1]:9999",
  5767  				},
  5768  			},
  5769  			Egress: []*networking.IstioEgressListener{
  5770  				{
  5771  					Hosts: []string{"*/*"},
  5772  				},
  5773  			},
  5774  		}, true, false},
  5775  		{"ALLOW_ANY sidecar egress policy with no egress proxy ", &networking.Sidecar{
  5776  			OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{
  5777  				Mode: networking.OutboundTrafficPolicy_ALLOW_ANY,
  5778  			},
  5779  			Egress: []*networking.IstioEgressListener{
  5780  				{
  5781  					Hosts: []string{"*/*"},
  5782  				},
  5783  			},
  5784  		}, true, false},
  5785  		{"sidecar egress proxy with RESGISTRY_ONLY(default)", &networking.Sidecar{
  5786  			OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{
  5787  				EgressProxy: &networking.Destination{
  5788  					Host:   "foo.bar",
  5789  					Subset: "shiny",
  5790  					Port: &networking.PortSelector{
  5791  						Number: 5000,
  5792  					},
  5793  				},
  5794  			},
  5795  			Egress: []*networking.IstioEgressListener{
  5796  				{
  5797  					Hosts: []string{"*/*"},
  5798  				},
  5799  			},
  5800  		}, false, false},
  5801  		{"sidecar egress proxy with ALLOW_ANY", &networking.Sidecar{
  5802  			OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{
  5803  				Mode: networking.OutboundTrafficPolicy_ALLOW_ANY,
  5804  				EgressProxy: &networking.Destination{
  5805  					Host:   "foo.bar",
  5806  					Subset: "shiny",
  5807  					Port: &networking.PortSelector{
  5808  						Number: 5000,
  5809  					},
  5810  				},
  5811  			},
  5812  			Egress: []*networking.IstioEgressListener{
  5813  				{
  5814  					Hosts: []string{"*/*"},
  5815  				},
  5816  			},
  5817  		}, true, false},
  5818  		{"sidecar egress proxy with ALLOW_ANY, service hostname invalid fqdn", &networking.Sidecar{
  5819  			OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{
  5820  				Mode: networking.OutboundTrafficPolicy_ALLOW_ANY,
  5821  				EgressProxy: &networking.Destination{
  5822  					Host:   "foobar*123",
  5823  					Subset: "shiny",
  5824  					Port: &networking.PortSelector{
  5825  						Number: 5000,
  5826  					},
  5827  				},
  5828  			},
  5829  			Egress: []*networking.IstioEgressListener{
  5830  				{
  5831  					Hosts: []string{"*/*"},
  5832  				},
  5833  			},
  5834  		}, false, false},
  5835  		{"sidecar egress proxy(without Port) with ALLOW_ANY", &networking.Sidecar{
  5836  			OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{
  5837  				Mode: networking.OutboundTrafficPolicy_ALLOW_ANY,
  5838  				EgressProxy: &networking.Destination{
  5839  					Host:   "foo.bar",
  5840  					Subset: "shiny",
  5841  				},
  5842  			},
  5843  			Egress: []*networking.IstioEgressListener{
  5844  				{
  5845  					Hosts: []string{"*/*"},
  5846  				},
  5847  			},
  5848  		}, false, false},
  5849  		{"sidecar egress only one wildcarded", &networking.Sidecar{
  5850  			Egress: []*networking.IstioEgressListener{
  5851  				{
  5852  					Hosts: []string{
  5853  						"*/*",
  5854  						"test/a.com",
  5855  					},
  5856  				},
  5857  			},
  5858  		}, true, true},
  5859  		{"sidecar egress wildcarded ns", &networking.Sidecar{
  5860  			Egress: []*networking.IstioEgressListener{
  5861  				{
  5862  					Hosts: []string{
  5863  						"*/b.com",
  5864  						"test/a.com",
  5865  					},
  5866  				},
  5867  			},
  5868  		}, true, false},
  5869  		{"sidecar egress duplicated with wildcarded same namespace", &networking.Sidecar{
  5870  			Egress: []*networking.IstioEgressListener{
  5871  				{
  5872  					Hosts: []string{
  5873  						"test/*",
  5874  						"test/a.com",
  5875  					},
  5876  				},
  5877  			},
  5878  		}, true, true},
  5879  		{"invalid partial wildcard", &networking.Sidecar{
  5880  			Egress: []*networking.IstioEgressListener{
  5881  				{
  5882  					Hosts: []string{
  5883  						"test/*a.com",
  5884  					},
  5885  				},
  5886  			},
  5887  		}, false, false},
  5888  		{"sidecar egress duplicated with wildcarded same namespace .", &networking.Sidecar{
  5889  			Egress: []*networking.IstioEgressListener{
  5890  				{
  5891  					Hosts: []string{
  5892  						"./*",
  5893  						"bar/a.com",
  5894  					},
  5895  				},
  5896  			},
  5897  		}, true, true},
  5898  		{"ingress tls mode set to ISTIO_MUTUAL in IPv4", &networking.Sidecar{
  5899  			Ingress: []*networking.IstioIngressListener{
  5900  				{
  5901  					Port: &networking.SidecarPort{
  5902  						Protocol: "http",
  5903  						Number:   90,
  5904  						Name:     "foo",
  5905  					},
  5906  					DefaultEndpoint: "127.0.0.1:9999",
  5907  					Tls: &networking.ServerTLSSettings{
  5908  						Mode: networking.ServerTLSSettings_ISTIO_MUTUAL,
  5909  					},
  5910  				},
  5911  			},
  5912  		}, false, false},
  5913  		{"ingress tls mode set to ISTIO_MUTUAL in IPv6", &networking.Sidecar{
  5914  			Ingress: []*networking.IstioIngressListener{
  5915  				{
  5916  					Port: &networking.SidecarPort{
  5917  						Protocol: "http",
  5918  						Number:   90,
  5919  						Name:     "foo",
  5920  					},
  5921  					DefaultEndpoint: "[::1]:9999",
  5922  					Tls: &networking.ServerTLSSettings{
  5923  						Mode: networking.ServerTLSSettings_ISTIO_MUTUAL,
  5924  					},
  5925  				},
  5926  			},
  5927  		}, false, false},
  5928  		{"ingress tls mode set to ISTIO_AUTO_PASSTHROUGH in IPv4", &networking.Sidecar{
  5929  			Ingress: []*networking.IstioIngressListener{
  5930  				{
  5931  					Port: &networking.SidecarPort{
  5932  						Protocol: "http",
  5933  						Number:   90,
  5934  						Name:     "foo",
  5935  					},
  5936  					DefaultEndpoint: "127.0.0.1:9999",
  5937  					Tls: &networking.ServerTLSSettings{
  5938  						Mode: networking.ServerTLSSettings_AUTO_PASSTHROUGH,
  5939  					},
  5940  				},
  5941  			},
  5942  		}, false, false},
  5943  		{"ingress tls mode set to ISTIO_AUTO_PASSTHROUGH in IPv6", &networking.Sidecar{
  5944  			Ingress: []*networking.IstioIngressListener{
  5945  				{
  5946  					Port: &networking.SidecarPort{
  5947  						Protocol: "http",
  5948  						Number:   90,
  5949  						Name:     "foo",
  5950  					},
  5951  					DefaultEndpoint: "[::1]:9999",
  5952  					Tls: &networking.ServerTLSSettings{
  5953  						Mode: networking.ServerTLSSettings_AUTO_PASSTHROUGH,
  5954  					},
  5955  				},
  5956  			},
  5957  		}, false, false},
  5958  		{"ingress tls invalid protocol in IPv4", &networking.Sidecar{
  5959  			Ingress: []*networking.IstioIngressListener{
  5960  				{
  5961  					Port: &networking.SidecarPort{
  5962  						Protocol: "tcp",
  5963  						Number:   90,
  5964  						Name:     "foo",
  5965  					},
  5966  					DefaultEndpoint: "127.0.0.1:9999",
  5967  					Tls: &networking.ServerTLSSettings{
  5968  						Mode: networking.ServerTLSSettings_SIMPLE,
  5969  					},
  5970  				},
  5971  			},
  5972  		}, false, false},
  5973  		{"ingress tls invalid protocol in IPv6", &networking.Sidecar{
  5974  			Ingress: []*networking.IstioIngressListener{
  5975  				{
  5976  					Port: &networking.SidecarPort{
  5977  						Protocol: "tcp",
  5978  						Number:   90,
  5979  						Name:     "foo",
  5980  					},
  5981  					DefaultEndpoint: "[::1]:9999",
  5982  					Tls: &networking.ServerTLSSettings{
  5983  						Mode: networking.ServerTLSSettings_SIMPLE,
  5984  					},
  5985  				},
  5986  			},
  5987  		}, false, false},
  5988  		{"ingress tls httpRedirect is not supported in IPv4", &networking.Sidecar{
  5989  			Ingress: []*networking.IstioIngressListener{
  5990  				{
  5991  					Port: &networking.SidecarPort{
  5992  						Protocol: "tcp",
  5993  						Number:   90,
  5994  						Name:     "foo",
  5995  					},
  5996  					DefaultEndpoint: "127.0.0.1:9999",
  5997  					Tls: &networking.ServerTLSSettings{
  5998  						Mode:          networking.ServerTLSSettings_SIMPLE,
  5999  						HttpsRedirect: true,
  6000  					},
  6001  				},
  6002  			},
  6003  		}, false, false},
  6004  		{"ingress tls httpRedirect is not supported in IPv6", &networking.Sidecar{
  6005  			Ingress: []*networking.IstioIngressListener{
  6006  				{
  6007  					Port: &networking.SidecarPort{
  6008  						Protocol: "tcp",
  6009  						Number:   90,
  6010  						Name:     "foo",
  6011  					},
  6012  					DefaultEndpoint: "[::1]:9999",
  6013  					Tls: &networking.ServerTLSSettings{
  6014  						Mode:          networking.ServerTLSSettings_SIMPLE,
  6015  						HttpsRedirect: true,
  6016  					},
  6017  				},
  6018  			},
  6019  		}, false, false},
  6020  		{"ingress tls SAN entries are not supported in IPv4", &networking.Sidecar{
  6021  			Ingress: []*networking.IstioIngressListener{
  6022  				{
  6023  					Port: &networking.SidecarPort{
  6024  						Protocol: "tcp",
  6025  						Number:   90,
  6026  						Name:     "foo",
  6027  					},
  6028  					DefaultEndpoint: "127.0.0.1:9999",
  6029  					Tls: &networking.ServerTLSSettings{
  6030  						Mode:            networking.ServerTLSSettings_SIMPLE,
  6031  						SubjectAltNames: []string{"httpbin.com"},
  6032  					},
  6033  				},
  6034  			},
  6035  		}, false, false},
  6036  		{"ingress tls SAN entries are not supported in IPv6", &networking.Sidecar{
  6037  			Ingress: []*networking.IstioIngressListener{
  6038  				{
  6039  					Port: &networking.SidecarPort{
  6040  						Protocol: "tcp",
  6041  						Number:   90,
  6042  						Name:     "foo",
  6043  					},
  6044  					DefaultEndpoint: "[::1]:9999",
  6045  					Tls: &networking.ServerTLSSettings{
  6046  						Mode:            networking.ServerTLSSettings_SIMPLE,
  6047  						SubjectAltNames: []string{"httpbin.com"},
  6048  					},
  6049  				},
  6050  			},
  6051  		}, false, false},
  6052  		{"ingress tls credentialName is not supported in IPv4", &networking.Sidecar{
  6053  			Ingress: []*networking.IstioIngressListener{
  6054  				{
  6055  					Port: &networking.SidecarPort{
  6056  						Protocol: "tcp",
  6057  						Number:   90,
  6058  						Name:     "foo",
  6059  					},
  6060  					DefaultEndpoint: "127.0.0.1:9999",
  6061  					Tls: &networking.ServerTLSSettings{
  6062  						Mode:           networking.ServerTLSSettings_SIMPLE,
  6063  						CredentialName: "secret-name",
  6064  					},
  6065  				},
  6066  			},
  6067  		}, false, false},
  6068  		{"ingress tls credentialName is not supported in IPv6", &networking.Sidecar{
  6069  			Ingress: []*networking.IstioIngressListener{
  6070  				{
  6071  					Port: &networking.SidecarPort{
  6072  						Protocol: "tcp",
  6073  						Number:   90,
  6074  						Name:     "foo",
  6075  					},
  6076  					DefaultEndpoint: "[::1]:9999",
  6077  					Tls: &networking.ServerTLSSettings{
  6078  						Mode:           networking.ServerTLSSettings_SIMPLE,
  6079  						CredentialName: "secret-name",
  6080  					},
  6081  				},
  6082  			},
  6083  		}, false, false},
  6084  		// We're using the same validation code as DestinationRule, so we're really trusting the TrafficPolicy
  6085  		// validation code's testing. Here we just want to exercise the edge cases around Sidecar specifically.
  6086  		{"valid inbound connection pool", &networking.Sidecar{
  6087  			InboundConnectionPool: &networking.ConnectionPoolSettings{
  6088  				Http: &networking.ConnectionPoolSettings_HTTPSettings{
  6089  					Http1MaxPendingRequests:  1024,
  6090  					Http2MaxRequests:         1024,
  6091  					MaxRequestsPerConnection: 1024,
  6092  					MaxRetries:               1024,
  6093  				},
  6094  			},
  6095  		}, true, false},
  6096  		{"valid port-level connection pool with top level default", &networking.Sidecar{
  6097  			Ingress: []*networking.IstioIngressListener{
  6098  				{
  6099  					Port: &networking.SidecarPort{
  6100  						Protocol: "http",
  6101  						Number:   90,
  6102  						Name:     "foo",
  6103  					},
  6104  					ConnectionPool: &networking.ConnectionPoolSettings{
  6105  						Http: &networking.ConnectionPoolSettings_HTTPSettings{
  6106  							Http1MaxPendingRequests:  1024,
  6107  							Http2MaxRequests:         1024,
  6108  							MaxRequestsPerConnection: 1024,
  6109  							MaxRetries:               1024,
  6110  						},
  6111  					},
  6112  				},
  6113  			},
  6114  			InboundConnectionPool: &networking.ConnectionPoolSettings{
  6115  				Http: &networking.ConnectionPoolSettings_HTTPSettings{
  6116  					Http1MaxPendingRequests:  1024,
  6117  					Http2MaxRequests:         1024,
  6118  					MaxRequestsPerConnection: 1024,
  6119  					MaxRetries:               1024,
  6120  				},
  6121  			},
  6122  		}, true, false},
  6123  		{"valid port-level connection pool without a top level default", &networking.Sidecar{
  6124  			Ingress: []*networking.IstioIngressListener{
  6125  				{
  6126  					Port: &networking.SidecarPort{
  6127  						Protocol: "http",
  6128  						Number:   90,
  6129  						Name:     "foo",
  6130  					},
  6131  					ConnectionPool: &networking.ConnectionPoolSettings{
  6132  						Http: &networking.ConnectionPoolSettings_HTTPSettings{
  6133  							Http1MaxPendingRequests:  1024,
  6134  							Http2MaxRequests:         1024,
  6135  							MaxRequestsPerConnection: 1024,
  6136  							MaxRetries:               1024,
  6137  						},
  6138  					},
  6139  				},
  6140  			},
  6141  		}, true, false},
  6142  		{"warn http connection settings on tcp port", &networking.Sidecar{
  6143  			Ingress: []*networking.IstioIngressListener{
  6144  				{
  6145  					Port: &networking.SidecarPort{
  6146  						Protocol: "tcp",
  6147  						Number:   90,
  6148  						Name:     "foo",
  6149  					},
  6150  					ConnectionPool: &networking.ConnectionPoolSettings{
  6151  						Http: &networking.ConnectionPoolSettings_HTTPSettings{
  6152  							Http1MaxPendingRequests:  1024,
  6153  							Http2MaxRequests:         1024,
  6154  							MaxRequestsPerConnection: 1024,
  6155  							MaxRetries:               1024,
  6156  						},
  6157  					},
  6158  				},
  6159  			},
  6160  		}, true, true},
  6161  		{"invalid top level connection pool", &networking.Sidecar{
  6162  			InboundConnectionPool: &networking.ConnectionPoolSettings{},
  6163  		}, false, false},
  6164  		{"invalid port-level connection pool", &networking.Sidecar{
  6165  			Ingress: []*networking.IstioIngressListener{
  6166  				{
  6167  					Port: &networking.SidecarPort{
  6168  						Protocol: "tcp",
  6169  						Number:   90,
  6170  						Name:     "foo",
  6171  					},
  6172  					ConnectionPool: &networking.ConnectionPoolSettings{},
  6173  				},
  6174  			},
  6175  		}, false, false},
  6176  	}
  6177  
  6178  	for _, tt := range tests {
  6179  		t.Run(tt.name, func(t *testing.T) {
  6180  			warn, err := ValidateSidecar(config.Config{
  6181  				Meta: config.Meta{
  6182  					Name:      "foo",
  6183  					Namespace: "bar",
  6184  				},
  6185  				Spec: tt.in,
  6186  			})
  6187  			checkValidation(t, warn, err, tt.valid, tt.warn)
  6188  		})
  6189  	}
  6190  }
  6191  
  6192  func TestValidateRequestAuthentication(t *testing.T) {
  6193  	cases := []struct {
  6194  		name        string
  6195  		configName  string
  6196  		annotations map[string]string
  6197  		in          proto.Message
  6198  		valid       bool
  6199  		warning     bool
  6200  	}{
  6201  		{
  6202  			name:       "empty spec",
  6203  			configName: constants.DefaultAuthenticationPolicyName,
  6204  			in:         &security_beta.RequestAuthentication{},
  6205  			valid:      true,
  6206  		},
  6207  		{
  6208  			name:       "another empty spec",
  6209  			configName: constants.DefaultAuthenticationPolicyName,
  6210  			in: &security_beta.RequestAuthentication{
  6211  				Selector: &api.WorkloadSelector{},
  6212  			},
  6213  			valid:   true,
  6214  			warning: true,
  6215  		},
  6216  		{
  6217  			name:       "empty spec with non default name",
  6218  			configName: someName,
  6219  			in:         &security_beta.RequestAuthentication{},
  6220  			valid:      true,
  6221  		},
  6222  		{
  6223  			name:        "dry run annotation not supported",
  6224  			configName:  someName,
  6225  			annotations: map[string]string{"istio.io/dry-run": "true"},
  6226  			in:          &security_beta.RequestAuthentication{},
  6227  			valid:       false,
  6228  		},
  6229  		{
  6230  			name:       "default name with non empty selector",
  6231  			configName: constants.DefaultAuthenticationPolicyName,
  6232  			in: &security_beta.RequestAuthentication{
  6233  				Selector: &api.WorkloadSelector{
  6234  					MatchLabels: map[string]string{
  6235  						"app": "httpbin",
  6236  					},
  6237  				},
  6238  			},
  6239  			valid: true,
  6240  		},
  6241  		{
  6242  			name:       "empty jwt rule",
  6243  			configName: "foo",
  6244  			in: &security_beta.RequestAuthentication{
  6245  				JwtRules: []*security_beta.JWTRule{
  6246  					{},
  6247  				},
  6248  			},
  6249  			valid: false,
  6250  		},
  6251  		{
  6252  			name:       "empty issuer",
  6253  			configName: "foo",
  6254  			in: &security_beta.RequestAuthentication{
  6255  				JwtRules: []*security_beta.JWTRule{
  6256  					{
  6257  						Issuer: "",
  6258  					},
  6259  				},
  6260  			},
  6261  			valid: false,
  6262  		},
  6263  		{
  6264  			name:       "bad JwksUri - no protocol",
  6265  			configName: "foo",
  6266  			in: &security_beta.RequestAuthentication{
  6267  				JwtRules: []*security_beta.JWTRule{
  6268  					{
  6269  						Issuer:  "foo.com",
  6270  						JwksUri: "foo.com",
  6271  					},
  6272  				},
  6273  			},
  6274  			valid: false,
  6275  		},
  6276  		{
  6277  			name:       "bad JwksUri - invalid port",
  6278  			configName: "foo",
  6279  			in: &security_beta.RequestAuthentication{
  6280  				JwtRules: []*security_beta.JWTRule{
  6281  					{
  6282  						Issuer:  "foo.com",
  6283  						JwksUri: "https://foo.com:not-a-number",
  6284  					},
  6285  				},
  6286  			},
  6287  			valid: false,
  6288  		},
  6289  		{
  6290  			name:       "empty value",
  6291  			configName: "foo",
  6292  			in: &security_beta.RequestAuthentication{
  6293  				Selector: &api.WorkloadSelector{
  6294  					MatchLabels: map[string]string{
  6295  						"app":     "httpbin",
  6296  						"version": "",
  6297  					},
  6298  				},
  6299  				JwtRules: []*security_beta.JWTRule{
  6300  					{
  6301  						Issuer:  "foo.com",
  6302  						JwksUri: "https://foo.com/cert",
  6303  					},
  6304  				},
  6305  			},
  6306  			valid: true,
  6307  		},
  6308  		{
  6309  			name:       "bad workload selector - empty key",
  6310  			configName: "foo",
  6311  			in: &security_beta.RequestAuthentication{
  6312  				Selector: &api.WorkloadSelector{
  6313  					MatchLabels: map[string]string{
  6314  						"app": "httpbin",
  6315  						"":    "v1",
  6316  					},
  6317  				},
  6318  				JwtRules: []*security_beta.JWTRule{
  6319  					{
  6320  						Issuer:  "foo.com",
  6321  						JwksUri: "https://foo.com/cert",
  6322  					},
  6323  				},
  6324  			},
  6325  			valid: false,
  6326  		},
  6327  		{
  6328  			name:       "good targetRef",
  6329  			configName: "foo",
  6330  			in: &security_beta.RequestAuthentication{
  6331  				TargetRef: &api.PolicyTargetReference{
  6332  					Group: gvk.KubernetesGateway.Group,
  6333  					Kind:  gvk.KubernetesGateway.Kind,
  6334  					Name:  "foo",
  6335  				},
  6336  				JwtRules: []*security_beta.JWTRule{
  6337  					{
  6338  						Issuer:  "foo.com",
  6339  						JwksUri: "https://foo.com/cert",
  6340  					},
  6341  				},
  6342  			},
  6343  			valid: true,
  6344  		},
  6345  		{
  6346  			name:       "bad targetRef - empty name",
  6347  			configName: "foo",
  6348  			in: &security_beta.RequestAuthentication{
  6349  				TargetRef: &api.PolicyTargetReference{
  6350  					Group: gvk.KubernetesGateway.Group,
  6351  					Kind:  gvk.KubernetesGateway.Kind,
  6352  				},
  6353  				JwtRules: []*security_beta.JWTRule{
  6354  					{
  6355  						Issuer:  "foo.com",
  6356  						JwksUri: "https://foo.com/cert",
  6357  					},
  6358  				},
  6359  			},
  6360  			valid: false,
  6361  		},
  6362  		{
  6363  			name:       "bad targetRef - non-empty namespace",
  6364  			configName: "foo",
  6365  			in: &security_beta.RequestAuthentication{
  6366  				TargetRef: &api.PolicyTargetReference{
  6367  					Group:     gvk.KubernetesGateway.Group,
  6368  					Kind:      gvk.KubernetesGateway.Kind,
  6369  					Name:      "foo",
  6370  					Namespace: "bar",
  6371  				},
  6372  				JwtRules: []*security_beta.JWTRule{
  6373  					{
  6374  						Issuer:  "foo.com",
  6375  						JwksUri: "https://foo.com/cert",
  6376  					},
  6377  				},
  6378  			},
  6379  			valid: false,
  6380  		},
  6381  		{
  6382  			name:       "bad targetRef - wrong group",
  6383  			configName: "foo",
  6384  			in: &security_beta.RequestAuthentication{
  6385  				TargetRef: &api.PolicyTargetReference{
  6386  					Group: "wrong-group",
  6387  					Kind:  gvk.KubernetesGateway.Kind,
  6388  					Name:  "foo",
  6389  				},
  6390  				JwtRules: []*security_beta.JWTRule{
  6391  					{
  6392  						Issuer:  "foo.com",
  6393  						JwksUri: "https://foo.com/cert",
  6394  					},
  6395  				},
  6396  			},
  6397  			valid: false,
  6398  		},
  6399  		{
  6400  			name:       "bad targetRef - wrong kind",
  6401  			configName: "foo",
  6402  			in: &security_beta.RequestAuthentication{
  6403  				TargetRef: &api.PolicyTargetReference{
  6404  					Group: gvk.KubernetesGateway.Group,
  6405  					Kind:  "wrong-kind",
  6406  					Name:  "foo",
  6407  				},
  6408  				JwtRules: []*security_beta.JWTRule{
  6409  					{
  6410  						Issuer:  "foo.com",
  6411  						JwksUri: "https://foo.com/cert",
  6412  					},
  6413  				},
  6414  			},
  6415  			valid: false,
  6416  		},
  6417  		{
  6418  			name:       "targetRef and selector cannot both be set",
  6419  			configName: "foo",
  6420  			in: &security_beta.RequestAuthentication{
  6421  				TargetRef: &api.PolicyTargetReference{
  6422  					Group: gvk.KubernetesGateway.Group,
  6423  					Kind:  gvk.KubernetesGateway.Kind,
  6424  					Name:  "foo",
  6425  				},
  6426  				JwtRules: []*security_beta.JWTRule{
  6427  					{
  6428  						Issuer:  "foo.com",
  6429  						JwksUri: "https://foo.com/cert",
  6430  					},
  6431  				},
  6432  				Selector: &api.WorkloadSelector{
  6433  					MatchLabels: map[string]string{
  6434  						"app": "httpbin",
  6435  					},
  6436  				},
  6437  			},
  6438  			valid: false,
  6439  		},
  6440  		{
  6441  			name:       "bad header location",
  6442  			configName: constants.DefaultAuthenticationPolicyName,
  6443  			in: &security_beta.RequestAuthentication{
  6444  				JwtRules: []*security_beta.JWTRule{
  6445  					{
  6446  						Issuer:  "foo.com",
  6447  						JwksUri: "https://foo.com",
  6448  						FromHeaders: []*security_beta.JWTHeader{
  6449  							{
  6450  								Name:   "",
  6451  								Prefix: "Bearer ",
  6452  							},
  6453  						},
  6454  					},
  6455  				},
  6456  			},
  6457  			valid: false,
  6458  		},
  6459  		{
  6460  			name:       "bad param location",
  6461  			configName: constants.DefaultAuthenticationPolicyName,
  6462  			in: &security_beta.RequestAuthentication{
  6463  				JwtRules: []*security_beta.JWTRule{
  6464  					{
  6465  						Issuer:     "foo.com",
  6466  						JwksUri:    "https://foo.com",
  6467  						FromParams: []string{""},
  6468  					},
  6469  				},
  6470  			},
  6471  			valid: false,
  6472  		},
  6473  		{
  6474  			name:       "good",
  6475  			configName: constants.DefaultAuthenticationPolicyName,
  6476  			in: &security_beta.RequestAuthentication{
  6477  				JwtRules: []*security_beta.JWTRule{
  6478  					{
  6479  						Issuer:  "foo.com",
  6480  						JwksUri: "https://foo.com",
  6481  						FromHeaders: []*security_beta.JWTHeader{
  6482  							{
  6483  								Name:   "x-foo",
  6484  								Prefix: "Bearer ",
  6485  							},
  6486  						},
  6487  					},
  6488  				},
  6489  			},
  6490  			valid: true,
  6491  		},
  6492  		{
  6493  			name:       "bad cookie location",
  6494  			configName: constants.DefaultAuthenticationPolicyName,
  6495  			in: &security_beta.RequestAuthentication{
  6496  				JwtRules: []*security_beta.JWTRule{
  6497  					{
  6498  						Issuer:      "foo.com",
  6499  						JwksUri:     "https://foo.com",
  6500  						FromCookies: []string{"", "foo"},
  6501  					},
  6502  				},
  6503  			},
  6504  			valid: false,
  6505  		},
  6506  		{
  6507  			name:       "jwks ok",
  6508  			configName: constants.DefaultAuthenticationPolicyName,
  6509  			in: &security_beta.RequestAuthentication{
  6510  				JwtRules: []*security_beta.JWTRule{
  6511  					{
  6512  						Issuer: "foo.com",
  6513  						Jwks:   "{ \"keys\":[ {\"e\":\"AQAB\",\"kid\":\"DHFbpoIUqrY8t2zpA2qXfCmr5VO5ZEr4RzHU_-envvQ\",\"kty\":\"RSA\",\"n\":\"xAE7eB6qugXyCAG3yhh7pkDkT65pHymX-P7KfIupjf59vsdo91bSP9C8H07pSAGQO1MV_xFj9VswgsCg4R6otmg5PV2He95lZdHtOcU5DXIg_pbhLdKXbi66GlVeK6ABZOUW3WYtnNHD-91gVuoeJT_DwtGGcp4ignkgXfkiEm4sw-4sfb4qdt5oLbyVpmW6x9cfa7vs2WTfURiCrBoUqgBo_-4WTiULmmHSGZHOjzwa8WtrtOQGsAFjIbno85jp6MnGGGZPYZbDAa_b3y5u-YpW7ypZrvD8BgtKVjgtQgZhLAGezMt0ua3DRrWnKqTZ0BJ_EyxOGuHJrLsn00fnMQ\"}]}", // nolint: lll
  6514  						FromHeaders: []*security_beta.JWTHeader{
  6515  							{
  6516  								Name:   "x-foo",
  6517  								Prefix: "Bearer ",
  6518  							},
  6519  						},
  6520  					},
  6521  				},
  6522  			},
  6523  			valid: true,
  6524  		},
  6525  		{
  6526  			name:       "jwks error",
  6527  			configName: constants.DefaultAuthenticationPolicyName,
  6528  			in: &security_beta.RequestAuthentication{
  6529  				JwtRules: []*security_beta.JWTRule{
  6530  					{
  6531  						Issuer: "foo.com",
  6532  						Jwks:   "foo",
  6533  						FromHeaders: []*security_beta.JWTHeader{
  6534  							{
  6535  								Name:   "x-foo",
  6536  								Prefix: "Bearer ",
  6537  							},
  6538  						},
  6539  					},
  6540  				},
  6541  			},
  6542  			valid: false,
  6543  		},
  6544  		{
  6545  			name:       "null outputClaimToHeader",
  6546  			configName: constants.DefaultAuthenticationPolicyName,
  6547  			in: &security_beta.RequestAuthentication{
  6548  				JwtRules: []*security_beta.JWTRule{
  6549  					{
  6550  						Issuer:               "foo.com",
  6551  						JwksUri:              "https://foo.com",
  6552  						OutputClaimToHeaders: []*security_beta.ClaimToHeader{{}},
  6553  					},
  6554  				},
  6555  			},
  6556  			valid: false,
  6557  		},
  6558  		{
  6559  			name:       "null claim value in outputClaimToHeader ",
  6560  			configName: constants.DefaultAuthenticationPolicyName,
  6561  			in: &security_beta.RequestAuthentication{
  6562  				JwtRules: []*security_beta.JWTRule{
  6563  					{
  6564  						Issuer:  "foo.com",
  6565  						JwksUri: "https://foo.com",
  6566  						OutputClaimToHeaders: []*security_beta.ClaimToHeader{
  6567  							{
  6568  								Header: "x-jwt-claim",
  6569  								Claim:  "",
  6570  							},
  6571  						},
  6572  					},
  6573  				},
  6574  			},
  6575  			valid: false,
  6576  		},
  6577  		{
  6578  			name:       "null header value in outputClaimToHeader ",
  6579  			configName: constants.DefaultAuthenticationPolicyName,
  6580  			in: &security_beta.RequestAuthentication{
  6581  				JwtRules: []*security_beta.JWTRule{
  6582  					{
  6583  						Issuer:  "foo.com",
  6584  						JwksUri: "https://foo.com",
  6585  						OutputClaimToHeaders: []*security_beta.ClaimToHeader{
  6586  							{
  6587  								Header: "",
  6588  								Claim:  "sub",
  6589  							},
  6590  						},
  6591  					},
  6592  				},
  6593  			},
  6594  			valid: false,
  6595  		},
  6596  		{
  6597  			name:       "invalid header value in outputClaimToHeader ",
  6598  			configName: constants.DefaultAuthenticationPolicyName,
  6599  			in: &security_beta.RequestAuthentication{
  6600  				JwtRules: []*security_beta.JWTRule{
  6601  					{
  6602  						Issuer:  "foo.com",
  6603  						JwksUri: "https://foo.com",
  6604  						OutputClaimToHeaders: []*security_beta.ClaimToHeader{
  6605  							{
  6606  								Header: "abc%123",
  6607  								Claim:  "sub",
  6608  							},
  6609  						},
  6610  					},
  6611  				},
  6612  			},
  6613  			valid: false,
  6614  		},
  6615  	}
  6616  
  6617  	for _, c := range cases {
  6618  		t.Run(c.name, func(t *testing.T) {
  6619  			warn, got := ValidateRequestAuthentication(config.Config{
  6620  				Meta: config.Meta{
  6621  					Name:        c.configName,
  6622  					Namespace:   someNamespace,
  6623  					Annotations: c.annotations,
  6624  				},
  6625  				Spec: c.in,
  6626  			})
  6627  			if (got == nil) != c.valid {
  6628  				t.Errorf("test: %q got(%v) != want(%v)\n", c.name, got, c.valid)
  6629  			}
  6630  			if (warn == nil) == c.warning {
  6631  				t.Errorf("test: %q warn(%v) != want(%v)\n", c.name, warn, c.warning)
  6632  			}
  6633  		})
  6634  	}
  6635  }
  6636  
  6637  func TestValidatePeerAuthentication(t *testing.T) {
  6638  	cases := []struct {
  6639  		name       string
  6640  		configName string
  6641  		in         proto.Message
  6642  		valid      bool
  6643  		warning    bool
  6644  	}{
  6645  		{
  6646  			name:       "empty spec",
  6647  			configName: constants.DefaultAuthenticationPolicyName,
  6648  			in:         &security_beta.PeerAuthentication{},
  6649  			valid:      true,
  6650  		},
  6651  		{
  6652  			name:       "empty mtls with selector of empty labels",
  6653  			configName: constants.DefaultAuthenticationPolicyName,
  6654  			in: &security_beta.PeerAuthentication{
  6655  				Selector: &api.WorkloadSelector{},
  6656  			},
  6657  			valid:   true,
  6658  			warning: true,
  6659  		},
  6660  		{
  6661  			name:       "empty spec with non default name",
  6662  			configName: someName,
  6663  			in:         &security_beta.PeerAuthentication{},
  6664  			valid:      true,
  6665  		},
  6666  		{
  6667  			name:       "default name with non empty selector",
  6668  			configName: constants.DefaultAuthenticationPolicyName,
  6669  			in: &security_beta.PeerAuthentication{
  6670  				Selector: &api.WorkloadSelector{
  6671  					MatchLabels: map[string]string{
  6672  						"app": "httpbin",
  6673  					},
  6674  				},
  6675  			},
  6676  			valid: true,
  6677  		},
  6678  		{
  6679  			name:       "empty port level mtls",
  6680  			configName: "foo",
  6681  			in: &security_beta.PeerAuthentication{
  6682  				Selector: &api.WorkloadSelector{
  6683  					MatchLabels: map[string]string{
  6684  						"app": "httpbin",
  6685  					},
  6686  				},
  6687  				PortLevelMtls: map[uint32]*security_beta.PeerAuthentication_MutualTLS{},
  6688  			},
  6689  			valid: false,
  6690  		},
  6691  		{
  6692  			name:       "empty selector with port level mtls",
  6693  			configName: constants.DefaultAuthenticationPolicyName,
  6694  			in: &security_beta.PeerAuthentication{
  6695  				PortLevelMtls: map[uint32]*security_beta.PeerAuthentication_MutualTLS{
  6696  					8080: {
  6697  						Mode: security_beta.PeerAuthentication_MutualTLS_UNSET,
  6698  					},
  6699  				},
  6700  			},
  6701  			valid: false,
  6702  		},
  6703  		{
  6704  			name:       "port 0",
  6705  			configName: "foo",
  6706  			in: &security_beta.PeerAuthentication{
  6707  				PortLevelMtls: map[uint32]*security_beta.PeerAuthentication_MutualTLS{
  6708  					0: {
  6709  						Mode: security_beta.PeerAuthentication_MutualTLS_UNSET,
  6710  					},
  6711  				},
  6712  			},
  6713  			valid: false,
  6714  		},
  6715  		{
  6716  			name:       "unset mode",
  6717  			configName: constants.DefaultAuthenticationPolicyName,
  6718  			in: &security_beta.PeerAuthentication{
  6719  				Mtls: &security_beta.PeerAuthentication_MutualTLS{
  6720  					Mode: security_beta.PeerAuthentication_MutualTLS_UNSET,
  6721  				},
  6722  			},
  6723  			valid: true,
  6724  		},
  6725  		{
  6726  			name:       "port level",
  6727  			configName: "port-level",
  6728  			in: &security_beta.PeerAuthentication{
  6729  				Selector: &api.WorkloadSelector{
  6730  					MatchLabels: map[string]string{
  6731  						"app": "httpbin",
  6732  					},
  6733  				},
  6734  				PortLevelMtls: map[uint32]*security_beta.PeerAuthentication_MutualTLS{
  6735  					8080: {
  6736  						Mode: security_beta.PeerAuthentication_MutualTLS_UNSET,
  6737  					},
  6738  				},
  6739  			},
  6740  			valid: true,
  6741  		},
  6742  	}
  6743  
  6744  	for _, c := range cases {
  6745  		t.Run(c.name, func(t *testing.T) {
  6746  			warn, got := ValidatePeerAuthentication(config.Config{
  6747  				Meta: config.Meta{
  6748  					Name:      c.configName,
  6749  					Namespace: someNamespace,
  6750  				},
  6751  				Spec: c.in,
  6752  			})
  6753  			if (got == nil) != c.valid {
  6754  				t.Errorf("got(%v) != want(%v)\n", got, c.valid)
  6755  			}
  6756  			if (warn == nil) == c.warning {
  6757  				t.Errorf("warn(%v) != want(%v)\n", warn, c.warning)
  6758  			}
  6759  		})
  6760  	}
  6761  }
  6762  
  6763  func Test_validateExportTo(t *testing.T) {
  6764  	tests := []struct {
  6765  		name                          string
  6766  		namespace                     string
  6767  		exportTo                      []string
  6768  		isDestinationRuleWithSelector bool
  6769  		isServiceEntry                bool
  6770  		wantErr                       bool
  6771  	}{
  6772  		{
  6773  			name:      "empty exportTo is okay",
  6774  			namespace: "ns5",
  6775  			wantErr:   false,
  6776  		},
  6777  		{
  6778  			name:      "* is allowed",
  6779  			namespace: "ns5",
  6780  			exportTo:  []string{"*"},
  6781  			wantErr:   false,
  6782  		},
  6783  		{
  6784  			name:      ". and ns1 are allowed",
  6785  			namespace: "ns5",
  6786  			exportTo:  []string{".", "ns1"},
  6787  			wantErr:   false,
  6788  		},
  6789  		{
  6790  			name:      "bunch of namespaces in exportTo is okay",
  6791  			namespace: "ns5",
  6792  			exportTo:  []string{"ns1", "ns2", "ns5"},
  6793  			wantErr:   false,
  6794  		},
  6795  		{
  6796  			name:           "~ is allowed for service entry configs",
  6797  			namespace:      "ns5",
  6798  			exportTo:       []string{"~"},
  6799  			isServiceEntry: true,
  6800  			wantErr:        false,
  6801  		},
  6802  		{
  6803  			name:      "~ not allowed for non service entry configs",
  6804  			namespace: "ns5",
  6805  			exportTo:  []string{"~", "ns1"},
  6806  			wantErr:   true,
  6807  		},
  6808  		{
  6809  			name:      ". and * together are not allowed",
  6810  			namespace: "ns5",
  6811  			exportTo:  []string{".", "*"},
  6812  			wantErr:   true,
  6813  		},
  6814  		{
  6815  			name:      "* and ns1 together are not allowed",
  6816  			namespace: "ns5",
  6817  			exportTo:  []string{"*", "ns1"},
  6818  			wantErr:   true,
  6819  		},
  6820  		{
  6821  			name:      ". and same namespace in exportTo is not okay",
  6822  			namespace: "ns5",
  6823  			exportTo:  []string{".", "ns5"},
  6824  			wantErr:   true,
  6825  		},
  6826  		{
  6827  			name:      "duplicate namespaces in exportTo is not okay",
  6828  			namespace: "ns5",
  6829  			exportTo:  []string{"ns1", "ns2", "ns1"},
  6830  			wantErr:   true,
  6831  		},
  6832  		{
  6833  			name:           "duplicate none in service entry exportTo is not okay",
  6834  			namespace:      "ns5",
  6835  			exportTo:       []string{"~", "~", "ns1"},
  6836  			isServiceEntry: true,
  6837  			wantErr:        true,
  6838  		},
  6839  		{
  6840  			name:      "invalid namespace names are not okay",
  6841  			namespace: "ns5",
  6842  			exportTo:  []string{"ns1_"},
  6843  			wantErr:   true,
  6844  		},
  6845  		{
  6846  			name:           "none and other namespaces cannot be combined in service entry exportTo",
  6847  			namespace:      "ns5",
  6848  			exportTo:       []string{"~", "ns1"},
  6849  			isServiceEntry: true,
  6850  			wantErr:        true,
  6851  		},
  6852  		{
  6853  			name:                          "destination rule with workloadselector cannot have exportTo (*)",
  6854  			namespace:                     "ns5",
  6855  			exportTo:                      []string{"*"},
  6856  			isServiceEntry:                false,
  6857  			isDestinationRuleWithSelector: true,
  6858  			wantErr:                       true,
  6859  		},
  6860  		{
  6861  			name:                          "destination rule with workloadselector can have only exportTo (.)",
  6862  			namespace:                     "ns5",
  6863  			exportTo:                      []string{"."},
  6864  			isServiceEntry:                false,
  6865  			isDestinationRuleWithSelector: true,
  6866  			wantErr:                       false,
  6867  		},
  6868  		{
  6869  			name:                          "destination rule with workloadselector cannot have another ns in exportTo (.)",
  6870  			namespace:                     "ns5",
  6871  			exportTo:                      []string{"somens"},
  6872  			isServiceEntry:                false,
  6873  			isDestinationRuleWithSelector: true,
  6874  			wantErr:                       true,
  6875  		},
  6876  		{
  6877  			name:                          "destination rule with workloadselector cannot have another ns in addition to own ns in exportTo (.)",
  6878  			namespace:                     "ns5",
  6879  			exportTo:                      []string{".", "somens"},
  6880  			isServiceEntry:                false,
  6881  			isDestinationRuleWithSelector: true,
  6882  			wantErr:                       true,
  6883  		},
  6884  	}
  6885  	for _, tt := range tests {
  6886  		t.Run(tt.name, func(t *testing.T) {
  6887  			if err := validateExportTo(tt.namespace, tt.exportTo, tt.isServiceEntry, tt.isDestinationRuleWithSelector); (err != nil) != tt.wantErr {
  6888  				t.Errorf("validateExportTo() error = %v, wantErr %v", err, tt.wantErr)
  6889  			}
  6890  		})
  6891  	}
  6892  }
  6893  
  6894  func TestValidateTelemetry(t *testing.T) {
  6895  	tests := []struct {
  6896  		name    string
  6897  		in      proto.Message
  6898  		err     string
  6899  		warning string
  6900  	}{
  6901  		{"empty", &telemetry.Telemetry{}, "", ""},
  6902  		{"invalid message", &networking.Server{}, "cannot cast", ""},
  6903  		{
  6904  			"multiple providers",
  6905  			&telemetry.Telemetry{
  6906  				Tracing: []*telemetry.Tracing{{
  6907  					Providers: []*telemetry.ProviderRef{
  6908  						{Name: "a"},
  6909  						{Name: "b"},
  6910  					},
  6911  				}},
  6912  			},
  6913  			"", "multiple providers",
  6914  		},
  6915  		{
  6916  			"multiple tracers",
  6917  			&telemetry.Telemetry{
  6918  				Tracing: []*telemetry.Tracing{{}, {}},
  6919  			},
  6920  			"", "multiple tracing",
  6921  		},
  6922  		{
  6923  			"bad randomSamplingPercentage",
  6924  			&telemetry.Telemetry{
  6925  				Tracing: []*telemetry.Tracing{{
  6926  					RandomSamplingPercentage: &wrapperspb.DoubleValue{Value: 101},
  6927  				}},
  6928  			},
  6929  			"randomSamplingPercentage", "",
  6930  		},
  6931  		{
  6932  			"tracing with a good custom tag",
  6933  			&telemetry.Telemetry{
  6934  				Tracing: []*telemetry.Tracing{{
  6935  					CustomTags: map[string]*telemetry.Tracing_CustomTag{
  6936  						"clusterID": {
  6937  							Type: &telemetry.Tracing_CustomTag_Environment{
  6938  								Environment: &telemetry.Tracing_Environment{
  6939  									Name: "FOO",
  6940  								},
  6941  							},
  6942  						},
  6943  					},
  6944  				}},
  6945  			},
  6946  			"", "",
  6947  		},
  6948  		{
  6949  			"tracing with a nil custom tag",
  6950  			&telemetry.Telemetry{
  6951  				Tracing: []*telemetry.Tracing{{
  6952  					CustomTags: map[string]*telemetry.Tracing_CustomTag{
  6953  						"clusterID": nil,
  6954  					},
  6955  				}},
  6956  			},
  6957  			"tag 'clusterID' may not have a nil value", "",
  6958  		},
  6959  		{
  6960  			"bad metrics operation",
  6961  			&telemetry.Telemetry{
  6962  				Metrics: []*telemetry.Metrics{{
  6963  					Overrides: []*telemetry.MetricsOverrides{
  6964  						{
  6965  							TagOverrides: map[string]*telemetry.MetricsOverrides_TagOverride{
  6966  								"my-tag": {
  6967  									Operation: telemetry.MetricsOverrides_TagOverride_UPSERT,
  6968  									Value:     "",
  6969  								},
  6970  							},
  6971  						},
  6972  					},
  6973  				}},
  6974  			},
  6975  			"must be set when operation is UPSERT", "",
  6976  		},
  6977  		{
  6978  			"good metrics operation",
  6979  			&telemetry.Telemetry{
  6980  				Metrics: []*telemetry.Metrics{{
  6981  					Overrides: []*telemetry.MetricsOverrides{
  6982  						{
  6983  							TagOverrides: map[string]*telemetry.MetricsOverrides_TagOverride{
  6984  								"my-tag": {
  6985  									Operation: telemetry.MetricsOverrides_TagOverride_UPSERT,
  6986  									Value:     "some-cel-expression",
  6987  								},
  6988  							},
  6989  						},
  6990  					},
  6991  				}},
  6992  			},
  6993  			"", "",
  6994  		},
  6995  		{
  6996  			"multi-accessloggings",
  6997  			&telemetry.Telemetry{
  6998  				AccessLogging: []*telemetry.AccessLogging{
  6999  					{
  7000  						Providers: []*telemetry.ProviderRef{
  7001  							{
  7002  								Name: "envoy",
  7003  							},
  7004  						},
  7005  					},
  7006  					{
  7007  						Providers: []*telemetry.ProviderRef{
  7008  							{
  7009  								Name: "otel",
  7010  							},
  7011  						},
  7012  					},
  7013  				},
  7014  			},
  7015  			"", "",
  7016  		},
  7017  		{
  7018  			"multi-accesslogging-providers",
  7019  			&telemetry.Telemetry{
  7020  				AccessLogging: []*telemetry.AccessLogging{
  7021  					{
  7022  						Providers: []*telemetry.ProviderRef{
  7023  							{
  7024  								Name: "envoy",
  7025  							},
  7026  							{
  7027  								Name: "otel",
  7028  							},
  7029  						},
  7030  					},
  7031  				},
  7032  			},
  7033  			"", "",
  7034  		},
  7035  		{
  7036  			"good targetRef",
  7037  			&telemetry.Telemetry{
  7038  				Tracing: []*telemetry.Tracing{{
  7039  					CustomTags: map[string]*telemetry.Tracing_CustomTag{
  7040  						"clusterID": {
  7041  							Type: &telemetry.Tracing_CustomTag_Environment{
  7042  								Environment: &telemetry.Tracing_Environment{
  7043  									Name: "FOO",
  7044  								},
  7045  							},
  7046  						},
  7047  					},
  7048  				}},
  7049  				TargetRef: &api.PolicyTargetReference{
  7050  					Group: gvk.KubernetesGateway.Group,
  7051  					Kind:  gvk.KubernetesGateway.Kind,
  7052  					Name:  "foo",
  7053  				},
  7054  			},
  7055  			"", "",
  7056  		},
  7057  		{
  7058  			"bad targetRef - empty name",
  7059  			&telemetry.Telemetry{
  7060  				Tracing: []*telemetry.Tracing{{
  7061  					CustomTags: map[string]*telemetry.Tracing_CustomTag{
  7062  						"clusterID": {
  7063  							Type: &telemetry.Tracing_CustomTag_Environment{
  7064  								Environment: &telemetry.Tracing_Environment{
  7065  									Name: "FOO",
  7066  								},
  7067  							},
  7068  						},
  7069  					},
  7070  				}},
  7071  				TargetRef: &api.PolicyTargetReference{
  7072  					Group: gvk.KubernetesGateway.Group,
  7073  					Kind:  gvk.KubernetesGateway.Kind,
  7074  				},
  7075  			},
  7076  			"targetRef name must be set", "",
  7077  		},
  7078  		{
  7079  			"bad targetRef - non-empty namespace",
  7080  			&telemetry.Telemetry{
  7081  				Tracing: []*telemetry.Tracing{{
  7082  					CustomTags: map[string]*telemetry.Tracing_CustomTag{
  7083  						"clusterID": {
  7084  							Type: &telemetry.Tracing_CustomTag_Environment{
  7085  								Environment: &telemetry.Tracing_Environment{
  7086  									Name: "FOO",
  7087  								},
  7088  							},
  7089  						},
  7090  					},
  7091  				}},
  7092  				TargetRef: &api.PolicyTargetReference{
  7093  					Group:     gvk.KubernetesGateway.Group,
  7094  					Kind:      gvk.KubernetesGateway.Kind,
  7095  					Name:      "foo",
  7096  					Namespace: "bar",
  7097  				},
  7098  			},
  7099  			"targetRef namespace must not be set", "",
  7100  		},
  7101  		{
  7102  			"bad targetRef - wrong group",
  7103  			&telemetry.Telemetry{
  7104  				Tracing: []*telemetry.Tracing{{
  7105  					CustomTags: map[string]*telemetry.Tracing_CustomTag{
  7106  						"clusterID": {
  7107  							Type: &telemetry.Tracing_CustomTag_Environment{
  7108  								Environment: &telemetry.Tracing_Environment{
  7109  									Name: "FOO",
  7110  								},
  7111  							},
  7112  						},
  7113  					},
  7114  				}},
  7115  				TargetRef: &api.PolicyTargetReference{
  7116  					Group: "wrong-group",
  7117  					Kind:  gvk.KubernetesGateway.Kind,
  7118  					Name:  "foo",
  7119  				},
  7120  			},
  7121  			fmt.Sprintf("targetRef must be to one of %v but was %s/%s",
  7122  				allowedTargetRefs, "wrong-group", gvk.KubernetesGateway.Kind), "",
  7123  		},
  7124  		{
  7125  			"bad targetRef - wrong kind",
  7126  			&telemetry.Telemetry{
  7127  				Tracing: []*telemetry.Tracing{{
  7128  					CustomTags: map[string]*telemetry.Tracing_CustomTag{
  7129  						"clusterID": {
  7130  							Type: &telemetry.Tracing_CustomTag_Environment{
  7131  								Environment: &telemetry.Tracing_Environment{
  7132  									Name: "FOO",
  7133  								},
  7134  							},
  7135  						},
  7136  					},
  7137  				}},
  7138  				TargetRef: &api.PolicyTargetReference{
  7139  					Group: gvk.KubernetesGateway.Group,
  7140  					Kind:  "wrong-kind",
  7141  					Name:  "foo",
  7142  				},
  7143  			},
  7144  			fmt.Sprintf("targetRef must be to one of %v but was %s/%s",
  7145  				allowedTargetRefs, gvk.KubernetesGateway.Group, "wrong-kind"), "",
  7146  		},
  7147  		{
  7148  			"targetRef and selector cannot both be set",
  7149  			&telemetry.Telemetry{
  7150  				Tracing: []*telemetry.Tracing{{
  7151  					CustomTags: map[string]*telemetry.Tracing_CustomTag{
  7152  						"clusterID": {
  7153  							Type: &telemetry.Tracing_CustomTag_Environment{
  7154  								Environment: &telemetry.Tracing_Environment{
  7155  									Name: "FOO",
  7156  								},
  7157  							},
  7158  						},
  7159  					},
  7160  				}},
  7161  				TargetRef: &api.PolicyTargetReference{
  7162  					Group: gvk.KubernetesGateway.Group,
  7163  					Kind:  "wrong-kind",
  7164  					Name:  "foo",
  7165  				},
  7166  				Selector: &api.WorkloadSelector{
  7167  					MatchLabels: map[string]string{
  7168  						"app": "httpbin",
  7169  					},
  7170  				},
  7171  			},
  7172  			"only one of targetRefs or workloadSelector can be set", "",
  7173  		},
  7174  	}
  7175  	for _, tt := range tests {
  7176  		t.Run(tt.name, func(t *testing.T) {
  7177  			warn, err := ValidateTelemetry(config.Config{
  7178  				Meta: config.Meta{
  7179  					Name:      someName,
  7180  					Namespace: someNamespace,
  7181  				},
  7182  				Spec: tt.in,
  7183  			})
  7184  			checkValidationMessage(t, warn, err, tt.warning, tt.err)
  7185  		})
  7186  	}
  7187  }
  7188  
  7189  func TestValidateProxyConfig(t *testing.T) {
  7190  	tests := []struct {
  7191  		name    string
  7192  		in      proto.Message
  7193  		out     string
  7194  		warning string
  7195  	}{
  7196  		{"empty", &networkingv1beta1.ProxyConfig{}, "", ""},
  7197  		{name: "invalid concurrency", in: &networkingv1beta1.ProxyConfig{
  7198  			Concurrency: &wrapperspb.Int32Value{Value: -1},
  7199  		}, out: "concurrency must be greater than or equal to 0"},
  7200  	}
  7201  	for _, tt := range tests {
  7202  		t.Run(tt.name, func(t *testing.T) {
  7203  			warn, err := ValidateProxyConfig(config.Config{
  7204  				Meta: config.Meta{
  7205  					Name:      someName,
  7206  					Namespace: someNamespace,
  7207  				},
  7208  				Spec: tt.in,
  7209  			})
  7210  			checkValidationMessage(t, warn, err, tt.warning, tt.out)
  7211  		})
  7212  	}
  7213  }
  7214  
  7215  func TestValidateTelemetryFilter(t *testing.T) {
  7216  	cases := []struct {
  7217  		filter *telemetry.AccessLogging_Filter
  7218  		valid  bool
  7219  	}{
  7220  		{
  7221  			filter: &telemetry.AccessLogging_Filter{
  7222  				Expression: "response.code >= 400",
  7223  			},
  7224  			valid: true,
  7225  		},
  7226  		{
  7227  			filter: &telemetry.AccessLogging_Filter{
  7228  				Expression: "connection.mtls && request.url_path.contains('v1beta3')",
  7229  			},
  7230  			valid: true,
  7231  		},
  7232  		{
  7233  			filter: &telemetry.AccessLogging_Filter{
  7234  				// TODO: find a better way to verify this
  7235  				// this should be an invalid expression
  7236  				Expression: "response.code",
  7237  			},
  7238  			valid: true,
  7239  		},
  7240  		{
  7241  			filter: &telemetry.AccessLogging_Filter{
  7242  				Expression: ")++++",
  7243  			},
  7244  			valid: false,
  7245  		},
  7246  	}
  7247  
  7248  	for _, tc := range cases {
  7249  		t.Run("", func(t *testing.T) {
  7250  			err := validateTelemetryFilter(tc.filter)
  7251  			errFound := err != nil
  7252  			if tc.valid && errFound {
  7253  				t.Errorf("validateTelemetryFilter(%v) produced unexpected error: %v", tc.filter, err)
  7254  			}
  7255  			if !tc.valid && !errFound {
  7256  				t.Errorf("validateTelemetryFilter(%v) did not produce expected error", tc.filter)
  7257  			}
  7258  		})
  7259  	}
  7260  }
  7261  
  7262  func TestValidateWasmPlugin(t *testing.T) {
  7263  	tests := []struct {
  7264  		name    string
  7265  		in      proto.Message
  7266  		out     string
  7267  		warning string
  7268  	}{
  7269  		{"empty", &extensions.WasmPlugin{}, "url field needs to be set", ""},
  7270  		{"invalid message", &networking.Server{}, "cannot cast", ""},
  7271  		{
  7272  			"wrong scheme",
  7273  			&extensions.WasmPlugin{
  7274  				Url: "ftp://test.com/test",
  7275  			},
  7276  			"unsupported scheme", "",
  7277  		},
  7278  		{
  7279  			"valid http",
  7280  			&extensions.WasmPlugin{
  7281  				Url: "http://test.com/test",
  7282  			},
  7283  			"", "",
  7284  		},
  7285  		{
  7286  			"valid http w/ sha",
  7287  			&extensions.WasmPlugin{
  7288  				Url:    "http://test.com/test",
  7289  				Sha256: "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b",
  7290  			},
  7291  			"", "",
  7292  		},
  7293  		{
  7294  			"short sha",
  7295  			&extensions.WasmPlugin{
  7296  				Url:    "http://test.com/test",
  7297  				Sha256: "01ba47",
  7298  			},
  7299  			"sha256 field must be 64 characters long", "",
  7300  		},
  7301  		{
  7302  			"invalid sha",
  7303  			&extensions.WasmPlugin{
  7304  				Url:    "http://test.com/test",
  7305  				Sha256: "test",
  7306  			},
  7307  			"sha256 field must be 64 characters long", "",
  7308  		},
  7309  		{
  7310  			"invalid sha characters",
  7311  			&extensions.WasmPlugin{
  7312  				Url:    "http://test.com/test",
  7313  				Sha256: "01Ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b",
  7314  			},
  7315  			"sha256 field must match [a-f0-9]{64} pattern", "",
  7316  		},
  7317  		{
  7318  			"valid oci",
  7319  			&extensions.WasmPlugin{
  7320  				Url: "oci://test.com/test",
  7321  			},
  7322  			"", "",
  7323  		},
  7324  		{
  7325  			"valid oci no scheme",
  7326  			&extensions.WasmPlugin{
  7327  				Url: "test.com/test",
  7328  			},
  7329  			"", "",
  7330  		},
  7331  		{
  7332  			"invalid vm config - invalid env name",
  7333  			&extensions.WasmPlugin{
  7334  				Url: "test.com/test",
  7335  				VmConfig: &extensions.VmConfig{
  7336  					Env: []*extensions.EnvVar{
  7337  						{
  7338  							Name:      "",
  7339  							ValueFrom: extensions.EnvValueSource_HOST,
  7340  						},
  7341  					},
  7342  				},
  7343  			},
  7344  			"spec.vmConfig.env invalid", "",
  7345  		},
  7346  		{
  7347  			"invalid vm config - duplicate env",
  7348  			&extensions.WasmPlugin{
  7349  				Url: "test.com/test",
  7350  				VmConfig: &extensions.VmConfig{
  7351  					Env: []*extensions.EnvVar{
  7352  						{
  7353  							Name:  "ENV1",
  7354  							Value: "VAL1",
  7355  						},
  7356  						{
  7357  							Name:  "ENV1",
  7358  							Value: "VAL1",
  7359  						},
  7360  					},
  7361  				},
  7362  			},
  7363  			"duplicate env", "",
  7364  		},
  7365  		{
  7366  			"target-ref-good",
  7367  			&extensions.WasmPlugin{
  7368  				Url: "http://test.com/test",
  7369  				TargetRef: &api.PolicyTargetReference{
  7370  					Group: gvk.KubernetesGateway.Group,
  7371  					Kind:  gvk.KubernetesGateway.Kind,
  7372  					Name:  "foo",
  7373  				},
  7374  			},
  7375  			"", "",
  7376  		},
  7377  		{
  7378  			"target-ref-good-service",
  7379  			&extensions.WasmPlugin{
  7380  				Url: "http://test.com/test",
  7381  				TargetRef: &api.PolicyTargetReference{
  7382  					Group: gvk.Service.Group,
  7383  					Kind:  gvk.Service.Kind,
  7384  					Name:  "foo",
  7385  				},
  7386  			},
  7387  			"", "",
  7388  		},
  7389  		{
  7390  			"target-ref-non-empty-namespace",
  7391  			&extensions.WasmPlugin{
  7392  				Url: "http://test.com/test",
  7393  				TargetRef: &api.PolicyTargetReference{
  7394  					Group:     gvk.KubernetesGateway.Group,
  7395  					Kind:      gvk.KubernetesGateway.Kind,
  7396  					Name:      "foo",
  7397  					Namespace: "bar",
  7398  				},
  7399  			},
  7400  			"targetRef namespace must not be set", "",
  7401  		},
  7402  		{
  7403  			"target-ref-empty-name",
  7404  			&extensions.WasmPlugin{
  7405  				Url: "http://test.com/test",
  7406  				TargetRef: &api.PolicyTargetReference{
  7407  					Group: gvk.KubernetesGateway.Group,
  7408  					Kind:  gvk.KubernetesGateway.Kind,
  7409  				},
  7410  			},
  7411  			"targetRef name must be set", "",
  7412  		},
  7413  		{
  7414  			"target-ref-wrong-group",
  7415  			&extensions.WasmPlugin{
  7416  				Url: "http://test.com/test",
  7417  				TargetRef: &api.PolicyTargetReference{
  7418  					Group: "wrong-group",
  7419  					Kind:  gvk.KubernetesGateway.Kind,
  7420  					Name:  "foo",
  7421  				},
  7422  			},
  7423  			fmt.Sprintf("targetRef must be to one of %v but was %s/%s",
  7424  				allowedTargetRefs, "wrong-group", gvk.KubernetesGateway.Kind), "",
  7425  		},
  7426  		{
  7427  			"target-ref-wrong-kind",
  7428  			&extensions.WasmPlugin{
  7429  				Url: "http://test.com/test",
  7430  				TargetRef: &api.PolicyTargetReference{
  7431  					Group: gvk.KubernetesGateway.Group,
  7432  					Kind:  "wrong-kind",
  7433  					Name:  "foo",
  7434  				},
  7435  			},
  7436  			fmt.Sprintf("targetRef must be to one of %v but was %s/%s",
  7437  				allowedTargetRefs, gvk.KubernetesGateway.Group, "wrong-kind"), "",
  7438  		},
  7439  		{
  7440  			"target-ref-and-selector-cannot-both-be-set",
  7441  			&extensions.WasmPlugin{
  7442  				Url: "http://test.com/test",
  7443  				TargetRef: &api.PolicyTargetReference{
  7444  					Group: gvk.KubernetesGateway.Group,
  7445  					Kind:  gvk.KubernetesGateway.Kind,
  7446  					Name:  "foo",
  7447  				},
  7448  				Selector: &api.WorkloadSelector{
  7449  					MatchLabels: map[string]string{
  7450  						"app": "httpbin",
  7451  					},
  7452  				},
  7453  			},
  7454  			"only one of targetRefs or workloadSelector can be set", "",
  7455  		},
  7456  	}
  7457  	for _, tt := range tests {
  7458  		t.Run(tt.name, func(t *testing.T) {
  7459  			warn, err := ValidateWasmPlugin(config.Config{
  7460  				Meta: config.Meta{
  7461  					Name:      someName,
  7462  					Namespace: someNamespace,
  7463  				},
  7464  				Spec: tt.in,
  7465  			})
  7466  			checkValidationMessage(t, warn, err, tt.warning, tt.out)
  7467  		})
  7468  	}
  7469  }
  7470  
  7471  func TestValidateHTTPHeaderValue(t *testing.T) {
  7472  	cases := []struct {
  7473  		input    string
  7474  		expected error
  7475  	}{
  7476  		{
  7477  			input:    "foo",
  7478  			expected: nil,
  7479  		},
  7480  		{
  7481  			input:    "%HOSTNAME%",
  7482  			expected: nil,
  7483  		},
  7484  		{
  7485  			input:    "100%%",
  7486  			expected: nil,
  7487  		},
  7488  		{
  7489  			input:    "prefix %HOSTNAME% suffix",
  7490  			expected: nil,
  7491  		},
  7492  		{
  7493  			input:    "%DOWNSTREAM_PEER_CERT_V_END(%b %d %H:%M:%S %Y %Z)%",
  7494  			expected: nil,
  7495  		},
  7496  		{
  7497  			input: "%DYNAMIC_METADATA(com.test.my_filter)%",
  7498  		},
  7499  		{
  7500  			input:    "%START_TIME%%",
  7501  			expected: errors.New("header value configuration %START_TIME%% is invalid"),
  7502  		},
  7503  		{
  7504  			input:    "abc%123",
  7505  			expected: errors.New("header value configuration abc%123 is invalid"),
  7506  		},
  7507  	}
  7508  
  7509  	for _, tc := range cases {
  7510  		t.Run(tc.input, func(t *testing.T) {
  7511  			got := ValidateHTTPHeaderValue(tc.input)
  7512  			if tc.expected == nil {
  7513  				assert.NoError(t, got)
  7514  			} else {
  7515  				assert.Error(t, got)
  7516  			}
  7517  		})
  7518  	}
  7519  }