Repository: JanDeDobbeleer/oh-my-posh
Pull request #1440 (2021-12-15 18:06:26)

fix(kubectl): validate array length before indexing values
relates to #1439

### Prerequisites

- [ ] I have read and understood the `CONTRIBUTING` guide
- [ ] The commit message follows the [conventional commits][cc] guidelines
- [ ] Tests for the changes have been added (for bug fixes/features)
- [ ] Docs have been added / updated (for bug fixes/features)

### Description

[Description of the change]

<!---

Tips:

If you're not comfortable with working with Git, we're working a guide (https://ohmyposh.dev/docs/contributing_git) to help you out.
Oh My Posh advises GitKraken (https://www.gitkraken.com/invite/nQmDPR9D) as your preferred cross platform Git GUI power tool.

-->

[cc]: https://www.conventionalcommits.org/en/v1.0.0/#summary



Patch:
diff --git a/src/segment_kubectl.go b/src/segment_kubectl.go
index 0a875824..d9f86653 100644
--- a/src/segment_kubectl.go
+++ b/src/segment_kubectl.go
@@ -121,6 +121,9 @@ func (k *kubectl) doCallKubectl() bool {
 	}
 
 	values := strings.Split(result, ",")
+	if len(values) < 4 {
+		return false
+	}
 	k.Context = values[0]
 	k.Namespace = values[1]
 	k.User = values[2]


Test patch:
diff --git a/src/segment_kubectl_test.go b/src/segment_kubectl_test.go
index 9af7e04d..76a59458 100644
--- a/src/segment_kubectl_test.go
+++ b/src/segment_kubectl_test.go
@@ -7,53 +7,8 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
-type kubectlArgs struct {
-	kubectlExists       bool
-	kubectlErr          bool
-	kubeconfig          string
-	parseKubeConfig     bool
-	template            string
-	displayError        bool
-	kubectlOutContext   string
-	kubectlOutNamespace string
-	kubectlOutUser      string
-	kubectlOutCluster   string
-	files               map[string]string
-}
-
 const testKubectlAllInfoTemplate = "{{.Context}} :: {{.Namespace}} :: {{.User}} :: {{.Cluster}}"
 
-func bootStrapKubectlTest(args *kubectlArgs) *kubectl {
-	env := new(MockedEnvironment)
-	env.On("hasCommand", "kubectl").Return(args.kubectlExists)
-	kubectlOut := args.kubectlOutContext + "," + args.kubectlOutNamespace + "," + args.kubectlOutUser + "," + args.kubectlOutCluster
-	var kubectlErr error
-	if args.kubectlErr {
-		kubectlErr = &commandError{
-			err:      "oops",
-			exitCode: 1,
-		}
-	}
-	env.On("runCommand", "kubectl",
-		[]string{"config", "view", "--minify", "--output", "jsonpath={..current-context},{..namespace},{..context.user},{..context.cluster}"}).Return(kubectlOut, kubectlErr)
-
-	env.On("getenv", "KUBECONFIG").Return(args.kubeconfig)
-	for path, content := range args.files {
-		env.On("getFileContent", path).Return(content)
-	}
-	env.On("homeDir", nil).Return("testhome")
-
-	k := &kubectl{
-		env: env,
-		props: map[Property]interface{}{
-			SegmentTemplate: args.template,
-			DisplayError:    args.displayError,
-			ParseKubeConfig: args.parseKubeConfig,
-		},
-	}
-	return k
-}
-
 func TestKubectlSegment(t *testing.T) {
 	standardTemplate := "{{.Context}}{{if .Namespace}} :: {{.Namespace}}{{end}}"
 	lsep := string(filepath.ListSeparator)
@@ -75,48 +30,127 @@ func TestKubectlSegment(t *testing.T) {
 		Files           map[string]string
 	}{
 		{Case: "disabled", Template: standardTemplate, KubectlExists: false, Context: "aaa", Namespace: "bbb", ExpectedString: "", ExpectedEnabled: false},
-		{Case: "normal", Template: standardTemplate, KubectlExists: true, Context: "aaa", Namespace: "bbb", ExpectedString: "aaa :: bbb", ExpectedEnabled: true},
-		{Case: "all information", Template: testKubectlAllInfoTemplate, KubectlExists: true, Context: "aaa", Namespace: "bbb", User: "ccc", Cluster: "ddd",
-			ExpectedString: "aaa :: bbb :: ccc :: ddd", ExpectedEnabled: true},
-		{Case: "no namespace", Template: standardTemplate, KubectlExists: true, Context: "aaa", Namespace: "", ExpectedString: "aaa", ExpectedEnabled: true},
-		{Case: "kubectl error", Template: standardTemplate, DisplayError: true, KubectlExists: true, Context: "aaa", Namespace: "bbb", KubectlErr: true,
-			ExpectedString: "KUBECTL ERR :: KUBECTL ERR", ExpectedEnabled: true},
+		{
+			Case:            "not enough arguments",
+			Template:        standardTemplate,
+			KubectlExists:   true,
+			Context:         "aaa",
+			Namespace:       "bbb",
+			ExpectedString:  "",
+			ExpectedEnabled: false,
+		},
+		{
+			Case:            "all information",
+			Template:        testKubectlAllInfoTemplate,
+			KubectlExists:   true,
+			Context:         "aaa",
+			Namespace:       "bbb",
+			User:            "ccc",
+			Cluster:         "ddd",
+			ExpectedString:  "aaa :: bbb :: ccc :: ddd",
+			ExpectedEnabled: true,
+		},
+		{Case: "no namespace", Template: standardTemplate, KubectlExists: true, Context: "aaa", Namespace: "", ExpectedString: "", ExpectedEnabled: false},
+		{
+			Case:            "kubectl error",
+			Template:        standardTemplate,
+			DisplayError:    true,
+			KubectlExists:   true,
+			Context:         "aaa",
+			Namespace:       "bbb",
+			KubectlErr:      true,
+			ExpectedString:  "KUBECTL ERR :: KUBECTL ERR",
+			ExpectedEnabled: true,
+		},
 		{Case: "kubectl error hidden", Template: standardTemplate, DisplayError: false, KubectlExists: true, Context: "aaa", Namespace: "bbb", KubectlErr: true, ExpectedEnabled: false},
-		{Case: "kubeconfig home", Template: testKubectlAllInfoTemplate, ParseKubeConfig: true, Files: testKubeConfigFiles, ExpectedString: "aaa :: bbb :: ccc :: ddd",
-			ExpectedEnabled: true},
-		{Case: "kubeconfig multiple current marker first", Template: testKubectlAllInfoTemplate, ParseKubeConfig: true,
-			Kubeconfig: "" + lsep + "currentcontextmarker" + lsep + "contextdefinition" + lsep + "contextredefinition",
-			Files:      testKubeConfigFiles, ExpectedString: "ctx :: ns :: usr :: cl", ExpectedEnabled: true},
-		{Case: "kubeconfig multiple context first", Template: testKubectlAllInfoTemplate, ParseKubeConfig: true,
-			Kubeconfig: "contextdefinition" + lsep + "contextredefinition" + lsep + "currentcontextmarker" + lsep,
-			Files:      testKubeConfigFiles, ExpectedString: "ctx :: ns :: usr :: cl", ExpectedEnabled: true},
-		{Case: "kubeconfig error hidden", Template: testKubectlAllInfoTemplate, ParseKubeConfig: true, Kubeconfig: "invalid", Files: testKubeConfigFiles, ExpectedEnabled: false},
-		{Case: "kubeconfig error", Template: testKubectlAllInfoTemplate, ParseKubeConfig: true,
-			Kubeconfig: "invalid", Files: testKubeConfigFiles, DisplayError: true,
-			ExpectedString: "KUBECONFIG ERR :: KUBECONFIG ERR :: KUBECONFIG ERR :: KUBECONFIG ERR", ExpectedEnabled: true},
-		{Case: "kubeconfig incomplete", Template: testKubectlAllInfoTemplate, ParseKubeConfig: true,
-			Kubeconfig: "currentcontextmarker" + lsep + "contextdefinitionincomplete",
-			Files:      testKubeConfigFiles, ExpectedString: "ctx ::  ::  :: ", ExpectedEnabled: true},
+		{
+			Case:            "kubeconfig home",
+			Template:        testKubectlAllInfoTemplate,
+			ParseKubeConfig: true,
+			Files:           testKubeConfigFiles,
+			ExpectedString:  "aaa :: bbb :: ccc :: ddd",
+			ExpectedEnabled: true,
+		},
+		{
+			Case:            "kubeconfig multiple current marker first",
+			Template:        testKubectlAllInfoTemplate,
+			ParseKubeConfig: true,
+			Kubeconfig:      "" + lsep + "currentcontextmarker" + lsep + "contextdefinition" + lsep + "contextredefinition",
+			Files:           testKubeConfigFiles,
+			ExpectedString:  "ctx :: ns :: usr :: cl",
+			ExpectedEnabled: true,
+		},
+		{
+			Case:     "kubeconfig multiple context first",
+			Template: testKubectlAllInfoTemplate, ParseKubeConfig: true,
+			Kubeconfig:      "contextdefinition" + lsep + "contextredefinition" + lsep + "currentcontextmarker" + lsep,
+			Files:           testKubeConfigFiles,
+			ExpectedString:  "ctx :: ns :: usr :: cl",
+			ExpectedEnabled: true,
+		},
+		{
+			Case: "kubeconfig error hidden", Template: testKubectlAllInfoTemplate, ParseKubeConfig: true, Kubeconfig: "invalid", Files: testKubeConfigFiles, ExpectedEnabled: false},
+		{
+			Case:            "kubeconfig error",
+			Template:        testKubectlAllInfoTemplate,
+			ParseKubeConfig: true,
+			Kubeconfig:      "invalid",
+			Files:           testKubeConfigFiles,
+			DisplayError:    true,
+			ExpectedString:  "KUBECONFIG ERR :: KUBECONFIG ERR :: KUBECONFIG ERR :: KUBECONFIG ERR",
+			ExpectedEnabled: true,
+		},
+		{
+			Case:            "kubeconfig incomplete",
+			Template:        testKubectlAllInfoTemplate,
+			ParseKubeConfig: true,
+			Kubeconfig:      "currentcontextmarker" + lsep + "contextdefinitionincomplete",
+			Files:           testKubeConfigFiles,
+			ExpectedString:  "ctx ::  ::  :: ",
+			ExpectedEnabled: true,
+		},
 	}
 
 	for _, tc := range cases {
-		args := &kubectlArgs{
-			kubectlExists:       tc.KubectlExists,
-			template:            tc.Template,
-			displayError:        tc.DisplayError,
-			kubectlOutContext:   tc.Context,
-			kubectlOutNamespace: tc.Namespace,
-			kubectlOutUser:      tc.User,
-			kubectlOutCluster:   tc.Cluster,
-			kubectlErr:          tc.KubectlErr,
-			parseKubeConfig:     tc.ParseKubeConfig,
-			files:               tc.Files,
-			kubeconfig:          tc.Kubeconfig,
+		env := new(MockedEnvironment)
+		env.On("hasCommand", "kubectl").Return(tc.KubectlExists)
+		addCommaAndvalue := func(s string) string {
+			if s == "" {
+				return ""
+			}
+			return "," + s
+		}
+		kubectlOut := tc.Context
+		kubectlOut += addCommaAndvalue(tc.Namespace)
+		kubectlOut += addCommaAndvalue(tc.User)
+		kubectlOut += addCommaAndvalue(tc.Cluster)
+		var kubectlErr error
+		if tc.KubectlErr {
+			kubectlErr = &commandError{
+				err:      "oops",
+				exitCode: 1,
+			}
+		}
+		env.On("runCommand", "kubectl",
+			[]string{"config", "view", "--minify", "--output", "jsonpath={..current-context},{..namespace},{..context.user},{..context.cluster}"}).Return(kubectlOut, kubectlErr)
+
+		env.On("getenv", "KUBECONFIG").Return(tc.Kubeconfig)
+		for path, content := range tc.Files {
+			env.On("getFileContent", path).Return(content)
+		}
+		env.On("homeDir", nil).Return("testhome")
+
+		k := &kubectl{
+			env: env,
+			props: map[Property]interface{}{
+				SegmentTemplate: tc.Template,
+				DisplayError:    tc.DisplayError,
+				ParseKubeConfig: tc.ParseKubeConfig,
+			},
 		}
-		kubectl := bootStrapKubectlTest(args)
-		assert.Equal(t, tc.ExpectedEnabled, kubectl.enabled(), tc.Case)
+		assert.Equal(t, tc.ExpectedEnabled, k.enabled(), tc.Case)
 		if tc.ExpectedEnabled {
-			assert.Equal(t, tc.ExpectedString, kubectl.string(), tc.Case)
+			assert.Equal(t, tc.ExpectedString, k.string(), tc.Case)
 		}
 	}
 }