ljharb / ljharb/json-stable-stringify

README note: what the output does not distinguish when used as a hash input

Open Beginner friendly
#24 0 comments 0 reactions 0 assignees View on GitHub

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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.