Files
LumaWeb/docs/decisions/0002-xmpp-sasl-validation.md
T
wt 1850dbc39f S00-017: Approve fail-closed SCRAM validation after independent integration checks
Changes: Accept the bounded S00-017 fixture experiment and ADR; record independent security review and handoff with remaining browser, WSS, mechanism-policy and timing limits.
Validation: Fresh-cache npm ci and npm test passed on Node 24.21.0/npm 11.19.0 with 44 integrated tests. Verify exact installed pins, eight MIT notices, HTTPS/integrity and unchanged vendor files. make check passed 72 tooling tests; inspect executor scope/message and review metadata whitespace. No browser/server interoperability or constant-time assurance claimed.

Task: S00-017
Agent: auto-security-9ab954f6e021
Role: security
Action: review
Status: done

Task title: Establish fail-closed SCRAM server-final validation
2026-10-15 02:20:57 +07:00

125 lines
7.5 KiB
Markdown

# ADR 0002-SASL: Fail-closed SCRAM server-final experiment
- Status: accepted for the bounded S00-017 fixture experiment; independent review: `state/evidence/S00-017-review.md`.
- Task: S00-017, remediation for S00-003.
- Date: 2026-10-15.
- Owner: auto-protocol-599343cd9e3f (protocol).
- Reviewer: auto-security-9ab954f6e021 (security), 2026-10-15.
- Requirements: ACC-004, NET-003, NET-004; accepted ADR 0000/0001 preserved.
## Question and bounded result
Can the pinned StanzaJS candidate validate SCRAM server authentication before
emitting success/restarting without modifying vendor source or implementing
cryptographic primitives? Output is one isolated, runnable feature experiment,
not selection of Stanza as the production library. S01 shared contracts remain
unaccepted; no application adapter or interface is created here.
The original blocker is reproducible in 12.22.1: its SASL success branch marks
negotiation/transport authenticated, emits auth:success and requests restart
without processing additional-data or calling the SCRAM verifier. Empty and
forged success before challenge/proof are accepted. The isolated tests retain
this baseline reproduction and exercise the replacement through the real XML
registry and feature dispatcher.
## Options and proposed decision
1. **Replace the named SASL feature through public APIs.** Chosen for this bounded
experiment. `Client.use` and `registerFeature` are exposed in installed package
declarations; the registry assigns handlers by name. SCRAM already exposes
`processSuccess` and `finalize`. Keep all crypto in that existing implementation
and add fail-closed ordering, strict fixture-profile input and cleanup.
2. **Patch ignored vendor code or intercept success after emission.** Rejected:
ignored patches are not reproducible deliverables, and a post-success listener
cannot prevent earlier authenticated effects. No upstream patch is required.
3. **Replace the entire client/mechanism stack.** Deferred to S00-003. The original
evidence's xmpp.js source comparison also lacked server-final processing;
no installed/tested alternative or maintenance/security superiority is claimed.
Install the proposed plugin on a fresh Client before connect/feature events;
never permit the stock SASL handler to start first. The named registration
replaces its callback without editing registry internals. The API appends an
order entry; the actual dispatcher exits on restart/disconnect, with tests
confirming one completion and successful later negotiation after reset.
Only after proof send completes does success data reach library `processSuccess`
and `finalize`; both authenticated and mutuallyAuthenticated must be true before
any authenticated flag, event or restart. Missing, forged, erroneous, duplicated,
malformed or premature final is terminal failure. Reset removes attempt listeners
and cancels asynchronous continuation. Proof/credential/send exceptions fail
without logging credentials, peer data or crypto state. No cache is persisted.
## Sources, pins and licenses
Primary sources inspected on 2026-10-15:
- [RFC 5802 §5](https://www.rfc-editor.org/rfc/rfc5802.html#section-5): mutual
authentication and SHA-1 exchange; the public user/pencil fixture is copied as
test data, not used as an account. Invalid verifier must fail the exchange.
- [RFC 7677 §3](https://www.rfc-editor.org/rfc/rfc7677.html#section-3): SHA-256
exchange used as a second fixed primary-spec transcript.
- [Stanza 12.22.1 registry record](https://registry.npmjs.org/stanza/12.22.1):
exact package provenance location/license and locked tarball. Installed shipped
`Client.js/.d.ts`, `plugins/features.js/.d.ts`, `plugins/sasl.js` and
`lib/sasl/index.js/.d.ts` are the exact API/implementation evidence. Upstream
[Client source](https://github.com/legastero/stanza/blob/master/src/Client.ts)
corroborates plugin usage, but is not version-pinned acceptance evidence.
Dependency: stanza 12.22.1, MIT. Exact transitive pins: @types/node 22.20.5,
@types/punycode 2.1.4, @types/ws 8.18.2, punycode 2.3.1, sdp 3.2.2,
undici-types 6.21.0, ws 8.22.0; all MIT. Separate manifest/lock under
`experiments/0002-xmpp-library/sasl-validation` does not alter the parent's
unaccepted exploratory graph. Every locked package has HTTPS registry URL,
SHA512 integrity and inspected installed license text; inventory and notices
are preserved. Runtime stays Node 24.21.0/npm 11.19.0 from accepted ADR 0001.
No new crypto dependency, crypto primitive or vendor-source edit is introduced.
## Runnable evidence and constraints
See experiment README and `state/evidence/S00-017-auto-protocol-599343cd9e3f.md`.
Clean npm ci with an empty cache and npm test passed: 44 tests, including two
RFC client proofs/verified finals, original insecure baseline, missing/forged/
premature rejection, duplicate/out-of-order inputs, send/credential failure,
asynchronous reset and replay rejection. Tests observe real feature-dispatcher
restart/disconnect calls and transport/negotiation flags; they are local parsed
XML integration tests with captured outbound effects, not WSS/server tests.
The experiment accepts only non-PLUS SHA-256/SHA-1, selecting the strongest of
those offered and failing if neither is available. No plaintext fallback or
invented browser TLS binding. SHA-512, complete mechanism policy and compatibility
remain S00-003. Server-final is accepted only as success additional-data; separate
final challenge, SCRAM extensions and extra challenge round trips are rejected.
Decoded-data and iteration bounds (4096 bytes / 100000) are local experiment
limits. A success racing a pending send is conservatively rejected. These
restrictions can reduce availability and need explicit future integration review.
Node exercises Stanza's SCRAM composition and Node crypto primitives. Browser
Stanza uses bundled synchronous hashing/HMAC, global crypto RNG, WebSocket and
fetch; its backend/build is not tested in this task. No browser support matrix,
constant-time comparison guarantee (Stanza compares verifier strings), advisory/
provenance clearance, WSS/TLS/channel-binding test, independent-client/server
interoperability or production readiness is implied. No live credentials,
network authentication, certificate/CORS bypass or persisted secrets are used.
## Handoff and consequences
Independent security reviewer must reproduce the integrated positive/negative
cases and assess extension API, cleanup, license/pin evidence and the stated
restrictions. This ADR stays proposed until review. Reviewed acceptance permits
a fresh S00-003 executor to evaluate this approach in its own experiment; it does
not implement that original task or change the accepted stack/shared contracts.
Any required broader policy/interface or vendor replacement is a separately
scoped plan decision. Coordinator owns transition commits and fresh processes.
## Independent acceptance
Security reviewer auto-security-9ab954f6e021 independently repeated fresh-cache
npm ci and all 44 integrated tests on Node 24.21.0/npm 11.19.0. Exact installed
pins, all eight MIT notices, HTTPS/integrity entries, shipped public extension
APIs and unchanged vendor files were verified. RFC proofs/finals match primary
examples; forged/missing/premature final cannot authenticate in the demonstrated
handler. make check passed 72 tooling tests. This bounded experiment is accepted;
Stanza selection, complete mechanism policy, browser crypto/build, WSS/server
interoperability, parser certification, timeout and constant-time assurance remain
unverified. S00-003 must evaluate integration in a new assigned session.