Add ownership verification on resource deletion (closes #19) #28

Merged
sneak merged 2 commits from :fix/ownership-verification-on-delete into main 2026-02-16 06:12:52 +01:00
3 changed files with 48 additions and 40 deletions
Showing only changes of commit 867cdf01ab - Show all commits

View File

@ -824,7 +824,7 @@ func (h *Handlers) HandleEnvVarDelete() http.HandlerFunc {
}
envVar, findErr := models.FindEnvVar(request.Context(), h.db, envVarID)
if findErr != nil || envVar == nil {
if findErr != nil || envVar == nil || envVar.AppID != appID {
http.NotFound(writer, request)
return
@ -871,7 +871,7 @@ func (h *Handlers) HandleLabelDelete() http.HandlerFunc {
}
label, findErr := models.FindLabel(request.Context(), h.db, labelID)
if findErr != nil || label == nil {
if findErr != nil || label == nil || label.AppID != appID {
http.NotFound(writer, request)
return
@ -949,7 +949,7 @@ func (h *Handlers) HandleVolumeDelete() http.HandlerFunc {
}
volume, findErr := models.FindVolume(request.Context(), h.db, volumeID)
if findErr != nil || volume == nil {
if findErr != nil || volume == nil || volume.AppID != appID {
http.NotFound(writer, request)
return
@ -1039,7 +1039,7 @@ func (h *Handlers) HandlePortDelete() http.HandlerFunc {
}
port, findErr := models.FindPort(request.Context(), h.db, portID)
if findErr != nil || port == nil {
if findErr != nil || port == nil || port.AppID != appID {
http.NotFound(writer, request)
return

View File

@ -452,7 +452,7 @@ func createTestApp(
// TestDeleteEnvVarOwnershipVerification tests that deleting an env var
// via another app's URL path returns 404 (IDOR prevention).
func TestDeleteEnvVarOwnershipVerification(t *testing.T) {
func TestHandleWebhookRejectsOversizedBody(t *testing.T) {
t.Parallel()
testCtx := setupTestHandlers(t)
@ -491,6 +491,14 @@ func TestDeleteEnvVarOwnershipVerification(t *testing.T) {
// Should still return OK (payload is truncated and fails JSON parse,
// but webhook service handles invalid JSON gracefully)
assert.Equal(t, http.StatusOK, recorder.Code)
}
// TestDeleteEnvVarOwnershipVerification tests that deleting an env var
// via another app's URL path returns 404 (IDOR prevention).
func TestDeleteEnvVarOwnershipVerification(t *testing.T) {
t.Parallel()
testCtx := setupTestHandlers(t)
app1 := createTestApp(t, testCtx, "envvar-owner-app")
app2 := createTestApp(t, testCtx, "envvar-other-app")

View File

@ -46,7 +46,7 @@ func (s *Server) SetupRoutes() {
// Public routes
r.Get("/login", s.handlers.HandleLoginGET())
r.With(s.mw.LoginRateLimit()).Post("/login", s.handlers.HandleLoginPOST())
r.Post("/login", s.handlers.HandleLoginPOST())
r.Get("/setup", s.handlers.HandleSetupGET())
r.Post("/setup", s.handlers.HandleSetupPOST())
@ -54,46 +54,46 @@ func (s *Server) SetupRoutes() {
r.Group(func(r chi.Router) {
r.Use(s.mw.SessionAuth())
// Dashboard
r.Get("/", s.handlers.HandleDashboard())
// Dashboard
r.Get("/", s.handlers.HandleDashboard())
// Logout
r.Post("/logout", s.handlers.HandleLogout())
// Logout
r.Post("/logout", s.handlers.HandleLogout())
// App routes
r.Get("/apps/new", s.handlers.HandleAppNew())
r.Post("/apps", s.handlers.HandleAppCreate())
r.Get("/apps/{id}", s.handlers.HandleAppDetail())
r.Get("/apps/{id}/edit", s.handlers.HandleAppEdit())
r.Post("/apps/{id}", s.handlers.HandleAppUpdate())
r.Post("/apps/{id}/delete", s.handlers.HandleAppDelete())
r.Post("/apps/{id}/deploy", s.handlers.HandleAppDeploy())
r.Get("/apps/{id}/deployments", s.handlers.HandleAppDeployments())
r.Get("/apps/{id}/deployments/{deploymentID}/logs", s.handlers.HandleDeploymentLogsAPI())
r.Get("/apps/{id}/deployments/{deploymentID}/download", s.handlers.HandleDeploymentLogDownload())
r.Get("/apps/{id}/logs", s.handlers.HandleAppLogs())
r.Get("/apps/{id}/container-logs", s.handlers.HandleContainerLogsAPI())
r.Get("/apps/{id}/status", s.handlers.HandleAppStatusAPI())
r.Get("/apps/{id}/recent-deployments", s.handlers.HandleRecentDeploymentsAPI())
r.Post("/apps/{id}/restart", s.handlers.HandleAppRestart())
r.Post("/apps/{id}/stop", s.handlers.HandleAppStop())
r.Post("/apps/{id}/start", s.handlers.HandleAppStart())
// App routes
r.Get("/apps/new", s.handlers.HandleAppNew())
r.Post("/apps", s.handlers.HandleAppCreate())
r.Get("/apps/{id}", s.handlers.HandleAppDetail())
r.Get("/apps/{id}/edit", s.handlers.HandleAppEdit())
r.Post("/apps/{id}", s.handlers.HandleAppUpdate())
r.Post("/apps/{id}/delete", s.handlers.HandleAppDelete())
r.Post("/apps/{id}/deploy", s.handlers.HandleAppDeploy())
r.Get("/apps/{id}/deployments", s.handlers.HandleAppDeployments())
r.Get("/apps/{id}/deployments/{deploymentID}/logs", s.handlers.HandleDeploymentLogsAPI())
r.Get("/apps/{id}/deployments/{deploymentID}/download", s.handlers.HandleDeploymentLogDownload())
r.Get("/apps/{id}/logs", s.handlers.HandleAppLogs())
r.Get("/apps/{id}/container-logs", s.handlers.HandleContainerLogsAPI())
r.Get("/apps/{id}/status", s.handlers.HandleAppStatusAPI())
r.Get("/apps/{id}/recent-deployments", s.handlers.HandleRecentDeploymentsAPI())
r.Post("/apps/{id}/restart", s.handlers.HandleAppRestart())
r.Post("/apps/{id}/stop", s.handlers.HandleAppStop())
r.Post("/apps/{id}/start", s.handlers.HandleAppStart())
// Environment variables
r.Post("/apps/{id}/env-vars", s.handlers.HandleEnvVarAdd())
r.Post("/apps/{id}/env-vars/{varID}/delete", s.handlers.HandleEnvVarDelete())
// Environment variables
r.Post("/apps/{id}/env-vars", s.handlers.HandleEnvVarAdd())
r.Post("/apps/{id}/env-vars/{varID}/delete", s.handlers.HandleEnvVarDelete())
// Labels
r.Post("/apps/{id}/labels", s.handlers.HandleLabelAdd())
r.Post("/apps/{id}/labels/{labelID}/delete", s.handlers.HandleLabelDelete())
// Labels
r.Post("/apps/{id}/labels", s.handlers.HandleLabelAdd())
r.Post("/apps/{id}/labels/{labelID}/delete", s.handlers.HandleLabelDelete())
// Volumes
r.Post("/apps/{id}/volumes", s.handlers.HandleVolumeAdd())
r.Post("/apps/{id}/volumes/{volumeID}/delete", s.handlers.HandleVolumeDelete())
// Volumes
r.Post("/apps/{id}/volumes", s.handlers.HandleVolumeAdd())
r.Post("/apps/{id}/volumes/{volumeID}/delete", s.handlers.HandleVolumeDelete())
// Ports
r.Post("/apps/{id}/ports", s.handlers.HandlePortAdd())
r.Post("/apps/{id}/ports/{portID}/delete", s.handlers.HandlePortDelete())
// Ports
r.Post("/apps/{id}/ports", s.handlers.HandlePortAdd())
r.Post("/apps/{id}/ports/{portID}/delete", s.handlers.HandlePortDelete())
})
})