github.com/argoproj/argo-cd/v2@v2.10.9/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/v2/pkg/apis/application/v1alpha1" 13 "github.com/argoproj/argo-cd/v2/util/argo" 14 "github.com/argoproj/argo-cd/v2/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 if result != tcase.expectedResult { 78 t.Errorf("Expected result %v, but got %v", tcase.expectedResult, result) 79 } 80 }) 81 } 82 } 83 84 func TestIsValidPodName(t *testing.T) { 85 for _, tcase := range []struct { 86 name string 87 resourceName string 88 expectedResult bool 89 }{ 90 { 91 name: "valid pod name", 92 resourceName: "argocd-server-794644486d-r8v9d", 93 expectedResult: true, 94 }, 95 { 96 name: "not valid contains spaces", 97 resourceName: "kubectl delete pods", 98 expectedResult: false, 99 }, 100 { 101 name: "not valid", 102 resourceName: "kubectl -n kube-system delete pods --all", 103 expectedResult: false, 104 }, 105 { 106 name: "not valid contains special characters", 107 resourceName: "delete+*+from+etcd%3b", 108 expectedResult: false, 109 }, 110 } { 111 t.Run(tcase.name, func(t *testing.T) { 112 result := argo.IsValidPodName(tcase.resourceName) 113 if result != tcase.expectedResult { 114 t.Errorf("Expected result %v, but got %v", tcase.expectedResult, result) 115 } 116 }) 117 } 118 } 119 120 func TestIsValidNamespaceName(t *testing.T) { 121 for _, tcase := range []struct { 122 name string 123 resourceName string 124 expectedResult bool 125 }{ 126 { 127 name: "valid pod namespace name", 128 resourceName: "argocd", 129 expectedResult: true, 130 }, 131 { 132 name: "not valid contains spaces", 133 resourceName: "kubectl delete ns argocd", 134 expectedResult: false, 135 }, 136 { 137 name: "not valid contains special characters", 138 resourceName: "delete+*+from+etcd%3b", 139 expectedResult: false, 140 }, 141 } { 142 t.Run(tcase.name, func(t *testing.T) { 143 result := argo.IsValidNamespaceName(tcase.resourceName) 144 if result != tcase.expectedResult { 145 t.Errorf("Expected result %v, but got %v", tcase.expectedResult, result) 146 } 147 }) 148 } 149 } 150 151 func TestIsValidContainerNameName(t *testing.T) { 152 for _, tcase := range []struct { 153 name string 154 resourceName string 155 expectedResult bool 156 }{ 157 { 158 name: "valid container name", 159 resourceName: "argocd-server", 160 expectedResult: true, 161 }, 162 { 163 name: "not valid contains spaces", 164 resourceName: "kubectl delete pods", 165 expectedResult: false, 166 }, 167 { 168 name: "not valid contains special characters", 169 resourceName: "delete+*+from+etcd%3b", 170 expectedResult: false, 171 }, 172 } { 173 t.Run(tcase.name, func(t *testing.T) { 174 result := argo.IsValidContainerName(tcase.resourceName) 175 if result != tcase.expectedResult { 176 t.Errorf("Expected result %v, but got %v", tcase.expectedResult, result) 177 } 178 }) 179 } 180 } 181 182 func TestTerminalHandler_ServeHTTP_empty_params(t *testing.T) { 183 testKeys := []string{ 184 "pod", 185 "container", 186 "app", 187 "project", 188 "namespace", 189 } 190 191 // test both empty and invalid 192 testValues := []string{"", "invalid%20name"} 193 194 for _, testKey := range testKeys { 195 testKeyCopy := testKey 196 197 for _, testValue := range testValues { 198 testValueCopy := testValue 199 200 t.Run(testKeyCopy+" "+testValueCopy, func(t *testing.T) { 201 t.Parallel() 202 203 handler := terminalHandler{} 204 params := map[string]string{ 205 "pod": "valid", 206 "container": "valid", 207 "app": "valid", 208 "project": "valid", 209 "namespace": "valid", 210 } 211 params[testKeyCopy] = testValueCopy 212 var paramsArray []string 213 for key, value := range params { 214 paramsArray = append(paramsArray, key+"="+value) 215 } 216 paramsString := strings.Join(paramsArray, "&") 217 request := httptest.NewRequest(http.MethodGet, "https://argocd.example.com/api/v1/terminal?"+paramsString, nil) 218 recorder := httptest.NewRecorder() 219 handler.ServeHTTP(recorder, request) 220 response := recorder.Result() 221 assert.Equal(t, http.StatusBadRequest, response.StatusCode) 222 }) 223 } 224 } 225 } 226 227 func TestTerminalHandler_ServeHTTP_disallowed_namespace(t *testing.T) { 228 handler := terminalHandler{namespace: "argocd", enabledNamespaces: []string{"allowed"}} 229 request := httptest.NewRequest(http.MethodGet, "https://argocd.example.com/api/v1/terminal?pod=valid&container=valid&appName=valid&projectName=valid&namespace=test&appNamespace=disallowed", nil) 230 recorder := httptest.NewRecorder() 231 handler.ServeHTTP(recorder, request) 232 response := recorder.Result() 233 assert.Equal(t, http.StatusForbidden, response.StatusCode) 234 assert.Equal(t, security.NamespaceNotPermittedError("disallowed").Error()+"\n", recorder.Body.String()) 235 }