Alright, so we've been running Anomali ThreatStream for a while now, and while the UI is fine for analysts during business hours, the high-severity alerts just weren't getting the attention they needed fast enough, especially off-hours. The built-in notification options felt a bit clunky for our SRE/on-call workflow. I wanted those critical alerts to hit a dedicated Slack channel immediately, with enough context for whoever's on call to start triaging.
I ended up writing a simple Python webhook handler that sits between Anomali and Slack. The core idea is to use Anomali's ability to send alerts via HTTP POST (you can set this up in the Notification Profiles). My service filters for only high-severity stuff, formats a decent Slack message with key fields, and pushes it out. Here's the basic flow and the main chunk of the code.
First, you need to configure Anomali to send alerts to your webhook endpoint. In the Notification Profile, set the Webhook URL to your service's endpoint. The payload comes in as JSON. My service is a simple Flask app, but you could use anything. The key part is parsing Anomali's alert schema and filtering.
```python
from flask import Flask, request, jsonify
import requests
import os
app = Flask(__name__)
SLACK_WEBHOOK_URL = os.environ.get('SLACK_WEBHOOK_URL')
SEVERITY_THRESHOLD = 80 # Anomali's severity score, 80+ is "high"
@app.route('/webhook/anomali', methods=['POST'])
def anomali_webhook():
data = request.json
# Anomali's alert payload structure
alert = data.get('alert', {})
severity = alert.get('severity', 0)
if severity >= SEVERITY_THRESHOLD:
# Extract key info
alert_id = alert.get('id', 'N/A')
title = alert.get('title', 'No Title')
description = alert.get('description', '')[:200]
created = alert.get('created', '')
link = alert.get('link', '#')
slack_payload = {
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": f"🚨 Anomali High Severity Alert"
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": f"*Alert ID:*n{alert_id}"
},
{
"type": "mrkdwn",
"text": f"*Severity:*n{severity}"
}
]
},
{
"type": "section",
"text": {
"type": "plain_text",
"text": f"{title}nn{description}"
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Open in Anomali"
},
"url": link
}
]
}
]
}
# Post to Slack
requests.post(SLACK_WEBHOOK_URL, json=slack_payload)
return jsonify({"status": "forwarded"}), 200
else:
return jsonify({"status": "ignored, low severity"}), 200
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
```
Deployment-wise, I just threw this in a Docker container and deployed it as a Kubernetes Deployment behind a Service. Made sure to set the `SLACK_WEBHOOK_URL` as a Secret. The whole thing took maybe an afternoon to get right, including testing with some mocked alert data.
Some gotchas I ran into:
* Anomali's webhook payload structure isn't super well-documented; I had to inspect a few real alerts to get the field mapping right.
* You need to handle authentication on your webhook endpoint, either via a simple token check in the request header (which Anomali can send) or network-level firewalling.
* Make sure your Flask app is production-ready (use gunicorn, proper logging, etc.) – this example is just the bare bones.
Now the on-call gets a clean, actionable Slack message immediately for anything serious, and they can jump straight into the investigation. It's been running solid for a couple of months now. If you've built something similar, curious how you handled deduplication or added more context like related IoCs.
Automate everything. Twice.
This is a solid approach for getting those high-severity alerts to the right place fast. A quick note on filtering - are you handling any potential schema changes from ThreatStream's alert payloads over time? I've seen teams add a validation step to their webhook to log and ignore unrecognized formats, which helps with uptime if Anomali pushes an update.
Also, for the SRE triage, did you consider including a direct link back to the specific alert in the Anomali UI within the Slack message? That one-click access can save precious minutes when someone's responding at 3 AM.
Stay curious, stay critical.
Schema drift is an excellent point, and it's one of those things that will absolutely bite you during a minor vendor update when you're least prepared. A simple validation step is good, but I'd also recommend shipping those failed payloads to a dead-letter queue or a specific index in your observability stack. That way, you're not just silently ignoring them; you have a record to debug when your alert volume mysteriously drops by 30% next Tuesday.
Including a direct link is non-negotiable for any actionable alert. The time saved in context switching from Slack to hunting through a UI is measurable in mean time to acknowledge. If the Anomali API doesn't provide a ready-made URL, you can often construct it from the tenant domain and the alert ID they do provide. It's a few lines of code that pays for itself on the first 2 a.m. page.
P99 or bust.
Shipping failed payloads to a dead-letter queue is a smart move, but I'd emphasize making that queue itself observable. A simple counter for "schema_failures" on your monitoring dashboard can act as a canary. If you see it tick upward, you know to check the queue before your alerting completely degrades.
For constructing the direct link, that's definitely the way to go. Just be cautious about assuming the alert ID in the payload is always the one used in the UI permalink. In some systems, there's an internal UUID and a separate, shorter "display ID." It's worth a quick API test to verify the mapping.
Commit early, deploy often, but always rollback-ready.