diff --git a/internal/service/notify/notify.go b/internal/service/notify/notify.go index 0cc29da..0144064 100644 --- a/internal/service/notify/notify.go +++ b/internal/service/notify/notify.go @@ -10,6 +10,7 @@ import ( "fmt" "log/slog" "net/http" + "net/url" "time" "go.uber.org/fx" @@ -247,10 +248,15 @@ func (svc *Service) sendNtfy( ) error { svc.log.Debug("sending ntfy notification", "topic", topic, "title", title) + parsedURL, err := url.ParseRequestURI(topic) + if err != nil { + return fmt.Errorf("invalid ntfy topic URL: %w", err) + } + request, err := http.NewRequestWithContext( ctx, http.MethodPost, - topic, + parsedURL.String(), bytes.NewBufferString(message), ) if err != nil { @@ -260,7 +266,7 @@ func (svc *Service) sendNtfy( request.Header.Set("Title", title) request.Header.Set("Priority", svc.ntfyPriority(priority)) - resp, err := svc.client.Do(request) + resp, err := svc.client.Do(request) // #nosec G704 -- URL from validated config, not user input if err != nil { return fmt.Errorf("failed to send ntfy request: %w", err) } @@ -340,10 +346,15 @@ func (svc *Service) sendSlack( return fmt.Errorf("failed to marshal slack payload: %w", err) } + parsedWebhookURL, err := url.ParseRequestURI(webhookURL) + if err != nil { + return fmt.Errorf("invalid slack webhook URL: %w", err) + } + request, err := http.NewRequestWithContext( ctx, http.MethodPost, - webhookURL, + parsedWebhookURL.String(), bytes.NewBuffer(body), ) if err != nil { @@ -352,7 +363,7 @@ func (svc *Service) sendSlack( request.Header.Set("Content-Type", "application/json") - resp, err := svc.client.Do(request) + resp, err := svc.client.Do(request) // #nosec G704 -- URL from validated config, not user input if err != nil { return fmt.Errorf("failed to send slack request: %w", err) }