Parents: #493, #473 Related: #755, #756
Problem
JSON.stringify builds the replacer-array PropertyList with std.StringHashMapUnmanaged, whose default string context has a process-predictable seed. Every accepted String/Number property name comes from observable, attacker-controlled input, so an adversary can precompute collision-heavy replacer lists and force avoidable worst-case deduplication cost.
Scope
- Replace only the replacer PropertyList membership index with an exact-byte custom hash context.
- Lazily draw one secure seed per stringify invocation when the first accepted property name is deduplicated; no replacer, callable replacer, empty array, and ignored-only arrays must not request entropy or allocate the membership table.
- Reuse the interpreter-owned cryptographic seed stream and fail closed if its initial entropy request fails.
- Preserve first-occurrence ordering, duplicate suppression, String/Number wrapper coercion and abrupt completion, encoded NUL keys, integer-like names, wide keys, GC lifetimes, and OOM behavior.
No-workaround rules
- No input blacklist, property-count cap, deterministic fallback seed, hash-only equality, skipped duplicate suppression, reordered coercion, or reduced replacer semantics.
- No stack, queue, job, Promise, Worker, or Map/Set iterator implementation changes.
Acceptance
- Replacer PropertyList membership uses a non-default per-stringify secure hash context.
- Non-array/callable/empty/ignored-only paths remain lazy and allocate no membership hash table.
- Duplicate, escaped, NUL-bearing, integer-like, wrapper-derived, and adversarial-width keys retain exact output/coercion order.
- Focused normal and TSan tests pass without leaks or races.
- Exact-parent JSON Test262 inventory has zero regressions.
Implemented by 36ad04d4.
Evidence: JSON units 16/16 with zero leaks; focused native witness 1/1; focused TSan witness and observable end-to-end paths 2/2 with zero races; exact-parent test/built-ins/JSON remains 165 pass, 0 fail (0 flips, 0 regressions).