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)
}
}
// This example uses express.js framework. See https://expressjs.com
const express = require('express');
const app = express();
const port = 3000;
// Define the sec3 WatchTower secret
// In the actual server, the secret should be put in the environmental variables
const SEC3_WATCHTOWER_SECRET = "your_secret";
// Verify the incoming request. If the secret is empty or does not match
// with the one you set, return 401 status code
app.use((req, res, next) => {
const incomingSecret = req.header("X-Sec3-Watchtower-Secret");
if (incomingSecret != SEC3_WATCHTOWER_SECRET) {
res.sendStatus(401);
return;
}
next();
})
app.use(express.json());
app.post('/sec3_webhook', (req, res) => {
const alert = req.body;
// Do something with the alert...
console.log(alert);
res.sendStatus(200);
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
Last updated