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.cjs
CHANGED
|
@@ -23,6 +23,7 @@ __export(index_exports, {
|
|
|
23
23
|
DANGEROUS_KEYS: () => DANGEROUS_KEYS,
|
|
24
24
|
DEFAULT_SOURCES: () => DEFAULT_SOURCES,
|
|
25
25
|
DEFAULT_STRATEGY: () => DEFAULT_STRATEGY,
|
|
26
|
+
__resetPathSegmentCache: () => __resetPathSegmentCache,
|
|
26
27
|
default: () => hppx,
|
|
27
28
|
sanitize: () => sanitize
|
|
28
29
|
});
|
|
@@ -30,6 +31,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
30
31
|
var DEFAULT_SOURCES = ["query", "body", "params"];
|
|
31
32
|
var DEFAULT_STRATEGY = "keepLast";
|
|
32
33
|
var DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
|
|
34
|
+
var FORBIDDEN_KEY_CHARS = /[\u0000-\u001F\u007F-\u009F\u200E\u200F\u202A-\u202E\u2066-\u2069\uFEFF]/;
|
|
33
35
|
function isPlainObject(value) {
|
|
34
36
|
if (value === null || typeof value !== "object") return false;
|
|
35
37
|
const proto = Object.getPrototypeOf(value);
|
|
@@ -38,47 +40,65 @@ function isPlainObject(value) {
|
|
|
38
40
|
function sanitizeKey(key, maxKeyLength) {
|
|
39
41
|
if (typeof key !== "string") return null;
|
|
40
42
|
if (DANGEROUS_KEYS.has(key)) return null;
|
|
41
|
-
if (
|
|
43
|
+
if (FORBIDDEN_KEY_CHARS.test(key)) return null;
|
|
42
44
|
const maxLen = maxKeyLength ?? 200;
|
|
43
45
|
if (key.length > maxLen) return null;
|
|
44
46
|
if (key.length > 1 && /^[.\[\]]+$/.test(key)) return null;
|
|
45
47
|
return key;
|
|
46
48
|
}
|
|
49
|
+
var PATH_SEGMENT_CACHE_LIMIT = 500;
|
|
47
50
|
var pathSegmentCache = /* @__PURE__ */ new Map();
|
|
48
51
|
function parsePathSegments(key) {
|
|
49
52
|
const cached = pathSegmentCache.get(key);
|
|
50
53
|
if (cached) return cached;
|
|
51
54
|
const dotted = key.replace(/\]/g, "").replace(/\[/g, ".");
|
|
52
55
|
const result = dotted.split(".").filter((s) => s.length > 0);
|
|
53
|
-
if (pathSegmentCache.size
|
|
54
|
-
pathSegmentCache.
|
|
56
|
+
if (pathSegmentCache.size >= PATH_SEGMENT_CACHE_LIMIT) {
|
|
57
|
+
pathSegmentCache.clear();
|
|
55
58
|
}
|
|
59
|
+
pathSegmentCache.set(key, result);
|
|
56
60
|
return result;
|
|
57
61
|
}
|
|
58
|
-
function
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
62
|
+
function __resetPathSegmentCache() {
|
|
63
|
+
pathSegmentCache.clear();
|
|
64
|
+
}
|
|
65
|
+
function expandObjectPaths(obj, maxKeyLength, maxDepth = 20, currentDepth = 0, seen) {
|
|
66
|
+
if (currentDepth > maxDepth) {
|
|
67
|
+
throw new Error(`Maximum object depth (${maxDepth}) exceeded`);
|
|
68
|
+
}
|
|
69
|
+
const seenSet = seen ?? /* @__PURE__ */ new WeakSet();
|
|
70
|
+
if (seenSet.has(obj)) return {};
|
|
71
|
+
seenSet.add(obj);
|
|
72
|
+
try {
|
|
73
|
+
const result = {};
|
|
74
|
+
for (const rawKey of Object.keys(obj)) {
|
|
75
|
+
const safeKey = sanitizeKey(rawKey, maxKeyLength);
|
|
76
|
+
if (!safeKey) continue;
|
|
77
|
+
const value = obj[rawKey];
|
|
78
|
+
const expandedValue = isPlainObject(value) ? expandObjectPaths(
|
|
79
|
+
value,
|
|
80
|
+
maxKeyLength,
|
|
81
|
+
maxDepth,
|
|
82
|
+
currentDepth + 1,
|
|
83
|
+
seenSet
|
|
84
|
+
) : value;
|
|
85
|
+
if (safeKey.includes(".") || safeKey.includes("[")) {
|
|
86
|
+
const segments = parsePathSegments(safeKey);
|
|
87
|
+
if (segments.length > 0) {
|
|
88
|
+
setIn(result, segments, expandedValue);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
70
91
|
}
|
|
92
|
+
result[safeKey] = expandedValue;
|
|
71
93
|
}
|
|
72
|
-
result
|
|
94
|
+
return result;
|
|
95
|
+
} finally {
|
|
96
|
+
seenSet.delete(obj);
|
|
73
97
|
}
|
|
74
|
-
return result;
|
|
75
98
|
}
|
|
76
|
-
function setReqPropertySafe(target, key, value) {
|
|
99
|
+
function setReqPropertySafe(target, key, value, onFailure) {
|
|
77
100
|
try {
|
|
78
101
|
const desc = Object.getOwnPropertyDescriptor(target, key);
|
|
79
|
-
if (desc && desc.configurable === false && desc.writable === false) {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
102
|
if (!desc || desc.configurable !== false) {
|
|
83
103
|
Object.defineProperty(target, key, {
|
|
84
104
|
value,
|
|
@@ -86,28 +106,79 @@ function setReqPropertySafe(target, key, value) {
|
|
|
86
106
|
configurable: true,
|
|
87
107
|
enumerable: true
|
|
88
108
|
});
|
|
89
|
-
return;
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
if (desc.writable) {
|
|
112
|
+
target[key] = value;
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
target[key] = value;
|
|
117
|
+
if (target[key] === value) return true;
|
|
118
|
+
} catch (_assignErr) {
|
|
119
|
+
}
|
|
120
|
+
if (onFailure) {
|
|
121
|
+
onFailure(
|
|
122
|
+
`[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}.`
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
return false;
|
|
126
|
+
} catch (_definePropErr) {
|
|
127
|
+
try {
|
|
128
|
+
target[key] = value;
|
|
129
|
+
if (target[key] === value) return true;
|
|
130
|
+
} catch (_assignErr) {
|
|
131
|
+
}
|
|
132
|
+
{
|
|
133
|
+
if (onFailure) {
|
|
134
|
+
onFailure(`[hppx] Could not write sanitized value to req.${key}: defineProperty failed.`);
|
|
135
|
+
}
|
|
136
|
+
return false;
|
|
90
137
|
}
|
|
91
|
-
} catch (_) {
|
|
92
|
-
}
|
|
93
|
-
try {
|
|
94
|
-
target[key] = value;
|
|
95
|
-
} catch (_) {
|
|
96
138
|
}
|
|
97
139
|
}
|
|
98
|
-
function safeDeepClone(input, maxKeyLength, maxArrayLength) {
|
|
140
|
+
function safeDeepClone(input, maxKeyLength, maxArrayLength, maxDepth = 20, currentDepth = 0, seen) {
|
|
99
141
|
if (Array.isArray(input)) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
142
|
+
if (currentDepth > maxDepth) {
|
|
143
|
+
throw new Error(`Maximum object depth (${maxDepth}) exceeded`);
|
|
144
|
+
}
|
|
145
|
+
const seenSet = seen ?? /* @__PURE__ */ new WeakSet();
|
|
146
|
+
if (seenSet.has(input)) return [];
|
|
147
|
+
seenSet.add(input);
|
|
148
|
+
try {
|
|
149
|
+
const limit = maxArrayLength ?? 1e3;
|
|
150
|
+
const limited = input.slice(0, limit);
|
|
151
|
+
return limited.map(
|
|
152
|
+
(v) => safeDeepClone(v, maxKeyLength, maxArrayLength, maxDepth, currentDepth + 1, seenSet)
|
|
153
|
+
);
|
|
154
|
+
} finally {
|
|
155
|
+
seenSet.delete(input);
|
|
156
|
+
}
|
|
103
157
|
}
|
|
104
158
|
if (isPlainObject(input)) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
159
|
+
if (currentDepth > maxDepth) {
|
|
160
|
+
throw new Error(`Maximum object depth (${maxDepth}) exceeded`);
|
|
161
|
+
}
|
|
162
|
+
const seenSet = seen ?? /* @__PURE__ */ new WeakSet();
|
|
163
|
+
if (seenSet.has(input)) return {};
|
|
164
|
+
seenSet.add(input);
|
|
165
|
+
try {
|
|
166
|
+
const out = {};
|
|
167
|
+
for (const k of Object.keys(input)) {
|
|
168
|
+
if (!sanitizeKey(k, maxKeyLength)) continue;
|
|
169
|
+
out[k] = safeDeepClone(
|
|
170
|
+
input[k],
|
|
171
|
+
maxKeyLength,
|
|
172
|
+
maxArrayLength,
|
|
173
|
+
maxDepth,
|
|
174
|
+
currentDepth + 1,
|
|
175
|
+
seenSet
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
return out;
|
|
179
|
+
} finally {
|
|
180
|
+
seenSet.delete(input);
|
|
109
181
|
}
|
|
110
|
-
return out;
|
|
111
182
|
}
|
|
112
183
|
return input;
|
|
113
184
|
}
|
|
@@ -123,8 +194,15 @@ function mergeValues(values, strategy) {
|
|
|
123
194
|
else acc.push(v);
|
|
124
195
|
return acc;
|
|
125
196
|
}, []);
|
|
126
|
-
|
|
127
|
-
|
|
197
|
+
/* istanbul ignore next -- exhaustiveness check unreachable from outside:
|
|
198
|
+
validateSanitizeOptions rejects every non-listed strategy at construction
|
|
199
|
+
time, so the only way to reach this branch is a programmer error (a new
|
|
200
|
+
MergeStrategy union member added without updating this switch). Failing
|
|
201
|
+
loudly here is preferable to a silent fallback. */
|
|
202
|
+
default: {
|
|
203
|
+
const _exhaustive = strategy;
|
|
204
|
+
throw new Error(`Unknown mergeStrategy: ${_exhaustive}`);
|
|
205
|
+
}
|
|
128
206
|
}
|
|
129
207
|
}
|
|
130
208
|
function isUrlEncodedContentType(req) {
|
|
@@ -148,6 +226,7 @@ function normalizeWhitelist(whitelist) {
|
|
|
148
226
|
if (typeof whitelist === "string") return [whitelist];
|
|
149
227
|
return whitelist.filter((w) => typeof w === "string");
|
|
150
228
|
}
|
|
229
|
+
var WHITELIST_PATH_CACHE_LIMIT = 1e3;
|
|
151
230
|
function buildWhitelistHelpers(whitelist) {
|
|
152
231
|
const exact = new Set(whitelist);
|
|
153
232
|
const prefixes = whitelist.filter((w) => w.length > 0);
|
|
@@ -173,9 +252,10 @@ function buildWhitelistHelpers(whitelist) {
|
|
|
173
252
|
}
|
|
174
253
|
}
|
|
175
254
|
}
|
|
176
|
-
if (pathCache.size
|
|
177
|
-
pathCache.
|
|
255
|
+
if (pathCache.size >= WHITELIST_PATH_CACHE_LIMIT) {
|
|
256
|
+
pathCache.clear();
|
|
178
257
|
}
|
|
258
|
+
pathCache.set(full, result);
|
|
179
259
|
return result;
|
|
180
260
|
}
|
|
181
261
|
};
|
|
@@ -223,20 +303,18 @@ function moveWhitelistedFromPolluted(reqPart, polluted, isWhitelisted) {
|
|
|
223
303
|
function detectAndReduce(input, opts) {
|
|
224
304
|
let keyCount = 0;
|
|
225
305
|
const polluted = {};
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
306
|
+
const pollutedKeysSet = /* @__PURE__ */ new Set();
|
|
307
|
+
const cloned = safeDeepClone(input, opts.maxKeyLength, opts.maxArrayLength, opts.maxDepth);
|
|
308
|
+
function processNode(node, path = [], depth = 0, inArray = false) {
|
|
309
|
+
if (node === null) return opts.preserveNull ? null : void 0;
|
|
310
|
+
if (node === void 0) return node;
|
|
229
311
|
if (Array.isArray(node)) {
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
return mergeValues(mapped, "combine");
|
|
312
|
+
const mapped = node.map((v) => processNode(v, path, depth, true));
|
|
313
|
+
if (!inArray) {
|
|
314
|
+
setIn(polluted, path, node);
|
|
315
|
+
pollutedKeysSet.add(path.join("."));
|
|
235
316
|
}
|
|
236
|
-
|
|
237
|
-
pollutedKeys.push(path.join("."));
|
|
238
|
-
const reduced = mergeValues(mapped, opts.mergeStrategy);
|
|
239
|
-
return reduced;
|
|
317
|
+
return mergeValues(mapped, opts.mergeStrategy);
|
|
240
318
|
}
|
|
241
319
|
if (isPlainObject(node)) {
|
|
242
320
|
if (depth > opts.maxDepth)
|
|
@@ -251,7 +329,7 @@ function detectAndReduce(input, opts) {
|
|
|
251
329
|
if (!safeKey) continue;
|
|
252
330
|
const child = node[rawKey];
|
|
253
331
|
const childPath = path.concat([safeKey]);
|
|
254
|
-
let value = processNode(child, childPath, depth + 1);
|
|
332
|
+
let value = processNode(child, childPath, depth + 1, false);
|
|
255
333
|
if (typeof value === "string" && opts.trimValues) value = value.trim();
|
|
256
334
|
out[safeKey] = value;
|
|
257
335
|
}
|
|
@@ -259,13 +337,14 @@ function detectAndReduce(input, opts) {
|
|
|
259
337
|
}
|
|
260
338
|
return node;
|
|
261
339
|
}
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
return { cleaned, pollutedTree: polluted, pollutedKeys };
|
|
340
|
+
const cleaned = processNode(cloned, [], 0, false);
|
|
341
|
+
return { cleaned, pollutedTree: polluted, pollutedKeys: Array.from(pollutedKeysSet) };
|
|
265
342
|
}
|
|
266
343
|
function sanitize(input, options = {}) {
|
|
344
|
+
validateSanitizeOptions(options);
|
|
267
345
|
const maxKeyLength = options.maxKeyLength ?? 200;
|
|
268
|
-
const
|
|
346
|
+
const maxDepthVal = options.maxDepth ?? 20;
|
|
347
|
+
const expandedInput = isPlainObject(input) ? expandObjectPaths(input, maxKeyLength, maxDepthVal) : input;
|
|
269
348
|
const whitelist = normalizeWhitelist(options.whitelist);
|
|
270
349
|
const { isWhitelistedPath } = buildWhitelistHelpers(whitelist);
|
|
271
350
|
const {
|
|
@@ -288,7 +367,7 @@ function sanitize(input, options = {}) {
|
|
|
288
367
|
moveWhitelistedFromPolluted(cleaned, pollutedTree, isWhitelistedPath);
|
|
289
368
|
return cleaned;
|
|
290
369
|
}
|
|
291
|
-
function
|
|
370
|
+
function validateSanitizeOptions(options) {
|
|
292
371
|
if (options.maxDepth !== void 0 && (typeof options.maxDepth !== "number" || options.maxDepth < 1 || options.maxDepth > 100)) {
|
|
293
372
|
throw new TypeError("maxDepth must be a number between 1 and 100");
|
|
294
373
|
}
|
|
@@ -304,10 +383,34 @@ function validateOptions(options) {
|
|
|
304
383
|
if (options.mergeStrategy !== void 0 && !["keepFirst", "keepLast", "combine"].includes(options.mergeStrategy)) {
|
|
305
384
|
throw new TypeError("mergeStrategy must be 'keepFirst', 'keepLast', or 'combine'");
|
|
306
385
|
}
|
|
386
|
+
if (options.trimValues !== void 0 && typeof options.trimValues !== "boolean") {
|
|
387
|
+
throw new TypeError("trimValues must be a boolean");
|
|
388
|
+
}
|
|
389
|
+
if (options.preserveNull !== void 0 && typeof options.preserveNull !== "boolean") {
|
|
390
|
+
throw new TypeError("preserveNull must be a boolean");
|
|
391
|
+
}
|
|
392
|
+
if (options.whitelist !== void 0) {
|
|
393
|
+
if (typeof options.whitelist !== "string" && !Array.isArray(options.whitelist)) {
|
|
394
|
+
throw new TypeError("whitelist must be a string or an array of strings");
|
|
395
|
+
}
|
|
396
|
+
if (Array.isArray(options.whitelist)) {
|
|
397
|
+
for (const entry of options.whitelist) {
|
|
398
|
+
if (typeof entry !== "string") {
|
|
399
|
+
throw new TypeError("whitelist must be a string or an array of strings");
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
function validateOptions(options) {
|
|
406
|
+
validateSanitizeOptions(options);
|
|
307
407
|
if (options.sources !== void 0 && !Array.isArray(options.sources)) {
|
|
308
408
|
throw new TypeError("sources must be an array");
|
|
309
409
|
}
|
|
310
410
|
if (options.sources !== void 0) {
|
|
411
|
+
if (options.sources.length === 0) {
|
|
412
|
+
throw new TypeError("sources must contain at least one of 'query', 'body', 'params'");
|
|
413
|
+
}
|
|
311
414
|
for (const source of options.sources) {
|
|
312
415
|
if (!["query", "body", "params"].includes(source)) {
|
|
313
416
|
throw new TypeError("sources must only contain 'query', 'body', or 'params'");
|
|
@@ -317,8 +420,27 @@ function validateOptions(options) {
|
|
|
317
420
|
if (options.checkBodyContentType !== void 0 && !["urlencoded", "any", "none"].includes(options.checkBodyContentType)) {
|
|
318
421
|
throw new TypeError("checkBodyContentType must be 'urlencoded', 'any', or 'none'");
|
|
319
422
|
}
|
|
320
|
-
if (options.excludePaths !== void 0
|
|
321
|
-
|
|
423
|
+
if (options.excludePaths !== void 0) {
|
|
424
|
+
if (!Array.isArray(options.excludePaths)) {
|
|
425
|
+
throw new TypeError("excludePaths must be an array");
|
|
426
|
+
}
|
|
427
|
+
for (const entry of options.excludePaths) {
|
|
428
|
+
if (typeof entry !== "string") {
|
|
429
|
+
throw new TypeError("excludePaths must contain only strings");
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
if (options.logger !== void 0 && typeof options.logger !== "function") {
|
|
434
|
+
throw new TypeError("logger must be a function");
|
|
435
|
+
}
|
|
436
|
+
if (options.onPollutionDetected !== void 0 && typeof options.onPollutionDetected !== "function") {
|
|
437
|
+
throw new TypeError("onPollutionDetected must be a function");
|
|
438
|
+
}
|
|
439
|
+
if (options.strict !== void 0 && typeof options.strict !== "boolean") {
|
|
440
|
+
throw new TypeError("strict must be a boolean");
|
|
441
|
+
}
|
|
442
|
+
if (options.logPollution !== void 0 && typeof options.logPollution !== "boolean") {
|
|
443
|
+
throw new TypeError("logPollution must be a boolean");
|
|
322
444
|
}
|
|
323
445
|
}
|
|
324
446
|
function hppx(options = {}) {
|
|
@@ -344,9 +466,39 @@ function hppx(options = {}) {
|
|
|
344
466
|
const { isWhitelistedPath } = buildWhitelistHelpers(whitelistArr);
|
|
345
467
|
return function hppxMiddleware(req, res, next) {
|
|
346
468
|
try {
|
|
347
|
-
|
|
469
|
+
let pathForExclusion;
|
|
470
|
+
try {
|
|
471
|
+
pathForExclusion = req?.path;
|
|
472
|
+
} catch (pathErr) {
|
|
473
|
+
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)}`;
|
|
474
|
+
if (logger) {
|
|
475
|
+
try {
|
|
476
|
+
logger(message);
|
|
477
|
+
} catch (_) {
|
|
478
|
+
console.warn(message);
|
|
479
|
+
}
|
|
480
|
+
} else {
|
|
481
|
+
console.warn(message);
|
|
482
|
+
}
|
|
483
|
+
pathForExclusion = void 0;
|
|
484
|
+
}
|
|
485
|
+
if (shouldExcludePath(pathForExclusion, excludePaths)) return next();
|
|
348
486
|
let anyPollutionDetected = false;
|
|
349
487
|
const allPollutedKeys = [];
|
|
488
|
+
const warned = /* @__PURE__ */ new Set();
|
|
489
|
+
const warn = (message) => {
|
|
490
|
+
if (warned.has(message)) return;
|
|
491
|
+
warned.add(message);
|
|
492
|
+
if (logger) {
|
|
493
|
+
try {
|
|
494
|
+
logger(message);
|
|
495
|
+
} catch (_) {
|
|
496
|
+
console.warn(message);
|
|
497
|
+
}
|
|
498
|
+
} else {
|
|
499
|
+
console.warn(message);
|
|
500
|
+
}
|
|
501
|
+
};
|
|
350
502
|
for (const source of sources) {
|
|
351
503
|
if (!req || typeof req !== "object") break;
|
|
352
504
|
if (req[source] === void 0) continue;
|
|
@@ -356,10 +508,10 @@ function hppx(options = {}) {
|
|
|
356
508
|
}
|
|
357
509
|
const part = req[source];
|
|
358
510
|
if (!isPlainObject(part)) continue;
|
|
359
|
-
const expandedPart = expandObjectPaths(part, maxKeyLength);
|
|
511
|
+
const expandedPart = expandObjectPaths(part, maxKeyLength, maxDepth);
|
|
360
512
|
const pollutedKey = `${source}Polluted`;
|
|
361
513
|
const processedKey = `__hppxProcessed_${source}`;
|
|
362
|
-
const hasProcessedBefore =
|
|
514
|
+
const hasProcessedBefore = Object.prototype.hasOwnProperty.call(req, processedKey);
|
|
363
515
|
if (!hasProcessedBefore) {
|
|
364
516
|
const { cleaned, pollutedTree, pollutedKeys } = detectAndReduce(expandedPart, {
|
|
365
517
|
mergeStrategy,
|
|
@@ -370,9 +522,21 @@ function hppx(options = {}) {
|
|
|
370
522
|
trimValues,
|
|
371
523
|
preserveNull
|
|
372
524
|
});
|
|
373
|
-
setReqPropertySafe(req, source, cleaned);
|
|
374
|
-
setReqPropertySafe(req, pollutedKey, pollutedTree);
|
|
375
|
-
|
|
525
|
+
setReqPropertySafe(req, source, cleaned, warn);
|
|
526
|
+
setReqPropertySafe(req, pollutedKey, pollutedTree, warn);
|
|
527
|
+
try {
|
|
528
|
+
Object.defineProperty(req, processedKey, {
|
|
529
|
+
value: true,
|
|
530
|
+
writable: false,
|
|
531
|
+
configurable: false,
|
|
532
|
+
enumerable: false
|
|
533
|
+
});
|
|
534
|
+
} catch (_) {
|
|
535
|
+
try {
|
|
536
|
+
req[processedKey] = true;
|
|
537
|
+
} catch (_assignErr) {
|
|
538
|
+
}
|
|
539
|
+
}
|
|
376
540
|
const sourceData = req[source];
|
|
377
541
|
const pollutedData = req[pollutedKey];
|
|
378
542
|
if (isPlainObject(sourceData) && isPlainObject(pollutedData)) {
|
|
@@ -439,10 +603,8 @@ function hppx(options = {}) {
|
|
|
439
603
|
try {
|
|
440
604
|
logger(error);
|
|
441
605
|
} catch (logErr) {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
console.error("[hppx] Original error:", error);
|
|
445
|
-
}
|
|
606
|
+
console.error("[hppx] Logger failed:", logErr);
|
|
607
|
+
console.error("[hppx] Original error:", error);
|
|
446
608
|
}
|
|
447
609
|
}
|
|
448
610
|
return next(error);
|
|
@@ -454,6 +616,7 @@ function hppx(options = {}) {
|
|
|
454
616
|
DANGEROUS_KEYS,
|
|
455
617
|
DEFAULT_SOURCES,
|
|
456
618
|
DEFAULT_STRATEGY,
|
|
619
|
+
__resetPathSegmentCache,
|
|
457
620
|
sanitize
|
|
458
621
|
});
|
|
459
622
|
if (module.exports.default) { module.exports = Object.assign(module.exports.default, module.exports); }
|