76 lines
1.9 KiB
Go
76 lines
1.9 KiB
Go
package webhook
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
ErrMissingSignature = errors.New("missing X-Gitea-Signature header")
|
|
ErrInvalidSignature = errors.New("invalid webhook signature")
|
|
)
|
|
|
|
// Validator validates Gitea webhook signatures
|
|
type Validator struct {
|
|
secret []byte
|
|
}
|
|
|
|
// NewValidator creates a new webhook validator with the given secret
|
|
func NewValidator(secret string) *Validator {
|
|
return &Validator{
|
|
secret: []byte(secret),
|
|
}
|
|
}
|
|
|
|
// ValidateRequest validates the signature of an incoming webhook request
|
|
// It returns the request body if valid, or an error if validation fails
|
|
func (v *Validator) ValidateRequest(r *http.Request) ([]byte, error) {
|
|
// Read the body
|
|
body, err := io.ReadAll(r.Body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Get signature from header
|
|
signature := r.Header.Get("X-Gitea-Signature")
|
|
if signature == "" {
|
|
return nil, ErrMissingSignature
|
|
}
|
|
|
|
// Validate signature
|
|
if !v.validateSignature(body, signature) {
|
|
return nil, ErrInvalidSignature
|
|
}
|
|
|
|
return body, nil
|
|
}
|
|
|
|
// validateSignature checks if the HMAC-SHA256 signature matches
|
|
func (v *Validator) validateSignature(payload []byte, signature string) bool {
|
|
// Gitea sends the signature as a hex-encoded HMAC-SHA256
|
|
mac := hmac.New(sha256.New, v.secret)
|
|
mac.Write(payload)
|
|
expectedMAC := mac.Sum(nil)
|
|
expectedSignature := hex.EncodeToString(expectedMAC)
|
|
|
|
// Handle both with and without "sha256=" prefix
|
|
signature = strings.TrimPrefix(signature, "sha256=")
|
|
|
|
return hmac.Equal([]byte(signature), []byte(expectedSignature))
|
|
}
|
|
|
|
// GetEventType extracts the event type from the request headers
|
|
func GetEventType(r *http.Request) GiteaEventType {
|
|
return GiteaEventType(r.Header.Get("X-Gitea-Event"))
|
|
}
|
|
|
|
// GetDeliveryID extracts the unique delivery ID from the request headers
|
|
func GetDeliveryID(r *http.Request) string {
|
|
return r.Header.Get("X-Gitea-Delivery")
|
|
}
|