@stackline/stable-stringify 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/CONTRIBUTING.md +54 -0
- package/LICENSE +21 -0
- package/NOTICE +21 -0
- package/README.md +290 -0
- package/SECURITY.md +55 -0
- package/dist/index.cjs +728 -0
- package/dist/index.cjs.map +7 -0
- package/dist/index.d.cts +101 -0
- package/dist/index.d.mts +107 -0
- package/dist/index.d.ts +101 -0
- package/dist/index.js +705 -0
- package/dist/index.js.map +7 -0
- package/dist/index.min.js +7 -0
- package/dist/index.min.js.map +7 -0
- package/package.json +102 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes are documented here. This project follows Semantic
|
|
4
|
+
Versioning.
|
|
5
|
+
|
|
6
|
+
## [1.0.0] - 2026-08-16
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- Deterministic recursive JSON serialization with zero runtime dependencies.
|
|
11
|
+
- Compatible `fast-json-stable-stringify` comparator and cycle options.
|
|
12
|
+
- Circular object and array policies: throw, marker, path, and null.
|
|
13
|
+
- Iterative traversal tested with 25,000 nested containers.
|
|
14
|
+
- BigInt, replacer, indentation, accessor, and `toJSON` policies.
|
|
15
|
+
- Opt-in depth, entry, and output-length limits with structured errors.
|
|
16
|
+
- Safe diagnostic serialization with bounded defaults and controlled fallback.
|
|
17
|
+
- Strict RFC 8785 JSON canonicalization and UTF-8 byte output.
|
|
18
|
+
- ESM, callable CommonJS, browser-global, and source-map distributions.
|
|
19
|
+
- TypeScript declarations tested from 3.9 through 7.0.
|
|
20
|
+
- 30,000 differential cases against `fast-json-stable-stringify@2.1.0`.
|
|
21
|
+
- Public documentation, security policy, CI matrix, and live playground.
|
|
22
|
+
|
|
23
|
+
[1.0.0]: https://github.com/alexandroit/stackline-stable-stringify/releases/tag/v1.0.0
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thank you for improving `@stackline/stable-stringify`.
|
|
4
|
+
|
|
5
|
+
## Development
|
|
6
|
+
|
|
7
|
+
Use Node.js 20 or newer for the development toolchain. Published runtime
|
|
8
|
+
support is tested separately.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm ci
|
|
12
|
+
npm test
|
|
13
|
+
npm run test:attw
|
|
14
|
+
npm run benchmark
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Pull requests
|
|
18
|
+
|
|
19
|
+
Keep each change focused. Include:
|
|
20
|
+
|
|
21
|
+
- the behavior being changed;
|
|
22
|
+
- tests for normal and adversarial input;
|
|
23
|
+
- compatibility impact;
|
|
24
|
+
- performance and security impact for traversal changes;
|
|
25
|
+
- documentation for public API changes.
|
|
26
|
+
|
|
27
|
+
Do not reduce canonical strictness, safe-mode limits, or error visibility to
|
|
28
|
+
improve a benchmark.
|
|
29
|
+
|
|
30
|
+
## Compatibility changes
|
|
31
|
+
|
|
32
|
+
The default API tracks documented `fast-json-stable-stringify@2.1.0` behavior.
|
|
33
|
+
Before changing stable-mode semantics:
|
|
34
|
+
|
|
35
|
+
1. add a focused local regression;
|
|
36
|
+
2. update the differential corpus when applicable;
|
|
37
|
+
3. document every intentional difference;
|
|
38
|
+
4. validate direct and npm-alias installation shapes.
|
|
39
|
+
|
|
40
|
+
Canonical mode follows RFC 8785 and I-JSON constraints. Standards changes need
|
|
41
|
+
a source citation and interoperability vector.
|
|
42
|
+
|
|
43
|
+
## Security changes
|
|
44
|
+
|
|
45
|
+
Do not discuss a suspected vulnerability in a public issue before a patched
|
|
46
|
+
release exists. Follow [SECURITY.md](SECURITY.md).
|
|
47
|
+
|
|
48
|
+
## Commits
|
|
49
|
+
|
|
50
|
+
Use a short imperative subject. Keep generated output and unrelated formatting
|
|
51
|
+
out of the same commit.
|
|
52
|
+
|
|
53
|
+
By contributing, you agree that your contribution is licensed under the MIT
|
|
54
|
+
License.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alexandro Paixao Marques and contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/NOTICE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
@stackline/stable-stringify
|
|
2
|
+
Copyright (c) 2026 Alexandro Paixao Marques and contributors
|
|
3
|
+
|
|
4
|
+
This package is an independent implementation. Its default public behavior is
|
|
5
|
+
designed to support migration from fast-json-stable-stringify version 2.1.0,
|
|
6
|
+
a separate MIT-licensed project originally authored by James Halliday and
|
|
7
|
+
maintained by Evgeny Poberezkin and contributors:
|
|
8
|
+
|
|
9
|
+
https://github.com/epoberezkin/fast-json-stable-stringify
|
|
10
|
+
|
|
11
|
+
The upstream package is used only as a development dependency and behavioral
|
|
12
|
+
reference in differential tests. Its source code is not bundled and it is not
|
|
13
|
+
a runtime dependency.
|
|
14
|
+
|
|
15
|
+
The package names "fast-json-stable-stringify", "safe-stable-stringify",
|
|
16
|
+
"fast-safe-stringify", and "json-stringify-safe" are used descriptively in
|
|
17
|
+
compatibility tests, benchmarks, and market research. This project is not
|
|
18
|
+
affiliated with or endorsed by their maintainers.
|
|
19
|
+
|
|
20
|
+
RFC 8785, JSON Canonicalization Scheme (JCS), is an Internet Engineering Task
|
|
21
|
+
Force standards-track document. Standards references do not imply endorsement.
|
package/README.md
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# @stackline/stable-stringify
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@stackline/stable-stringify)
|
|
4
|
+
[](https://github.com/alexandroit/stackline-stable-stringify/actions/workflows/ci.yml)
|
|
5
|
+
[](https://github.com/alexandroit/stackline-stable-stringify/actions/workflows/codeql.yml)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
Deterministic JSON serialization, safe diagnostics, and RFC 8785 canonical
|
|
9
|
+
JSON in one zero-dependency package.
|
|
10
|
+
|
|
11
|
+
The default export follows the familiar `fast-json-stable-stringify` API. It
|
|
12
|
+
also handles circular arrays, deeply nested data without recursive call-stack
|
|
13
|
+
failure, BigInt policies, resource limits, safe logging, and strict JSON
|
|
14
|
+
Canonicalization Scheme output.
|
|
15
|
+
|
|
16
|
+
## Why this package
|
|
17
|
+
|
|
18
|
+
Stable serialization is infrastructure for cache keys, signatures, snapshots,
|
|
19
|
+
deduplication, logs, and reproducible builds. The JavaScript ecosystem has
|
|
20
|
+
separate high-volume packages for deterministic key order, circular data, safe
|
|
21
|
+
logging, and canonical JSON. This package provides those contracts through one
|
|
22
|
+
small, tested runtime while keeping the established call shape.
|
|
23
|
+
|
|
24
|
+
- recursive deterministic key ordering;
|
|
25
|
+
- compatible comparator and `{ cmp, cycles }` options;
|
|
26
|
+
- cycle policies for objects and arrays;
|
|
27
|
+
- safe defaults for diagnostics and logging;
|
|
28
|
+
- RFC 8785 / JCS canonical strings and UTF-8 bytes;
|
|
29
|
+
- iterative traversal for very deep input;
|
|
30
|
+
- opt-in depth, entry, and output-size limits;
|
|
31
|
+
- ESM, callable CommonJS, browser global, and TypeScript declarations;
|
|
32
|
+
- zero runtime dependencies.
|
|
33
|
+
|
|
34
|
+
## Install
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm install @stackline/stable-stringify
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Keep an existing package name and imports with an npm alias:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm install fast-json-stable-stringify@npm:@stackline/stable-stringify
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```js
|
|
47
|
+
const stringify = require('fast-json-stable-stringify');
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Quick start
|
|
51
|
+
|
|
52
|
+
```js
|
|
53
|
+
import stringify from '@stackline/stable-stringify';
|
|
54
|
+
|
|
55
|
+
const first = { z: 1, nested: { y: 2, x: 3 }, a: true };
|
|
56
|
+
const second = { a: true, nested: { x: 3, y: 2 }, z: 1 };
|
|
57
|
+
|
|
58
|
+
stringify(first) === stringify(second); // true
|
|
59
|
+
// {"a":true,"nested":{"x":3,"y":2},"z":1}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Arrays retain their input order. Object keys are sorted at every depth. Input
|
|
63
|
+
objects are never mutated.
|
|
64
|
+
|
|
65
|
+
## Safe serialization
|
|
66
|
+
|
|
67
|
+
`safeStringify` is intended for logs, diagnostics, and error reporting. It
|
|
68
|
+
serializes BigInt as strings, marks cycles, and applies bounded defaults.
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
import { safeStringify } from '@stackline/stable-stringify';
|
|
72
|
+
|
|
73
|
+
const event = { id: 42n };
|
|
74
|
+
event.request = event;
|
|
75
|
+
|
|
76
|
+
safeStringify(event);
|
|
77
|
+
// {"id":"42","request":"[Circular]"}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Its default budgets are `maxDepth: 100`, `maxEntries: 100000`, and
|
|
81
|
+
`maxLength: 1000000`. A hostile getter, throwing `toJSON`, or exceeded limit
|
|
82
|
+
returns a controlled JSON string. Set `throwOnError: true` to propagate the
|
|
83
|
+
original error.
|
|
84
|
+
|
|
85
|
+
```js
|
|
86
|
+
safeStringify(value, replacer, 2, {
|
|
87
|
+
depthLimit: 20,
|
|
88
|
+
edgesLimit: 5000,
|
|
89
|
+
maxLength: 250000,
|
|
90
|
+
throwOnError: true
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## RFC 8785 canonical JSON
|
|
95
|
+
|
|
96
|
+
Use `canonicalize` when bytes must be reproducible across systems, such as
|
|
97
|
+
digital signatures, content-addressed storage, or cryptographic hashes.
|
|
98
|
+
|
|
99
|
+
```js
|
|
100
|
+
import {
|
|
101
|
+
canonicalize,
|
|
102
|
+
canonicalizeBytes
|
|
103
|
+
} from '@stackline/stable-stringify';
|
|
104
|
+
|
|
105
|
+
canonicalize({ amount: 4.50, currency: 'CAD', active: true });
|
|
106
|
+
// {"active":true,"amount":4.5,"currency":"CAD"}
|
|
107
|
+
|
|
108
|
+
const bytes = canonicalizeBytes({ z: 1, a: 2 });
|
|
109
|
+
// Uint8Array containing UTF-8 bytes for {"a":2,"z":1}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Canonical mode intentionally rejects data that cannot satisfy RFC 8785 and
|
|
113
|
+
I-JSON, including:
|
|
114
|
+
|
|
115
|
+
- `NaN`, `Infinity`, BigInt, `undefined`, functions, and symbols;
|
|
116
|
+
- circular references and sparse arrays;
|
|
117
|
+
- lone UTF-16 surrogates;
|
|
118
|
+
- accessors, enumerable symbol properties, and class instances.
|
|
119
|
+
|
|
120
|
+
It does not invoke getters or `toJSON`. Convert application objects to plain
|
|
121
|
+
JSON data before canonicalization.
|
|
122
|
+
|
|
123
|
+
## API
|
|
124
|
+
|
|
125
|
+
### `stringify(value, options?)`
|
|
126
|
+
|
|
127
|
+
The default export and the named `stableStringify` and `stringify` exports are
|
|
128
|
+
the same function.
|
|
129
|
+
|
|
130
|
+
```js
|
|
131
|
+
import stringify from '@stackline/stable-stringify';
|
|
132
|
+
|
|
133
|
+
stringify(value);
|
|
134
|
+
stringify(value, comparator);
|
|
135
|
+
stringify(value, { cmp: comparator, cycles: true });
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Like `JSON.stringify`, a root `undefined`, function, or symbol returns
|
|
139
|
+
`undefined`. Unsupported object properties are omitted and unsupported array
|
|
140
|
+
items become `null`.
|
|
141
|
+
|
|
142
|
+
### Stable options
|
|
143
|
+
|
|
144
|
+
| Option | Default | Purpose |
|
|
145
|
+
| :--- | :--- | :--- |
|
|
146
|
+
| `cmp` | key order | Compare `{ key, value }` records |
|
|
147
|
+
| `cycles` | `false` | Compatible shortcut for a `__cycle__` marker |
|
|
148
|
+
| `onCycle` | `throw` | `throw`, `marker`, `path`, or `null` |
|
|
149
|
+
| `cycleValue` | `__cycle__` | Custom marker for `onCycle: 'marker'` |
|
|
150
|
+
| `bigint` | `throw` | `throw`, `string`, or safe `number` |
|
|
151
|
+
| `replacer` | none | Function or JSON-style property list |
|
|
152
|
+
| `space` | none | JSON-style indentation, capped at 10 characters |
|
|
153
|
+
| `accessors` | `invoke` | `invoke`, `omit`, or `throw` |
|
|
154
|
+
| `toJSON` | `true` | Invoke compatible `toJSON` hooks |
|
|
155
|
+
| `maxDepth` | unlimited | Maximum container nesting depth |
|
|
156
|
+
| `maxEntries` | unlimited | Maximum visited array items and object keys |
|
|
157
|
+
| `maxLength` | unlimited | Maximum UTF-16 output length |
|
|
158
|
+
|
|
159
|
+
Unlimited defaults preserve compatibility. Apply explicit limits at exposed
|
|
160
|
+
trust boundaries, or use `safeStringify`.
|
|
161
|
+
|
|
162
|
+
### Cycle paths
|
|
163
|
+
|
|
164
|
+
```js
|
|
165
|
+
const value = { id: 1 };
|
|
166
|
+
value.child = { owner: value };
|
|
167
|
+
|
|
168
|
+
stringify(value, { onCycle: 'path' });
|
|
169
|
+
// {"child":{"owner":"[Circular <root>]"},"id":1}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Repeated sibling references are serialized normally; only references to an
|
|
173
|
+
active ancestor are cycles.
|
|
174
|
+
|
|
175
|
+
### Comparator
|
|
176
|
+
|
|
177
|
+
```js
|
|
178
|
+
const descending = (left, right) =>
|
|
179
|
+
right.key.localeCompare(left.key);
|
|
180
|
+
|
|
181
|
+
stringify({ a: 1, c: 3, b: 2 }, descending);
|
|
182
|
+
// {"c":3,"b":2,"a":1}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### `configure(defaults?)`
|
|
186
|
+
|
|
187
|
+
Create a reusable serializer without rebuilding option objects at call sites.
|
|
188
|
+
|
|
189
|
+
```js
|
|
190
|
+
import { configure } from '@stackline/stable-stringify';
|
|
191
|
+
|
|
192
|
+
const cacheKey = configure({
|
|
193
|
+
maxDepth: 50,
|
|
194
|
+
maxEntries: 10000,
|
|
195
|
+
maxLength: 500000
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
cacheKey({ route: '/users', query: { page: 2 } });
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Per-call options override configured defaults.
|
|
202
|
+
|
|
203
|
+
### Errors
|
|
204
|
+
|
|
205
|
+
`StableStringifyLimitError` extends `RangeError` and exposes `code`, `kind`,
|
|
206
|
+
`limit`, and `path`. `CanonicalizationError` extends `TypeError` and exposes
|
|
207
|
+
`code`, `reason`, and `path`.
|
|
208
|
+
|
|
209
|
+
## CommonJS
|
|
210
|
+
|
|
211
|
+
```js
|
|
212
|
+
const stringify = require('@stackline/stable-stringify');
|
|
213
|
+
|
|
214
|
+
stringify({ z: 1, a: 2 });
|
|
215
|
+
stringify.safeStringify(value);
|
|
216
|
+
stringify.canonicalize(value);
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Browser
|
|
220
|
+
|
|
221
|
+
```html
|
|
222
|
+
<script src="https://cdn.jsdelivr.net/npm/@stackline/stable-stringify/dist/index.min.js"></script>
|
|
223
|
+
<script>
|
|
224
|
+
const output = StacklineStableStringify({ z: 1, a: 2 });
|
|
225
|
+
</script>
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
The browser global is callable and includes the same helper methods as the
|
|
229
|
+
CommonJS export.
|
|
230
|
+
|
|
231
|
+
## Compatibility
|
|
232
|
+
|
|
233
|
+
The default contract is tested against `fast-json-stable-stringify@2.1.0`.
|
|
234
|
+
The regression suite includes 30,000 deterministic differential
|
|
235
|
+
serializations across generated values and comparator forms.
|
|
236
|
+
|
|
237
|
+
Intentional additions do not alter default output:
|
|
238
|
+
|
|
239
|
+
- circular arrays are handled instead of overflowing the call stack;
|
|
240
|
+
- very deep structures use an iterative task stack;
|
|
241
|
+
- replacer, indentation, BigInt, accessor, and resource policies are opt-in;
|
|
242
|
+
- safe and canonical modes are separate named APIs.
|
|
243
|
+
|
|
244
|
+
See [compatibility details](docs/COMPATIBILITY.md) before replacing a
|
|
245
|
+
serializer that relies on undocumented edge behavior.
|
|
246
|
+
|
|
247
|
+
## Runtime support
|
|
248
|
+
|
|
249
|
+
The package is tested on Node.js 14.17 through current releases, Linux,
|
|
250
|
+
Windows, macOS, Deno 2, Bun, and browsers. Type declarations are compiled in a
|
|
251
|
+
matrix from TypeScript 3.9 through 7.0.
|
|
252
|
+
|
|
253
|
+
## Performance
|
|
254
|
+
|
|
255
|
+
Run the local comparison:
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
npm run benchmark
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
The benchmark reports native `JSON.stringify`,
|
|
262
|
+
`fast-json-stable-stringify@2.1.0`, stable mode, safe mode, and RFC 8785 mode
|
|
263
|
+
on the same payload. Native JSON is shown only as a throughput baseline; it
|
|
264
|
+
does not recursively sort object keys. Measure with representative production
|
|
265
|
+
data before selecting limits or modes.
|
|
266
|
+
|
|
267
|
+
## Verification
|
|
268
|
+
|
|
269
|
+
Every release runs:
|
|
270
|
+
|
|
271
|
+
- 100% statement, line, and function coverage with at least 95% branches;
|
|
272
|
+
- differential compatibility tests;
|
|
273
|
+
- TypeScript 3.9 through 7.0 compilation;
|
|
274
|
+
- clean tarball installs for direct and aliased package names;
|
|
275
|
+
- ESM, CommonJS, browser, Deno, and Bun runtime checks;
|
|
276
|
+
- `publint`, `attw`, dependency audit, CodeQL, SBOM, and checksum generation.
|
|
277
|
+
|
|
278
|
+
The [live playground](https://alexandro.net/docs/vanilla/stable-stringify/)
|
|
279
|
+
runs the published browser bundle.
|
|
280
|
+
|
|
281
|
+
## Security
|
|
282
|
+
|
|
283
|
+
Read [SECURITY.md](SECURITY.md) for the supported versions, trust boundaries,
|
|
284
|
+
resource-limit guidance, and private reporting process.
|
|
285
|
+
|
|
286
|
+
## License
|
|
287
|
+
|
|
288
|
+
MIT. This is an independent implementation and is not affiliated with or
|
|
289
|
+
endorsed by the maintainers of the comparison packages. See [NOTICE](NOTICE)
|
|
290
|
+
for attribution and compatibility context.
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported versions
|
|
4
|
+
|
|
5
|
+
| Version | Security updates |
|
|
6
|
+
| :--- | :---: |
|
|
7
|
+
| `1.x` | Yes |
|
|
8
|
+
| `< 1.0.0` | No public releases |
|
|
9
|
+
|
|
10
|
+
## Reporting a vulnerability
|
|
11
|
+
|
|
12
|
+
Do not open a public issue for a suspected vulnerability. Use the repository's
|
|
13
|
+
[private security advisory form](https://github.com/alexandroit/stackline-stable-stringify/security/advisories/new).
|
|
14
|
+
|
|
15
|
+
Include the affected version, a minimal reproducer, observed impact, runtime,
|
|
16
|
+
module system, and proposed mitigation when available. Complete reports are
|
|
17
|
+
normally acknowledged within three business days. Confirmed issues are fixed
|
|
18
|
+
privately, covered by regression tests, and disclosed after a patched release.
|
|
19
|
+
|
|
20
|
+
## Trust boundaries
|
|
21
|
+
|
|
22
|
+
Stable mode preserves `JSON.stringify`-style behavior for compatibility. It
|
|
23
|
+
may invoke a value's `toJSON`, a replacer callback, custom comparator, or
|
|
24
|
+
property getter. Those are executable application code and must not be sourced
|
|
25
|
+
from untrusted input.
|
|
26
|
+
|
|
27
|
+
For hostile or externally supplied object graphs:
|
|
28
|
+
|
|
29
|
+
- use `accessors: 'omit'` or `accessors: 'throw'`;
|
|
30
|
+
- use `toJSON: false`;
|
|
31
|
+
- set finite `maxDepth`, `maxEntries`, and `maxLength` values;
|
|
32
|
+
- do not accept comparator or replacer functions from users.
|
|
33
|
+
|
|
34
|
+
`safeStringify` applies finite defaults but preserves compatible getter and
|
|
35
|
+
`toJSON` behavior unless explicitly disabled. Its fallback prevents a logging
|
|
36
|
+
path from throwing by default; use `throwOnError: true` when failure must be
|
|
37
|
+
observable by the caller.
|
|
38
|
+
|
|
39
|
+
## Canonical mode
|
|
40
|
+
|
|
41
|
+
`canonicalize` is the strictest input boundary. It does not invoke getters or
|
|
42
|
+
`toJSON` and rejects accessors, class instances, symbol members, cycles, sparse
|
|
43
|
+
arrays, non-finite numbers, BigInt, and malformed Unicode. It still traverses
|
|
44
|
+
the supplied data, so keep finite limits when the input size is attacker
|
|
45
|
+
controlled.
|
|
46
|
+
|
|
47
|
+
Canonical JSON only provides deterministic bytes. It does not validate domain
|
|
48
|
+
schemas, authenticate input, create signatures, or choose a cryptographic hash.
|
|
49
|
+
|
|
50
|
+
## Denial-of-service controls
|
|
51
|
+
|
|
52
|
+
The iterative traversal avoids JavaScript call-stack exhaustion. Memory and
|
|
53
|
+
CPU use still grow with visited input and produced output. Resource limits are
|
|
54
|
+
part of the security contract for public endpoints, log ingestion, and build
|
|
55
|
+
systems processing third-party data.
|