VYPR
Moderate severityNVD Advisory· Published Oct 1, 2020· Updated Aug 4, 2024

CVE-2020-16844

CVE-2020-16844

Description

In Istio 1.5.0 though 1.5.8 and Istio 1.6.0 through 1.6.7, when users specify an AuthorizationPolicy resource with DENY actions using wildcard suffixes (e.g. *-some-suffix) for source principals or namespace fields, callers will never be denied access, bypassing the intended policy.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

In Istio 1.5.0-1.5.8 and 1.6.0-1.6.7, AuthorizationPolicy DENY rules with wildcard suffixes fail to deny access, bypassing the intended policy.

Vulnerability

In Istio versions 1.5.0 through 1.5.8 and 1.6.0 through 1.6.7, the authorization policy enforcement contains a bug when processing DENY actions with wildcard suffixes in source principal or namespace fields. Specifically, when a user specifies a pattern like *-some-suffix in the principal or namespace field of a DENY rule, the wildcard suffix matching logic is not properly applied, causing the rule to never match and thus never deny access. This was identified in the Istio authorization engine's handling of TCP and HTTP filters [1][2].

Exploitation

An attacker can exploit this vulnerability by crafting requests that should be denied by a policy with a wildcard suffix. For example, if a policy denies all principals ending with -malicious, an attacker using a principal like user-malicious would not be blocked because the suffix match fails. The attack requires network access to a service protected by such a misconfigured AuthorizationPolicy. No special privileges are needed beyond the ability to send requests [1].

Impact

Successful exploitation allows an attacker to bypass intended access controls, gaining unauthorized access to protected services. Since the DENY rule is effectively ignored, any principal or namespace that should be denied based on a suffix pattern can instead reach the resource. This can lead to data exposure, privilege escalation, or other security breaches depending on the protected assets [2].

Mitigation

The issue has been fixed in Istio releases 1.5.9 and 1.6.8. Users are strongly advised to upgrade to these or later versions. For those unable to upgrade immediately, removing wildcard suffixes from DENY policies or using prefixes instead is a temporary workaround. The fix corrects the suffix matching logic to properly evaluate patterns like *-suffix [3].

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
istio.io/istioGo
>= 1.5.0, < 1.5.91.5.9
istio.io/istioGo
>= 1.6.0, < 1.6.81.6.8

Affected products

2

Patches

2
72d2e135374f

[release-1.5] fix authz suffix matching in TCP (#30)

https://github.com/istio/istioYangmin ZhuJul 25, 2020via ghsa
8 files changed · +193 41
  • pilot/pkg/security/authz/builder/testdata/v1beta1/action-deny-HTTP-for-TCP-filter-in.yaml+8 8 modified
    @@ -53,13 +53,13 @@ spec:
       # rule[8] `from`: all fields, `to`: all fields, `when`: all fields.
       - from:
         - source:
    -        principals: ["principal"]
    +        principals: ["principal", "*principal-suffix", "principal-prefix*", "*"]
             requestPrincipals: ["requestPrincipals"]
    -        namespaces: ["ns"]
    +        namespaces: ["ns", "*ns-suffix", "ns-prefix*", "*"]
             ipBlocks: ["1.2.3.4"]
    -        notPrincipals: ["not-principal"]
    +        notPrincipals: ["not-principal", "*not-principal-suffix", "not-principal-prefix*", "*"]
             notRequestPrincipals: ["not-requestPrincipals"]
    -        notNamespaces: ["not-ns"]
    +        notNamespaces: ["not-ns", "*not-ns-suffix", "not-ns-prefix*", "*"]
             notIpBlocks: ["9.0.0.1"]
         to:
         - operation:
    @@ -79,11 +79,11 @@ spec:
             values: ["10.10.10.10"]
             notValues: ["90.10.10.10"]
           - key: "source.namespace"
    -        values: ["ns"]
    -        notValues: ["not-ns"]
    +        values: ["ns", "*ns-suffix", "ns-prefix*", "*"]
    +        notValues: ["not-ns", "*not-ns-suffix", "not-ns-prefix*", "*"]
           - key: "source.principal"
    -        values: ["principal"]
    -        notValues: ["not-principal"]
    +        values: ["principal", "*principal-suffix", "principal-prefix*", "*"]
    +        notValues: ["not-principal", "*not-principal-suffix", "not-principal-prefix*", "*"]
           - key: "request.auth.principal"
             values: ["requestPrincipals"]
             notValues: ["not-requestPrincipals"]
    
  • pilot/pkg/security/authz/builder/testdata/v1beta1/action-deny-HTTP-for-TCP-filter-out.yaml+112 0 modified
    @@ -156,19 +156,60 @@ rules:
                   - authenticated:
                       principalName:
                         exact: spiffe://principal
    +              - authenticated:
    +                  principalName:
    +                    safeRegex:
    +                      googleRe2: {}
    +                      regex: spiffe://.*principal-suffix
    +              - authenticated:
    +                  principalName:
    +                    prefix: spiffe://principal-prefix
    +              - authenticated:
    +                  principalName:
    +                    safeRegex:
    +                      googleRe2: {}
    +                      regex: .+
               - notId:
                   orIds:
                     ids:
                     - authenticated:
                         principalName:
                           exact: spiffe://not-principal
    +                - authenticated:
    +                    principalName:
    +                      safeRegex:
    +                        googleRe2: {}
    +                        regex: spiffe://.*not-principal-suffix
    +                - authenticated:
    +                    principalName:
    +                      prefix: spiffe://not-principal-prefix
    +                - authenticated:
    +                    principalName:
    +                      safeRegex:
    +                        googleRe2: {}
    +                        regex: .+
               - orIds:
                   ids:
                   - authenticated:
                       principalName:
                         safeRegex:
                           googleRe2: {}
                           regex: .*/ns/ns/.*
    +              - authenticated:
    +                  principalName:
    +                    safeRegex:
    +                      googleRe2: {}
    +                      regex: .*/ns/.*ns-suffix/.*
    +              - authenticated:
    +                  principalName:
    +                    safeRegex:
    +                      googleRe2: {}
    +                      regex: .*/ns/ns-prefix.*/.*
    +              - authenticated:
    +                  principalName:
    +                    safeRegex:
    +                      googleRe2: {}
    +                      regex: .*/ns/.*/.*
               - notId:
                   orIds:
                     ids:
    @@ -177,6 +218,21 @@ rules:
                           safeRegex:
                             googleRe2: {}
                             regex: .*/ns/not-ns/.*
    +                - authenticated:
    +                    principalName:
    +                      safeRegex:
    +                        googleRe2: {}
    +                        regex: .*/ns/.*not-ns-suffix/.*
    +                - authenticated:
    +                    principalName:
    +                      safeRegex:
    +                        googleRe2: {}
    +                        regex: .*/ns/not-ns-prefix.*/.*
    +                - authenticated:
    +                    principalName:
    +                      safeRegex:
    +                        googleRe2: {}
    +                        regex: .*/ns/.*/.*
               - orIds:
                   ids:
                   - sourceIp:
    @@ -206,6 +262,21 @@ rules:
                         safeRegex:
                           googleRe2: {}
                           regex: .*/ns/ns/.*
    +              - authenticated:
    +                  principalName:
    +                    safeRegex:
    +                      googleRe2: {}
    +                      regex: .*/ns/.*ns-suffix/.*
    +              - authenticated:
    +                  principalName:
    +                    safeRegex:
    +                      googleRe2: {}
    +                      regex: .*/ns/ns-prefix.*/.*
    +              - authenticated:
    +                  principalName:
    +                    safeRegex:
    +                      googleRe2: {}
    +                      regex: .*/ns/.*/.*
               - notId:
                   orIds:
                     ids:
    @@ -214,14 +285,55 @@ rules:
                           safeRegex:
                             googleRe2: {}
                             regex: .*/ns/not-ns/.*
    +                - authenticated:
    +                    principalName:
    +                      safeRegex:
    +                        googleRe2: {}
    +                        regex: .*/ns/.*not-ns-suffix/.*
    +                - authenticated:
    +                    principalName:
    +                      safeRegex:
    +                        googleRe2: {}
    +                        regex: .*/ns/not-ns-prefix.*/.*
    +                - authenticated:
    +                    principalName:
    +                      safeRegex:
    +                        googleRe2: {}
    +                        regex: .*/ns/.*/.*
               - orIds:
                   ids:
                   - authenticated:
                       principalName:
                         exact: spiffe://principal
    +              - authenticated:
    +                  principalName:
    +                    safeRegex:
    +                      googleRe2: {}
    +                      regex: spiffe://.*principal-suffix
    +              - authenticated:
    +                  principalName:
    +                    prefix: spiffe://principal-prefix
    +              - authenticated:
    +                  principalName:
    +                    safeRegex:
    +                      googleRe2: {}
    +                      regex: .+
               - notId:
                   orIds:
                     ids:
                     - authenticated:
                         principalName:
                           exact: spiffe://not-principal
    +                - authenticated:
    +                    principalName:
    +                      safeRegex:
    +                        googleRe2: {}
    +                        regex: spiffe://.*not-principal-suffix
    +                - authenticated:
    +                    principalName:
    +                      prefix: spiffe://not-principal-prefix
    +                - authenticated:
    +                    principalName:
    +                      safeRegex:
    +                        googleRe2: {}
    +                        regex: .+
    
  • pilot/pkg/security/authz/model/matcher/string.go+7 4 modified
    @@ -52,11 +52,14 @@ func StringMatcherWithPrefix(v, prefix string, treatWildcardAsRequired bool) *en
     		}
     		return StringMatcherRegex(".*")
     	case strings.HasPrefix(v, "*"):
    -		return &envoy_matcher.StringMatcher{
    -			MatchPattern: &envoy_matcher.StringMatcher_Suffix{
    -				Suffix: prefix + strings.TrimPrefix(v, "*"),
    -			},
    +		if prefix == "" {
    +			return &envoy_matcher.StringMatcher{
    +				MatchPattern: &envoy_matcher.StringMatcher_Suffix{
    +					Suffix: strings.TrimPrefix(v, "*"),
    +				},
    +			}
     		}
    +		return StringMatcherRegex(prefix + ".*" + strings.TrimPrefix(v, "*"))
     	case strings.HasSuffix(v, "*"):
     		return &envoy_matcher.StringMatcher{
     			MatchPattern: &envoy_matcher.StringMatcher_Prefix{
    
  • pilot/pkg/security/authz/model/matcher/string_test.go+23 12 modified
    @@ -25,42 +25,53 @@ func TestStringMatcherWithPrefix(t *testing.T) {
     	testCases := []struct {
     		name                    string
     		v                       string
    +		prefix                  string
     		treatWildcardAsRequired bool
     		want                    *envoy_matcher.StringMatcher
     	}{
     		{
     			name:                    "wildcardAsRequired",
     			v:                       "*",
    +			prefix:                  "abc",
     			treatWildcardAsRequired: true,
     			want:                    StringMatcherRegex(".+"),
     		},
     		{
    -			name:                    "wildcard",
    -			v:                       "*",
    -			treatWildcardAsRequired: false,
    -			want:                    StringMatcherRegex(".*"),
    +			name:   "wildcard",
    +			v:      "*",
    +			prefix: "abc",
    +			want:   StringMatcherRegex(".*"),
     		},
     		{
    -			name: "prefix",
    -			v:    "-prefix-*",
    +			name:   "prefix",
    +			v:      "-prefix-*",
    +			prefix: "abc",
     			want: &envoy_matcher.StringMatcher{
     				MatchPattern: &envoy_matcher.StringMatcher_Prefix{
     					Prefix: "abc-prefix-",
     				},
     			},
     		},
     		{
    -			name: "suffix",
    -			v:    "*-suffix",
    +			name:   "suffix-empty-prefix",
    +			v:      "*-suffix",
    +			prefix: "",
     			want: &envoy_matcher.StringMatcher{
     				MatchPattern: &envoy_matcher.StringMatcher_Suffix{
    -					Suffix: "abc-suffix",
    +					Suffix: "-suffix",
     				},
     			},
     		},
     		{
    -			name: "exact",
    -			v:    "-exact",
    +			name:   "suffix",
    +			v:      "*-suffix",
    +			prefix: "abc",
    +			want:   StringMatcherRegex("abc.*-suffix"),
    +		},
    +		{
    +			name:   "exact",
    +			v:      "-exact",
    +			prefix: "abc",
     			want: &envoy_matcher.StringMatcher{
     				MatchPattern: &envoy_matcher.StringMatcher_Exact{
     					Exact: "abc-exact",
    @@ -71,7 +82,7 @@ func TestStringMatcherWithPrefix(t *testing.T) {
     
     	for _, tc := range testCases {
     		t.Run(tc.name, func(t *testing.T) {
    -			actual := StringMatcherWithPrefix(tc.v, "abc", tc.treatWildcardAsRequired)
    +			actual := StringMatcherWithPrefix(tc.v, tc.prefix, tc.treatWildcardAsRequired)
     			if !reflect.DeepEqual(*actual, *tc.want) {
     				t.Errorf("want %s but got %s", tc.want.String(), actual.String())
     			}
    
  • pilot/pkg/security/authz/model/principal.go+2 4 modified
    @@ -253,15 +253,13 @@ func (principal *Principal) forKeyValue(key, value string, forTCPFilter bool) *e
     		}
     		return principalSourceIP(cidr)
     	case attrSrcNamespace == key:
    +		value = strings.Replace(value, "*", ".*", -1)
    +		m := matcher.StringMatcherRegex(fmt.Sprintf(".*/ns/%s/.*", value))
     		if forTCPFilter {
    -			regex := fmt.Sprintf(".*/ns/%s/.*", value)
    -			m := matcher.StringMatcherRegex(regex)
     			return principalAuthenticated(m)
     		}
     		// Proxy doesn't have attrSrcNamespace directly, but the information is encoded in attrSrcPrincipal
     		// with format: cluster.local/ns/{NAMESPACE}/sa/{SERVICE-ACCOUNT}.
    -		value = strings.Replace(value, "*", ".*", -1)
    -		m := matcher.StringMatcherRegex(fmt.Sprintf(".*/ns/%s/.*", value))
     		metadata := matcher.MetadataStringMatcher(authn_model.AuthnFilterName, attrSrcPrincipal, m)
     		return principalMetadata(metadata)
     	case attrSrcPrincipal == key:
    
  • tests/integration/security/rbac_v1beta1_test.go+24 11 modified
    @@ -684,10 +684,15 @@ func TestV1beta1_TCP(t *testing.T) {
     					InstancePort: 8091,
     				},
     				{
    -					Name:         "tcp",
    +					Name:         "tcp-8092",
     					Protocol:     protocol.TCP,
     					InstancePort: 8092,
     				},
    +				{
    +					Name:         "tcp-8093",
    +					Protocol:     protocol.TCP,
    +					InstancePort: 8093,
    +				},
     			}
     			echoboot.NewBuilderOrFail(t, ctx).
     				With(&x, util.EchoConfig("x", ns2, false, nil, g, p)).
    @@ -752,38 +757,46 @@ func TestV1beta1_TCP(t *testing.T) {
     				// The policy on workload b denies request with path "/data" to port 8090:
     				// - request to port http-8090 should be denied because both path and port are matched.
     				// - request to port http-8091 should be allowed because the port is not matched.
    -				// - request to port tcp should be allowed because the port is not matched.
    +				// - request to port tcp-8092 should be allowed because the port is not matched.
     				newTestCase(a, b, "http-8090", false),
     				newTestCase(a, b, "http-8091", true),
    -				newTestCase(a, b, "tcp", true),
    +				newTestCase(a, b, "tcp-8092", true),
     
     				// The policy on workload c denies request to port 8090:
     				// - request to port http-8090 should be denied because the port is matched.
     				// - request to http port 8091 should be allowed because the port is not matched.
     				// - request to tcp port 8092 should be allowed because the port is not matched.
    +				// - request from b to tcp port 8092 should be allowed by default.
    +				// - request from b to tcp port 8093 should be denied because the principal is matched.
    +				// - request from x to tcp port 8092 should be denied because the namespace is matched.
    +				// - request from x to tcp port 8093 should be allowed by default.
     				newTestCase(a, c, "http-8090", false),
     				newTestCase(a, c, "http-8091", true),
    -				newTestCase(a, c, "tcp", true),
    +				newTestCase(a, c, "tcp-8092", true),
    +				newTestCase(b, c, "tcp-8092", true),
    +				newTestCase(b, c, "tcp-8093", false),
    +				newTestCase(x, c, "tcp-8092", false),
    +				newTestCase(x, c, "tcp-8093", true),
     
     				// The policy on workload d denies request from service account a and workloads in namespace 2:
     				// - request from a to d should be denied because it has service account a.
     				// - request from b to d should be allowed.
     				// - request from c to d should be allowed.
     				// - request from x to a should be allowed because there is no policy on a.
     				// - request from x to d should be denied because it's in namespace 2.
    -				newTestCase(a, d, "tcp", false),
    -				newTestCase(b, d, "tcp", true),
    -				newTestCase(c, d, "tcp", true),
    -				newTestCase(x, a, "tcp", true),
    -				newTestCase(x, d, "tcp", false),
    +				newTestCase(a, d, "tcp-8092", false),
    +				newTestCase(b, d, "tcp-8092", true),
    +				newTestCase(c, d, "tcp-8092", true),
    +				newTestCase(x, a, "tcp-8092", true),
    +				newTestCase(x, d, "tcp-8092", false),
     
     				// The policy on workload e denies request with path "/other":
     				// - request to port http-8090 should be allowed because the path is not matched.
     				// - request to port http-8091 should be allowed because the path is not matched.
    -				// - request to port tcp should be denied because policy uses HTTP fields.
    +				// - request to port tcp-8092 should be denied because policy uses HTTP fields.
     				newTestCase(a, e, "http-8090", true),
     				newTestCase(a, e, "http-8091", true),
    -				newTestCase(a, e, "tcp", false),
    +				newTestCase(a, e, "tcp-8092", false),
     			}
     
     			rbacUtil.RunRBACTest(t, cases)
    
  • tests/integration/security/testdata/rbac/v1beta1-tcp.yaml.tmpl+16 1 modified
    @@ -17,7 +17,10 @@ spec:
             ports: ["8090"]
     ---
     
    -# The following policy denies request to port 8090 for workload c
    +# The following policy denies:
    +# request to port 8090 for workload c
    +# request to port 8093 with principal suffix matching
    +# request to port 8092 with namespace suffix matching
     
     apiVersion: "security.istio.io/v1beta1"
     kind: AuthorizationPolicy
    @@ -33,6 +36,18 @@ spec:
       - to:
         - operation:
             ports: ["8090"]
    +  - to:
    +    - operation:
    +        ports: ["8093"]
    +    from:
    +    - source:
    +        principals: ["*/ns/{{ .Namespace }}/sa/b"]
    +  - to:
    +    - operation:
    +        ports: ["8092"]
    +    from:
    +    - source:
    +        namespaces: ["*{{ .Namespace2 }}"]
     ---
     
     # The following policy denies request from service account a and namespace 2 for workload d
    
  • tests/integration/security/util/rbac_util/util.go+1 1 modified
    @@ -73,7 +73,7 @@ func (tc TestCase) CheckRBACRequest() error {
     			return getError(req, "allow with code 200", fmt.Sprintf("error: %v", err))
     		}
     	} else {
    -		if req.Options.PortName == "tcp" || req.Options.PortName == "grpc" {
    +		if strings.HasPrefix(req.Options.PortName, "tcp") || req.Options.PortName == "grpc" {
     			expectedErrMsg := "EOF" // TCP deny message.
     			if req.Options.PortName == "grpc" {
     				expectedErrMsg = "rpc error: code = PermissionDenied desc = RBAC: access denied"
    
4c73414556b8

fix authz suffix matching in TCP (#29)

https://github.com/istio/istioYangmin ZhuJul 24, 2020via ghsa
7 files changed · +168 31
  • pilot/pkg/security/authz/builder/testdata/action-deny-HTTP-for-TCP-filter-in.yaml+8 8 modified
    @@ -53,13 +53,13 @@ spec:
       # rule[8] `from`: all fields, `to`: all fields, `when`: all fields.
       - from:
         - source:
    -        principals: ["principal"]
    +        principals: ["principal", "*principal-suffix", "principal-prefix*", "*"]
             requestPrincipals: ["requestPrincipals"]
    -        namespaces: ["ns"]
    +        namespaces: ["ns", "*ns-suffix", "ns-prefix*", "*"]
             ipBlocks: ["1.2.3.4"]
    -        notPrincipals: ["not-principal"]
    +        notPrincipals: ["not-principal", "*not-principal-suffix", "not-principal-prefix*", "*"]
             notRequestPrincipals: ["not-requestPrincipals"]
    -        notNamespaces: ["not-ns"]
    +        notNamespaces: ["not-ns", "*not-ns-suffix", "not-ns-prefix*", "*"]
             notIpBlocks: ["9.0.0.1"]
         to:
         - operation:
    @@ -79,11 +79,11 @@ spec:
             values: ["10.10.10.10"]
             notValues: ["90.10.10.10"]
           - key: "source.namespace"
    -        values: ["ns"]
    -        notValues: ["not-ns"]
    +        values: ["ns", "*ns-suffix", "ns-prefix*", "*"]
    +        notValues: ["not-ns", "*not-ns-suffix", "not-ns-prefix*", "*"]
           - key: "source.principal"
    -        values: ["principal"]
    -        notValues: ["not-principal"]
    +        values: ["principal", "*principal-suffix", "principal-prefix*", "*"]
    +        notValues: ["not-principal", "*not-principal-suffix", "not-principal-prefix*", "*"]
           - key: "request.auth.principal"
             values: ["requestPrincipals"]
             notValues: ["not-requestPrincipals"]
    
  • pilot/pkg/security/authz/builder/testdata/action-deny-HTTP-for-TCP-filter-out.yaml+112 0 modified
    @@ -159,19 +159,60 @@ typedConfig:
                     - authenticated:
                         principalName:
                           exact: spiffe://principal
    +                - authenticated:
    +                    principalName:
    +                      safeRegex:
    +                        googleRe2: {}
    +                        regex: spiffe://.*principal-suffix
    +                - authenticated:
    +                    principalName:
    +                      prefix: spiffe://principal-prefix
    +                - authenticated:
    +                    principalName:
    +                      safeRegex:
    +                        googleRe2: {}
    +                        regex: .+
                 - notId:
                     orIds:
                       ids:
                       - authenticated:
                           principalName:
                             exact: spiffe://not-principal
    +                  - authenticated:
    +                      principalName:
    +                        safeRegex:
    +                          googleRe2: {}
    +                          regex: spiffe://.*not-principal-suffix
    +                  - authenticated:
    +                      principalName:
    +                        prefix: spiffe://not-principal-prefix
    +                  - authenticated:
    +                      principalName:
    +                        safeRegex:
    +                          googleRe2: {}
    +                          regex: .+
                 - orIds:
                     ids:
                     - authenticated:
                         principalName:
                           safeRegex:
                             googleRe2: {}
                             regex: .*/ns/ns/.*
    +                - authenticated:
    +                    principalName:
    +                      safeRegex:
    +                        googleRe2: {}
    +                        regex: .*/ns/.*ns-suffix/.*
    +                - authenticated:
    +                    principalName:
    +                      safeRegex:
    +                        googleRe2: {}
    +                        regex: .*/ns/ns-prefix.*/.*
    +                - authenticated:
    +                    principalName:
    +                      safeRegex:
    +                        googleRe2: {}
    +                        regex: .*/ns/.*/.*
                 - notId:
                     orIds:
                       ids:
    @@ -180,6 +221,21 @@ typedConfig:
                             safeRegex:
                               googleRe2: {}
                               regex: .*/ns/not-ns/.*
    +                  - authenticated:
    +                      principalName:
    +                        safeRegex:
    +                          googleRe2: {}
    +                          regex: .*/ns/.*not-ns-suffix/.*
    +                  - authenticated:
    +                      principalName:
    +                        safeRegex:
    +                          googleRe2: {}
    +                          regex: .*/ns/not-ns-prefix.*/.*
    +                  - authenticated:
    +                      principalName:
    +                        safeRegex:
    +                          googleRe2: {}
    +                          regex: .*/ns/.*/.*
                 - orIds:
                     ids:
                     - sourceIp:
    @@ -209,6 +265,21 @@ typedConfig:
                           safeRegex:
                             googleRe2: {}
                             regex: .*/ns/ns/.*
    +                - authenticated:
    +                    principalName:
    +                      safeRegex:
    +                        googleRe2: {}
    +                        regex: .*/ns/.*ns-suffix/.*
    +                - authenticated:
    +                    principalName:
    +                      safeRegex:
    +                        googleRe2: {}
    +                        regex: .*/ns/ns-prefix.*/.*
    +                - authenticated:
    +                    principalName:
    +                      safeRegex:
    +                        googleRe2: {}
    +                        regex: .*/ns/.*/.*
                 - notId:
                     orIds:
                       ids:
    @@ -217,15 +288,56 @@ typedConfig:
                             safeRegex:
                               googleRe2: {}
                               regex: .*/ns/not-ns/.*
    +                  - authenticated:
    +                      principalName:
    +                        safeRegex:
    +                          googleRe2: {}
    +                          regex: .*/ns/.*not-ns-suffix/.*
    +                  - authenticated:
    +                      principalName:
    +                        safeRegex:
    +                          googleRe2: {}
    +                          regex: .*/ns/not-ns-prefix.*/.*
    +                  - authenticated:
    +                      principalName:
    +                        safeRegex:
    +                          googleRe2: {}
    +                          regex: .*/ns/.*/.*
                 - orIds:
                     ids:
                     - authenticated:
                         principalName:
                           exact: spiffe://principal
    +                - authenticated:
    +                    principalName:
    +                      safeRegex:
    +                        googleRe2: {}
    +                        regex: spiffe://.*principal-suffix
    +                - authenticated:
    +                    principalName:
    +                      prefix: spiffe://principal-prefix
    +                - authenticated:
    +                    principalName:
    +                      safeRegex:
    +                        googleRe2: {}
    +                        regex: .+
                 - notId:
                     orIds:
                       ids:
                       - authenticated:
                           principalName:
                             exact: spiffe://not-principal
    +                  - authenticated:
    +                      principalName:
    +                        safeRegex:
    +                          googleRe2: {}
    +                          regex: spiffe://.*not-principal-suffix
    +                  - authenticated:
    +                      principalName:
    +                        prefix: spiffe://not-principal-prefix
    +                  - authenticated:
    +                      principalName:
    +                        safeRegex:
    +                          googleRe2: {}
    +                          regex: .+
       statPrefix: tcp.
    
  • pilot/pkg/security/authz/matcher/string.go+7 4 modified
    @@ -49,11 +49,14 @@ func StringMatcherWithPrefix(v, prefix string) *matcherpb.StringMatcher {
     	case v == "*":
     		return StringMatcherRegex(".+")
     	case strings.HasPrefix(v, "*"):
    -		return &matcherpb.StringMatcher{
    -			MatchPattern: &matcherpb.StringMatcher_Suffix{
    -				Suffix: prefix + strings.TrimPrefix(v, "*"),
    -			},
    +		if prefix == "" {
    +			return &matcherpb.StringMatcher{
    +				MatchPattern: &matcherpb.StringMatcher_Suffix{
    +					Suffix: strings.TrimPrefix(v, "*"),
    +				},
    +			}
     		}
    +		return StringMatcherRegex(prefix + ".*" + strings.TrimPrefix(v, "*"))
     	case strings.HasSuffix(v, "*"):
     		return &matcherpb.StringMatcher{
     			MatchPattern: &matcherpb.StringMatcher_Prefix{
    
  • pilot/pkg/security/authz/matcher/string_test.go+25 14 modified
    @@ -23,36 +23,47 @@ import (
     
     func TestStringMatcherWithPrefix(t *testing.T) {
     	testCases := []struct {
    -		name string
    -		v    string
    -		want *matcherpb.StringMatcher
    +		name   string
    +		v      string
    +		prefix string
    +		want   *matcherpb.StringMatcher
     	}{
     		{
    -			name: "wildcardAsRequired",
    -			v:    "*",
    -			want: StringMatcherRegex(".+"),
    +			name:   "wildcardAsRequired",
    +			v:      "*",
    +			prefix: "abc",
    +			want:   StringMatcherRegex(".+"),
     		},
     		{
    -			name: "prefix",
    -			v:    "-prefix-*",
    +			name:   "prefix",
    +			v:      "-prefix-*",
    +			prefix: "abc",
     			want: &matcherpb.StringMatcher{
     				MatchPattern: &matcherpb.StringMatcher_Prefix{
     					Prefix: "abc-prefix-",
     				},
     			},
     		},
     		{
    -			name: "suffix",
    -			v:    "*-suffix",
    +			name:   "suffix-empty-prefix",
    +			v:      "*-suffix",
    +			prefix: "",
     			want: &matcherpb.StringMatcher{
     				MatchPattern: &matcherpb.StringMatcher_Suffix{
    -					Suffix: "abc-suffix",
    +					Suffix: "-suffix",
     				},
     			},
     		},
     		{
    -			name: "exact",
    -			v:    "-exact",
    +			name:   "suffix",
    +			v:      "*-suffix",
    +			prefix: "abc",
    +			want:   StringMatcherRegex("abc.*-suffix"),
    +		},
    +		{
    +			name:   "exact",
    +			v:      "-exact",
    +			prefix: "abc",
     			want: &matcherpb.StringMatcher{
     				MatchPattern: &matcherpb.StringMatcher_Exact{
     					Exact: "abc-exact",
    @@ -63,7 +74,7 @@ func TestStringMatcherWithPrefix(t *testing.T) {
     
     	for _, tc := range testCases {
     		t.Run(tc.name, func(t *testing.T) {
    -			actual := StringMatcherWithPrefix(tc.v, "abc")
    +			actual := StringMatcherWithPrefix(tc.v, tc.prefix)
     			if !reflect.DeepEqual(*actual, *tc.want) {
     				t.Errorf("want %s but got %s", tc.want.String(), actual.String())
     			}
    
  • pilot/pkg/security/authz/model/generator.go+2 4 modified
    @@ -116,15 +116,13 @@ func (srcNamespaceGenerator) permission(_, _ string, _ bool) (*rbacpb.Permission
     }
     
     func (srcNamespaceGenerator) principal(_, value string, forTCP bool) (*rbacpb.Principal, error) {
    +	v := strings.Replace(value, "*", ".*", -1)
    +	m := matcher.StringMatcherRegex(fmt.Sprintf(".*/ns/%s/.*", v))
     	if forTCP {
    -		regex := fmt.Sprintf(".*/ns/%s/.*", value)
    -		m := matcher.StringMatcherRegex(regex)
     		return principalAuthenticated(m), nil
     	}
     	// Proxy doesn't have attrSrcNamespace directly, but the information is encoded in attrSrcPrincipal
     	// with format: cluster.local/ns/{NAMESPACE}/sa/{SERVICE-ACCOUNT}.
    -	v := strings.Replace(value, "*", ".*", -1)
    -	m := matcher.StringMatcherRegex(fmt.Sprintf(".*/ns/%s/.*", v))
     	metadata := matcher.MetadataStringMatcher(sm.AuthnFilterName, attrSrcPrincipal, m)
     	return principalMetadata(metadata), nil
     }
    
  • tests/integration/security/authorization_test.go+8 0 modified
    @@ -715,9 +715,17 @@ func TestAuthorization_TCP(t *testing.T) {
     				// - request to port http-8090 should be denied because the port is matched.
     				// - request to http port 8091 should be allowed because the port is not matched.
     				// - request to tcp port 8092 should be allowed because the port is not matched.
    +				// - request from b should be denied because the principal is matched
    +				// - request from x should be denied because the namespace is matched
     				newTestCase(a, c, "http-8090", false, scheme.HTTP),
     				newTestCase(a, c, "http-8091", true, scheme.HTTP),
     				newTestCase(a, c, "tcp", true, scheme.TCP),
    +				newTestCase(b, c, "http-8090", false, scheme.HTTP),
    +				newTestCase(b, c, "http-8091", false, scheme.HTTP),
    +				newTestCase(b, c, "tcp", false, scheme.TCP),
    +				newTestCase(x, c, "http-8090", false, scheme.HTTP),
    +				newTestCase(x, c, "http-8091", false, scheme.HTTP),
    +				newTestCase(x, c, "tcp", false, scheme.TCP),
     
     				// The policy on workload d denies request from service account a and workloads in namespace 2:
     				// - request from a to d should be denied because it has service account a.
    
  • tests/integration/security/testdata/authz/v1beta1-tcp.yaml.tmpl+6 1 modified
    @@ -17,7 +17,7 @@ spec:
             ports: ["8090"]
     ---
     
    -# The following policy denies request to port 8090 for workload c
    +# The following policy denies request to port 8090 for workload c, requests from principal of suffix "b" or namespace of suffix Namespace2
     
     apiVersion: "security.istio.io/v1beta1"
     kind: AuthorizationPolicy
    @@ -33,6 +33,11 @@ spec:
       - to:
         - operation:
             ports: ["8090"]
    +  - from:
    +    - source:
    +        principals: ["*b"]
    +    - source:
    +        namespaces: ["*{{ .Namespace2 }}"]
     ---
     
     # The following policy denies request from service account a and namespace 2 for workload d
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

10

News mentions

0

No linked articles in our index yet.