github.com/argoproj/argo-cd/v3@v3.2.1/server/application/terminal_test.go (about) 1 package application 2 3 import ( 4 "net/http" 5 "net/http/httptest" 6 "strings" 7 "testing" 8 9 "github.com/argoproj/gitops-engine/pkg/utils/kube" 10 "github.com/stretchr/testify/assert" 11 12 appv1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1" 13 "github.com/argoproj/argo-cd/v3/util/argo" 14 "github.com/argoproj/argo-cd/v3/util/security" 15 ) 16 17 func TestPodExists(t *testing.T) { 18 for _, tcase := range []struct { 19 name string 20 podName string 21 namespace string 22 treeNodes []appv1.ResourceNode 23 expectedResult bool 24 }{ 25 { 26 name: "empty tree nodes", 27 podName: "test-pod", 28 namespace: "test", 29 treeNodes: []appv1.ResourceNode{}, 30 expectedResult: false, 31 }, 32 { 33 name: "matched Pod but empty UID", 34 podName: "test-pod", 35 namespace: "test", 36 treeNodes: []appv1.ResourceNode{{ResourceRef: appv1.ResourceRef{Name: "test-pod", Namespace: "test", UID: "", Kind: kube.PodKind}}}, 37 expectedResult: false, 38 }, 39 { 40 name: "matched Pod", 41 podName: "test-pod", 42 namespace: "test", 43 treeNodes: []appv1.ResourceNode{{ResourceRef: appv1.ResourceRef{Name: "test-pod", Namespace: "test", UID: "testUID", Kind: kube.PodKind}}}, 44 expectedResult: true, 45 }, 46 { 47 name: "unmatched Pod Namespace", 48 podName: "test-pod", 49 namespace: "test", 50 treeNodes: []appv1.ResourceNode{{ResourceRef: appv1.ResourceRef{Name: "test-pod", Namespace: "test-A", UID: "testUID", Kind: kube.PodKind}}}, 51 expectedResult: false, 52 }, 53 { 54 name: "unmatched Kind", 55 podName: "test-pod", 56 namespace: "test", 57 treeNodes: []appv1.ResourceNode{{ResourceRef: appv1.ResourceRef{Name: "test-pod", Namespace: "test-A", UID: "testUID", Kind: kube.DeploymentKind}}}, 58 expectedResult: false, 59 }, 60 { 61 name: "unmatched Group", 62 podName: "test-pod", 63 namespace: "test", 64 treeNodes: []appv1.ResourceNode{{ResourceRef: appv1.ResourceRef{Name: "test-pod", Namespace: "test", UID: "testUID", Group: "A", Kind: kube.PodKind}}}, 65 expectedResult: false, 66 }, 67 { 68 name: "unmatched Pod Name", 69 podName: "test-pod", 70 namespace: "test", 71 treeNodes: []appv1.ResourceNode{{ResourceRef: appv1.ResourceRef{Name: "test", Namespace: "test", UID: "testUID", Kind: kube.PodKind}}}, 72 expectedResult: false, 73 }, 74 } { 75 t.Run(tcase.name, func(t *testing.T) { 76 result := podExists(tcase.treeNodes, tcase.podName, tcase.namespace) 77 assert.Equalf(t, tcase.expectedResult, result, "Expected result %v, but got %v", tcase.expectedResult, result) 78 }) 79 } 80 } 81 82 func TestIsValidPodName(t *testing.T) { 83 for _, tcase := range []struct { 84 name string 85 resourceName string 86 expectedResult bool 87 }{ 88 { 89 name: "valid pod name", 90 resourceName: "argocd-server-794644486d-r8v9d", 91 expectedResult: true, 92 }, 93 { 94 name: "not valid contains spaces", 95 resourceName: "kubectl delete pods", 96 expectedResult: false, 97 }, 98 { 99 name: "not valid", 100 resourceName: "kubectl -n kube-system delete pods --all", 101 expectedResult: false, 102 }, 103 { 104 name: "not valid contains special characters", 105 resourceName: "delete+*+from+etcd%3b", 106 expectedResult: false, 107 }, 108 } { 109 t.Run(tcase.name, func(t *testing.T) { 110 result := argo.IsValidPodName(tcase.resourceName) 111 assert.Equalf(t, tcase.expectedResult, result, "Expected result %v, but got %v", tcase.expectedResult, result) 112 }) 113 } 114 } 115 116 func TestIsValidNamespaceName(t *testing.T) { 117 for _, tcase := range []struct { 118 name string 119 resourceName string 120 expectedResult bool 121 }{ 122 { 123 name: "valid pod namespace name", 124 resourceName: "argocd", 125 expectedResult: true, 126 }, 127 { 128 name: "not valid contains spaces", 129 resourceName: "kubectl delete ns argocd", 130 expectedResult: false, 131 }, 132 { 133 name: "not valid contains special characters", 134 resourceName: "delete+*+from+etcd%3b", 135 expectedResult: false, 136 }, 137 } { 138 t.Run(tcase.name, func(t *testing.T) { 139 result := argo.IsValidNamespaceName(tcase.resourceName) 140 assert.Equalf(t, tcase.expectedResult, result, "Expected result %v, but got %v", tcase.expectedResult, result) 141 }) 142 } 143 } 144 145 func TestIsValidContainerNameName(t *testing.T) { 146 for _, tcase := range []struct { 147 name string 148 resourceName string 149 expectedResult bool 150 }{ 151 { 152 name: "valid container name", 153 resourceName: "argocd-server", 154 expectedResult: true, 155 }, 156 { 157 name: "not valid contains spaces", 158 resourceName: "kubectl delete pods", 159 expectedResult: false, 160 }, 161 { 162 name: "not valid contains special characters", 163 resourceName: "delete+*+from+etcd%3b", 164 expectedResult: false, 165 }, 166 } { 167 t.Run(tcase.name, func(t *testing.T) { 168 result := argo.IsValidContainerName(tcase.resourceName) 169 assert.Equalf(t, tcase.expectedResult, result, "Expected result %v, but got %v", tcase.expectedResult, result) 170 }) 171 } 172 } 173 174 func TestTerminalHandler_ServeHTTP_empty_params(t *testing.T) { 175 t.Parallel() 176 177 testKeys := []string{ 178 "pod", 179 "container", 180 "app", 181 "project", 182 "namespace", 183 } 184 185 // test both empty and invalid 186 testValues := []string{"", "invalid%20name"} 187 188 for _, testKey := range testKeys { 189 testKeyCopy := testKey 190 191 for _, testValue := range testValues { 192 testValueCopy := testValue 193 194 t.Run(testKeyCopy+" "+testValueCopy, func(t *testing.T) { 195 t.Parallel() 196 197 handler := terminalHandler{} 198 params := map[string]string{ 199 "pod": "valid", 200 "container": "valid", 201 "app": "valid", 202 "project": "valid", 203 "namespace": "valid", 204 } 205 params[testKeyCopy] = testValueCopy 206 var paramsArray []string 207 for key, value := range params { 208 paramsArray = append(paramsArray, key+"="+value) 209 } 210 paramsString := strings.Join(paramsArray, "&") 211 request := httptest.NewRequest(http.MethodGet, "https://argocd.example.com/api/v1/terminal?"+paramsString, http.NoBody) 212 recorder := httptest.NewRecorder() 213 handler.ServeHTTP(recorder, request) 214 response := recorder.Result() 215 assert.Equal(t, http.StatusBadRequest, response.StatusCode) 216 }) 217 } 218 } 219 } 220 221 func TestTerminalHandler_ServeHTTP_disallowed_namespace(t *testing.T) { 222 handler := terminalHandler{namespace: "argocd", enabledNamespaces: []string{"allowed"}} 223 request := httptest.NewRequest(http.MethodGet, "https://argocd.example.com/api/v1/terminal?pod=valid&container=valid&appName=valid&projectName=valid&namespace=test&appNamespace=disallowed", http.NoBody) 224 recorder := httptest.NewRecorder() 225 handler.ServeHTTP(recorder, request) 226 response := recorder.Result() 227 assert.Equal(t, http.StatusForbidden, response.StatusCode) 228 assert.Equal(t, security.NamespaceNotPermittedError("disallowed").Error()+"\n", recorder.Body.String()) 229 }