ljharb / ljharb/json-stable-stringify
README note: what the output does not distinguish when used as a hash input
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 79
- Forks
- 18
- PR merge metrics
- No merged PRs in 30d
Description
The README's first line is "deterministic JSON.stringify()", and a very common reason to want that is hashing an object for identity or caching. In that use there are two pairs the output does not distinguish, and I think a sentence in the README would save people a surprise.
const stringify = require('json-stable-stringify');
// 1. non-finite numbers all become null
stringify(JSON.parse('{"v":1e400}')) // '{"v":null}'
stringify(JSON.parse('{"v":1e401}')) // '{"v":null}'
// 2. integers above 2^53 that differ by 1 share a double
stringify(JSON.parse('{"n":9007199254740993}')) // '{"n":9007199254740992}'
stringify(JSON.parse('{"n":9007199254740992}')) // '{"n":9007199254740992}'
Node v22.22.3, json-stable-stringify 1.3.0.
Neither is a defect in this package — both happen in JSON.parse / the double number model before the library runs, and JSON.stringify behaves identically. But "deterministic" is easily read by a hashing user as "distinct inputs give distinct output", and these are the two places that fails for reasons that are invisible at the call site.
Suggested change
A short README section — something like "Determinism is not injectivity: values that are equal after parsing produce equal output. Non-finite numbers serialize to null, and integers outside the safe range are rounded, so if you are hashing untrusted input, validate the raw text before parsing." No code change.
How I found it
A 31-class pathology corpus of byte-distinct document pairs, each with a declared consumer intent, checking which pairs receive one digest:
npm install -g @ghost-ark/kernel-probe
kernel-probe --command "node your-wrapper.mjs"
Measurement across five canonicalization/stable-stringify libraries, with the adapters used: https://github.com/PSUCyberSecurityLab/ghost-ark/blob/main/docs/research/JCS_CANONICALIZER_PROBE.md
Entirely reasonable to close as wontfix if you consider this the caller's problem — it is, in the sense that the library is doing exactly what it says.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in the README, especially the opening description and the examples showing JSON.stringify behavior. Review the two demonstrated cases involving non-finite numbers and integers beyond the safe range, then add a concise warning about determinism not implying injectivity. Done means the README clearly explains the hashing caveat without requiring a code change.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript
- Domain
- documentation
- Issue type
- Documentation
- Difficulty
- 1/5
- Estimated time
- Under an hour
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 85/100