fix: disable API v1 write methods (closes #112)
All checks were successful
Check / check (pull_request) Successful in 11m21s

Remove POST /apps, DELETE /apps/{id}, and POST /apps/{id}/deploy from
the API v1 route group. These endpoints used cookie-based session auth
without CSRF protection, creating a CSRF vulnerability.

Read-only endpoints (GET /apps, GET /apps/{id}, GET /apps/{id}/deployments),
login, and whoami are retained.

Removed handlers: HandleAPICreateApp, HandleAPIDeleteApp,
HandleAPITriggerDeploy, along with apiCreateRequest struct and
validateCreateRequest function.

Updated tests to use service layer directly for app creation in
remaining read-only endpoint tests.
This commit is contained in:
user
2026-02-20 05:33:07 -08:00
parent 4217e62f27
commit ab7c43b887
4 changed files with 24 additions and 268 deletions

View File

@@ -2,7 +2,6 @@ package handlers_test
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"net/url"
@@ -843,33 +842,6 @@ func TestSetupRequiredExemptsHealthAndStaticAndAPI(t *testing.T) {
})
}
// TestAPITriggerDeployUsesDetachedContext verifies that HandleAPITriggerDeploy
// does not pass the request context directly to the deploy operation.
// This is a compile-time/code-level fix verified by the deployment not being
// cancelled when the request context is cancelled.
func TestAPITriggerDeployUsesDetachedContext(t *testing.T) {
t.Parallel()
// This test verifies the fix exists by checking the handler doesn't
// fail when called — the actual context detachment is verified by code review.
// The deploy will fail (no docker) but shouldn't panic.
tc, cookies := setupAPITest(t)
body := `{"name":"detach-ctx-app","repoUrl":"https://github.com/example/repo"}`
rr := apiRequest(t, tc, cookies, http.MethodPost, "/api/v1/apps", body)
require.Equal(t, http.StatusCreated, rr.Code)
var created map[string]any
require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &created))
appID, ok := created["id"].(string)
require.True(t, ok)
rr = apiRequest(t, tc, cookies, http.MethodPost, "/api/v1/apps/"+appID+"/deploy", "")
// Should get conflict (deploy will fail) or accepted, but not panic
assert.Contains(t, []int{http.StatusAccepted, http.StatusConflict}, rr.Code)
}
func TestHandleCancelDeployRedirects(t *testing.T) {
t.Parallel()