istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/security/authz/matcher/header_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 matcher
    16  
    17  import (
    18  	"regexp"
    19  	"testing"
    20  
    21  	routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
    22  	matcher "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"
    23  	"github.com/google/go-cmp/cmp"
    24  	"google.golang.org/protobuf/testing/protocmp"
    25  )
    26  
    27  func TestHeaderMatcher(t *testing.T) {
    28  	testCases := []struct {
    29  		Name   string
    30  		K      string
    31  		V      string
    32  		Expect *routepb.HeaderMatcher
    33  	}{
    34  		{
    35  			Name: "exact match",
    36  			K:    ":path",
    37  			V:    "/productpage",
    38  			Expect: &routepb.HeaderMatcher{
    39  				Name: ":path",
    40  				HeaderMatchSpecifier: &routepb.HeaderMatcher_StringMatch{
    41  					StringMatch: StringMatcherExact("/productpage", false),
    42  				},
    43  			},
    44  		},
    45  		{
    46  			Name: "suffix match",
    47  			K:    ":path",
    48  			V:    "*/productpage*",
    49  			Expect: &routepb.HeaderMatcher{
    50  				Name: ":path",
    51  				HeaderMatchSpecifier: &routepb.HeaderMatcher_StringMatch{
    52  					StringMatch: StringMatcherSuffix("/productpage*", false),
    53  				},
    54  			},
    55  		},
    56  		{
    57  			Name: "prefix match",
    58  			K:    ":path",
    59  			V:    "/productpage*",
    60  			Expect: &routepb.HeaderMatcher{
    61  				Name: ":path",
    62  				HeaderMatchSpecifier: &routepb.HeaderMatcher_StringMatch{
    63  					StringMatch: StringMatcherPrefix("/productpage", false),
    64  				},
    65  			},
    66  		},
    67  		{
    68  			Name: "* match",
    69  			K:    ":path",
    70  			V:    "*",
    71  			Expect: &routepb.HeaderMatcher{
    72  				Name: ":path",
    73  				HeaderMatchSpecifier: &routepb.HeaderMatcher_PresentMatch{
    74  					PresentMatch: true,
    75  				},
    76  			},
    77  		},
    78  	}
    79  
    80  	for _, tc := range testCases {
    81  		t.Run(tc.Name, func(t *testing.T) {
    82  			actual := HeaderMatcher(tc.K, tc.V)
    83  			if !cmp.Equal(tc.Expect, actual, protocmp.Transform()) {
    84  				t.Errorf("expecting %v, but got %v", tc.Expect, actual)
    85  			}
    86  		})
    87  	}
    88  }
    89  
    90  func TestHostMatcherWithRegex(t *testing.T) {
    91  	testCases := []struct {
    92  		Name   string
    93  		K      string
    94  		V      string
    95  		Expect *routepb.HeaderMatcher
    96  	}{
    97  		{
    98  			Name: "present match",
    99  			K:    ":authority",
   100  			V:    "*",
   101  			Expect: &routepb.HeaderMatcher{
   102  				Name:                 ":authority",
   103  				HeaderMatchSpecifier: &routepb.HeaderMatcher_PresentMatch{PresentMatch: true},
   104  			},
   105  		},
   106  		{
   107  			Name: "prefix match",
   108  			K:    ":authority",
   109  			V:    "*.example.com",
   110  			Expect: &routepb.HeaderMatcher{
   111  				Name: ":authority",
   112  				HeaderMatchSpecifier: &routepb.HeaderMatcher_StringMatch{
   113  					StringMatch: StringMatcherRegex(`(?i).*\.example\.com`),
   114  				},
   115  			},
   116  		},
   117  		{
   118  			Name: "suffix match",
   119  			K:    ":authority",
   120  			V:    "example.*",
   121  			Expect: &routepb.HeaderMatcher{
   122  				Name: ":authority",
   123  				HeaderMatchSpecifier: &routepb.HeaderMatcher_StringMatch{
   124  					StringMatch: StringMatcherRegex(`(?i)example\..*`),
   125  				},
   126  			},
   127  		},
   128  		{
   129  			Name: "exact match",
   130  			K:    ":authority",
   131  			V:    "example.com",
   132  			Expect: &routepb.HeaderMatcher{
   133  				Name: ":authority",
   134  				HeaderMatchSpecifier: &routepb.HeaderMatcher_StringMatch{
   135  					StringMatch: StringMatcherRegex(`(?i)example\.com`),
   136  				},
   137  			},
   138  		},
   139  	}
   140  
   141  	for _, tc := range testCases {
   142  		t.Run(tc.Name, func(t *testing.T) {
   143  			actual := HostMatcherWithRegex(tc.K, tc.V)
   144  			if re := actual.GetStringMatch().GetSafeRegex().GetRegex(); re != "" {
   145  				_, err := regexp.Compile(re)
   146  				if err != nil {
   147  					t.Errorf("failed to compile regex %s: %v", re, err)
   148  				}
   149  			}
   150  			if !cmp.Equal(tc.Expect, actual, protocmp.Transform()) {
   151  				t.Errorf("expecting %v, but got %v", tc.Expect, actual)
   152  			}
   153  		})
   154  	}
   155  }
   156  
   157  func TestHostMatcher(t *testing.T) {
   158  	testCases := []struct {
   159  		Name   string
   160  		K      string
   161  		V      string
   162  		Expect *routepb.HeaderMatcher
   163  	}{
   164  		{
   165  			Name: "present match",
   166  			K:    ":authority",
   167  			V:    "*",
   168  			Expect: &routepb.HeaderMatcher{
   169  				Name: ":authority",
   170  				HeaderMatchSpecifier: &routepb.HeaderMatcher_PresentMatch{
   171  					PresentMatch: true,
   172  				},
   173  			},
   174  		},
   175  		{
   176  			Name: "suffix match",
   177  			K:    ":authority",
   178  			V:    "*.example.com",
   179  			Expect: &routepb.HeaderMatcher{
   180  				Name: ":authority",
   181  				HeaderMatchSpecifier: &routepb.HeaderMatcher_StringMatch{
   182  					StringMatch: StringMatcherSuffix(".example.com", true),
   183  				},
   184  			},
   185  		},
   186  		{
   187  			Name: "prefix match",
   188  			K:    ":authority",
   189  			V:    "example.*",
   190  			Expect: &routepb.HeaderMatcher{
   191  				Name: ":authority",
   192  				HeaderMatchSpecifier: &routepb.HeaderMatcher_StringMatch{
   193  					StringMatch: StringMatcherPrefix("example.", true),
   194  				},
   195  			},
   196  		},
   197  		{
   198  			Name: "exact match",
   199  			K:    ":authority",
   200  			V:    "example.com",
   201  			Expect: &routepb.HeaderMatcher{
   202  				Name: ":authority",
   203  				HeaderMatchSpecifier: &routepb.HeaderMatcher_StringMatch{
   204  					StringMatch: StringMatcherExact("example.com", true),
   205  				},
   206  			},
   207  		},
   208  	}
   209  
   210  	for _, tc := range testCases {
   211  		t.Run(tc.Name, func(t *testing.T) {
   212  			actual := HostMatcher(tc.K, tc.V)
   213  			if !cmp.Equal(tc.Expect, actual, protocmp.Transform()) {
   214  				t.Errorf("expecting %v, but got %v", tc.Expect, actual)
   215  			}
   216  		})
   217  	}
   218  }
   219  
   220  func TestPathMatcher(t *testing.T) {
   221  	testCases := []struct {
   222  		Name   string
   223  		V      string
   224  		Expect *matcher.PathMatcher
   225  	}{
   226  		{
   227  			Name: "exact match",
   228  			V:    "/productpage",
   229  			Expect: &matcher.PathMatcher{
   230  				Rule: &matcher.PathMatcher_Path{
   231  					Path: StringMatcherExact("/productpage", false),
   232  				},
   233  			},
   234  		},
   235  		{
   236  			Name: "prefix match",
   237  			V:    "/prefix*",
   238  			Expect: &matcher.PathMatcher{
   239  				Rule: &matcher.PathMatcher_Path{
   240  					Path: StringMatcherPrefix("/prefix", false),
   241  				},
   242  			},
   243  		},
   244  		{
   245  			Name: "suffix match",
   246  			V:    "*suffix",
   247  			Expect: &matcher.PathMatcher{
   248  				Rule: &matcher.PathMatcher_Path{
   249  					Path: StringMatcherSuffix("suffix", false),
   250  				},
   251  			},
   252  		},
   253  		{
   254  			Name: "wildcard match",
   255  			V:    "*",
   256  			Expect: &matcher.PathMatcher{
   257  				Rule: &matcher.PathMatcher_Path{
   258  					Path: StringMatcherRegex(".+"),
   259  				},
   260  			},
   261  		},
   262  	}
   263  
   264  	for _, tc := range testCases {
   265  		t.Run(tc.Name, func(t *testing.T) {
   266  			actual := PathMatcher(tc.V)
   267  			if !cmp.Equal(tc.Expect, actual, protocmp.Transform()) {
   268  				t.Errorf("expecting %v, but got %v", tc.Expect, actual)
   269  			}
   270  		})
   271  	}
   272  }