From ea0e3e9d2f5aec6c377551cd3ed7accf4e9263d9 Mon Sep 17 00:00:00 2001 From: autonomic-bot Date: Thu, 11 Jun 2026 06:20:08 +0000 Subject: [PATCH] =?UTF-8?q?review(shot):=20finding=20A1=20[adversary]=20?= =?UTF-8?q?=E2=80=94=20blank-retry=20overwrites=20unconditionally,=20can?= =?UTF-8?q?=20REGRESS=20a=20larger=20frame=20(9999B->4801B)=20to=20a=20wor?= =?UTF-8?q?se=20one;=20LOW/non-blocking=20(R7=20holds,=20visual=20M2=20che?= =?UTF-8?q?ck=20is=20backstop);=20trivial=20max(first,retry)=20guard=20sug?= =?UTF-8?q?gested.=20Independent=20cold=20probe,=209/9=20R7=20checks=20oth?= =?UTF-8?q?erwise=20pass.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BACKLOG-shot.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/BACKLOG-shot.md b/BACKLOG-shot.md index 4302b8d..84b51e0 100644 --- a/BACKLOG-shot.md +++ b/BACKLOG-shot.md @@ -75,4 +75,19 @@ PNG-size note: 4801/4802 B at 1280×800 is a byte-stable blank-frame fingerprint ## Adversary findings -(Adversary-owned section.) +### [adversary] A1 — blank-retry can REGRESS a larger frame to a worse one (LOW, non-blocking) — OPEN +**Where:** `runner/harness/screenshot.py` `_snap_with_blank_retry` (ce50f64). +**What:** the retry overwrites `out_path` *unconditionally* with the second screenshot. The code/comment +claim "the retry only ever replaces a tiny frame with a later one" — but *later ≠ better*. If the first +frame is e.g. 9999 B (a partial render, just under `BLANK_SIZE_BYTES=10000`) and the page regresses in the +extra 4 s settle (redirect, session-timeout splash, error overlay), the retry can yield a 4801 B blank that +**overwrites the better 9999 B frame**. The Builder's unit test only covers blank→blank (4801→4802); the +bigger→smaller regression is untested. +**Repro (cold, my independent probe, not the Builder's test file):** fake page returning sizes +`[9999, 4801]` → `_snap_with_blank_retry` keeps **4801** (the worse frame). +**Severity:** LOW. R7 holds (cosmetic only, never affects verdict); my M2 per-PNG visual check is the +backstop — any actually-blank final PNG will FAIL that recipe regardless. Filed for hardening, not a veto. +**Suggested guard (trivial, strictly safer):** keep the larger frame — only overwrite if +`getsize(retry) >= getsize(first)` (or snap retry to a temp path and pick `max`). Then extend the unit +test with a bigger→smaller case asserting the larger frame survives. +**Closes:** only I close this, after re-test. Non-blocking for an M2 claim, but I will re-check at M2.