From da2d50bd706bb1d880e1e94d29aca3d6d9220c74 Mon Sep 17 00:00:00 2001 From: Mazyar Date: Mon, 25 May 2026 13:23:49 +0330 Subject: [PATCH] fixed critical security issues --- cmd/web/main.go | 2 ++ cmd/web/router/routes.go | 22 +++++++++------------- internal/document_scraper/middleware.go | 3 ++- pkg/security/csp.go | 21 +++++++++++++++++++++ 4 files changed, 34 insertions(+), 14 deletions(-) diff --git a/cmd/web/main.go b/cmd/web/main.go index 98216da..f9c238e 100644 --- a/cmd/web/main.go +++ b/cmd/web/main.go @@ -248,6 +248,8 @@ func main() { // Initialize HTTP server e := bootstrap.InitHTTPServer(conf, logger) + router.SetupCSPSecurity(e) + // Register routes router.RegisterAdminRoutes(e, userHandler, companyHandler, customerHandler, tenderHandler, feedbackHandler, tenderApprovalHandler, inquiryHandler, categoryHandler, flagHandler, notificationHandler, contactHandler, cmsHandler, kanbanHandler, fileStoreHandler, dashboardHandler, xssPolicy) router.RegisterPublicRoutes(e, customerHandler, tenderHandler, feedbackHandler, tenderApprovalHandler, companyHandler, inquiryHandler, categoryHandler, flagHandler, notificationHandler, contactHandler, cmsHandler, fileStoreHandler, xssPolicy) diff --git a/cmd/web/router/routes.go b/cmd/web/router/routes.go index 9b15f05..c1e73fc 100644 --- a/cmd/web/router/routes.go +++ b/cmd/web/router/routes.go @@ -22,17 +22,20 @@ import ( "github.com/labstack/echo/v4" ) -func RegisterAdminRoutes(e *echo.Echo, userHandler *user.Handler, companyHandler *company.Handler, customerHandler *customer.Handler, tenderHandler *tender.TenderHandler, feedbackHandler *feedback.Handler, tenderApprovalHandler *tender_approval.Handler, inquiryHandler *inquiry.Handler, categoryHandler *company_category.Handler, flagHandler *assets.Handler, notificationHandler *notification.NotificationHandler, contactHandler *contact.Handler, cmsHandler *cms.Handler, kanbanHandler *kanban.Handler, fileStoreHandler *filestore.Handler, dashboardHandler *dashboard.Handler, xssPolicy *security.XSSPolicy) { - adminV1 := e.Group("/admin/v1") - - // Add CSP middleware to admin routes (stricter policy) +// SetupCSPSecurity registers global CSP middleware and the CSP report endpoint once. +func SetupCSPSecurity(e *echo.Echo) { adminCSPConfig := security.DefaultCSPConfig() adminCSPConfig.ScriptSrc = []string{"'self'"} // No unsafe-inline for admin adminCSPConfig.StyleSrc = []string{"'self'"} // No unsafe-inline for admin - e.Use(security.CSPMiddleware(adminCSPConfig)) - // Add CSP report endpoint + publicCSPConfig := security.DefaultCSPConfig() + + e.Use(security.CSPMiddlewareForPaths("/admin", adminCSPConfig, "/api/v1", publicCSPConfig)) e.POST("/api/v1/csp-report", security.CSPReportHandler) +} + +func RegisterAdminRoutes(e *echo.Echo, userHandler *user.Handler, companyHandler *company.Handler, customerHandler *customer.Handler, tenderHandler *tender.TenderHandler, feedbackHandler *feedback.Handler, tenderApprovalHandler *tender_approval.Handler, inquiryHandler *inquiry.Handler, categoryHandler *company_category.Handler, flagHandler *assets.Handler, notificationHandler *notification.NotificationHandler, contactHandler *contact.Handler, cmsHandler *cms.Handler, kanbanHandler *kanban.Handler, fileStoreHandler *filestore.Handler, dashboardHandler *dashboard.Handler, xssPolicy *security.XSSPolicy) { + adminV1 := e.Group("/admin/v1") // Admin Users Routes adminUsersGP := adminV1.Group("/users") @@ -221,13 +224,6 @@ func RegisterAdminRoutes(e *echo.Echo, userHandler *user.Handler, companyHandler func RegisterPublicRoutes(e *echo.Echo, customerHandler *customer.Handler, tenderHandler *tender.TenderHandler, feedbackHandler *feedback.Handler, tenderApprovalHandler *tender_approval.Handler, companyHandler *company.Handler, inquiryHandler *inquiry.Handler, categoryHandler *company_category.Handler, flagHandler *assets.Handler, notificationHandler *notification.NotificationHandler, contactHandler *contact.Handler, cmsHandler *cms.Handler, fileStoreHandler *filestore.Handler, xssPolicy *security.XSSPolicy) { v1 := e.Group("/api/v1") - // Add CSP middleware to public routes - cspConfig := security.DefaultCSPConfig() - e.Use(security.CSPMiddleware(cspConfig)) - - // Add CSP report endpoint - e.POST("/api/v1/csp-report", security.CSPReportHandler) - customerGP := v1.Group("/profile") { customerGP.POST("/login", customerHandler.Login) diff --git a/internal/document_scraper/middleware.go b/internal/document_scraper/middleware.go index 9fc4f2b..360676a 100644 --- a/internal/document_scraper/middleware.go +++ b/internal/document_scraper/middleware.go @@ -1,6 +1,7 @@ package document_scraper import ( + "crypto/subtle" "strings" "tm/pkg/response" @@ -25,7 +26,7 @@ func APIKeyMiddleware(apiKey string) echo.MiddlewareFunc { return response.Unauthorized(c, "API key required. Use X-API-Key header or Authorization: ApiKey ") } - if apiKeyHeader != apiKey { + if subtle.ConstantTimeCompare([]byte(apiKeyHeader), []byte(apiKey)) != 1 { return response.Unauthorized(c, "Invalid API key") } diff --git a/pkg/security/csp.go b/pkg/security/csp.go index 7bf066c..e32fbdc 100644 --- a/pkg/security/csp.go +++ b/pkg/security/csp.go @@ -108,6 +108,27 @@ func CSPMiddleware(config *CSPConfig) echo.MiddlewareFunc { } } +// CSPMiddlewareForPaths applies CSP middleware based on request path prefix. +// Requests matching no prefix proceed without a CSP header. +func CSPMiddlewareForPaths(adminPrefix string, adminConfig *CSPConfig, publicPrefix string, publicConfig *CSPConfig) echo.MiddlewareFunc { + adminMW := CSPMiddleware(adminConfig) + publicMW := CSPMiddleware(publicConfig) + + return func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + path := c.Request().URL.Path + switch { + case strings.HasPrefix(path, adminPrefix): + return adminMW(next)(c) + case strings.HasPrefix(path, publicPrefix): + return publicMW(next)(c) + default: + return next(c) + } + } + } +} + // CSPReportHandler handles CSP violation reports func CSPReportHandler(c echo.Context) error { var report struct {