istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/config/security/security_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 security_test 16 17 import ( 18 "reflect" 19 "testing" 20 21 "istio.io/istio/pkg/config/security" 22 "istio.io/istio/pkg/test/util/assert" 23 ) 24 25 func TestParseJwksURI(t *testing.T) { 26 cases := []struct { 27 in string 28 expected security.JwksInfo 29 expectedError bool 30 }{ 31 { 32 in: "foo.bar.com", 33 expectedError: true, 34 }, 35 { 36 in: "tcp://foo.bar.com:abc", 37 expectedError: true, 38 }, 39 { 40 in: "http://foo.bar.com:abc", 41 expectedError: true, 42 }, 43 { 44 in: "http://foo.bar.com", 45 expected: security.JwksInfo{ 46 Hostname: "foo.bar.com", 47 Scheme: "http", 48 Port: 80, 49 UseSSL: false, 50 }, 51 }, 52 { 53 in: "https://foo.bar.com", 54 expected: security.JwksInfo{ 55 Hostname: "foo.bar.com", 56 Scheme: "https", 57 Port: 443, 58 UseSSL: true, 59 }, 60 }, 61 { 62 in: "http://foo.bar.com:1234", 63 expected: security.JwksInfo{ 64 Hostname: "foo.bar.com", 65 Scheme: "http", 66 Port: 1234, 67 UseSSL: false, 68 }, 69 }, 70 { 71 in: "https://foo.bar.com:1234/secure/key", 72 expected: security.JwksInfo{ 73 Hostname: "foo.bar.com", 74 Scheme: "https", 75 Port: 1234, 76 UseSSL: true, 77 }, 78 }, 79 } 80 for _, c := range cases { 81 actual, err := security.ParseJwksURI(c.in) 82 if c.expectedError == (err == nil) { 83 t.Fatalf("ParseJwksURI(%s): expected error (%v), got (%v)", c.in, c.expectedError, err) 84 } 85 if !reflect.DeepEqual(c.expected, actual) { 86 t.Fatalf("expected %+v, got %+v", c.expected, actual) 87 } 88 } 89 } 90 91 func TestValidateCondition(t *testing.T) { 92 cases := []struct { 93 key string 94 values []string 95 wantError bool 96 }{ 97 { 98 key: "request.headers[:authority]", 99 values: []string{"productpage", ""}, 100 wantError: true, 101 }, 102 { 103 key: "request.headers[:authority]", 104 values: []string{"productpage"}, 105 }, 106 { 107 key: "request.headers[]", 108 values: []string{"productpage"}, 109 wantError: true, 110 }, 111 { 112 key: "source.ip", 113 values: []string{"1.2.3.4", "5.6.7.0/24"}, 114 }, 115 { 116 key: "source.ip", 117 values: []string{"a.b.c.d"}, 118 wantError: true, 119 }, 120 { 121 key: "remote.ip", 122 values: []string{"1.2.3.4", "5.6.7.0/24"}, 123 }, 124 { 125 key: "remote.ip", 126 values: []string{"a.b.c.d"}, 127 wantError: true, 128 }, 129 { 130 key: "source.namespace", 131 values: []string{"value"}, 132 }, 133 { 134 key: "source.user", 135 values: []string{"value"}, 136 wantError: true, 137 }, 138 { 139 key: "source.principal", 140 values: []string{"value"}, 141 }, 142 { 143 key: "request.auth.principal", 144 values: []string{"value"}, 145 }, 146 { 147 key: "request.auth.audiences", 148 values: []string{"value"}, 149 }, 150 { 151 key: "request.auth.presenter", 152 values: []string{"value"}, 153 }, 154 { 155 key: "request.auth.claims[id]", 156 values: []string{"123"}, 157 }, 158 { 159 key: "request.auth.claims[]", 160 values: []string{"value"}, 161 wantError: true, 162 }, 163 { 164 key: "destination.ip", 165 values: []string{"1.2.3.4", "5.6.7.0/24"}, 166 }, 167 { 168 key: "destination.ip", 169 values: []string{"a.b.c.d"}, 170 wantError: true, 171 }, 172 { 173 key: "destination.port", 174 values: []string{"80", "90"}, 175 }, 176 { 177 key: "destination.port", 178 values: []string{"80", "x"}, 179 wantError: true, 180 }, 181 { 182 key: "destination.labels[app]", 183 values: []string{"value"}, 184 wantError: true, 185 }, 186 { 187 key: "destination.name", 188 values: []string{"value"}, 189 wantError: true, 190 }, 191 { 192 key: "destination.namespace", 193 values: []string{"value"}, 194 wantError: true, 195 }, 196 { 197 key: "destination.user", 198 values: []string{"value"}, 199 wantError: true, 200 }, 201 { 202 key: "connection.sni", 203 values: []string{"value"}, 204 }, 205 { 206 key: "experimental.envoy.filters.a.b[c]", 207 values: []string{"value"}, 208 }, 209 { 210 key: "experimental.envoy.filters.a.b.x", 211 values: []string{"value"}, 212 wantError: true, 213 }, 214 } 215 for _, c := range cases { 216 err := security.ValidateAttribute(c.key, c.values) 217 if c.wantError == (err == nil) { 218 t.Fatalf("ValidateAttribute(%s): want error (%v) but got (%v)", c.key, c.wantError, err) 219 } 220 } 221 } 222 223 func TestCheckValidPathTemplate(t *testing.T) { 224 cases := []struct { 225 name string 226 values []string 227 wantError bool 228 }{ 229 { 230 name: "valid path template - matchOneTemplate", 231 values: []string{"/{*}/foo"}, 232 }, 233 { 234 name: "valid path template - matchAnyTemplate", 235 values: []string{"/foo/{**}/bar"}, 236 }, 237 { 238 name: "valid path template - matchAnyTemplate at end", 239 values: []string{"/foo/bar/{**}"}, 240 }, 241 { 242 name: "unsupported path template - matchAnyTemplate with additional chars", 243 values: []string{"/foo/{**}buzz/bar"}, 244 wantError: true, 245 }, 246 { 247 name: "unsupported path template - empty curly braces", 248 values: []string{"/{*}/foo/{}/bar"}, 249 wantError: true, 250 }, 251 { 252 name: "unsupported path template - matchOneTemplate with `*`", 253 values: []string{"/foo/{*}/bar/*"}, 254 wantError: true, 255 }, 256 { 257 name: "unsupported path template - matchOneTemplate with `**`", 258 values: []string{"/foo/{*}/bar/**/buzz"}, 259 wantError: true, 260 }, 261 { 262 name: "unsupported path/path template - named var: {buzz}", 263 values: []string{"/foo/{buzz}/bar"}, 264 wantError: true, 265 }, 266 { 267 name: "unsupported path/path template - only named var: {buzz}", 268 values: []string{"/{buzz}"}, 269 wantError: true, 270 }, 271 { 272 name: "unsupported path template - matchAnyTemplate with named var: {buzz}", 273 values: []string{"/foo/{buzz}/bar/{**}"}, 274 wantError: true, 275 }, 276 { 277 name: "unsupported path template - matchAnyTemplate with named var: {buzz=*}", 278 values: []string{"/foo/{buzz=*}/bar/{**}"}, 279 wantError: true, 280 }, 281 { 282 name: "unsupported path template - matchAnyTemplate with named var: {buzz=**}", 283 values: []string{"/{*}/foo/{buzz=**}/bar"}, 284 wantError: true, 285 }, 286 { 287 name: "unsupported path template - matchAnyTemplate with additional chars at end", 288 values: []string{"/{*}/foo/{**}bar"}, 289 wantError: true, 290 }, 291 { 292 name: "unsupported path template - matchOneTemplate with file extension", 293 values: []string{"/{*}/foo/{*}.txt"}, 294 wantError: true, 295 }, 296 { 297 name: "unsupported path template - matchOneTemplate with unmatched open curly brace", 298 values: []string{"/{*}/foo/{temp"}, 299 wantError: true, 300 }, 301 { 302 name: "unsupported path template - matchOneTemplate with unmatched closed curly brace", 303 values: []string{"/{*}/foo/temp}/bar"}, 304 wantError: true, 305 }, 306 { 307 name: "unsupported path template - matchOneTemplate with unmatched closed curly brace and `*`", 308 values: []string{"/{*}/foo/temp}/*"}, 309 wantError: true, 310 }, 311 } 312 for _, c := range cases { 313 err := security.CheckValidPathTemplate(c.name, c.values) 314 if c.wantError == (err == nil) { 315 t.Fatalf("CheckValidPathTemplate(%s): want error (%v) but got (%v)", c.name, c.wantError, err) 316 } 317 } 318 } 319 320 func TestContainsPathTemplate(t *testing.T) { 321 testCases := []struct { 322 name string 323 path string 324 isPathTemplate bool 325 }{ 326 { 327 name: "matchOneOnly", 328 path: "foo/bar/{*}", 329 isPathTemplate: true, 330 }, 331 { 332 name: "matchAnyOnly", 333 path: "foo/{**}/bar", 334 isPathTemplate: true, 335 }, 336 { 337 name: "matchAnyAndOne", 338 path: "{*}/bar/{**}", 339 isPathTemplate: true, 340 }, 341 { 342 name: "stringMatch", 343 path: "foo/bar/*", 344 isPathTemplate: false, 345 }, 346 { 347 name: "namedVariable", 348 path: "foo/bar/{buzz}", 349 isPathTemplate: false, 350 }, 351 } 352 353 for _, tc := range testCases { 354 t.Run(tc.name, func(t *testing.T) { 355 pathTemplate := security.ContainsPathTemplate(tc.path) 356 assert.Equal(t, tc.isPathTemplate, pathTemplate) 357 }) 358 } 359 }