Skip to content

Commit 3160e52

Browse files
committed
address review: clamp seconds before multiplying, preserve negatives
Per @lbellows / @eternal-flame-AD feedback: - Clamp the requested seconds to [-maxElevationSeconds, maxElevationSeconds] BEFORE multiplying, so an int64 multiply can never overflow (including wraps that land positive, e.g. 20023544073) and large negatives (e.g. -9223372037). - Preserve negative values instead of mapping them to DefaultElevationDuration, so the UI's cancel (durationSeconds: -1) still sets elevatedUntil in the past rather than granting a fresh hour. - Keep it simple: a small guard inside the function, no extra ceremony. Tests updated: cancel -1 preserved, zero stays zero, overflow-positive and large-negative both clamped.
1 parent 41a18ec commit 3160e52

2 files changed

Lines changed: 25 additions & 29 deletions

File tree

‎model/elevate.go‎

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,23 @@ type ElevateRequest struct {
1515

1616
var DefaultElevationDuration = time.Hour
1717

18-
// maxElevationDuration caps a requested elevation duration. A practically
19-
// "infinite" elevation is an intentionally supported relief valve, so this is
20-
// deliberately large (~100 years) rather than a security limit. Its only
21-
// purpose is to keep the value well below the point where
22-
// time.Duration(seconds) * time.Second overflows (int64 nanoseconds wrap past
23-
// ~292 years), which would otherwise yield an elevatedUntil in the past and
24-
// silently drop the elevation.
25-
const maxElevationDuration = 100 * 365 * 24 * time.Hour
18+
// maxElevationSeconds caps a requested elevation duration (~100 years). A
19+
// practically "infinite" elevation is intentionally supported, so this is not a
20+
// security limit; its only purpose is to keep the value well below the point
21+
// where time.Duration(seconds) * time.Second overflows int64 nanoseconds
22+
// (~292 years) and would otherwise wrap to a bogus (even past) timestamp.
23+
const maxElevationSeconds = 100 * 365 * 24 * 60 * 60
2624

2725
// ElevationDuration converts a requested duration in seconds to a
28-
// time.Duration, clamping absurdly large or negative values to a safe range so
29-
// the subsequent time.Time.Add cannot overflow into a past timestamp.
26+
// time.Duration, clamping the seconds (before multiplying) to a safe range so
27+
// the subsequent multiply cannot overflow. Negative values are preserved so the
28+
// client can still cancel an elevation (the UI sends durationSeconds: -1, which
29+
// must set elevatedUntil in the past rather than grant a fresh elevation).
3030
func ElevationDuration(seconds int) time.Duration {
31-
if seconds <= 0 {
32-
return DefaultElevationDuration
31+
if seconds > maxElevationSeconds {
32+
seconds = maxElevationSeconds
33+
} else if seconds < -maxElevationSeconds {
34+
seconds = -maxElevationSeconds
3335
}
34-
d := time.Duration(seconds) * time.Second
35-
// Detect overflow (wrap to negative) or an intentionally huge value and
36-
// clamp to the ceiling.
37-
if d <= 0 || d > maxElevationDuration {
38-
return maxElevationDuration
39-
}
40-
return d
36+
return time.Duration(seconds) * time.Second
4137
}

‎model/elevate_test.go‎

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,21 @@ func TestElevationDuration(t *testing.T) {
1212
want time.Duration
1313
}{
1414
{"normal", 900, 900 * time.Second},
15-
{"zero falls back to default", 0, DefaultElevationDuration},
16-
{"negative falls back to default", -5, DefaultElevationDuration},
17-
{"huge value is clamped", 9223372036854775807, maxElevationDuration},
18-
{"just under ceiling is unchanged", 100 * 365 * 24 * 3600, maxElevationDuration},
15+
{"zero stays zero", 0, 0},
16+
// Negative values must be preserved so the client can cancel an
17+
// elevation (UI sends -1); they must NOT become a positive duration.
18+
{"cancel -1 preserved", -1, -1 * time.Second},
19+
{"huge clamped to max", 9223372036854775807, maxElevationSeconds * time.Second},
20+
// Values that would overflow when multiplied but land positive are
21+
// clamped by clamping seconds first.
22+
{"overflow-positive clamped", 20023544073, maxElevationSeconds * time.Second},
23+
{"large negative clamped", -9223372037, -maxElevationSeconds * time.Second},
1924
}
2025
for _, c := range cases {
2126
t.Run(c.name, func(t *testing.T) {
22-
got := ElevationDuration(c.seconds)
23-
if got != c.want {
27+
if got := ElevationDuration(c.seconds); got != c.want {
2428
t.Fatalf("ElevationDuration(%d) = %v, want %v", c.seconds, got, c.want)
2529
}
26-
// The resulting time must always be in the future (no overflow).
27-
if time.Now().Add(got).Before(time.Now()) {
28-
t.Fatalf("ElevationDuration(%d) produced a past time", c.seconds)
29-
}
3030
})
3131
}
3232
}

0 commit comments

Comments
 (0)