mirror of
https://github.com/cozystack/cozystack.git
synced 2026-11-12 23:54:01 +09:00
fix(observability-proxy): refuse durations past the int64 range
parseDuration summed duration parts without an overflow check and converted out-of-range floats to Duration, which Go leaves implementation-defined. A start_offset such as 99999999999h99999999999h1s wrapped to 999ms and was accepted, and a huge step saturated on some architectures and wrapped on others. Such values now answer 400 like any other invalid duration. Assisted-by: LLM Signed-off-by: ZverGuy <maximbel2003@gmail.com>
This commit is contained in:
1 parent
889712d244
commit
97a536b89a
2 files changed
+40
-2
No files matched your search
@@ -18,6 +18,7 @@ package observabilityproxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
@@ -275,7 +276,11 @@ func parseDuration(s string) (time.Duration, error) {
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("invalid duration %q", s)
|
||||
}
|
||||
return time.Duration(f * float64(time.Second)), nil
|
||||
d, ok := scaleDuration(f, time.Second)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("duration %q out of range", s)
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
if s == "" {
|
||||
return 0, fmt.Errorf("empty duration")
|
||||
@@ -287,12 +292,27 @@ func parseDuration(s string) (time.Duration, error) {
|
||||
return 0, fmt.Errorf("invalid duration %q", s)
|
||||
}
|
||||
f, _ := strconv.ParseFloat(m[1], 64)
|
||||
total += time.Duration(f * float64(durationUnits[m[2]]))
|
||||
d, ok := scaleDuration(f, durationUnits[m[2]])
|
||||
if !ok || total > math.MaxInt64-d {
|
||||
return 0, fmt.Errorf("duration %q out of range", s)
|
||||
}
|
||||
total += d
|
||||
rest = rest[len(m[0]):]
|
||||
}
|
||||
return total, nil
|
||||
}
|
||||
|
||||
// scaleDuration refuses what does not fit a Duration: Go leaves an
|
||||
// out-of-range float to int conversion implementation-defined.
|
||||
func scaleDuration(f float64, unit time.Duration) (time.Duration, bool) {
|
||||
v := f * float64(unit)
|
||||
// float64(math.MaxInt64) rounds up to 2^63, which is already too large.
|
||||
if v >= float64(math.MaxInt64) {
|
||||
return 0, false
|
||||
}
|
||||
return time.Duration(v), true
|
||||
}
|
||||
|
||||
// parseTime accepts "now", unix seconds with an optional fraction, RFC3339,
|
||||
// and a duration meaning that long before now (VictoriaLogs' "start=3h").
|
||||
// Anything else is refused rather than guessed, since a misread timestamp
|
||||
|
||||
@@ -143,6 +143,11 @@ func TestBuildLogsParamsRejects(t *testing.T) {
|
||||
{"too many hits fields", "hits", url.Values{"field": strings.Fields("a b c d e f g h i j k")}, http.StatusBadRequest},
|
||||
{"since and start_offset", "tail", url.Values{"since": {"2026-11-04T11:59:00Z"}, "start_offset": {"1m"}}, http.StatusBadRequest},
|
||||
{"bad since", "tail", url.Values{"since": {"soon"}}, http.StatusBadRequest},
|
||||
{"start_offset summing past int64", "tail", url.Values{"start_offset": {"99999999999h99999999999h1s"}}, http.StatusBadRequest},
|
||||
{"step summing past int64", "hits", url.Values{"step": {"2562047h2562047h"}}, http.StatusBadRequest},
|
||||
{"step token past int64", "hits", url.Values{"step": {"99999999999h"}}, http.StatusBadRequest},
|
||||
{"bare-seconds step past int64", "hits", url.Values{"step": {"99999999999999999999"}}, http.StatusBadRequest},
|
||||
{"relative start summing past int64", "query", url.Values{"start": {"106751d1d"}}, http.StatusBadRequest},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -352,3 +357,16 @@ func TestBuildLogsParamsStepKeepsBucketCap(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseDurationBounds(t *testing.T) {
|
||||
for _, in := range []string{"2562047h", "106751d", "9223372036s", "292y"} {
|
||||
if d, err := parseDuration(in); err != nil || d <= 0 {
|
||||
t.Errorf("parseDuration(%q) = %s, %v; want a positive duration", in, d, err)
|
||||
}
|
||||
}
|
||||
for _, in := range []string{"2562048h", "106751d1d", "9223372037s", "9223372037", "293y", "1y292y"} {
|
||||
if d, err := parseDuration(in); err == nil {
|
||||
t.Errorf("parseDuration(%q) = %s; want an error past the int64 range", in, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user