Set up integrations using webhooks
Webhooks enable real-time communication between different systems or applications over the internet. They allow one application to send data to another application as soon as a specific event or a trigger occurs.
Use webhooks to integrate Endor Labs with applications such as Slack, Microsoft Teams or more, and instantly get notified about projects if your configured policies are violated.
When events are triggered, Endor Labs sends HTTP POST requests to URLs of your configured events, with all the information you need.
Configure a webhook integration
Set up a custom integration with Endor Labs webhooks.
- Sign in to Endor Labs and click Integrations from the sidebar.
- Navigate to Webhooks under Notifications and click Add.
- Click Add Notification Integration.
- Enter a name and description for this integration.
- Enter the URL endpoint for the webhooks.
- Enter the authentication method such as API Key, Basic, or None.
- Enter the details for the authentication method such as USERNAME, PASSWORD, or API KEY. Make sure the API Key has required permissions to post messages using webhook.
- If you want to ensure integrity, de-select Disable HMAC Integration Check and enter the HMAC Shared Key. The Hash-Based Message Authentication Code (HMAC) ensures the authenticity of a message using a cryptographic hash function and a secret key. The HMAC signature is passed as a header in the HTTP request.
- Click Add Notification Integration.
Endor Labs webhook payload
Endor Labs provides the following webhook payload, that you can customize for your needs.
Name | Description |
---|---|
data.message |
Brief message about the number of findings discovered for a project |
data.project_url |
Link to the scanned project in the Endor Labs application |
data.policy.name |
Name of the violated policy that triggered the notification |
data.policy.url |
Link to the violated policy in the Endor Labs application |
data.findings |
Complete list of findings |
data.findings[].uuid |
Unique identifier of the finding |
data.findings[].description |
Brief description of the finding |
data.findings[].severity |
Severity of the finding |
data.findings[].dependency [CONDITIONAL] |
Name of dependency that caused the policy violation. This field is only present for findings that have a dependency associated. For example, vulnerability findings |
data.findings[].package [CONDITIONAL] |
The version of the package in the project that imported the dependency causing the policy violation. This field is only present for findings that have a package version associated with them. For example, vulnerability findings |
data.findings[].repositoryVersion [CONDITIONAL] |
Repository version of the project that triggered the policy violation. This field is only present for findings that have a repository version associated with them. For example, secrets findings |
data.findings[].findingURL |
Link to the finding in the Endor Labs application |
Example:
See the following example for a sample notification payload.
{
"data": {
"message": "6 findings discovered for project endorlabs/monorepo",
"projectURL": "https://localhost:8082/t/endor/projects/65e5b83466145505541d9664",
"policy": {
"name": "Webhook vuln",
"url": "https://localhost:8082/t/endor/policies/actions?filter.default=Webhook+vuln"
},
"findings": [
{
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"description": "GHSA-c2qf-rxjj-qqgw: semver vulnerable to Regular Expression Denial of Service",
"severity": "FINDING_LEVEL_MEDIUM",
"dependency": "semver@7.5.0",
"package": "endorlabs-vscode-extension@1.5.0",
"findingURL": "https://localhost:8082/t/endor/findings/6614ec9141aef3ab8e90ed80"
},
{
"uuid": "550e8400-e29b-41d4-a716-446655440001",
"description": "GHSA-c2qf-rxjj-qqgw: semver vulnerable to Regular Expression Denial of Service",
"severity": "FINDING_LEVEL_MEDIUM",
"dependency": "semver@7.3.8",
"package": "endorlabs-vscode-extension@1.5.0",
"findingURL": "https://localhost:8082/t/endor/findings/6614ec9141aef3ab8e90ed81"
},
{
"uuid": "550e8400-e29b-41d4-a716-446655440002",
"description": "GHSA-c2qf-rxjj-qqgw: semver vulnerable to Regular Expression Denial of Service",
"severity": "FINDING_LEVEL_MEDIUM",
"dependency": "semver@5.7.1",
"package": "endorlabs-vscode-extension@1.5.0",
"findingURL": "https://localhost:8082/t/endor/findings/6614ec9141aef3ab8e90ed82"
},
{
"uuid": "550e8400-e29b-41d4-a716-446655440003",
"description": "GHSA-c2qf-rxjj-qqgw: semver vulnerable to Regular Expression Denial of Service",
"severity": "FINDING_LEVEL_MEDIUM",
"dependency": "semver@6.3.0",
"package": "endorlabs-vscode-extension@1.5.0",
"findingURL": "https://localhost:8082/t/endor/findings/6614ec9141aef3ab8e90ed83"
}
]
}
}
Use Endor Labs webhooks to integrate with Slack
If you use Slack as a collaborative tool, integrate Slack channels using webhooks in Endor Labs to publish notifications as messages in the respective channels.
- Create incoming webhooks in Slack
- Configure a webhook integration
- Create a webhook handler to post Slack notifications
Create incoming webhooks in Slack
Create an incoming webhook to your Slack channel to enable Endor Labs to post notifications in the channel. The webhook provides a unique URL which is used to integrate the channel in Endor Labs. To send messages into Slack using incoming webhooks, see Slack Integration.
If you have already created an incoming webhook in the channel, copy the unique URL and integrate the channel in Endor Labs.
Webhook handler example for Slack
Create a webhook handler or a cloud function to receive webhook requests generated by Endor Labs, authorize the request, and post messages to your Slack channel.
See the following code sample hosted as a cloud function or a webhook handler.
// Package p contains an HTTP Cloud Function.
package p
import (
"encoding/json"
"fmt"
"html"
"io"
"io/ioutil"
"bytes"
"log"
"net/http"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"strings"
wrapperspb "google.golang.org/protobuf/types/known/wrapperspb"
//"github.com/golang/protobuf/proto"
)
// Struct representation of default webhook payload from Endor Lab's notification.
type WebhookMessage {
Data Payload `json:"data"`
}
type Payload struct {
Message string `json:"message"`
ProjectUrl string `json:"projectURL"`
Policy Policy `json:"policy"`
Findings []Finding `json:"findings"`
}
type Finding struct {
Uuid string `json:"uuid"`
Description string `json:"description"`
Severity string `json:"severity"`
Dependency string `json:"dependency,omitempty"`
Package string `json:"package,omitempty"`
RepositoryVersion string `json:"repositoryVersion,omitempty"`
FindingUrl string `json:"findingURL"`
}
type Policy struct {
Name string `json:"name"`
Url string `json:"url"`
}
// HelloWorld deserializes the default webhook payload from the notification,
// formats it into a format that Slack supports and send the message to Slack via webhook.
func HelloWorld(w http.ResponseWriter, r *http.Request) {
var d WebhookMessage
if err := json.NewDecoder(r.Body).Decode(&d); err != nil {
switch err {
case io.EOF:
log.Printf("succcess")
return
default:
log.Printf("json.NewDecoder: %v", err)
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
}
log.Printf("%s", d.Data.Message)
// Perform the HMAC sign to make sure that the request is not tampered with.
hmacSign := ""
for headerName, headerValues := range r.Header {
if headerName == "X-Endor-Hmac-Signature" {
if headerValues[0] == "" {
http.Error(w, "hmac empty", http.StatusUnauthorized)
return
}
hmacSign = headerValues[0]
log.Printf("hmac sign %s", hmacSign)
}
}
receivedMessage := d.Message
// Secret configured in Endor
secretKey := "Secret"
// Validate the HMAC
isValid := validateHMAC(receivedMessage, hmacSign, secretKey)
// Process the result
if isValid {
fmt.Fprint(w, html.EscapeString("success"))
} else {
http.Error(w, "unauthorized, something changed", http.StatusUnauthorized)
return
}
textToSlack := fmt.Sprintf("%s which violates policy %s", d.Data.Message, d.Data.Policy.Name)
sendMessageToSlack(textToSlack)
}
func validateHMAC(receivedMessage, receivedHMAC, secretKey string) bool {
// Create a new HMAC hasher using the SHA-256 hash function and the secret key
mac := hmac.New(sha256.New, []byte(secretKey))
// Write the received message to the HMAC hasher
mac.Write([]byte(receivedMessage))
// Calculate the HMAC value
expectedHMAC := mac.Sum(nil)
// Convert the expected HMAC to a hexadecimal string
expectedHMACString := hex.EncodeToString(expectedHMAC)
// Compare the expected HMAC with the received HMAC (ignoring case)
return strings.EqualFold(receivedHMAC, expectedHMACString)
}
func sendMessageToSlack(msg string) {
// Replace this url with the url hook from the Slack App
url := "https://slack.webhook"
payload := []byte(`{"text": "Hey there are findings in project https://github.com/endorlabs/python-deps.git which violates policy DemoNotification"}`)
req, err := http.NewRequest("POST", url, bytes.NewBuffer(payload))
if err != nil {
fmt.Println("Error creating request:", err)
return
}
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error sending request:", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading response body:", err)
return
}
}
Feedback
Was this page helpful? Send your feedback to support@endor.ai