@zudojs/serialization 1.2.1 → 1.2.3
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/README.md
CHANGED
|
@@ -143,6 +143,11 @@ Failures throw `@zudojs/errors` serialization classes: `SerializationPayloadTooL
|
|
|
143
143
|
or malformed tags under `strict`), `TransformerError` and `SerializeError` (for
|
|
144
144
|
example an invalid `Date`). All extend `SerializationError`.
|
|
145
145
|
|
|
146
|
+
An oversized string passed to `deserialize` is untrusted input, so its
|
|
147
|
+
`SerializationPayloadTooLargeError` is an exposed 413 whose message names only
|
|
148
|
+
the two sizes. Oversized output from `serialize` was built by the server, so
|
|
149
|
+
it is an unexposed 500 that public error responses do not describe.
|
|
150
|
+
|
|
146
151
|
### Serialized errors
|
|
147
152
|
|
|
148
153
|
Stack traces are **not** serialized unless you ask, because a serialized error
|
|
@@ -63,7 +63,7 @@ export class JSONSerializer {
|
|
|
63
63
|
const json = opts.pretty
|
|
64
64
|
? JSON.stringify(transformed, null, opts.indent ?? 2)
|
|
65
65
|
: JSON.stringify(transformed);
|
|
66
|
-
assertByteSize(json, maxSize);
|
|
66
|
+
assertByteSize(json, maxSize, "output");
|
|
67
67
|
return json;
|
|
68
68
|
}
|
|
69
69
|
// The fast path stays a bare `JSON.stringify` by default, but a depth
|
|
@@ -76,7 +76,7 @@ export class JSONSerializer {
|
|
|
76
76
|
const json = opts.pretty
|
|
77
77
|
? JSON.stringify(value, null, opts.indent ?? 2)
|
|
78
78
|
: JSON.stringify(value);
|
|
79
|
-
assertByteSize(json, maxSize);
|
|
79
|
+
assertByteSize(json, maxSize, "output");
|
|
80
80
|
return json;
|
|
81
81
|
}
|
|
82
82
|
deserialize(value, options) {
|
|
@@ -86,7 +86,7 @@ export class JSONSerializer {
|
|
|
86
86
|
// the string handed to `deserialize` arrives from a queue, an RPC peer, or
|
|
87
87
|
// a request body.
|
|
88
88
|
const maxSize = opts.maxSize ?? SerializationLimits.MAX_SIZE;
|
|
89
|
-
assertByteSize(value, maxSize);
|
|
89
|
+
assertByteSize(value, maxSize, "input");
|
|
90
90
|
const parsed = this.parse(value);
|
|
91
91
|
if (opts.preserveTypes === true) {
|
|
92
92
|
const maxDepth = opts.maxDepth ?? SerializationLimits.MAX_DEPTH;
|
|
@@ -3,11 +3,22 @@
|
|
|
3
3
|
*/
|
|
4
4
|
/** Byte length of a string, in whichever runtime we are on. */
|
|
5
5
|
export declare function byteLength(value: string): number;
|
|
6
|
+
/**
|
|
7
|
+
* Error options for an oversized payload the server built itself (the
|
|
8
|
+
* output of `serialize`): an internal error that is never shown to the
|
|
9
|
+
* client. Oversized input to `deserialize` keeps the error's default, an
|
|
10
|
+
* exposed 413.
|
|
11
|
+
*/
|
|
12
|
+
export declare const SERVER_BUILT_PAYLOAD_ERROR: Readonly<{
|
|
13
|
+
statusCode: 500;
|
|
14
|
+
expose: false;
|
|
15
|
+
}>;
|
|
6
16
|
/**
|
|
7
17
|
* Throws `SerializationPayloadTooLargeError` when a JSON string is larger
|
|
8
|
-
* than `maxSize` bytes.
|
|
18
|
+
* than `maxSize` bytes. `origin` says whose payload it is: `"input"`
|
|
19
|
+
* (untrusted, an exposed 413) or `"output"` (server-built, an unexposed 500).
|
|
9
20
|
*/
|
|
10
|
-
export declare function assertByteSize(json: string, maxSize: number): void;
|
|
21
|
+
export declare function assertByteSize(json: string, maxSize: number, origin: "input" | "output"): void;
|
|
11
22
|
/**
|
|
12
23
|
* Assigns a key onto a freshly built object without invoking a setter.
|
|
13
24
|
*
|
|
@@ -9,14 +9,28 @@ export function byteLength(value) {
|
|
|
9
9
|
? Buffer.byteLength(value, "utf-8")
|
|
10
10
|
: new TextEncoder().encode(value).byteLength;
|
|
11
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Error options for an oversized payload the server built itself (the
|
|
14
|
+
* output of `serialize`): an internal error that is never shown to the
|
|
15
|
+
* client. Oversized input to `deserialize` keeps the error's default, an
|
|
16
|
+
* exposed 413.
|
|
17
|
+
*/
|
|
18
|
+
export const SERVER_BUILT_PAYLOAD_ERROR = Object.freeze({
|
|
19
|
+
statusCode: 500,
|
|
20
|
+
expose: false,
|
|
21
|
+
});
|
|
12
22
|
/**
|
|
13
23
|
* Throws `SerializationPayloadTooLargeError` when a JSON string is larger
|
|
14
|
-
* than `maxSize` bytes.
|
|
24
|
+
* than `maxSize` bytes. `origin` says whose payload it is: `"input"`
|
|
25
|
+
* (untrusted, an exposed 413) or `"output"` (server-built, an unexposed 500).
|
|
15
26
|
*/
|
|
16
|
-
export function assertByteSize(json, maxSize) {
|
|
27
|
+
export function assertByteSize(json, maxSize, origin) {
|
|
17
28
|
const size = byteLength(json);
|
|
18
|
-
if (size
|
|
19
|
-
|
|
29
|
+
if (size <= maxSize)
|
|
30
|
+
return;
|
|
31
|
+
throw origin === "output"
|
|
32
|
+
? new SerializationPayloadTooLargeError(size, maxSize, SERVER_BUILT_PAYLOAD_ERROR)
|
|
33
|
+
: new SerializationPayloadTooLargeError(size, maxSize);
|
|
20
34
|
}
|
|
21
35
|
/**
|
|
22
36
|
* Assigns a key onto a freshly built object without invoking a setter.
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* plain objects (see `jsonSerializer.escape.ts`).
|
|
6
6
|
*/
|
|
7
7
|
import { InvalidSerializedDataError, SerializationDepthError, TransformerError, isSerializationError, } from "@zudojs/errors";
|
|
8
|
+
import { UNTRUSTED_DEPTH_ERROR } from "@zudojs/validation";
|
|
8
9
|
import { SerializationLimits, SerializationTags } from "@zudojs/constants";
|
|
9
10
|
import { isPlainObject } from "@zudojs/types";
|
|
10
11
|
import { escapedBody } from "./jsonSerializer.escape.js";
|
|
@@ -22,7 +23,10 @@ export function restoreValue(walk, value, depth) {
|
|
|
22
23
|
if (value === null || typeof value !== "object")
|
|
23
24
|
return value;
|
|
24
25
|
if (depth >= walk.maxDepth) {
|
|
25
|
-
|
|
26
|
+
// The restore walk only runs in `deserialize`, on untrusted input, so a
|
|
27
|
+
// too-deep payload is the client's fault: an exposed 400, not a hidden
|
|
28
|
+
// 500 (the serialize-side walk keeps the server-error default).
|
|
29
|
+
throw new SerializationDepthError(depth + 1, walk.maxDepth, UNTRUSTED_DEPTH_ERROR);
|
|
26
30
|
}
|
|
27
31
|
if (Array.isArray(value)) {
|
|
28
32
|
return value.map((item) => restoreValue(walk, item, depth + 1));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zudojs/serialization",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"description": "Data translation layer with JSON serializer, type transformers, envelopes, and registry for Zudojs applications.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -19,10 +19,10 @@
|
|
|
19
19
|
"LICENSE"
|
|
20
20
|
],
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@zudojs/constants": "1.1.
|
|
23
|
-
"@zudojs/errors": "1.3.
|
|
22
|
+
"@zudojs/constants": "1.1.4",
|
|
23
|
+
"@zudojs/errors": "1.3.2",
|
|
24
24
|
"@zudojs/types": "1.2.0",
|
|
25
|
-
"@zudojs/validation": "1.1.
|
|
25
|
+
"@zudojs/validation": "1.1.2"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"typescript": "7.0.2",
|