fix(mapper): handle VOID entries in _ignored_source with FLS#141506
Merged
salvatore-campagna merged 12 commits intoelastic:mainfrom Feb 3, 2026
Merged
Conversation
When Field Level Security (FLS) processes _ignored_source entries from documents with synthetic source enabled, it calls decodeAsMap() to parse and potentially filter field values. However, copy_to targets are recorded in _ignored_source as VOID entries marking the field as existing but stored elsewhere (the copy_to source field). Previously, decodeAsMap() would attempt to parse these VOID entries, building a CBOR object with a field name but no value. When convertToMap tried to parse this malformed CBOR, the Jackson parser encountered an unexpected Break token (0xFF) and threw. The core fix is in nameValueToMapped(), which both encoding paths share. It now checks isDataPresent() before attempting to parse, returning null for VOID entries instead of building malformed CBOR. Both encoding paths call nameValueToMapped() through their respective decodeAsMap methods, so both are protected by the same check. The only difference is how the null propagates: 1. Coalesced path: decodeAsMap iterates over entries and skips null results, filtering VOID entries out of the returned list. 2. Legacy path: decodeAsMap returns null directly, and filterValue propagates the null to FieldSubsetReader, which drops the entry. In both cases, VOID entries carry no field data, so omitting them is correct.
romseygeek
approved these changes
Feb 2, 2026
Contributor
romseygeek
left a comment
There was a problem hiding this comment.
One small suggesetion, LGTM otherwise.
| } | ||
|
|
||
| private static MappedNameValue nameValueToMapped(NameValue nameValue) throws IOException { | ||
| if (XContentDataHelper.isDataPresent(nameValue.value()) == false) { |
Contributor
There was a problem hiding this comment.
Can this just be nameValue.hasValue() == false?
…per.isDataPresent()
salvatore-campagna
added a commit
to salvatore-campagna/elasticsearch
that referenced
this pull request
Feb 3, 2026
…#141506) * fix(mapper): handle VOID entries in _ignored_source with FLS When Field Level Security (FLS) processes _ignored_source entries from documents with synthetic source enabled, it calls decodeAsMap() to parse and potentially filter field values. However, copy_to targets are recorded in _ignored_source as VOID entries marking the field as existing but stored elsewhere (the copy_to source field). Previously, decodeAsMap() would attempt to parse these VOID entries, building a CBOR object with a field name but no value. When convertToMap tried to parse this malformed CBOR, the Jackson parser encountered an unexpected Break token (0xFF) and threw. The core fix is in nameValueToMapped(), which both encoding paths share. It now checks isDataPresent() before attempting to parse, returning null for VOID entries instead of building malformed CBOR. Both encoding paths call nameValueToMapped() through their respective decodeAsMap methods, so both are protected by the same check. The only difference is how the null propagates: 1. Coalesced path: decodeAsMap iterates over entries and skips null results, filtering VOID entries out of the returned list. 2. Legacy path: decodeAsMap returns null directly, and filterValue propagates the null to FieldSubsetReader, which drops the entry. In both cases, VOID entries carry no field data, so omitting them is correct.
Collaborator
💔 Backport failed
You can use sqren/backport to manually backport by running |
Collaborator
💔 Backport failed
You can use sqren/backport to manually backport by running |
salvatore-campagna
added a commit
to salvatore-campagna/elasticsearch
that referenced
this pull request
Feb 3, 2026
…#141506) * fix(mapper): handle VOID entries in _ignored_source with FLS When Field Level Security (FLS) processes _ignored_source entries from documents with synthetic source enabled, it calls decodeAsMap() to parse and potentially filter field values. However, copy_to targets are recorded in _ignored_source as VOID entries marking the field as existing but stored elsewhere (the copy_to source field). Previously, decodeAsMap() would attempt to parse these VOID entries, building a CBOR object with a field name but no value. When convertToMap tried to parse this malformed CBOR, the Jackson parser encountered an unexpected Break token (0xFF) and threw. The core fix is in nameValueToMapped(), which both encoding paths share. It now checks isDataPresent() before attempting to parse, returning null for VOID entries instead of building malformed CBOR. Both encoding paths call nameValueToMapped() through their respective decodeAsMap methods, so both are protected by the same check. The only difference is how the null propagates: 1. Coalesced path: decodeAsMap iterates over entries and skips null results, filtering VOID entries out of the returned list. 2. Legacy path: decodeAsMap returns null directly, and filterValue propagates the null to FieldSubsetReader, which drops the entry. In both cases, VOID entries carry no field data, so omitting them is correct.
elasticsearchmachine
pushed a commit
that referenced
this pull request
Feb 3, 2026
#141703) * fix(mapper): handle VOID entries in _ignored_source with FLS When Field Level Security (FLS) processes _ignored_source entries from documents with synthetic source enabled, it calls decodeAsMap() to parse and potentially filter field values. However, copy_to targets are recorded in _ignored_source as VOID entries marking the field as existing but stored elsewhere (the copy_to source field). Previously, decodeAsMap() would attempt to parse these VOID entries, building a CBOR object with a field name but no value. When convertToMap tried to parse this malformed CBOR, the Jackson parser encountered an unexpected Break token (0xFF) and threw. The core fix is in nameValueToMapped(), which both encoding paths share. It now checks isDataPresent() before attempting to parse, returning null for VOID entries instead of building malformed CBOR. Both encoding paths call nameValueToMapped() through their respective decodeAsMap methods, so both are protected by the same check. The only difference is how the null propagates: 1. Coalesced path: decodeAsMap iterates over entries and skips null results, filtering VOID entries out of the returned list. 2. Legacy path: decodeAsMap returns null directly, and filterValue propagates the null to FieldSubsetReader, which drops the entry. In both cases, VOID entries carry no field data, so omitting them is correct.
elasticsearchmachine
pushed a commit
that referenced
this pull request
Feb 3, 2026
#141705) * fix(mapper): handle VOID entries in _ignored_source with FLS When Field Level Security (FLS) processes _ignored_source entries from documents with synthetic source enabled, it calls decodeAsMap() to parse and potentially filter field values. However, copy_to targets are recorded in _ignored_source as VOID entries marking the field as existing but stored elsewhere (the copy_to source field). Previously, decodeAsMap() would attempt to parse these VOID entries, building a CBOR object with a field name but no value. When convertToMap tried to parse this malformed CBOR, the Jackson parser encountered an unexpected Break token (0xFF) and threw. The core fix is in nameValueToMapped(), which both encoding paths share. It now checks isDataPresent() before attempting to parse, returning null for VOID entries instead of building malformed CBOR. Both encoding paths call nameValueToMapped() through their respective decodeAsMap methods, so both are protected by the same check. The only difference is how the null propagates: 1. Coalesced path: decodeAsMap iterates over entries and skips null results, filtering VOID entries out of the returned list. 2. Legacy path: decodeAsMap returns null directly, and filterValue propagates the null to FieldSubsetReader, which drops the entry. In both cases, VOID entries carry no field data, so omitting them is correct.
lukewhiting
pushed a commit
to lukewhiting/elasticsearch
that referenced
this pull request
Feb 3, 2026
…#141506) * fix(mapper): handle VOID entries in _ignored_source with FLS When Field Level Security (FLS) processes _ignored_source entries from documents with synthetic source enabled, it calls decodeAsMap() to parse and potentially filter field values. However, copy_to targets are recorded in _ignored_source as VOID entries marking the field as existing but stored elsewhere (the copy_to source field). Previously, decodeAsMap() would attempt to parse these VOID entries, building a CBOR object with a field name but no value. When convertToMap tried to parse this malformed CBOR, the Jackson parser encountered an unexpected Break token (0xFF) and threw. The core fix is in nameValueToMapped(), which both encoding paths share. It now checks isDataPresent() before attempting to parse, returning null for VOID entries instead of building malformed CBOR. Both encoding paths call nameValueToMapped() through their respective decodeAsMap methods, so both are protected by the same check. The only difference is how the null propagates: 1. Coalesced path: decodeAsMap iterates over entries and skips null results, filtering VOID entries out of the returned list. 2. Legacy path: decodeAsMap returns null directly, and filterValue propagates the null to FieldSubsetReader, which drops the entry. In both cases, VOID entries carry no field data, so omitting them is correct.
jfreden
pushed a commit
to jfreden/elasticsearch
that referenced
this pull request
Feb 4, 2026
…#141506) * fix(mapper): handle VOID entries in _ignored_source with FLS When Field Level Security (FLS) processes _ignored_source entries from documents with synthetic source enabled, it calls decodeAsMap() to parse and potentially filter field values. However, copy_to targets are recorded in _ignored_source as VOID entries marking the field as existing but stored elsewhere (the copy_to source field). Previously, decodeAsMap() would attempt to parse these VOID entries, building a CBOR object with a field name but no value. When convertToMap tried to parse this malformed CBOR, the Jackson parser encountered an unexpected Break token (0xFF) and threw. The core fix is in nameValueToMapped(), which both encoding paths share. It now checks isDataPresent() before attempting to parse, returning null for VOID entries instead of building malformed CBOR. Both encoding paths call nameValueToMapped() through their respective decodeAsMap methods, so both are protected by the same check. The only difference is how the null propagates: 1. Coalesced path: decodeAsMap iterates over entries and skips null results, filtering VOID entries out of the returned list. 2. Legacy path: decodeAsMap returns null directly, and filterValue propagates the null to FieldSubsetReader, which drops the entry. In both cases, VOID entries carry no field data, so omitting them is correct.
salvatore-campagna
added a commit
that referenced
this pull request
Feb 6, 2026
…141506) (#141716) When Field Level Security (FLS) processes _ignored_source entries from documents with synthetic source enabled, it calls decodeAsMap() to parse and potentially filter field values. However, copy_to targets are recorded in _ignored_source as VOID entries marking the field as existing but stored elsewhere (the copy_to source field). Previously, decodeAsMap() would attempt to parse these VOID entries, building a CBOR object with a field name but no value. When convertToMap tried to parse this malformed CBOR, the Jackson parser encountered an unexpected Break token (0xFF) and threw. The core fix is in nameValueToMapped(), which both encoding paths share. It now checks isDataPresent() before attempting to parse, returning null for VOID entries instead of building malformed CBOR. Both encoding paths call nameValueToMapped() through their respective decodeAsMap methods, so both are protected by the same check. The only difference is how the null propagates: 1. Coalesced path: decodeAsMap iterates over entries and skips null results, filtering VOID entries out of the returned list. 2. Legacy path: decodeAsMap returns null directly, and filterValue propagates the null to FieldSubsetReader, which drops the entry. In both cases, VOID entries carry no field data, so omitting them is correct. Backport note: On 8.19, FieldSubsetReader.java requires an explicit null check for the return value of decodeAsMap(). On main, this class was refactored with an IgnoredSourceFormat parameter that handles nulls through a different code path, so the null check is not needed there.
salvatore-campagna
added a commit
to salvatore-campagna/elasticsearch
that referenced
this pull request
Mar 23, 2026
…#141506) Backport of elastic#141506 to 9.1. When Field Level Security is active on an index using synthetic source, fields with copy_to targets are recorded in _ignored_source as VOID entries. FieldSubsetReader attempted to parse these entries as CBOR, causing search failures with an unexpected Break token (0xFF). The fix checks hasValue() in decodeAsMap before parsing and returns null for VOID entries. FieldSubsetReader now skips null entries gracefully. Also unmutes the test from elastic#142341.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When Field Level Security (FLS) is active on an index using synthetic source, any field with a
copy_totarget causes search requests to fail with:The failure path is:
copy_totarget fields are recorded in_ignored_sourceas VOID entries (XContentDataHelper.voidValue()): markers indicating the field exists but its data is stored elsewhere (in thecopy_tosource field).FieldSubsetReaderiterates over_ignored_sourcestored fields and callsIgnoredSourceFieldMapper.decodeAsMapfor each entry to decide which fields to keep or strip based on FLS access permissions.decodeAsMapbuilds a CBOR object containing the field name but writes no value for VOID entries (XContentDataHelper.decodeAndWriteis a no-op). WhenXContentHelper.convertToMaptries to parse this malformed CBOR, the Jackson CBOR parser encounters an unexpected Break token (0xFF) and throws.Fix
The core fix is in
nameValueToMapped(), which both encoding paths share. It now checksXContentDataHelper.isDataPresent(nameValue.value())before attempting to parse, returningnullfor VOID entries instead of building malformed CBOR.Both encoding paths call
nameValueToMapped()through their respectivedecodeAsMapmethods, so both are protected by the same check. The only difference is how thenullpropagates:Coalesced path:
CoalescedIgnoredSourceEncoding.decodeAsMapiterates over entries and skipsnullresults, filtering VOID entries out of the returned list.Legacy path:
LegacyIgnoredSourceEncoding.decodeAsMapreturnsnulldirectly, andfilterValuepropagates thenulltoFieldSubsetReader, which drops the entry.In both cases, VOID entries carry no field data, so omitting them is correct.
Tests
testCoalescedDecodeAsMapReturnsNullForVoidEntry: unit test verifyingCoalescedIgnoredSourceEncoding.decodeAsMapfilters out VOID entries, returning an empty list.testLegacyDecodeAsMapReturnsNullForVoidEntry: unit test verifyingLegacyIgnoredSourceEncoding.decodeAsMapreturnsnullfor a VOID entry.testSyntheticSourceWithCopyToAndFLSCoalesced: integration test exercisingFieldSubsetReaderwithcopy_to, synthetic source, and FLS using the coalesced encoding format.testSyntheticSourceWithCopyToAndFLSLegacy: same integration test but using the legacy encoding format (pre-IGNORED_SOURCE_COALESCED_ENTRIES_WITH_FF).copy_to, and one covering theskip_ignored_source_readworkaround.