浏览代码

Move UUID validation from multiple places into a middleware func

Kailash Nadh 6 年之前
父节点
当前提交
a060d94cb5
共有 2 个文件被更改,包括 32 次插入42 次删除
  1. 29 8
      handlers.go
  2. 3 34
      public.go

+ 29 - 8
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
 }
 

+ 3 - 34
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 {