S00-018: Bound standalone XML ingress before Stanza parsing and import
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
This commit is contained in:
1 parent
f556fc0deb
commit
e2b4dbdb76
19 files changed
+1648
-8
No files matched your search
@@ -0,0 +1,96 @@
|
||||
# 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.
|
||||
Reference in new issue
Block a user