From fcfc497a771d8eae15f31276ec2fb091769c9b56 Mon Sep 17 00:00:00 2001 From: bircni Date: Sun, 11 Oct 2026 21:57:52 +0200 Subject: [PATCH] fix(webhook): explain why webhook edit needs an authorization choice Gitea 28 drops a webhook's stored authorization header on every edit that does not send it again, and never returns it, so tea cannot leave it unchanged. The error offered --clear-authorization as an equal alternative, which wiped credentials users still needed. It now says the header is lost unless sent again, asks for it first, and reserves --clear-authorization for hooks without one. Signed-off-by: bircni --- internal/app/validation_test.go | 2 +- internal/app/webhooks.go | 4 ++-- internal/services/webhooks.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/app/validation_test.go b/internal/app/validation_test.go index f6dd61c9..b543817d 100644 --- a/internal/app/validation_test.go +++ b/internal/app/validation_test.go @@ -233,7 +233,7 @@ func TestUsageErrorsPrecedeRequests(t *testing.T) { {[]string{"webhook", "create", "https://host/path", "--authorization-header", "first\nsecond"}, "authorization header must be a single line"}, {[]string{"webhook", "create", "https://host/path", "--events", ""}, "--events needs at least one trigger"}, {[]string{"webhook", "create", "https://host/path", "--config", "secret=private"}, "--config cannot override the URL, secret, or webhook scope"}, - {[]string{"webhook", "edit", "1", "--active=false"}, "editing requires --authorization-header, --authorization-header-file, or --clear-authorization because Gitea replaces authorization"}, + {[]string{"webhook", "edit", "1", "--active=false"}, "editing a webhook drops its stored authorization header unless it is sent again, and Gitea never returns it: pass --authorization-header-file or --authorization-header (use --clear-authorization only for a hook without one)"}, {[]string{"webhook", "delete", "not-an-id"}, "invalid resource number \"not-an-id\""}, {[]string{"webhook", "list", "--org", "organization", "--account"}, "--org, --account, --global, and --repo select mutually exclusive scopes"}, {[]string{"webhook", "list", "--global", "-R", "owner/repo"}, "--org, --account, --global, and --repo select mutually exclusive scopes"}, diff --git a/internal/app/webhooks.go b/internal/app/webhooks.go index d0161aa5..633578a2 100644 --- a/internal/app/webhooks.go +++ b/internal/app/webhooks.go @@ -76,7 +76,7 @@ func (a *application) webhookCommand() *cobra.Command { command.Flags().StringVar(&filter, "branch-filter", "", "Branch glob for push events") command.Flags().StringVar(&authorization, "authorization-header", "", "Authorization value (prefer file input)") command.Flags().StringVar(&authorizationFile, "authorization-header-file", "", "Read authorization value from file or -") - command.Flags().BoolVar(&clearAuthorization, "clear-authorization", false, "Explicitly clear delivery authorization") + command.Flags().BoolVar(&clearAuthorization, "clear-authorization", false, "Send no authorization header; on edit, this removes the stored one") command.Flags().StringToStringVar(&config, "config", nil, "Adapter-specific configuration, for example channel=alerts") if verb == "create" { command.Flags().StringVar(&kind, "type", "gitea", "Webhook adapter type") @@ -116,7 +116,7 @@ func (a *application) webhookCommand() *cobra.Command { return clierr.Wrap(clierr.Usage, &services.CapabilityError{Feature: "webhook secret editing", Reason: "Gitea does not update signing secrets through its edit API; recreate the hook to rotate it"}) } if verb == "edit" && authSources == 0 { - return clierr.Wrap(clierr.Usage, errors.New("editing requires --authorization-header, --authorization-header-file, or --clear-authorization because Gitea replaces authorization")) + return clierr.Wrap(clierr.Usage, errors.New("editing a webhook drops its stored authorization header unless it is sent again, and Gitea never returns it: pass --authorization-header-file or --authorization-header (use --clear-authorization only for a hook without one)")) } if len(args) > 0 && verb == "create" { if cmd.Flags().Changed("url") { diff --git a/internal/services/webhooks.go b/internal/services/webhooks.go index 3986e6a3..ff5b03d9 100644 --- a/internal/services/webhooks.go +++ b/internal/services/webhooks.go @@ -145,7 +145,7 @@ func (s Webhooks) Edit(ctx context.Context, id int64, opts WebhookOptions) (Webh return Webhook{}, &CapabilityError{Feature: "webhook secret editing", Reason: "Gitea's edit API does not update the signing secret; recreate the hook to rotate it"} } if opts.Authorization == nil { - return Webhook{}, &CapabilityError{Feature: "webhook editing without an authorization choice", Reason: "Gitea replaces authorization and can hide its existing value; supply --authorization-header-file or --clear-authorization"} + return Webhook{}, &CapabilityError{Feature: "webhook editing without an authorization choice", Reason: "Gitea drops the stored authorization header on every edit and never returns it, so the caller must send it again or clear it"} } current, err := s.get(ctx, id) if err != nil {