[Security Solution] Fix bulk close returning 0 updated when a data view runtime field is used in the filter - #288946
Conversation
There was a problem hiding this comment.
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
|
Pinging @elastic/security-solution (Team: SecuritySolution) |
kelvtanv
left a comment
There was a problem hiding this comment.
thanks for fixing this!
…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>
6d09887 to
cd5b6af
Compare
💛 Build succeeded, but was flaky
Failed CI StepsMetrics [docs]Async chunks
Unknown metric groupsshared chunks total size
total optimizer output size
History
cc @jonwalstedt |
|
Starting backport for target branches: 8.19, 9.4, 9.5 |
💔 Some backports could not be created
Note: Successful backport PRs will be merged automatically after passing CI. Manual backportTo create the backport manually run: Questions ?Please refer to the Backport tool documentation |
…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
left a comment
There was a problem hiding this comment.
Libra found 1 issue.
Generated by Libra
| 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]); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
@jonwalstedt was it a deliberate decision to support only _source backed runtime fields?
For the direct source keyword fields the fix worked for me:

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
…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>
💚 All backports created successfully
Note: Successful backport PRs will be merged automatically after passing CI. Questions ?Please refer to the Backport tool documentation |
…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>
|
@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: |
…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>
…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
|
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 The group path never wired up runtime field forwarding at all. That's also fixed in the follow-up. |
…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
…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
…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
…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>
…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
…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
…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
…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
…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
…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


Summary
When "Select all" → Close selected is used for bulk-closing alerts, the query-based close path called
updateAlertStatuswithoutruntimeFields. The alerts index hasdynamic: 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_mappingssourced fromdataView.getRuntimeMappings(). The bulk close request (POST /api/detection_engine/signals/status) never forwardedruntime_fields, so the server-side_update_by_queryhad no way to resolve unmapped fields.The server route already fully supports a
runtime_fieldsbody parameter and converts it toruntime_mappingson 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 fromdataView.getRuntimeMappings()inalerts_table/index.tsx) down to the bulk close API call:alerts_table/index.tsxruntimeMappingsas 4th arg touseBulkActionsByTableTypeuse_bulk_actions.tsxruntimeMappingsinalertActionParamsuse_alert_actions.tsxruntimeMappings, deriveruntimeFieldsviauseMemo, pass toupdateAlertStatusTest coverage
use_alert_actions.test.tsx: 3 new tests verifying thatupdateAlertStatusreceivesruntimeFieldsderived fromruntimeMappingswhenisSelectAllChecked(query path),undefinedwhen no mappings provided, and that the query path is taken (not signalIds)use_bulk_actions.test.tsx: updated existing params test to assertruntimeMappings: undefinedby default; new test verifyingruntimeMappingsis forwarded touseBulkAlertActionItemsChecklist
release_note:*label and by linking to any notifications if necessary🤖 Generated with Claude Code