Repository navigation
Delete personal Gitea repositories on creation again, behind API checks - #114
Merged
Merged
Conversation
Team code belongs in team organizations. Until 0c1c3bb, any repository whose provisioning failed was deleted, which is how personal repositories were kept out. That commit stopped deleting because the webhook was unauthenticated and the delete used the owner and name from the body, so a forged event could delete any repository. Since then people could create personal repositories, and those get no branch protection. Webhooks are now authenticated, and this brings the delete back for personal repositories only. The owner's kind comes from the API, since Gitea fills the body's `organization` with the owner even for personal repositories. The repository is deleted only when the API's copy has the event's id and owner, belongs to a user, and was created within 10 minutes; a replayed or forged event can't delete an older or different repository. Any failure, including not being able to tell whether the owner is an organization, logs and leaves the repository alone. Organization repositories go through provisioning exactly as before, and a failed provisioning still never deletes. Tests cover each guard with faked clients and complete webhook payloads. End to end on Gitea 1.24.3 (as in production): a new personal repository was deleted within seconds of creation, and a new team repository in the same run was made public and had main protected on its first push.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Brings back the deletion of personal Gitea repositories on creation, guarded so a forged or mismatched webhook can't delete anything. Replaces #113 (closed with #112, since we're staying on Gitea 1.24.3); this one is based directly on
master.Why it stopped
Until
0c1c3bb("authenticate Gitea webhooks and close two IDOR holes", Sept 5),handleRepoCreationdeleted any repository whose provisioning failed. A personal repo always fails (no Authentik team with that name), so personal repos were removed automatically. That commit replaced the delete with "leave untouched" because the endpoint was unauthenticated and the delete used the owner/name from the request body. Since then, personal repositories stay, without branch protection.What this does
On a
createdrepository event:GET /orgs/{owner}; only a 404 means "user", any other error leaves the repo alone). The body can't be used: Gitea fillsorganizationwith the owner even for personal repos.PERSONAL_REPO_DELETE_WINDOW_MS). A replayed or forged event can't delete an older or different repository. Every refusal is logged.Also adds
GiteaClient.isOrganizationand URL-encodesdeleteRepository's path. Webhook models androutes.tsare unchanged.Behaviour to be aware of
createdtoo, so they are deleted as well.createdevent (only aforkevent on the source repo), so personal forks are not affected by this change.Test plan
gitea-personal-repos.test.jswith faked clients and complete (strict-model) webhook payloads: fresh personal repo deleted once; org repo provisioned, never deleted; no delete on id mismatch, owner mismatch, repo older than the window, unparseablecreated_at, or when the org lookup errors; non-createdactions never deleteprettier@3.9.10 --check;nx buildDeleted personal repository ...logged); newTechOps/hook-mastermade public with default branchmain, and its first push created themainrule (2 approvals,PeoplePortalTeamOwners, no force push)