Skip to content

Pushover, Telegram and Pushbullet webhook URLs are logged with their credential intact #982

Description

@m4bard

What happens

NotificationService.Webhooks.cs logs every outbound provider request at Information level, and it
redacts the URL with LogRedaction.RedactText(webhookUrl, LogRedaction.GetSensitiveValuesFromEnvironment()):

var redactedUrl = LogRedaction.RedactText(webhookUrl, LogRedaction.GetSensitiveValuesFromEnvironment());
_logger.LogInformation("Sending Pushover POST to {WebhookUrl} with body: {Body}", redactedUrl, redactedRequestBody);

RedactText replaces occurrences of values it finds in the process environment. A webhook URL the
user typed into Settings is not one of those, so nothing is replaced and the URL is written out
whole. The same call shape appears on the NTFY, Pushover, Telegram, Pushbullet, Slack, Discord and
generic branches.
Those URLs are the credential:

  • Pushover: https://api.pushover.net/1/messages.json?token=<app token>&user=<user key>
  • Telegram: https://api.telegram.org/bot<bot token>/sendMessage?chat_id=...
  • Pushbullet: https://api.pushbullet.com/v2/pushes?token=<access token>
  • Discord and Slack: the webhook path itself is the bearer secret
    The request bodies go through NotificationDiagnostics.AggressiveRedact, which is also
    environment-sourced only, so the Pushover body's token= and user= fields are logged as well.
    The codebase already has the right helper. LogRedaction.SanitizeUrl drops userinfo and the whole
    query string, and the same file uses it two lines away on the failure path:
_logger.LogWarning("Pushover webhook URL missing 'token' or 'user' query parameter: {WebhookUrl}", LogRedaction.SanitizeUrl(webhookUrl));

So within one branch the warning is sanitised and the success line is not.

Why it matters

Information is the default level, so this is not a debug-only path. Anyone the operator sends a log
bundle to gets a working push credential for the operator's own devices, and container logs are
routinely shipped somewhere central.

Suggested shape

SanitizeUrl alone is not sufficient, because Telegram and Discord carry the secret in the path
rather than the query. A LogRedaction.SanitizeWebhookUrl that keeps scheme, host and port, drops
userinfo and query, and masks path segments for the known providers would cover all of them:

  • api.telegram.org/bot<token>/... becomes api.telegram.org/bot<redacted>/sendMessage
  • discord.com/api/webhooks/<id>/<token> keeps webhooks and masks the two segments after it
  • hooks.slack.com/services/... masks everything after services
  • everything else keeps the path and loses the query
    Then use it for the {WebhookUrl} argument on every logging call in
    NotificationService.Webhooks.cs and in NotificationDiagnostics.LogFailedResponseAsync, and drop
    the Pushover form body from the Information line rather than trying to redact it.

Sequencing note

NotificationService.Webhooks.cs already has two open pull requests against it, #754 and #943, so a
change here wants to land after those rather than beside them.
Disclosure: drafted with Claude Code at my direction; I read the cited code at the stated commit and reviewed this before posting.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions