From cf309f8903f6217f5d9b94ba91c50d5f7f980ca7 Mon Sep 17 00:00:00 2001 From: David Leadbeater Date: Mon, 31 Oct 2022 01:08:43 +1000 Subject: [PATCH] Escape terminal special characters in kubectl (#112553) * Escape terminal special characters in kubectl * Add escaping for kubectl alpha events Kubernetes-commit: dad0e937c0f76344363eb691b2668490ffef8537 --- go.mod | 8 +++---- go.sum | 8 +++---- pkg/printers/tableprinter.go | 13 ++++++----- pkg/printers/tableprinter_test.go | 12 ++++++++++ pkg/printers/terminal.go | 39 +++++++++++++++++++++++++++++++ 5 files changed, 66 insertions(+), 14 deletions(-) create mode 100644 pkg/printers/terminal.go diff --git a/go.mod b/go.mod index d84f2d33..91589f22 100644 --- a/go.mod +++ b/go.mod @@ -16,8 +16,8 @@ require ( golang.org/x/text v0.4.0 gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.0.0-20221028075226-689257039cfb - k8s.io/apimachinery v0.0.0-20221028075009-0524d6c61445 - k8s.io/client-go v0.0.0-20221028075543-f87d0472f2aa + k8s.io/apimachinery v0.0.0-20221028155017-b03a432a2a6d + k8s.io/client-go v0.0.0-20221028155554-0d5739633518 k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 k8s.io/utils v0.0.0-20220922133306-665eaaec4324 sigs.k8s.io/kustomize/api v0.12.1 @@ -69,6 +69,6 @@ require ( replace ( k8s.io/api => k8s.io/api v0.0.0-20221028075226-689257039cfb - k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20221028075009-0524d6c61445 - k8s.io/client-go => k8s.io/client-go v0.0.0-20221028075543-f87d0472f2aa + k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20221028155017-b03a432a2a6d + k8s.io/client-go => k8s.io/client-go v0.0.0-20221028155554-0d5739633518 ) diff --git a/go.sum b/go.sum index 7f85df20..e771e0c5 100644 --- a/go.sum +++ b/go.sum @@ -491,10 +491,10 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.0.0-20221028075226-689257039cfb h1:QZsHf1k3xHSbYozSHt8DZOrH36F8MbdLP18n4ZkQ/1c= k8s.io/api v0.0.0-20221028075226-689257039cfb/go.mod h1:96woDPRgmgH0tVTXgsLdv0cwHXPUvZ97ghr8sMUtxx0= -k8s.io/apimachinery v0.0.0-20221028075009-0524d6c61445 h1:hXu9HT0t7U8yoa+ag/YUgy2/7yj0NvlVHbHjqw1R3qs= -k8s.io/apimachinery v0.0.0-20221028075009-0524d6c61445/go.mod h1:nG4gaqgN0dCXlF11Oh/vA7GosujtSKqnr19oq8ag6qA= -k8s.io/client-go v0.0.0-20221028075543-f87d0472f2aa h1:9+11V9Ct7evjC9yl+E3qfBgJi21lzG9VHpwzCx0yrIw= -k8s.io/client-go v0.0.0-20221028075543-f87d0472f2aa/go.mod h1:hg9A6iQF4ggK+BmrByy91yyQ/gkUlEPXQEfJYh4iWM8= +k8s.io/apimachinery v0.0.0-20221028155017-b03a432a2a6d h1:fg/DbLqFKxFESf3AnU5iwCexZWOUnFFLb0JraG20wZo= +k8s.io/apimachinery v0.0.0-20221028155017-b03a432a2a6d/go.mod h1:zSkBXgO5G/dSQOe256tx5Yo2OJytojpY3bsXu/4/ZJE= +k8s.io/client-go v0.0.0-20221028155554-0d5739633518 h1:KlSjZkXeVyocbVpEU157nadMdQIfWXchaItVtjmMVUE= +k8s.io/client-go v0.0.0-20221028155554-0d5739633518/go.mod h1:9OZTm80DH1AI7P4cpx8yehVlTU1xZQCsMtAtlJYLWDw= k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= diff --git a/pkg/printers/tableprinter.go b/pkg/printers/tableprinter.go index 87bc3f41..54859665 100644 --- a/pkg/printers/tableprinter.go +++ b/pkg/printers/tableprinter.go @@ -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)) } } } diff --git a/pkg/printers/tableprinter_test.go b/pkg/printers/tableprinter_test.go index b2caaa12..edbff3ff 100644 --- a/pkg/printers/tableprinter_test.go +++ b/pkg/printers/tableprinter_test.go @@ -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^[ `, }, } diff --git a/pkg/printers/terminal.go b/pkg/printers/terminal.go new file mode 100644 index 00000000..5a59491e --- /dev/null +++ b/pkg/printers/terminal.go @@ -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) +}