cncf / cncf/maintainer-d

FOSSA invite refresh could soft-delete/clobber invitations for confirmed team members

Open
#144 0 comments 0 reactions 0 assignees View on GitHub
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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.