Skip to content

Commit

Permalink
feat(backend): Reject invalid requests.
Browse files Browse the repository at this point in the history
Signed-off-by: Yael <fishel.yael@gmail.com>
  • Loading branch information
Yael-F committed Dec 5, 2024
1 parent 68a0060 commit 5ec5d4d
Show file tree
Hide file tree
Showing 6 changed files with 347 additions and 1 deletion.
1 change: 0 additions & 1 deletion workspaces/backend/api/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ func (a *App) LogError(r *http.Request, err error) {
a.logger.Error(err.Error(), "method", method, "uri", uri)
}

//nolint:unused
func (a *App) badRequestResponse(w http.ResponseWriter, r *http.Request, err error) {
httpError := &HTTPError{
StatusCode: http.StatusBadRequest,
Expand Down
54 changes: 54 additions & 0 deletions workspaces/backend/api/validation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
Copyright 2024.
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 api

import (
"fmt"
"unicode/utf8"
)

// ValidateKubernetesResourceName validates one or more Kubernetes resource names.
// It ensures each name meets the following criteria:
// 1. The name must not contain non-ASCII characters.
// 2. The name must not exceed 255 characters in length.
func ValidateKubernetesResourceName(params ...string) error {
for _, param := range params {
if err := NonASCIIValidator(param); err != nil {
return err
}
if err := LengthValidator(param); err != nil {
return err
}
}
return nil
}

// NonASCIIValidator checks if a given string contains only ASCII characters.
func NonASCIIValidator(param string) error {
if utf8.ValidString(param) && len(param) == len([]rune(param)) {
return nil
}
return fmt.Errorf("Invalid value: '%s' contains non-ASCII characters.", param)
}

// LengthValidator ensures a given string does not exceed 255 characters.
func LengthValidator(param string) error {
if len(param) > 255 {
return fmt.Errorf("Invalid value: '%s' exceeds the allowed limit of 255 characters.", param)
}

return nil
}
4 changes: 4 additions & 0 deletions workspaces/backend/api/workspacekinds_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ func (a *App) GetWorkspaceKindHandler(w http.ResponseWriter, r *http.Request, ps

if name == "" {
a.serverErrorResponse(w, r, fmt.Errorf("workspace kind name is missing"))
}

if err := ValidateKubernetesResourceName(name); err != nil {
a.badRequestResponse(w, r, err)
return
}

Expand Down
106 changes: 106 additions & 0 deletions workspaces/backend/api/workspacekinds_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"encoding/json"
"fmt"
"io"
"math/rand"
"net/http"
"net/http/httptest"
"strings"
Expand Down Expand Up @@ -265,4 +266,109 @@ var _ = Describe("WorkspaceKinds Handler", func() {
Expect(rs.StatusCode).To(Equal(http.StatusNotFound), "Expected HTTP status 404 Not Found")
})
})

Context("with unsupported request parameters", Ordered, func() {

var (
a App
validAsciiName string
invalidAsciiName string
validMaxLengthName string
invalidLengthName string
)

// generateASCII generates a random ASCII string of the specified length.
generateASCII := func(length int) string {
const asciiChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

var sb strings.Builder
for i := 0; i < length; i++ {
sb.WriteByte(asciiChars[rand.Intn(len(asciiChars))])
}
return sb.String()
}

BeforeAll(func() {
validAsciiName = "test"
invalidAsciiName = validAsciiName + string(rune(rand.Intn(0x10FFFF-128)+128))
validMaxLengthName = generateASCII(255)
invalidLengthName = generateASCII(256)

repos := repositories.NewRepositories(k8sClient)
a = App{
Config: config.EnvConfig{
Port: 4000,
},
repositories: repos,
}
})

It("should return 400 status code for a non-ascii workspace", func() {
By("creating the HTTP request")
path := strings.Replace(WorkspacesByNamespacePath, ":"+WorkspaceNamePathParam, invalidAsciiName, 1)
req, err := http.NewRequest(http.MethodGet, path, nil)
Expect(err).NotTo(HaveOccurred(), "Failed to create HTTP request")

By("executing GetWorkspaceKindHandler")
ps := httprouter.Params{
httprouter.Param{
Key: WorkspaceNamePathParam,
Value: invalidAsciiName,
},
}
rr := httptest.NewRecorder()
a.GetWorkspaceKindHandler(rr, req, ps)
rs := rr.Result()
defer rs.Body.Close() // nolint: errcheck

By("verifying the HTTP response status code")
Expect(rs.StatusCode).To(Equal(http.StatusBadRequest), "Expected HTTP status 400 Bad Request")
})

It("should return 400 status code for a workspace longer than 255", func() {
By("creating the HTTP request")
path := strings.Replace(WorkspacesByNamespacePath, ":"+WorkspaceNamePathParam, invalidLengthName, 1)
req, err := http.NewRequest(http.MethodGet, path, nil)
Expect(err).NotTo(HaveOccurred(), "Failed to create HTTP request")

By("executing GetWorkspaceKindHandler")
ps := httprouter.Params{
httprouter.Param{
Key: WorkspaceNamePathParam,
Value: invalidLengthName,
},
}
rr := httptest.NewRecorder()
a.GetWorkspaceKindHandler(rr, req, ps)
rs := rr.Result()
defer rs.Body.Close() // nolint: errcheck

By("verifying the HTTP response status code")
Expect(rs.StatusCode).To(Equal(http.StatusBadRequest), "Expected HTTP status 400 Bad Request")

})

It("should return 200 status code for a workspace with a length of 255 characters", func() {
By("creating the HTTP request")
fmt.Println("Here Should except 255 length params")
path := strings.Replace(WorkspacesByNamespacePath, ":"+WorkspaceNamePathParam, validMaxLengthName, 1)
req, err := http.NewRequest(http.MethodGet, path, nil)
Expect(err).NotTo(HaveOccurred(), "Failed to create HTTP request")

By("executing GetWorkspaceKindHandler")
ps := httprouter.Params{
httprouter.Param{
Key: WorkspaceNamePathParam,
Value: validMaxLengthName,
},
}
rr := httptest.NewRecorder()
a.GetWorkspaceKindHandler(rr, req, ps)
rs := rr.Result()
defer rs.Body.Close() // nolint: errcheck

By("verifying the HTTP response status code")
Expect(rs.StatusCode).To(Equal(http.StatusNotFound), "Expected HTTP status 404 Not Found")
})
})
})
18 changes: 18 additions & 0 deletions workspaces/backend/api/workspaces_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ func (a *App) GetWorkspaceHandler(w http.ResponseWriter, r *http.Request, ps htt
}
if workspaceName == "" {
a.serverErrorResponse(w, r, fmt.Errorf("workspaceName is nil"))
}

if err := ValidateKubernetesResourceName(namespace, workspaceName); err != nil {
a.badRequestResponse(w, r, err)
return
}

Expand Down Expand Up @@ -70,6 +74,11 @@ func (a *App) GetWorkspaceHandler(w http.ResponseWriter, r *http.Request, ps htt
func (a *App) GetWorkspacesHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
namespace := ps.ByName(NamespacePathParam)

if err := ValidateKubernetesResourceName(namespace); err != nil {
a.badRequestResponse(w, r, err)
return
}

var workspaces []models.WorkspaceModel
var err error
if namespace == "" {
Expand Down Expand Up @@ -97,6 +106,10 @@ func (a *App) CreateWorkspaceHandler(w http.ResponseWriter, r *http.Request, ps

if namespace == "" {
a.serverErrorResponse(w, r, fmt.Errorf("namespace is missing"))
}

if err := ValidateKubernetesResourceName(namespace); err != nil {
a.badRequestResponse(w, r, err)
return
}

Expand All @@ -106,6 +119,11 @@ func (a *App) CreateWorkspaceHandler(w http.ResponseWriter, r *http.Request, ps
return
}

if err := ValidateKubernetesResourceName(workspaceModel.Name, workspaceModel.Namespace); err != nil {
a.badRequestResponse(w, r, err)
return
}

workspaceModel.Namespace = namespace

createdWorkspace, err := a.repositories.Workspace.CreateWorkspace(r.Context(), workspaceModel)
Expand Down
Loading

0 comments on commit 5ec5d4d

Please sign in to comment.