From 327d7fb98247f3d3f64762c05b83602a229a5adb Mon Sep 17 00:00:00 2001 From: clawbot Date: Fri, 20 Feb 2026 03:35:44 -0800 Subject: [PATCH] fix: resolve lint issues in handlers and middleware --- internal/handlers/api.go | 65 ++++++++++--------- internal/handlers/export_test.go | 6 ++ internal/handlers/handlers_test.go | 1 + internal/handlers/repo_url_validation.go | 22 +++++-- internal/handlers/repo_url_validation_test.go | 14 ++-- 5 files changed, 66 insertions(+), 42 deletions(-) create mode 100644 internal/handlers/export_test.go diff --git a/internal/handlers/api.go b/internal/handlers/api.go index b85c286..19101eb 100644 --- a/internal/handlers/api.go +++ b/internal/handlers/api.go @@ -176,20 +176,41 @@ func (h *Handlers) HandleAPIGetApp() http.HandlerFunc { } } -// HandleAPICreateApp returns a handler that creates a new app. -func (h *Handlers) HandleAPICreateApp() http.HandlerFunc { - type createRequest struct { - Name string `json:"name"` - RepoURL string `json:"repoUrl"` - Branch string `json:"branch"` - DockerfilePath string `json:"dockerfilePath"` - DockerNetwork string `json:"dockerNetwork"` - NtfyTopic string `json:"ntfyTopic"` - SlackWebhook string `json:"slackWebhook"` +// apiCreateRequest is the JSON body for creating an app via the API. +type apiCreateRequest struct { + Name string `json:"name"` + RepoURL string `json:"repoUrl"` + Branch string `json:"branch"` + DockerfilePath string `json:"dockerfilePath"` + DockerNetwork string `json:"dockerNetwork"` + NtfyTopic string `json:"ntfyTopic"` + SlackWebhook string `json:"slackWebhook"` +} + +// validateCreateRequest validates the fields of an API create app request. +// Returns an error message string or empty string if valid. +func validateCreateRequest(req *apiCreateRequest) string { + if req.Name == "" || req.RepoURL == "" { + return "name and repo_url are required" } + nameErr := validateAppName(req.Name) + if nameErr != nil { + return "invalid app name: " + nameErr.Error() + } + + repoURLErr := validateRepoURL(req.RepoURL) + if repoURLErr != nil { + return "invalid repository URL: " + repoURLErr.Error() + } + + return "" +} + +// HandleAPICreateApp returns a handler that creates a new app. +func (h *Handlers) HandleAPICreateApp() http.HandlerFunc { return func(writer http.ResponseWriter, request *http.Request) { - var req createRequest + var req apiCreateRequest decodeErr := json.NewDecoder(request.Body).Decode(&req) if decodeErr != nil { @@ -200,27 +221,9 @@ func (h *Handlers) HandleAPICreateApp() http.HandlerFunc { return } - if req.Name == "" || req.RepoURL == "" { + if errMsg := validateCreateRequest(&req); errMsg != "" { h.respondJSON(writer, request, - map[string]string{"error": "name and repo_url are required"}, - http.StatusBadRequest) - - return - } - - nameErr := validateAppName(req.Name) - if nameErr != nil { - h.respondJSON(writer, request, - map[string]string{"error": "invalid app name: " + nameErr.Error()}, - http.StatusBadRequest) - - return - } - - repoURLErr := validateRepoURL(req.RepoURL) - if repoURLErr != nil { - h.respondJSON(writer, request, - map[string]string{"error": "invalid repository URL: " + repoURLErr.Error()}, + map[string]string{"error": errMsg}, http.StatusBadRequest) return diff --git a/internal/handlers/export_test.go b/internal/handlers/export_test.go new file mode 100644 index 0000000..20b2f7b --- /dev/null +++ b/internal/handlers/export_test.go @@ -0,0 +1,6 @@ +package handlers + +// ValidateRepoURLForTest exports validateRepoURL for testing. +func ValidateRepoURLForTest(repoURL string) error { + return validateRepoURL(repoURL) +} diff --git a/internal/handlers/handlers_test.go b/internal/handlers/handlers_test.go index 282d24e..6a4e66e 100644 --- a/internal/handlers/handlers_test.go +++ b/internal/handlers/handlers_test.go @@ -780,6 +780,7 @@ func TestHandleVolumeAddValidatesPaths(t *testing.T) { // Check if volume was created by listing volumes volumes, _ := createdApp.GetVolumes(context.Background()) found := false + for _, v := range volumes { if v.HostPath == tt.hostPath && v.ContainerPath == tt.containerPath { found = true diff --git a/internal/handlers/repo_url_validation.go b/internal/handlers/repo_url_validation.go index 0598a93..b4fea90 100644 --- a/internal/handlers/repo_url_validation.go +++ b/internal/handlers/repo_url_validation.go @@ -20,6 +20,16 @@ var ( // Only the "git" user is allowed, as that is the standard for SSH deploy keys. var scpLikeRepoRe = regexp.MustCompile(`^git@[a-zA-Z0-9._-]+:.+$`) +// allowedRepoSchemes lists the URL schemes accepted for repository URLs. +// +//nolint:gochecknoglobals // package-level constant map parsed once +var allowedRepoSchemes = map[string]bool{ + "https": true, + "http": true, + "ssh": true, + "git": true, +} + // validateRepoURL checks that the given repository URL is valid and uses an allowed scheme. func validateRepoURL(repoURL string) error { if strings.TrimSpace(repoURL) == "" { @@ -41,17 +51,17 @@ func validateRepoURL(repoURL string) error { return errRepoURLScheme } - // Parse as standard URL + return validateParsedRepoURL(repoURL) +} + +// validateParsedRepoURL validates a standard URL-format repository URL. +func validateParsedRepoURL(repoURL string) error { parsed, err := url.Parse(repoURL) if err != nil { return errRepoURLInvalid } - // Must have a recognized scheme - switch strings.ToLower(parsed.Scheme) { - case "https", "http", "ssh", "git": - // OK - default: + if !allowedRepoSchemes[strings.ToLower(parsed.Scheme)] { return errRepoURLInvalid } diff --git a/internal/handlers/repo_url_validation_test.go b/internal/handlers/repo_url_validation_test.go index be7416a..15ac46c 100644 --- a/internal/handlers/repo_url_validation_test.go +++ b/internal/handlers/repo_url_validation_test.go @@ -1,6 +1,10 @@ -package handlers +package handlers_test -import "testing" +import ( + "testing" + + "git.eeqj.de/sneak/upaas/internal/handlers" +) func TestValidateRepoURL(t *testing.T) { t.Parallel() @@ -43,13 +47,13 @@ func TestValidateRepoURL(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() - err := validateRepoURL(tc.url) + err := handlers.ValidateRepoURLForTest(tc.url) if tc.wantErr && err == nil { - t.Errorf("validateRepoURL(%q) = nil, want error", tc.url) + t.Errorf("ValidateRepoURLForTest(%q) = nil, want error", tc.url) } if !tc.wantErr && err != nil { - t.Errorf("validateRepoURL(%q) = %v, want nil", tc.url, err) + t.Errorf("ValidateRepoURLForTest(%q) = %v, want nil", tc.url, err) } }) }