forked from QORT/qortal
-
Notifications
You must be signed in to change notification settings - Fork 67
Open
Description
Bug: GROUP_INVITE expiration not enforced (invites never expire)
Summary
Closed-group invites (GROUP_INVITE) include a TTL/expiration parameter, but expiration is not enforced anywhere in Qortal Core. As a result, any invite can be accepted indefinitely, even long after its intended expiry, allowing late/unwanted joins to closed groups.
Impact / Why this matters
- Closed-groups effectively have permanent invites unless admins manually cancel them or kick members after they join.
- Admins who issue short-lived invites (e.g. “valid for 1 day”) cannot rely on TTL for access control.
- Users can accept an old invite months later and become a member, which is surprising and can be a privacy/security concern for groups that assume invite expiry works.
- Current workaround is manual: kick members after they join, or preemptively cancel invites.
Expected behavior
For closed groups, once an invite’s TTL has passed:
- The invite should be treated as invalid/expired.
- A
JOIN_GROUPshould not be auto-approved by an expired invite (ideally it becomes a join request again, requiring a fresh invite/approval).
Actual behavior
- Issued invites can be accepted at any time, regardless of TTL/expiration.
JOIN_GROUPfinds an invite record and finalizes membership even if the invite’sexpires_whenis in the past.
Technical cause (current implementation)
-
Invite TTL is stored and
expires_whenis computed when savingGROUP_INVITE(expiry persisted inGroupInvites.expires_when). -
However:
- Join processing does not check
expires_whenat all; it only checks whether an invite exists and then uses it to approve membership. - Invite retrieval APIs / repository queries also do not filter expired invites; they return all invites (expired or not), and the join path treats any returned invite as valid.
- Join processing does not check
In short: expiration is data-only right now; it is never consulted in validation/finalization logic.
Reproduction (simple)
- Create a closed group.
- Admin sends
GROUP_INVITEto address X with a short TTL (e.g. 60 seconds). - Wait until TTL has passed.
- Address X sends
JOIN_GROUP. - Observe: X is still added as a member (invite behaves as non-expiring).
Proposed fix (high level)
-
Consensus-enforced expiry on membership finalization (behind a feature trigger):
- After the trigger height, treat an invite as valid only if
expiry == null/0 (never)ORfinalization-time <= expires_when. - If expired, ignore invite and handle
JOIN_GROUPas a join request (preferred) or reject (more disruptive). - Must be feature-triggered because it changes membership outcomes (consensus).
- After the trigger height, treat an invite as valid only if
-
API filtering for invites:
- Default invite-list endpoints should omit expired invites.
- Add optional flag (e.g.
includeExpired=true) to show them for debugging/UI needs.
-
Tests:
-
Add unit/integration tests covering:
- pre-trigger vs post-trigger behavior
- invite-first and join-first ordering
- expired invite does not grant membership
- API does not return expired invites by default
-
Notes / Related
- There is a manual cancellation tx (
CANCEL_GROUP_INVITE), but that’s not a substitute for TTL enforcement. - Prefer filtering over DB pruning for expired invites (avoid unnecessary state mutation; keep consensus logic deterministic).
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels