FOSSA invite refresh could soft-delete/clobber invitations for confirmed team members
- Dominant language
- Go
- Stars
- 9
- Forks
- 10
- Avg merge
- 16h 55m
- Merged PRs (30d)
- 6
Description
## Summary
`handleFossaInviteRefresh` (staff-only endpoint, `POST /api/services/fossa/invites/refresh`) had a guard bug that could soft-delete or corrupt `service_invitations` rows for maintainers who were confirmed, current FOSSA team members.
The removal loop only skipped rows with `Status == "accepted"`. A row whose `Status` had been independently clobbered to `"error"` by a transient `FetchUserInvitations` failure (e.g. a 502) — even though `TeamAssignmentStatus == "done"` still correctly showed the person as a real FOSSA team member — got soft-deleted on the next staff-triggered refresh. Once soft-deleted, `ListServiceInvitationsByStatus` never surfaces the row again, so the poller could never self-heal it; only a manual refresh (which caused the bug) or direct DB remediation could fix it.
## How this was found
This was first noticed as a one-off oddity while onboarding the Tekton project onto FOSSA — a confirmed team member's invitation kept showing as stale/missing despite being a real, current member. It was examined in much more depth during the post-kcp-decommissioning cleanup work, where a broader cross-check of `service_invitations` against FOSSA UI/audit-log ground truth across several projects (Cedar, Flux, Kubevirt, Serverless Devs, Tekton, Velero) surfaced the same pattern repeatedly, which led to tracing it back to this guard.
## Fix
Widened the guard to also skip any row with `TeamAssignmentStatus == "done"`, since that field is only ever set at confirmed-membership points in the `fossa-poller` state machine and, unlike `Status`, is not touched by transient API-error branches:
```go
if invite.Status == "accepted" || (invite.TeamAssignmentStatus != nil && *invite.TeamAssignmentStatus == "done") {
continue
}
```
Two regression tests were added covering both the pre-existing accepted-row case and the previously-uncovered status-clobbered-but-team-assignment-done case.
## Data remediation
11 production rows across Cedar, Flux, Kubevirt, Serverless Devs, Tekton, and Velero had already been wrongly soft-deleted or clobbered by this bug. These were restored via a hand-curated, FOSSA-verified remediation script (`scripts/restore-stranded-service-invitations.sql`), which is dry-run by default with several preflight safety guards. It has already been run against production; each restored row has a corresponding `FOSSA_INVITATION_MANUAL_REMEDIATION` audit log entry.
## Branch
`fix-fossa-invite-refresh-guard` (off `main`)
Contributor guide
No contributing guide indexed for this repository
Research direction
Start at handleFossaInviteRefresh for POST /api/services/fossa/invites/refresh and review the two regression tests described in the issue. Check the fix branch and confirm the refresh preserves confirmed members even when Status is error; completion is demonstrated by both regression cases passing.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- api, backend, databases
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 20/100