From 7c9192063db1c19d196458909e91034d63c8cc6d Mon Sep 17 00:00:00 2001 From: Hongming Wang Date: Wed, 15 Apr 2026 11:00:49 -0700 Subject: [PATCH] =?UTF-8?q?fix(security):=20#190=20=E2=80=94=20gate=20POST?= =?UTF-8?q?=20/templates/import=20behind=20AdminAuth?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #190 (HIGH). The route was registered on the root router with no auth middleware, letting any unauthenticated caller write arbitrary files into configsDir via a crafted template. Same vulnerability class as #164 (bundles/import) and path-traversal risk same as #103 (org/import). One-line gate via the existing wsAdmin pattern. Lazy-bootstrap fail-open preserved for fresh installs. Co-Authored-By: Claude Opus 4.6 (1M context) --- platform/internal/router/router.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/platform/internal/router/router.go b/platform/internal/router/router.go index b1e482fc..fc3e579c 100644 --- a/platform/internal/router/router.go +++ b/platform/internal/router/router.go @@ -308,7 +308,12 @@ func Setup(hub *ws.Hub, broadcaster *events.Broadcaster, prov *provisioner.Provi // Templates tmplh := handlers.NewTemplatesHandler(configsDir, dockerCli) r.GET("/templates", tmplh.List) - r.POST("/templates/import", tmplh.Import) + // #190: POST /templates/import writes arbitrary files into configsDir. + // Must be admin-gated — same class as /bundles/import (#164) and /org/import. + { + tmplAdmin := r.Group("", middleware.AdminAuth(db.DB)) + tmplAdmin.POST("/templates/import", tmplh.Import) + } wsAuth.GET("/shared-context", tmplh.SharedContext) wsAuth.PUT("/files", tmplh.ReplaceFiles) wsAuth.GET("/files", tmplh.ListFiles)