AWS Alert Handler
Normalize AWS alerts into a consistent JSON shape. Works standalone or as
the first step of an incident-triage
workflow.
Scope
Parses Security Hub (ASFF), Inspector v2 (raw API or EventBridge envelope),
and CloudWatch alarm state changes (EventBridge or SNS-direct). Any of these
wrapped in an SNS Notification envelope is unwrapped automatically.
Does not parse custom Slack messages, AWS Chatbot output, or Cost Anomaly findings. Format details and out-of-scope guidance: references/alert-formats.md.
Usage
Pipe any alert JSON into the unified entry point:
cat alert.json | scripts/parse-alert.sh
echo "$ALERT_JSON" | scripts/parse-alert.sh
parse-alert.sh auto-detects the format, unwraps SNS envelopes, and
dispatches to the right format-specific parser.
For webhook-based delivery (SNS → OpenClaw /hooks/aws-alert) see
references/webhook-setup.md.
Normalized output shape
Conforms to the shared normalized-alert-v0
interface. Core fields:
source—security-hub | inspector2 | cloudwatch-alarmsource_subtype— format-specific (e.g.guardduty,package-vulnerability)severity—CRITICAL | HIGH | MEDIUM | LOW | INFORMATIONALstate—ACTIVE | ALARM | OK | INSUFFICIENT_DATA | RESOLVEDaccount_id,region,resource_ids[],title,summary,detected_at,console_urlraw— the original payload, preserved
Inspector v2 findings additionally carry inspector_score (CVSS 0.0–10.0)
when present. Full schema, examples, severity-mapping rules, and the
CloudWatch noise-filter list: references/severity-mapping.md
and the interface contract.
Exit codes
parse-alert.sh exits:
0— normal parse success; stdout is the normalized JSON2— payload didn't match any known format; stderr lists accepted shapes3— missingjq4— SNSSubscriptionConfirmation(not an alert); stderr includes theSubscribeURL10— non-incident alarm filtered out; stdout is the skip sentinel (see below)
Skip sentinel
The CloudWatch parser emits a skip sentinel instead of a normalized
payload for known non-incident alarms (ECS TargetTracking autoscaling,
Elastic Beanstalk default alarms, EC2 AutoScaling policies, Synthetics
canary state changes). When this happens, parse-alert.sh exits 10 and
stdout is:
{ "skip": "non-incident-alarm", "reason": "...", "alarm_name": "..." }
On exit 10, drop the event: do not invoke downstream handling, do not post to chat.
Optional: hand off to incident-triage
If incident-triage is installed and the alert warrants triage, invoke
it and pass the full normalized payload — the shape includes everything
triage needs to classify, scope, correlate, investigate, and advise.
Works well with
ggettert/incident-triage— recommended downstream for full triage workflow.github— used byincident-triageduring the Correlate step.
Security
- Parser scripts run locally; no external API calls.
- The
rawfield preserves the full original payload. If alerts may contain sensitive data, striprawbefore posting anywhere visible. - For webhook-delivered alerts: OpenClaw's bearer-token auth protects the endpoint, but SNS does not send your bearer token by default. See references/webhook-setup.md for spoofing risk and mitigation patterns.