Mercurial > prosody-modules
view mod_xmllang_check/xml-lang-enforcement-findings.md @ 6562:5da6fb562df9 default tip
mod_unified_push: Fix push error handling (fixes #2000)
Use the error object that send_iq() passes
as an argument to it's reject callback instead of attempting
and failing to do the parsing in the callback itself.
| author | kmq |
|---|---|
| date | Mon, 06 Jul 2026 14:23:57 +0200 |
| parents | 4ddb5c69bc89 |
| children |
line wrap: on
line source
# XMPP `xml:lang` Enforcement Findings (ejabberd + xmpp) ## Scope This documents how ProcessOne `xmpp` + `ejabberd` handle invalid `xml:lang`, including: - where validation is applied, - when the stream is closed vs stanza-level error, - enforcement start date/commit, - likely first `ejabberd` release containing it, - exhaustive built-in element set validated with `xmpp_lang:check`. ## Validation Mechanism - Validator function: `xmpp_lang:check/1` - File: [`xmpp/src/xmpp_lang.erl`](https://github.com/processone/xmpp/blob/master/src/xmpp_lang.erl) - Behavior: returns input if valid, throws `{bad_lang, Value}` if invalid. - Parser/grammar backend: - [`xmpp/c_src/xmpp_lang.rl`](https://github.com/processone/xmpp/blob/master/c_src/xmpp_lang.rl) - [`xmpp/c_src/lang.rl`](https://github.com/processone/xmpp/blob/master/c_src/lang.rl) - Grammar is enforced in NIF (`xmpp_lang`), from generated Ragel rules. ## How Validation Is Applied Validation is explicit per codec field, not global for every XML attribute. The source of truth is `xmpp/specs/xmpp_codec.spec`, where `xml:lang` attrs with `dec = {xmpp_lang, check, []}` are validated. ### Exhaustive built-in element set validated for `xml:lang` From `xmpp/specs/xmpp_codec.spec`: - `stream_start` - `iq` - `message` - `message_body` - `message_subject` - `presence` - `presence_status` - `error_text` - `stream_error_text` - `sasl_failure_text` - `disco_identity` - `adhoc_command` - `jingle_reason_text` - `jingle_ft_desc` - `report_text` Notes: - This is exhaustive for built-in generated codec elements. - Unknown or ignored extension elements are not necessarily validated unless their decoder explicitly uses `xmpp_lang:check`. ## Runtime Behavior: Disconnect vs Non-Disconnect Inbound processing happens in `xmpp_stream_in`. ### Stream start (`<stream:stream ...>`) path - Event: `xmlstreamstart`. - Decode failures (`{xmpp_codec, Why}`), including bad `xml:lang` on stream start, are converted to `stream:error` `invalid-xml`. - Sending a `#stream_error{}` triggers stream close. Consequence: - Invalid `xml:lang` on top stream element causes stream error + disconnect. ### Stanza/element (`xmlstreamelement`) path - Event: `xmlstreamelement`. - Decode failures go through `process_invalid_xml/3`. - If incoming element is a stanza (`iq/message/presence`), server sends stanza `bad-request` and does **not** close stream. - For some non-stanza protocol elements (e.g., SASL auth/response/abort), server sends protocol failure (e.g., SASL malformed-request), not stream close. Consequence: - Invalid `xml:lang` on validated stanza-level or validated child elements is generally stanza/protocol-level failure, not connection drop. ## Extra Policy: Long Stream `xml:lang` `xmpp_stream_in` has a separate rule for client stream start: - if `size(Lang) > 35` on stream start, send `policy-violation` stream error. - this causes disconnect (because it is a stream error). ## Enforcement Start (Git) ### Core enforcement commit in `xmpp` - Commit: `f6cb9a194126049fa0d562d720fd4489e7446be2` - Date: `2018-11-23T11:50:39+03:00` - Subject: `Validate xml:lang attribute according to RFC5234` ### Release lineage in `xmpp` - First tag containing this commit: `1.2.6` - Tags containing it include: `1.2.6`, `1.2.7`, `1.2.8`, and later. ## Likely First `ejabberd` Release Including Enforcement Based on `ejabberd` tag -> `xmpp` dependency tag in `rebar.config`: - `ejabberd 18.09` -> `xmpp 1.2.5` (before enforcement) - `ejabberd 18.12` -> `xmpp 1.2.7` (after enforcement) - `ejabberd 18.12.1` -> `xmpp 1.2.8` (after enforcement) Likely first `ejabberd` release with this enforcement: **`18.12`**. ## Test Evidence in `ejabberd` `ejabberd` suite contains explicit bad-lang stream test: - `test_connect_bad_lang` expects `#stream_error{reason = 'invalid-xml'}` then stream end. - File: [`ejabberd/test/ejabberd_SUITE.erl`](https://github.com/processone/ejabberd/blob/master/test/ejabberd_SUITE.erl) ## Practical Compatibility Guidance for Alternate Implementations To emulate this behavior closely: 1. Validate `xml:lang` only on known fields/elements (codec-defined), not universally on every attribute. 2. If stream-start `xml:lang` is invalid, send stream `invalid-xml` and close stream. 3. If stream-start `xml:lang` length > 35 (client stream), send stream `policy-violation` and close stream. 4. If stanza-level/child validated `xml:lang` is invalid, return stanza/protocol error without dropping the connection.
