Read the expiry form value where the body is bounded (#43)
All checks were successful
check / check (push) Successful in 2m36s
All checks were successful
check / check (push) Successful in 2m36s
The pinned CI linter's gosec G120 flagged r.FormValue in buildDatabaseTargetConfig because the MaxBytesReader guard lives one function up in processTargetCreate, out of static-analysis sight. Read the expiry alongside the other form values in processTargetCreate and pass it down as a string, matching how the http and slack config builders receive their URL.
This commit is contained in:
@@ -28,7 +28,7 @@ func (s *Handlers) BuildSlackTargetConfigForTest(
|
|||||||
// package.
|
// package.
|
||||||
func (s *Handlers) BuildDatabaseTargetConfigForTest(
|
func (s *Handlers) BuildDatabaseTargetConfigForTest(
|
||||||
w http.ResponseWriter,
|
w http.ResponseWriter,
|
||||||
r *http.Request,
|
expiry string,
|
||||||
) (string, error) {
|
) (string, error) {
|
||||||
return s.buildDatabaseTargetConfig(w, r)
|
return s.buildDatabaseTargetConfig(w, expiry)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -189,25 +187,6 @@ func TestRenderTemplate(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// databaseConfigRequest builds a POST request carrying the
|
|
||||||
// given expiry as a form value, as the add-target form does.
|
|
||||||
func databaseConfigRequest(expiry string) *http.Request {
|
|
||||||
form := url.Values{}
|
|
||||||
if expiry != "" {
|
|
||||||
form.Set("expiry", expiry)
|
|
||||||
}
|
|
||||||
|
|
||||||
req := httptest.NewRequestWithContext(
|
|
||||||
context.Background(), http.MethodPost, "/",
|
|
||||||
strings.NewReader(form.Encode()),
|
|
||||||
)
|
|
||||||
req.Header.Set(
|
|
||||||
"Content-Type", "application/x-www-form-urlencoded",
|
|
||||||
)
|
|
||||||
|
|
||||||
return req
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuildDatabaseTargetConfig_Valid(t *testing.T) {
|
func TestBuildDatabaseTargetConfig_Valid(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -220,25 +199,19 @@ func TestBuildDatabaseTargetConfig_Valid(t *testing.T) {
|
|||||||
|
|
||||||
// Empty expiry: the keep-forever default, empty config.
|
// Empty expiry: the keep-forever default, empty config.
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
cfg, err := h.BuildDatabaseTargetConfigForTest(
|
cfg, err := h.BuildDatabaseTargetConfigForTest(w, "")
|
||||||
w, databaseConfigRequest(""),
|
|
||||||
)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Empty(t, cfg)
|
assert.Empty(t, cfg)
|
||||||
|
|
||||||
// Explicit never is stored as config.
|
// Explicit never is stored as config.
|
||||||
w = httptest.NewRecorder()
|
w = httptest.NewRecorder()
|
||||||
cfg, err = h.BuildDatabaseTargetConfigForTest(
|
cfg, err = h.BuildDatabaseTargetConfigForTest(w, "never")
|
||||||
w, databaseConfigRequest("never"),
|
|
||||||
)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.JSONEq(t, `{"expiry":"never"}`, cfg)
|
assert.JSONEq(t, `{"expiry":"never"}`, cfg)
|
||||||
|
|
||||||
// A positive duration is stored as config.
|
// A positive duration is stored as config.
|
||||||
w = httptest.NewRecorder()
|
w = httptest.NewRecorder()
|
||||||
cfg, err = h.BuildDatabaseTargetConfigForTest(
|
cfg, err = h.BuildDatabaseTargetConfigForTest(w, "720h")
|
||||||
w, databaseConfigRequest("720h"),
|
|
||||||
)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.JSONEq(t, `{"expiry":"720h"}`, cfg)
|
assert.JSONEq(t, `{"expiry":"720h"}`, cfg)
|
||||||
}
|
}
|
||||||
@@ -257,9 +230,7 @@ func TestBuildDatabaseTargetConfig_RejectsBadExpiry(
|
|||||||
|
|
||||||
for _, bad := range []string{"nonsense", "7d", "-5h"} {
|
for _, bad := range []string{"nonsense", "7d", "-5h"} {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
cfg, err := h.BuildDatabaseTargetConfigForTest(
|
cfg, err := h.BuildDatabaseTargetConfigForTest(w, bad)
|
||||||
w, databaseConfigRequest(bad),
|
|
||||||
)
|
|
||||||
|
|
||||||
require.Error(t, err, "expiry %q", bad)
|
require.Error(t, err, "expiry %q", bad)
|
||||||
assert.Empty(t, cfg)
|
assert.Empty(t, cfg)
|
||||||
|
|||||||
@@ -816,6 +816,7 @@ func (h *Handlers) processTargetCreate(
|
|||||||
targetType := database.TargetType(r.FormValue("type"))
|
targetType := database.TargetType(r.FormValue("type"))
|
||||||
targetURL := r.FormValue("url")
|
targetURL := r.FormValue("url")
|
||||||
maxRetriesStr := r.FormValue("max_retries")
|
maxRetriesStr := r.FormValue("max_retries")
|
||||||
|
expiry := r.FormValue("expiry")
|
||||||
|
|
||||||
if name == "" {
|
if name == "" {
|
||||||
http.Error(
|
http.Error(
|
||||||
@@ -835,7 +836,7 @@ func (h *Handlers) processTargetCreate(
|
|||||||
}
|
}
|
||||||
|
|
||||||
configJSON, err := h.buildTargetConfig(
|
configJSON, err := h.buildTargetConfig(
|
||||||
w, r, targetType, targetURL,
|
w, r, targetType, targetURL, expiry,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@@ -893,11 +894,13 @@ func parseNonNegativeInt(s string) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// buildTargetConfig builds the JSON config string for a target.
|
// buildTargetConfig builds the JSON config string for a target.
|
||||||
|
// The expiry form value is read by the caller (which bounds the
|
||||||
|
// request body) and applies to database targets only.
|
||||||
func (h *Handlers) buildTargetConfig(
|
func (h *Handlers) buildTargetConfig(
|
||||||
w http.ResponseWriter,
|
w http.ResponseWriter,
|
||||||
r *http.Request,
|
r *http.Request,
|
||||||
targetType database.TargetType,
|
targetType database.TargetType,
|
||||||
targetURL string,
|
targetURL, expiry string,
|
||||||
) (string, error) {
|
) (string, error) {
|
||||||
switch targetType {
|
switch targetType {
|
||||||
case database.TargetTypeHTTP:
|
case database.TargetTypeHTTP:
|
||||||
@@ -905,7 +908,7 @@ func (h *Handlers) buildTargetConfig(
|
|||||||
case database.TargetTypeSlack:
|
case database.TargetTypeSlack:
|
||||||
return h.buildSlackTargetConfig(w, r, targetURL)
|
return h.buildSlackTargetConfig(w, r, targetURL)
|
||||||
case database.TargetTypeDatabase:
|
case database.TargetTypeDatabase:
|
||||||
return h.buildDatabaseTargetConfig(w, r)
|
return h.buildDatabaseTargetConfig(w, expiry)
|
||||||
case database.TargetTypeLog:
|
case database.TargetTypeLog:
|
||||||
return "", nil
|
return "", nil
|
||||||
default:
|
default:
|
||||||
@@ -1017,16 +1020,16 @@ func (h *Handlers) buildSlackTargetConfig(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// buildDatabaseTargetConfig builds config JSON for a database
|
// buildDatabaseTargetConfig builds config JSON for a database
|
||||||
// (archive) target. The optional expiry form value is validated
|
// (archive) target. The optional expiry (a form value read by
|
||||||
// here, at creation time, so an unparseable value is rejected
|
// the caller, which bounds the request body) is validated here,
|
||||||
// with a 400 instead of failing every subsequent delivery. An
|
// at creation time, so an unparseable value is rejected with a
|
||||||
// empty expiry yields an empty config (the keep-forever
|
// 400 instead of failing every subsequent delivery. An empty
|
||||||
// default).
|
// expiry yields an empty config (the keep-forever default).
|
||||||
func (h *Handlers) buildDatabaseTargetConfig(
|
func (h *Handlers) buildDatabaseTargetConfig(
|
||||||
w http.ResponseWriter,
|
w http.ResponseWriter,
|
||||||
r *http.Request,
|
expiry string,
|
||||||
) (string, error) {
|
) (string, error) {
|
||||||
expiry := strings.TrimSpace(r.FormValue("expiry"))
|
expiry = strings.TrimSpace(expiry)
|
||||||
if expiry == "" {
|
if expiry == "" {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user