mirror of
https://github.com/adnanh/webhook.git
synced 2025-05-12 00:24:45 +00:00
Add support for validating MS Teams outgoing webhooks
This commit is contained in:
parent
390e3bd772
commit
43a10ec3c2
2 changed files with 111 additions and 10 deletions
|
@ -234,6 +234,39 @@ func CheckPayloadSignature512(payload []byte, secret, signature string) (string,
|
|||
return ValidateMAC(payload, hmac.New(sha512.New, []byte(secret)), signatures)
|
||||
}
|
||||
|
||||
func CheckMSTeamsSignature(r *Request, signingKey string) (bool, error) {
|
||||
if r.Headers == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Check if the signing key is valid
|
||||
if signingKey == "" {
|
||||
return false, errors.New("signature validation key can not be empty")
|
||||
}
|
||||
secret, err := base64.StdEncoding.DecodeString(signingKey)
|
||||
if err != nil {
|
||||
return false, errors.New("signature validation key must be valid base64")
|
||||
}
|
||||
// Check if a valid HMAC header was provided
|
||||
if _, ok := r.Headers["Authorization"]; !ok {
|
||||
return false, nil
|
||||
}
|
||||
headerParts := strings.SplitN(r.Headers["Authorization"].(string), " ", 2)
|
||||
if len(headerParts) != 2 || headerParts[0] != "HMAC" {
|
||||
return false, errors.New("malformed 'Authorization' header")
|
||||
}
|
||||
providedSignature := headerParts[1]
|
||||
|
||||
mac := hmac.New(sha256.New, secret)
|
||||
mac.Write(r.Body)
|
||||
expectedSignature := base64.StdEncoding.EncodeToString(mac.Sum(nil))
|
||||
if !hmac.Equal([]byte(providedSignature), []byte(expectedSignature)) {
|
||||
return false, &SignatureError{Signature: providedSignature}
|
||||
}
|
||||
return true, nil
|
||||
|
||||
}
|
||||
|
||||
func CheckScalrSignature(r *Request, signingKey string, checkDate bool) (bool, error) {
|
||||
if r.Headers == nil {
|
||||
return false, nil
|
||||
|
@ -896,16 +929,17 @@ type MatchRule struct {
|
|||
|
||||
// Constants for the MatchRule type
|
||||
const (
|
||||
MatchValue string = "value"
|
||||
MatchRegex string = "regex"
|
||||
MatchHMACSHA1 string = "payload-hmac-sha1"
|
||||
MatchHMACSHA256 string = "payload-hmac-sha256"
|
||||
MatchHMACSHA512 string = "payload-hmac-sha512"
|
||||
MatchHashSHA1 string = "payload-hash-sha1"
|
||||
MatchHashSHA256 string = "payload-hash-sha256"
|
||||
MatchHashSHA512 string = "payload-hash-sha512"
|
||||
IPWhitelist string = "ip-whitelist"
|
||||
ScalrSignature string = "scalr-signature"
|
||||
MatchValue string = "value"
|
||||
MatchRegex string = "regex"
|
||||
MatchHMACSHA1 string = "payload-hmac-sha1"
|
||||
MatchHMACSHA256 string = "payload-hmac-sha256"
|
||||
MatchHMACSHA512 string = "payload-hmac-sha512"
|
||||
MatchHashSHA1 string = "payload-hash-sha1"
|
||||
MatchHashSHA256 string = "payload-hash-sha256"
|
||||
MatchHashSHA512 string = "payload-hash-sha512"
|
||||
IPWhitelist string = "ip-whitelist"
|
||||
ScalrSignature string = "scalr-signature"
|
||||
MSTeamsSignature string = "msteams-signature"
|
||||
)
|
||||
|
||||
// Evaluate MatchRule will return based on the type
|
||||
|
@ -916,6 +950,9 @@ func (r MatchRule) Evaluate(req *Request) (bool, error) {
|
|||
if r.Type == ScalrSignature {
|
||||
return CheckScalrSignature(req, r.Secret, true)
|
||||
}
|
||||
if r.Type == MSTeamsSignature {
|
||||
return CheckMSTeamsSignature(req, r.Secret)
|
||||
}
|
||||
|
||||
arg, err := r.Parameter.Get(req)
|
||||
if err == nil {
|
||||
|
|
|
@ -192,6 +192,70 @@ func TestCheckScalrSignature(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
var checkMSTeamsSignatureTests = []struct {
|
||||
description string
|
||||
headers map[string]interface{}
|
||||
body []byte
|
||||
secret string
|
||||
expectedSignature string
|
||||
ok bool
|
||||
}{
|
||||
{
|
||||
"Valid signature",
|
||||
map[string]interface{}{"Authorization": "HMAC gpjdTlOlaReTBLRFdwqdXhLqG7hFXVYTBorGDpaW5UE="},
|
||||
[]byte(`{"a": "b"}`), "bmV2ZXJnb25uYWdpdmV5b3V1cA==",
|
||||
"gpjdTlOlaReTBLRFdwqdXhLqG7hFXVYTBorGDpaW5UE=", true,
|
||||
},
|
||||
{
|
||||
"Wrong signature",
|
||||
map[string]interface{}{"Authorization": "HMAC 1337TlOlaReTBLRFdwqdXhLqG7hFXVYTBorGDpaW5UE="},
|
||||
[]byte(`{"a": "b"}`), "bmV2ZXJnb25uYWdpdmV5b3V1cA==",
|
||||
"gpjdTlOlaReTBLRFdwqdXhLqG7hFXVYTBorGDpaW5UE=", false,
|
||||
},
|
||||
{
|
||||
"Missing Authorization header",
|
||||
map[string]interface{}{"Different-Header": "HMAC wrong"},
|
||||
[]byte(`{"a": "b"}`), "bmV2ZXJnb25uYWdpdmV5b3V1cA==",
|
||||
"gpjdTlOlaReTBLRFdwqdXhLqG7hFXVYTBorGDpaW5UE=", false,
|
||||
},
|
||||
{
|
||||
"Malformed Authorization header",
|
||||
map[string]interface{}{"Authorization": "HMAC 123---"},
|
||||
[]byte(`{"a": "b"}`), "bmV2ZXJnb25uYWdpdmV5b3V1cA==",
|
||||
"gpjdTlOlaReTBLRFdwqdXhLqG7hFXVYTBorGDpaW5UE=", false,
|
||||
},
|
||||
{
|
||||
"Missing signing key",
|
||||
map[string]interface{}{"Authorization": "HMAC gpjdTlOlaReTBLRFdwqdXhLqG7hFXVYTBorGDpaW5UE="},
|
||||
[]byte(`{"a": "b"}`), "",
|
||||
"gpjdTlOlaReTBLRFdwqdXhLqG7hFXVYTBorGDpaW5UE=", false,
|
||||
},
|
||||
{
|
||||
"Malformed signing key",
|
||||
map[string]interface{}{"Authorization": "HMAC gpjdTlOlaReTBLRFdwqdXhLqG7hFXVYTBorGDpaW5UE="},
|
||||
[]byte(`{"a": "b"}`), "---2ZXJnb25uYWdpdmV5b3V1cA==",
|
||||
"gpjdTlOlaReTBLRFdwqdXhLqG7hFXVYTBorGDpaW5UE=", false,
|
||||
},
|
||||
}
|
||||
|
||||
func TestCheckMSTeamsSignature(t *testing.T) {
|
||||
for _, testCase := range checkMSTeamsSignatureTests {
|
||||
r := &Request{
|
||||
Headers: testCase.headers,
|
||||
Body: testCase.body,
|
||||
}
|
||||
valid, err := CheckMSTeamsSignature(r, testCase.secret)
|
||||
if valid != testCase.ok {
|
||||
t.Errorf("failed to check MS Teams signature fot test case: %s\nexpected ok:%#v, got ok:%#v}",
|
||||
testCase.description, testCase.ok, valid)
|
||||
}
|
||||
|
||||
if err != nil && testCase.secret != "" && strings.Contains(err.Error(), testCase.expectedSignature) {
|
||||
t.Errorf("error message should not disclose expected mac: %s on test case %s", err, testCase.description)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var checkIPWhitelistTests = []struct {
|
||||
addr string
|
||||
ipRange string
|
||||
|
|
Loading…
Add table
Reference in a new issue