Skip to content

Delete personal Gitea repositories on creation again, behind API checks - #114

Merged
IanCou merged 1 commit into
masterfrom
gitea-delete-personal-repos
Oct 28, 2026
Merged

IanCou merged 1 commit into
masterfrom
gitea-delete-personal-repos

Conversation

@IanCou

@IanCou IanCou commented Oct 28, 2026

Copy link
Copy Markdown
Collaborator

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), handleRepoCreation deleted 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 created repository event:

  1. Ask the API whether the owner is an organization (GET /orgs/{owner}; only a 404 means "user", any other error leaves the repo alone). The body can't be used: Gitea fills organization with the owner even for personal repos.
  2. Organizations: provisioning exactly as before. A failed provisioning still never deletes.
  3. Users: delete only if the API's copy of the repository has the event's id and owner and was created within 10 minutes (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.isOrganization and URL-encodes deleteRepository's path. Webhook models and routes.ts are unchanged.

Behaviour to be aware of

  • Applies to every user, including site admins, as the pre-September behaviour did. Mirrors and migrations into a personal namespace emit created too, so they are deleted as well.
  • Forks into a personal account do not emit a repository created event (only a fork event on the source repo), so personal forks are not affected by this change.
  • Only new creations are affected; existing personal repositories are not touched.

Test plan

  • gitea-personal-repos.test.js with 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, unparseable created_at, or when the org lookup errors; non-created actions never delete
  • Full server suite 136/136; prettier@3.9.10 --check; nx build
  • End to end on Gitea 1.24.3 (local dev stack + server): new personal repo deleted within seconds (Deleted personal repository ... logged); new TechOps/hook-master made public with default branch main, and its first push created the main rule (2 approvals, PeoplePortalTeamOwners, no force push)

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.
Copilot AI balanced review requested due to automatic review settings October 28, 2026 05:33

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

@IanCou
IanCou merged commit 19c24b9 into master Oct 28, 2026
3 checks passed
@IanCou
IanCou deleted the gitea-delete-personal-repos branch October 28, 2026 05:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants