fix(wake): #941 review §2 round 2 — validate WAKE_SNAPSHOT_TS_FUTURE_SLACK before arithmetic
ci/woodpecker/pr/ci Pipeline was canceled

The operator's slack knob was interpolated raw into $((...)) under set -u:
'300s'/'5m' -> 'value too great', 'abc' -> 'unbound variable' — all FATAL to
the poll, falsifying the PR's own 'poll never fails' invariant, and a negative
value silently inverted the guard into deny-all. Now resolved once into a
local, validated ^[0-9]{1,9}$, loud fallback to 300 on mismatch. New D13
group: '300s'/'abc'/negative all fall back and KEEP valid metadata; a valid
tightened knob (0) still rejects a future ts. Manifest states the skew
guarantee explicitly: a surviving snapshot_ts is attested only to within SLACK
seconds of the DETECTOR's clock — bounded negative age at render is possible;
treat age<=0 as effectively-current, never proof of freshness.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NsKce8iZuSuRnu3gVMCBKB
This commit is contained in:
Jason Woltje
2026-07-30 05:57:17 -05:00
co-authored by Claude Fable 5
parent 8710d0f6d7
commit 313f3087e6
3 changed files with 95 additions and 5 deletions
@@ -291,10 +291,19 @@ _poll_source() {
# Cross-host NTP skew of a few seconds is the NORMAL case, so allow a small
# slack; beyond it, drop the ts (the sha stays: independently verifiable).
if [ -n "$snap_ts" ]; then
local now_s
# The OPERATOR's knob gets the same discipline as the adapter's ts: it
# is interpolated into $((...)) under set -u, so a non-numeric value
# ('300s', '5m', 'abc') would be FATAL to the poll — the one thing this
# block must never be. Resolve it ONCE, validate, fall back loudly.
local now_s slack
slack="${WAKE_SNAPSHOT_TS_FUTURE_SLACK:-300}"
if ! printf '%s' "$slack" | grep -Eq '^[0-9]{1,9}$'; then
echo "detector.sh: WAKE_SNAPSHOT_TS_FUTURE_SLACK='$slack' is not a plain non-negative integer (seconds) — falling back to 300, poll continues (#940)." >&2
slack=300
fi
now_s="$(date +%s)"
if [ "$snap_ts" -gt $((now_s + ${WAKE_SNAPSHOT_TS_FUTURE_SLACK:-300})) ]; then
echo "detector.sh: source '$kind/$id' snapshot_ts is beyond the ${WAKE_SNAPSHOT_TS_FUTURE_SLACK:-300}s future-skew allowance (ts=$snap_ts now=$now_s) — snapshot_ts DROPPED, poll continues (#940)." >&2
if [ "$snap_ts" -gt $((now_s + slack)) ]; then
echo "detector.sh: source '$kind/$id' snapshot_ts is beyond the ${slack}s future-skew allowance (ts=$snap_ts now=$now_s) — snapshot_ts DROPPED, poll continues (#940)." >&2
snap_ts=""
fi
fi