Skip to content

[9.4] [Entity Analytics][Leads generation] Improve error states for ES index-level permission denials (#265956)#266434

Merged
kibanamachine merged 1 commit intoelastic:9.4from
kibanamachine:backport/9.4/pr-265956
Apr 29, 2026
Merged

[9.4] [Entity Analytics][Leads generation] Improve error states for ES index-level permission denials (#265956)#266434
kibanamachine merged 1 commit intoelastic:9.4from
kibanamachine:backport/9.4/pr-265956

Conversation

@kibanamachine
Copy link
Copy Markdown
Contributor

Backport

This will backport the following commits from main to 9.4:

Questions ?

Please refer to the Backport tool documentation

…x-level permission denials (elastic#265956)

## Summary

This PR improves the error handling in the Lead Generation feature when
Elasticsearch index-level permissions are missing. Previously,
`security_exception` errors from ES were silently swallowed, resulting
in either empty data responses (misleading 200s) or unhelpful 500
errors. After this change, those errors propagate as proper HTTP 403
responses with an actionable message.

---

## Background

The Lead Generation feature stores leads in
`.entity-analytics.entity-leads-*` indices. Two separate permission
layers protect these APIs:

1. **Kibana feature privileges** (`securitySolution` +
`${APP_ID}-entity-analytics`) — enforced by the Kibana framework before
the handler runs. Missing this results in a Kibana-level 403.
2. **Elasticsearch index-level privileges** — enforced by ES when
`esClient.asCurrentUser` executes a query. Missing `read`/`write` access
on the leads indices causes ES to return a `security_exception` with
status 403.

Layer 1 was already correctly configured on all routes. Layer 2 was
silently absorbed.

---

## What was broken

In `lead_data_client.ts`, the `findLeads`, `getStatus`, and
`createLeads` methods all had catch blocks that swallowed every error:

```typescript
// findLeads (before)
} catch (e) {
  // checked only for index_not_found_exception; security_exception was also silently absorbed
  logger.error(`Unable to find leads due to error: ${e}`);
  return { leads: [], total: 0, page, perPage }; //  misleading 200 OK
}
```

A user without `.entity-analytics.entity-leads-*` read access would get
back an empty list with a 200 OK instead of a 403 "access denied"
response. For `createLeads`, the write would silently fail with only a
log warning.

### API flow before fix

```
GET /internal/entity_analytics/leads

returns { leads: [], total: 0 }   200 OK with empty data
```

---

### API flow after fix

```
GET /internal/entity_analytics/leads
transformError -> HTTP 403
- > frontend useQuery onError → toast: "security_exception: indices:data/read/search (...)
    is unauthorized for user [...]"
```

`index_not_found_exception` continues to be swallowed and returns an
empty result, which is the intended behaviour before the indices are
first created.

---

## Testing steps (before fix)

1. Create a Kibana role with `securitySolution` feature access but
**no** access to `.entity-analytics.entity-leads-*` ES indices.
2. Log in as a user with that role.
3. `GET /internal/entity_analytics/leads` — returns `200 { leads: [],
total: 0 }` instead of `403`.
4. `GET /internal/entity_analytics/leads/status` — returns `200 {
isEnabled: false, indexExists: false, ... }` instead of `403`.

## Testing steps (after fix)

Same setup:
3. `GET /internal/entity_analytics/leads` -> `403 security_exception:
indices:data/read/search is unauthorized`
4. `GET /internal/entity_analytics/leads/status` -> `403
security_exception: ...`
5. UI: toast notification displays the permission error rather than
showing empty leads.

---------

Co-authored-by: Ying <ying.mao@elastic.co>
(cherry picked from commit 812db45)
@kibanamachine
Copy link
Copy Markdown
Contributor Author

kibanamachine commented Apr 29, 2026

💛 Build succeeded, but was flaky

Failed CI Steps

Metrics [docs]

✅ unchanged

History

cc @abhishekbhatia1710

@kibanamachine kibanamachine merged commit 53d9f97 into elastic:9.4 Apr 29, 2026
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport This PR is a backport of another PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants