8000 fix(api): validate index-creation, mapping, alias and template requests like ES by amartya1523 · Pull Request #1870 · xerj-org/xerj · GitHub
[go: up one dir, main page]

Skip to content

fix(api): validate index-creation, mapping, alias and template requests like ES - #1870

Merged
xerj-org merged 1 commit into
xerj-org:mainfrom
amartya1523:fix/index-admin-validation
Oct 20, 2026
Merged

xerj-org merged 1 commit into
xerj-org:mainfrom
amartya1523:fix/index-admin-validation

Conversation

@amartya1523
Copy link
Copy Markdown
Contributor

AI-assisted change, run and reviewed by @amartya1523.

Burns down 17 lines of the catch-lenient.txt ratchet from #1583 (151 → 134). These are index-admin requests that Elasticsearch 8.13 refuses with a 400 and XERJ accepted with a 200, leaving the bad state behind (an index with an empty-named field, an alias named like an index, …).

Independent of my other catch-lenient PRs (#1824, #1854, #1859, #1861): different handlers. Only catch-lenient.txt overlaps, so the later merge needs a trivial rebase there.

What this changes, and why

All checks run before anything is created or attached, so a refusal leaves nothing behind.

Request ES 8.13 response now returned
create-index body whose mappings / settings / aliases is not an object, or mappings.properties is not a map 400
a field named "" (create index, put mapping) 400 mapper_parsing_exception caused by illegal_argument_exception "field name cannot be an empty string"
soft_deletes.enabled: false (any spelling) 400 IAE "Creating indices with soft-deletes disabled is no longer supported. …"
index.mode: lookup with number_of_shards ≠ 1 400 IAE "index with [lookup] mode must have [index.number_of_shards] set to 1 or unset; provided 2"
ignore_above < 0 on keyword / flattened / wildcard 400 "[ignore_above] must be positive, got [-2]"
index.mapping.ignore_above < 0 400 IAE "Failed to parse value [-1] for setting [index.mapping.ignore_above] must be >= 0"
dense_vector bbq_* with element_type byte/bit, or index_options with index: false 400 (bfloat16 stays allowed)
alias is_write_index: "true", routing: true 400 IAE "Unsupported String type value [true] for field [is_write_index] in alias [test_alias]" / "Unsupported boolean type value …"
PUT /i/_alias/test_*, or an alias named like an existing index 400 invalid_alias_name_exception
alias body with an unknown field / array routing 400 "Unsupported field [foo]" / "… Unexpected token [START_ARRAY]"
legacy template without index_patterns 400 action_request_validation_exception "Validation Failed: 1: index patterns are missing;"
GET /_foo 400 invalid_index_name_exception (was 404)

Care taken against false positives:

  • Alias collision: only a concrete index of that name blocks an alias. Re-putting an existing alias (to change its filter or routing) still works, because Engine::get_index also resolves aliases and is not used alone for this check.
  • GET /<index>: only a leading _ (other than _all) is refused; a leading - in a list is an exclusion.
  • Alias bodies: I surveyed every put_alias body and create-index aliases spec in the YAML corpus (filter, routing, is_write_index); all remain accepted.
  • A new es_bad_request builds ES-shaped 400s for refusals whose type or caused_by no XerjError variant renders.

Evidence

Full ES-YAML suite against a node built from this commit. The runner reported exactly these 17 entries stale, with 0 failed:

  • indices.create/10_basic.yml ×5: invalid mappings, without soft deletes, lookup index with two shards, poorly formatted request, unsupported value type
  • indices.get/10_basic.yml ×1
  • indices.put_alias/10_basic.yml ×3
  • indices.put_mapping/10_basic.yml ×1
  • indices.put_template/10_basic.yml ×1 (without index_patterns)
  • search/550_ignore_above_invalid.yml ×4
  • 46_knn_search_bbq_ivf.yml ×2 (both copies)

With them deleted and enforced:

$ es-yaml-runner --dir tests/es-compat-yaml/yaml
1402 passed · 0 failed · 10 skipped · 1412 total
134 cases ran catch-lenient (an expected error XERJ does not raise yet; see catch-lenient.txt)

Checks

  • cargo fmt --all -- --check (exit 0)
  • Scoped release build: cargo build --release -p xerj-server -p es-yaml-runner
  • ES-YAML suite at 0 failed (above). The node ran with [limits] disk_flood_stage_percent = 0 because this machine's disk is above the 95% flood stage.
  • Conformance coverage: the 17 existing upstream YAML cases, now strict
  • No new unit tests: the upstream YAML cases are the regression tests, run against the real binary
  • Docs: none describe the lenient behaviour

Not run: cargo test -p xerj-api (release/fat-LTO test binaries do not fit this machine's free disk; CI runs them) and clippy.

Left in the list: indices.put_template/10_basic.yml · Put template create. The runner does not forward the create parameter (tests/es-compat-yaml/src/main.rs), so it needs a runner change first.

Provenance

  • Written by: AI-assisted, run by @amartya1523, who has reviewed it.
  • Verified (commands run, output observed): everything under Evidence and Checks, on macOS aarch64, rustc 1.99.1.
  • Assumed (not tested): ES's exact wording for the malformed-section and bbq-element-type messages. The YAML only checks for a 400 (bad_request) there, and I did not compare those strings against a live ES.

…ts like ES

Motivation:
17 `catch-lenient.txt` lines (xerj-org#1583) are index-admin requests that
Elasticsearch 8.13 refuses with a 400 and XERJ accepted with a 200,
leaving the bad state behind: a create-index body whose `mappings`,
`properties`, `settings` or `aliases` is not an object, an empty field
name, `soft_deletes.enabled: false`, a `lookup` index with more than one
shard, a negative `ignore_above` (mapping or `index.mapping` setting),
`bbq_*` vector options on byte/bit vectors or with `index: false`,
alias values of the wrong type, an alias named like an index or with
forbidden characters, unknown alias body fields, a legacy template
without `index_patterns`, and `GET /_foo`.

Root cause:
`create_index` validated only the index name and field types;
`put_alias`, `put_legacy_template` and `get_index_inner` validated
nothing of this.

Fix (es_compat.rs), all before anything is created or attached:
- `create_index_body_refusal`: section shapes, soft_deletes, lookup
  shards, `index.mapping.ignore_above`, alias value types; settings read
  in every spelling ES accepts (`create_setting`).
- `empty_field_name_refusal` (create_index and put_mapping):
  `mapper_parsing_exception` caused by `illegal_argument_exception`.
- `validate_properties`: negative `ignore_above` on keyword / flattened /
  wildcard; dense_vector `index_options` with `index: false`, and `bbq_*`
  on byte/bit (bfloat16 stays allowed).
- `put_alias`: forbidden characters, a name that is a concrete index (an
  existing alias of that name can still be re-put), unknown body fields,
  non-scalar routing, wrong value types.
- `put_legacy_template`: `index patterns are missing`.
- `get_index_inner`: a `_`-prefixed name other than `_all` is
  `invalid_index_name_exception` (a leading `-` is an exclusion, left
  alone).
- `es_bad_request`: a 400 in ES's error shape for refusals whose type or
  `caused_by` no `XerjError` variant renders.

Evidence:
- Full ES-YAML suite against a node built from this commit: 0 failed,
  and exactly these 17 catch-lenient entries reported stale; with them
  deleted and enforced: `1402 passed · 0 failed · 10 skipped`,
  catch-lenient 151 -> 134.
@cla-bot
cla-bot Bot commented Oct 19, 2026
Copy link
Copy Markdown

Thanks for this contribution — we'd like to merge it.

Before we can, we need a signed Contributor License Agreement from: @amartya1523

It is a one-time thing per contributor, not per pull request. Read CLA.md, then open a small pull request adding your GitHub username to .contributors — that pull request is your signature, since it is made from your own account.

Once it is merged, comment @cla-bot check here and this check will go green. If you believe you are already covered, comment @cla-bot check and it will re-verify.

@xerj-org
Copy link
Copy Markdown
Owner

@cla-bot check

@cla-bot cla-bot Bot added the cla-signed label Oct 20, 2026
@cla-bot
cla-bot Bot commented Oct 20, 2026
Copy link
Copy Markdown

Re-checking the CLA status for every commit author on this pull request…

@xerj-org
xerj-org merged commit 4df4eba into xerj-org:main Oct 20, 2026
22 of 23 checks passed
@xerj-org
Copy link
Copy Markdown
Owner

Xerj Merger made the fix: index-admin requests that Elasticsearch 8.13 refuses are now refused by XERJ too, before anything is created: malformed create-index bodies, empty field names, soft_deletes disabled, lookup-mode indices with more than one shard, negative ignore_above, invalid bbq dense_vector options, bad alias bodies and alias names that collide with an index, legacy templates without index_patterns, and GET /_foo. That burns 17 lines off catch-lenient.txt (151 to 134). Squash-merged on head 6d1e1c6 after Build + Test passed. Thanks @amartya1523!

amartya1523 added a commit to amartya1523/xerj that referenced this pull request Oct 20, 2026
Motivation:
20 `catch-lenient.txt` lines (xerj-org#1583) are aggregation requests that
Elasticsearch 8.13 refuses with a 400 at parse time and XERJ answered
200, ignoring the bad value or returning an empty or defaulted result:
a negative cardinality `precision_threshold` or unknown
`execution_hint`, `filters` with no filters, a non-object `meta`, an
`ip_prefix` `prefix_length` out of range, a non-positive
`median_absolute_deviation` `compression`, unknown tdigest execution
hints, `percentile_ranks` without usable `values`, a `terms` with
neither `field` nor `script`, `random_sampler` misuse, and `time_series`
under `global` or next to an aggregation incompatible with time-series
execution.

Root cause:
None of these parameters or placements were validated; the executors
treat each malformed shape as "use a default" or "nothing to return".

Fix:
`find_invalid_agg_params` (es_compat.rs), a request-time walk chained
after `find_invalid_agg_order_path` (xerj-org#1283) at its four call sites
(`_search`, `_msearch`, `_search/template`, `_msearch/template`), with
ES 8.13's messages. Only checks that need no mapping are made: e.g.
`avg` on a keyword field (which needs the field type) is left alone.

Evidence:
- Full ES-YAML suite against a node built from this commit: 0 failed,
  and exactly 20 catch-lenient entries reported stale; with them deleted
  and enforced: `1404 passed · 0 failed · 10 skipped` on current main
  (after xerj-org#1854 and xerj-org#1870), catch-lenient 128 -> 108.
- Unit tests pin the valid shapes the suite relies on (the five
  cardinality hints, `default`/`high_accuracy`, random_sampler 0.1 and
  1.0, time_series with date_histogram, terms with only a script, an
  incompatible type in a request without time_series) and each message.
xerj-org pushed a commit that referenced this pull request Oct 20, 2026
#1861)

Motivation:
20 `catch-lenient.txt` lines (#1583) are aggregation requests that
Elasticsearch 8.13 refuses with a 400 at parse time and XERJ answered
200, ignoring the bad value or returning an empty or defaulted result:
a negative cardinality `precision_threshold` or unknown
`execution_hint`, `filters` with no filters, a non-object `meta`, an
`ip_prefix` `prefix_length` out of range, a non-positive
`median_absolute_deviation` `compression`, unknown tdigest execution
hints, `percentile_ranks` without usable `values`, a `terms` with
neither `field` nor `script`, `random_sampler` misuse, and `time_series`
under `global` or next to an aggregation incompatible with time-series
execution.

Root cause:
None of these parameters or placements were validated; the executors
treat each malformed shape as "use a default" or "nothing to return".

Fix:
`find_invalid_agg_params` (es_compat.rs), a request-time walk chained
after `find_invalid_agg_order_path` (#1283) at its four call sites
(`_search`, `_msearch`, `_search/template`, `_msearch/template`), with
ES 8.13's messages. Only checks that need no mapping are made: e.g.
`avg` on a keyword field (which needs the field type) is left alone.

Evidence:
- Full ES-YAML suite against a node built from this commit: 0 failed,
  and exactly 20 catch-lenient entries reported stale; with them deleted
  and enforced: `1404 passed · 0 failed · 10 skipped` on current main
  (after #1854 and #1870), catch-lenient 128 -> 108.
- Unit tests pin the valid shapes the suite relies on (the five
  cardinality hints, `default`/`high_accuracy`, random_sampler 0.1 and
  1.0, time_series with date_histogram, terms with only a script, an
  incompatible type in a request without time_series) and each message.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

0