VYPR
Moderate severityNVD Advisory· Published Mar 20, 2026· Updated Mar 20, 2026

Tekton Pipelines: Controller can panic when setting long resolver names in TaskRun/PipelineRun

CVE-2026-33022

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.

PackageAffected versionsPatched versions
github.com/tektoncd/pipelineGo
>= 0.60.0, < 1.0.11.0.1
github.com/tektoncd/pipelineGo
>= 1.1.0, < 1.3.31.3.3
github.com/tektoncd/pipelineGo
>= 1.4.0, < 1.6.11.6.1
github.com/tektoncd/pipelineGo
>= 1.7.0, < 1.9.21.9.2
github.com/tektoncd/pipelineGo
>= 1.10.0, < 1.10.21.10.2

Affected products

2
  • Tektoncd/Pipelinellm-fuzzy2 versions
    >=0.60.0 <=1.10.1+ 1 more
    • (no CPE)range: >=0.60.0 <=1.10.1
    • (no CPE)range: >= 0.60.0, < 1.0.1

Patches

6
5eead3f859b9

Fix panic in GenerateDeterministicNameFromSpec with long resolver names

https://github.com/tektoncd/pipelineVincent DemeesterMar 11, 2026via ghsa
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) {
    
ebc197e2b973

Fix panic in GenerateDeterministicNameFromSpec with long resolver names

https://github.com/tektoncd/pipelineVincent DemeesterMar 11, 2026via ghsa
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) {
    
edc64bbf2232

Fix panic in GenerateDeterministicNameFromSpec with long resolver names

https://github.com/tektoncd/pipelineVincent DemeesterMar 11, 2026via ghsa
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) {
    
01673237c464

Fix panic in GenerateDeterministicNameFromSpec with long resolver names

https://github.com/tektoncd/pipelineVincent DemeesterMar 11, 2026via ghsa
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) {
    
5e4905fb6754

Fix panic in GenerateDeterministicNameFromSpec with long resolver names

https://github.com/tektoncd/pipelineVincent DemeesterMar 11, 2026via ghsa
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) {
    
0fa2d66cff81

Fix panic in GenerateDeterministicNameFromSpec with long resolver names

https://github.com/tektoncd/pipelineVincent DemeesterMar 11, 2026via ghsa
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

News mentions

50