Skip to content

[Security Solution] Fix bulk close returning 0 updated when a data view runtime field is used in the filter - #288946

Merged
jonwalstedt merged 2 commits into
elastic:mainfrom
jonwalstedt:19185-unable-to-bulk-close-alerts
Sep 3, 2026
Merged

[Security Solution] Fix bulk close returning 0 updated when a data view runtime field is used in the filter#288946
jonwalstedt merged 2 commits into
elastic:mainfrom
jonwalstedt:19185-unable-to-bulk-close-alerts

Conversation

@jonwalstedt

@jonwalstedt jonwalstedt commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Summary

When "Select all" → Close selected is used for bulk-closing alerts, the query-based close path called updateAlertStatus without runtimeFields. The alerts index has dynamic: false, so fields not in the static index mapping — e.g. ES|QL detection rule output columns made searchable via data view runtime fields — cannot be resolved by the _update_by_query, causing it to match 0 documents and return "updated": 0.

Single-alert close (checkbox only, no "Select all") is unaffected because it uses the ID-based path which doesn't need field resolution.

Root cause

The alert listing query (grid + summary panels) correctly includes runtime_mappings sourced from dataView.getRuntimeMappings(). The bulk close request (POST /api/detection_engine/signals/status) never forwarded runtime_fields, so the server-side _update_by_query had no way to resolve unmapped fields.

The server route already fully supports a runtime_fields body parameter and converts it to runtime_mappings on the _update_by_query — the client call site was simply never passing it.

Before fix

bulk-removal-issue.mov

After fix

bulk-removal-issue-fixed.mov

Changes

Thread runtimeMappings (already computed from dataView.getRuntimeMappings() in alerts_table/index.tsx) down to the bulk close API call:

File Change
alerts_table/index.tsx Pass runtimeMappings as 4th arg to useBulkActionsByTableType
use_bulk_actions.tsx Accept and forward runtimeMappings in alertActionParams
use_alert_actions.tsx Accept runtimeMappings, derive runtimeFields via useMemo, pass to updateAlertStatus

Test coverage

  • use_alert_actions.test.tsx: 3 new tests verifying that updateAlertStatus receives runtimeFields derived from runtimeMappings when isSelectAllChecked (query path), undefined when no mappings provided, and that the query path is taken (not signalIds)
  • use_bulk_actions.test.tsx: updated existing params test to assert runtimeMappings: undefined by default; new test verifying runtimeMappings is forwarded to useBulkAlertActionItems

Checklist

  • Any text added follows EUI's writing guidelines, uses sentence case text and includes i18n support
  • Documentation was added for features that require explanation or tutorials
  • Unit or functional tests were updated or added to match the most common scenarios
  • The PR description includes the appropriate Risk by setting the correct label under the release_note:* label and by linking to any notifications if necessary

🤖 Generated with Claude Code

@jonwalstedt
jonwalstedt requested a review from a team as a code owner September 3, 2026 10:10

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the runtime-mappings threading. One concrete correctness concern on how unsupported runtime field types are forwarded to the bulk-close request; details inline.

Generated by Claude Reviewer for #288946 · opus · 127.3 AIC · ⌖ 14.3 AIC · ⊞ 5.1K

@jonwalstedt jonwalstedt self-assigned this Sep 3, 2026
@jonwalstedt jonwalstedt added release_note:fix backport:all-open Backport to all branches that could still receive a release Team: Security Investigations Security solution alert triage & investigations Team: SecuritySolution Security Solutions Team working on SIEM, Endpoint, Timeline, Resolver, etc. labels Sep 3, 2026
@infra-vault-gh-plugin-prod

Copy link
Copy Markdown

Pinging @elastic/security-solution (Team: SecuritySolution)

@jonwalstedt jonwalstedt added v9.6.0 reviewer:libra PR review with Libra. This disables Claude and Scout reviewers and removed reviewer:libra PR review with Libra. This disables Claude and Scout reviewers labels Sep 3, 2026

@kelvtanv kelvtanv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for fixing this!

jonwalstedt and others added 2 commits September 3, 2026 16:08
…ew runtime field is used in the filter

When "Select all" is used for bulk close, the query-based path called
`updateAlertStatus` without `runtimeFields`. The alerts index has
`dynamic: false`, so fields not in the static mapping (e.g. ES|QL
output columns exposed via data view runtime fields) cannot be resolved
by `_update_by_query`, causing it to match 0 documents.

Thread `runtimeMappings` from `dataView.getRuntimeMappings()` (already
computed in `alerts_table/index.tsx`) through `useBulkActionsByTableType`
→ `useBulkAlertActionItems` → `updateAlertStatus`. The server-side
plumbing for `runtime_fields` was already complete; only the client call
site was missing it.

Fixes elastic/security-team#19185

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…bulk-close request

A data view can contain runtime fields of type 'composite' or 'lookup'
(valid in ES but not in the server's RuntimeFieldType Zod enum). Forwarding
those types causes a Zod 400 on the bulk-close route that fails the entire
request — worse than the original silent no-op.

Filter the mappings against the server's accepted types (derived from
RuntimeFieldTypeEnum rather than a hardcoded list so it stays in sync).
Also fix a TS2722 error in the test where BulkActionsConfig.onClick is
typed as optional.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@jonwalstedt
jonwalstedt force-pushed the 19185-unable-to-bulk-close-alerts branch from 6d09887 to cd5b6af Compare September 3, 2026 14:08
@kibanamachine

Copy link
Copy Markdown
Contributor

💛 Build succeeded, but was flaky

Failed CI Steps

Metrics [docs]

Async chunks

Total size of all lazy-loaded chunks that will be downloaded as the user navigates the app

id before after diff
securitySolution 981.0KB 981.2KB +279.0B
Unknown metric groups

shared chunks total size

id before after diff
all 7.0MB 7.0MB +279.0B

total optimizer output size

id before after diff
all 63.2MB 63.2MB +558.0B

History

cc @jonwalstedt

@jonwalstedt
jonwalstedt merged commit 9819e83 into elastic:main Sep 3, 2026
45 checks passed
@kibanamachine

Copy link
Copy Markdown
Contributor

Starting backport for target branches: 8.19, 9.4, 9.5

https://github.com/elastic/kibana/actions/runs/33770681165

@kibanamachine

Copy link
Copy Markdown
Contributor

💔 Some backports could not be created

Status Branch Result
8.19 Backport failed because of merge conflicts
9.4
9.5

Note: Successful backport PRs will be merged automatically after passing CI.

Manual backport

To create the backport manually run:

node scripts/backport --pr 288946

Questions ?

Please refer to the Backport tool documentation

