VYPR
Low severityNVD Advisory· Published Jan 7, 2022· Updated Sep 16, 2024

ANSI escape characters in kubectl output are not being filtered

CVE-2021-25743

Description

kubectl does not neutralize escape, meta or control sequences contained in the raw data it outputs to a terminal. This includes but is not limited to the unstructured string fields in objects such as Events.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
k8s.io/kubernetesGo
< 1.26.0-alpha.31.26.0-alpha.3

Affected products

1

Patches

1
dad0e937c0f7

Escape terminal special characters in kubectl (#112553)

https://github.com/kubernetes/kubernetesDavid LeadbeaterOct 30, 2022via ghsa
9 files changed · +139 13
  • staging/src/k8s.io/cli-runtime/pkg/printers/tableprinter.go+7 6 modified
    @@ -212,18 +212,19 @@ func printTable(table *metav1.Table, output io.Writer, options PrintOptions) err
     				case string:
     					print := val
     					truncated := false
    -					// truncate at newlines
    -					newline := strings.Index(print, "\n")
    -					if newline >= 0 {
    +					// Truncate at the first newline, carriage return or formfeed
    +					// (treated as a newline by tabwriter).
    +					breakchar := strings.IndexAny(print, "\f\n\r")
    +					if breakchar >= 0 {
     						truncated = true
    -						print = print[:newline]
    +						print = print[:breakchar]
     					}
    -					fmt.Fprint(output, print)
    +					WriteEscaped(output, print)
     					if truncated {
     						fmt.Fprint(output, "...")
     					}
     				default:
    -					fmt.Fprint(output, val)
    +					WriteEscaped(output, fmt.Sprint(val))
     				}
     			}
     		}
    
  • staging/src/k8s.io/cli-runtime/pkg/printers/tableprinter_test.go+12 0 modified
    @@ -769,6 +769,18 @@ test1   20h   This is first line which is long and goes for on and on and on an
     			},
     			expected: `NAME    AGE   DESCRIPTION
     test1   20h   This is first...
    +`,
    +		},
    +		// terminal special character, should be escaped
    +		{
    +			columns: []metav1.TableColumnDefinition{
    +				{Name: "Name", Type: "string"},
    +			},
    +			rows: []metav1.TableRow{
    +				{Cells: []interface{}{"test1\x1b"}},
    +			},
    +			expected: `NAME
    +test1^[
     `,
     		},
     	}
    
  • staging/src/k8s.io/cli-runtime/pkg/printers/terminal.go+39 0 added
    @@ -0,0 +1,39 @@
    +/*
    +Copyright 2022 The Kubernetes Authors.
    +
    +Licensed under the Apache License, Version 2.0 (the "License");
    +you may not use this file except in compliance with the License.
    +You may obtain a copy of the License at
    +
    +    http://www.apache.org/licenses/LICENSE-2.0
    +
    +Unless required by applicable law or agreed to in writing, software
    +distributed under the License is distributed on an "AS IS" BASIS,
    +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +See the License for the specific language governing permissions and
    +limitations under the License.
    +*/
    +
    +package printers
    +
    +import (
    +	"io"
    +	"strings"
    +)
    +
    +// terminalEscaper replaces ANSI escape sequences and other terminal special
    +// characters to avoid terminal escape character attacks (issue #101695).
    +var terminalEscaper = strings.NewReplacer("\x1b", "^[", "\r", "\\r")
    +
    +// WriteEscaped replaces unsafe terminal characters with replacement strings
    +// and writes them to the given writer.
    +func WriteEscaped(writer io.Writer, output string) error {
    +	_, err := terminalEscaper.WriteString(writer, output)
    +	return err
    +}
    +
    +// EscapeTerminal escapes terminal special characters in a human readable (but
    +// non-reversible) format.
    +func EscapeTerminal(in string) string {
    +	return terminalEscaper.Replace(in)
    +}
    
  • staging/src/k8s.io/kubectl/pkg/cmd/events/event_printer.go+6 4 modified
    @@ -26,6 +26,7 @@ import (
     	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
     	"k8s.io/apimachinery/pkg/runtime"
     	"k8s.io/apimachinery/pkg/util/duration"
    +	"k8s.io/cli-runtime/pkg/printers"
     )
     
     // EventPrinter stores required fields to be used for
    @@ -72,10 +73,11 @@ func (ep *EventPrinter) printOneEvent(w io.Writer, e corev1.Event) {
     	}
     	fmt.Fprintf(w, "%s\t%s\t%s\t%s/%s\t%v\n",
     		interval,
    -		e.Type,
    -		e.Reason,
    -		e.InvolvedObject.Kind, e.InvolvedObject.Name,
    -		strings.TrimSpace(e.Message),
    +		printers.EscapeTerminal(e.Type),
    +		printers.EscapeTerminal(e.Reason),
    +		printers.EscapeTerminal(e.InvolvedObject.Kind),
    +		printers.EscapeTerminal(e.InvolvedObject.Name),
    +		printers.EscapeTerminal(strings.TrimSpace(e.Message)),
     	)
     }
     
    
  • staging/src/k8s.io/kubectl/pkg/cmd/events/event_printer_test.go+34 0 modified
    @@ -208,6 +208,40 @@ foo	12m (x3 over 20m)	Normal	ScalingReplicaSet	Deployment/bar	Scaled up replica
     				},
     			},
     			expected: `foo	12m (x3 over 20m)	Normal	ScalingReplicaSet	Deployment/bar	Scaled up replica set bar-002 to 1
    +`,
    +		},
    +		{
    +			printer: EventPrinter{
    +				NoHeaders:     false,
    +				AllNamespaces: false,
    +			},
    +			obj: &corev1.EventList{
    +				Items: []corev1.Event{
    +					{
    +						ObjectMeta: metav1.ObjectMeta{
    +							Name:      "bar-000",
    +							Namespace: "foo",
    +						},
    +						InvolvedObject: corev1.ObjectReference{
    +							APIVersion: "apps/v1",
    +							Kind:       "Deployment",
    +							Name:       "bar\x1b",
    +							Namespace:  "foo",
    +						},
    +						Type:                "test\x1b",
    +						Reason:              "test\x1b",
    +						Message:             "\x1b",
    +						ReportingController: "deployment-controller",
    +						EventTime:           metav1.NewMicroTime(time.Now().Add(-20 * time.Minute)),
    +						Series: &corev1.EventSeries{
    +							Count:            3,
    +							LastObservedTime: metav1.NewMicroTime(time.Now().Add(-1 * time.Minute)),
    +						},
    +					},
    +				},
    +			},
    +			expected: `LAST SEEN	TYPE	REASON	OBJECT	MESSAGE
    +60s (x3 over 20m)	test^[	test^[	Deployment/bar^[	^[
     `,
     		},
     	}
    
  • staging/src/k8s.io/kubectl/pkg/cmd/get/customcolumn.go+1 1 modified
    @@ -252,7 +252,7 @@ func (s *CustomColumnsPrinter) printOneObject(obj runtime.Object, parsers []*jso
     		}
     		for arrIx := range values {
     			for valIx := range values[arrIx] {
    -				valueStrings = append(valueStrings, fmt.Sprintf("%v", values[arrIx][valIx].Interface()))
    +				valueStrings = append(valueStrings, printers.EscapeTerminal(fmt.Sprint(values[arrIx][valIx].Interface())))
     			}
     		}
     		columns[ix] = strings.Join(valueStrings, ",")
    
  • staging/src/k8s.io/kubectl/pkg/cmd/get/customcolumn_test.go+16 0 modified
    @@ -311,6 +311,22 @@ foo    baz
     			obj: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}, TypeMeta: metav1.TypeMeta{APIVersion: "baz"}},
     			expectedOutput: `NAME   API_VERSION   NOT_FOUND
     foo    baz           <none>
    +`,
    +		},
    +		{
    +			columns: []Column{
    +				{
    +					Header:    "NAME",
    +					FieldSpec: "{.metadata.name}",
    +				},
    +			},
    +			obj: &corev1.PodList{
    +				Items: []corev1.Pod{
    +					{ObjectMeta: metav1.ObjectMeta{Name: "\x1b \r"}},
    +				},
    +			},
    +			expectedOutput: `NAME
    +^[ \r
     `,
     		},
     	}
    
  • staging/src/k8s.io/kubectl/pkg/describe/describe.go+5 2 modified
    @@ -65,6 +65,7 @@ import (
     	"k8s.io/apimachinery/pkg/util/intstr"
     	"k8s.io/apimachinery/pkg/util/sets"
     	"k8s.io/cli-runtime/pkg/genericclioptions"
    +	"k8s.io/cli-runtime/pkg/printers"
     	runtimeresource "k8s.io/cli-runtime/pkg/resource"
     	"k8s.io/client-go/dynamic"
     	clientset "k8s.io/client-go/kubernetes"
    @@ -148,11 +149,13 @@ func (pw *prefixWriter) Write(level int, format string, a ...interface{}) {
     	for i := 0; i < level; i++ {
     		prefix += levelSpace
     	}
    -	fmt.Fprintf(pw.out, prefix+format, a...)
    +	output := fmt.Sprintf(prefix+format, a...)
    +	printers.WriteEscaped(pw.out, output)
     }
     
     func (pw *prefixWriter) WriteLine(a ...interface{}) {
    -	fmt.Fprintln(pw.out, a...)
    +	output := fmt.Sprintln(a...)
    +	printers.WriteEscaped(pw.out, output)
     }
     
     func (pw *prefixWriter) Flush() {
    
  • staging/src/k8s.io/kubectl/pkg/describe/describe_test.go+19 0 modified
    @@ -5507,3 +5507,22 @@ func TestControllerRef(t *testing.T) {
     		t.Errorf("unexpected out: %s", out)
     	}
     }
    +
    +func TestDescribeTerminalEscape(t *testing.T) {
    +	fake := fake.NewSimpleClientset(&corev1.ConfigMap{
    +		ObjectMeta: metav1.ObjectMeta{
    +			Name:        "mycm",
    +			Namespace:   "foo",
    +			Annotations: map[string]string{"annotation1": "terminal escape: \x1b"},
    +		},
    +	})
    +	c := &describeClient{T: t, Namespace: "foo", Interface: fake}
    +	d := ConfigMapDescriber{c}
    +	out, err := d.Describe("foo", "mycm", DescriberSettings{ShowEvents: true})
    +	if err != nil {
    +		t.Errorf("unexpected error: %v", err)
    +	}
    +	if strings.Contains(out, "\x1b") || !strings.Contains(out, "^[") {
    +		t.Errorf("unexpected out: %s", out)
    +	}
    +}
    

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

7

News mentions

0

No linked articles in our index yet.