Changes: Add an isolated public transport integration with finite byte, depth, element and attribute budgets; validate complete text XML with pinned saxes before real JXT parsing/import. Add adversarial and boundary fixtures, terminal cleanup tests, dependency notices and a proposed parser ADR. Validation: Fresh-cache npm ci and npm test passed all 55 tests with Node 24.21.0/npm 11.19.0. Reproduced three stock parser failures, checked ten exact dependency licenses/integrities and 340 unchanged Stanza files; make check passed 72 tooling tests. Scope and whitespace inspected. Browser runtime, live WSS and interoperability were not tested. Task: S00-018 Agent: auto-protocol-89fe6563092c Role: protocol Action: claim Status: review Task title: Validate bounded standalone XML ingress for the XMPP candidate
97 lines
6.2 KiB
Markdown
97 lines
6.2 KiB
Markdown
# Bounded standalone XML ingress experiment (S00-018)
|
|
|
|
This fixture experiment uses real Stanza 12.22.1 Client dispatch, JXT.parse and
|
|
registry.import, preceded by saxes 6.0.0 XML 1.0 namespace-aware validation.
|
|
It adds a constructor to the public Client.transports registry and selects only
|
|
that transport via configuration. It does not use the stock persistent parser,
|
|
subclass private transport fields, intercept already imported events, or patch
|
|
vendor files. It is not a production transport or a library selection.
|
|
|
|
From project root, reuse the reviewed isolated Node runtime (macOS ARM64):
|
|
|
|
```sh
|
|
export PATH=/private/tmp/luma-stack-runtime/node-v24.21.0-darwin-arm64/bin:$PATH
|
|
npm ci --prefix experiments/0002-xmpp-library/parser-validation --ignore-scripts --no-audit --no-fund --cache /private/tmp/luma-parser-review-cache --fetch-retries=0 --fetch-timeout=15000
|
|
npm test --prefix experiments/0002-xmpp-library/parser-validation
|
|
python3 experiments/0002-xmpp-library/parser-validation/check-dependencies.py
|
|
node experiments/0002-xmpp-library/parser-probe/reproduce.mjs
|
|
```
|
|
|
|
The last command reproduces stock defects; exit 0 is NOT safe parsing.
|
|
The dependency checker also requires the original candidate already installed in
|
|
experiments/0002-xmpp-library/node_modules for byte comparison. It checks all 340
|
|
Stanza files and ten exact locked installed package licenses/integrities. The
|
|
saxes tarball omits LICENSE: the upstream v6.0.0 license is preserved locally,
|
|
with source https://raw.githubusercontent.com/lddubeau/saxes/v6.0.0/LICENSE.
|
|
|
|
## Enforcement order and local policy
|
|
|
|
1. Require primitive string. Reject Blob/ArrayBuffer/typed arrays before coercion.
|
|
2. Reject more than 4096 UTF-16 units without encoding; then reject more than
|
|
4096 UTF-8 bytes using TextEncoder. The temporary encoding buffer is bounded
|
|
by the unit check (at most 12288 bytes for any input); it is not an unbounded
|
|
copy. First character must be `<` as required by RFC 7395.
|
|
3. New saxes parser per message, fragment=false, xmlns=true, XML 1.0 forced,
|
|
no inherited namespace resolver. Throw on the first error; close() checks
|
|
document completion. No tree is built during this phase.
|
|
4. At opentagstart reject depth >8 (including root), elements >32, name >128
|
|
UTF-16 units. At attribute reject total attributes >16 (including xmlns),
|
|
name >128, decoded value >256, or aggregate decoded attribute name+value
|
|
units >1024. Checks occur before JXT tree construction/import. Saxes has
|
|
already stored one completed attribute at its callback; lexical tokens,
|
|
namespace tables and DTD buffers before callbacks are bounded by 4096 input
|
|
bytes. No claim of allocation-free SAX processing is made.
|
|
5. Reject DTDs, comments, processing instructions and XML declarations. Only
|
|
built-in/numeric entities are available; no DTD fetching/entity expansion is
|
|
enabled. CDATA is accepted. Declaration rejection is local conservative
|
|
policy; RFC 7395 discourages but does not forbid declarations.
|
|
6. Only after complete validation call real JXT.parse and registry.import. An
|
|
unknown top-level root or candidate parser/import error closes the attempt.
|
|
Parsed output is delivered through Client stream:data only after import
|
|
succeeds. No invalid document's earlier root is emitted.
|
|
7. On rejection close the socket, detach all four callbacks, clear config and
|
|
stream references before emitting a payload-free error code. Every old
|
|
captured callback checks the terminal flag. Parsers/trees are synchronous
|
|
function locals, never retained between messages. A fresh Client.connect
|
|
creates another transport; restart on a failed transport cannot revive it.
|
|
|
|
The combined boundary fixture hits 4096 bytes, depth 8, 32 elements,
|
|
16 attributes and 1024 attribute units simultaneously and imports a real body.
|
|
Separate tests cover name/value equality, multibyte byte equality, one-over
|
|
bounds, stock 1 MiB/depth-256/18-message cases, malformed tails, incomplete XML,
|
|
binary, duplicate attributes/namespaces/entities/invalid characters, callback
|
|
replay, fresh connect, stream open/close/restart and outgoing registry export.
|
|
Tracing registry.import calls the real implementation; it never fakes success.
|
|
FixtureSocket replaces only browser socket effects; tests use actual Client
|
|
connect/transport selection and stanza queue events.
|
|
|
|
## Constraints
|
|
|
|
The default socket constructor is globalThis.WebSocket; browser use requires a
|
|
CommonJS-capable bundler for Stanza/saxes/xmlchars, native WebSocket, URL and
|
|
TextEncoder plus support for private class fields. The injected fixture socket
|
|
is explicitly separate and never chosen by the default path. Only explicit
|
|
wss URLs and the xmpp subprotocol are allowed. TLS/CORS/binding are unchanged;
|
|
no real WSS handshake is exercised here. Browser build/runtime and selected
|
|
browser versions are NOT verified. Node tests run 24.21.0/npm 11.19.0.
|
|
|
|
Limits protect application parsing after the browser delivers a complete message.
|
|
They cannot prevent the browser/network stack from allocating a huge inbound
|
|
message, nor bound total traffic, Client queue growth, CPU rate, reconnect
|
|
policy or downstream extension work. Those need separate product/provider
|
|
policies. The experiment closes locally on violations rather than implementing
|
|
full XMPP stream-error reporting/close negotiation/redirect/keepalive behavior.
|
|
It does not enforce complete XMPP stream sequencing or preserve inherited stream
|
|
language. JXT interpretation/normalization is retained; SAX and JXT equivalence
|
|
for all accepted XML forms is not established. No auth/crypto changes, complete
|
|
NET-003 policy, OMEMO, independent server/client interoperability, exhaustion
|
|
benchmark, advisory/provenance audit, or release acceptance is claimed.
|
|
|
|
All ten package license texts/notices are in THIRD_PARTY_NOTICES.md and exact
|
|
versions in LICENSE_INVENTORY.md/package-lock.json. Saxes is a mature existing
|
|
strict parser with documented XML/namespace conformance and public event APIs;
|
|
6.0.0 is an older release, and no ongoing-maintenance SLA is assumed. Its
|
|
absence of built-in token limits is why the mandatory byte precheck comes first.
|
|
See proposed docs/decisions/0002-xmpp-parser-validation.md. Independent security
|
|
review is required; S00-003 remains a separate assignment after reviewed done.
|