kibanamachine added a commit that referenced this pull request Sep 3, 2026
…ata view runtime field is used in the filter (#288946) (#289052)

# Backport

This will backport the following commits from `main` to `9.5`:
- [[Security Solution] Fix bulk close returning 0 updated when a data
view runtime field is used in the filter
(#288946)](#288946)

<!--- Backport version: 9.6.6 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sorenlouv/backport)

<!--BACKPORT [{"author":{"name":"Jon
Wålstedt","email":"jon.walstedt@elastic.co"},"sourceCommit":{"committedDate":"2026-09-03T15:05:54Z","message":"[Security
Solution] Fix bulk close returning 0 updated when a data view runtime
field is used in the filter (#288946)\n\n## Summary\n\nWhen **\"Select
all\" → Close selected** is used for bulk-closing alerts,\nthe
query-based close path called `updateAlertStatus`
without\n`runtimeFields`. The alerts index has `dynamic: false`, so
fields not in\nthe static index mapping — e.g. ES|QL detection rule
output columns made\nsearchable via data view runtime fields — cannot be
resolved by the\n`_update_by_query`, causing it to match 0 documents and
return\n`\"updated\": 0`.\n\nSingle-alert close (checkbox only, no
\"Select all\") is unaffected\nbecause it uses the ID-based path which
doesn't need field resolution.\n\n## Root cause\n\nThe alert listing
query (grid + summary panels) correctly includes\n`runtime_mappings`
sourced from `dataView.getRuntimeMappings()`. The\nbulk close request
(`POST /api/detection_engine/signals/status`) never\nforwarded
`runtime_fields`, so the server-side `_update_by_query` had no\nway to
resolve unmapped fields.\n\nThe server route already fully supports a
`runtime_fields` body\nparameter and converts it to `runtime_mappings`
on the\n`_update_by_query` — the client call site was simply never
passing it.\n\n**Before
fix**\n\n\nhttps://github.com/user-attachments/assets/ec4565f3-df31-4f16-a79a-dda133c81407\n\n\n**After
fix**\n\n\nhttps://github.com/user-attachments/assets/fc19273a-50f2-420c-8119-d631b34cb0f2\n\n\n##
Changes\n\nThread `runtimeMappings` (already computed
from\n`dataView.getRuntimeMappings()` in `alerts_table/index.tsx`) down
to the\nbulk close API call:\n\n| File | Change |\n|---|---|\n|
`alerts_table/index.tsx` | Pass `runtimeMappings` as 4th arg
to\n`useBulkActionsByTableType` |\n| `use_bulk_actions.tsx` | Accept and
forward `runtimeMappings` in\n`alertActionParams` |\n|
`use_alert_actions.tsx` | Accept `runtimeMappings`,
derive\n`runtimeFields` via `useMemo`, pass to `updateAlertStatus`
|\n\n## Test coverage\n\n- **`use_alert_actions.test.tsx`**: 3 new tests
verifying that\n`updateAlertStatus` receives `runtimeFields` derived
from\n`runtimeMappings` when `isSelectAllChecked` (query path),
`undefined`\nwhen no mappings provided, and that the query path is taken
(not\nsignalIds)\n- **`use_bulk_actions.test.tsx`**: updated existing
params test to\nassert `runtimeMappings: undefined` by default; new test
verifying\n`runtimeMappings` is forwarded to
`useBulkAlertActionItems`\n\n## Checklist\n\n- [x] Any text added
follows [EUI's
writing\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\nsentence case text and includes
[i18n\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\n-
[x]\n[Documentation](https://www.elastic.co/guide/en/security/master/index.html)\nwas
added for features that require explanation or tutorials\n- [x] [Unit or
functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere
updated or added to match the most common scenarios\n- [x] The PR
description includes the appropriate Risk by setting the\ncorrect label
under the `release_note:*` label and by linking to any\nnotifications if
necessary\n\n🤖 Generated with [Claude
Code](https://claude.com/claude-code)\n\n---------\n\nCo-authored-by:
Claude Sonnet 4.6
<noreply@anthropic.com>","sha":"9819e8399aea758a0353edd66e1a4b18c0237f89","branchLabelMapping":{"^v9.6.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","Team:
SecuritySolution","backport:all-open","v9.6.0","reviewer:libra","Team:
Security Investigations"],"title":"[Security Solution] Fix bulk close
returning 0 updated when a data view runtime field is used in the
filter","number":288946,"url":"https://github.com/elastic/kibana/pull/288946","mergeCommit":{"message":"[Security
Solution] Fix bulk close returning 0 updated when a data view runtime
field is used in the filter (#288946)\n\n## Summary\n\nWhen **\"Select
all\" → Close selected** is used for bulk-closing alerts,\nthe
query-based close path called `updateAlertStatus`
without\n`runtimeFields`. The alerts index has `dynamic: false`, so
fields not in\nthe static index mapping — e.g. ES|QL detection rule
output columns made\nsearchable via data view runtime fields — cannot be
resolved by the\n`_update_by_query`, causing it to match 0 documents and
return\n`\"updated\": 0`.\n\nSingle-alert close (checkbox only, no
\"Select all\") is unaffected\nbecause it uses the ID-based path which
doesn't need field resolution.\n\n## Root cause\n\nThe alert listing
query (grid + summary panels) correctly includes\n`runtime_mappings`
sourced from `dataView.getRuntimeMappings()`. The\nbulk close request
(`POST /api/detection_engine/signals/status`) never\nforwarded
`runtime_fields`, so the server-side `_update_by_query` had no\nway to
resolve unmapped fields.\n\nThe server route already fully supports a
`runtime_fields` body\nparameter and converts it to `runtime_mappings`
on the\n`_update_by_query` — the client call site was simply never
passing it.\n\n**Before
fix**\n\n\nhttps://github.com/user-attachments/assets/ec4565f3-df31-4f16-a79a-dda133c81407\n\n\n**After
fix**\n\n\nhttps://github.com/user-attachments/assets/fc19273a-50f2-420c-8119-d631b34cb0f2\n\n\n##
Changes\n\nThread `runtimeMappings` (already computed
from\n`dataView.getRuntimeMappings()` in `alerts_table/index.tsx`) down
to the\nbulk close API call:\n\n| File | Change |\n|---|---|\n|
`alerts_table/index.tsx` | Pass `runtimeMappings` as 4th arg
to\n`useBulkActionsByTableType` |\n| `use_bulk_actions.tsx` | Accept and
forward `runtimeMappings` in\n`alertActionParams` |\n|
`use_alert_actions.tsx` | Accept `runtimeMappings`,
derive\n`runtimeFields` via `useMemo`, pass to `updateAlertStatus`
|\n\n## Test coverage\n\n- **`use_alert_actions.test.tsx`**: 3 new tests
verifying that\n`updateAlertStatus` receives `runtimeFields` derived
from\n`runtimeMappings` when `isSelectAllChecked` (query path),
`undefined`\nwhen no mappings provided, and that the query path is taken
(not\nsignalIds)\n- **`use_bulk_actions.test.tsx`**: updated existing
params test to\nassert `runtimeMappings: undefined` by default; new test
verifying\n`runtimeMappings` is forwarded to
`useBulkAlertActionItems`\n\n## Checklist\n\n- [x] Any text added
follows [EUI's
writing\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\nsentence case text and includes
[i18n\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\n-
[x]\n[Documentation](https://www.elastic.co/guide/en/security/master/index.html)\nwas
added for features that require explanation or tutorials\n- [x] [Unit or
functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere
updated or added to match the most common scenarios\n- [x] The PR
description includes the appropriate Risk by setting the\ncorrect label
under the `release_note:*` label and by linking to any\nnotifications if
necessary\n\n🤖 Generated with [Claude
Code](https://claude.com/claude-code)\n\n---------\n\nCo-authored-by:
Claude Sonnet 4.6
<noreply@anthropic.com>","sha":"9819e8399aea758a0353edd66e1a4b18c0237f89"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.6.0","branchLabelMappingKey":"^v9.6.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/288946","number":288946,"mergeCommit":{"message":"[Security
Solution] Fix bulk close returning 0 updated when a data view runtime
field is used in the filter (#288946)\n\n## Summary\n\nWhen **\"Select
all\" → Close selected** is used for bulk-closing alerts,\nthe
query-based close path called `updateAlertStatus`
without\n`runtimeFields`. The alerts index has `dynamic: false`, so
fields not in\nthe static index mapping — e.g. ES|QL detection rule
output columns made\nsearchable via data view runtime fields — cannot be
resolved by the\n`_update_by_query`, causing it to match 0 documents and
return\n`\"updated\": 0`.\n\nSingle-alert close (checkbox only, no
\"Select all\") is unaffected\nbecause it uses the ID-based path which
doesn't need field resolution.\n\n## Root cause\n\nThe alert listing
query (grid + summary panels) correctly includes\n`runtime_mappings`
sourced from `dataView.getRuntimeMappings()`. The\nbulk close request
(`POST /api/detection_engine/signals/status`) never\nforwarded
`runtime_fields`, so the server-side `_update_by_query` had no\nway to
resolve unmapped fields.\n\nThe server route already fully supports a
`runtime_fields` body\nparameter and converts it to `runtime_mappings`
on the\n`_update_by_query` — the client call site was simply never
passing it.\n\n**Before
fix**\n\n\nhttps://github.com/user-attachments/assets/ec4565f3-df31-4f16-a79a-dda133c81407\n\n\n**After
fix**\n\n\nhttps://github.com/user-attachments/assets/fc19273a-50f2-420c-8119-d631b34cb0f2\n\n\n##
Changes\n\nThread `runtimeMappings` (already computed
from\n`dataView.getRuntimeMappings()` in `alerts_table/index.tsx`) down
to the\nbulk close API call:\n\n| File | Change |\n|---|---|\n|
`alerts_table/index.tsx` | Pass `runtimeMappings` as 4th arg
to\n`useBulkActionsByTableType` |\n| `use_bulk_actions.tsx` | Accept and
forward `runtimeMappings` in\n`alertActionParams` |\n|
`use_alert_actions.tsx` | Accept `runtimeMappings`,
derive\n`runtimeFields` via `useMemo`, pass to `updateAlertStatus`
|\n\n## Test coverage\n\n- **`use_alert_actions.test.tsx`**: 3 new tests
verifying that\n`updateAlertStatus` receives `runtimeFields` derived
from\n`runtimeMappings` when `isSelectAllChecked` (query path),
`undefined`\nwhen no mappings provided, and that the query path is taken
(not\nsignalIds)\n- **`use_bulk_actions.test.tsx`**: updated existing
params test to\nassert `runtimeMappings: undefined` by default; new test
verifying\n`runtimeMappings` is forwarded to
`useBulkAlertActionItems`\n\n## Checklist\n\n- [x] Any text added
follows [EUI's
writing\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\nsentence case text and includes
[i18n\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\n-
[x]\n[Documentation](https://www.elastic.co/guide/en/security/master/index.html)\nwas
added for features that require explanation or tutorials\n- [x] [Unit or
functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere
updated or added to match the most common scenarios\n- [x] The PR
description includes the appropriate Risk by setting the\ncorrect label
under the `release_note:*` label and by linking to any\nnotifications if
necessary\n\n🤖 Generated with [Claude
Code](https://claude.com/claude-code)\n\n---------\n\nCo-authored-by:
Claude Sonnet 4.6
<noreply@anthropic.com>","sha":"9819e8399aea758a0353edd66e1a4b18c0237f89"}}]}]
BACKPORT-->

Co-authored-by: Jon Wålstedt <jon.walstedt@elastic.co>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

@kibanamachine kibanamachine left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Libra found 1 issue.

Generated by Libra

Comment on lines +64 to +67
if (!runtimeMappings) return undefined;
const entries = Object.entries(runtimeMappings)
.filter(([, field]) => SUPPORTED_RUNTIME_FIELD_TYPES.has(field.type))
.map(([name, field]) => [name, field.type] as [string, RuntimeFieldType]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Converting each data-view runtime mapping to only its type discards the mapping's script (and other semantics such as date formatting). The status API recreates these as fields that read _source[fieldName], so a normal computed runtime field—for example, display_name whose script combines two source fields—still has no value during _update_by_query; a transformed field can even match a different set of alerts than the table. Thus select-all actions do not reliably operate on the same query results shown in the grid. Preserve the actual runtime mapping semantics through the request/server path, or restrict this conversion to mappings proven to be equivalent direct-source readers and handle unsupported filtered mappings explicitly.

MappingRuntimeFields supports scripted/computed mappings, but this projection retains only [name, field.type]. The imported route contract documents that the server then reads _source[fieldName], which is not equivalent to the data view mapping used to build and execute the table query. The new tests cover only scriptless { type: ... } mappings.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jonwalstedt was it a deliberate decision to support only _source backed runtime fields?
For the direct source keyword fields the fix worked for me:
Screenshot 2026-09-07 at 10 57 51

But for runtime fields defined with a Painless script, bulk close still returns 0 updated. AFAIU because only type is extracted from the mapping (as noted in the comment above comment), so the script is stripped before the request reaches the server:

Screen.Recording.2026-09-07.at.10.37.28.mov

juliaElastic pushed a commit to juliaElastic/kibana that referenced this pull request Sep 4, 2026
…ew runtime field is used in the filter (elastic#288946)

## Summary

When **"Select all" → Close selected** is used for bulk-closing alerts,
the query-based close path called `updateAlertStatus` without
`runtimeFields`. The alerts index has `dynamic: false`, so fields not in
the static index mapping — e.g. ES|QL detection rule output columns made
searchable via data view runtime fields — cannot be resolved by the
`_update_by_query`, causing it to match 0 documents and return
`"updated": 0`.

Single-alert close (checkbox only, no "Select all") is unaffected
because it uses the ID-based path which doesn't need field resolution.

## Root cause

The alert listing query (grid + summary panels) correctly includes
`runtime_mappings` sourced from `dataView.getRuntimeMappings()`. The
bulk close request (`POST /api/detection_engine/signals/status`) never
forwarded `runtime_fields`, so the server-side `_update_by_query` had no
way to resolve unmapped fields.

The server route already fully supports a `runtime_fields` body
parameter and converts it to `runtime_mappings` on the
`_update_by_query` — the client call site was simply never passing it.

**Before fix**


https://github.com/user-attachments/assets/ec4565f3-df31-4f16-a79a-dda133c81407


**After fix**


https://github.com/user-attachments/assets/fc19273a-50f2-420c-8119-d631b34cb0f2


## Changes

Thread `runtimeMappings` (already computed from
`dataView.getRuntimeMappings()` in `alerts_table/index.tsx`) down to the
bulk close API call:

| File | Change |
|---|---|
| `alerts_table/index.tsx` | Pass `runtimeMappings` as 4th arg to
`useBulkActionsByTableType` |
| `use_bulk_actions.tsx` | Accept and forward `runtimeMappings` in
`alertActionParams` |
| `use_alert_actions.tsx` | Accept `runtimeMappings`, derive
`runtimeFields` via `useMemo`, pass to `updateAlertStatus` |

## Test coverage

- **`use_alert_actions.test.tsx`**: 3 new tests verifying that
`updateAlertStatus` receives `runtimeFields` derived from
`runtimeMappings` when `isSelectAllChecked` (query path), `undefined`
when no mappings provided, and that the query path is taken (not
signalIds)
- **`use_bulk_actions.test.tsx`**: updated existing params test to
assert `runtimeMappings: undefined` by default; new test verifying
`runtimeMappings` is forwarded to `useBulkAlertActionItems`

## Checklist

- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [x]
[Documentation](https://www.elastic.co/guide/en/security/master/index.html)
was added for features that require explanation or tutorials
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] The PR description includes the appropriate Risk by setting the
correct label under the `release_note:*` label and by linking to any
notifications if necessary

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
@jonwalstedt

Copy link
Copy Markdown
Contributor Author

💚 All backports created successfully

Status Branch Result
8.19

Note: Successful backport PRs will be merged automatically after passing CI.

Questions ?

Please refer to the Backport tool documentation

kibanamachine added a commit that referenced this pull request Sep 4, 2026
…ata view runtime field is used in the filter (#288946) (#289051)

# Backport

This will backport the following commits from `main` to `9.4`:
- [[Security Solution] Fix bulk close returning 0 updated when a data
view runtime field is used in the filter
(#288946)](#288946)

<!--- Backport version: 9.6.6 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sorenlouv/backport)

<!--BACKPORT [{"author":{"name":"Jon
Wålstedt","email":"jon.walstedt@elastic.co"},"sourceCommit":{"committedDate":"2026-09-03T15:05:54Z","message":"[Security
Solution] Fix bulk close returning 0 updated when a data view runtime
field is used in the filter (#288946)\n\n## Summary\n\nWhen **\"Select
all\" → Close selected** is used for bulk-closing alerts,\nthe
query-based close path called `updateAlertStatus`
without\n`runtimeFields`. The alerts index has `dynamic: false`, so
fields not in\nthe static index mapping — e.g. ES|QL detection rule
output columns made\nsearchable via data view runtime fields — cannot be
resolved by the\n`_update_by_query`, causing it to match 0 documents and
return\n`\"updated\": 0`.\n\nSingle-alert close (checkbox only, no
\"Select all\") is unaffected\nbecause it uses the ID-based path which
doesn't need field resolution.\n\n## Root cause\n\nThe alert listing
query (grid + summary panels) correctly includes\n`runtime_mappings`
sourced from `dataView.getRuntimeMappings()`. The\nbulk close request
(`POST /api/detection_engine/signals/status`) never\nforwarded
`runtime_fields`, so the server-side `_update_by_query` had no\nway to
resolve unmapped fields.\n\nThe server route already fully supports a
`runtime_fields` body\nparameter and converts it to `runtime_mappings`
on the\n`_update_by_query` — the client call site was simply never
passing it.\n\n**Before
fix**\n\n\nhttps://github.com/user-attachments/assets/ec4565f3-df31-4f16-a79a-dda133c81407\n\n\n**After
fix**\n\n\nhttps://github.com/user-attachments/assets/fc19273a-50f2-420c-8119-d631b34cb0f2\n\n\n##
Changes\n\nThread `runtimeMappings` (already computed
from\n`dataView.getRuntimeMappings()` in `alerts_table/index.tsx`) down
to the\nbulk close API call:\n\n| File | Change |\n|---|---|\n|
`alerts_table/index.tsx` | Pass `runtimeMappings` as 4th arg
to\n`useBulkActionsByTableType` |\n| `use_bulk_actions.tsx` | Accept and
forward `runtimeMappings` in\n`alertActionParams` |\n|
`use_alert_actions.tsx` | Accept `runtimeMappings`,
derive\n`runtimeFields` via `useMemo`, pass to `updateAlertStatus`
|\n\n## Test coverage\n\n- **`use_alert_actions.test.tsx`**: 3 new tests
verifying that\n`updateAlertStatus` receives `runtimeFields` derived
from\n`runtimeMappings` when `isSelectAllChecked` (query path),
`undefined`\nwhen no mappings provided, and that the query path is taken
(not\nsignalIds)\n- **`use_bulk_actions.test.tsx`**: updated existing
params test to\nassert `runtimeMappings: undefined` by default; new test
verifying\n`runtimeMappings` is forwarded to
`useBulkAlertActionItems`\n\n## Checklist\n\n- [x] Any text added
follows [EUI's
writing\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\nsentence case text and includes
[i18n\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\n-
[x]\n[Documentation](https://www.elastic.co/guide/en/security/master/index.html)\nwas
added for features that require explanation or tutorials\n- [x] [Unit or
functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere
updated or added to match the most common scenarios\n- [x] The PR
description includes the appropriate Risk by setting the\ncorrect label
under the `release_note:*` label and by linking to any\nnotifications if
necessary\n\n🤖 Generated with [Claude
Code](https://claude.com/claude-code)\n\n---------\n\nCo-authored-by:
Claude Sonnet 4.6
<noreply@anthropic.com>","sha":"9819e8399aea758a0353edd66e1a4b18c0237f89","branchLabelMapping":{"^v9.6.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","Team:
SecuritySolution","backport:all-open","v9.6.0","reviewer:libra","Team:
Security Investigations"],"title":"[Security Solution] Fix bulk close
returning 0 updated when a data view runtime field is used in the
filter","number":288946,"url":"https://github.com/elastic/kibana/pull/288946","mergeCommit":{"message":"[Security
Solution] Fix bulk close returning 0 updated when a data view runtime
field is used in the filter (#288946)\n\n## Summary\n\nWhen **\"Select
all\" → Close selected** is used for bulk-closing alerts,\nthe
query-based close path called `updateAlertStatus`
without\n`runtimeFields`. The alerts index has `dynamic: false`, so
fields not in\nthe static index mapping — e.g. ES|QL detection rule
output columns made\nsearchable via data view runtime fields — cannot be
resolved by the\n`_update_by_query`, causing it to match 0 documents and
return\n`\"updated\": 0`.\n\nSingle-alert close (checkbox only, no
\"Select all\") is unaffected\nbecause it uses the ID-based path which
doesn't need field resolution.\n\n## Root cause\n\nThe alert listing
query (grid + summary panels) correctly includes\n`runtime_mappings`
sourced from `dataView.getRuntimeMappings()`. The\nbulk close request
(`POST /api/detection_engine/signals/status`) never\nforwarded
`runtime_fields`, so the server-side `_update_by_query` had no\nway to
resolve unmapped fields.\n\nThe server route already fully supports a
`runtime_fields` body\nparameter and converts it to `runtime_mappings`
on the\n`_update_by_query` — the client call site was simply never
passing it.\n\n**Before
fix**\n\n\nhttps://github.com/user-attachments/assets/ec4565f3-df31-4f16-a79a-dda133c81407\n\n\n**After
fix**\n\n\nhttps://github.com/user-attachments/assets/fc19273a-50f2-420c-8119-d631b34cb0f2\n\n\n##
Changes\n\nThread `runtimeMappings` (already computed
from\n`dataView.getRuntimeMappings()` in `alerts_table/index.tsx`) down
to the\nbulk close API call:\n\n| File | Change |\n|---|---|\n|
`alerts_table/index.tsx` | Pass `runtimeMappings` as 4th arg
to\n`useBulkActionsByTableType` |\n| `use_bulk_actions.tsx` | Accept and
forward `runtimeMappings` in\n`alertActionParams` |\n|
`use_alert_actions.tsx` | Accept `runtimeMappings`,
derive\n`runtimeFields` via `useMemo`, pass to `updateAlertStatus`
|\n\n## Test coverage\n\n- **`use_alert_actions.test.tsx`**: 3 new tests
verifying that\n`updateAlertStatus` receives `runtimeFields` derived
from\n`runtimeMappings` when `isSelectAllChecked` (query path),
`undefined`\nwhen no mappings provided, and that the query path is taken
(not\nsignalIds)\n- **`use_bulk_actions.test.tsx`**: updated existing
params test to\nassert `runtimeMappings: undefined` by default; new test
verifying\n`runtimeMappings` is forwarded to
`useBulkAlertActionItems`\n\n## Checklist\n\n- [x] Any text added
follows [EUI's
writing\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\nsentence case text and includes
[i18n\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\n-
[x]\n[Documentation](https://www.elastic.co/guide/en/security/master/index.html)\nwas
added for features that require explanation or tutorials\n- [x] [Unit or
functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere
updated or added to match the most common scenarios\n- [x] The PR
description includes the appropriate Risk by setting the\ncorrect label
under the `release_note:*` label and by linking to any\nnotifications if
necessary\n\n🤖 Generated with [Claude
Code](https://claude.com/claude-code)\n\n---------\n\nCo-authored-by:
Claude Sonnet 4.6
<noreply@anthropic.com>","sha":"9819e8399aea758a0353edd66e1a4b18c0237f89"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.6.0","branchLabelMappingKey":"^v9.6.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/288946","number":288946,"mergeCommit":{"message":"[Security
Solution] Fix bulk close returning 0 updated when a data view runtime
field is used in the filter (#288946)\n\n## Summary\n\nWhen **\"Select
all\" → Close selected** is used for bulk-closing alerts,\nthe
query-based close path called `updateAlertStatus`
without\n`runtimeFields`. The alerts index has `dynamic: false`, so
fields not in\nthe static index mapping — e.g. ES|QL detection rule
output columns made\nsearchable via data view runtime fields — cannot be
resolved by the\n`_update_by_query`, causing it to match 0 documents and
return\n`\"updated\": 0`.\n\nSingle-alert close (checkbox only, no
\"Select all\") is unaffected\nbecause it uses the ID-based path which
doesn't need field resolution.\n\n## Root cause\n\nThe alert listing
query (grid + summary panels) correctly includes\n`runtime_mappings`
sourced from `dataView.getRuntimeMappings()`. The\nbulk close request
(`POST /api/detection_engine/signals/status`) never\nforwarded
`runtime_fields`, so the server-side `_update_by_query` had no\nway to
resolve unmapped fields.\n\nThe server route already fully supports a
`runtime_fields` body\nparameter and converts it to `runtime_mappings`
on the\n`_update_by_query` — the client call site was simply never
passing it.\n\n**Before
fix**\n\n\nhttps://github.com/user-attachments/assets/ec4565f3-df31-4f16-a79a-dda133c81407\n\n\n**After
fix**\n\n\nhttps://github.com/user-attachments/assets/fc19273a-50f2-420c-8119-d631b34cb0f2\n\n\n##
Changes\n\nThread `runtimeMappings` (already computed
from\n`dataView.getRuntimeMappings()` in `alerts_table/index.tsx`) down
to the\nbulk close API call:\n\n| File | Change |\n|---|---|\n|
`alerts_table/index.tsx` | Pass `runtimeMappings` as 4th arg
to\n`useBulkActionsByTableType` |\n| `use_bulk_actions.tsx` | Accept and
forward `runtimeMappings` in\n`alertActionParams` |\n|
`use_alert_actions.tsx` | Accept `runtimeMappings`,
derive\n`runtimeFields` via `useMemo`, pass to `updateAlertStatus`
|\n\n## Test coverage\n\n- **`use_alert_actions.test.tsx`**: 3 new tests
verifying that\n`updateAlertStatus` receives `runtimeFields` derived
from\n`runtimeMappings` when `isSelectAllChecked` (query path),
`undefined`\nwhen no mappings provided, and that the query path is taken
(not\nsignalIds)\n- **`use_bulk_actions.test.tsx`**: updated existing
params test to\nassert `runtimeMappings: undefined` by default; new test
verifying\n`runtimeMappings` is forwarded to
`useBulkAlertActionItems`\n\n## Checklist\n\n- [x] Any text added
follows [EUI's
writing\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\nsentence case text and includes
[i18n\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\n-
[x]\n[Documentation](https://www.elastic.co/guide/en/security/master/index.html)\nwas
added for features that require explanation or tutorials\n- [x] [Unit or
functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere
updated or added to match the most common scenarios\n- [x] The PR
description includes the appropriate Risk by setting the\ncorrect label
under the `release_note:*` label and by linking to any\nnotifications if
necessary\n\n🤖 Generated with [Claude
Code](https://claude.com/claude-code)\n\n---------\n\nCo-authored-by:
Claude Sonnet 4.6
<noreply@anthropic.com>","sha":"9819e8399aea758a0353edd66e1a4b18c0237f89"}}]}]
BACKPORT-->

---------

Co-authored-by: Jon Wålstedt <jon.walstedt@elastic.co>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
@radtke-vale

Copy link
Copy Markdown
Contributor

@jonwalstedt Can it be that the same fix is needed for the group actions?

I tried applying "Group by a rule" and then closing a group, but it does not seem to work:
Screenshot 2026-09-07 at 11 16 49
Screenshot 2026-09-07 at 11 19 18

jonwalstedt added a commit that referenced this pull request Sep 7, 2026
…data view runtime field is used in the filter (#288946) (#289256)

# Backport

This will backport the following commits from `main` to `8.19`:
- [[Security Solution] Fix bulk close returning 0 updated when a data
view runtime field is used in the filter
(#288946)](#288946)

<!--- Backport version: 12.0.0 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sorenlouv/backport)

<!--BACKPORT [{"author":{"name":"Jon
Wålstedt","email":"jon.walstedt@elastic.co"},"sourceCommit":{"committedDate":"2026-09-03T15:05:54Z","message":"[Security
Solution] Fix bulk close returning 0 updated when a data view runtime
field is used in the filter (#288946)\n\n## Summary\n\nWhen **\"Select
all\" → Close selected** is used for bulk-closing alerts,\nthe
query-based close path called `updateAlertStatus`
without\n`runtimeFields`. The alerts index has `dynamic: false`, so
fields not in\nthe static index mapping — e.g. ES|QL detection rule
output columns made\nsearchable via data view runtime fields — cannot be
resolved by the\n`_update_by_query`, causing it to match 0 documents and
return\n`\"updated\": 0`.\n\nSingle-alert close (checkbox only, no
\"Select all\") is unaffected\nbecause it uses the ID-based path which
doesn't need field resolution.\n\n## Root cause\n\nThe alert listing
query (grid + summary panels) correctly includes\n`runtime_mappings`
sourced from `dataView.getRuntimeMappings()`. The\nbulk close request
(`POST /api/detection_engine/signals/status`) never\nforwarded
`runtime_fields`, so the server-side `_update_by_query` had no\nway to
resolve unmapped fields.\n\nThe server route already fully supports a
`runtime_fields` body\nparameter and converts it to `runtime_mappings`
on the\n`_update_by_query` — the client call site was simply never
passing it.\n\n**Before
fix**\n\n\nhttps://github.com/user-attachments/assets/ec4565f3-df31-4f16-a79a-dda133c81407\n\n\n**After
fix**\n\n\nhttps://github.com/user-attachments/assets/fc19273a-50f2-420c-8119-d631b34cb0f2\n\n\n##
Changes\n\nThread `runtimeMappings` (already computed
from\n`dataView.getRuntimeMappings()` in `alerts_table/index.tsx`) down
to the\nbulk close API call:\n\n| File | Change |\n|---|---|\n|
`alerts_table/index.tsx` | Pass `runtimeMappings` as 4th arg
to\n`useBulkActionsByTableType` |\n| `use_bulk_actions.tsx` | Accept and
forward `runtimeMappings` in\n`alertActionParams` |\n|
`use_alert_actions.tsx` | Accept `runtimeMappings`,
derive\n`runtimeFields` via `useMemo`, pass to `updateAlertStatus`
|\n\n## Test coverage\n\n- **`use_alert_actions.test.tsx`**: 3 new tests
verifying that\n`updateAlertStatus` receives `runtimeFields` derived
from\n`runtimeMappings` when `isSelectAllChecked` (query path),
`undefined`\nwhen no mappings provided, and that the query path is taken
(not\nsignalIds)\n- **`use_bulk_actions.test.tsx`**: updated existing
params test to\nassert `runtimeMappings: undefined` by default; new test
verifying\n`runtimeMappings` is forwarded to
`useBulkAlertActionItems`\n\n## Checklist\n\n- [x] Any text added
follows [EUI's
writing\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\nsentence case text and includes
[i18n\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\n-
[x]\n[Documentation](https://www.elastic.co/guide/en/security/master/index.html)\nwas
added for features that require explanation or tutorials\n- [x] [Unit or
functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere
updated or added to match the most common scenarios\n- [x] The PR
description includes the appropriate Risk by setting the\ncorrect label
under the `release_note:*` label and by linking to any\nnotifications if
necessary\n\n🤖 Generated with [Claude
Code](https://claude.com/claude-code)\n\n---------\n\nCo-authored-by:
Claude Sonnet 4.6
<noreply@anthropic.com>","sha":"9819e8399aea758a0353edd66e1a4b18c0237f89","branchLabelMapping":{"^v9.6.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","Team:
SecuritySolution","backport:all-open","v9.6.0","reviewer:libra","Team:
Security Investigations","v9.5.3"],"title":"[Security Solution] Fix bulk
close returning 0 updated when a data view runtime field is used in the
filter","number":288946,"url":"https://github.com/elastic/kibana/pull/288946","mergeCommit":{"message":"[Security
Solution] Fix bulk close returning 0 updated when a data view runtime
field is used in the filter (#288946)\n\n## Summary\n\nWhen **\"Select
all\" → Close selected** is used for bulk-closing alerts,\nthe
query-based close path called `updateAlertStatus`
without\n`runtimeFields`. The alerts index has `dynamic: false`, so
fields not in\nthe static index mapping — e.g. ES|QL detection rule
output columns made\nsearchable via data view runtime fields — cannot be
resolved by the\n`_update_by_query`, causing it to match 0 documents and
return\n`\"updated\": 0`.\n\nSingle-alert close (checkbox only, no
\"Select all\") is unaffected\nbecause it uses the ID-based path which
doesn't need field resolution.\n\n## Root cause\n\nThe alert listing
query (grid + summary panels) correctly includes\n`runtime_mappings`
sourced from `dataView.getRuntimeMappings()`. The\nbulk close request
(`POST /api/detection_engine/signals/status`) never\nforwarded
`runtime_fields`, so the server-side `_update_by_query` had no\nway to
resolve unmapped fields.\n\nThe server route already fully supports a
`runtime_fields` body\nparameter and converts it to `runtime_mappings`
on the\n`_update_by_query` — the client call site was simply never
passing it.\n\n**Before
fix**\n\n\nhttps://github.com/user-attachments/assets/ec4565f3-df31-4f16-a79a-dda133c81407\n\n\n**After
fix**\n\n\nhttps://github.com/user-attachments/assets/fc19273a-50f2-420c-8119-d631b34cb0f2\n\n\n##
Changes\n\nThread `runtimeMappings` (already computed
from\n`dataView.getRuntimeMappings()` in `alerts_table/index.tsx`) down
to the\nbulk close API call:\n\n| File | Change |\n|---|---|\n|
`alerts_table/index.tsx` | Pass `runtimeMappings` as 4th arg
to\n`useBulkActionsByTableType` |\n| `use_bulk_actions.tsx` | Accept and
forward `runtimeMappings` in\n`alertActionParams` |\n|
`use_alert_actions.tsx` | Accept `runtimeMappings`,
derive\n`runtimeFields` via `useMemo`, pass to `updateAlertStatus`
|\n\n## Test coverage\n\n- **`use_alert_actions.test.tsx`**: 3 new tests
verifying that\n`updateAlertStatus` receives `runtimeFields` derived
from\n`runtimeMappings` when `isSelectAllChecked` (query path),
`undefined`\nwhen no mappings provided, and that the query path is taken
(not\nsignalIds)\n- **`use_bulk_actions.test.tsx`**: updated existing
params test to\nassert `runtimeMappings: undefined` by default; new test
verifying\n`runtimeMappings` is forwarded to
`useBulkAlertActionItems`\n\n## Checklist\n\n- [x] Any text added
follows [EUI's
writing\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\nsentence case text and includes
[i18n\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\n-
[x]\n[Documentation](https://www.elastic.co/guide/en/security/master/index.html)\nwas
added for features that require explanation or tutorials\n- [x] [Unit or
functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere
updated or added to match the most common scenarios\n- [x] The PR
description includes the appropriate Risk by setting the\ncorrect label
under the `release_note:*` label and by linking to any\nnotifications if
necessary\n\n🤖 Generated with [Claude
Code](https://claude.com/claude-code)\n\n---------\n\nCo-authored-by:
Claude Sonnet 4.6
<noreply@anthropic.com>","sha":"9819e8399aea758a0353edd66e1a4b18c0237f89"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.6.0","branchLabelMappingKey":"^v9.6.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/288946","number":288946,"mergeCommit":{"message":"[Security
Solution] Fix bulk close returning 0 updated when a data view runtime
field is used in the filter (#288946)\n\n## Summary\n\nWhen **\"Select
all\" → Close selected** is used for bulk-closing alerts,\nthe
query-based close path called `updateAlertStatus`
without\n`runtimeFields`. The alerts index has `dynamic: false`, so
fields not in\nthe static index mapping — e.g. ES|QL detection rule
output columns made\nsearchable via data view runtime fields — cannot be
resolved by the\n`_update_by_query`, causing it to match 0 documents and
return\n`\"updated\": 0`.\n\nSingle-alert close (checkbox only, no
\"Select all\") is unaffected\nbecause it uses the ID-based path which
doesn't need field resolution.\n\n## Root cause\n\nThe alert listing
query (grid + summary panels) correctly includes\n`runtime_mappings`
sourced from `dataView.getRuntimeMappings()`. The\nbulk close request
(`POST /api/detection_engine/signals/status`) never\nforwarded
`runtime_fields`, so the server-side `_update_by_query` had no\nway to
resolve unmapped fields.\n\nThe server route already fully supports a
`runtime_fields` body\nparameter and converts it to `runtime_mappings`
on the\n`_update_by_query` — the client call site was simply never
passing it.\n\n**Before
fix**\n\n\nhttps://github.com/user-attachments/assets/ec4565f3-df31-4f16-a79a-dda133c81407\n\n\n**After
fix**\n\n\nhttps://github.com/user-attachments/assets/fc19273a-50f2-420c-8119-d631b34cb0f2\n\n\n##
Changes\n\nThread `runtimeMappings` (already computed
from\n`dataView.getRuntimeMappings()` in `alerts_table/index.tsx`) down
to the\nbulk close API call:\n\n| File | Change |\n|---|---|\n|
`alerts_table/index.tsx` | Pass `runtimeMappings` as 4th arg
to\n`useBulkActionsByTableType` |\n| `use_bulk_actions.tsx` | Accept and
forward `runtimeMappings` in\n`alertActionParams` |\n|
`use_alert_actions.tsx` | Accept `runtimeMappings`,
derive\n`runtimeFields` via `useMemo`, pass to `updateAlertStatus`
|\n\n## Test coverage\n\n- **`use_alert_actions.test.tsx`**: 3 new tests
verifying that\n`updateAlertStatus` receives `runtimeFields` derived
from\n`runtimeMappings` when `isSelectAllChecked` (query path),
`undefined`\nwhen no mappings provided, and that the query path is taken
(not\nsignalIds)\n- **`use_bulk_actions.test.tsx`**: updated existing
params test to\nassert `runtimeMappings: undefined` by default; new test
verifying\n`runtimeMappings` is forwarded to
`useBulkAlertActionItems`\n\n## Checklist\n\n- [x] Any text added
follows [EUI's
writing\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\nsentence case text and includes
[i18n\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\n-
[x]\n[Documentation](https://www.elastic.co/guide/en/security/master/index.html)\nwas
added for features that require explanation or tutorials\n- [x] [Unit or
functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere
updated or added to match the most common scenarios\n- [x] The PR
description includes the appropriate Risk by setting the\ncorrect label
under the `release_note:*` label and by linking to any\nnotifications if
necessary\n\n🤖 Generated with [Claude
Code](https://claude.com/claude-code)\n\n---------\n\nCo-authored-by:
Claude Sonnet 4.6
<noreply@anthropic.com>","sha":"9819e8399aea758a0353edd66e1a4b18c0237f89"}},{"branch":"9.5","label":"v9.5.3","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"url":"https://github.com/elastic/kibana/pull/289052","number":289052,"state":"MERGED","mergeCommit":{"sha":"49c151219bb3e1e955f8aefbe27f567a57e3775f","message":"[9.5]
[Security Solution] Fix bulk close returning 0 updated when a data view
runtime field is used in the filter (#288946) (#289052)\n\n#
Backport\n\nThis will backport the following commits from `main` to
`9.5`:\n- [[Security Solution] Fix bulk close returning 0 updated when a
data\nview runtime field is used in the
filter\n(#288946)](https://github.com/elastic/kibana/pull/288946)\n\n\n\n###
Questions ?\nPlease refer to the [Backport
tool\ndocumentation](https://github.com/sorenlouv/backport)\n\n\n\nCo-authored-by:
Jon Wålstedt <jon.walstedt@elastic.co>\nCo-authored-by: Claude Sonnet
4.6
<noreply@anthropic.com>"}},{"url":"https://github.com/elastic/kibana/pull/289051","number":289051,"branch":"9.4","state":"OPEN"}]}]
BACKPORT-->

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
jonwalstedt added a commit to jonwalstedt/kibana that referenced this pull request Sep 7, 2026
…n bulk-close route

Two tests in the existing alert_status FTR suite, both inside 'tests with
auditbeat data' (shares the index lifecycle and rule cleanup hooks):

1. Scripted runtime field via runtime_mappings: script always emits
   "scripted_match"; filter term: { alert_test_rt: "scripted_match" }.
   Without runtime_mappings reaching ES the field is unknown and the
   term filter matches 0 docs → updated = 0. updated > 0 proves the
   Painless script was actually evaluated at query time.

2. Scriptless runtime field via runtime_mappings: field process.executable
   (no script) + filter term: { "process.executable": "/usr/bin/sudo" }.
   Guards the runtime_mappings passthrough code path for the scriptless
   case — regression coverage for the original PR elastic#288946 fix, which now
   also has an ES-backed assertion.

Also drops a stale @ts-expect-error on composite type in
use_alert_actions.test.tsx — the ES client's MappingRuntimeFields does
include composite, so TypeScript never raised an error there.

Closes: elastic/security-team#19185
@jonwalstedt

Copy link
Copy Markdown
Contributor Author

Thanks for the review @radtke-vale! The gaps you found is fixed in this follow-up PR: #289549

This PR only forwarded the field name and type, which lets the server synthesise a _source reader. That works for fields whose value is copied into _source at alert creation time, but not for data view runtime fields that are computed by a Painless script at query time. The follow-up PR forwards the full field definition (type + script + format) verbatim to Elasticsearch, so the Painless script actually runs.

The group path never wired up runtime field forwarding at all. That's also fixed in the follow-up.

jonwalstedt added a commit to jonwalstedt/kibana that referenced this pull request Sep 8, 2026
…elect-all close

The previous fix (PR elastic#288946) projected dataView.getRuntimeMappings() down to
[name, type] only, because runtime_fields only accepts a name→type map. The
server then synthesised a _source[fieldName] reader per entry, which is correct
for rule-source runtime fields but wrong for data view runtime fields:

- Scripted fields: the synthesised reader reads from _source, finds nothing
  (the value is only available at ES query time via the Painless script), and
  the _update_by_query matches 0 docs → "Successfully closed 0 alerts".
- Transforming scripts: the synthesised reader returns the untransformed value,
  so the close operates on a different set than the grid displayed.

Add toBulkCloseRuntimeMappings() in a new shared helper that converts the full
MappingRuntimeFields from the data view into the new runtime_mappings body
parameter, preserving type + script + format. Composite and lookup types are
filtered out (not queryable in filter context). Bare-string scripts (data view
format) are normalised to { source } (route schema format).

Pass the result through use_alert_actions → updateAlertStatus →
updateAlertStatusByQuery → api.ts (serialised as runtime_mappings in the JSON
body). runtime_fields is kept untouched for the exceptions-flyout path.

Closes: elastic/security-team#19185
jonwalstedt added a commit to jonwalstedt/kibana that referenced this pull request Sep 8, 2026
…ons path

The group-level "Take actions → Mark as closed" path (alerts_sub_grouping →
use_group_take_action_items) called updateAlertStatus with a bare query and
no runtime fields at all. With a data view runtime field active as a column or
filter, the _update_by_query had no mapping to resolve the field, so the
filter matched 0 alerts → "Successfully closed 0 alerts" — even for
scriptless fields that worked on the select-all path after PR elastic#288946.

alerts_sub_grouping already held the page-scoped runtimeMappings prop
(MappingRuntimeFields from dataView.getRuntimeMappings()). Thread it into
takeActionParams → GroupTakeActionItems.runtimeMappings → onClickUpdate →
updateAlertStatus via toBulkCloseRuntimeMappings (converting from the data
view format to the route's runtime_mappings schema). All four onClickUpdate
call sites are updated: open, acknowledged, closed direct, and the
close-reason panel — the last one was easy to miss.

No consumer of useGroupTakeActionsItems needs changing: the runtimeMappings
comes from alerts_sub_grouping which is where the data view lives.

Closes: elastic/security-team#19185
jonwalstedt added a commit to jonwalstedt/kibana that referenced this pull request Sep 8, 2026
…n bulk-close route

Two tests in the existing alert_status FTR suite, both inside 'tests with
auditbeat data' (shares the index lifecycle and rule cleanup hooks):

1. Scripted runtime field via runtime_mappings: script always emits
   "scripted_match"; filter term: { alert_test_rt: "scripted_match" }.
   Without runtime_mappings reaching ES the field is unknown and the
   term filter matches 0 docs → updated = 0. updated > 0 proves the
   Painless script was actually evaluated at query time.

2. Scriptless runtime field via runtime_mappings: field process.executable
   (no script) + filter term: { "process.executable": "/usr/bin/sudo" }.
   Guards the runtime_mappings passthrough code path for the scriptless
   case — regression coverage for the original PR elastic#288946 fix, which now
   also has an ES-backed assertion.

Also drops a stale @ts-expect-error on composite type in
use_alert_actions.test.tsx — the ES client's MappingRuntimeFields does
include composite, so TypeScript never raised an error there.

Closes: elastic/security-team#19185
Supplementing pushed a commit to Supplementing/kibana that referenced this pull request Sep 8, 2026
…ew runtime field is used in the filter (elastic#288946)

## Summary

When **"Select all" → Close selected** is used for bulk-closing alerts,
the query-based close path called `updateAlertStatus` without
`runtimeFields`. The alerts index has `dynamic: false`, so fields not in
the static index mapping — e.g. ES|QL detection rule output columns made
searchable via data view runtime fields — cannot be resolved by the
`_update_by_query`, causing it to match 0 documents and return
`"updated": 0`.

Single-alert close (checkbox only, no "Select all") is unaffected
because it uses the ID-based path which doesn't need field resolution.

## Root cause

The alert listing query (grid + summary panels) correctly includes
`runtime_mappings` sourced from `dataView.getRuntimeMappings()`. The
bulk close request (`POST /api/detection_engine/signals/status`) never
forwarded `runtime_fields`, so the server-side `_update_by_query` had no
way to resolve unmapped fields.

The server route already fully supports a `runtime_fields` body
parameter and converts it to `runtime_mappings` on the
`_update_by_query` — the client call site was simply never passing it.

**Before fix**


https://github.com/user-attachments/assets/ec4565f3-df31-4f16-a79a-dda133c81407


**After fix**


https://github.com/user-attachments/assets/fc19273a-50f2-420c-8119-d631b34cb0f2


## Changes

Thread `runtimeMappings` (already computed from
`dataView.getRuntimeMappings()` in `alerts_table/index.tsx`) down to the
bulk close API call:

| File | Change |
|---|---|
| `alerts_table/index.tsx` | Pass `runtimeMappings` as 4th arg to
`useBulkActionsByTableType` |
| `use_bulk_actions.tsx` | Accept and forward `runtimeMappings` in
`alertActionParams` |
| `use_alert_actions.tsx` | Accept `runtimeMappings`, derive
`runtimeFields` via `useMemo`, pass to `updateAlertStatus` |

## Test coverage

- **`use_alert_actions.test.tsx`**: 3 new tests verifying that
`updateAlertStatus` receives `runtimeFields` derived from
`runtimeMappings` when `isSelectAllChecked` (query path), `undefined`
when no mappings provided, and that the query path is taken (not
signalIds)
- **`use_bulk_actions.test.tsx`**: updated existing params test to
assert `runtimeMappings: undefined` by default; new test verifying
`runtimeMappings` is forwarded to `useBulkAlertActionItems`

## Checklist

- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [x]
[Documentation](https://www.elastic.co/guide/en/security/master/index.html)
was added for features that require explanation or tutorials
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] The PR description includes the appropriate Risk by setting the
correct label under the `release_note:*` label and by linking to any
notifications if necessary

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
jonwalstedt added a commit to jonwalstedt/kibana that referenced this pull request Sep 9, 2026
…elect-all close

The previous fix (PR elastic#288946) projected dataView.getRuntimeMappings() down to
[name, type] only, because runtime_fields only accepts a name→type map. The
server then synthesised a _source[fieldName] reader per entry, which is correct
for rule-source runtime fields but wrong for data view runtime fields:

- Scripted fields: the synthesised reader reads from _source, finds nothing
  (the value is only available at ES query time via the Painless script), and
  the _update_by_query matches 0 docs → "Successfully closed 0 alerts".
- Transforming scripts: the synthesised reader returns the untransformed value,
  so the close operates on a different set than the grid displayed.

Add toBulkCloseRuntimeMappings() in a new shared helper that converts the full
MappingRuntimeFields from the data view into the new runtime_mappings body
parameter, preserving type + script + format. Composite and lookup types are
filtered out (not queryable in filter context). Bare-string scripts (data view
format) are normalised to { source } (route schema format).

Pass the result through use_alert_actions → updateAlertStatus →
updateAlertStatusByQuery → api.ts (serialised as runtime_mappings in the JSON
body). runtime_fields is kept untouched for the exceptions-flyout path.

Closes: elastic/security-team#19185
jonwalstedt added a commit to jonwalstedt/kibana that referenced this pull request Sep 9, 2026
…ons path

The group-level "Take actions → Mark as closed" path (alerts_sub_grouping →
use_group_take_action_items) called updateAlertStatus with a bare query and
no runtime fields at all. With a data view runtime field active as a column or
filter, the _update_by_query had no mapping to resolve the field, so the
filter matched 0 alerts → "Successfully closed 0 alerts" — even for
scriptless fields that worked on the select-all path after PR elastic#288946.

alerts_sub_grouping already held the page-scoped runtimeMappings prop
(MappingRuntimeFields from dataView.getRuntimeMappings()). Thread it into
takeActionParams → GroupTakeActionItems.runtimeMappings → onClickUpdate →
updateAlertStatus via toBulkCloseRuntimeMappings (converting from the data
view format to the route's runtime_mappings schema). All four onClickUpdate
call sites are updated: open, acknowledged, closed direct, and the
close-reason panel — the last one was easy to miss.

No consumer of useGroupTakeActionsItems needs changing: the runtimeMappings
comes from alerts_sub_grouping which is where the data view lives.

Closes: elastic/security-team#19185
jonwalstedt added a commit to jonwalstedt/kibana that referenced this pull request Sep 9, 2026
…n bulk-close route

Two tests in the existing alert_status FTR suite, both inside 'tests with
auditbeat data' (shares the index lifecycle and rule cleanup hooks):

1. Scripted runtime field via runtime_mappings: script always emits
   "scripted_match"; filter term: { alert_test_rt: "scripted_match" }.
   Without runtime_mappings reaching ES the field is unknown and the
   term filter matches 0 docs → updated = 0. updated > 0 proves the
   Painless script was actually evaluated at query time.

2. Scriptless runtime field via runtime_mappings: field process.executable
   (no script) + filter term: { "process.executable": "/usr/bin/sudo" }.
   Guards the runtime_mappings passthrough code path for the scriptless
   case — regression coverage for the original PR elastic#288946 fix, which now
   also has an ES-backed assertion.

Also drops a stale @ts-expect-error on composite type in
use_alert_actions.test.tsx — the ES client's MappingRuntimeFields does
include composite, so TypeScript never raised an error there.

Closes: elastic/security-team#19185
jonwalstedt added a commit to jonwalstedt/kibana that referenced this pull request Sep 9, 2026
…elect-all close

The previous fix (PR elastic#288946) projected dataView.getRuntimeMappings() down to
[name, type] only, because runtime_fields only accepts a name→type map. The
server then synthesised a _source[fieldName] reader per entry, which is correct
for rule-source runtime fields but wrong for data view runtime fields:

- Scripted fields: the synthesised reader reads from _source, finds nothing
  (the value is only available at ES query time via the Painless script), and
  the _update_by_query matches 0 docs → "Successfully closed 0 alerts".
- Transforming scripts: the synthesised reader returns the untransformed value,
  so the close operates on a different set than the grid displayed.

Add toBulkCloseRuntimeMappings() in a new shared helper that converts the full
MappingRuntimeFields from the data view into the new runtime_mappings body
parameter, preserving type + script + format. Composite and lookup types are
filtered out (not queryable in filter context). Bare-string scripts (data view
format) are normalised to { source } (route schema format).

Pass the result through use_alert_actions → updateAlertStatus →
updateAlertStatusByQuery → api.ts (serialised as runtime_mappings in the JSON
body). runtime_fields is kept untouched for the exceptions-flyout path.

Closes: elastic/security-team#19185
jonwalstedt added a commit to jonwalstedt/kibana that referenced this pull request Sep 9, 2026
…ons path

The group-level "Take actions → Mark as closed" path (alerts_sub_grouping →
use_group_take_action_items) called updateAlertStatus with a bare query and
no runtime fields at all. With a data view runtime field active as a column or
filter, the _update_by_query had no mapping to resolve the field, so the
filter matched 0 alerts → "Successfully closed 0 alerts" — even for
scriptless fields that worked on the select-all path after PR elastic#288946.

alerts_sub_grouping already held the page-scoped runtimeMappings prop
(MappingRuntimeFields from dataView.getRuntimeMappings()). Thread it into
takeActionParams → GroupTakeActionItems.runtimeMappings → onClickUpdate →
updateAlertStatus via toBulkCloseRuntimeMappings (converting from the data
view format to the route's runtime_mappings schema). All four onClickUpdate
call sites are updated: open, acknowledged, closed direct, and the
close-reason panel — the last one was easy to miss.

No consumer of useGroupTakeActionsItems needs changing: the runtimeMappings
comes from alerts_sub_grouping which is where the data view lives.

Closes: elastic/security-team#19185
jonwalstedt added a commit to jonwalstedt/kibana that referenced this pull request Sep 9, 2026
…n bulk-close route

Two tests in the existing alert_status FTR suite, both inside 'tests with
auditbeat data' (shares the index lifecycle and rule cleanup hooks):

1. Scripted runtime field via runtime_mappings: script always emits
   "scripted_match"; filter term: { alert_test_rt: "scripted_match" }.
   Without runtime_mappings reaching ES the field is unknown and the
   term filter matches 0 docs → updated = 0. updated > 0 proves the
   Painless script was actually evaluated at query time.

2. Scriptless runtime field via runtime_mappings: field process.executable
   (no script) + filter term: { "process.executable": "/usr/bin/sudo" }.
   Guards the runtime_mappings passthrough code path for the scriptless
   case — regression coverage for the original PR elastic#288946 fix, which now
   also has an ES-backed assertion.

Also drops a stale @ts-expect-error on composite type in
use_alert_actions.test.tsx — the ES client's MappingRuntimeFields does
include composite, so TypeScript never raised an error there.

Closes: elastic/security-team#19185
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport:all-open Backport to all branches that could still receive a release release_note:fix reviewer:libra PR review with Libra. This disables Claude and Scout reviewers Team: Security Investigations Security solution alert triage & investigations Team: SecuritySolution Security Solutions Team working on SIEM, Endpoint, Timeline, Resolver, etc. v8.19.22 v9.4.7 v9.5.4 v9.6.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants