Validate max_retries on both target forms (closes #221)
All checks were successful
check / check (push) Successful in 3m36s

This commit was merged in pull request #259.
This commit is contained in:
2026-08-24 01:32:48 +02:00
parent 0082f216fa
commit fd5966f807
7 changed files with 571 additions and 31 deletions

View File

@@ -905,16 +905,7 @@ func (h *Handlers) loadTargetMap(
// parsePage extracts a page number from the query string.
func (h *Handlers) parsePage(r *http.Request) int {
page := 1
if p := r.URL.Query().Get("page"); p != "" {
v, err := strconv.Atoi(p)
if err == nil && v > 0 {
page = v
}
}
return page
return pageOrFirst(r.URL.Query().Get("page"))
}
// loadEventsWithDeliveries loads paginated events and their
@@ -1443,7 +1434,6 @@ func (h *Handlers) processTargetCreate(
// Referer headers and error trackers record.
name := r.PostFormValue("name")
targetType := database.TargetType(r.PostFormValue("type"))
maxRetriesStr := r.PostFormValue("max_retries")
if name == "" {
http.Error(
@@ -1469,7 +1459,14 @@ func (h *Handlers) processTargetCreate(
return
}
maxRetries := parseNonNegativeInt(maxRetriesStr)
// A new target has no stored retry count, so an absent field
// takes the fire-and-forget default. A field the operator filled
// in with something invalid is rejected rather than becoming
// that default.
maxRetries, ok := targetMaxRetries(w, r, 0)
if !ok {
return
}
target := &database.Target{
WebhookID: webhook.ID,
@@ -1505,19 +1502,22 @@ func isValidTargetType(tt database.TargetType) bool {
}
}
// parseNonNegativeInt parses s as a non-negative integer,
// returning 0 if s is empty or invalid.
func parseNonNegativeInt(s string) int {
if s == "" {
return 0
// pageOrFirst parses a paginated page number, answering 1 for
// anything empty, unparseable or out of range.
//
// Falling back rather than rejecting is correct here and only here:
// a page number is where to send the browser next, not configuration
// the operator is storing, and the actions that submit one have
// already completed by the time it is read — answering 400 would
// report a failure that did not happen. Anything an operator SETS
// must be validated instead; see parseMaxRetries.
func pageOrFirst(s string) int {
v, err := strconv.Atoi(strings.TrimSpace(s))
if err != nil || v < 1 {
return 1
}
v, err := strconv.Atoi(s)
if err == nil && v >= 0 {
return v
}
return 0
return v
}
// targetFormInput carries the raw form values describing a target's