fix(merge-queue): fail-closed on empty PUSH_REQUIRED_CONTEXTS + untrustworthy snapshot ts (#3210 tail) #3224
Reference in New Issue
Block a user
Delete Branch "fix/merge-gate-hardening-3210b"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
#3210 merge-gate hardening tail (HIGH + MEDIUM)
Sibling fail-opens after the CRITICAL+HIGH approval fixes (shipped in #3222). Both strictly tightening, mirror the existing fail-closed exception convention.
🟠 FIX A (HIGH) — empty
PUSH_REQUIRED_CONTEXTS→ vacuous main-green passpush_required_contexts()returned[]on an empty/whitespace/comma env →required_contexts_green(main_latest, [])passed for ANY main state incl. all-red → the queue's own main-green backstop was silently disabled (merge onto a red main). Fix: newPushRequiredContextsUnavailable(ApiError), raised on an empty parse → propagates tomain()→ rc 1 (HOLD + page); never returns[].🟡 FIX B (MEDIUM) — malformed/absent snapshot
tstreated as FRESHload_conductor_snapshot()skipped the age-check on absent/emptytsand swallowed an unparseabletsas 'fresh (conservative)' — actually anti-conservative (an undated/old snapshot trusted as current). Fix: require a present+parseable+in-windowts; elsereturn None(self-fetch via the existing cache-miss path).Tests: +10 (incl. an all-red-main integration proving the tick HOLDs, and absent/malformed-ts discard); 157 passed locally; new tests proven to fail against pre-fix behavior. Addresses the #3210 hardening tail.
APPROVED on
d9b63d1bef.5-axis + adversarial security review:
CI notes: Platform(Go) and CI/all-required are green. Ops Scripts is red in unrelated test_sop_checklist.py tuple/list expectations, not in the changed merge-queue tests.
APPROVED on
d9b63d1b.5-axis/security review: Correctness: empty/blank/all-comma PUSH_REQUIRED_CONTEXTS now raises PushRequiredContextsUnavailable instead of letting required_contexts_green(..., []) pass vacuously, so the main-green backstop cannot silently disappear. The exception is an ApiError, so the conductor tick holds/no-merges rather than continuing. Conductor snapshots with absent/empty/unparseable ts are discarded and self-fetch live state instead of being trusted as fresh; fresh dated snapshots still work. Robustness: preserves the existing self-fetch fallback and happy-path snapshot use. Security: strictly tightens merge authorization; no new network/secrets surface. Performance: only snapshot parsing/env parsing checks. Readability: comments/tests clearly pin the fail-closed invariants.
Reviewed files: .gitea/scripts/gitea-merge-queue.py and .gitea/scripts/tests/test_gitea_merge_queue.py. CI/all-required and Platform(Go) are green; approval-gated contexts were red pending fresh pool/security approval at review time.