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.
| Package | Affected versions | Patched versions |
|---|---|---|
istio.io/istioGo | >= 0.0.0-20241024090207-0bf27d49ba4b, < 0.0.0-20260403004500-692e460c342d | 0.0.0-20260403004500-692e460c342d |
Affected products
1Patches
1692e460c342dQuote metacharacters in SA regex (#59700)
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
4News mentions
0No linked articles in our index yet.