go-notificationgo-notification
Features

Security & Redaction

API keys stay out of logs. Drivers use raw HTTP with no third-party SDKs.

Redaction is opt-in, not automatic

go-notification does not auto-redact via struct tags or a custom String(). If you log a Config, an error message, or a request URL that embeds credentials, the secret will appear in plain text. Wrap anything you log around the library through middleware.Redact / middleware.RedactAttr (see below). Never log a whole config struct.

go-notification is built around two security defaults:

  1. No secrets logged by the library itself — the notifier's own logging never prints your API keys. But anything you log around it is your responsibility — use the helper below.
  2. Zero third-party SDKs — every driver uses net/http directly.

Redaction helper

Redaction is a regex-based string scrubber in the middleware package — not a struct-tag or String() mechanism. It replaces bearer tokens, api_key=…, password=…, and token=… patterns with ***:

main.go
import "github.com/gopackx/go-notification/middleware"

safe := middleware.Redact(`Authorization: Bearer abc123 api_key=key-secret`)
// "Authorization: Bearer *** api_key=***"

For structured logging, RedactAttr returns a redacted slog.Attr:

main.go
slog.Info("calling provider", middleware.RedactAttr("url", requestURL))

There is no redact:"true" struct tag, no notification.Redact(cfg), and no redacting String() on config types. Don't log whole config structs with %+v — pass individual values through middleware.Redact instead.

Keeping secrets out of logs

The notifier's own logging never prints credentials. When you log around the library — request URLs, headers, error strings that may embed a key — run the value through middleware.Redact/RedactAttr first. That covers the common leak vectors (bearer tokens, api_key/token/password pairs).

What the library redacts for you

Since v1.0.1 every HTTP driver sanitises its own error values before they reach the logger or OnError:

  • Request URL — only the scheme and host survive (https://api.telegram.org). Path, query string and userinfo are dropped, so a Telegram bot token embedded in the path, or a Slack/Discord/Teams webhook URL, never appears in an error message.
  • Response body — the snippet of the provider's response that is embedded in the error passes through middleware.Redact.
  • Webhook URL parse errors — the webhook driver redacts the URL in parse failures too, since Go's url.Parse error echoes its input.
  • The notifier's own log lines (since v1.0.2) — the "notification delivery failed" and callback-panic logs pass the error text through middleware.Redact. This covers errors from custom channels too, which httpx never sees. Note that the err passed to OnError is not redacted.

So an OnError callback that logs err as-is is safe for the library's own errors:

main.go
OnError: func(ctx context.Context, n notification.Notifiable, channel string, err error) {
    // err reads like: telegram: http 401 https://api.telegram.org: {"ok":false,"description":"Unauthorized"}
    slog.Error("send failed", "channel", channel, "err", err)
},

This does not change the rule above. Anything you log — the config, the URL you built, a request you dumped — still has to go through middleware.Redact yourself.

Zero SDK dependencies

Every driver uses net/http. No Twilio Go SDK, no AWS SDK, no SendGrid client library.

Why it matters:

  • Tiny binary. Adding WhatsApp support doesn't pull in the Twilio SDK's ~10MB dependency tree.
  • Audit surface. You audit this library's raw HTTP calls. You don't transitively audit six SDK versions with their own CVE history.
  • Upgradeability. If an upstream API changes, fixing the driver is editing one file — no waiting for the SDK maintainer.

Trade-off: when upstream APIs add brand-new features (e.g., FCM's new batching API), this library has to catch up manually. That's explicit project scope.

Secrets in environment variables

A common pitfall: shell scripts that print env vars. Don't echo $FCM_SERVICE_ACCOUNT_JSON. Don't include API keys in git-committed .env.example files.

Recommended:

  • Use a secrets manager in production (AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault).
  • Load secrets at application boot, pass them as []byte where possible, and avoid roundtripping through the environment after boot.

TLS

Every HTTP driver talks HTTPS using Go's default transport and the system CA bundle; each builds its own client from the Timeout you configure. There is no HTTPClient config field to inject a custom CA or mutual-TLS client. If you need a custom CA or client certificates, the supported path is a network-level egress proxy or a custom channel (Writing a Custom Channel) where you control the http.Client.

Reporting a vulnerability

Please don't open a public issue for security problems. Report privately through GitHub's private vulnerability reporting: open the repository's Security tab and click Report a vulnerability, or go straight to github.com/gopackx/go-notification/security/advisories/new.

The full policy — supported versions, what to include in a report, and response expectations (acknowledgement within 72 hours, fix or mitigation plan within 14 days for confirmed issues) — lives in SECURITY.md in the repository.