Security events
Security events confirm web application firewall (WAF) actions for the selected virtual host. The API returns events as JSON-encoded strings; decode each string to read the event fields.
Query scope
Section titled “Query scope”Query XCSH_NAMESPACE and the discovered XCSH_VIRTUAL_HOST over the 24-hour XCSH_START_24H–XCSH_END_TIME window. Select action="block" and at most three events; the current demo requires its WAF and rejection traffic.
Request procedure
Section titled “Request procedure”-
Build a virtual-host and block-action query.
Terminal window XCSH_EVENT_QUERY="$(jq -nr --arg vh "<XCSH_VIRTUAL_HOST>" \'"{vh_name=\"" + $vh + "\",action=\"block\"}"')"XCSH_REQUEST="$(jq -nc --arg ns "<XCSH_NAMESPACE>" --arg start "$XCSH_START_24H" \--arg end "$XCSH_END_TIME" --arg query "$XCSH_EVENT_QUERY" '{namespace: $ns, start_time: $start, end_time: $end,limit: 3, sort: "DESCENDING", query: $query}')" -
Query the events.
Terminal window curl --silent --show-error --fail-with-body --request POST \--header "Authorization: APIToken <XCSH_API_TOKEN>" \--header 'Content-Type: application/json' \--data-binary "$XCSH_REQUEST" \"<XCSH_API_URL>/api/data/namespaces/<XCSH_NAMESPACE>/app_security/events" |jq -er --arg ns "<XCSH_NAMESPACE>" --arg vh "<XCSH_VIRTUAL_HOST>" '{total_hits: (.total_hits | tonumber),returned: (.events | length),events: [.events[]? | (if type == "string" then fromjson else . end)| select(.namespace == $ns and .vh_name == $vh)| {time, namespace, vh_name, action, sec_event_type,sec_event_name, rsp_code, app_firewall_name}]}'
Observed response
Section titled “Observed response”Captured 2026-10-06T03:13:55Z; HTTP 200.
{ "total_hits": 135, "returned": 3, "events": [ { "time": "2026-10-06T03:12:02.413Z", "namespace": "<XCSH_NAMESPACE>", "vh_name": "<XCSH_VIRTUAL_HOST>", "action": "block", "sec_event_type": "waf_sec_event", "sec_event_name": "WAF", "rsp_code": "200", "app_firewall_name": "<XCSH_WAF_NAME>" }, { "time": "2026-10-06T03:07:02.531Z", "namespace": "<XCSH_NAMESPACE>", "vh_name": "<XCSH_VIRTUAL_HOST>", "action": "block", "sec_event_type": "waf_sec_event", "sec_event_name": "WAF", "rsp_code": "200", "app_firewall_name": "<XCSH_WAF_NAME>" }, { "time": "2026-10-06T03:02:03.054Z", "namespace": "<XCSH_NAMESPACE>", "vh_name": "<XCSH_VIRTUAL_HOST>", "action": "block", "sec_event_type": "waf_sec_event", "sec_event_name": "WAF", "rsp_code": "200", "app_firewall_name": "<XCSH_WAF_NAME>" } ]}Expect total_hits and matching events with action: "block" and an application firewall name. total_hits can exceed the first three returned events; use the scroll API for complete exports.
Empty security-event metric response
Section titled “Empty security-event metric response”Security-event metrics aggregate counters separately from the event-log API above. Select XCSH_NAMESPACE and XCSH_VIRTUAL_HOST over the fixed two-hour window, grouped by VH_NAME and SEC_EVENT_TYPE.
-
Build a namespace and virtual-host metrics selector for the fixed two-hour window.
Terminal window XCSH_REQUEST="$(jq -nc --arg ns "<XCSH_NAMESPACE>" --arg start "$XCSH_START_TIME" \--arg end "$XCSH_END_TIME" --arg vh "<XCSH_VIRTUAL_HOST>" '{namespace: $ns, start_time: $start, end_time: $end,step: "5m", group_by: ["VH_NAME", "SEC_EVENT_TYPE"],label_filter: [{label: "VH_NAME", op: "EQ", value: $vh}]}')" -
Query security-event metrics.
Terminal window curl --silent --show-error --fail-with-body --max-time 60 --request POST \--header "Authorization: APIToken <XCSH_API_TOKEN>" \--header 'Content-Type: application/json' \--data-binary "$XCSH_REQUEST" \"<XCSH_API_URL>/api/data/namespaces/<XCSH_NAMESPACE>/app_security/metrics" |jq -er '{data, step}'
Measured metrics response
Section titled “Measured metrics response”The namespace security-event metrics query at 2026-10-06T03:12:47Z returned HTTP 200 with no counters. Its two-hour selector used group_by: ["VH_NAME", "SEC_EVENT_TYPE"] and filtered VH_NAME to the discovered virtual host.
{ "data": [], "step": "5m"}This empty metric response does not contradict populated WAF counters or authoritative block-event logs. Treat it and the empty connection series as unavailable telemetry until meaningful points are captured.