fix(wake): #943 whole-string validation for WAKE_SNAPSHOT_TS_FUTURE_SLACK + version 0.6.13
ci/woodpecker/push/publish Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful

grep is line-oriented, so a multi-line knob value passed the per-line anchors and was still fatal in arithmetic. Replaced with a case pattern matching the whole string, so an embedded or leading newline rejects. Manifest bumped 0.6.12 -> 0.6.13: three materially different detectors had shipped under one version string, and version= is the component sole self-identity claim.

Authored-by: pepper
Reviewed-by: mos-dt (independent, at this head; transfer proven by blob-hash equality)
Merged-by: Mos
Co-authored-by: mos-dt-0 <[email protected]>
This commit was merged in pull request #943.
This commit is contained in:
2026-07-30 11:56:18 +00:00
committed by Mos
parent 3e47fc076f
commit 539b475a92
3 changed files with 59 additions and 13 deletions
@@ -276,15 +276,21 @@
# yields a NEGATIVE age — stale-reads-fresher-than-fresh, the
# exact failure class #940 fixes. The SLACK knob itself is
# operator input interpolated into arithmetic under set -u, so it
# gets the same discipline (#941 §2 round 2): validated
# ^[0-9]{1,9}$, else LOUD fallback to 300 — a malformed knob
# gets the same discipline (#941 §2 round 2): shape-validated as
# a plain non-negative integer of at most 9 digits, else LOUD
# fallback to 300 — a malformed knob
# ('300s', '5m', 'abc') must never kill the poll, and a negative
# one must never invert the guard into deny-all. Shape validation
# is NOT radix validation (#942 review): bash reads leading zeros
# as OCTAL, so '08'/'09' pass the regex yet are fatal in $((...))
# and '0300' silently means 192 — the knob is therefore forced
# base-10 (10#) after validation, so it means what the operator
# wrote. SKEW GUARANTEE
# as OCTAL, so '08'/'09' pass the shape check yet are fatal in
# $((...)) and '0300' silently means 192 — the knob is therefore
# forced base-10 (10#) after validation, so it means what the
# operator wrote. The validator and the consumer must also agree
# on STRING EXTENT (#942 follow-up): grep's ^...$ anchors bind
# per LINE, so a multi-line value ($'300\n8') passed the regex
# whole yet was fatal in $((...)) — validation is a whole-string
# case pattern, not grep, so an embedded newline rejects.
# SKEW GUARANTEE
# (stated, not implied): the future-skew check runs against the
# DETECTOR's clock; consumer-side age arithmetic runs on the
# consumer's. A surviving snapshot_ts is therefore attested only
@@ -300,8 +306,20 @@
# absence of these fields.
# Changed: detector.sh, digest.sh (+ test-wake-detector.sh
# D10/D11/D12/D13, test-wake-digest-quarantine.sh Q10).
# 0.6.13 #942/#943 SLACK-knob validation hardening, split from 0.6.12 because
# version= is the component's SOLE self-identity claim (no per-file
# hashes here) and two detector-changing merges after the 0.6.12
# stamp had left three materially different detectors under one
# version string (#943 review §2). #942: the knob is resolved once,
# shape-validated, LOUD fallback 300, and forced base-10 (10#) so
# zero-padded values mean what the operator wrote instead of octal.
# #943: validation is a whole-string case pattern, not grep, so an
# embedded newline ($'300\n8' — accepted per-line by grep's ^...$
# anchors, fatal in $((...))) rejects. Full rationale in the knob
# paragraph of the 0.6.12 entry above.
# Changed: detector.sh (+ test-wake-detector.sh D13).
component=wake
version=0.6.12
version=0.6.13
# Watch-list schema this component consumes, and the INCLUSIVE range of
# schema_version values it supports. A wake-watch-list.json whose schema_version