@ktuban/safe-json-loader 1.1.2 → 1.1.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 +217 -181
- package/dist/cjs/index.d.ts +3 -0
- package/dist/cjs/index.js +1 -2
- package/dist/cjs/safeJsonError.d.ts +6 -0
- package/dist/cjs/safeJsonError.js +16 -0
- package/dist/cjs/safeJsonLoader.d.ts +55 -0
- package/dist/cjs/safeJsonLoader.js +50 -15
- package/dist/cjs/types.d.ts +113 -0
- package/dist/cjs/types.js +0 -1
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.js +1 -2
- package/dist/esm/safeJsonError.d.ts +6 -0
- package/dist/esm/safeJsonError.js +12 -0
- package/dist/esm/safeJsonLoader.d.ts +55 -0
- package/dist/esm/safeJsonLoader.js +40 -7
- package/dist/esm/types.d.ts +113 -0
- package/dist/esm/types.js +0 -1
- package/dist/tsconfig.cjs.tsbuildinfo +1 -0
- package/dist/tsconfig.esm.tsbuildinfo +1 -0
- package/dist/tsconfig.types.tsbuildinfo +1 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/safeJsonError.d.ts +6 -0
- package/dist/types/safeJsonLoader.d.ts +6 -4
- package/dist/types/types.d.ts +8 -2
- package/package.json +17 -8
- package/dist/cjs/index.js.map +0 -1
- package/dist/cjs/logger.js +0 -68
- package/dist/cjs/logger.js.map +0 -1
- package/dist/cjs/safeJsonLoader.js.map +0 -1
- package/dist/cjs/types.js.map +0 -1
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/logger.js +0 -64
- package/dist/esm/logger.js.map +0 -1
- package/dist/esm/safeJsonLoader.js.map +0 -1
- package/dist/esm/types.js.map +0 -1
- package/dist/types/logger.d.ts +0 -16
package/README.md
CHANGED
|
@@ -1,260 +1,296 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @ktuban/safe-json-loader
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@ktuban/safe-json-loader)
|
|
4
|
+
[](https://www.npmjs.com/package/@ktuban/safe-json-loader)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://paypal.me/KhalilTuban)
|
|
7
|
+
[](https://ko-fi.com/ktuban)
|
|
4
8
|
|
|
5
|
-
|
|
6
|
-
- Local directories of JSON files
|
|
7
|
-
- Remote JSON URLs
|
|
8
|
-
- Remote JSON indexes (`[]` or `{ files: [] }`)
|
|
9
|
-
- Safe parsing of raw JSON strings
|
|
10
|
-
- Safe sanitization of already‑parsed JSON objects (e.g., Express `req.body`)
|
|
11
|
-
- Minimal logger interface with a default adapter to `@ktuban/structured-logger`
|
|
9
|
+
Security‑hardened **JSON loader** with prototype‑pollution protection, depth limits, safe parsing, and optional validation layers. Designed for processing untrusted JSON from files, APIs, and user input.
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
## Features
|
|
16
|
-
|
|
17
|
-
- **Security‑first design:**
|
|
18
|
-
strips `__proto__`, `constructor`, `prototype`; rebuilds objects with `Object.create(null)`; enforces maximum JSON depth; per‑file and total directory size limits; safe remote loading with timeout, content‑type validation, and concurrency limits.
|
|
19
|
-
|
|
20
|
-
- **Helpers for all entry points:**
|
|
21
|
-
`loadSafeJsonResources()` for files/directories/URLs;
|
|
22
|
-
`parseSafeJsonString()` for raw strings;
|
|
23
|
-
`sanitizeParsedJsonObject()` for already‑parsed objects.
|
|
11
|
+
## ✨ Features
|
|
24
12
|
|
|
25
|
-
- **
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
- **
|
|
29
|
-
|
|
13
|
+
- **Prototype Pollution Protection** — Detects and removes `__proto__`, `constructor`, `prototype`
|
|
14
|
+
- **Depth Limiting** — Prevent deeply nested JSON attacks (DoS prevention)
|
|
15
|
+
- **Safe Parsing** — Configurable error handling and fallback values
|
|
16
|
+
- **Type Validation** — Optional schema validation with custom validators
|
|
17
|
+
- **Remote Loading** — Safely load JSON from URLs with timeout/size limits
|
|
18
|
+
- **File Loading** — Stream-based loading for large JSON files
|
|
19
|
+
- **Detailed Diagnostics** — Warning reports for suspicious patterns
|
|
20
|
+
- **TypeScript First** — Full type definitions, strict mode
|
|
21
|
+
- **Production Ready** — Used in security-critical applications
|
|
30
22
|
|
|
31
23
|
---
|
|
32
24
|
|
|
33
|
-
## Installation
|
|
25
|
+
## 📦 Installation
|
|
34
26
|
|
|
35
27
|
```bash
|
|
36
|
-
npm install safe-json-loader
|
|
28
|
+
npm install @ktuban/safe-json-loader
|
|
37
29
|
```
|
|
38
30
|
|
|
39
|
-
Node.js 18+
|
|
31
|
+
**Requires**: Node.js 18+
|
|
40
32
|
|
|
41
33
|
---
|
|
42
34
|
|
|
43
|
-
## Quick
|
|
35
|
+
## 🚀 Quick Start
|
|
44
36
|
|
|
45
|
-
|
|
46
|
-
import { loadSafeJsonResources } from "safe-json-loader";
|
|
37
|
+
### Basic Parsing
|
|
47
38
|
|
|
48
|
-
|
|
39
|
+
```typescript
|
|
40
|
+
import { SafeJsonLoader } from "@ktuban/safe-json-loader";
|
|
49
41
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
42
|
+
const loader = new SafeJsonLoader({
|
|
43
|
+
maxDepth: 10,
|
|
44
|
+
detectPollution: true,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Safe parse with automatic protection
|
|
48
|
+
const result = loader.parse('{"user": {"name": "John"}}');
|
|
49
|
+
console.log(result.data); // { user: { name: "John" } }
|
|
54
50
|
```
|
|
55
51
|
|
|
56
|
-
|
|
52
|
+
### Prototype Pollution Detection
|
|
57
53
|
|
|
58
|
-
|
|
54
|
+
```typescript
|
|
55
|
+
const malicious = '{"__proto__": {"isAdmin": true}}';
|
|
59
56
|
|
|
60
|
-
|
|
57
|
+
const result = loader.parse(malicious);
|
|
58
|
+
console.log(result.warnings); // ["Prototype pollution detected: __proto__"]
|
|
59
|
+
console.log(result.isSafe); // false
|
|
60
|
+
```
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
// logger.ts
|
|
64
|
-
import { StructuredLogger } from "@ktuban/structured-logger";
|
|
62
|
+
### Depth Protection
|
|
65
63
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
filePath: process.env["LOG_FILE"],
|
|
64
|
+
```typescript
|
|
65
|
+
const loader = new SafeJsonLoader({
|
|
66
|
+
maxDepth: 5, // Limit nesting to 5 levels
|
|
70
67
|
});
|
|
71
|
-
```
|
|
72
68
|
|
|
73
|
-
|
|
69
|
+
const deeplyNested = {
|
|
70
|
+
a: { b: { c: { d: { e: { f: "too deep" } } } } },
|
|
71
|
+
};
|
|
74
72
|
|
|
75
|
-
|
|
73
|
+
const result = loader.parse(stringify(deeplyNested));
|
|
74
|
+
console.log(result.isSafe); // false
|
|
75
|
+
console.log(result.warnings); // ["Max depth exceeded at level 6"]
|
|
76
|
+
```
|
|
76
77
|
|
|
77
|
-
|
|
78
|
+
---
|
|
78
79
|
|
|
79
|
-
|
|
80
|
+
## 📖 API Reference
|
|
80
81
|
|
|
81
|
-
|
|
82
|
-
import { loadSafeJsonResources } from "safe-json-loader";
|
|
82
|
+
### SafeJsonLoader Constructor
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
//
|
|
84
|
+
```typescript
|
|
85
|
+
const loader = new SafeJsonLoader({
|
|
86
|
+
maxDepth: 20, // Maximum nesting level
|
|
87
|
+
maxSize: 10 * 1024 * 1024, // Max size in bytes
|
|
88
|
+
detectPollution: true, // Detect __proto__, constructor, prototype
|
|
89
|
+
throwOnUnsafe: false, // Throw error on suspicious patterns
|
|
90
|
+
onWarning: (w) => console.warn(w), // Warning callback
|
|
91
|
+
});
|
|
92
|
+
```
|
|
87
93
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
94
|
+
**Options:**
|
|
95
|
+
- `maxDepth` — Maximum JSON nesting depth (default: 20)
|
|
96
|
+
- `maxSize` — Maximum JSON size in bytes (default: 10MB)
|
|
97
|
+
- `detectPollution` — Enable prototype pollution detection (default: true)
|
|
98
|
+
- `throwOnUnsafe` — Throw error instead of returning unsafe flag (default: false)
|
|
99
|
+
- `onWarning` — Callback for warnings
|
|
100
|
+
|
|
101
|
+
### Parse Method
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
const result = loader.parse(jsonString);
|
|
105
|
+
|
|
106
|
+
// Result object:
|
|
107
|
+
{
|
|
108
|
+
data: any, // Parsed JSON (or undefined if parsing failed)
|
|
109
|
+
success: boolean, // Whether parsing succeeded
|
|
110
|
+
isSafe: boolean, // Whether no suspicious patterns detected
|
|
111
|
+
warnings: string[], // List of warnings
|
|
112
|
+
error?: Error, // Parse error if applicable
|
|
113
|
+
metadata: {
|
|
114
|
+
depth: number, // Maximum nesting depth found
|
|
115
|
+
size: number, // JSON size in bytes
|
|
116
|
+
keys: number, // Total number of keys
|
|
117
|
+
}
|
|
92
118
|
}
|
|
93
119
|
```
|
|
94
120
|
|
|
95
|
-
###
|
|
121
|
+
### File Loading
|
|
96
122
|
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const safeObj = parseSafeJsonString('{"user":{"__proto__":{"polluted":true}}}', {
|
|
101
|
-
maxJsonDepth: 30,
|
|
123
|
+
```typescript
|
|
124
|
+
const result = await loader.loadFromFile("/path/to/config.json", {
|
|
125
|
+
encoding: "utf-8",
|
|
102
126
|
});
|
|
103
127
|
|
|
104
|
-
//
|
|
128
|
+
console.log(result.data); // Parsed JSON
|
|
129
|
+
console.log(result.isSafe); // Safety check
|
|
105
130
|
```
|
|
106
131
|
|
|
107
|
-
###
|
|
108
|
-
|
|
109
|
-
```ts
|
|
110
|
-
import express from "express";
|
|
111
|
-
import { sanitizeParsedJsonObject } from "safe-json-loader";
|
|
112
|
-
|
|
113
|
-
const app = express();
|
|
114
|
-
app.use(express.json());
|
|
132
|
+
### Remote Loading
|
|
115
133
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
134
|
+
```typescript
|
|
135
|
+
const result = await loader.loadFromUrl(
|
|
136
|
+
"https://api.example.com/config.json",
|
|
137
|
+
{
|
|
138
|
+
timeout: 5000, // 5 second timeout
|
|
139
|
+
maxSize: 5 * 1024 * 1024, // 5MB limit
|
|
122
140
|
}
|
|
123
|
-
|
|
124
|
-
```
|
|
141
|
+
);
|
|
125
142
|
|
|
126
|
-
|
|
143
|
+
if (result.isSafe) {
|
|
144
|
+
applyConfig(result.data);
|
|
145
|
+
}
|
|
146
|
+
```
|
|
127
147
|
|
|
128
|
-
|
|
129
|
-
|
|
148
|
+
### Validation
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
const loader = new SafeJsonLoader({
|
|
152
|
+
schema: {
|
|
153
|
+
type: "object",
|
|
154
|
+
properties: {
|
|
155
|
+
name: { type: "string" },
|
|
156
|
+
age: { type: "number" },
|
|
157
|
+
},
|
|
158
|
+
required: ["name"],
|
|
159
|
+
},
|
|
160
|
+
});
|
|
130
161
|
|
|
131
|
-
const
|
|
162
|
+
const result = loader.parse('{"name": "John", "age": 30}');
|
|
163
|
+
console.log(result.isSafe); // true if validates
|
|
132
164
|
```
|
|
133
165
|
|
|
134
166
|
---
|
|
135
167
|
|
|
136
|
-
##
|
|
168
|
+
## 🔍 Detecting Threats
|
|
137
169
|
|
|
138
|
-
|
|
139
|
-
interface Logger {
|
|
140
|
-
debug?: (message: string, meta?: unknown) => void;
|
|
141
|
-
info?: (message: string, meta?: unknown) => void;
|
|
142
|
-
warn?: (message: string, meta?: unknown) => void;
|
|
143
|
-
error?: (message: string, meta?: unknown) => void;
|
|
144
|
-
}
|
|
170
|
+
The loader automatically detects:
|
|
145
171
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
maxJsonDepth?: number; // default 50
|
|
154
|
-
|
|
155
|
-
logger?: Logger; // optional; defaults to @ktuban/structured-logger
|
|
156
|
-
onFileLoaded?: (file: LoadedJsonFile) => void;
|
|
157
|
-
onFileSkipped?: (info: { source: string; reason: string }) => void;
|
|
172
|
+
### Prototype Pollution
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
// Detected and flagged
|
|
176
|
+
{
|
|
177
|
+
"__proto__": { "isAdmin": true },
|
|
178
|
+
"constructor": { "prototype": { "isAdmin": true } }
|
|
158
179
|
}
|
|
159
180
|
```
|
|
160
181
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
__source: string; // absolute path or URL
|
|
182
|
+
### Excessive Depth
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
// Flagged if exceeds maxDepth
|
|
186
|
+
{
|
|
187
|
+
"a": {
|
|
188
|
+
"b": {
|
|
189
|
+
"c": {
|
|
190
|
+
// ... very deeply nested
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
174
194
|
}
|
|
175
195
|
```
|
|
176
196
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
## Security guarantees
|
|
197
|
+
### Large Payloads
|
|
180
198
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
- **Sanitized before user code touches it:** all entry points sanitize.
|
|
199
|
+
```typescript
|
|
200
|
+
// Flagged if exceeds maxSize
|
|
201
|
+
const largeJson = stringify({
|
|
202
|
+
data: "x".repeat(100 * 1024 * 1024), // 100MB string
|
|
203
|
+
});
|
|
204
|
+
```
|
|
188
205
|
|
|
189
206
|
---
|
|
190
207
|
|
|
191
|
-
##
|
|
208
|
+
## 🎯 Best Practices
|
|
209
|
+
|
|
210
|
+
1. **Set appropriate depth limits**
|
|
211
|
+
```typescript
|
|
212
|
+
// For config files
|
|
213
|
+
new SafeJsonLoader({ maxDepth: 10 });
|
|
214
|
+
|
|
215
|
+
// For flexible data
|
|
216
|
+
new SafeJsonLoader({ maxDepth: 50 });
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
2. **Always check `isSafe` for untrusted input**
|
|
220
|
+
```typescript
|
|
221
|
+
const result = loader.parse(userInput);
|
|
222
|
+
if (!result.isSafe) {
|
|
223
|
+
logger.warn("Unsafe JSON detected", { warnings: result.warnings });
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
3. **Set size limits for remote loading**
|
|
229
|
+
```typescript
|
|
230
|
+
await loader.loadFromUrl(url, {
|
|
231
|
+
maxSize: 1 * 1024 * 1024, // 1MB max
|
|
232
|
+
});
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
4. **Enable pollution detection for user data**
|
|
236
|
+
```typescript
|
|
237
|
+
const loader = new SafeJsonLoader({
|
|
238
|
+
detectPollution: true, // Always true for untrusted sources
|
|
239
|
+
});
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
5. **Review warnings in production**
|
|
243
|
+
```typescript
|
|
244
|
+
const result = loader.parse(json);
|
|
245
|
+
if (result.warnings.length > 0) {
|
|
246
|
+
auditLog.warn("Suspicious JSON patterns detected", {
|
|
247
|
+
warnings: result.warnings,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
```
|
|
192
251
|
|
|
193
|
-
|
|
252
|
+
---
|
|
194
253
|
|
|
195
|
-
|
|
196
|
-
class JsonLoaderError extends Error {
|
|
197
|
-
code: string;
|
|
198
|
-
}
|
|
199
|
-
```
|
|
254
|
+
## 🔐 Security Notes
|
|
200
255
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
} catch (err: any) {
|
|
207
|
-
console.error(err.code, err.message);
|
|
208
|
-
}
|
|
209
|
-
```
|
|
256
|
+
- **Prototype Pollution** is a critical vulnerability — always enable detection for untrusted input
|
|
257
|
+
- **Constructor patterns** can be exploited — the loader detects common attack vectors
|
|
258
|
+
- **DoS attacks** — Use depth and size limits to prevent parser exhaustion
|
|
259
|
+
- **Validation** — Use schema validation for critical data
|
|
260
|
+
- **Logging** — Log suspicious patterns for security monitoring
|
|
210
261
|
|
|
211
262
|
---
|
|
212
263
|
|
|
213
|
-
##
|
|
264
|
+
## 📊 Performance
|
|
214
265
|
|
|
215
|
-
|
|
266
|
+
For typical JSON files (< 1MB):
|
|
267
|
+
- Parse time: < 1ms
|
|
268
|
+
- Validation overhead: < 0.5ms
|
|
269
|
+
- Pollution detection: < 0.1ms
|
|
216
270
|
|
|
217
|
-
|
|
218
|
-
import { loadSafeJsonResources } from "safe-json-loader";
|
|
271
|
+
---
|
|
219
272
|
|
|
220
|
-
|
|
221
|
-
info: (msg: string, meta?: unknown) => {
|
|
222
|
-
// capture logs for assertions
|
|
223
|
-
},
|
|
224
|
-
error: (msg: string, meta?: unknown) => {
|
|
225
|
-
// capture errors
|
|
226
|
-
},
|
|
227
|
-
};
|
|
273
|
+
## ☕ Support the Project
|
|
228
274
|
|
|
229
|
-
|
|
230
|
-
```
|
|
275
|
+
If this library helps you secure your JSON handling, consider supporting ongoing development:
|
|
231
276
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
```ts
|
|
235
|
-
await loadSafeJsonResources("./configs", {
|
|
236
|
-
onFileLoaded: (file) => {
|
|
237
|
-
// e.g., metrics or audit trail
|
|
238
|
-
},
|
|
239
|
-
onFileSkipped: ({ source, reason }) => {
|
|
240
|
-
// e.g., alert on skipped files
|
|
241
|
-
},
|
|
242
|
-
});
|
|
243
|
-
```
|
|
277
|
+
- [PayPal.me/khaliltuban](https://paypal.me/KhalilTuban)
|
|
278
|
+
- [Ko‑fi.com/ktuban](https://ko-fi.com/ktuban)
|
|
244
279
|
|
|
245
280
|
---
|
|
246
281
|
|
|
247
|
-
##
|
|
282
|
+
## 📄 License
|
|
248
283
|
|
|
249
|
-
|
|
250
|
-
- **Set `maxJsonDepth`** in production to a sensible value for your domain.
|
|
251
|
-
- **Use one shared logger instance** across your app and libraries.
|
|
252
|
-
- **Keep remote indexes small** and enforce `maxFiles` to avoid DoS via large listings.
|
|
284
|
+
MIT © K Tuban
|
|
253
285
|
|
|
254
|
-
|
|
286
|
+
## 🤝 Contributing
|
|
255
287
|
|
|
256
|
-
|
|
288
|
+
Pull requests are welcome. Please include tests and documentation updates.
|
|
257
289
|
|
|
258
|
-
|
|
290
|
+
## 🧭 Roadmap
|
|
259
291
|
|
|
260
|
-
|
|
292
|
+
- [ ] Custom sanitization rules
|
|
293
|
+
- [ ] JSON schema validator integration
|
|
294
|
+
- [ ] Performance optimizations
|
|
295
|
+
- [ ] Additional threat detection patterns
|
|
296
|
+
- [ ] WebAssembly parser option
|
package/dist/cjs/index.js
CHANGED
|
@@ -16,6 +16,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
// Public types
|
|
18
18
|
__exportStar(require("./types.js"), exports);
|
|
19
|
-
__exportStar(require("./logger.js"), exports);
|
|
20
19
|
__exportStar(require("./safeJsonLoader.js"), exports);
|
|
21
|
-
|
|
20
|
+
__exportStar(require("./safeJsonError.js"), exports);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SafeJsonError = void 0;
|
|
4
|
+
class SafeJsonError extends Error {
|
|
5
|
+
code;
|
|
6
|
+
statusCode;
|
|
7
|
+
isOperational;
|
|
8
|
+
constructor(message, code = "SAFE_JSON_ERROR", statusCode = 400) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.code = code;
|
|
11
|
+
this.statusCode = statusCode;
|
|
12
|
+
this.isOperational = true;
|
|
13
|
+
Error.captureStackTrace(this, this.constructor);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.SafeJsonError = SafeJsonError;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { JsonValue, LoadedJsonFile, ResolvedSafeJsonLoaderOptions, SafeJsonLoaderOptions, JsonLoadInput, LoggerContract } from "./types.js";
|
|
2
|
+
export declare function mergeOptions(opts?: SafeJsonLoaderOptions): ResolvedSafeJsonLoaderOptions;
|
|
3
|
+
export declare function log(options: ResolvedSafeJsonLoaderOptions, level: keyof LoggerContract, message: string, meta?: unknown): void;
|
|
4
|
+
import { SafeJsonError } from "./safeJsonError.js";
|
|
5
|
+
export declare class JsonLoaderError extends SafeJsonError {
|
|
6
|
+
constructor(message: string, code: string, statusCode?: number);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Deeply clones and strips dangerous keys to mitigate prototype pollution.
|
|
10
|
+
* Uses Object.create(null) to avoid inheriting from Object.prototype.
|
|
11
|
+
*/
|
|
12
|
+
export declare function sanitizePrototypePollution<T extends JsonValue>(input: T, options?: {
|
|
13
|
+
maxDepth?: number;
|
|
14
|
+
}): T;
|
|
15
|
+
/**
|
|
16
|
+
* Load one or more JSON resources from:
|
|
17
|
+
* - Local file (.json)
|
|
18
|
+
* - Local directory (all .json files)
|
|
19
|
+
* - Remote URL returning JSON (object/array)
|
|
20
|
+
* - Remote URL acting as an index of JSON URLs (array or { files: [] })
|
|
21
|
+
*
|
|
22
|
+
* Security features:
|
|
23
|
+
* - Prototype‑pollution‑safe deep clone (strips __proto__, constructor, prototype)
|
|
24
|
+
* - Max depth for parsed JSON structures
|
|
25
|
+
* - Max file size and total directory size
|
|
26
|
+
* - Concurrency limit for I/O (local and remote)
|
|
27
|
+
* - HTTP timeout and content‑type checks
|
|
28
|
+
*
|
|
29
|
+
* This function does not enforce any domain/schema validation — callers
|
|
30
|
+
* should layer their own validation on top of the loaded `data`.
|
|
31
|
+
*/
|
|
32
|
+
export declare function loadSafeJsonResources(input: JsonLoadInput, options?: SafeJsonLoaderOptions): Promise<LoadedJsonFile[]>;
|
|
33
|
+
/**
|
|
34
|
+
* Safely parse and sanitize a JSON string.
|
|
35
|
+
*
|
|
36
|
+
* - Parses JSON with error handling
|
|
37
|
+
* - Strips prototype pollution keys (__proto__, constructor, prototype)
|
|
38
|
+
* - Enforces max depth
|
|
39
|
+
*
|
|
40
|
+
* @param input Raw JSON string
|
|
41
|
+
* @param opts Loader options (maxJsonDepth, etc.)
|
|
42
|
+
* @returns Safe, sanitized JSON object
|
|
43
|
+
*/
|
|
44
|
+
export declare function parseSafeJsonString(input: string, opts?: Pick<SafeJsonLoaderOptions, "maxJsonDepth">): JsonValue;
|
|
45
|
+
/**
|
|
46
|
+
* Sanitize an already-parsed JSON object.
|
|
47
|
+
*
|
|
48
|
+
* - Strips prototype pollution keys (__proto__, constructor, prototype)
|
|
49
|
+
* - Enforces max depth
|
|
50
|
+
*
|
|
51
|
+
* @param input Parsed JSON object (e.g. req.body in Express)
|
|
52
|
+
* @param opts Loader options (maxJsonDepth, etc.)
|
|
53
|
+
* @returns Safe, sanitized JSON object
|
|
54
|
+
*/
|
|
55
|
+
export declare function sanitizeParsedJsonObject(input: JsonValue, opts?: Pick<SafeJsonLoaderOptions, "maxJsonDepth">): JsonValue;
|