mirror of
https://github.com/projectsend/projectsend.git
synced 2026-09-16 16:45:07 +00:00
38187dcf1a
The expired page offers a "send me a new one" button that re-issues the invitation with no staff member involved. On its own that is reasonable -- the new link goes to the address on the invitation, never to whoever clicked, so holding a leaked URL gets nobody a working one, and it is the same shape as a password reset. What it spent was the operator's expiry window. A link could be renewed from a dead link, indefinitely, so a window set to 72 hours was only ever as short as the longest anybody bothered to wait. That matters in the case expiry is actually for: a link sitting somewhere it should not be -- a forwarded thread, a shared inbox, a mailbox that changed hands. So the chain gets a limit: three renewals, then a staff member has to send a new invitation. The count is carried forward on each renewal rather than stored per row, which is what makes it apply to the chain; a staff-sent invitation starts at zero, because sending one is somebody deciding to. A renewal beyond the limit answers in exactly the same words as a spent, unknown or revoked token, and sends nothing. Four situations, one sentence: telling them apart is how this door would become a way to learn which addresses an installation has invited. Renewals are now logged, which they were not -- sending and redeeming already were, and renewing was the one step that moved an invitation along with nobody behind it and left no trace. The limit bounds how many rows an anonymous door can write. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CPk8qAs38pudYGWwmGkYPe
195 KiB
195 KiB