Cosmic Bull

Rendered from pearl/r/timelock_guardian/FIXES.md at commit f191063fd89d in the project repository. The committed file is the source of truth; this page is a rendering of it.

timelock_guardian — security fix series (2026-09-21)

Base: mechanical pearl port (port.py) of SillyZir/timelock_guardian @ 44a4f3a685b9f2c263d51f4362065aa26b880dc9. The user then authorized fixing every YELLOW audit finding, so the files in this directory are port + fix series — they intentionally diverge from the GitHub commit. port.py is kept as provenance for the original mechanical transform only; the committed .gno files here are the deployable source of truth until the fixes are pushed upstream.

Three fresh-context audit rounds: round 1 (ported code) 0 RED / 5+1 YELLOW; round 2 (after fixes 1-6) verified all six closed, found 3 new low YELLOWs; round 3 verified those closed too — 0 RED, 0 YELLOW remaining, cosmetic notes only.

Round 1 findings → fixes

#FindingFixRegression test
Y1caller() helper derived identity via stack-walking unsafe.PreviousRealm() in a non-crossing helper — correct on all paths but enforced only by call-site disciplineAll crossing entrypoints declare cur realm; identity is cur.Previous().Address() inline; helper deleted; unsafe retained for OriginSend onlywhole suite (every auth test crosses the new shape)
Y2Sweep-queue starvation: long-delay sybil wall in the global FIFO window made victims' expired actions unreapable — wedging quota and SetGuardianPermissionless Expire(cur, actionID) reaps any provably expired action (refuses live/executed); recovery no longer depends on sweep orderTestExpire_PermissionlessRecovery, TestExpire_RefusesLiveAndExecuted, TestVictimRecoversBehindSybilWall
Y3sanitize() missed < > — raw HTML (incl. <script>) reached the shared Render pageReplacer maps <(, >)TestRender_SanitizesHTMLBrackets
Y4O(n) removeID scans over globally shared, attacker-growable pendingIDs/actionIDs; counter map could desyncPer-target pending map[string][]string (each ≤ MaxPendingPerTarget); per-target sweep (global SweepBudget removed); pendingCount = list length (no counter to desync); render index self-trims to RenderIndexCapTestGetPending_GroupedByTarget, TestPendingCounterConsistency, TestRenderIndexSelfTrims, TestVictimRecoversBehindSybilWall
Y5One-step TransferTargetOwnership consumed the recipient's quota and dumped pending obligations without consentTwo-step: transfer sets PendingOwner (or clears with ""); new AcceptTargetOwnership moves ownership + quota at consent timeTestTransferTargetOwnership_TwoStep, _ClearOffer, _QuotaCheckedAtAccept, TestUnsolicitedOfferConsumesNothing
Y6No stray-send guard and no banker — coins attached to any call stranded on the realm address foreverrejectStraySend(cur) first statement of all 9 crossing functionsTestStraySendRejectedEverywhere (all 9 probed)

Round 2 findings → fixes

#FindingFixRegression test
Y-1GetPending iterated every target ever registered (append-only targetNames) — unbounded sybil-growable scanpendingTargets list (targets with ≥1 live pending) maintained via O(1) swap-remove (ptIndex); GetPending/Render iterate only itTestPendingTargetsBookkeeping
Y-2Render's Pending section read the trimmed index — executed churn could push a live pending action off the page for its whole delay windowPending section built from the live per-target lists (flood-immune, capped with note); Executed section keeps the bounded index windowTestRenderShowsPendingDespiteChurn
Y-3float64 Duration.Seconds() fed a consensus-visible abort messageInteger remaining / time.Second at both sites; realm is float-freeTestExecute_TooEarlyBlocked (message path)
G2reap() would delete an executed record if ever called on onereap early-returns on Executed — permanence enforced at the sink— (defensive)
UXExpired-but-unswept actions blocked SetGuardianOwner-gated sweep before the pending gateTestSetGuardian_SweepsExpiredFirst
G4Coverage gapsVeto added to stray-send probe; already-owner offer branch tested; misleading gone locals renamedin-suite

Suite: 50 tests, all passing on the chain/pearl toolchain (c4c72fd); lint clean; full workspace green.

Provenance v2 (2026-09-21, post-push)

Remediation pushed upstream: SillyZir/timelock_guardian @ bb05e64898967125b900a9275ca84229bff5e007 (main), authored solely by SillyZir, no AI attribution (verified via fresh clone + GitHub API). GitHub is again the source of truth; the transformation to this directory is T1 ONLY: gnomod module-path namespace rewrite — both .gno files are byte-identical to the GitHub commit (verified against the fresh clone).