@traffic-twin/sdk 0.0.0-stage → 0.1.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/LICENSE +202 -0
- package/README.md +171 -2
- package/dist/cjs/agent.d.ts +143 -0
- package/dist/cjs/agent.js +927 -0
- package/dist/cjs/capture/body.d.ts +11 -0
- package/dist/cjs/capture/body.js +73 -0
- package/dist/cjs/capture/common.d.ts +75 -0
- package/dist/cjs/capture/common.js +180 -0
- package/dist/cjs/capture/context.d.ts +28 -0
- package/dist/cjs/capture/context.js +92 -0
- package/dist/cjs/capture/inbound.d.ts +12 -0
- package/dist/cjs/capture/inbound.js +195 -0
- package/dist/cjs/capture/outbound-http.d.ts +4 -0
- package/dist/cjs/capture/outbound-http.js +200 -0
- package/dist/cjs/capture/outbound-undici.d.ts +2 -0
- package/dist/cjs/capture/outbound-undici.js +242 -0
- package/dist/cjs/capture/wire.d.ts +27 -0
- package/dist/cjs/capture/wire.js +68 -0
- package/dist/cjs/export/event.d.ts +46 -0
- package/dist/cjs/export/event.js +91 -0
- package/dist/cjs/export/exporter.d.ts +50 -0
- package/dist/cjs/export/exporter.js +192 -0
- package/dist/cjs/export/pipeline.d.ts +20 -0
- package/dist/cjs/export/pipeline.js +40 -0
- package/dist/cjs/fleet/asn1.d.ts +44 -0
- package/dist/cjs/fleet/asn1.js +150 -0
- package/dist/cjs/fleet/client.d.ts +84 -0
- package/dist/cjs/fleet/client.js +216 -0
- package/dist/cjs/fleet/http.d.ts +17 -0
- package/dist/cjs/fleet/http.js +80 -0
- package/dist/cjs/fleet/store.d.ts +18 -0
- package/dist/cjs/fleet/store.js +115 -0
- package/dist/cjs/index.d.ts +14 -0
- package/dist/cjs/index.js +69 -0
- package/dist/cjs/log.d.ts +4 -0
- package/dist/cjs/log.js +30 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/register.d.ts +1 -0
- package/dist/cjs/register.js +18 -0
- package/dist/cjs/sanitizer/bodies.d.ts +55 -0
- package/dist/cjs/sanitizer/bodies.js +710 -0
- package/dist/cjs/sanitizer/formats.d.ts +26 -0
- package/dist/cjs/sanitizer/formats.js +110 -0
- package/dist/cjs/sanitizer/graphql.d.ts +23 -0
- package/dist/cjs/sanitizer/graphql.js +478 -0
- package/dist/cjs/sanitizer/hostpat.d.ts +15 -0
- package/dist/cjs/sanitizer/hostpat.js +129 -0
- package/dist/cjs/sanitizer/json.d.ts +60 -0
- package/dist/cjs/sanitizer/json.js +460 -0
- package/dist/cjs/sanitizer/policy.d.ts +224 -0
- package/dist/cjs/sanitizer/policy.js +820 -0
- package/dist/cjs/sanitizer/sanitizer.d.ts +149 -0
- package/dist/cjs/sanitizer/sanitizer.js +1473 -0
- package/dist/cjs/sanitizer/utf8.d.ts +22 -0
- package/dist/cjs/sanitizer/utf8.js +180 -0
- package/dist/cjs/sanitizer/vectors.d.ts +22 -0
- package/dist/cjs/sanitizer/vectors.js +130 -0
- package/dist/cjs/version.d.ts +2 -0
- package/dist/cjs/version.js +5 -0
- package/dist/cjs/worker/dir.d.ts +1 -0
- package/dist/cjs/worker/dir.js +8 -0
- package/dist/cjs/worker/pool.d.ts +47 -0
- package/dist/cjs/worker/pool.js +179 -0
- package/dist/cjs/worker/protocol.d.ts +37 -0
- package/dist/cjs/worker/protocol.js +3 -0
- package/dist/cjs/worker/sanitizer-worker.d.ts +1 -0
- package/dist/cjs/worker/sanitizer-worker.js +53 -0
- package/dist/esm/agent.d.ts +143 -0
- package/dist/esm/agent.js +921 -0
- package/dist/esm/capture/body.d.ts +11 -0
- package/dist/esm/capture/body.js +69 -0
- package/dist/esm/capture/common.d.ts +75 -0
- package/dist/esm/capture/common.js +166 -0
- package/dist/esm/capture/context.d.ts +28 -0
- package/dist/esm/capture/context.js +84 -0
- package/dist/esm/capture/inbound.d.ts +12 -0
- package/dist/esm/capture/inbound.js +187 -0
- package/dist/esm/capture/outbound-http.d.ts +4 -0
- package/dist/esm/capture/outbound-http.js +193 -0
- package/dist/esm/capture/outbound-undici.d.ts +2 -0
- package/dist/esm/capture/outbound-undici.js +236 -0
- package/dist/esm/capture/wire.d.ts +27 -0
- package/dist/esm/capture/wire.js +65 -0
- package/dist/esm/export/event.d.ts +46 -0
- package/dist/esm/export/event.js +85 -0
- package/dist/esm/export/exporter.d.ts +50 -0
- package/dist/esm/export/exporter.js +188 -0
- package/dist/esm/export/pipeline.d.ts +20 -0
- package/dist/esm/export/pipeline.js +37 -0
- package/dist/esm/fleet/asn1.d.ts +44 -0
- package/dist/esm/fleet/asn1.js +128 -0
- package/dist/esm/fleet/client.d.ts +84 -0
- package/dist/esm/fleet/client.js +204 -0
- package/dist/esm/fleet/http.d.ts +17 -0
- package/dist/esm/fleet/http.js +73 -0
- package/dist/esm/fleet/store.d.ts +18 -0
- package/dist/esm/fleet/store.js +110 -0
- package/dist/esm/index.d.ts +14 -0
- package/dist/esm/index.js +48 -0
- package/dist/esm/log.d.ts +4 -0
- package/dist/esm/log.js +25 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/register.d.ts +1 -0
- package/dist/esm/register.js +16 -0
- package/dist/esm/sanitizer/bodies.d.ts +55 -0
- package/dist/esm/sanitizer/bodies.js +694 -0
- package/dist/esm/sanitizer/formats.d.ts +26 -0
- package/dist/esm/sanitizer/formats.js +101 -0
- package/dist/esm/sanitizer/graphql.d.ts +23 -0
- package/dist/esm/sanitizer/graphql.js +472 -0
- package/dist/esm/sanitizer/hostpat.d.ts +15 -0
- package/dist/esm/sanitizer/hostpat.js +122 -0
- package/dist/esm/sanitizer/json.d.ts +60 -0
- package/dist/esm/sanitizer/json.js +447 -0
- package/dist/esm/sanitizer/policy.d.ts +224 -0
- package/dist/esm/sanitizer/policy.js +800 -0
- package/dist/esm/sanitizer/sanitizer.d.ts +149 -0
- package/dist/esm/sanitizer/sanitizer.js +1459 -0
- package/dist/esm/sanitizer/utf8.d.ts +22 -0
- package/dist/esm/sanitizer/utf8.js +166 -0
- package/dist/esm/sanitizer/vectors.d.ts +22 -0
- package/dist/esm/sanitizer/vectors.js +125 -0
- package/dist/esm/version.d.ts +2 -0
- package/dist/esm/version.js +2 -0
- package/dist/esm/worker/dir.d.ts +1 -0
- package/dist/esm/worker/dir.js +5 -0
- package/dist/esm/worker/pool.d.ts +47 -0
- package/dist/esm/worker/pool.js +175 -0
- package/dist/esm/worker/protocol.d.ts +37 -0
- package/dist/esm/worker/protocol.js +2 -0
- package/dist/esm/worker/sanitizer-worker.d.ts +1 -0
- package/dist/esm/worker/sanitizer-worker.js +51 -0
- package/package.json +75 -4
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Host patterns and IP classes, docs/spec/sanitizer.md 3.1 (IPv6 literals), 3.5 and 3.6 (spec v1.5).
|
|
3
|
+
//
|
|
4
|
+
// Everything here is written from the spec: the IPv6 parser is our own (no node:net helpers,
|
|
5
|
+
// whose accepted forms differ between versions) and the private ranges are the closed table
|
|
6
|
+
// of 3.6 (no "is private" library function).
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.hostLabelSafe = hostLabelSafe;
|
|
9
|
+
exports.parseIpv6 = parseIpv6;
|
|
10
|
+
exports.ipv4Private = ipv4Private;
|
|
11
|
+
exports.ipv6Private = ipv6Private;
|
|
12
|
+
exports.hostPattern = hostPattern;
|
|
13
|
+
const formats_js_1 = require("./formats.js");
|
|
14
|
+
const SAFE_LABEL = /^[a-z][a-z0-9-]{0,62}$/; // 3.5 rule a
|
|
15
|
+
const SIX_DIGITS = /[0-9]{6}/; // rule b
|
|
16
|
+
const HEX12 = /[0-9a-f]{12}/; // rule c
|
|
17
|
+
const HEX_GROUP = /^[0-9a-f]{1,4}$/;
|
|
18
|
+
/** 3.5: a host label stays literal only if every rule holds. */
|
|
19
|
+
function hostLabelSafe(label) {
|
|
20
|
+
return (SAFE_LABEL.test(label) &&
|
|
21
|
+
!SIX_DIGITS.test(label) &&
|
|
22
|
+
!HEX12.test(label) &&
|
|
23
|
+
(0, formats_js_1.detectFormat)(label) === null // rule d (implied by c, kept for fidelity)
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
function groups(parts, tailOk) {
|
|
27
|
+
const out = [];
|
|
28
|
+
for (let k = 0; k < parts.length; k++) {
|
|
29
|
+
const g = parts[k];
|
|
30
|
+
if (tailOk && k === parts.length - 1 && g.includes('.')) {
|
|
31
|
+
if (!formats_js_1.RE_IPV4.test(g))
|
|
32
|
+
return null;
|
|
33
|
+
const o = g.split('.').map((x) => Number(x));
|
|
34
|
+
out.push((o[0] << 8) | o[1], (o[2] << 8) | o[3]);
|
|
35
|
+
}
|
|
36
|
+
else if (HEX_GROUP.test(g)) {
|
|
37
|
+
out.push(parseInt(g, 16));
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* 3.1 point 5: RFC 4291 2.2 text form of an already lowercased string. 1-4 hex digits per
|
|
47
|
+
* group, at most one `::` standing for at least one group, an optional dotted IPv4 (6.3
|
|
48
|
+
* `ipv4`) as the last 32 bits, no zone. Returns the 16 address bytes or null.
|
|
49
|
+
*/
|
|
50
|
+
function parseIpv6(s) {
|
|
51
|
+
let g;
|
|
52
|
+
const first = s.indexOf('::');
|
|
53
|
+
if (first >= 0) {
|
|
54
|
+
if (s.indexOf('::', first + 1) >= 0)
|
|
55
|
+
return null;
|
|
56
|
+
const left = s.slice(0, first);
|
|
57
|
+
const right = s.slice(first + 2);
|
|
58
|
+
const a = groups(left === '' ? [] : left.split(':'), false);
|
|
59
|
+
const b = groups(right === '' ? [] : right.split(':'), true);
|
|
60
|
+
if (!a || !b || a.length + b.length > 7)
|
|
61
|
+
return null;
|
|
62
|
+
g = [...a, ...new Array(8 - a.length - b.length).fill(0), ...b];
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
const r = groups(s.split(':'), true);
|
|
66
|
+
if (!r || r.length !== 8)
|
|
67
|
+
return null;
|
|
68
|
+
g = r;
|
|
69
|
+
}
|
|
70
|
+
const out = new Uint8Array(16);
|
|
71
|
+
g.forEach((x, i) => {
|
|
72
|
+
out[2 * i] = x >> 8;
|
|
73
|
+
out[2 * i + 1] = x & 0xff;
|
|
74
|
+
});
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
/** True if the first `bits` bits of `addr` equal those of `prefix` (same length arrays). */
|
|
78
|
+
function inPrefix(addr, prefix, bits) {
|
|
79
|
+
let i = 0;
|
|
80
|
+
for (; bits >= 8; bits -= 8, i++)
|
|
81
|
+
if (addr[i] !== prefix[i])
|
|
82
|
+
return false;
|
|
83
|
+
if (bits > 0) {
|
|
84
|
+
const mask = (0xff << (8 - bits)) & 0xff;
|
|
85
|
+
if ((addr[i] & mask) !== (prefix[i] & mask))
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
// 3.6: every address outside these ranges is public.
|
|
91
|
+
const IPV4_PRIVATE = [
|
|
92
|
+
[[0, 0, 0, 0], 8], [[10, 0, 0, 0], 8], [[100, 64, 0, 0], 10], [[127, 0, 0, 0], 8],
|
|
93
|
+
[[169, 254, 0, 0], 16], [[172, 16, 0, 0], 12], [[192, 0, 0, 0], 24], [[192, 0, 2, 0], 24],
|
|
94
|
+
[[192, 168, 0, 0], 16], [[198, 18, 0, 0], 15], [[198, 51, 100, 0], 24], [[203, 0, 113, 0], 24],
|
|
95
|
+
[[224, 0, 0, 0], 4], [[240, 0, 0, 0], 4],
|
|
96
|
+
];
|
|
97
|
+
const v6 = (s) => parseIpv6(s);
|
|
98
|
+
const IPV6_EMBEDDED_V4 = [[v6('::ffff:0:0'), 96], [v6('64:ff9b::'), 96]];
|
|
99
|
+
const IPV6_PRIVATE = [
|
|
100
|
+
[v6('::'), 96], [v6('64:ff9b:1::'), 48], [v6('100::'), 64], [v6('2001::'), 23], [v6('2001:db8::'), 32],
|
|
101
|
+
[v6('fc00::'), 7], [v6('fe80::'), 10], [v6('fec0::'), 10], [v6('ff00::'), 8],
|
|
102
|
+
];
|
|
103
|
+
function ipv4Private(a) {
|
|
104
|
+
return IPV4_PRIVATE.some(([p, n]) => inPrefix(a, p, n));
|
|
105
|
+
}
|
|
106
|
+
function ipv6Private(a) {
|
|
107
|
+
// The two ranges with an embedded IPv4 come first (3.6).
|
|
108
|
+
for (const [p, n] of IPV6_EMBEDDED_V4)
|
|
109
|
+
if (inPrefix(a, p, n))
|
|
110
|
+
return ipv4Private(a.subarray(12));
|
|
111
|
+
return IPV6_PRIVATE.some(([p, n]) => inPrefix(a, p, n));
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* 3.5: pattern of a valid host (3.1), without the `host:` prefix, or null when a label is
|
|
115
|
+
* empty (`host:invalid`). `port` is the normalized port (null when absent or default).
|
|
116
|
+
*/
|
|
117
|
+
function hostPattern(host, port, ip6) {
|
|
118
|
+
const suffix = port ? ':' + port : '';
|
|
119
|
+
if (ip6)
|
|
120
|
+
return 'ip:' + (ipv6Private(ip6) ? 'private' : 'public') + suffix;
|
|
121
|
+
const h = host.endsWith('.') ? host.slice(0, -1) : host;
|
|
122
|
+
const labels = h.split('.');
|
|
123
|
+
if (labels.some((l) => l === ''))
|
|
124
|
+
return null;
|
|
125
|
+
if (formats_js_1.RE_IPV4.test(h)) {
|
|
126
|
+
return 'ip:' + (ipv4Private(labels.map((x) => Number(x))) ? 'private' : 'public') + suffix;
|
|
127
|
+
}
|
|
128
|
+
return labels.map((l) => (hostLabelSafe(l) ? l : '{*}')).join('.') + suffix;
|
|
129
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export type JNode = JObj | JArr | JStr | JNum | JBool | JNull;
|
|
2
|
+
export interface JObj {
|
|
3
|
+
k: 'o';
|
|
4
|
+
m: Array<[JStr, JNode]>;
|
|
5
|
+
}
|
|
6
|
+
export interface JArr {
|
|
7
|
+
k: 'a';
|
|
8
|
+
a: JNode[];
|
|
9
|
+
}
|
|
10
|
+
export interface JStr {
|
|
11
|
+
k: 's';
|
|
12
|
+
s: string;
|
|
13
|
+
bad: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface JNum {
|
|
16
|
+
k: 'n';
|
|
17
|
+
lex: string;
|
|
18
|
+
}
|
|
19
|
+
export interface JBool {
|
|
20
|
+
k: 'b';
|
|
21
|
+
v: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface JNull {
|
|
24
|
+
k: 'z';
|
|
25
|
+
}
|
|
26
|
+
export declare class JsonError extends Error {
|
|
27
|
+
constructor(msg: string);
|
|
28
|
+
}
|
|
29
|
+
/** Parses a complete JSON document. Throws JsonError on any deviation from RFC 8259. */
|
|
30
|
+
export declare function parseJson(input: Uint8Array): JNode;
|
|
31
|
+
export declare const INT_LEXEME: RegExp;
|
|
32
|
+
/** 5.3: lexeme of at most 64 bytes and |written exponent| <= 9999. */
|
|
33
|
+
export declare function lexemeWithinLimits(lex: string): boolean;
|
|
34
|
+
/** Canonical form of a valid JSON number lexeme, computed in exact decimal arithmetic. */
|
|
35
|
+
export declare function canonicalNumber(lex: string): string;
|
|
36
|
+
/** A number already in canonical form. */
|
|
37
|
+
export declare class CNum {
|
|
38
|
+
readonly canon: string;
|
|
39
|
+
constructor(canon: string);
|
|
40
|
+
}
|
|
41
|
+
export type Out = string | boolean | null | CNum | Out[] | OMap;
|
|
42
|
+
export type OMap = Map<string, Out>;
|
|
43
|
+
export declare function writeString(s: string): string;
|
|
44
|
+
export declare function writeCanonical(v: Out): string;
|
|
45
|
+
/** Converts a parsed JSON document to an Out tree (numbers canonicalized). Used for policy and event documents. */
|
|
46
|
+
export declare function nodeToOut(node: JNode): Out;
|
|
47
|
+
/** Plain JS value with numbers kept as lexeme wrappers. */
|
|
48
|
+
export declare class Lex {
|
|
49
|
+
readonly lex: string;
|
|
50
|
+
constructor(lex: string);
|
|
51
|
+
toNumber(): number;
|
|
52
|
+
}
|
|
53
|
+
export type Plain = string | boolean | null | Lex | Plain[] | {
|
|
54
|
+
[k: string]: Plain;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Converts a parsed document to plain objects (null-prototype), rejecting duplicate keys
|
|
58
|
+
* and lone surrogates (used for policy bodies and envelopes: fail closed).
|
|
59
|
+
*/
|
|
60
|
+
export declare function nodeToPlain(node: JNode, strict: boolean): Plain;
|
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Strict RFC 8259 JSON parser that keeps number lexemes (sanitizer.md 9.2), the tt-cjson
|
|
3
|
+
// canonical writer (10.1) and the canonical number form (10.2).
|
|
4
|
+
//
|
|
5
|
+
// The parser is iterative (no recursion), validates the whole input, rejects a leading
|
|
6
|
+
// BOM, invalid UTF-8, control characters in strings, and anything outside RFC 8259.
|
|
7
|
+
// Lone surrogates produced by \u escapes are allowed syntactically and flagged on the
|
|
8
|
+
// string node (`bad`), as 9.2 requires.
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.Lex = exports.CNum = exports.INT_LEXEME = exports.JsonError = void 0;
|
|
11
|
+
exports.parseJson = parseJson;
|
|
12
|
+
exports.lexemeWithinLimits = lexemeWithinLimits;
|
|
13
|
+
exports.canonicalNumber = canonicalNumber;
|
|
14
|
+
exports.writeString = writeString;
|
|
15
|
+
exports.writeCanonical = writeCanonical;
|
|
16
|
+
exports.nodeToOut = nodeToOut;
|
|
17
|
+
exports.nodeToPlain = nodeToPlain;
|
|
18
|
+
const utf8_js_1 = require("./utf8.js");
|
|
19
|
+
class JsonError extends Error {
|
|
20
|
+
constructor(msg) {
|
|
21
|
+
super(msg);
|
|
22
|
+
this.name = 'JsonError';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.JsonError = JsonError;
|
|
26
|
+
const strictDecoder = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true });
|
|
27
|
+
const NULL_NODE = { k: 'z' };
|
|
28
|
+
const TRUE_NODE = { k: 'b', v: true };
|
|
29
|
+
const FALSE_NODE = { k: 'b', v: false };
|
|
30
|
+
function isWs(c) {
|
|
31
|
+
return c === 0x20 || c === 0x09 || c === 0x0a || c === 0x0d;
|
|
32
|
+
}
|
|
33
|
+
function hex4(buf, i) {
|
|
34
|
+
let v = 0;
|
|
35
|
+
for (let j = 0; j < 4; j++) {
|
|
36
|
+
const c = buf[i + j];
|
|
37
|
+
let d;
|
|
38
|
+
if (c === undefined)
|
|
39
|
+
throw new JsonError('eof in escape');
|
|
40
|
+
if (c >= 0x30 && c <= 0x39)
|
|
41
|
+
d = c - 0x30;
|
|
42
|
+
else if (c >= 0x41 && c <= 0x46)
|
|
43
|
+
d = c - 0x37;
|
|
44
|
+
else if (c >= 0x61 && c <= 0x66)
|
|
45
|
+
d = c - 0x57;
|
|
46
|
+
else
|
|
47
|
+
throw new JsonError('bad hex escape');
|
|
48
|
+
v = v * 16 + d;
|
|
49
|
+
}
|
|
50
|
+
return v;
|
|
51
|
+
}
|
|
52
|
+
/** Parses a complete JSON document. Throws JsonError on any deviation from RFC 8259. */
|
|
53
|
+
function parseJson(input) {
|
|
54
|
+
const buf = input;
|
|
55
|
+
const n = buf.length;
|
|
56
|
+
let i = 0;
|
|
57
|
+
if (n >= 3 && buf[0] === 0xef && buf[1] === 0xbb && buf[2] === 0xbf)
|
|
58
|
+
throw new JsonError('bom');
|
|
59
|
+
const ws = () => {
|
|
60
|
+
while (i < n && isWs(buf[i]))
|
|
61
|
+
i++;
|
|
62
|
+
};
|
|
63
|
+
const readString = () => {
|
|
64
|
+
// buf[i] === '"'
|
|
65
|
+
i++;
|
|
66
|
+
let out = '';
|
|
67
|
+
let start = i;
|
|
68
|
+
let nonAscii = false;
|
|
69
|
+
const flush = (end) => {
|
|
70
|
+
if (end > start) {
|
|
71
|
+
if (nonAscii) {
|
|
72
|
+
try {
|
|
73
|
+
out += strictDecoder.decode(buf.subarray(start, end));
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
throw new JsonError('invalid utf-8');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
out += Buffer.from(buf.buffer, buf.byteOffset + start, end - start).toString('latin1');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
nonAscii = false;
|
|
84
|
+
};
|
|
85
|
+
for (;;) {
|
|
86
|
+
if (i >= n)
|
|
87
|
+
throw new JsonError('unterminated string');
|
|
88
|
+
const c = buf[i];
|
|
89
|
+
if (c === 0x22) {
|
|
90
|
+
flush(i);
|
|
91
|
+
i++;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
if (c === 0x5c) {
|
|
95
|
+
flush(i);
|
|
96
|
+
const e = buf[i + 1];
|
|
97
|
+
switch (e) {
|
|
98
|
+
case 0x22:
|
|
99
|
+
out += '"';
|
|
100
|
+
i += 2;
|
|
101
|
+
break;
|
|
102
|
+
case 0x5c:
|
|
103
|
+
out += '\\';
|
|
104
|
+
i += 2;
|
|
105
|
+
break;
|
|
106
|
+
case 0x2f:
|
|
107
|
+
out += '/';
|
|
108
|
+
i += 2;
|
|
109
|
+
break;
|
|
110
|
+
case 0x62:
|
|
111
|
+
out += '\b';
|
|
112
|
+
i += 2;
|
|
113
|
+
break;
|
|
114
|
+
case 0x66:
|
|
115
|
+
out += '\f';
|
|
116
|
+
i += 2;
|
|
117
|
+
break;
|
|
118
|
+
case 0x6e:
|
|
119
|
+
out += '\n';
|
|
120
|
+
i += 2;
|
|
121
|
+
break;
|
|
122
|
+
case 0x72:
|
|
123
|
+
out += '\r';
|
|
124
|
+
i += 2;
|
|
125
|
+
break;
|
|
126
|
+
case 0x74:
|
|
127
|
+
out += '\t';
|
|
128
|
+
i += 2;
|
|
129
|
+
break;
|
|
130
|
+
case 0x75:
|
|
131
|
+
out += String.fromCharCode(hex4(buf, i + 2));
|
|
132
|
+
i += 6;
|
|
133
|
+
break;
|
|
134
|
+
default: throw new JsonError('bad escape');
|
|
135
|
+
}
|
|
136
|
+
start = i;
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (c < 0x20)
|
|
140
|
+
throw new JsonError('control character in string');
|
|
141
|
+
if (c >= 0x80)
|
|
142
|
+
nonAscii = true;
|
|
143
|
+
i++;
|
|
144
|
+
}
|
|
145
|
+
return { k: 's', s: out, bad: !(0, utf8_js_1.isWellFormed)(out) };
|
|
146
|
+
};
|
|
147
|
+
const readNumber = () => {
|
|
148
|
+
const s = i;
|
|
149
|
+
if (buf[i] === 0x2d)
|
|
150
|
+
i++;
|
|
151
|
+
if (buf[i] === 0x30) {
|
|
152
|
+
i++;
|
|
153
|
+
}
|
|
154
|
+
else if (buf[i] !== undefined && buf[i] >= 0x31 && buf[i] <= 0x39) {
|
|
155
|
+
while (i < n && buf[i] >= 0x30 && buf[i] <= 0x39)
|
|
156
|
+
i++;
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
throw new JsonError('bad number');
|
|
160
|
+
}
|
|
161
|
+
if (buf[i] === 0x2e) {
|
|
162
|
+
i++;
|
|
163
|
+
const f = i;
|
|
164
|
+
while (i < n && buf[i] >= 0x30 && buf[i] <= 0x39)
|
|
165
|
+
i++;
|
|
166
|
+
if (i === f)
|
|
167
|
+
throw new JsonError('bad fraction');
|
|
168
|
+
}
|
|
169
|
+
if (buf[i] === 0x65 || buf[i] === 0x45) {
|
|
170
|
+
i++;
|
|
171
|
+
if (buf[i] === 0x2b || buf[i] === 0x2d)
|
|
172
|
+
i++;
|
|
173
|
+
const f = i;
|
|
174
|
+
while (i < n && buf[i] >= 0x30 && buf[i] <= 0x39)
|
|
175
|
+
i++;
|
|
176
|
+
if (i === f)
|
|
177
|
+
throw new JsonError('bad exponent');
|
|
178
|
+
}
|
|
179
|
+
return { k: 'n', lex: Buffer.from(buf.buffer, buf.byteOffset + s, i - s).toString('latin1') };
|
|
180
|
+
};
|
|
181
|
+
const literal = (word, node) => {
|
|
182
|
+
for (let j = 0; j < word.length; j++) {
|
|
183
|
+
if (buf[i + j] !== word.charCodeAt(j))
|
|
184
|
+
throw new JsonError('bad literal');
|
|
185
|
+
}
|
|
186
|
+
i += word.length;
|
|
187
|
+
return node;
|
|
188
|
+
};
|
|
189
|
+
const readKey = () => {
|
|
190
|
+
if (buf[i] !== 0x22)
|
|
191
|
+
throw new JsonError('expected key');
|
|
192
|
+
const k = readString();
|
|
193
|
+
ws();
|
|
194
|
+
if (buf[i] !== 0x3a)
|
|
195
|
+
throw new JsonError('expected colon');
|
|
196
|
+
i++;
|
|
197
|
+
ws();
|
|
198
|
+
return k;
|
|
199
|
+
};
|
|
200
|
+
const stack = [];
|
|
201
|
+
ws();
|
|
202
|
+
for (;;) {
|
|
203
|
+
let v;
|
|
204
|
+
const c = buf[i];
|
|
205
|
+
if (c === 0x7b) {
|
|
206
|
+
i++;
|
|
207
|
+
ws();
|
|
208
|
+
const o = { k: 'o', m: [] };
|
|
209
|
+
if (buf[i] === 0x7d) {
|
|
210
|
+
i++;
|
|
211
|
+
v = o;
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
stack.push({ node: o, key: readKey() });
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
else if (c === 0x5b) {
|
|
219
|
+
i++;
|
|
220
|
+
ws();
|
|
221
|
+
const a = { k: 'a', a: [] };
|
|
222
|
+
if (buf[i] === 0x5d) {
|
|
223
|
+
i++;
|
|
224
|
+
v = a;
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
stack.push({ node: a, key: null });
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
else if (c === 0x22) {
|
|
232
|
+
v = readString();
|
|
233
|
+
}
|
|
234
|
+
else if (c === 0x2d || (c !== undefined && c >= 0x30 && c <= 0x39)) {
|
|
235
|
+
v = readNumber();
|
|
236
|
+
}
|
|
237
|
+
else if (c === 0x74) {
|
|
238
|
+
v = literal('true', TRUE_NODE);
|
|
239
|
+
}
|
|
240
|
+
else if (c === 0x66) {
|
|
241
|
+
v = literal('false', FALSE_NODE);
|
|
242
|
+
}
|
|
243
|
+
else if (c === 0x6e) {
|
|
244
|
+
v = literal('null', NULL_NODE);
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
throw new JsonError('unexpected character');
|
|
248
|
+
}
|
|
249
|
+
// Attach v to its parent, closing containers as needed.
|
|
250
|
+
for (;;) {
|
|
251
|
+
const f = stack[stack.length - 1];
|
|
252
|
+
if (f === undefined) {
|
|
253
|
+
ws();
|
|
254
|
+
if (i !== n)
|
|
255
|
+
throw new JsonError('trailing data');
|
|
256
|
+
return v;
|
|
257
|
+
}
|
|
258
|
+
if (f.node.k === 'o')
|
|
259
|
+
f.node.m.push([f.key, v]);
|
|
260
|
+
else
|
|
261
|
+
f.node.a.push(v);
|
|
262
|
+
ws();
|
|
263
|
+
const d = buf[i];
|
|
264
|
+
if (d === 0x2c) {
|
|
265
|
+
i++;
|
|
266
|
+
ws();
|
|
267
|
+
if (f.node.k === 'o')
|
|
268
|
+
f.key = readKey();
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
if ((f.node.k === 'o' && d === 0x7d) || (f.node.k === 'a' && d === 0x5d)) {
|
|
272
|
+
i++;
|
|
273
|
+
stack.pop();
|
|
274
|
+
v = f.node;
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
throw new JsonError('expected separator');
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
// ---------------------------------------------------------------------------------
|
|
282
|
+
// Canonical numbers (10.2)
|
|
283
|
+
exports.INT_LEXEME = /^-?(0|[1-9][0-9]*)$/;
|
|
284
|
+
/** 5.3: lexeme of at most 64 bytes and |written exponent| <= 9999. */
|
|
285
|
+
function lexemeWithinLimits(lex) {
|
|
286
|
+
if (lex.length > 64)
|
|
287
|
+
return false;
|
|
288
|
+
let e = lex.indexOf('e');
|
|
289
|
+
if (e < 0)
|
|
290
|
+
e = lex.indexOf('E');
|
|
291
|
+
if (e < 0)
|
|
292
|
+
return true;
|
|
293
|
+
let d = lex.slice(e + 1);
|
|
294
|
+
if (d[0] === '+' || d[0] === '-')
|
|
295
|
+
d = d.slice(1);
|
|
296
|
+
d = d.replace(/^0+/, '');
|
|
297
|
+
return d.length <= 4;
|
|
298
|
+
}
|
|
299
|
+
/** Canonical form of a valid JSON number lexeme, computed in exact decimal arithmetic. */
|
|
300
|
+
function canonicalNumber(lex) {
|
|
301
|
+
let i = 0;
|
|
302
|
+
let neg = false;
|
|
303
|
+
if (lex[0] === '-') {
|
|
304
|
+
neg = true;
|
|
305
|
+
i = 1;
|
|
306
|
+
}
|
|
307
|
+
let j = i;
|
|
308
|
+
while (j < lex.length && lex.charCodeAt(j) >= 0x30 && lex.charCodeAt(j) <= 0x39)
|
|
309
|
+
j++;
|
|
310
|
+
const intp = lex.slice(i, j);
|
|
311
|
+
let frac = '';
|
|
312
|
+
if (lex[j] === '.') {
|
|
313
|
+
let k = j + 1;
|
|
314
|
+
while (k < lex.length && lex.charCodeAt(k) >= 0x30 && lex.charCodeAt(k) <= 0x39)
|
|
315
|
+
k++;
|
|
316
|
+
frac = lex.slice(j + 1, k);
|
|
317
|
+
j = k;
|
|
318
|
+
}
|
|
319
|
+
let exp = 0;
|
|
320
|
+
if (lex[j] === 'e' || lex[j] === 'E')
|
|
321
|
+
exp = parseInt(lex.slice(j + 1), 10);
|
|
322
|
+
let digits = intp + frac;
|
|
323
|
+
exp -= frac.length;
|
|
324
|
+
let s = 0;
|
|
325
|
+
while (s < digits.length && digits[s] === '0')
|
|
326
|
+
s++;
|
|
327
|
+
digits = digits.slice(s);
|
|
328
|
+
let t = digits.length;
|
|
329
|
+
while (t > 0 && digits[t - 1] === '0')
|
|
330
|
+
t--;
|
|
331
|
+
exp += digits.length - t;
|
|
332
|
+
digits = digits.slice(0, t);
|
|
333
|
+
if (digits.length === 0)
|
|
334
|
+
return '0';
|
|
335
|
+
const nd = digits.length;
|
|
336
|
+
const e = exp + nd;
|
|
337
|
+
let out;
|
|
338
|
+
if (e > -6 && e <= 21) {
|
|
339
|
+
if (e <= 0)
|
|
340
|
+
out = '0.' + '0'.repeat(-e) + digits;
|
|
341
|
+
else if (e >= nd)
|
|
342
|
+
out = digits + '0'.repeat(e - nd);
|
|
343
|
+
else
|
|
344
|
+
out = digits.slice(0, e) + '.' + digits.slice(e);
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
const k = e - 1;
|
|
348
|
+
out = digits[0] + (nd > 1 ? '.' + digits.slice(1) : '') + 'e' + (k >= 0 ? '+' : '-') + Math.abs(k);
|
|
349
|
+
}
|
|
350
|
+
return (neg ? '-' : '') + out;
|
|
351
|
+
}
|
|
352
|
+
// ---------------------------------------------------------------------------------
|
|
353
|
+
// Output values and the tt-cjson writer (10.1)
|
|
354
|
+
/** A number already in canonical form. */
|
|
355
|
+
class CNum {
|
|
356
|
+
canon;
|
|
357
|
+
constructor(canon) {
|
|
358
|
+
this.canon = canon;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
exports.CNum = CNum;
|
|
362
|
+
const NEEDS_ESCAPE = /["\\\u0000-\u001f\u007f-]/;
|
|
363
|
+
function writeString(s) {
|
|
364
|
+
if (!NEEDS_ESCAPE.test(s))
|
|
365
|
+
return '"' + s + '"';
|
|
366
|
+
let out = '"';
|
|
367
|
+
for (let i = 0; i < s.length; i++) {
|
|
368
|
+
const c = s.charCodeAt(i);
|
|
369
|
+
if (c === 0x22)
|
|
370
|
+
out += '\\"';
|
|
371
|
+
else if (c === 0x5c)
|
|
372
|
+
out += '\\\\';
|
|
373
|
+
else if (c < 0x20 || c >= 0x7f)
|
|
374
|
+
out += '\\u' + c.toString(16).padStart(4, '0');
|
|
375
|
+
else
|
|
376
|
+
out += s[i];
|
|
377
|
+
}
|
|
378
|
+
return out + '"';
|
|
379
|
+
}
|
|
380
|
+
function writeCanonical(v) {
|
|
381
|
+
if (typeof v === 'string')
|
|
382
|
+
return writeString(v);
|
|
383
|
+
if (v === null)
|
|
384
|
+
return 'null';
|
|
385
|
+
if (v === true)
|
|
386
|
+
return 'true';
|
|
387
|
+
if (v === false)
|
|
388
|
+
return 'false';
|
|
389
|
+
if (v instanceof CNum)
|
|
390
|
+
return v.canon;
|
|
391
|
+
if (Array.isArray(v)) {
|
|
392
|
+
let out = '[';
|
|
393
|
+
for (let i = 0; i < v.length; i++) {
|
|
394
|
+
if (i > 0)
|
|
395
|
+
out += ',';
|
|
396
|
+
out += writeCanonical(v[i]);
|
|
397
|
+
}
|
|
398
|
+
return out + ']';
|
|
399
|
+
}
|
|
400
|
+
const keys = [...v.keys()].sort(utf8_js_1.cmpUtf8);
|
|
401
|
+
let out = '{';
|
|
402
|
+
for (let i = 0; i < keys.length; i++) {
|
|
403
|
+
if (i > 0)
|
|
404
|
+
out += ',';
|
|
405
|
+
out += writeString(keys[i]) + ':' + writeCanonical(v.get(keys[i]));
|
|
406
|
+
}
|
|
407
|
+
return out + '}';
|
|
408
|
+
}
|
|
409
|
+
/** Converts a parsed JSON document to an Out tree (numbers canonicalized). Used for policy and event documents. */
|
|
410
|
+
function nodeToOut(node) {
|
|
411
|
+
switch (node.k) {
|
|
412
|
+
case 'z': return null;
|
|
413
|
+
case 'b': return node.v;
|
|
414
|
+
case 'n': return new CNum(canonicalNumber(node.lex));
|
|
415
|
+
case 's': return node.s;
|
|
416
|
+
case 'a': return node.a.map(nodeToOut);
|
|
417
|
+
case 'o': {
|
|
418
|
+
const m = new Map();
|
|
419
|
+
for (const [k, v] of node.m)
|
|
420
|
+
m.set(k.s, nodeToOut(v));
|
|
421
|
+
return m;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
/** Plain JS value with numbers kept as lexeme wrappers. */
|
|
426
|
+
class Lex {
|
|
427
|
+
lex;
|
|
428
|
+
constructor(lex) {
|
|
429
|
+
this.lex = lex;
|
|
430
|
+
}
|
|
431
|
+
toNumber() {
|
|
432
|
+
return Number(this.lex);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
exports.Lex = Lex;
|
|
436
|
+
/**
|
|
437
|
+
* Converts a parsed document to plain objects (null-prototype), rejecting duplicate keys
|
|
438
|
+
* and lone surrogates (used for policy bodies and envelopes: fail closed).
|
|
439
|
+
*/
|
|
440
|
+
function nodeToPlain(node, strict) {
|
|
441
|
+
switch (node.k) {
|
|
442
|
+
case 'z': return null;
|
|
443
|
+
case 'b': return node.v;
|
|
444
|
+
case 'n': return new Lex(node.lex);
|
|
445
|
+
case 's':
|
|
446
|
+
if (strict && node.bad)
|
|
447
|
+
throw new JsonError('lone surrogate');
|
|
448
|
+
return node.s;
|
|
449
|
+
case 'a': return node.a.map((x) => nodeToPlain(x, strict));
|
|
450
|
+
case 'o': {
|
|
451
|
+
const o = Object.create(null);
|
|
452
|
+
for (const [k, v] of node.m) {
|
|
453
|
+
if (strict && (k.bad || Object.prototype.hasOwnProperty.call(o, k.s)))
|
|
454
|
+
throw new JsonError('duplicate or invalid key');
|
|
455
|
+
o[k.s] = nodeToPlain(v, strict);
|
|
456
|
+
}
|
|
457
|
+
return o;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|