Skip to content

Comments

Mark empty _terms_enum results due to DLS as incomplete (#91720)#2

Open
MitchLewis930 wants to merge 1 commit intopr_012_beforefrom
pr_012_after
Open

Mark empty _terms_enum results due to DLS as incomplete (#91720)#2
MitchLewis930 wants to merge 1 commit intopr_012_beforefrom
pr_012_after

Conversation

@MitchLewis930
Copy link

@MitchLewis930 MitchLewis930 commented Jan 29, 2026

PR_012

Summary by CodeRabbit

  • Enhancements

    • _terms_enum search results now properly mark results as incomplete when Document Level Security or Field Level Security restrictions prevent access to all matching terms.
  • Tests

    • Updated verification tests for security-restricted search result behavior.
  • Documentation

    • Added changelog entry documenting the enhancement.

✏️ Tip: You can customize this high-level summary in your review settings.

)

Today `_terms_enum` returns empty results for indices with document level
security. Elasticsearch should return some hint in case the user hits empty
results due to DLS limitation so the caller (ie. Kibana) can fall back to other
strategies or notify the user with some appropriate error message.

This changes the behaviour of the NodeTransportHandler so that it returns a
NodeTermsEnumResponse with an error indication. The resulting API response will
flag the enum as "incomplete" and list the error in the shard errors section.
Clients can choose to react to this in the appropriate way.

Closes elastic#88321
@coderabbitai
Copy link

coderabbitai bot commented Jan 29, 2026

📝 Walkthrough

Walkthrough

The changes enhance the _terms_enum action's security handling by implementing explicit behavior when Document Level Security (DLS) or Field Level Security (FLS) restrictions deny access. Permission checks now immediately respond with empty, incomplete results and clear error messaging, with corresponding test updates to validate this behavior.

Changes

Cohort / File(s) Summary
Documentation
docs/changelog/91720.yaml
Adds changelog entry documenting enhancement to mark empty _terms_enum results as incomplete when restricted by DLS.
Permission Handling
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/termsenum/action/TransportTermsEnumAction.java
Separates combined permission check into distinct steps: access denial immediately returns NodeTermsEnumResponse with empty terms and DLS/FLS restriction message; shard filtering is now independent.
Test Updates
x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/terms_enum/10_basic.yml
Updates test assertions from single length check to comprehensive verification: expects length: 0, complete: false, _shards.failed: 1, and broadcast_shard_operation_failed_exception with DLS/FLS restriction details.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 When terms come asking what they can see,
DLS whispers "that's not meant for thee!"
We mark results incomplete with care,
Empty but honest, forever fair,
Security dancing through the air! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly summarizes the main change: marking empty _terms_enum results due to DLS as incomplete, which aligns with the core modification across all three files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/termsenum/action/TransportTermsEnumAction.java`:
- Around line 718-734: In TransportTermsEnumAction (inside the shard iteration
where canAccess(shardId, request, frozenLicenseState, threadContext) is checked)
add an immediate return after calling listener.onResponse(...) so the
method/loop exits and no further responses are sent; also update the nearby
comment above the canMatchShard(shardId, request) check (currently mentioning
"Permission denied") to accurately reflect that this branch only handles index
filter/match failures. Ensure the change is made where the NodeTermsEnumResponse
is constructed and where request.remove(shardId) is invoked so no duplicate
listener responses occur.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 91d69d9 and 635a4fe.

📒 Files selected for processing (3)
  • docs/changelog/91720.yaml
  • x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/termsenum/action/TransportTermsEnumAction.java
  • x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/terms_enum/10_basic.yml
🔇 Additional comments (5)
docs/changelog/91720.yaml (1)

1-6: LGTM!

The changelog entry is well-structured and accurately describes the enhancement. The summary clearly communicates the change, and the issue reference links back to the original feature request.

x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/terms_enum/10_basic.yml (4)

551-561: Test assertions correctly validate the new DLS restriction behavior.

The updated assertions properly verify that when a user with partial DLS access (dls_some_user) attempts to enumerate terms:

  • Empty results are returned (terms: 0)
  • The response is marked as incomplete (complete: false)
  • A shard failure is reported with the expected error message

This aligns well with the implementation changes in TransportTermsEnumAction.java.


563-570: FLS behavior correctly differs from DLS behavior.

The fls_user test correctly expects complete: true and _shards.failed: 0 because Field Level Security (FLS) works differently from Document Level Security (DLS). FLS restricts field visibility rather than document access, so the operation completes successfully but returns no terms because the foo field is excluded from the user's view.


622-633: API key test assertions are consistent with the new behavior.

The test correctly verifies that an API key with restrictive DLS permissions (dls_all_user_bad_key) receives incomplete results with the expected shard failure.


654-664: Bounded API key permissions test is correctly updated.

The test validates that even when an API key has permissive DLS settings, it remains bounded by the owner user's (dls_some_user) restrictions, resulting in incomplete results with shard failures.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +718 to 734
if (canAccess(shardId, request, frozenLicenseState, threadContext) == false) {
listener.onResponse(
new NodeTermsEnumResponse(
request.nodeId(),
Collections.emptyList(),
"cannot execute [_terms_enum] request on index ["
+ shardId.getIndexName()
+ "] due to "
+ "DLS/FLS security restrictions.",
false
)
);
}
if (canMatchShard(shardId, request) == false) {
// Permission denied or can't match, remove shardID from request
request.remove(shardId);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Missing return after early failure response causes undefined behavior.

When canAccess() returns false, the code sends a response to the listener but does not exit the loop or method. Execution continues to canMatchShard() and potentially sends additional responses. This leads to:

  1. Multiple responses sent to the same ActionListener
  2. If multiple shards fail access checks, multiple error responses are sent
  3. After the loop, another response may be sent at line 737 or via the executor at line 746
🐛 Proposed fix: Add return statement after early failure response
             if (canAccess(shardId, request, frozenLicenseState, threadContext) == false) {
                 listener.onResponse(
                     new NodeTermsEnumResponse(
                         request.nodeId(),
                         Collections.emptyList(),
                         "cannot execute [_terms_enum] request on index ["
                             + shardId.getIndexName()
                             + "] due to "
                             + "DLS/FLS security restrictions.",
                         false
                     )
                 );
+                return;
             }
             if (canMatchShard(shardId, request) == false) {
-                // Permission denied or can't match, remove shardID from request
+                // Can't match index filter, remove shardID from request
                 request.remove(shardId);
             }

Additionally, the comment on line 732 mentions "Permission denied" but that condition is now handled separately above. Consider updating the comment to only reference the index filter check.

🤖 Prompt for AI Agents
In
`@x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/termsenum/action/TransportTermsEnumAction.java`
around lines 718 - 734, In TransportTermsEnumAction (inside the shard iteration
where canAccess(shardId, request, frozenLicenseState, threadContext) is checked)
add an immediate return after calling listener.onResponse(...) so the
method/loop exits and no further responses are sent; also update the nearby
comment above the canMatchShard(shardId, request) check (currently mentioning
"Permission denied") to accurately reflect that this branch only handles index
filter/match failures. Ensure the change is made where the NodeTermsEnumResponse
is constructed and where request.remove(shardId) is invoked so no duplicate
listener responses occur.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant