fix(observability-proxy): refuse logs times vlselect cannot hold

The proxy sends vlselect RFC3339 times, which it never reads as a
guessed-unit number, but it accepted any RFC3339 year and unix
seconds up to 2286. vlselect keeps times as int64 nanoseconds through
time.UnixNano, which is undefined outside 1678..2262, so such a
window landed anywhere. An absolute time outside 1970..2262 now
answers 400; nothing was logged before 1970.

Assisted-by: LLM
Signed-off-by: ZverGuy <maximbel2003@gmail.com>
This commit is contained in:
ZverGuy committed 2026-11-10 00:45:07 +07:00
1 parent 429bf5ae76
commit 15eec1e5b1
2 files changed
+21 -2

No files matched your search

+15 -2
View File
@@ -325,6 +325,19 @@ func scaleDuration(f float64, unit time.Duration) (time.Duration, bool) {
// and a duration meaning that long before now (VictoriaLogs' "start=3h").
// Anything else is refused rather than guessed, since a misread timestamp
// silently moves the window.
// maxLogsTime is the last instant int64 nanoseconds hold.
var maxLogsTime = time.Unix(0, math.MaxInt64)
// absoluteLogsTime refuses a time vlselect cannot hold: it keeps times as
// int64 nanoseconds (time.UnixNano), which is undefined outside 1678..2262
// and would move the window anywhere; nothing was logged before 1970.
func absoluteLogsTime(s string, t time.Time) (time.Time, error) {
if t.Before(time.Unix(0, 0)) || !t.Before(maxLogsTime) {
return time.Time{}, fmt.Errorf("time %q is outside 1970..2262", s)
}
return t, nil
}
func parseTime(s string, now time.Time) (time.Time, error) {
if s == "now" {
return now, nil
@@ -344,10 +357,10 @@ func parseTime(s string, now time.Time) (time.Time, error) {
frac = (frac + "000000000")[:9]
nanos, _ = strconv.ParseInt(frac, 10, 64)
}
return time.Unix(secs, nanos), nil
return absoluteLogsTime(s, time.Unix(secs, nanos))
}
if t, err := time.Parse(time.RFC3339Nano, s); err == nil {
return t, nil
return absoluteLogsTime(s, t)
}
if d, err := parseDuration(s); err == nil {
return now.Add(-d), nil
@@ -135,6 +135,12 @@ func TestBuildLogsParamsRejects(t *testing.T) {
{"exponent duration", "query", url.Values{"start": {"3e2"}}, http.StatusBadRequest},
{"trailing dot", "query", url.Values{"start": {"300."}}, http.StatusBadRequest},
{"infinite time", "query", url.Values{"end": {"Inf"}}, http.StatusBadRequest},
// vlselect keeps times as int64 nanoseconds (time.UnixNano), which
// overflows outside 1678..2262; before 1970 is no log time either.
{"time before 1970", "query", url.Values{"start": {"0001-01-01T00:00:00Z"}, "end": {"0001-01-02T00:00:00Z"}}, http.StatusBadRequest},
{"time past int64 nanoseconds", "query", url.Values{"start": {"9999-01-01T00:00:00Z"}, "end": {"9999-01-02T00:00:00Z"}}, http.StatusBadRequest},
{"tail since before 1970", "tail", url.Values{"since": {"1969-12-31T23:59:59Z"}}, http.StatusBadRequest},
{"unix seconds past int64 nanoseconds", "query", url.Values{"start": {"9999999990"}, "end": {"9999999999"}}, http.StatusBadRequest},
{"NaN time", "query", url.Values{"start": {"NaN"}}, http.StatusBadRequest},
{"NaN step", "hits", url.Values{"step": {"NaN"}}, http.StatusBadRequest},
{"NaN start_offset", "tail", url.Values{"start_offset": {"NaN"}}, http.StatusBadRequest},