Handle Alert via Webhook

sec3 provides a simple webhook for alert notification

Alert Object

WatchTower will send each alert as a JSON object to the configured webhook URL via a POST request.

Alert Object Example
{
    "id": "634cf5d77b42a046f68f5a5d",
    "botId": "634cf5d77b42a046f68f5a5e",
    "botName": "MyToken 500+",
    "projectId": "634cf5d77b42a046f68f5a5a",
    "projectName": "My Token",
    "userId": "634cf5d77b42a046f68f5a5f",
    "username": "John Doe",
    "template": "AbnormalTransferToken",
    "severity": "critical",
    "tx": ["6CfQwX7YwvA2bFRT8wfzZEtVEUbbrjUeMzBa9AbtThioHC6xfrfxSC3nCQWB5Y3BLV9cqadf69n9ApeBYhvwzNF1"],
    "detail": "The smart contract is involved in a transaction transferring 923750.55 Unknown Token. The token mint is 9Mu1Kaxbe2fehdDoeTJ5oD7XFQmEiZxzspEd3TZGkavx",
    "target": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
    "args": {"threshold": "500"},
    "isArchived": false,
    "created_at": "2022-12-06T17:10:21.672Z",
    "link": "https://pro.sec3.dev/alert/634cf5d77b42a046f68f5a5d"
}

Sample webhook handler code

package main

import (
	"log"
	"net/http"
	"encoding/json"
	"fmt"
	"time"
)

// Define the sec3 WatchTower secret
// In the actual server, the secret should be put in the environmental variables
const SEC3_WATCHTOWER_SECRET = "your_secret"

type Alert struct {
	ID          string            `json:"id"`
	BotId       string            `json:"botId"`
	UserId      string            `json:"userId"`
	ProjectId   string            `json:"projectId"`
	Username    string            `json:"username"`
	ProjectName string            `json:"projectName"`
	BotName     string            `json:"botName"`
	Template    string            `json:"template"`
	Severity    string            `json:"severity"`
	Tx          []string          `json:"tx"`
	Detail      string            `json:"detail"`
	Targets     []string          `json:"targets"`
	Args        map[string]string `json:"args"`
	IsArchived  bool              `json:"isArchived"`
	CreatedAt   time.Time         `json:"created_at"`
	Link        string            `json:"link"`
}

func sec3WebhookHandler(w http.ResponseWriter, r *http.Request) {
	// Verify the incoming request. If the secret is empty or does not match
	// with the one you set, return 401 status code
	incomingSecret := r.Header.Get("X-Sec3-Watchtower-Secret")
	if incomingSecret == "" || incomingSecret != SEC3_WATCHTOWER_SECRET {
		http.Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}
	
	// Declare a new Alert struct.
	var alert Alert

	// Try to decode the request body into the struct. If there is an error,
	// respond to the client with the error message and a 400 status code.
	err = json.NewDecoder(r.Body).Decode(&alert)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Do something with the alert...
	fmt.Printf("Alert:\n %+v\n", alert)
}

func main() {
	http.HandleFunc("/sec3_webhook", sec3WebhookHandler)
	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		log.Fatal(err)
	}
}

Last updated