elastic / elastic/docs-content
[Security][DE/Workflows][9.6 & Serverless] Create exception item from a workflow
- Dominant language
- No language data
- Stars
- 47
- Forks
- 261
- Avg merge
- 3d 21h
- Merged PRs (30d)
- 141
Description
### Description
For 9.6 we are adding a named Workflow action step under the existing `security.*` namespace: `security.createExceptionListItem`. Until now, adding an item to an exception list from a workflow was only reachable through the generic `kibana.request` step calling the exception-list-items API directly, which requires API knowledge and offers no discoverability or schema validation in the Workflows editor.
This new step exposes an explicit input schema (validated at save time, discoverable in the Workflows editor) and native exception-creation semantics, with optional idempotency via `item_id`, so detection engineers can automate allowlisting/suppression workflows without hand-writing exceptions-API requests.
`security.createExceptionListItem` adds an exception item to an existing exception list, identified by `list_id`. Adding to a shared exception list affects every rule the list is linked to.
This step is **GA** in 9.6. It lives in the `StepCategory.KibanaSecurity` category (the same category as the `security.*` alert/attack triage steps and the `security.enableRule`/`disableRule` rule-management steps) and is automatically authenticated using the permissions/API key of the identity executing the workflow (same model as the existing `kibana.*` and `cases.*` steps). It is implemented as a custom step on top of the exception-list-items API via the `callKibanaApi` platform utility.
The step optionally accepts an `item_id` to make repeated runs idempotent: when an item with that `item_id` already exists on the target list, the step skips creation (or updates it, with `overwrite: true`) instead of creating a duplicate. The output's `outcome` reports what happened (`created`, `skipped`, or `overwritten`).
---
## Docs work
### 1. Add this step to the Security action steps page
On `explore-analyze/workflows/steps/security.md`, add an **Exceptions** group (parallel to the Alerts, Attacks, and Rules groups) with jump links — or, if the companion `security.createRuleException` step already added this group, add this step as a subsection within it. Follow the same layout as the other groups: purpose sentence, a parameter table (`Parameter | Location | Type | Required | Description`), and a YAML example.
Reuse the page's existing conventions (`applies_to` frontmatter, the `:::{include} ../_snippets/schema-location-legend.md :::` legend, the Related section). This step is GA in 9.6: use `stack: ga 9.6+` / `serverless: ga`, consistent with the rest of the page.
#### Conventions to document
- **All parameters live under `with`.**
- **Entries use a flat, verb-based `operator` shape**, not the raw exceptions-API discriminated union. Each entry combines a `field` with an `operator`: `is`, `is_not`, `matches`, and `does_not_match` take a single `value` (the match operators support `*` and `?` wildcards); `is_one_of` and `is_not_one_of` take a `values` array; `exists` and `does_not_exist` take no operand; `is_in_list` and `is_not_in_list` reference a value list via `list.id` and `list.type`.
- **A value-list entry cannot be mixed with other entry types in the same item.** If any entry in `entries` uses `is_in_list`/`is_not_in_list`, every entry in that item must.
- **`entries` is a logical AND.** All entries of an item must match for the exception to apply; author separate items for alternative (OR) conditions.
- **Nested conditions are not supported.** Fields mapped as `nested` in the source indices (mostly Endpoint objects) can't be targeted from this step; use the Security UI or API for those.
- **`expire_time` (optional, ISO 8601) makes the exception temporary.**
- **The target list must already exist.** The step fails if `list_id` doesn't resolve to an existing exception list (unlike the rule-exception step, this one does not create the list for you).
- **Optional idempotency via `item_id`.** When provided and an item with that `item_id` already exists on `list_id`, the step skips creation and returns the existing item, or updates it when `overwrite: true` (existing comments are preserved). `overwrite: true` requires `item_id`; providing `overwrite` without `item_id` fails validation. **If `item_id` is omitted, the API assigns a new random one on every run** — the step has nothing to match against, so it always creates a fresh item. Re-running the same workflow (a retry, a scheduled trigger, a loop) without `item_id` produces a new duplicate exception item each time instead of reusing the previous one. Provide `item_id` whenever the workflow might run more than once for what is logically the same exception.
- **A conflicting `item_id` on a different list fails loudly.** `item_id` is not scoped to a particular list. If an item with that `item_id` already exists on a list *other than* `list_id`, the step fails rather than silently creating a duplicate or touching the wrong item. Worth an explicit callout, since this is a step-level design choice, not an API-enforced rule.
- **Output summary.** The step returns `{ id, item_id, list_id, namespace_type, name, created_at, created_by, expire_time?, outcome }`. `outcome` is one of `created`, `skipped`, `overwritten`.
#### Reference (schema as implemented)
**`security.createExceptionListItem`** — Add an exception item to an existing exception list.
| Parameter | Location | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `list_id` | `with` | `string` | Required | The target exception list's `list_id`. The list must already exist. |
| `namespace_type` | `with` | `string` (`single` \| `agnostic`) | Optional (default `single`) | `single` for the current space, `agnostic` for space-agnostic lists. |
| `item_id` | `with` | `string` | Optional | Stable identifier for idempotency; see conventions above. |
| `overwrite` | `with` | `boolean` | Optional (default `false`) | Update the existing item instead of skipping when `item_id` already exists. Requires `item_id`. |
| `name` | `with` | `string` | Required | Exception item name. |
| `description` | `with` | `string` | Required | Exception item description. |
| `entries` | `with` | `array` | Required (at least 1) | The item's match conditions; see conventions above. |
| `os_types` | `with` | `string[]` | Optional | OS types the exception applies to. |
| `tags` | `with` | `string[]` | Optional | Tags for the item. |
| `expire_time` | `with` | `string` (ISO 8601) | Optional | Makes the exception temporary. |
| `comments` | `with` | `string[]` | Optional | Comments attached to the item. |
```yaml
# Add an item to a shared exception list
- name: add_exception_to_shared_list
type: security.createExceptionListItem
with:
list_id: corporate-allowlist
name: 'Allow scanner IP'
description: 'Vulnerability scanner traffic'
entries:
- field: source.ip
operator: is
value: '{{ event.source.ip }}'
```
```yaml
# Item referencing a value list, in a space-agnostic list
- name: add_value_list_exception
type: security.createExceptionListItem
with:
list_id: corporate-allowlist
namespace_type: agnostic
name: 'Allow approved scanner IPs'
description: 'Source IPs of the approved scanners'
entries:
- field: source.ip
operator: is_in_list
list:
id: approved_scanner_ips
type: ip
```
```yaml
# Idempotent: re-running this workflow updates the same item instead of duplicating it
- name: add_or_update_shared_exception
type: security.createExceptionListItem
with:
list_id: corporate-allowlist
item_id: 'scanner-ip-{{ event.source.ip }}'
overwrite: true
name: 'Allow scanner IP'
description: 'Vulnerability scanner traffic'
entries:
- field: source.ip
operator: is
value: '{{ event.source.ip }}'
```
Without `item_id`, running this same workflow again would create a second, separate exception item for the same IP rather than updating the first. `item_id` and `overwrite: true` together make re-runs safe.
#### Output reference
| Field | Type | Description |
| --- | --- | --- |
| `id` | `string` | Saved-object `id` of the created (or existing) item. |
| `item_id` | `string` | The item's `item_id`. |
| `list_id` | `string` | The list the item is on. |
| `namespace_type` | `string` | `single` or `agnostic`. |
| `name` | `string` | The item's name. |
| `created_at` | `string` | Creation timestamp. |
| `created_by` | `string` | Creator. |
| `expire_time` | `string` | Present only if set on the item. |
| `outcome` | `string` | `created`, `skipped`, or `overwritten`. |
### 2. Update the Action steps overview page
On [`explore-analyze/workflows/steps/action-steps.md`](https://github.com/elastic/docs-content/blob/main/explore-analyze/workflows/steps/action-steps.md), add (or extend, if the companion ticket got there first) a bullet on the Security category's "Use … to:" list:
- Add an item to an exception list (`security.createExceptionListItem`).
### 3. Update the step-type index
On [`reference/step-types.md`](https://github.com/elastic/docs-content/blob/main/explore-analyze/workflows/reference/step-types.md) (A–Z index), add a `security.createExceptionListItem` row to the **Security** category. One-line summary: add an exception item to an existing (possibly shared) exception list, with optional idempotency via `item_id`. Applies from 9.6.0 and in serverless.
### (Optional) Rule-ops / exceptions use-case guide
If there is a detection-rule-operations or exceptions-management use-case guide, add a short mention of `security.createExceptionListItem` as the native option instead of a generic `kibana.request` call. Skip if no such guide exists.
---
## Timeline & environment
- **Stack release:** 9.6.0
- **Serverless:** active by default (no feature flag)
- GA in 9.6. Tag everything `stack: ga 9.6+` / `serverless: ga`.
### Which deployment methods does this change impact?
Elastic On-Prem and Cloud (all)
### What Elastic Stack release is this request related to?
9.6
## Resources
- **Implementation PR:** [kibana#277802 — [Security Solution] Add exception creation workflow steps](https://github.com/elastic/kibana/pull/277802) (implements both this step and its companion, `security.createRuleException`)
- **Companion ticket:** `security.createRuleException` — add an exception item to a rule's own default exception list (filed separately, same PR, same target page)
- **Sibling epic:** [security-team#18163 — [Radar] - Exception creation workflow step](https://github.com/elastic/security-team/issues/18163)
- **Existing reference to mirror:** [Enable/disable detection rules from a workflow](https://github.com/elastic/docs-content/issues/7352) (same target page, same layout pattern) and [Cases action steps](https://www.elastic.co/docs/explore-analyze/workflows/steps/cases)
- **Target page (shared with triage, rule-management, and the companion exception step):** `explore-analyze/workflows/steps/security.md`
### Implementation notes for the drafter (source of truth: PR #277802)
- Step ID `security.createExceptionListItem`; category `StepCategory.KibanaSecurity`.
- Calls `POST /api/exception_lists/items` directly against an existing list; fails if `list_id` doesn't resolve to an existing list.
- Goes through the `callKibanaApi` platform utility.
- Editor icon: `list_add`.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.