From a060d94cb5c904b5393764652f3f49355e613e15 Mon Sep 17 00:00:00 2001 From: Kailash Nadh Date: Sun, 21 Jul 2019 20:11:11 +0530 Subject: [PATCH] Move UUID validation from multiple places into a middleware func --- handlers.go | 37 +++++++++++++++++++++++++++++-------- public.go | 37 +++---------------------------------- 2 files changed, 32 insertions(+), 42 deletions(-) diff --git a/handlers.go b/handlers.go index 0a0448d..733020c 100644 --- a/handlers.go +++ b/handlers.go @@ -4,6 +4,7 @@ import ( "encoding/json" "net/http" "net/url" + "regexp" "strconv" "strings" @@ -37,6 +38,8 @@ type pagination struct { Limit int `json:"limit"` } +var reUUID = regexp.MustCompile("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$") + // registerHandlers registers HTTP handlers. func registerHandlers(e *echo.Echo) { e.GET("/", handleIndexPage) @@ -97,12 +100,18 @@ func registerHandlers(e *echo.Echo) { e.DELETE("/api/templates/:id", handleDeleteTemplate) // Subscriber facing views. - e.GET("/subscription/:campUUID/:subUUID", handleSubscriptionPage) - e.POST("/subscription/:campUUID/:subUUID", handleSubscriptionPage) - e.POST("/subscription/export/:subUUID", handleSelfExportSubscriberData) - e.POST("/subscription/wipe/:subUUID", handleWipeSubscriberData) - e.GET("/link/:linkUUID/:campUUID/:subUUID", handleLinkRedirect) - e.GET("/campaign/:campUUID/:subUUID/px.png", handleRegisterCampaignView) + e.GET("/subscription/:campUUID/:subUUID", validateUUID(handleSubscriptionPage, + "campUUID", "subUUID")) + e.POST("/subscription/:campUUID/:subUUID", validateUUID(handleSubscriptionPage, + "campUUID", "subUUID")) + e.POST("/subscription/export/:subUUID", validateUUID(handleSelfExportSubscriberData, + "subUUID")) + e.POST("/subscription/wipe/:subUUID", validateUUID(handleWipeSubscriberData, + "subUUID")) + e.GET("/link/:linkUUID/:campUUID/:subUUID", validateUUID(handleLinkRedirect, + "linkUUID", "campUUID", "subUUID")) + e.GET("/campaign/:campUUID/:subUUID/px.png", validateUUID(handleRegisterCampaignView, + "campUUID", "subUUID")) // Static views. e.GET("/lists", handleIndexPage) @@ -130,6 +139,20 @@ func handleIndexPage(c echo.Context) error { return c.String(http.StatusOK, string(b)) } +// validateUUID validates the UUID string format for a given set of params. +func validateUUID(next echo.HandlerFunc, params ...string) echo.HandlerFunc { + return func(c echo.Context) error { + for _, p := range params { + if !reUUID.MatchString(c.Param(p)) { + return c.Render(http.StatusBadRequest, "message", + makeMsgTpl("Invalid request", "", + `One or more UUIDs in the request are invalid.`)) + } + } + return next(c) + } +} + // makeAttribsBlob takes a list of keys and values and creates // a JSON map out of them. func makeAttribsBlob(keys []string, vals []string) ([]byte, bool) { @@ -154,7 +177,6 @@ func makeAttribsBlob(keys []string, vals []string) ([]byte, bool) { val = s } } - attribs[key] = val } @@ -162,7 +184,6 @@ func makeAttribsBlob(keys []string, vals []string) ([]byte, bool) { j, _ := json.Marshal(attribs) return j, true } - return nil, false } diff --git a/public.go b/public.go index 7d14155..e656ff1 100644 --- a/public.go +++ b/public.go @@ -7,7 +7,6 @@ import ( "image/png" "io" "net/http" - "regexp" "strconv" "github.com/knadh/listmonk/messenger" @@ -52,8 +51,7 @@ type msgTpl struct { } var ( - regexValidUUID = regexp.MustCompile("^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$") - pixelPNG = drawTransparentImage(3, 14) + pixelPNG = drawTransparentImage(3, 14) ) // Render executes and renders a template for echo. @@ -83,14 +81,6 @@ func handleSubscriptionPage(c echo.Context) error { out.AllowExport = app.Constants.Privacy.AllowExport out.AllowWipe = app.Constants.Privacy.AllowWipe - if !regexValidUUID.MatchString(campUUID) || - !regexValidUUID.MatchString(subUUID) { - return c.Render(http.StatusBadRequest, "message", - makeMsgTpl("Invalid request", "", - `The unsubscription request contains invalid IDs. - Please follow the correct link.`)) - } - // Unsubscribe. if unsub { // Is blacklisting allowed? @@ -119,12 +109,6 @@ func handleLinkRedirect(c echo.Context) error { campUUID = c.Param("campUUID") subUUID = c.Param("subUUID") ) - if !regexValidUUID.MatchString(linkUUID) || - !regexValidUUID.MatchString(campUUID) || - !regexValidUUID.MatchString(subUUID) { - return c.Render(http.StatusBadRequest, "message", - makeMsgTpl("Invalid link", "", "The link you clicked is invalid.")) - } var url string if err := app.Queries.RegisterLinkClick.Get(&url, linkUUID, campUUID, subUUID); err != nil { @@ -146,13 +130,9 @@ func handleRegisterCampaignView(c echo.Context) error { campUUID = c.Param("campUUID") subUUID = c.Param("subUUID") ) - if regexValidUUID.MatchString(campUUID) && - regexValidUUID.MatchString(subUUID) { - if _, err := app.Queries.RegisterCampaignView.Exec(campUUID, subUUID); err != nil { - app.Logger.Printf("error registering campaign view: %s", err) - } + if _, err := app.Queries.RegisterCampaignView.Exec(campUUID, subUUID); err != nil { + app.Logger.Printf("error registering campaign view: %s", err) } - c.Response().Header().Set("Cache-Control", "no-cache") return c.Blob(http.StatusOK, "image/png", pixelPNG) } @@ -166,12 +146,6 @@ func handleSelfExportSubscriberData(c echo.Context) error { app = c.Get("app").(*App) subUUID = c.Param("subUUID") ) - if !regexValidUUID.MatchString(subUUID) { - return c.Render(http.StatusInternalServerError, "message", - makeMsgTpl("Invalid request", "", - "The subscriber ID is invalid.")) - } - // Is export allowed? if !app.Constants.Privacy.AllowExport { return c.Render(http.StatusBadRequest, "message", @@ -230,11 +204,6 @@ func handleWipeSubscriberData(c echo.Context) error { app = c.Get("app").(*App) subUUID = c.Param("subUUID") ) - if !regexValidUUID.MatchString(subUUID) { - return c.Render(http.StatusInternalServerError, "message", - makeMsgTpl("Invalid request", "", - "The subscriber ID is invalid.")) - } // Is wiping allowed? if !app.Constants.Privacy.AllowExport {