Tekton Pipelines: Controller can panic when setting long resolver names in TaskRun/PipelineRun
Description
Tekton Pipelines project provides k8s-style resources for declaring CI/CD-style pipelines. Versions 0.60.0 through 1.0.0, 1.1.0 through 1.3.2, 1.4.0 through 1.6.0, 1.7.0 through 1.9.0, 1.10.0, and 1.10.1 have a denial-of-service vulnerability in that allows any user who can create a TaskRun or PipelineRun to crash the controller cluster-wide by setting .spec.taskRef.resolver (or .spec.pipelineRef.resolver) to a string of 31+ characters. The crash occurs because GenerateDeterministicNameFromSpec produces a name exceeding the 63-character DNS-1123 label limit, and its truncation logic panics on a [-1] slice bound since the generated name contains no spaces. Once crashed, the controller enters a CrashLoopBackOff on restart (as it re-reconciles the offending resource), blocking all CI/CD reconciliation until the resource is manually deleted. Built-in resolvers (git, cluster, bundles, hub) are unaffected due to their short names, but any custom resolver name triggers the bug. The fix truncates the resolver-name prefix instead of the full string, preserving the hash suffix for determinism and uniqueness. This issue has been patched in versions 1.0.1, 1.3.3, 1.6.1, 1.9.2 and 1.10.2.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Tekton Pipelines controller panics and enters a restart loop when a TaskRun or PipelineRun uses a resolver name of 31+ characters, causing a cluster-wide denial of service.
Vulnerability
The vulnerability is a denial-of-service (DoS) flaw in Tekton Pipelines that allows any user with permission to create or update TaskRun or PipelineRun resources to crash the controller cluster-wide. The root cause is a panic in the GenerateDeterministicNameFromSpec function when constructing a deterministic ResolutionRequest name. The generated name has the format {resolver}-{hash}, and if the resolver name is 31 characters or more, the resulting name exceeds the DNS-1123 label limit of 63 characters [1][2]. The truncation logic attempts to find a word boundary using strings.LastIndex(name, " "), but since the name contains no spaces (only alphanumeric characters and a dash), LastIndex returns -1, which is then used as a slice bound, causing a runtime panic [2].
Exploitation
An attacker who can create or update a TaskRun or PipelineRun can set .spec.taskRef.resolver or .spec.pipelineRef.resolver to a string of 31 or more characters. This triggers the panic in the controller, crashing it instantly. Upon restart, the controller attempts to re-reconcile the offending resource, causing it to panic again and enter a CrashLoopBackOff state [1][2]. No additional authentication or network position is required—only the ability to create the referenced resource objects. Built-in resolver names (git, cluster, bundles, hub) are short enough to avoid triggering the bug [2].
Impact
The crash and subsequent restart loop effectively block all TaskRun and PipelineRun reconciliation cluster-wide until the malicious resource is manually deleted. This constitutes a complete denial of service for CI/CD pipeline operations across the cluster, with no automated recovery [1][2].
Mitigation
The issue has been patched in Tekton Pipelines versions 1.0.1, 1.3.3, 1.6.1, 1.9.2, and 1.10.2 [1][2]. The fix changes the truncation logic to first compute the hash, then truncate only the resolver prefix while preserving the full hash, ensuring the generated name stays within the 63-character limit without losing determinism [3][4]. As a workaround, administrators can restrict who has permission to create TaskRun and PipelineRun resources via Kubernetes RBAC [2].
AI Insight generated on May 18, 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.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/tektoncd/pipelineGo | >= 0.60.0, < 1.0.1 | 1.0.1 |
github.com/tektoncd/pipelineGo | >= 1.1.0, < 1.3.3 | 1.3.3 |
github.com/tektoncd/pipelineGo | >= 1.4.0, < 1.6.1 | 1.6.1 |
github.com/tektoncd/pipelineGo | >= 1.7.0, < 1.9.2 | 1.9.2 |
github.com/tektoncd/pipelineGo | >= 1.10.0, < 1.10.2 | 1.10.2 |
Affected products
2Patches
65eead3f859b9Fix panic in GenerateDeterministicNameFromSpec with long resolver names
2 files changed · +79 −7
pkg/resolution/resource/name.go+15 −7 modified@@ -21,7 +21,6 @@ import ( "hash" "hash/fnv" "sort" - "strings" v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" "github.com/tektoncd/pipeline/pkg/apis/resolution/v1beta1" @@ -77,6 +76,19 @@ func nameHasher() hash.Hash { return fnv.New128a() } +// sanitizedName builds a "{prefix}-{hash}" string that fits within the +// DNS-1123 label max length. If the prefix is too long, it is truncated +// so that the full hash is always preserved, maintaining determinism and +// uniqueness. +func sanitizedName(prefix string, hasher hash.Hash) string { + hex := fmt.Sprintf("%x", hasher.Sum(nil)) + maxPrefixLen := maxLength - len(hex) - 1 // 1 for the "-" separator + if len(prefix) > maxPrefixLen { + prefix = prefix[:maxPrefixLen] + } + return fmt.Sprintf("%s-%s", prefix, hex) +} + // GenerateDeterministicNameFromSpec makes a best-effort attempt to create a // unique but reproducible name for use in a Request. The returned value // will have the format {prefix}-{hash} where {prefix} is @@ -89,7 +101,7 @@ func GenerateDeterministicNameFromSpec(prefix, base string, resolutionSpec *v1be } if resolutionSpec == nil { - return fmt.Sprintf("%s-%x", prefix, hasher.Sum(nil)), nil + return sanitizedName(prefix, hasher), nil } params := resolutionSpec.Params sortedParams := make(v1.Params, len(params)) @@ -123,11 +135,7 @@ func GenerateDeterministicNameFromSpec(prefix, base string, resolutionSpec *v1be return "", err } } - name := fmt.Sprintf("%s-%x", prefix, hasher.Sum(nil)) - if maxLength > len(name) { - return name, nil - } - return name[:strings.LastIndex(name[:maxLength], " ")], nil + return sanitizedName(prefix, hasher), nil } // GenerateErrorLogString makes a best effort attempt to get the name of the task
pkg/resolution/resource/name_test.go+64 −0 modified@@ -101,6 +101,14 @@ func TestGenerateDeterministicName(t *testing.T) { args: golden, want: "prefix-ba2f256f318de7f4154da577c283cb9e", }, + { + name: "long prefix with nil params truncates prefix preserving hash", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 31 chars + base: "default/my-taskrun", + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-2d162955e9c9fcfef736fd389e7fd796", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -201,6 +209,62 @@ func TestGenerateDeterministicNameFromSpec(t *testing.T) { args: golden, want: "prefix-ff25bd24688ab610bdc530a5ab3aabbd", }, + { + name: "long prefix at exactly max length is not truncated", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 30 chars, name = 30+1+32 = 63 = maxLength + base: "default/my-taskrun", + params: []v1.Param{{ + Name: "foo", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "bar"}, + }}, + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-da9d0a4501a276745547b4b4b21a2e77", // exactly 63, fits + }, + { + name: "long prefix exceeding max length truncates prefix not hash", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 31 chars, would be 64 > maxLength + base: "default/my-taskrun", + params: []v1.Param{{ + Name: "foo", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "bar"}, + }}, + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-da9d0a4501a276745547b4b4b21a2e77", // prefix trimmed to 30, hash preserved + }, + { + name: "very long prefix truncates prefix preserving hash", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 50 chars + base: "default/my-taskrun", + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-2d162955e9c9fcfef736fd389e7fd796", // prefix trimmed to 30, hash preserved + }, + { + name: "different inputs with same long prefix produce different names", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 31 chars + base: "default/other-taskrun", + params: []v1.Param{{ + Name: "baz", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "qux"}, + }}, + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-5592e5f983640b4274071dbe1c09068d", // same prefix length, different hash + }, + { + name: "prefix under max length returns full name", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 29 chars, name = 62 chars + base: "default/my-taskrun", + params: []v1.Param{{ + Name: "foo", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "bar"}, + }}, + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa-da9d0a4501a276745547b4b4b21a2e77", // 62 chars, no truncation + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {
ebc197e2b973Fix panic in GenerateDeterministicNameFromSpec with long resolver names
2 files changed · +79 −7
pkg/resolution/resource/name.go+15 −7 modified@@ -21,7 +21,6 @@ import ( "hash" "hash/fnv" "sort" - "strings" v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" "github.com/tektoncd/pipeline/pkg/apis/resolution/v1beta1" @@ -77,6 +76,19 @@ func nameHasher() hash.Hash { return fnv.New128a() } +// sanitizedName builds a "{prefix}-{hash}" string that fits within the +// DNS-1123 label max length. If the prefix is too long, it is truncated +// so that the full hash is always preserved, maintaining determinism and +// uniqueness. +func sanitizedName(prefix string, hasher hash.Hash) string { + hex := fmt.Sprintf("%x", hasher.Sum(nil)) + maxPrefixLen := maxLength - len(hex) - 1 // 1 for the "-" separator + if len(prefix) > maxPrefixLen { + prefix = prefix[:maxPrefixLen] + } + return fmt.Sprintf("%s-%s", prefix, hex) +} + // GenerateDeterministicNameFromSpec makes a best-effort attempt to create a // unique but reproducible name for use in a Request. The returned value // will have the format {prefix}-{hash} where {prefix} is @@ -89,7 +101,7 @@ func GenerateDeterministicNameFromSpec(prefix, base string, resolutionSpec *v1be } if resolutionSpec == nil { - return fmt.Sprintf("%s-%x", prefix, hasher.Sum(nil)), nil + return sanitizedName(prefix, hasher), nil } params := resolutionSpec.Params sortedParams := make(v1.Params, len(params)) @@ -123,11 +135,7 @@ func GenerateDeterministicNameFromSpec(prefix, base string, resolutionSpec *v1be return "", err } } - name := fmt.Sprintf("%s-%x", prefix, hasher.Sum(nil)) - if maxLength > len(name) { - return name, nil - } - return name[:strings.LastIndex(name[:maxLength], " ")], nil + return sanitizedName(prefix, hasher), nil } // GenerateErrorLogString makes a best effort attempt to get the name of the task
pkg/resolution/resource/name_test.go+64 −0 modified@@ -101,6 +101,14 @@ func TestGenerateDeterministicName(t *testing.T) { args: golden, want: "prefix-ba2f256f318de7f4154da577c283cb9e", }, + { + name: "long prefix with nil params truncates prefix preserving hash", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 31 chars + base: "default/my-taskrun", + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-2d162955e9c9fcfef736fd389e7fd796", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -201,6 +209,62 @@ func TestGenerateDeterministicNameFromSpec(t *testing.T) { args: golden, want: "prefix-ff25bd24688ab610bdc530a5ab3aabbd", }, + { + name: "long prefix at exactly max length is not truncated", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 30 chars, name = 30+1+32 = 63 = maxLength + base: "default/my-taskrun", + params: []v1.Param{{ + Name: "foo", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "bar"}, + }}, + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-da9d0a4501a276745547b4b4b21a2e77", // exactly 63, fits + }, + { + name: "long prefix exceeding max length truncates prefix not hash", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 31 chars, would be 64 > maxLength + base: "default/my-taskrun", + params: []v1.Param{{ + Name: "foo", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "bar"}, + }}, + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-da9d0a4501a276745547b4b4b21a2e77", // prefix trimmed to 30, hash preserved + }, + { + name: "very long prefix truncates prefix preserving hash", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 50 chars + base: "default/my-taskrun", + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-2d162955e9c9fcfef736fd389e7fd796", // prefix trimmed to 30, hash preserved + }, + { + name: "different inputs with same long prefix produce different names", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 31 chars + base: "default/other-taskrun", + params: []v1.Param{{ + Name: "baz", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "qux"}, + }}, + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-5592e5f983640b4274071dbe1c09068d", // same prefix length, different hash + }, + { + name: "prefix under max length returns full name", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 29 chars, name = 62 chars + base: "default/my-taskrun", + params: []v1.Param{{ + Name: "foo", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "bar"}, + }}, + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa-da9d0a4501a276745547b4b4b21a2e77", // 62 chars, no truncation + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {
edc64bbf2232Fix panic in GenerateDeterministicNameFromSpec with long resolver names
2 files changed · +79 −7
pkg/resolution/resource/name.go+15 −7 modified@@ -21,7 +21,6 @@ import ( "hash" "hash/fnv" "sort" - "strings" v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" "github.com/tektoncd/pipeline/pkg/apis/resolution/v1beta1" @@ -77,6 +76,19 @@ func nameHasher() hash.Hash { return fnv.New128a() } +// sanitizedName builds a "{prefix}-{hash}" string that fits within the +// DNS-1123 label max length. If the prefix is too long, it is truncated +// so that the full hash is always preserved, maintaining determinism and +// uniqueness. +func sanitizedName(prefix string, hasher hash.Hash) string { + hex := fmt.Sprintf("%x", hasher.Sum(nil)) + maxPrefixLen := maxLength - len(hex) - 1 // 1 for the "-" separator + if len(prefix) > maxPrefixLen { + prefix = prefix[:maxPrefixLen] + } + return fmt.Sprintf("%s-%s", prefix, hex) +} + // GenerateDeterministicNameFromSpec makes a best-effort attempt to create a // unique but reproducible name for use in a Request. The returned value // will have the format {prefix}-{hash} where {prefix} is @@ -89,7 +101,7 @@ func GenerateDeterministicNameFromSpec(prefix, base string, resolutionSpec *v1be } if resolutionSpec == nil { - return fmt.Sprintf("%s-%x", prefix, hasher.Sum(nil)), nil + return sanitizedName(prefix, hasher), nil } params := resolutionSpec.Params sortedParams := make(v1.Params, len(params)) @@ -123,11 +135,7 @@ func GenerateDeterministicNameFromSpec(prefix, base string, resolutionSpec *v1be return "", err } } - name := fmt.Sprintf("%s-%x", prefix, hasher.Sum(nil)) - if maxLength > len(name) { - return name, nil - } - return name[:strings.LastIndex(name[:maxLength], " ")], nil + return sanitizedName(prefix, hasher), nil } // GenerateErrorLogString makes a best effort attempt to get the name of the task
pkg/resolution/resource/name_test.go+64 −0 modified@@ -101,6 +101,14 @@ func TestGenerateDeterministicName(t *testing.T) { args: golden, want: "prefix-ba2f256f318de7f4154da577c283cb9e", }, + { + name: "long prefix with nil params truncates prefix preserving hash", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 31 chars + base: "default/my-taskrun", + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-2d162955e9c9fcfef736fd389e7fd796", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -201,6 +209,62 @@ func TestGenerateDeterministicNameFromSpec(t *testing.T) { args: golden, want: "prefix-ff25bd24688ab610bdc530a5ab3aabbd", }, + { + name: "long prefix at exactly max length is not truncated", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 30 chars, name = 30+1+32 = 63 = maxLength + base: "default/my-taskrun", + params: []v1.Param{{ + Name: "foo", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "bar"}, + }}, + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-da9d0a4501a276745547b4b4b21a2e77", // exactly 63, fits + }, + { + name: "long prefix exceeding max length truncates prefix not hash", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 31 chars, would be 64 > maxLength + base: "default/my-taskrun", + params: []v1.Param{{ + Name: "foo", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "bar"}, + }}, + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-da9d0a4501a276745547b4b4b21a2e77", // prefix trimmed to 30, hash preserved + }, + { + name: "very long prefix truncates prefix preserving hash", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 50 chars + base: "default/my-taskrun", + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-2d162955e9c9fcfef736fd389e7fd796", // prefix trimmed to 30, hash preserved + }, + { + name: "different inputs with same long prefix produce different names", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 31 chars + base: "default/other-taskrun", + params: []v1.Param{{ + Name: "baz", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "qux"}, + }}, + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-5592e5f983640b4274071dbe1c09068d", // same prefix length, different hash + }, + { + name: "prefix under max length returns full name", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 29 chars, name = 62 chars + base: "default/my-taskrun", + params: []v1.Param{{ + Name: "foo", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "bar"}, + }}, + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa-da9d0a4501a276745547b4b4b21a2e77", // 62 chars, no truncation + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {
01673237c464Fix panic in GenerateDeterministicNameFromSpec with long resolver names
2 files changed · +79 −7
pkg/resolution/resource/name.go+15 −7 modified@@ -21,7 +21,6 @@ import ( "hash" "hash/fnv" "sort" - "strings" v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" "github.com/tektoncd/pipeline/pkg/apis/resolution/v1beta1" @@ -77,6 +76,19 @@ func nameHasher() hash.Hash { return fnv.New128a() } +// sanitizedName builds a "{prefix}-{hash}" string that fits within the +// DNS-1123 label max length. If the prefix is too long, it is truncated +// so that the full hash is always preserved, maintaining determinism and +// uniqueness. +func sanitizedName(prefix string, hasher hash.Hash) string { + hex := fmt.Sprintf("%x", hasher.Sum(nil)) + maxPrefixLen := maxLength - len(hex) - 1 // 1 for the "-" separator + if len(prefix) > maxPrefixLen { + prefix = prefix[:maxPrefixLen] + } + return fmt.Sprintf("%s-%s", prefix, hex) +} + // GenerateDeterministicNameFromSpec makes a best-effort attempt to create a // unique but reproducible name for use in a Request. The returned value // will have the format {prefix}-{hash} where {prefix} is @@ -89,7 +101,7 @@ func GenerateDeterministicNameFromSpec(prefix, base string, resolutionSpec *v1be } if resolutionSpec == nil { - return fmt.Sprintf("%s-%x", prefix, hasher.Sum(nil)), nil + return sanitizedName(prefix, hasher), nil } params := resolutionSpec.Params sortedParams := make(v1.Params, len(params)) @@ -123,11 +135,7 @@ func GenerateDeterministicNameFromSpec(prefix, base string, resolutionSpec *v1be return "", err } } - name := fmt.Sprintf("%s-%x", prefix, hasher.Sum(nil)) - if maxLength > len(name) { - return name, nil - } - return name[:strings.LastIndex(name[:maxLength], " ")], nil + return sanitizedName(prefix, hasher), nil } // GenerateErrorLogString makes a best effort attempt to get the name of the task
pkg/resolution/resource/name_test.go+64 −0 modified@@ -101,6 +101,14 @@ func TestGenerateDeterministicName(t *testing.T) { args: golden, want: "prefix-ba2f256f318de7f4154da577c283cb9e", }, + { + name: "long prefix with nil params truncates prefix preserving hash", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 31 chars + base: "default/my-taskrun", + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-2d162955e9c9fcfef736fd389e7fd796", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -201,6 +209,62 @@ func TestGenerateDeterministicNameFromSpec(t *testing.T) { args: golden, want: "prefix-ff25bd24688ab610bdc530a5ab3aabbd", }, + { + name: "long prefix at exactly max length is not truncated", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 30 chars, name = 30+1+32 = 63 = maxLength + base: "default/my-taskrun", + params: []v1.Param{{ + Name: "foo", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "bar"}, + }}, + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-da9d0a4501a276745547b4b4b21a2e77", // exactly 63, fits + }, + { + name: "long prefix exceeding max length truncates prefix not hash", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 31 chars, would be 64 > maxLength + base: "default/my-taskrun", + params: []v1.Param{{ + Name: "foo", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "bar"}, + }}, + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-da9d0a4501a276745547b4b4b21a2e77", // prefix trimmed to 30, hash preserved + }, + { + name: "very long prefix truncates prefix preserving hash", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 50 chars + base: "default/my-taskrun", + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-2d162955e9c9fcfef736fd389e7fd796", // prefix trimmed to 30, hash preserved + }, + { + name: "different inputs with same long prefix produce different names", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 31 chars + base: "default/other-taskrun", + params: []v1.Param{{ + Name: "baz", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "qux"}, + }}, + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-5592e5f983640b4274071dbe1c09068d", // same prefix length, different hash + }, + { + name: "prefix under max length returns full name", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 29 chars, name = 62 chars + base: "default/my-taskrun", + params: []v1.Param{{ + Name: "foo", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "bar"}, + }}, + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa-da9d0a4501a276745547b4b4b21a2e77", // 62 chars, no truncation + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {
5e4905fb6754Fix panic in GenerateDeterministicNameFromSpec with long resolver names
2 files changed · +79 −7
pkg/resolution/resource/name.go+15 −7 modified@@ -21,7 +21,6 @@ import ( "hash" "hash/fnv" "sort" - "strings" v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" "github.com/tektoncd/pipeline/pkg/apis/resolution/v1beta1" @@ -77,6 +76,19 @@ func nameHasher() hash.Hash { return fnv.New128a() } +// sanitizedName builds a "{prefix}-{hash}" string that fits within the +// DNS-1123 label max length. If the prefix is too long, it is truncated +// so that the full hash is always preserved, maintaining determinism and +// uniqueness. +func sanitizedName(prefix string, hasher hash.Hash) string { + hex := fmt.Sprintf("%x", hasher.Sum(nil)) + maxPrefixLen := maxLength - len(hex) - 1 // 1 for the "-" separator + if len(prefix) > maxPrefixLen { + prefix = prefix[:maxPrefixLen] + } + return fmt.Sprintf("%s-%s", prefix, hex) +} + // GenerateDeterministicNameFromSpec makes a best-effort attempt to create a // unique but reproducible name for use in a Request. The returned value // will have the format {prefix}-{hash} where {prefix} is @@ -89,7 +101,7 @@ func GenerateDeterministicNameFromSpec(prefix, base string, resolutionSpec *v1be } if resolutionSpec == nil { - return fmt.Sprintf("%s-%x", prefix, hasher.Sum(nil)), nil + return sanitizedName(prefix, hasher), nil } params := resolutionSpec.Params sortedParams := make(v1.Params, len(params)) @@ -123,11 +135,7 @@ func GenerateDeterministicNameFromSpec(prefix, base string, resolutionSpec *v1be return "", err } } - name := fmt.Sprintf("%s-%x", prefix, hasher.Sum(nil)) - if maxLength > len(name) { - return name, nil - } - return name[:strings.LastIndex(name[:maxLength], " ")], nil + return sanitizedName(prefix, hasher), nil } // GenerateErrorLogString makes a best effort attempt to get the name of the task
pkg/resolution/resource/name_test.go+64 −0 modified@@ -101,6 +101,14 @@ func TestGenerateDeterministicName(t *testing.T) { args: golden, want: "prefix-ba2f256f318de7f4154da577c283cb9e", }, + { + name: "long prefix with nil params truncates prefix preserving hash", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 31 chars + base: "default/my-taskrun", + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-2d162955e9c9fcfef736fd389e7fd796", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -201,6 +209,62 @@ func TestGenerateDeterministicNameFromSpec(t *testing.T) { args: golden, want: "prefix-ff25bd24688ab610bdc530a5ab3aabbd", }, + { + name: "long prefix at exactly max length is not truncated", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 30 chars, name = 30+1+32 = 63 = maxLength + base: "default/my-taskrun", + params: []v1.Param{{ + Name: "foo", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "bar"}, + }}, + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-da9d0a4501a276745547b4b4b21a2e77", // exactly 63, fits + }, + { + name: "long prefix exceeding max length truncates prefix not hash", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 31 chars, would be 64 > maxLength + base: "default/my-taskrun", + params: []v1.Param{{ + Name: "foo", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "bar"}, + }}, + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-da9d0a4501a276745547b4b4b21a2e77", // prefix trimmed to 30, hash preserved + }, + { + name: "very long prefix truncates prefix preserving hash", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 50 chars + base: "default/my-taskrun", + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-2d162955e9c9fcfef736fd389e7fd796", // prefix trimmed to 30, hash preserved + }, + { + name: "different inputs with same long prefix produce different names", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 31 chars + base: "default/other-taskrun", + params: []v1.Param{{ + Name: "baz", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "qux"}, + }}, + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-5592e5f983640b4274071dbe1c09068d", // same prefix length, different hash + }, + { + name: "prefix under max length returns full name", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 29 chars, name = 62 chars + base: "default/my-taskrun", + params: []v1.Param{{ + Name: "foo", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "bar"}, + }}, + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa-da9d0a4501a276745547b4b4b21a2e77", // 62 chars, no truncation + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {
0fa2d66cff81Fix panic in GenerateDeterministicNameFromSpec with long resolver names
2 files changed · +79 −7
pkg/resolution/resource/name.go+15 −7 modified@@ -21,7 +21,6 @@ import ( "hash" "hash/fnv" "sort" - "strings" v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" "github.com/tektoncd/pipeline/pkg/apis/resolution/v1beta1" @@ -77,6 +76,19 @@ func nameHasher() hash.Hash { return fnv.New128a() } +// sanitizedName builds a "{prefix}-{hash}" string that fits within the +// DNS-1123 label max length. If the prefix is too long, it is truncated +// so that the full hash is always preserved, maintaining determinism and +// uniqueness. +func sanitizedName(prefix string, hasher hash.Hash) string { + hex := fmt.Sprintf("%x", hasher.Sum(nil)) + maxPrefixLen := maxLength - len(hex) - 1 // 1 for the "-" separator + if len(prefix) > maxPrefixLen { + prefix = prefix[:maxPrefixLen] + } + return fmt.Sprintf("%s-%s", prefix, hex) +} + // GenerateDeterministicNameFromSpec makes a best-effort attempt to create a // unique but reproducible name for use in a Request. The returned value // will have the format {prefix}-{hash} where {prefix} is @@ -89,7 +101,7 @@ func GenerateDeterministicNameFromSpec(prefix, base string, resolutionSpec *v1be } if resolutionSpec == nil { - return fmt.Sprintf("%s-%x", prefix, hasher.Sum(nil)), nil + return sanitizedName(prefix, hasher), nil } params := resolutionSpec.Params sortedParams := make(v1.Params, len(params)) @@ -123,11 +135,7 @@ func GenerateDeterministicNameFromSpec(prefix, base string, resolutionSpec *v1be return "", err } } - name := fmt.Sprintf("%s-%x", prefix, hasher.Sum(nil)) - if maxLength > len(name) { - return name, nil - } - return name[:strings.LastIndex(name[:maxLength], " ")], nil + return sanitizedName(prefix, hasher), nil } // GenerateErrorLogString makes a best effort attempt to get the name of the task
pkg/resolution/resource/name_test.go+64 −0 modified@@ -101,6 +101,14 @@ func TestGenerateDeterministicName(t *testing.T) { args: golden, want: "prefix-ba2f256f318de7f4154da577c283cb9e", }, + { + name: "long prefix with nil params truncates prefix preserving hash", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 31 chars + base: "default/my-taskrun", + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-2d162955e9c9fcfef736fd389e7fd796", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -201,6 +209,62 @@ func TestGenerateDeterministicNameFromSpec(t *testing.T) { args: golden, want: "prefix-ff25bd24688ab610bdc530a5ab3aabbd", }, + { + name: "long prefix at exactly max length is not truncated", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 30 chars, name = 30+1+32 = 63 = maxLength + base: "default/my-taskrun", + params: []v1.Param{{ + Name: "foo", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "bar"}, + }}, + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-da9d0a4501a276745547b4b4b21a2e77", // exactly 63, fits + }, + { + name: "long prefix exceeding max length truncates prefix not hash", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 31 chars, would be 64 > maxLength + base: "default/my-taskrun", + params: []v1.Param{{ + Name: "foo", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "bar"}, + }}, + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-da9d0a4501a276745547b4b4b21a2e77", // prefix trimmed to 30, hash preserved + }, + { + name: "very long prefix truncates prefix preserving hash", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 50 chars + base: "default/my-taskrun", + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-2d162955e9c9fcfef736fd389e7fd796", // prefix trimmed to 30, hash preserved + }, + { + name: "different inputs with same long prefix produce different names", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 31 chars + base: "default/other-taskrun", + params: []v1.Param{{ + Name: "baz", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "qux"}, + }}, + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-5592e5f983640b4274071dbe1c09068d", // same prefix length, different hash + }, + { + name: "prefix under max length returns full name", + args: args{ + prefix: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // 29 chars, name = 62 chars + base: "default/my-taskrun", + params: []v1.Param{{ + Name: "foo", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "bar"}, + }}, + }, + want: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa-da9d0a4501a276745547b4b4b21a2e77", // 62 chars, no truncation + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {
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
9- github.com/advisories/GHSA-cv4x-93xx-wgfjghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-33022ghsaADVISORY
- github.com/tektoncd/pipeline/commit/01673237c464cfac7e286183f5c9e9d6ec951a64ghsaWEB
- github.com/tektoncd/pipeline/commit/0fa2d66cff814838c3a10cce252104c7fe618932ghsaWEB
- github.com/tektoncd/pipeline/commit/5e4905fb6754efa5ecea54de195738d73fb0e01dghsaWEB
- github.com/tektoncd/pipeline/commit/5eead3f859b9f938e86039e4d29185092c1d4ee6ghsax_refsource_MISCWEB
- github.com/tektoncd/pipeline/commit/ebc197e2b9733deedaa1624212ec66dcdf61eaafghsaWEB
- github.com/tektoncd/pipeline/commit/edc64bbf22323fcf218170f19047c9bcd8163e90ghsaWEB
- github.com/tektoncd/pipeline/security/advisories/GHSA-cv4x-93xx-wgfjghsax_refsource_CONFIRMWEB
News mentions
50- Fuel Tank Breaches Expand Scope of Iran's Cyber OffensiveDark Reading · May 18, 2026
- TanStack weighs invitation-only pull requests after supply chain attackThe Register Security · May 18, 2026
- ⚡ Weekly Recap: Exchange 0-Day, npm Worm, Fake AI Repo, Cisco Exploit and MoreThe Hacker News · May 18, 2026
- SHub Reaper | macOS Stealer Spoofs Apple, Google, and Microsoft in a Single Attack ChainSentinelOne Labs · May 18, 2026
- Project Glasswing: what Mythos showed usCloudflare Blog · May 18, 2026
- Lyrie: Open-source autonomous pentesting agentHelp Net Security · May 18, 2026
- Week in review: Cisco patches SD-WAN 0-day, unpatched Microsoft Exchange Server flaw exploitedHelp Net Security · May 17, 2026
- Living Off the Pipeline: Defending Against CI/CD SubversionSentinelOne Labs · May 15, 2026
- Microsoft to automatically roll back faulty Windows driversBleepingComputer · May 15, 2026
- TanStack Supply Chain Attack Hits Two OpenAI Employee Devices, Forces macOS UpdatesThe Hacker News · May 15, 2026
- Frequently asked questions about the continued exploitation of Cisco Catalyst SD-WAN vulnerabilities (CVE-2026-20182)Tenable Blog · May 15, 2026
- Fragnesia (CVE-2026-46300): Frequently asked questions about new Linux Kernel XFRM ESP-in-TCP privilege escalationTenable Blog · May 14, 2026
- OpenAI confirms security breach in TanStack supply chain attackBleepingComputer · May 14, 2026
- Our billing pipeline was suddenly slow. The culprit was a hidden bottleneck in ClickHouseCloudflare Blog · May 14, 2026
- G7 Countries Release AI SBOM GuidanceSecurityWeek · May 14, 2026
- Microsoft’s WinUI agent plugin trims token use by over 70% during developmentHelp Net Security · May 14, 2026
- Vector embedding security gap exposes enterprise AI pipelinesHelp Net Security · May 14, 2026
- Welcome to the vulnpocalypse, as vendors use AI to find bugs and patches multiply like rabbitsThe Register Security · May 13, 2026
- Microsoft, Palo Alto Networks Find Many Vulnerabilities by Using AI on Their Own CodeSecurityWeek · May 13, 2026
- Microsoft's MDASH AI System Finds 16 Windows Flaws Fixed in Patch TuesdayThe Hacker News · May 13, 2026
- Thus Spoke…The GentlemenCheck Point Research · May 13, 2026
- [Webinar] How Modern Attack Paths Cross Code, Pipelines, and CloudThe Hacker News · May 13, 2026
- Sandyaa: Open-source autonomous security bug hunterHelp Net Security · May 13, 2026
- Fedora Hummingbird brings the container security model to a Linux host OSHelp Net Security · May 12, 2026
- Mini Shai-Hulud Hits TanStack npm PackagesInfosecurity Magazine · May 12, 2026
- Hugging Face Packages Weaponized With a Single File TweakDark Reading · May 12, 2026
- When "idle" isn't idle: how a Linux kernel optimization became a QUIC bugCloudflare Blog · May 12, 2026
- 20 Leaders Who Built the CISO Era: 2 Decades of ChangeDark Reading · May 12, 2026
- Mini Shai-Hulud Worm Compromises TanStack, Mistral AI, Guardrails AI & More PackagesThe Hacker News · May 12, 2026
- Shai Hulud attack ships signed malicious TanStack, Mistral npm packagesBleepingComputer · May 12, 2026
- TanStack, Mistral AI, UiPath Hit in Fresh Supply Chain AttackSecurityWeek · May 12, 2026
- HEIDI: Free IDE security plugin for open-source vulnerability checksHelp Net Security · May 12, 2026
- Official CheckMarx Jenkins package compromised with infostealerBleepingComputer · May 11, 2026
- Build Application Firewalls Aim to Stop the Next Supply Chain AttackSecurityWeek · May 11, 2026
- Checkmarx tackles another TeamPCP intrusion as Jenkins plugin sabotagedThe Register Security · May 11, 2026
- Checkmarx tackles another TeamPCP intrusion as Jenkins plugin sabotagedThe Register Security · May 11, 2026
- Week in review: cPanel vulnerability actively exploited, DigiCert breach, LinkedIn job scamsHelp Net Security · May 10, 2026
- Friday Squid Blogging: Giant Squid Live in the Waters of Western AustraliaSchneier on Security · May 8, 2026
- Dirty Frag (CVE-2026-43284, CVE-2026-43500): Frequently asked questions about this Linux kernel privilege escalation vulnerability chainTenable Blog · May 8, 2026
- Why the approaching flood of vulnerabilities changes everything — and what to do about itTenable Blog · May 8, 2026
- Zero Chaos: Scaling Detection Engineering at the Speed of Software, with Detection As CodeRapid7 Blog · May 8, 2026
- Quasar Linux RAT Steals Developer Credentials for Software Supply Chain CompromiseThe Hacker News · May 8, 2026
- New Linux PamDOORa Backdoor Uses PAM Modules to Steal SSH CredentialsThe Hacker News · May 8, 2026
- How Cloudflare responded to the “Copy Fail” Linux vulnerabilityCloudflare Blog · May 7, 2026
- Daemon Tools Developer Confirms Software Was TrojanizedInfosecurity Magazine · May 7, 2026
- From Stuxnet to ChatGPT: 20 News Events That Shaped CyberDark Reading · May 6, 2026
- Sophisticated Quasar Linux RAT Targets Software DevelopersSecurityWeek · May 6, 2026
- One in Eight Workers Has Sold Their Corporate LoginsInfosecurity Magazine · May 6, 2026
- Middle East Cyber Battle Field Broadens — Especially in UAEDark Reading · May 6, 2026
- DAEMON Tools Supply Chain Attack Compromises Official Installers with MalwareThe Hacker News · May 5, 2026