Changes: Add an isolated Stanza 12.22.1 SASL feature replacement using existing SCRAM crypto, exact dependency lock and MIT notices, 44 integrated fixture checks, proposed ADR and review handoff. Leave production adapters and the original library experiment unchanged. Validation: Fresh-cache npm ci and npm test passed on Node 24.21.0/npm 11.19.0; 44 tests cover RFC SHA-1/SHA-256 proofs and verified finals, stock defect reproduction, invalid/premature finals and async cleanup. make check passed 72 tooling tests. Pins/licenses/notices, unchanged vendor source and whitespace verified. Browser/WSS/server interoperability and constant-time assurance remain unverified; independent security review is pending. Task: S00-017 Agent: auto-protocol-599343cd9e3f Role: protocol Action: claim Status: review Task title: Establish fail-closed SCRAM server-final validation
SCRAM server-final validation experiment
S00-017 fixture-only remediation of S00-003. No network connection or production adapter. Public RFC example credentials are used only in memory. Requires the accepted Node 24.21.0 / npm 11.19.0 runtime; installs enforce exact engines and TLS.
From the project root, use the isolated runtime already verified by S00-002:
export PATH=/private/tmp/luma-stack-runtime/node-v24.21.0-darwin-arm64/bin:$PATH
node --version
npm --version
npm ci --prefix experiments/0002-xmpp-library/sasl-validation --ignore-scripts --no-audit --no-fund --cache /private/tmp/luma-sasl-review-cache --fetch-retries=0 --fetch-timeout=15000
npm test --prefix experiments/0002-xmpp-library/sasl-validation
On another host, obtain the official exact Node runtime with verified HTTPS and checksum; do not change system configuration or disable certificates. Use a fresh cache directory for an independent clean-install check. The original parent experiment manifest remains incomplete and is not used by these commands.
The 44 tests use Stanza's actual Client, XML parse/registry, named SASL feature handler, feature negotiation dispatcher and SCRAM implementation. Outgoing sends, restart and disconnect are captured fixture effects, not server evidence. Tests first reproduce stock 12.22.1 accepting missing/forged premature success, then verify rejection with the replacement. RFC 5802 §5 / RFC 7677 §3 client-first, client-proof and verified server-final are checked byte-for-byte for SHA-1/256. No real credentials or key material are printed. A passing stock reproduction means the defect was observed, not safe authentication.
validated-sasl.mjs installs through the public plugin and feature APIs:
// Illustrative integration for a fresh Client, BEFORE any connect/features.
client.use(validatedSasl);
The plugin calls registerFeature('sasl', 100, handler). Stanza's feature registry
uses one handler per name, so this replaces the stock callback. Registration also
adds another order entry; the real dispatcher exits on restart/disconnect, and
our integrated tests verify only one negotiation/completion, including after
reset. Do not install after authentication has started or alongside another SASL
replacement. The stock handler must never have an active event listener.
State is bounded to initial credentials/send, server-first, proof send,
server-final and closed. Success is allowed only after completed proof send,
strict final syntax, processSuccess and finalize reporting both authenticated
and mutuallyAuthenticated. Invalid input disconnects once, removes listeners
and never requests authenticated restart. Reset closes the old attempt before
awaited work resumes. Crypto remains entirely in the installed library, backed
by Node crypto in this run; no hash, HMAC, KDF or proof implementation is authored.
No credential cache/update is persisted by the replacement.
This is a deliberately limited non-PLUS SHA-256/SHA-1 experiment. It rejects other mechanisms, supplied TLS binding bytes, extensions, extra challenges and server-final delivered separately as a challenge. It accepts only server-final additional-data on success. It checks nonce extension and bounds decoded data at 4096 bytes and iterations at 100000; these are experiment limits, not shared contracts/product policy. Racing success before a send promise resolves is rejected conservatively. A final timeout/lifecycle policy belongs to the original integration; this experiment does not claim one.
Exact dependency graph and preserved licenses: LICENSE_INVENTORY.md and THIRD_PARTY_NOTICES.md. All eight locked packages are MIT; lock entries retain HTTPS registry URLs and SHA512 integrity. Node/npm notices stay with their runtime.
Security review must inspect the state transitions and actual library calls, repeat clean install/tests, check the diff, licenses and executor evidence, then approve/reject through the task workflow. No browser build/execution, live WSS, independent server, parser resource-limit certification, TLS channel binding, constant-time verification audit, advisory/provenance audit, production SASL policy or release readiness is claimed. Stanza's verifier uses ordinary string equality; these tests prove rejection correctness, not a timing guarantee. Browser Stanza uses its bundled synchronous hash/HMAC code and browser RNG/WS; that backend is not exercised here. S00-003 retains its library selection, browser/WSS/parser/extension experiment and stronger-mechanism policy work.