fix: push notification
This commit is contained in:
@@ -3,6 +3,7 @@ package handler
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/prabhupadasays/spsays-bff/internal/middleware"
|
||||
@@ -11,6 +12,40 @@ import (
|
||||
"github.com/prabhupadasays/spsays-bff/pkg/response"
|
||||
)
|
||||
|
||||
// Subscription limits. The endpoint length cap + host allow-list guard against
|
||||
// junk/abusive rows and SSRF (the BFF POSTs to the endpoint when sending), and
|
||||
// the per-user device cap bounds fan-out.
|
||||
const (
|
||||
maxEndpointLen = 2048
|
||||
maxKeyLen = 256
|
||||
maxDevicesPerUser = 10
|
||||
)
|
||||
|
||||
// validatePushEndpoint requires an https URL belonging to a known browser push
|
||||
// service — preventing the endpoint from pointing at internal/arbitrary hosts
|
||||
// (SSRF) and keeping the table free of junk.
|
||||
func validatePushEndpoint(endpoint string) bool {
|
||||
if l := len(endpoint); l == 0 || l > maxEndpointLen {
|
||||
return false
|
||||
}
|
||||
u, err := url.Parse(endpoint)
|
||||
if err != nil || u.Scheme != "https" || u.Host == "" {
|
||||
return false
|
||||
}
|
||||
host := strings.ToLower(u.Hostname())
|
||||
switch host {
|
||||
case "fcm.googleapis.com", "updates.push.services.mozilla.com":
|
||||
return true // Chrome/Brave/Edge (FCM), Firefox
|
||||
}
|
||||
// Safari/iOS (*.push.apple.com) and Windows/Edge WNS (*.notify.windows.com).
|
||||
for _, suffix := range []string{".push.apple.com", ".notify.windows.com", ".wns.windows.com"} {
|
||||
if strings.HasSuffix(host, suffix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// PushHandler serves Web Push subscription management. The VAPID public key
|
||||
// is handed to the browser so it can subscribe; subscriptions are stored per
|
||||
// user. All routes require auth.
|
||||
@@ -59,10 +94,20 @@ func (h *PushHandler) Subscribe(w http.ResponseWriter, r *http.Request) {
|
||||
response.Error(w, http.StatusBadRequest, "missing subscription fields")
|
||||
return
|
||||
}
|
||||
if !validatePushEndpoint(req.Endpoint) {
|
||||
response.Error(w, http.StatusBadRequest, "invalid push endpoint")
|
||||
return
|
||||
}
|
||||
if len(req.Keys.P256dh) > maxKeyLen || len(req.Keys.Auth) > maxKeyLen {
|
||||
response.Error(w, http.StatusBadRequest, "subscription keys too long")
|
||||
return
|
||||
}
|
||||
if err := h.repo.Upsert(r.Context(), uid, req.Endpoint, req.Keys.P256dh, req.Keys.Auth, r.UserAgent()); err != nil {
|
||||
response.Error(w, http.StatusInternalServerError, "could not save subscription")
|
||||
return
|
||||
}
|
||||
// Keep only the most-recent N devices per user (best-effort).
|
||||
_ = h.repo.PruneToMostRecent(r.Context(), uid, maxDevicesPerUser)
|
||||
response.NoContent(w)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user