a digital entity named phi that roams bsky phi.zzstoatzz.io
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

close cross-request exploit: require solo-owner batch for like-auth

_is_owner in batch mode now only unlocks if the batch contains no
authors other than the owner (and phi itself). closes the window where
a stranger's owner-gated request would inherit authorization from an
unrelated owner like landing in the same 10-second poll cycle. if a
stranger is in the batch, the owner can just re-like after it clears.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+22 -14
+22 -14
src/bot/tools/_helpers.py
··· 42 42 def _is_owner(ctx: RunContext[PhiDeps]) -> bool: 43 43 """Check if the bot's owner is participating in this interaction. 44 44 45 - In single-message mode, checks author_handle directly. In batch mode 46 - (author_handle is empty), only unlocks when the owner liked or 47 - reposted one of phi's posts. Like/repost notifications only fire 48 - for engagement on phi's own content (protocol-level guarantee), so 49 - a stranger mentioning phi in the same batch can't inherit owner 50 - authorization. Direct mentions/replies from the owner still go 51 - through the single-message path where author_handle is set. 45 + Single-message mode: direct author_handle check. 46 + 47 + Batch mode (author_handle is empty): unlock only when the owner liked 48 + or reposted one of phi's posts AND no other authors are present in 49 + the batch. The "no other authors" guard eliminates the cross-request 50 + exploit where a stranger's owner-gated request would inherit 51 + authorization from an unrelated owner like in the same poll window. 52 + If a stranger is in the batch, likes don't authorize — just re-like 53 + after the batch clears. 52 54 """ 53 55 if ctx.deps.author_handle == settings.owner_handle: 54 56 return True 55 - if ctx.deps.notifications_context: 56 - return any( 57 - e.get("author_handle") == settings.owner_handle 58 - and e.get("reason") in ("like", "repost") 59 - for e in ctx.deps.notifications_context.values() 60 - ) 61 - return False 57 + if not ctx.deps.notifications_context: 58 + return False 59 + 60 + authors = {e.get("author_handle") for e in ctx.deps.notifications_context.values()} 61 + # any author other than the owner or phi itself means batch is mixed 62 + if authors - {settings.owner_handle, settings.bluesky_handle}: 63 + return False 64 + 65 + return any( 66 + e.get("author_handle") == settings.owner_handle 67 + and e.get("reason") in ("like", "repost") 68 + for e in ctx.deps.notifications_context.values() 69 + ) 62 70 63 71 64 72 # --- formatting ---