# Enterprise Security Integration

> Whisper Splunk + Splunk ES: threat-intel KV Store collections, adaptive response actions, CIM aliases, graph macros, and worked detection examples.

*Source: https://www.whisper.security/docs/integrations/splunk/es-integration*
*Published: 2026-04-06*
*Last updated: 2026-09-25*

---
The Whisper Security Add-on is Splunk-ES-compatible but does not ship a
prebuilt correlation-search pack. What it gives ES is two Threat
Intelligence KV Store collections, an **Enrich with Whisper** adaptive
response action, CIM-compliant field aliases on `whisper:enrichment`
events, and graph-query macros you can call from your own detections.
This page covers what you switch on, and how to build a detection on
top of it.

## What ships

> **Read `coverage` before `band`.** Only `known-clean` licenses the word "clean"; `no-data` means
> *unknown*, which is a different thing again; `malicious-evidenced` and `ambiguous` mean there is
> evidence, whatever the band says.
> Full contract: [Coverage — what we looked at](/docs/whisper-graph/procedures/coverage).

### Threat Intelligence framework integration

The ES Threat Intelligence framework consumes KV Store collections that
match the ES `ip_intel` and `domain_intel` schemas. The add-on ships two,
`whisper_ip_intel` and `whisper_domain_intel`. Each is written either by a
populator search in `savedsearches.conf` or by the `whisper_threat_intel`
modular input, and the two writers key their records differently, so a
detection that filters on one will not see records written by the other.
Neither collection fills on its own in this release: both populator
searches are refused before they return rows, so the feeds do not appear
in ES.

To enable the populators, in Splunk:

1. Go to **Settings > Searches, Reports, and Alerts**.
2. Filter on **app = TA-whisper-graph**.
3. Edit **Whisper - Populate IP Threat Intel KV Store** and
   **Whisper - Populate Domain Threat Intel KV Store** -- set
   **Enabled = true** and pick a schedule (the default `0 */6 * * *`
   runs every 6 hours).
4. In ES, confirm the feeds appear under
   **Configure > Data Enrichment > Threat Intelligence Management**.

### Enrich with Whisper adaptive response action

`alert_actions.conf` ships the `[whisper_enrich]` custom alert action,
backed by `whisper_adaptive_response.py`. It lets an ES analyst, or a
correlation search, enrich an indicator on demand and write the result
back onto the notable event.

From a notable event:

1. Open the notable in **Incident Review**.
2. **Actions > Run Adaptive Response > Enrich with Whisper**.
3. The action pulls candidate indicators from `src`, `dest`, `src_dns`,
   and `dest_dns` on the notable, calls the WhisperGraph, and adds the
   enrichment result as a comment on the notable.

From a correlation search, attach `action.whisper_enrich = 1` to the
saved search to fire the enrichment automatically on each triggered
result.

### CIM field aliases on enrichment events

`props.conf` aliases Whisper-prefixed fields on `whisper:enrichment`
events to CIM field names, so notable events, RBA rules and ES dashboards
correlate without custom extractions. The events are tagged
`network resolution dns`, so they participate in the Network Resolution
and DNS CIM data models; `whisper:threat_intel` and `whisper:watchlist`
events are tagged `threat report` for Threat Intelligence. The alias
table is on [CIM Mapping](/docs/integrations/splunk/reference#cim-mapping).

### Templates and macros

`savedsearches.conf` ships four disabled enrichment pipeline templates
under the `Example - Whisper - ...` prefix. Clone one, replace its
`<source_index>`, `<source_sourcetype>`, `<destination_index>` and
`<indicator_field>` placeholders, and enable it. The templates are listed
on [Saved Searches](/docs/integrations/splunk/reference#saved-searches).

`macros.conf` ships nine `whisper_*` macros you can reuse inside
detections, dashboards or RBA rules to keep SPL short. They are query
helpers, not correlation-search thresholds, and every one is
parameterised -- the substitution flows into the `params` argument of
`whisperquery`, so nothing is interpolated into the Cypher query. Each
macro, with its arguments, is on
[Macros](/docs/integrations/splunk/reference#macros).

## Building your own detections

The recipe for a Whisper-powered ES detection: pick the index whose events
carry the indicator, clone one of the example templates, add Whisper
context with `whisperlookup` or a `whisper_*` macro, then put your
condition in a `where` clause on `whisper_risk_score`,
`whisper_threat_level`, one of the `whisper_is_*` flags, or a
graph-traversal result. Write the match to `index=risk` for RBA or to
`index=notable`. Schedule it disabled and tune it against historical data
before you enable it.

### Worked example -- multi-feed threat IP communication

Goal: flag internal hosts talking to IPs appearing on multiple distinct
threat feeds.

```spl
index=<firewall_index> sourcetype=<firewall_sourcetype> earliest=-15m action=allowed
| rename dest_ip AS indicator
| dedup src_ip indicator
| `whisper_explain(indicator)`
| spath sources{}.feedId output=feeds
| eval feed_count=mvcount(feeds)
| where feed_count >= 2
| eval risk_score=case(feed_count >= 4, 80, feed_count >= 2, 50, 1=1, 0)
| eval risk_message="Whisper: destination IP listed on " . feed_count . " threat feeds"
| eval risk_object=src_ip, risk_object_type="system"
| collect index=risk sourcetype=whisper:risk
```

`CALL explain()` returns its feed evidence in a column called `sources`, an
array of objects each carrying `feedId`, `weight`, `firstSeen` and `lastSeen`.
Extract `sources{}.feedId` rather than `sources{}` so `mvcount` counts feeds
and not objects. Verified against production on 2026-08-09.

The `>= 2` minimum feed count is the detection threshold -- change it
inline.

### Alerting on the risk score alone

Every `whisperlookup` result already carries `whisper_risk_score` and
`whisper_risk_level`, so an RBA rule can trigger on the score without a
detection of your own:

```spl
index=<source_index> sourcetype=<source_sourcetype> earliest=-15m
| rename <indicator_field> AS indicator
| dedup indicator
| whisperlookup field=indicator
| where whisper_risk_score >= 60
| eval risk_score=whisper_risk_score
| eval risk_message="Whisper risk " . whisper_risk_level . " (" . whisper_risk_factors_list . ")"
| eval risk_object=indicator, risk_object_type=if(whisper_indicator_type=="ip", "system", "other")
| collect index=risk sourcetype=whisper:risk
```

The factor weights in `lookups/whisper_risk_factors.csv`, and the full
factor list, are on the
[Enrichment page](/docs/integrations/splunk/using-it).

## What the add-on sends

Whisper commands send the indicator you look up — a hostname, IP address, ASN or CIDR — to graph.whisper.security over HTTPS, with this add-on's version and a random installation identifier. Your event data, field values, user names and search text are never sent. Scheduled use: a search you schedule keeps sending on that schedule, unattended, under this acknowledgement. See the [Privacy Policy](/privacy-policy) and the [Terms & Conditions](/terms-conditions).

## Where this runs

Install the add-on on the ES search head, or the ES search head cluster:
the adaptive response action and the populator searches run there.
Confirm `whisper_user` is imported into the ES roles your analysts use, so
they can run `whisperlookup` and `whisperquery` from the ES search bar.
Shipping the add-on to a distributed environment is on
[Deployment Architecture](/docs/integrations/splunk/deployment-architecture).
