hppx 0.1.8 → 0.2.2
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 +0 -0
- package/README.md +492 -253
- package/dist/index.cjs +233 -70
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -1
- package/dist/index.d.ts +14 -1
- package/dist/index.mjs +232 -70
- package/dist/index.mjs.map +1 -1
- package/package.json +20 -20
- package/src/index.d.cts +0 -70
package/dist/index.mjs
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
var DEFAULT_SOURCES = ["query", "body", "params"];
|
|
3
3
|
var DEFAULT_STRATEGY = "keepLast";
|
|
4
4
|
var DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
|
|
5
|
+
var FORBIDDEN_KEY_CHARS = /[\u0000-\u001F\u007F-\u009F\u200E\u200F\u202A-\u202E\u2066-\u2069\uFEFF]/;
|
|
5
6
|
function isPlainObject(value) {
|
|
6
7
|
if (value === null || typeof value !== "object") return false;
|
|
7
8
|
const proto = Object.getPrototypeOf(value);
|
|
@@ -10,47 +11,65 @@ function isPlainObject(value) {
|
|
|
10
11
|
function sanitizeKey(key, maxKeyLength) {
|
|
11
12
|
if (typeof key !== "string") return null;
|
|
12
13
|
if (DANGEROUS_KEYS.has(key)) return null;
|
|
13
|
-
if (
|
|
14
|
+
if (FORBIDDEN_KEY_CHARS.test(key)) return null;
|
|
14
15
|
const maxLen = maxKeyLength ?? 200;
|
|
15
16
|
if (key.length > maxLen) return null;
|
|
16
17
|
if (key.length > 1 && /^[.\[\]]+$/.test(key)) return null;
|
|
17
18
|
return key;
|
|
18
19
|
}
|
|
20
|
+
var PATH_SEGMENT_CACHE_LIMIT = 500;
|
|
19
21
|
var pathSegmentCache = /* @__PURE__ */ new Map();
|
|
20
22
|
function parsePathSegments(key) {
|
|
21
23
|
const cached = pathSegmentCache.get(key);
|
|
22
24
|
if (cached) return cached;
|
|
23
25
|
const dotted = key.replace(/\]/g, "").replace(/\[/g, ".");
|
|
24
26
|
const result = dotted.split(".").filter((s) => s.length > 0);
|
|
25
|
-
if (pathSegmentCache.size
|
|
26
|
-
pathSegmentCache.
|
|
27
|
+
if (pathSegmentCache.size >= PATH_SEGMENT_CACHE_LIMIT) {
|
|
28
|
+
pathSegmentCache.clear();
|
|
27
29
|
}
|
|
30
|
+
pathSegmentCache.set(key, result);
|
|
28
31
|
return result;
|
|
29
32
|
}
|
|
30
|
-
function
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
33
|
+
function __resetPathSegmentCache() {
|
|
34
|
+
pathSegmentCache.clear();
|
|
35
|
+
}
|
|
36
|
+
function expandObjectPaths(obj, maxKeyLength, maxDepth = 20, currentDepth = 0, seen) {
|
|
37
|
+
if (currentDepth > maxDepth) {
|
|
38
|
+
throw new Error(`Maximum object depth (${maxDepth}) exceeded`);
|
|
39
|
+
}
|
|
40
|
+
const seenSet = seen ?? /* @__PURE__ */ new WeakSet();
|
|
41
|
+
if (seenSet.has(obj)) return {};
|
|
42
|
+
seenSet.add(obj);
|
|
43
|
+
try {
|
|
44
|
+
const result = {};
|
|
45
|
+
for (const rawKey of Object.keys(obj)) {
|
|
46
|
+
const safeKey = sanitizeKey(rawKey, maxKeyLength);
|
|
47
|
+
if (!safeKey) continue;
|
|
48
|
+
const value = obj[rawKey];
|
|
49
|
+
const expandedValue = isPlainObject(value) ? expandObjectPaths(
|
|
50
|
+
value,
|
|
51
|
+
maxKeyLength,
|
|
52
|
+
maxDepth,
|
|
53
|
+
currentDepth + 1,
|
|
54
|
+
seenSet
|
|
55
|
+
) : value;
|
|
56
|
+
if (safeKey.includes(".") || safeKey.includes("[")) {
|
|
57
|
+
const segments = parsePathSegments(safeKey);
|
|
58
|
+
if (segments.length > 0) {
|
|
59
|
+
setIn(result, segments, expandedValue);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
42
62
|
}
|
|
63
|
+
result[safeKey] = expandedValue;
|
|
43
64
|
}
|
|
44
|
-
result
|
|
65
|
+
return result;
|
|
66
|
+
} finally {
|
|
67
|
+
seenSet.delete(obj);
|
|
45
68
|
}
|
|
46
|
-
return result;
|
|
47
69
|
}
|
|
48
|
-
function setReqPropertySafe(target, key, value) {
|
|
70
|
+
function setReqPropertySafe(target, key, value, onFailure) {
|
|
49
71
|
try {
|
|
50
72
|
const desc = Object.getOwnPropertyDescriptor(target, key);
|
|
51
|
-
if (desc && desc.configurable === false && desc.writable === false) {
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
73
|
if (!desc || desc.configurable !== false) {
|
|
55
74
|
Object.defineProperty(target, key, {
|
|
56
75
|
value,
|
|
@@ -58,28 +77,79 @@ function setReqPropertySafe(target, key, value) {
|
|
|
58
77
|
configurable: true,
|
|
59
78
|
enumerable: true
|
|
60
79
|
});
|
|
61
|
-
return;
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
if (desc.writable) {
|
|
83
|
+
target[key] = value;
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
target[key] = value;
|
|
88
|
+
if (target[key] === value) return true;
|
|
89
|
+
} catch (_assignErr) {
|
|
90
|
+
}
|
|
91
|
+
if (onFailure) {
|
|
92
|
+
onFailure(
|
|
93
|
+
`[hppx] Could not write sanitized value to req.${key}: property is non-configurable and non-writable. The original (potentially polluted) value remains on req.${key}.`
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
return false;
|
|
97
|
+
} catch (_definePropErr) {
|
|
98
|
+
try {
|
|
99
|
+
target[key] = value;
|
|
100
|
+
if (target[key] === value) return true;
|
|
101
|
+
} catch (_assignErr) {
|
|
102
|
+
}
|
|
103
|
+
{
|
|
104
|
+
if (onFailure) {
|
|
105
|
+
onFailure(`[hppx] Could not write sanitized value to req.${key}: defineProperty failed.`);
|
|
106
|
+
}
|
|
107
|
+
return false;
|
|
62
108
|
}
|
|
63
|
-
} catch (_) {
|
|
64
|
-
}
|
|
65
|
-
try {
|
|
66
|
-
target[key] = value;
|
|
67
|
-
} catch (_) {
|
|
68
109
|
}
|
|
69
110
|
}
|
|
70
|
-
function safeDeepClone(input, maxKeyLength, maxArrayLength) {
|
|
111
|
+
function safeDeepClone(input, maxKeyLength, maxArrayLength, maxDepth = 20, currentDepth = 0, seen) {
|
|
71
112
|
if (Array.isArray(input)) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
113
|
+
if (currentDepth > maxDepth) {
|
|
114
|
+
throw new Error(`Maximum object depth (${maxDepth}) exceeded`);
|
|
115
|
+
}
|
|
116
|
+
const seenSet = seen ?? /* @__PURE__ */ new WeakSet();
|
|
117
|
+
if (seenSet.has(input)) return [];
|
|
118
|
+
seenSet.add(input);
|
|
119
|
+
try {
|
|
120
|
+
const limit = maxArrayLength ?? 1e3;
|
|
121
|
+
const limited = input.slice(0, limit);
|
|
122
|
+
return limited.map(
|
|
123
|
+
(v) => safeDeepClone(v, maxKeyLength, maxArrayLength, maxDepth, currentDepth + 1, seenSet)
|
|
124
|
+
);
|
|
125
|
+
} finally {
|
|
126
|
+
seenSet.delete(input);
|
|
127
|
+
}
|
|
75
128
|
}
|
|
76
129
|
if (isPlainObject(input)) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
130
|
+
if (currentDepth > maxDepth) {
|
|
131
|
+
throw new Error(`Maximum object depth (${maxDepth}) exceeded`);
|
|
132
|
+
}
|
|
133
|
+
const seenSet = seen ?? /* @__PURE__ */ new WeakSet();
|
|
134
|
+
if (seenSet.has(input)) return {};
|
|
135
|
+
seenSet.add(input);
|
|
136
|
+
try {
|
|
137
|
+
const out = {};
|
|
138
|
+
for (const k of Object.keys(input)) {
|
|
139
|
+
if (!sanitizeKey(k, maxKeyLength)) continue;
|
|
140
|
+
out[k] = safeDeepClone(
|
|
141
|
+
input[k],
|
|
142
|
+
maxKeyLength,
|
|
143
|
+
maxArrayLength,
|
|
144
|
+
maxDepth,
|
|
145
|
+
currentDepth + 1,
|
|
146
|
+
seenSet
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
return out;
|
|
150
|
+
} finally {
|
|
151
|
+
seenSet.delete(input);
|
|
81
152
|
}
|
|
82
|
-
return out;
|
|
83
153
|
}
|
|
84
154
|
return input;
|
|
85
155
|
}
|
|
@@ -95,8 +165,15 @@ function mergeValues(values, strategy) {
|
|
|
95
165
|
else acc.push(v);
|
|
96
166
|
return acc;
|
|
97
167
|
}, []);
|
|
98
|
-
|
|
99
|
-
|
|
168
|
+
/* istanbul ignore next -- exhaustiveness check unreachable from outside:
|
|
169
|
+
validateSanitizeOptions rejects every non-listed strategy at construction
|
|
170
|
+
time, so the only way to reach this branch is a programmer error (a new
|
|
171
|
+
MergeStrategy union member added without updating this switch). Failing
|
|
172
|
+
loudly here is preferable to a silent fallback. */
|
|
173
|
+
default: {
|
|
174
|
+
const _exhaustive = strategy;
|
|
175
|
+
throw new Error(`Unknown mergeStrategy: ${_exhaustive}`);
|
|
176
|
+
}
|
|
100
177
|
}
|
|
101
178
|
}
|
|
102
179
|
function isUrlEncodedContentType(req) {
|
|
@@ -120,6 +197,7 @@ function normalizeWhitelist(whitelist) {
|
|
|
120
197
|
if (typeof whitelist === "string") return [whitelist];
|
|
121
198
|
return whitelist.filter((w) => typeof w === "string");
|
|
122
199
|
}
|
|
200
|
+
var WHITELIST_PATH_CACHE_LIMIT = 1e3;
|
|
123
201
|
function buildWhitelistHelpers(whitelist) {
|
|
124
202
|
const exact = new Set(whitelist);
|
|
125
203
|
const prefixes = whitelist.filter((w) => w.length > 0);
|
|
@@ -145,9 +223,10 @@ function buildWhitelistHelpers(whitelist) {
|
|
|
145
223
|
}
|
|
146
224
|
}
|
|
147
225
|
}
|
|
148
|
-
if (pathCache.size
|
|
149
|
-
pathCache.
|
|
226
|
+
if (pathCache.size >= WHITELIST_PATH_CACHE_LIMIT) {
|
|
227
|
+
pathCache.clear();
|
|
150
228
|
}
|
|
229
|
+
pathCache.set(full, result);
|
|
151
230
|
return result;
|
|
152
231
|
}
|
|
153
232
|
};
|
|
@@ -195,20 +274,18 @@ function moveWhitelistedFromPolluted(reqPart, polluted, isWhitelisted) {
|
|
|
195
274
|
function detectAndReduce(input, opts) {
|
|
196
275
|
let keyCount = 0;
|
|
197
276
|
const polluted = {};
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
277
|
+
const pollutedKeysSet = /* @__PURE__ */ new Set();
|
|
278
|
+
const cloned = safeDeepClone(input, opts.maxKeyLength, opts.maxArrayLength, opts.maxDepth);
|
|
279
|
+
function processNode(node, path = [], depth = 0, inArray = false) {
|
|
280
|
+
if (node === null) return opts.preserveNull ? null : void 0;
|
|
281
|
+
if (node === void 0) return node;
|
|
201
282
|
if (Array.isArray(node)) {
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
return mergeValues(mapped, "combine");
|
|
283
|
+
const mapped = node.map((v) => processNode(v, path, depth, true));
|
|
284
|
+
if (!inArray) {
|
|
285
|
+
setIn(polluted, path, node);
|
|
286
|
+
pollutedKeysSet.add(path.join("."));
|
|
207
287
|
}
|
|
208
|
-
|
|
209
|
-
pollutedKeys.push(path.join("."));
|
|
210
|
-
const reduced = mergeValues(mapped, opts.mergeStrategy);
|
|
211
|
-
return reduced;
|
|
288
|
+
return mergeValues(mapped, opts.mergeStrategy);
|
|
212
289
|
}
|
|
213
290
|
if (isPlainObject(node)) {
|
|
214
291
|
if (depth > opts.maxDepth)
|
|
@@ -223,7 +300,7 @@ function detectAndReduce(input, opts) {
|
|
|
223
300
|
if (!safeKey) continue;
|
|
224
301
|
const child = node[rawKey];
|
|
225
302
|
const childPath = path.concat([safeKey]);
|
|
226
|
-
let value = processNode(child, childPath, depth + 1);
|
|
303
|
+
let value = processNode(child, childPath, depth + 1, false);
|
|
227
304
|
if (typeof value === "string" && opts.trimValues) value = value.trim();
|
|
228
305
|
out[safeKey] = value;
|
|
229
306
|
}
|
|
@@ -231,13 +308,14 @@ function detectAndReduce(input, opts) {
|
|
|
231
308
|
}
|
|
232
309
|
return node;
|
|
233
310
|
}
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
return { cleaned, pollutedTree: polluted, pollutedKeys };
|
|
311
|
+
const cleaned = processNode(cloned, [], 0, false);
|
|
312
|
+
return { cleaned, pollutedTree: polluted, pollutedKeys: Array.from(pollutedKeysSet) };
|
|
237
313
|
}
|
|
238
314
|
function sanitize(input, options = {}) {
|
|
315
|
+
validateSanitizeOptions(options);
|
|
239
316
|
const maxKeyLength = options.maxKeyLength ?? 200;
|
|
240
|
-
const
|
|
317
|
+
const maxDepthVal = options.maxDepth ?? 20;
|
|
318
|
+
const expandedInput = isPlainObject(input) ? expandObjectPaths(input, maxKeyLength, maxDepthVal) : input;
|
|
241
319
|
const whitelist = normalizeWhitelist(options.whitelist);
|
|
242
320
|
const { isWhitelistedPath } = buildWhitelistHelpers(whitelist);
|
|
243
321
|
const {
|
|
@@ -260,7 +338,7 @@ function sanitize(input, options = {}) {
|
|
|
260
338
|
moveWhitelistedFromPolluted(cleaned, pollutedTree, isWhitelistedPath);
|
|
261
339
|
return cleaned;
|
|
262
340
|
}
|
|
263
|
-
function
|
|
341
|
+
function validateSanitizeOptions(options) {
|
|
264
342
|
if (options.maxDepth !== void 0 && (typeof options.maxDepth !== "number" || options.maxDepth < 1 || options.maxDepth > 100)) {
|
|
265
343
|
throw new TypeError("maxDepth must be a number between 1 and 100");
|
|
266
344
|
}
|
|
@@ -276,10 +354,34 @@ function validateOptions(options) {
|
|
|
276
354
|
if (options.mergeStrategy !== void 0 && !["keepFirst", "keepLast", "combine"].includes(options.mergeStrategy)) {
|
|
277
355
|
throw new TypeError("mergeStrategy must be 'keepFirst', 'keepLast', or 'combine'");
|
|
278
356
|
}
|
|
357
|
+
if (options.trimValues !== void 0 && typeof options.trimValues !== "boolean") {
|
|
358
|
+
throw new TypeError("trimValues must be a boolean");
|
|
359
|
+
}
|
|
360
|
+
if (options.preserveNull !== void 0 && typeof options.preserveNull !== "boolean") {
|
|
361
|
+
throw new TypeError("preserveNull must be a boolean");
|
|
362
|
+
}
|
|
363
|
+
if (options.whitelist !== void 0) {
|
|
364
|
+
if (typeof options.whitelist !== "string" && !Array.isArray(options.whitelist)) {
|
|
365
|
+
throw new TypeError("whitelist must be a string or an array of strings");
|
|
366
|
+
}
|
|
367
|
+
if (Array.isArray(options.whitelist)) {
|
|
368
|
+
for (const entry of options.whitelist) {
|
|
369
|
+
if (typeof entry !== "string") {
|
|
370
|
+
throw new TypeError("whitelist must be a string or an array of strings");
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
function validateOptions(options) {
|
|
377
|
+
validateSanitizeOptions(options);
|
|
279
378
|
if (options.sources !== void 0 && !Array.isArray(options.sources)) {
|
|
280
379
|
throw new TypeError("sources must be an array");
|
|
281
380
|
}
|
|
282
381
|
if (options.sources !== void 0) {
|
|
382
|
+
if (options.sources.length === 0) {
|
|
383
|
+
throw new TypeError("sources must contain at least one of 'query', 'body', 'params'");
|
|
384
|
+
}
|
|
283
385
|
for (const source of options.sources) {
|
|
284
386
|
if (!["query", "body", "params"].includes(source)) {
|
|
285
387
|
throw new TypeError("sources must only contain 'query', 'body', or 'params'");
|
|
@@ -289,8 +391,27 @@ function validateOptions(options) {
|
|
|
289
391
|
if (options.checkBodyContentType !== void 0 && !["urlencoded", "any", "none"].includes(options.checkBodyContentType)) {
|
|
290
392
|
throw new TypeError("checkBodyContentType must be 'urlencoded', 'any', or 'none'");
|
|
291
393
|
}
|
|
292
|
-
if (options.excludePaths !== void 0
|
|
293
|
-
|
|
394
|
+
if (options.excludePaths !== void 0) {
|
|
395
|
+
if (!Array.isArray(options.excludePaths)) {
|
|
396
|
+
throw new TypeError("excludePaths must be an array");
|
|
397
|
+
}
|
|
398
|
+
for (const entry of options.excludePaths) {
|
|
399
|
+
if (typeof entry !== "string") {
|
|
400
|
+
throw new TypeError("excludePaths must contain only strings");
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
if (options.logger !== void 0 && typeof options.logger !== "function") {
|
|
405
|
+
throw new TypeError("logger must be a function");
|
|
406
|
+
}
|
|
407
|
+
if (options.onPollutionDetected !== void 0 && typeof options.onPollutionDetected !== "function") {
|
|
408
|
+
throw new TypeError("onPollutionDetected must be a function");
|
|
409
|
+
}
|
|
410
|
+
if (options.strict !== void 0 && typeof options.strict !== "boolean") {
|
|
411
|
+
throw new TypeError("strict must be a boolean");
|
|
412
|
+
}
|
|
413
|
+
if (options.logPollution !== void 0 && typeof options.logPollution !== "boolean") {
|
|
414
|
+
throw new TypeError("logPollution must be a boolean");
|
|
294
415
|
}
|
|
295
416
|
}
|
|
296
417
|
function hppx(options = {}) {
|
|
@@ -316,9 +437,39 @@ function hppx(options = {}) {
|
|
|
316
437
|
const { isWhitelistedPath } = buildWhitelistHelpers(whitelistArr);
|
|
317
438
|
return function hppxMiddleware(req, res, next) {
|
|
318
439
|
try {
|
|
319
|
-
|
|
440
|
+
let pathForExclusion;
|
|
441
|
+
try {
|
|
442
|
+
pathForExclusion = req?.path;
|
|
443
|
+
} catch (pathErr) {
|
|
444
|
+
const message = `[hppx] Failed to read req.path during exclusion check; proceeding without path-based exclusion. Underlying error: ${pathErr instanceof Error ? pathErr.message : String(pathErr)}`;
|
|
445
|
+
if (logger) {
|
|
446
|
+
try {
|
|
447
|
+
logger(message);
|
|
448
|
+
} catch (_) {
|
|
449
|
+
console.warn(message);
|
|
450
|
+
}
|
|
451
|
+
} else {
|
|
452
|
+
console.warn(message);
|
|
453
|
+
}
|
|
454
|
+
pathForExclusion = void 0;
|
|
455
|
+
}
|
|
456
|
+
if (shouldExcludePath(pathForExclusion, excludePaths)) return next();
|
|
320
457
|
let anyPollutionDetected = false;
|
|
321
458
|
const allPollutedKeys = [];
|
|
459
|
+
const warned = /* @__PURE__ */ new Set();
|
|
460
|
+
const warn = (message) => {
|
|
461
|
+
if (warned.has(message)) return;
|
|
462
|
+
warned.add(message);
|
|
463
|
+
if (logger) {
|
|
464
|
+
try {
|
|
465
|
+
logger(message);
|
|
466
|
+
} catch (_) {
|
|
467
|
+
console.warn(message);
|
|
468
|
+
}
|
|
469
|
+
} else {
|
|
470
|
+
console.warn(message);
|
|
471
|
+
}
|
|
472
|
+
};
|
|
322
473
|
for (const source of sources) {
|
|
323
474
|
if (!req || typeof req !== "object") break;
|
|
324
475
|
if (req[source] === void 0) continue;
|
|
@@ -328,10 +479,10 @@ function hppx(options = {}) {
|
|
|
328
479
|
}
|
|
329
480
|
const part = req[source];
|
|
330
481
|
if (!isPlainObject(part)) continue;
|
|
331
|
-
const expandedPart = expandObjectPaths(part, maxKeyLength);
|
|
482
|
+
const expandedPart = expandObjectPaths(part, maxKeyLength, maxDepth);
|
|
332
483
|
const pollutedKey = `${source}Polluted`;
|
|
333
484
|
const processedKey = `__hppxProcessed_${source}`;
|
|
334
|
-
const hasProcessedBefore =
|
|
485
|
+
const hasProcessedBefore = Object.prototype.hasOwnProperty.call(req, processedKey);
|
|
335
486
|
if (!hasProcessedBefore) {
|
|
336
487
|
const { cleaned, pollutedTree, pollutedKeys } = detectAndReduce(expandedPart, {
|
|
337
488
|
mergeStrategy,
|
|
@@ -342,9 +493,21 @@ function hppx(options = {}) {
|
|
|
342
493
|
trimValues,
|
|
343
494
|
preserveNull
|
|
344
495
|
});
|
|
345
|
-
setReqPropertySafe(req, source, cleaned);
|
|
346
|
-
setReqPropertySafe(req, pollutedKey, pollutedTree);
|
|
347
|
-
|
|
496
|
+
setReqPropertySafe(req, source, cleaned, warn);
|
|
497
|
+
setReqPropertySafe(req, pollutedKey, pollutedTree, warn);
|
|
498
|
+
try {
|
|
499
|
+
Object.defineProperty(req, processedKey, {
|
|
500
|
+
value: true,
|
|
501
|
+
writable: false,
|
|
502
|
+
configurable: false,
|
|
503
|
+
enumerable: false
|
|
504
|
+
});
|
|
505
|
+
} catch (_) {
|
|
506
|
+
try {
|
|
507
|
+
req[processedKey] = true;
|
|
508
|
+
} catch (_assignErr) {
|
|
509
|
+
}
|
|
510
|
+
}
|
|
348
511
|
const sourceData = req[source];
|
|
349
512
|
const pollutedData = req[pollutedKey];
|
|
350
513
|
if (isPlainObject(sourceData) && isPlainObject(pollutedData)) {
|
|
@@ -411,10 +574,8 @@ function hppx(options = {}) {
|
|
|
411
574
|
try {
|
|
412
575
|
logger(error);
|
|
413
576
|
} catch (logErr) {
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
console.error("[hppx] Original error:", error);
|
|
417
|
-
}
|
|
577
|
+
console.error("[hppx] Logger failed:", logErr);
|
|
578
|
+
console.error("[hppx] Original error:", error);
|
|
418
579
|
}
|
|
419
580
|
}
|
|
420
581
|
return next(error);
|
|
@@ -425,6 +586,7 @@ export {
|
|
|
425
586
|
DANGEROUS_KEYS,
|
|
426
587
|
DEFAULT_SOURCES,
|
|
427
588
|
DEFAULT_STRATEGY,
|
|
589
|
+
__resetPathSegmentCache,
|
|
428
590
|
hppx as default,
|
|
429
591
|
sanitize
|
|
430
592
|
};
|