fix(2): F2-13 cryptpad roundtrip read-back robustness — poll all frames for marker
Adversary cold-verify of F2-9 FAILED: the read-back's CKEditor-frame-attach wait timed out on a fresh cold context (flaky, not 3x-reliable). Fix: read-back now polls EVERY frame's body text for the marker (don't require the specific ckeditor-inner frame to attach — that's the flaky part) with a generous ~240s deadline + periodic reloads to unstick cold loads. The marker appearing in a fresh context still proves server-side E2E-encrypted persistence (only URL+fragment key carried over). Also bumped the session-1 post-type sync wait 9s→12s. F2-13 Adversary-owned; will validate cold before it closes F2-9. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -85,6 +85,33 @@ def _ckeditor_frame(page, deadline_polls=90, reload_at=22, reload_url=None):
|
||||
return None
|
||||
|
||||
|
||||
def _poll_any_frame_for_text(page, needle, deadline_polls=120, reload_at=(20, 45, 75, 100), reload_url=None):
|
||||
"""Robust read-back (F2-13): poll EVERY frame's body text for `needle`, returning True as soon as
|
||||
it appears. The fresh cold-cache read-back context's deeply-nested CKEditor frame is slow/flaky to
|
||||
*attach* by URL (the prior `_ckeditor_frame` wait timed out on the Adversary's cold run), but the
|
||||
decrypted pad content lands in whichever frame renders it — so we don't require a specific frame,
|
||||
we just watch all of them for the marker. Reload periodically to unstick a stalled cold load.
|
||||
Generous deadline (~deadline_polls*2s) since the fresh context recompiles LESS + re-fetches +
|
||||
decrypts. Returns False if the marker never appears (genuine non-persistence)."""
|
||||
for i in range(deadline_polls):
|
||||
for f in page.frames:
|
||||
try:
|
||||
if needle in (f.locator("body").inner_text(timeout=2000) or ""):
|
||||
return True
|
||||
except Exception: # noqa: BLE001 — frame not ready / detached; keep polling
|
||||
pass
|
||||
if reload_url and i in reload_at:
|
||||
try:
|
||||
harness_browser.goto_with_retry(
|
||||
page, reload_url, accept_statuses=(200,), goto_timeout_ms=60_000,
|
||||
wait_until="load", deadline_seconds=120,
|
||||
)
|
||||
except Exception: # noqa: BLE001 — best-effort unstick
|
||||
pass
|
||||
page.wait_for_timeout(2000)
|
||||
return False
|
||||
|
||||
|
||||
def _dismiss_store_modal(page):
|
||||
"""Anonymous pads prompt 'store in CryptDrive?'. Dismiss it (DON'T STORE) so it can't intercept
|
||||
editor clicks. Non-fatal if absent."""
|
||||
@ -120,22 +147,23 @@ def test_cryptpad_pad_content_survives_fresh_session(live_app):
|
||||
body.click()
|
||||
page.wait_for_timeout(1000)
|
||||
body.type(marker, delay=40)
|
||||
page.wait_for_timeout(9000) # let CryptPad encrypt + sync the update to the server
|
||||
page.wait_for_timeout(12000) # let CryptPad encrypt + sync the update to the server
|
||||
assert marker in ck.locator("body").inner_text(), (
|
||||
"marker not present in the editor after typing — type did not land"
|
||||
)
|
||||
ctx1.close()
|
||||
|
||||
# --- session 2: FRESH context (no shared storage) reads the pad back by URL ---
|
||||
# --- session 2: FRESH context (no shared storage/localStorage) reads the pad back by URL.
|
||||
# Robust read-back (F2-13): poll ALL frames for the marker (don't require the CKEditor
|
||||
# frame to attach — that's the flaky part on a cold fresh context). The marker appearing
|
||||
# in a brand-new session proves the content was persisted server-side (encrypted) and
|
||||
# decrypted from only the URL+fragment key.
|
||||
ctx2 = browser.new_context(ignore_https_errors=True)
|
||||
page2, _ = _open_pad(ctx2, pad_url)
|
||||
ck2 = _ckeditor_frame(page2, reload_url=pad_url)
|
||||
assert ck2 is not None, "CKEditor content frame never attached on read-back"
|
||||
page2.wait_for_timeout(6000) # let the pad load + decrypt
|
||||
readback = ck2.locator("body").inner_text()
|
||||
assert marker in readback, (
|
||||
found = _poll_any_frame_for_text(page2, marker, reload_url=pad_url)
|
||||
assert found, (
|
||||
f"marker {marker!r} did NOT survive into a fresh session — content not persisted/"
|
||||
f"decrypted. Read-back body excerpt: {readback[:200]!r}"
|
||||
"decrypted (polled all frames + reloads to a generous deadline)"
|
||||
)
|
||||
finally:
|
||||
browser.close()
|
||||
|
||||
Reference in New Issue
Block a user