security

package
v0.22.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 7, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package security holds notifycat's inbound-request authentication: signature verifiers for the signed webhooks it receives. It exposes a provider-agnostic SignatureVerifier port and the GitHub and Bitbucket raw-body HMAC-SHA256 adapters (identical scheme; they differ only in the header the middleware reads).

Index

Constants

View Source
const (
	SignatureHeader          = "X-Hub-Signature-256"
	SignatureHeaderBitbucket = "X-Hub-Signature"
)

SignatureHeader is the HTTP header GitHub uses to carry the HMAC-SHA256 digest of the raw request body. SignatureHeaderBitbucket is Bitbucket's equivalent — same "sha256=<hex>" scheme, different header name.

View Source
const SlackDefaultMaxAge = 5 * time.Minute

SlackDefaultMaxAge is Slack's documented replay window: requests whose timestamp is more than five minutes from now (in either direction, to absorb clock skew) are rejected before the HMAC is checked.

View Source
const SlackSignatureHeader = "X-Slack-Signature"

SlackSignatureHeader carries Slack's "v0=<hex>" request signature.

View Source
const SlackTimestampHeader = "X-Slack-Request-Timestamp"

SlackTimestampHeader carries the Unix-seconds timestamp Slack signed into the base string. It is part of the signed payload, so trusting it for replay protection is safe once the signature checks out.

Variables

View Source
var ErrInvalidSignature = errors.New("security: invalid signature")

ErrInvalidSignature is returned when a signature does not match.

View Source
var ErrStaleTimestamp = errors.New("security: stale timestamp")

ErrStaleTimestamp is returned when the request timestamp falls outside the replay window. Kept distinct from ErrInvalidSignature so callers and tests can tell a replayed (or badly clock-skewed) request from a forged one, even though both map to 401 at the HTTP layer.

Functions

func Sign

func Sign(secret string, body []byte) string

Sign returns the "sha256=<hex>" HMAC of body under secret — the value GitHub puts in X-Hub-Signature-256. It is the inverse of Verify and shares the same scheme, so anything Sign produces, Verify accepts. Used by the smoke command to forge a correctly-signed request against the live endpoint.

Types

type BitbucketVerifier added in v0.22.0

type BitbucketVerifier struct {
	// contains filtered or unexported fields
}

BitbucketVerifier checks Bitbucket's HMAC-SHA256 signatures against a shared secret. The scheme is identical to GitHub's ("sha256=<hex>" over the raw body); the two differ only in the header the middleware reads (SignatureHeaderBitbucket vs SignatureHeader), so both delegate to the same verification core.

func NewBitbucketVerifier added in v0.22.0

func NewBitbucketVerifier(secret string) *BitbucketVerifier

NewBitbucketVerifier returns a BitbucketVerifier configured with the shared secret configured on the Bitbucket webhook (BITBUCKET_WEBHOOK_SECRET).

func (*BitbucketVerifier) Verify added in v0.22.0

func (v *BitbucketVerifier) Verify(body []byte, signature string) error

Verify checks that signature is a valid "sha256=<hex>" HMAC of body using the verifier's secret. Returns ErrInvalidSignature for any mismatch; the comparison runs in constant time.

type GitHubVerifier

type GitHubVerifier struct {
	// contains filtered or unexported fields
}

GitHubVerifier checks GitHub's HMAC-SHA256 signatures against a shared secret.

func NewGitHubVerifier

func NewGitHubVerifier(secret string) *GitHubVerifier

NewGitHubVerifier returns a GitHubVerifier configured with the shared secret.

func (*GitHubVerifier) Verify

func (v *GitHubVerifier) Verify(body []byte, signature string) error

Verify checks that signature is a valid "sha256=<hex>" HMAC of body using the verifier's secret. Returns ErrInvalidSignature for any mismatch. The comparison runs in constant time to prevent timing oracles.

type SignatureVerifier

type SignatureVerifier interface {
	Verify(body []byte, signature string) error
}

SignatureVerifier verifies a signed request body against its signature header.

type SlackOption

type SlackOption func(*SlackVerifier)

SlackOption configures a SlackVerifier.

func WithSlackClock

func WithSlackClock(now func() time.Time) SlackOption

WithSlackClock overrides the time source used for replay-window checks. Tests inject a fixed clock; production uses time.Now.

func WithSlackMaxAge

func WithSlackMaxAge(d time.Duration) SlackOption

WithSlackMaxAge overrides the replay window. Defaults to SlackDefaultMaxAge.

type SlackVerifier

type SlackVerifier struct {
	// contains filtered or unexported fields
}

SlackVerifier checks Slack request signatures against a shared signing secret and enforces the replay window.

To satisfy SignatureVerifier, Verify accepts a compound signature of the form "<unix-timestamp>\n<v0=hex>" — the slackhook middleware builds this string from the two Slack request headers before calling Verify.

func NewSlackVerifier

func NewSlackVerifier(secret string, opts ...SlackOption) *SlackVerifier

NewSlackVerifier returns a SlackVerifier configured with the given signing secret.

func (*SlackVerifier) Verify

func (v *SlackVerifier) Verify(body []byte, sig string) error

Verify checks that sig is a valid compound Slack signature of the form "<unix-timestamp>\n<v0=hex>". It checks that the timestamp is within the replay window and that the HMAC of Slack's base string ("v0:{timestamp}:{rawBody}") matches.

The staleness check runs first — the timestamp is part of the signed base string, so a forged-but-fresh timestamp still fails the HMAC. The HMAC comparison runs in constant time to prevent timing oracles.

Returns ErrStaleTimestamp for a timestamp outside the window and ErrInvalidSignature for any signature mismatch or malformed input.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL