VYPR
Medium severity5.4NVD Advisory· Published Apr 15, 2026· Updated Apr 23, 2026

CVE-2026-39350

CVE-2026-39350

Description

Istio is an open platform to connect, manage, and secure microservices. In versions 1.25.0 through 1.27.8, 1.28.0 through 1.28.5, 1.29.0, and 1.29.1, the serviceAccounts and notServiceAccounts fields in AuthorizationPolicy incorrectly interpret dots (.) as a regular expression matcher. Because . is a valid character in a service account name, an AuthorizationPolicy ALLOW rule targeting a service account such as cert-manager.io also matches cert-manager-io, cert-managerXio, etc. A DENY rule targeting the same name fails to block those variants. Fixes are available in versions 1.29.2, 1.28.6, and 1.27.9.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
istio.io/istioGo
>= 0.0.0-20241024090207-0bf27d49ba4b, < 0.0.0-20260403004500-692e460c342d0.0.0-20260403004500-692e460c342d

Affected products

1
  • cpe:2.3:a:istio:istio:*:*:*:*:*:*:*:*
    Range: >=1.25.0,<1.27.9

Patches

1
692e460c342d

Quote metacharacters in SA regex (#59700)

https://github.com/istio/istioKeith Mattix IIApr 3, 2026via ghsa
3 files changed · +46 9
  • pilot/pkg/security/authz/model/generator.go+2 1 modified
    @@ -16,6 +16,7 @@ package model
     
     import (
     	"fmt"
    +	"regexp"
     	"strings"
     
     	rbacpb "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v3"
    @@ -209,7 +210,7 @@ func serviceAccountRegex(defaultNamespace string, value string) string {
     	// optional arbitrary k/v pairs
     	// '/sa/<serviceAccount>'
     	// Either end of string OR / + arbitrary k/v pairs (the / ensures we do not match <service account>-some-junk)
    -	return fmt.Sprintf("spiffe://.+/ns/%s/(.+/|)sa/%s(/.+)?", ns, sa)
    +	return fmt.Sprintf("spiffe://.+/ns/%s/(.+/|)sa/%s(/.+)?", regexp.QuoteMeta(ns), regexp.QuoteMeta(sa))
     }
     
     type srcPrincipalGenerator struct{}
    
  • pilot/pkg/security/authz/model/generator_test.go+36 8 modified
    @@ -459,74 +459,81 @@ func TestGenerator(t *testing.T) {
     }
     
     func TestServiceAccount(t *testing.T) {
    -	input := "my-ns/my-sa"
     	cases := []struct {
     		Name     string
    +		Input    string
     		Identity string
     		Match    bool
     	}{
     		{
     			Name:     "standard",
    +			Input:    "my-ns/my-sa",
     			Identity: "spiffe://cluster.local/ns/my-ns/sa/my-sa",
     			Match:    true,
     		},
     		{
     			Name:     "suffix attributes",
    +			Input:    "my-ns/my-sa",
     			Identity: "spiffe://cluster.local/ns/my-ns/sa/my-sa/k/v",
     			Match:    true,
     		},
     		{
     			Name:     "prefix attributes",
    +			Input:    "my-ns/my-sa",
     			Identity: "spiffe://cluster.local/k/v/ns/my-ns/sa/my-sa",
     			Match:    true,
     		},
     		{
     			Name:     "middle attributes",
    +			Input:    "my-ns/my-sa",
     			Identity: "spiffe://cluster.local/ns/my-ns/k/v/sa/my-sa",
     			Match:    true,
     		},
     		{
     			Name:     "all attributes",
    +			Input:    "my-ns/my-sa",
     			Identity: "spiffe://cluster.local/k1/v1/ns/my-ns/k2/v2/sa/my-sa/k3/v3",
     			Match:    true,
     		},
     		{
     			Name:     "sa suffix string",
    +			Input:    "my-ns/my-sa",
     			Identity: "spiffe://cluster.local/ns/my-ns/sa/my-sa-suffix",
     			Match:    false,
     		},
     		{
     			Name:     "ns suffix string",
    +			Input:    "my-ns/my-sa",
     			Identity: "spiffe://cluster.local/ns/my-ns-suffix/sa/my-sa",
     			Match:    false,
     		},
     		{
     			Name:     "not spiffe",
    +			Input:    "my-ns/my-sa",
     			Identity: "cluster.local/ns/my-ns/sa/my-sa",
     			Match:    false,
     		},
     		{
     			Name:     "invalid spiffe",
    +			Input:    "my-ns/my-sa",
     			Identity: "spiffe://ns/my-ns/sa/my-sa",
     			Match:    false,
     		},
     		{
     			Name:     "missing sa",
    +			Input:    "my-ns/my-sa",
     			Identity: "spiffe://cluster.local/ns/my-ns",
     			Match:    false,
     		},
     		{
     			Name:     "missing ns",
    +			Input:    "my-ns/my-sa",
     			Identity: "spiffe://cluster.local/sa/my-sa",
     			Match:    false,
     		},
     		{
    -			Name:     "missing ns",
    -			Identity: "spiffe://cluster.local/sa/my-sa",
    -			Match:    false,
    -		},
    -		{
    -			Name: "weird keys",
    +			Name:  "weird keys",
    +			Input: "my-ns/my-sa",
     			// This test case matches when it shouldn't ideally.
     			// Spiffe is a set of k/v pairs. Here we are accidentally matching
     			// on previous value + next key when we shouldn't.
    @@ -539,10 +546,28 @@ func TestServiceAccount(t *testing.T) {
     			}, "/"),
     			Match: true,
     		},
    +		{
    +			Name:     "regex metachars - exact match with dots",
    +			Input:    "my.ns/my.sa",
    +			Identity: "spiffe://cluster.local/ns/my.ns/sa/my.sa",
    +			Match:    true,
    +		},
    +		{
    +			Name:     "regex metachars - dot should not match arbitrary char in ns",
    +			Input:    "my.ns/my.sa",
    +			Identity: "spiffe://cluster.local/ns/myXns/sa/my.sa",
    +			Match:    false,
    +		},
    +		{
    +			Name:     "regex metachars - dot should not match arbitrary char in sa",
    +			Input:    "my.ns/my.sa",
    +			Identity: "spiffe://cluster.local/ns/my.ns/sa/myXsa",
    +			Match:    false,
    +		},
     	}
     	for _, tt := range cases {
     		t.Run(tt.Name, func(t *testing.T) {
    -			r := serviceAccountRegex("", input)
    +			r := serviceAccountRegex("", tt.Input)
     			// Parse as regex. Envoy does a full string match, so handle that
     			rgx, err := regexp.Compile("^" + r + "$")
     			if err != nil {
    @@ -574,4 +599,7 @@ func yamlPrincipal(t *testing.T, yaml string) *rbacpb.Principal {
     func TestServiceAccountRegex(t *testing.T) {
     	assert.Equal(t, serviceAccountRegex("", "my-ns/my-sa"), `spiffe://.+/ns/my-ns/(.+/|)sa/my-sa(/.+)?`)
     	assert.Equal(t, serviceAccountRegex("my-ns", "my-sa"), `spiffe://.+/ns/my-ns/(.+/|)sa/my-sa(/.+)?`)
    +	// Regex metacharacters in namespace and SA name should be escaped
    +	assert.Equal(t, serviceAccountRegex("", "my.ns/my.sa"), `spiffe://.+/ns/my\.ns/(.+/|)sa/my\.sa(/.+)?`)
    +	assert.Equal(t, serviceAccountRegex("ns+foo", "sa(bar)"), `spiffe://.+/ns/ns\+foo/(.+/|)sa/sa\(bar\)(/.+)?`)
     }
    
  • releasenotes/notes/59700.yaml+8 0 added
    @@ -0,0 +1,8 @@
    +apiVersion: release-notes/v2
    +kind: bug-fix
    +area: security
    +issue:
    +- 59700
    +releaseNotes:
    +- |
    +    **Fixed** serivceAccount matcher regex in AuthorizationPolicy to properly quote the service account name, allowing for correct matching of service accounts with special characters in their names.
    

Vulnerability mechanics

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

References

4

News mentions

0

No linked articles in our index yet.