@arcis/node 1.1.0 → 1.3.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/README.md +156 -211
- package/dist/core/index.d.mts +4 -4
- package/dist/core/index.d.ts +4 -4
- package/dist/core/index.js +13 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +13 -2
- package/dist/core/index.mjs.map +1 -1
- package/dist/{headers-DBQedhrb.d.mts → encode-CrQCGlBq.d.mts} +202 -2
- package/dist/{headers-BJq2OA0i.d.ts → encode-jl9sOwmA.d.ts} +202 -2
- package/dist/{index-BvcFpoR3.d.ts → index-BAhgn9V2.d.ts} +96 -2
- package/dist/{index-iCOw8Fcg.d.ts → index-BGNKspqH.d.ts} +1 -1
- package/dist/{index-CslcoZUN.d.mts → index-Cd02z-0j.d.mts} +1 -1
- package/dist/{index-CCcPuTBo.d.mts → index-DgJtWMSj.d.mts} +96 -2
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +647 -7
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +629 -9
- package/dist/index.mjs.map +1 -1
- package/dist/logging/index.d.mts +1 -1
- package/dist/logging/index.d.ts +1 -1
- package/dist/logging/index.js +12 -1
- package/dist/logging/index.js.map +1 -1
- package/dist/logging/index.mjs +12 -1
- package/dist/logging/index.mjs.map +1 -1
- package/dist/middleware/index.d.mts +2 -2
- package/dist/middleware/index.d.ts +2 -2
- package/dist/middleware/index.js +168 -6
- package/dist/middleware/index.js.map +1 -1
- package/dist/middleware/index.mjs +165 -7
- package/dist/middleware/index.mjs.map +1 -1
- package/dist/sanitizers/index.d.mts +2 -2
- package/dist/sanitizers/index.d.ts +2 -2
- package/dist/sanitizers/index.js +403 -3
- package/dist/sanitizers/index.js.map +1 -1
- package/dist/sanitizers/index.mjs +388 -4
- package/dist/sanitizers/index.mjs.map +1 -1
- package/dist/stores/index.d.mts +1 -1
- package/dist/stores/index.d.ts +1 -1
- package/dist/stores/index.js.map +1 -1
- package/dist/stores/index.mjs.map +1 -1
- package/dist/{types-BOdL3ZWo.d.mts → types-BOkx5YJc.d.mts} +17 -2
- package/dist/{types-BOdL3ZWo.d.ts → types-BOkx5YJc.d.ts} +17 -2
- package/dist/validation/index.d.mts +2 -2
- package/dist/validation/index.d.ts +2 -2
- package/dist/validation/index.js +105 -3
- package/dist/validation/index.js.map +1 -1
- package/dist/validation/index.mjs +105 -3
- package/dist/validation/index.mjs.map +1 -1
- package/package.json +114 -114
package/dist/index.js
CHANGED
|
@@ -119,7 +119,11 @@ var SQL_PATTERNS = [
|
|
|
119
119
|
/** Time-based blind: SLEEP() */
|
|
120
120
|
/\bSLEEP\s*\(\s*\d+\s*\)/gi,
|
|
121
121
|
/** Time-based blind: BENCHMARK() */
|
|
122
|
-
/\bBENCHMARK\s*\(/gi
|
|
122
|
+
/\bBENCHMARK\s*\(/gi,
|
|
123
|
+
/** Time-based blind: PostgreSQL pg_sleep() */
|
|
124
|
+
/\bpg_sleep\s*\(/gi,
|
|
125
|
+
/** Time-based blind: MSSQL WAITFOR DELAY */
|
|
126
|
+
/\bWAITFOR\s+DELAY\b/gi
|
|
123
127
|
];
|
|
124
128
|
var PATH_PATTERNS = [
|
|
125
129
|
/** Unix path traversal */
|
|
@@ -137,6 +141,10 @@ var PATH_PATTERNS = [
|
|
|
137
141
|
/\.%2e[\\/]/gi,
|
|
138
142
|
/** Fully URL-encoded: %2e%2e%2f */
|
|
139
143
|
/%2e%2e%2f/gi,
|
|
144
|
+
/** Double URL-encoded forward slash: %252f */
|
|
145
|
+
/%252f/gi,
|
|
146
|
+
/** Dotdotslash bypass: ....// or ....\\ */
|
|
147
|
+
/\.{2,}[/\\]{2,}/g,
|
|
140
148
|
/** Null byte injection in paths */
|
|
141
149
|
/\0/g
|
|
142
150
|
];
|
|
@@ -152,7 +160,9 @@ var COMMAND_PATTERNS = [
|
|
|
152
160
|
*/
|
|
153
161
|
/[;&|`]/g,
|
|
154
162
|
/** Command substitution: $( ... ) — matched as a pair to reduce false positives */
|
|
155
|
-
/\$\(/g
|
|
163
|
+
/\$\(/g,
|
|
164
|
+
/** URL-encoded newline/carriage-return injection (%0a, %0d) */
|
|
165
|
+
/%0[ad]/gi
|
|
156
166
|
];
|
|
157
167
|
var DANGEROUS_PROTO_KEYS = /* @__PURE__ */ new Set([
|
|
158
168
|
"__proto__",
|
|
@@ -186,6 +196,7 @@ var NOSQL_DANGEROUS_KEYS = /* @__PURE__ */ new Set([
|
|
|
186
196
|
"$expr",
|
|
187
197
|
"$mod",
|
|
188
198
|
"$text",
|
|
199
|
+
"$jsonSchema",
|
|
189
200
|
// Array
|
|
190
201
|
"$elemMatch",
|
|
191
202
|
"$all",
|
|
@@ -296,7 +307,12 @@ function createHeaders(options = {}) {
|
|
|
296
307
|
hsts = true,
|
|
297
308
|
referrerPolicy = HEADERS.REFERRER_POLICY,
|
|
298
309
|
permissionsPolicy = HEADERS.PERMISSIONS_POLICY,
|
|
299
|
-
cacheControl = true
|
|
310
|
+
cacheControl = true,
|
|
311
|
+
crossOriginOpenerPolicy = "same-origin",
|
|
312
|
+
crossOriginResourcePolicy = "same-origin",
|
|
313
|
+
crossOriginEmbedderPolicy = "require-corp",
|
|
314
|
+
originAgentCluster = true,
|
|
315
|
+
dnsPrefetchControl = true
|
|
300
316
|
} = options;
|
|
301
317
|
return (req, res, next) => {
|
|
302
318
|
if (contentSecurityPolicy) {
|
|
@@ -304,7 +320,7 @@ function createHeaders(options = {}) {
|
|
|
304
320
|
res.setHeader("Content-Security-Policy", csp);
|
|
305
321
|
}
|
|
306
322
|
if (xssFilter) {
|
|
307
|
-
res.setHeader("X-XSS-Protection", "
|
|
323
|
+
res.setHeader("X-XSS-Protection", "0");
|
|
308
324
|
}
|
|
309
325
|
if (noSniff) {
|
|
310
326
|
res.setHeader("X-Content-Type-Options", HEADERS.CONTENT_TYPE_OPTIONS);
|
|
@@ -331,6 +347,21 @@ function createHeaders(options = {}) {
|
|
|
331
347
|
if (permissionsPolicy) {
|
|
332
348
|
res.setHeader("Permissions-Policy", permissionsPolicy);
|
|
333
349
|
}
|
|
350
|
+
if (crossOriginOpenerPolicy) {
|
|
351
|
+
res.setHeader("Cross-Origin-Opener-Policy", crossOriginOpenerPolicy);
|
|
352
|
+
}
|
|
353
|
+
if (crossOriginResourcePolicy) {
|
|
354
|
+
res.setHeader("Cross-Origin-Resource-Policy", crossOriginResourcePolicy);
|
|
355
|
+
}
|
|
356
|
+
if (crossOriginEmbedderPolicy) {
|
|
357
|
+
res.setHeader("Cross-Origin-Embedder-Policy", crossOriginEmbedderPolicy);
|
|
358
|
+
}
|
|
359
|
+
if (originAgentCluster) {
|
|
360
|
+
res.setHeader("Origin-Agent-Cluster", "?1");
|
|
361
|
+
}
|
|
362
|
+
if (dnsPrefetchControl) {
|
|
363
|
+
res.setHeader("X-DNS-Prefetch-Control", "off");
|
|
364
|
+
}
|
|
334
365
|
res.setHeader("X-Permitted-Cross-Domain-Policies", "none");
|
|
335
366
|
if (cacheControl) {
|
|
336
367
|
const cacheControlValue = typeof cacheControl === "string" ? cacheControl : HEADERS.CACHE_CONTROL;
|
|
@@ -786,7 +817,8 @@ function sanitizeObject(obj, options = {}) {
|
|
|
786
817
|
if (typeof obj === "string") return sanitizeString(obj, options);
|
|
787
818
|
if (typeof obj !== "object") return obj;
|
|
788
819
|
if (Array.isArray(obj)) return obj.map((item) => sanitizeObject(item, options));
|
|
789
|
-
|
|
820
|
+
const result = sanitizeObjectDepth(obj, options, 0);
|
|
821
|
+
return options.freeze ? Object.freeze(result) : result;
|
|
790
822
|
}
|
|
791
823
|
function sanitizeObjectDepth(obj, options, depth) {
|
|
792
824
|
if (depth >= INPUT.MAX_RECURSION_DEPTH) return obj;
|
|
@@ -883,6 +915,179 @@ function detectPrototypePollution(obj, maxDepth = 10) {
|
|
|
883
915
|
return false;
|
|
884
916
|
}
|
|
885
917
|
|
|
918
|
+
// src/sanitizers/ssti.ts
|
|
919
|
+
var SSTI_DETECT_PATTERNS = [
|
|
920
|
+
/** Jinja2 / Twig / Nunjucks: {{ ... }} */
|
|
921
|
+
/\{\{.*?\}\}/g,
|
|
922
|
+
/** Freemarker / Thymeleaf / Spring EL: ${ ... } */
|
|
923
|
+
/\$\{.*?\}/g,
|
|
924
|
+
/** ERB / EJS: <%= ... %> or <% ... %> */
|
|
925
|
+
/<%[=\-]?.*?%>/gs,
|
|
926
|
+
/** Pug / Jade / Slim: #{ ... } */
|
|
927
|
+
/#\{.*?\}/g,
|
|
928
|
+
/** Python dunder sandbox escape */
|
|
929
|
+
/__(?:class|mro|subclasses|globals|builtins|import)__/gi,
|
|
930
|
+
/** Jinja2 config leak: {{config.X}} or {{config['X']}} */
|
|
931
|
+
/\{\{\s*config[.\[]/gi,
|
|
932
|
+
/** Jinja2 built-in objects */
|
|
933
|
+
/\{\{\s*(?:self|request|lipsum|cycler|joiner|namespace|range)\b/gi
|
|
934
|
+
];
|
|
935
|
+
var SSTI_REMOVE_PATTERNS = [
|
|
936
|
+
/\{\{.*?\}\}/g,
|
|
937
|
+
/\$\{.*?\}/g,
|
|
938
|
+
/<%[=\-]?.*?%>/gs,
|
|
939
|
+
/#\{.*?\}/g,
|
|
940
|
+
/__(?:class|mro|subclasses|globals|builtins|import)__/gi
|
|
941
|
+
];
|
|
942
|
+
function sanitizeSsti(input, collectThreats = false) {
|
|
943
|
+
if (typeof input !== "string") {
|
|
944
|
+
return collectThreats ? { value: String(input), wasSanitized: false, threats: [] } : String(input);
|
|
945
|
+
}
|
|
946
|
+
const threats = [];
|
|
947
|
+
let value = input;
|
|
948
|
+
let wasSanitized = false;
|
|
949
|
+
for (const pattern of SSTI_REMOVE_PATTERNS) {
|
|
950
|
+
pattern.lastIndex = 0;
|
|
951
|
+
if (pattern.test(value)) {
|
|
952
|
+
pattern.lastIndex = 0;
|
|
953
|
+
if (collectThreats) {
|
|
954
|
+
const matches = value.match(pattern);
|
|
955
|
+
if (matches) {
|
|
956
|
+
for (const match of matches) {
|
|
957
|
+
threats.push({
|
|
958
|
+
type: "ssti",
|
|
959
|
+
pattern: pattern.source,
|
|
960
|
+
original: match
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
value = value.replace(pattern, "");
|
|
966
|
+
wasSanitized = true;
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
if (collectThreats) {
|
|
970
|
+
return { value, wasSanitized, threats };
|
|
971
|
+
}
|
|
972
|
+
return value;
|
|
973
|
+
}
|
|
974
|
+
function detectSsti(input) {
|
|
975
|
+
if (typeof input !== "string") return false;
|
|
976
|
+
for (const pattern of SSTI_DETECT_PATTERNS) {
|
|
977
|
+
pattern.lastIndex = 0;
|
|
978
|
+
if (pattern.test(input)) {
|
|
979
|
+
return true;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
return false;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
// src/sanitizers/xxe.ts
|
|
986
|
+
var XXE_DETECT_PATTERNS = [
|
|
987
|
+
/** DOCTYPE declaration */
|
|
988
|
+
/<!DOCTYPE\b/gi,
|
|
989
|
+
/** ENTITY declaration */
|
|
990
|
+
/<!ENTITY\b/gi,
|
|
991
|
+
/** SYSTEM keyword with URI */
|
|
992
|
+
/\bSYSTEM\s+["']/gi,
|
|
993
|
+
/** PUBLIC keyword with URI */
|
|
994
|
+
/\bPUBLIC\s+["']/gi,
|
|
995
|
+
/** Parameter entity reference (%entity;) */
|
|
996
|
+
/%\s*\w+\s*;/g,
|
|
997
|
+
/** CDATA section (often used to smuggle payloads) */
|
|
998
|
+
/<!\[CDATA\[/gi
|
|
999
|
+
];
|
|
1000
|
+
var XXE_REMOVE_PATTERNS = [
|
|
1001
|
+
/** Full DOCTYPE block with optional internal subset: <!DOCTYPE ... [...]> */
|
|
1002
|
+
/<!DOCTYPE\s[^[>]*(?:\[[^\]]*\]\s*)?>|<!DOCTYPE\s[^>]*>/gi,
|
|
1003
|
+
/** Full ENTITY declaration: <!ENTITY ... > */
|
|
1004
|
+
/<!ENTITY[^>]*>/gi,
|
|
1005
|
+
/** CDATA sections: <![CDATA[ ... ]]> */
|
|
1006
|
+
/<!\[CDATA\[[\s\S]*?\]\]>/gi
|
|
1007
|
+
];
|
|
1008
|
+
function sanitizeXxe(input, collectThreats = false) {
|
|
1009
|
+
if (typeof input !== "string") {
|
|
1010
|
+
return collectThreats ? { value: String(input), wasSanitized: false, threats: [] } : String(input);
|
|
1011
|
+
}
|
|
1012
|
+
const threats = [];
|
|
1013
|
+
let value = input;
|
|
1014
|
+
let wasSanitized = false;
|
|
1015
|
+
for (const pattern of XXE_REMOVE_PATTERNS) {
|
|
1016
|
+
pattern.lastIndex = 0;
|
|
1017
|
+
if (pattern.test(value)) {
|
|
1018
|
+
pattern.lastIndex = 0;
|
|
1019
|
+
if (collectThreats) {
|
|
1020
|
+
const matches = value.match(pattern);
|
|
1021
|
+
if (matches) {
|
|
1022
|
+
for (const match of matches) {
|
|
1023
|
+
threats.push({
|
|
1024
|
+
type: "xxe",
|
|
1025
|
+
pattern: pattern.source,
|
|
1026
|
+
original: match
|
|
1027
|
+
});
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
value = value.replace(pattern, "");
|
|
1032
|
+
wasSanitized = true;
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
if (collectThreats) {
|
|
1036
|
+
return { value, wasSanitized, threats };
|
|
1037
|
+
}
|
|
1038
|
+
return value;
|
|
1039
|
+
}
|
|
1040
|
+
function detectXxe(input) {
|
|
1041
|
+
if (typeof input !== "string") return false;
|
|
1042
|
+
for (const pattern of XXE_DETECT_PATTERNS) {
|
|
1043
|
+
pattern.lastIndex = 0;
|
|
1044
|
+
if (pattern.test(input)) {
|
|
1045
|
+
return true;
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
return false;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
// src/sanitizers/jsonp.ts
|
|
1052
|
+
var SAFE_CALLBACK_PATTERN = /^[a-zA-Z_$][a-zA-Z0-9_$.[\]]*$/;
|
|
1053
|
+
var DANGEROUS_CALLBACK_PATTERNS = [
|
|
1054
|
+
/\.\./,
|
|
1055
|
+
// prototype chain traversal
|
|
1056
|
+
/\[\s*\]/
|
|
1057
|
+
// empty bracket access
|
|
1058
|
+
];
|
|
1059
|
+
function sanitizeJsonpCallback(callback, maxLength = 128) {
|
|
1060
|
+
if (typeof callback !== "string" || callback.length === 0) {
|
|
1061
|
+
return null;
|
|
1062
|
+
}
|
|
1063
|
+
if (callback.length > maxLength) {
|
|
1064
|
+
return null;
|
|
1065
|
+
}
|
|
1066
|
+
if (!SAFE_CALLBACK_PATTERN.test(callback)) {
|
|
1067
|
+
return null;
|
|
1068
|
+
}
|
|
1069
|
+
for (const pattern of DANGEROUS_CALLBACK_PATTERNS) {
|
|
1070
|
+
if (pattern.test(callback)) {
|
|
1071
|
+
return null;
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
return callback;
|
|
1075
|
+
}
|
|
1076
|
+
function detectJsonpInjection(callback) {
|
|
1077
|
+
if (typeof callback !== "string" || callback.length === 0) {
|
|
1078
|
+
return false;
|
|
1079
|
+
}
|
|
1080
|
+
if (!SAFE_CALLBACK_PATTERN.test(callback)) {
|
|
1081
|
+
return true;
|
|
1082
|
+
}
|
|
1083
|
+
for (const pattern of DANGEROUS_CALLBACK_PATTERNS) {
|
|
1084
|
+
if (pattern.test(callback)) {
|
|
1085
|
+
return true;
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
return false;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
886
1091
|
// src/sanitizers/headers.ts
|
|
887
1092
|
var HEADER_INJECTION_PATTERN = /\r\n|\r|\n|\0/g;
|
|
888
1093
|
function sanitizeHeaderValue(input, collectThreats = false) {
|
|
@@ -932,6 +1137,205 @@ function detectHeaderInjection(input) {
|
|
|
932
1137
|
return HEADER_INJECTION_PATTERN.test(input);
|
|
933
1138
|
}
|
|
934
1139
|
|
|
1140
|
+
// src/sanitizers/pii.ts
|
|
1141
|
+
var EMAIL_RE = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z]{2,})+/g;
|
|
1142
|
+
var PHONE_RE = /(?:\+?1[-.\s]?)?\(?[2-9]\d{2}\)?[-.\s]?\d{3}[-.\s]?\d{4}/g;
|
|
1143
|
+
var CREDIT_CARD_RE = /\b(?:\d[ -]*?){13,19}\b/g;
|
|
1144
|
+
var SSN_RE = /\b\d{3}[-\s]\d{2}[-\s]\d{4}\b/g;
|
|
1145
|
+
var IPV4_RE = /\b(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\b/g;
|
|
1146
|
+
var IPV6_RE = /\b(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\b|\b(?:[0-9a-fA-F]{1,4}:){1,7}:|::(?:[0-9a-fA-F]{1,4}:){0,5}[0-9a-fA-F]{1,4}\b/g;
|
|
1147
|
+
var PATTERN_MAP = {
|
|
1148
|
+
email: [EMAIL_RE],
|
|
1149
|
+
phone: [PHONE_RE],
|
|
1150
|
+
credit_card: [CREDIT_CARD_RE],
|
|
1151
|
+
ssn: [SSN_RE],
|
|
1152
|
+
ip_address: [IPV4_RE, IPV6_RE]
|
|
1153
|
+
};
|
|
1154
|
+
var ALL_TYPES = ["email", "phone", "credit_card", "ssn", "ip_address"];
|
|
1155
|
+
var TYPE_LABELS = {
|
|
1156
|
+
email: "[EMAIL]",
|
|
1157
|
+
phone: "[PHONE]",
|
|
1158
|
+
credit_card: "[CREDIT_CARD]",
|
|
1159
|
+
ssn: "[SSN]",
|
|
1160
|
+
ip_address: "[IP_ADDRESS]"
|
|
1161
|
+
};
|
|
1162
|
+
function luhnCheck(value) {
|
|
1163
|
+
const digits = value.replace(/[\s-]/g, "");
|
|
1164
|
+
if (!/^\d{13,19}$/.test(digits)) return false;
|
|
1165
|
+
let sum = 0;
|
|
1166
|
+
let alternate = false;
|
|
1167
|
+
for (let i = digits.length - 1; i >= 0; i--) {
|
|
1168
|
+
let n = parseInt(digits[i], 10);
|
|
1169
|
+
if (alternate) {
|
|
1170
|
+
n *= 2;
|
|
1171
|
+
if (n > 9) n -= 9;
|
|
1172
|
+
}
|
|
1173
|
+
sum += n;
|
|
1174
|
+
alternate = !alternate;
|
|
1175
|
+
}
|
|
1176
|
+
return sum % 10 === 0;
|
|
1177
|
+
}
|
|
1178
|
+
function scanPii(input, options = {}) {
|
|
1179
|
+
if (!input || typeof input !== "string") return [];
|
|
1180
|
+
const types = options.types ?? ALL_TYPES;
|
|
1181
|
+
const matches = [];
|
|
1182
|
+
for (const type of types) {
|
|
1183
|
+
const patterns = PATTERN_MAP[type];
|
|
1184
|
+
if (!patterns) continue;
|
|
1185
|
+
for (const pattern of patterns) {
|
|
1186
|
+
const re = new RegExp(pattern.source, pattern.flags);
|
|
1187
|
+
let match;
|
|
1188
|
+
while ((match = re.exec(input)) !== null) {
|
|
1189
|
+
const value = match[0];
|
|
1190
|
+
if (type === "credit_card" && !luhnCheck(value)) continue;
|
|
1191
|
+
if (type === "ssn") {
|
|
1192
|
+
const area = parseInt(value.substring(0, 3), 10);
|
|
1193
|
+
if (area === 0 || area === 666 || area >= 900) continue;
|
|
1194
|
+
}
|
|
1195
|
+
matches.push({
|
|
1196
|
+
type,
|
|
1197
|
+
value,
|
|
1198
|
+
start: match.index,
|
|
1199
|
+
end: match.index + value.length
|
|
1200
|
+
});
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
matches.sort((a, b) => a.start - b.start);
|
|
1205
|
+
return matches;
|
|
1206
|
+
}
|
|
1207
|
+
function detectPii(input, options = {}) {
|
|
1208
|
+
return scanPii(input, options).length > 0;
|
|
1209
|
+
}
|
|
1210
|
+
function redactPii(input, options = {}) {
|
|
1211
|
+
if (!input || typeof input !== "string") return input;
|
|
1212
|
+
const matches = scanPii(input, options);
|
|
1213
|
+
if (matches.length === 0) return input;
|
|
1214
|
+
const replacement = options.replacement ?? "[REDACTED]";
|
|
1215
|
+
let result = input;
|
|
1216
|
+
for (let i = matches.length - 1; i >= 0; i--) {
|
|
1217
|
+
const m = matches[i];
|
|
1218
|
+
const label = options.typeLabels ? TYPE_LABELS[m.type] : replacement;
|
|
1219
|
+
result = result.substring(0, m.start) + label + result.substring(m.end);
|
|
1220
|
+
}
|
|
1221
|
+
return result;
|
|
1222
|
+
}
|
|
1223
|
+
function scanObjectPii(obj, options = {}, path = "") {
|
|
1224
|
+
const results = [];
|
|
1225
|
+
if (!obj || typeof obj !== "object") return results;
|
|
1226
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
1227
|
+
const fieldPath = path ? `${path}.${key}` : key;
|
|
1228
|
+
if (typeof value === "string") {
|
|
1229
|
+
const matches = scanPii(value, options);
|
|
1230
|
+
for (const m of matches) {
|
|
1231
|
+
results.push({ ...m, field: fieldPath });
|
|
1232
|
+
}
|
|
1233
|
+
} else if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
1234
|
+
results.push(...scanObjectPii(value, options, fieldPath));
|
|
1235
|
+
} else if (Array.isArray(value)) {
|
|
1236
|
+
for (let i = 0; i < value.length; i++) {
|
|
1237
|
+
const item = value[i];
|
|
1238
|
+
if (typeof item === "string") {
|
|
1239
|
+
const matches = scanPii(item, options);
|
|
1240
|
+
for (const m of matches) {
|
|
1241
|
+
results.push({ ...m, field: `${fieldPath}[${i}]` });
|
|
1242
|
+
}
|
|
1243
|
+
} else if (item && typeof item === "object") {
|
|
1244
|
+
results.push(...scanObjectPii(item, options, `${fieldPath}[${i}]`));
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
return results;
|
|
1250
|
+
}
|
|
1251
|
+
function redactObjectPii(obj, options = {}) {
|
|
1252
|
+
if (!obj || typeof obj !== "object") return obj;
|
|
1253
|
+
const result = {};
|
|
1254
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
1255
|
+
if (typeof value === "string") {
|
|
1256
|
+
result[key] = redactPii(value, options);
|
|
1257
|
+
} else if (Array.isArray(value)) {
|
|
1258
|
+
result[key] = value.map((item) => {
|
|
1259
|
+
if (typeof item === "string") return redactPii(item, options);
|
|
1260
|
+
if (item && typeof item === "object") return redactObjectPii(item, options);
|
|
1261
|
+
return item;
|
|
1262
|
+
});
|
|
1263
|
+
} else if (value && typeof value === "object") {
|
|
1264
|
+
result[key] = redactObjectPii(value, options);
|
|
1265
|
+
} else {
|
|
1266
|
+
result[key] = value;
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
return result;
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
// src/sanitizers/encode.ts
|
|
1273
|
+
var HTML_ENTITIES = {
|
|
1274
|
+
"&": "&",
|
|
1275
|
+
"<": "<",
|
|
1276
|
+
">": ">",
|
|
1277
|
+
'"': """,
|
|
1278
|
+
"'": "'"
|
|
1279
|
+
};
|
|
1280
|
+
var HTML_ENCODE_RE = /[&<>"']/g;
|
|
1281
|
+
function encodeForHtml(value) {
|
|
1282
|
+
if (!value) return "";
|
|
1283
|
+
return value.replace(HTML_ENCODE_RE, (ch) => HTML_ENTITIES[ch]);
|
|
1284
|
+
}
|
|
1285
|
+
function encodeForAttribute(value) {
|
|
1286
|
+
if (!value) return "";
|
|
1287
|
+
let result = "";
|
|
1288
|
+
for (let i = 0; i < value.length; i++) {
|
|
1289
|
+
const ch = value.charCodeAt(i);
|
|
1290
|
+
if (ch >= 48 && ch <= 57 || // 0-9
|
|
1291
|
+
ch >= 65 && ch <= 90 || // A-Z
|
|
1292
|
+
ch >= 97 && ch <= 122) {
|
|
1293
|
+
result += value[i];
|
|
1294
|
+
} else {
|
|
1295
|
+
result += `&#x${ch.toString(16).toUpperCase()};`;
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
return result;
|
|
1299
|
+
}
|
|
1300
|
+
function encodeForJs(value) {
|
|
1301
|
+
if (!value) return "";
|
|
1302
|
+
let result = "";
|
|
1303
|
+
for (let i = 0; i < value.length; i++) {
|
|
1304
|
+
const ch = value.charCodeAt(i);
|
|
1305
|
+
if (ch >= 48 && ch <= 57 || // 0-9
|
|
1306
|
+
ch >= 65 && ch <= 90 || // A-Z
|
|
1307
|
+
ch >= 97 && ch <= 122) {
|
|
1308
|
+
result += value[i];
|
|
1309
|
+
} else if (ch < 256) {
|
|
1310
|
+
result += `\\x${ch.toString(16).toUpperCase().padStart(2, "0")}`;
|
|
1311
|
+
} else {
|
|
1312
|
+
result += `\\u${ch.toString(16).toUpperCase().padStart(4, "0")}`;
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
return result;
|
|
1316
|
+
}
|
|
1317
|
+
function encodeForUrl(value) {
|
|
1318
|
+
if (!value) return "";
|
|
1319
|
+
return encodeURIComponent(value).replace(/[!'()*]/g, (ch) => {
|
|
1320
|
+
return `%${ch.charCodeAt(0).toString(16).toUpperCase()}`;
|
|
1321
|
+
});
|
|
1322
|
+
}
|
|
1323
|
+
function encodeForCss(value) {
|
|
1324
|
+
if (!value) return "";
|
|
1325
|
+
let result = "";
|
|
1326
|
+
for (let i = 0; i < value.length; i++) {
|
|
1327
|
+
const ch = value.charCodeAt(i);
|
|
1328
|
+
if (ch >= 48 && ch <= 57 || // 0-9
|
|
1329
|
+
ch >= 65 && ch <= 90 || // A-Z
|
|
1330
|
+
ch >= 97 && ch <= 122) {
|
|
1331
|
+
result += value[i];
|
|
1332
|
+
} else {
|
|
1333
|
+
result += `\\${ch.toString(16).toUpperCase()} `;
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
return result;
|
|
1337
|
+
}
|
|
1338
|
+
|
|
935
1339
|
// src/validation/schema.ts
|
|
936
1340
|
function validate(schema, source = "body") {
|
|
937
1341
|
return (req, res, next) => {
|
|
@@ -1312,6 +1716,18 @@ function validateUrl(url, options = {}) {
|
|
|
1312
1716
|
return { safe: false, reason: "loopback address" };
|
|
1313
1717
|
}
|
|
1314
1718
|
}
|
|
1719
|
+
if (!allowLocalhost || !allowPrivate) {
|
|
1720
|
+
const decimalCheck = checkDecimalIp(hostname, allowLocalhost, allowPrivate);
|
|
1721
|
+
if (decimalCheck) {
|
|
1722
|
+
return { safe: false, reason: decimalCheck };
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
if (!allowLocalhost || !allowPrivate) {
|
|
1726
|
+
const octalCheck = checkOctalIp(hostname, allowLocalhost, allowPrivate);
|
|
1727
|
+
if (octalCheck) {
|
|
1728
|
+
return { safe: false, reason: octalCheck };
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1315
1731
|
if (!allowPrivate) {
|
|
1316
1732
|
const privateCheck = checkPrivateIp(hostname);
|
|
1317
1733
|
if (privateCheck) {
|
|
@@ -1343,13 +1759,93 @@ function checkPrivateIp(hostname) {
|
|
|
1343
1759
|
if (/^0\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(hostname)) {
|
|
1344
1760
|
return "current network address (0.0.0.0/8)";
|
|
1345
1761
|
}
|
|
1346
|
-
if (hostname === "metadata.google.internal" || hostname === "metadata.internal") {
|
|
1762
|
+
if (hostname === "metadata.google.internal" || hostname === "metadata.internal" || hostname === "metadata.azure.internal") {
|
|
1347
1763
|
return "cloud metadata endpoint";
|
|
1348
1764
|
}
|
|
1349
1765
|
const ipv6 = hostname.replace(/^\[|\]$/g, "");
|
|
1350
1766
|
if (ipv6 === "::1" || ipv6 === "::" || ipv6.startsWith("fc") || ipv6.startsWith("fd") || ipv6.startsWith("fe80")) {
|
|
1351
1767
|
return "private IPv6 address";
|
|
1352
1768
|
}
|
|
1769
|
+
const mappedDotted = ipv6.match(/^::ffff:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/i);
|
|
1770
|
+
if (mappedDotted) {
|
|
1771
|
+
const mappedIp = mappedDotted[1];
|
|
1772
|
+
if (/^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(mappedIp)) {
|
|
1773
|
+
return "IPv6-mapped loopback address";
|
|
1774
|
+
}
|
|
1775
|
+
const mappedCheck = checkPrivateIp(mappedIp);
|
|
1776
|
+
if (mappedCheck) {
|
|
1777
|
+
return `IPv6-mapped ${mappedCheck}`;
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
const mappedHex = ipv6.match(/^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i);
|
|
1781
|
+
if (mappedHex) {
|
|
1782
|
+
const hi = parseInt(mappedHex[1], 16);
|
|
1783
|
+
const lo = parseInt(mappedHex[2], 16);
|
|
1784
|
+
const a = hi >> 8 & 255;
|
|
1785
|
+
const b = hi & 255;
|
|
1786
|
+
const c = lo >> 8 & 255;
|
|
1787
|
+
const d = lo & 255;
|
|
1788
|
+
const dotted = `${a}.${b}.${c}.${d}`;
|
|
1789
|
+
if (a === 127) {
|
|
1790
|
+
return "IPv6-mapped loopback address";
|
|
1791
|
+
}
|
|
1792
|
+
const hexCheck = checkPrivateIp(dotted);
|
|
1793
|
+
if (hexCheck) {
|
|
1794
|
+
return `IPv6-mapped ${hexCheck}`;
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
return null;
|
|
1798
|
+
}
|
|
1799
|
+
function checkDecimalIp(hostname, allowLocalhost, allowPrivate) {
|
|
1800
|
+
if (!/^\d+$/.test(hostname)) return null;
|
|
1801
|
+
const num = parseInt(hostname, 10);
|
|
1802
|
+
if (isNaN(num) || num < 0 || num > 4294967295) return null;
|
|
1803
|
+
const a = num >>> 24 & 255;
|
|
1804
|
+
const b = num >>> 16 & 255;
|
|
1805
|
+
const c = num >>> 8 & 255;
|
|
1806
|
+
const d = num & 255;
|
|
1807
|
+
const dotted = `${a}.${b}.${c}.${d}`;
|
|
1808
|
+
if (!allowLocalhost && a === 127) {
|
|
1809
|
+
return `loopback address (decimal IP: ${dotted})`;
|
|
1810
|
+
}
|
|
1811
|
+
if (!allowPrivate) {
|
|
1812
|
+
const privateCheck = checkPrivateIp(dotted);
|
|
1813
|
+
if (privateCheck) {
|
|
1814
|
+
return `${privateCheck} (decimal IP: ${dotted})`;
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
return null;
|
|
1818
|
+
}
|
|
1819
|
+
function checkOctalIp(hostname, allowLocalhost, allowPrivate) {
|
|
1820
|
+
const parts = hostname.split(".");
|
|
1821
|
+
if (parts.length !== 4) return null;
|
|
1822
|
+
const hasAlternateNotation = parts.some((p) => /^0[0-7]+$/.test(p) || /^0x[0-9a-fA-F]+$/i.test(p));
|
|
1823
|
+
if (!hasAlternateNotation) return null;
|
|
1824
|
+
const octets = [];
|
|
1825
|
+
for (const part of parts) {
|
|
1826
|
+
let val;
|
|
1827
|
+
if (/^0x[0-9a-fA-F]+$/i.test(part)) {
|
|
1828
|
+
val = parseInt(part, 16);
|
|
1829
|
+
} else if (/^0[0-7]*$/.test(part)) {
|
|
1830
|
+
val = parseInt(part, 8);
|
|
1831
|
+
} else if (/^\d+$/.test(part)) {
|
|
1832
|
+
val = parseInt(part, 10);
|
|
1833
|
+
} else {
|
|
1834
|
+
return null;
|
|
1835
|
+
}
|
|
1836
|
+
if (val < 0 || val > 255) return null;
|
|
1837
|
+
octets.push(val);
|
|
1838
|
+
}
|
|
1839
|
+
const dotted = octets.join(".");
|
|
1840
|
+
if (!allowLocalhost && octets[0] === 127) {
|
|
1841
|
+
return `loopback address (octal IP: ${dotted})`;
|
|
1842
|
+
}
|
|
1843
|
+
if (!allowPrivate) {
|
|
1844
|
+
const privateCheck = checkPrivateIp(dotted);
|
|
1845
|
+
if (privateCheck) {
|
|
1846
|
+
return `${privateCheck} (octal IP: ${dotted})`;
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1353
1849
|
return null;
|
|
1354
1850
|
}
|
|
1355
1851
|
|
|
@@ -1665,12 +2161,21 @@ function isValidEmailSyntax(email) {
|
|
|
1665
2161
|
}
|
|
1666
2162
|
|
|
1667
2163
|
// src/logging/redactor.ts
|
|
2164
|
+
var LOG_LEVELS = {
|
|
2165
|
+
debug: 0,
|
|
2166
|
+
info: 1,
|
|
2167
|
+
warn: 2,
|
|
2168
|
+
error: 3,
|
|
2169
|
+
silent: 4
|
|
2170
|
+
};
|
|
1668
2171
|
function createSafeLogger(options = {}) {
|
|
1669
2172
|
const {
|
|
1670
2173
|
redactKeys = [],
|
|
1671
2174
|
maxLength = REDACTION.DEFAULT_MAX_LENGTH,
|
|
1672
|
-
redactPatterns = []
|
|
2175
|
+
redactPatterns = [],
|
|
2176
|
+
level: minLevel = "debug"
|
|
1673
2177
|
} = options;
|
|
2178
|
+
const minLevelNum = LOG_LEVELS[minLevel] ?? 0;
|
|
1674
2179
|
const allRedactKeys = /* @__PURE__ */ new Set([
|
|
1675
2180
|
...Array.from(REDACTION.SENSITIVE_KEYS),
|
|
1676
2181
|
...redactKeys.map((k) => k.toLowerCase())
|
|
@@ -1696,6 +2201,8 @@ function createSafeLogger(options = {}) {
|
|
|
1696
2201
|
return result;
|
|
1697
2202
|
}
|
|
1698
2203
|
function log(level, message, data) {
|
|
2204
|
+
const levelNum = LOG_LEVELS[level] ?? 0;
|
|
2205
|
+
if (levelNum < minLevelNum) return;
|
|
1699
2206
|
const entry = {
|
|
1700
2207
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1701
2208
|
level,
|
|
@@ -2302,6 +2809,119 @@ function botProtection(options = {}) {
|
|
|
2302
2809
|
next();
|
|
2303
2810
|
};
|
|
2304
2811
|
}
|
|
2812
|
+
var DEFAULTS = {
|
|
2813
|
+
cookieName: "_csrf",
|
|
2814
|
+
headerName: "x-csrf-token",
|
|
2815
|
+
fieldName: "_csrf",
|
|
2816
|
+
tokenLength: 32,
|
|
2817
|
+
protectedMethods: ["POST", "PUT", "PATCH", "DELETE"]
|
|
2818
|
+
};
|
|
2819
|
+
function generateCsrfToken(length = 32) {
|
|
2820
|
+
return crypto.randomBytes(length).toString("hex");
|
|
2821
|
+
}
|
|
2822
|
+
function validateCsrfToken(cookieToken, requestToken) {
|
|
2823
|
+
if (!cookieToken || !requestToken) return false;
|
|
2824
|
+
if (cookieToken.length !== requestToken.length) return false;
|
|
2825
|
+
let result = 0;
|
|
2826
|
+
for (let i = 0; i < cookieToken.length; i++) {
|
|
2827
|
+
result |= cookieToken.charCodeAt(i) ^ requestToken.charCodeAt(i);
|
|
2828
|
+
}
|
|
2829
|
+
return result === 0;
|
|
2830
|
+
}
|
|
2831
|
+
function getRequestToken(req, headerName, fieldName) {
|
|
2832
|
+
const headerToken = req.headers[headerName.toLowerCase()];
|
|
2833
|
+
if (typeof headerToken === "string" && headerToken) return headerToken;
|
|
2834
|
+
if (req.body && typeof req.body === "object" && fieldName in req.body) {
|
|
2835
|
+
const bodyToken = req.body[fieldName];
|
|
2836
|
+
if (typeof bodyToken === "string" && bodyToken) return bodyToken;
|
|
2837
|
+
}
|
|
2838
|
+
if (req.query && fieldName in req.query) {
|
|
2839
|
+
const queryToken = req.query[fieldName];
|
|
2840
|
+
if (typeof queryToken === "string" && queryToken) return queryToken;
|
|
2841
|
+
}
|
|
2842
|
+
return void 0;
|
|
2843
|
+
}
|
|
2844
|
+
function csrfProtection(options = {}) {
|
|
2845
|
+
const cookieName = options.cookieName ?? DEFAULTS.cookieName;
|
|
2846
|
+
const headerName = options.headerName ?? DEFAULTS.headerName;
|
|
2847
|
+
const fieldName = options.fieldName ?? DEFAULTS.fieldName;
|
|
2848
|
+
const tokenLength = options.tokenLength ?? DEFAULTS.tokenLength;
|
|
2849
|
+
const protectedMethods = options.protectedMethods ?? [...DEFAULTS.protectedMethods];
|
|
2850
|
+
const excludePaths = options.excludePaths ?? [];
|
|
2851
|
+
const isProduction = process.env.NODE_ENV === "production";
|
|
2852
|
+
const cookieOpts = {
|
|
2853
|
+
path: options.cookie?.path ?? "/",
|
|
2854
|
+
httpOnly: options.cookie?.httpOnly ?? false,
|
|
2855
|
+
// Must be readable by client JS
|
|
2856
|
+
secure: options.cookie?.secure ?? isProduction,
|
|
2857
|
+
sameSite: options.cookie?.sameSite ?? "Lax",
|
|
2858
|
+
domain: options.cookie?.domain
|
|
2859
|
+
};
|
|
2860
|
+
const defaultOnError = (_req, res, _next) => {
|
|
2861
|
+
res.status(403).json({
|
|
2862
|
+
error: "CSRF token validation failed",
|
|
2863
|
+
message: "Invalid or missing CSRF token. Include the token from the cookie in the X-CSRF-Token header."
|
|
2864
|
+
});
|
|
2865
|
+
};
|
|
2866
|
+
const onError = options.onError ?? defaultOnError;
|
|
2867
|
+
const protectedSet = new Set(protectedMethods.map((m) => m.toUpperCase()));
|
|
2868
|
+
return (req, res, next) => {
|
|
2869
|
+
const method = req.method.toUpperCase();
|
|
2870
|
+
const requestPath = req.path || req.url;
|
|
2871
|
+
if (excludePaths.some((p) => requestPath === p || requestPath.startsWith(p + "/"))) {
|
|
2872
|
+
return next();
|
|
2873
|
+
}
|
|
2874
|
+
req.csrfToken = () => {
|
|
2875
|
+
const existing = getCookieValue(req, cookieName);
|
|
2876
|
+
if (existing) return existing;
|
|
2877
|
+
const token = generateCsrfToken(tokenLength);
|
|
2878
|
+
setCsrfCookie(res, cookieName, token, cookieOpts);
|
|
2879
|
+
return token;
|
|
2880
|
+
};
|
|
2881
|
+
if (!protectedSet.has(method)) {
|
|
2882
|
+
const existing = getCookieValue(req, cookieName);
|
|
2883
|
+
if (!existing) {
|
|
2884
|
+
const token = generateCsrfToken(tokenLength);
|
|
2885
|
+
setCsrfCookie(res, cookieName, token, cookieOpts);
|
|
2886
|
+
}
|
|
2887
|
+
return next();
|
|
2888
|
+
}
|
|
2889
|
+
const cookieToken = getCookieValue(req, cookieName);
|
|
2890
|
+
if (!cookieToken) {
|
|
2891
|
+
return onError(req, res, next);
|
|
2892
|
+
}
|
|
2893
|
+
const requestToken = getRequestToken(req, headerName, fieldName);
|
|
2894
|
+
if (!requestToken) {
|
|
2895
|
+
return onError(req, res, next);
|
|
2896
|
+
}
|
|
2897
|
+
if (!validateCsrfToken(cookieToken, requestToken)) {
|
|
2898
|
+
return onError(req, res, next);
|
|
2899
|
+
}
|
|
2900
|
+
next();
|
|
2901
|
+
};
|
|
2902
|
+
}
|
|
2903
|
+
function getCookieValue(req, name) {
|
|
2904
|
+
if (req.cookies && typeof req.cookies === "object" && name in req.cookies) {
|
|
2905
|
+
return req.cookies[name];
|
|
2906
|
+
}
|
|
2907
|
+
const cookieHeader = req.headers.cookie;
|
|
2908
|
+
if (!cookieHeader) return void 0;
|
|
2909
|
+
const match = cookieHeader.match(new RegExp(`(?:^|;\\s*)${escapeRegex(name)}=([^;]*)`));
|
|
2910
|
+
return match ? decodeURIComponent(match[1]) : void 0;
|
|
2911
|
+
}
|
|
2912
|
+
function setCsrfCookie(res, name, token, opts) {
|
|
2913
|
+
const parts = [`${name}=${token}`];
|
|
2914
|
+
parts.push(`Path=${opts.path}`);
|
|
2915
|
+
if (opts.httpOnly) parts.push("HttpOnly");
|
|
2916
|
+
if (opts.secure) parts.push("Secure");
|
|
2917
|
+
parts.push(`SameSite=${opts.sameSite}`);
|
|
2918
|
+
if (opts.domain) parts.push(`Domain=${opts.domain}`);
|
|
2919
|
+
res.setHeader("Set-Cookie", parts.join("; "));
|
|
2920
|
+
}
|
|
2921
|
+
function escapeRegex(str) {
|
|
2922
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2923
|
+
}
|
|
2924
|
+
var createCsrf = csrfProtection;
|
|
2305
2925
|
|
|
2306
2926
|
// src/utils/ip.ts
|
|
2307
2927
|
var PLATFORM_HEADERS = {
|
|
@@ -2585,6 +3205,7 @@ exports.arcis = arcis;
|
|
|
2585
3205
|
exports.arcisFunction = arcisWithMethods;
|
|
2586
3206
|
exports.botProtection = botProtection;
|
|
2587
3207
|
exports.createCors = createCors;
|
|
3208
|
+
exports.createCsrf = createCsrf;
|
|
2588
3209
|
exports.createErrorHandler = createErrorHandler;
|
|
2589
3210
|
exports.createHeaders = createHeaders;
|
|
2590
3211
|
exports.createRateLimiter = createRateLimiter;
|
|
@@ -2596,20 +3217,31 @@ exports.createSecureCookies = createSecureCookies;
|
|
|
2596
3217
|
exports.createSlidingWindowLimiter = createSlidingWindowLimiter;
|
|
2597
3218
|
exports.createTokenBucketLimiter = createTokenBucketLimiter;
|
|
2598
3219
|
exports.createValidator = createValidator;
|
|
3220
|
+
exports.csrfProtection = csrfProtection;
|
|
2599
3221
|
exports.default = main_default;
|
|
2600
3222
|
exports.detectBot = detectBot;
|
|
2601
3223
|
exports.detectClientIp = detectClientIp;
|
|
2602
3224
|
exports.detectCommandInjection = detectCommandInjection;
|
|
2603
3225
|
exports.detectHeaderInjection = detectHeaderInjection;
|
|
3226
|
+
exports.detectJsonpInjection = detectJsonpInjection;
|
|
2604
3227
|
exports.detectNoSqlInjection = detectNoSqlInjection;
|
|
2605
3228
|
exports.detectPathTraversal = detectPathTraversal;
|
|
3229
|
+
exports.detectPii = detectPii;
|
|
2606
3230
|
exports.detectPrototypePollution = detectPrototypePollution;
|
|
2607
3231
|
exports.detectSql = detectSql;
|
|
3232
|
+
exports.detectSsti = detectSsti;
|
|
2608
3233
|
exports.detectXss = detectXss;
|
|
3234
|
+
exports.detectXxe = detectXxe;
|
|
3235
|
+
exports.encodeForAttribute = encodeForAttribute;
|
|
3236
|
+
exports.encodeForCss = encodeForCss;
|
|
3237
|
+
exports.encodeForHtml = encodeForHtml;
|
|
3238
|
+
exports.encodeForJs = encodeForJs;
|
|
3239
|
+
exports.encodeForUrl = encodeForUrl;
|
|
2609
3240
|
exports.enforceSecureCookie = enforceSecureCookie;
|
|
2610
3241
|
exports.errorHandler = errorHandler;
|
|
2611
3242
|
exports.fingerprint = fingerprint;
|
|
2612
3243
|
exports.formatDuration = formatDuration;
|
|
3244
|
+
exports.generateCsrfToken = generateCsrfToken;
|
|
2613
3245
|
exports.isDangerousExtension = isDangerousExtension;
|
|
2614
3246
|
exports.isDangerousNoSqlKey = isDangerousNoSqlKey;
|
|
2615
3247
|
exports.isDangerousProtoKey = isDangerousProtoKey;
|
|
@@ -2619,20 +3251,28 @@ exports.isUrlSafe = isUrlSafe;
|
|
|
2619
3251
|
exports.isValidEmailSyntax = isValidEmailSyntax;
|
|
2620
3252
|
exports.parseDuration = parseDuration;
|
|
2621
3253
|
exports.rateLimit = rateLimit;
|
|
3254
|
+
exports.redactObjectPii = redactObjectPii;
|
|
3255
|
+
exports.redactPii = redactPii;
|
|
2622
3256
|
exports.safeCors = safeCors;
|
|
2623
3257
|
exports.safeLog = safeLog;
|
|
2624
3258
|
exports.sanitizeCommand = sanitizeCommand;
|
|
2625
3259
|
exports.sanitizeFilename = sanitizeFilename;
|
|
2626
3260
|
exports.sanitizeHeaderValue = sanitizeHeaderValue;
|
|
2627
3261
|
exports.sanitizeHeaders = sanitizeHeaders;
|
|
3262
|
+
exports.sanitizeJsonpCallback = sanitizeJsonpCallback;
|
|
2628
3263
|
exports.sanitizeObject = sanitizeObject;
|
|
2629
3264
|
exports.sanitizePath = sanitizePath;
|
|
2630
3265
|
exports.sanitizeSql = sanitizeSql;
|
|
3266
|
+
exports.sanitizeSsti = sanitizeSsti;
|
|
2631
3267
|
exports.sanitizeString = sanitizeString;
|
|
2632
3268
|
exports.sanitizeXss = sanitizeXss;
|
|
3269
|
+
exports.sanitizeXxe = sanitizeXxe;
|
|
3270
|
+
exports.scanObjectPii = scanObjectPii;
|
|
3271
|
+
exports.scanPii = scanPii;
|
|
2633
3272
|
exports.secureCookieDefaults = secureCookieDefaults;
|
|
2634
3273
|
exports.securityHeaders = securityHeaders;
|
|
2635
3274
|
exports.validate = validate;
|
|
3275
|
+
exports.validateCsrfToken = validateCsrfToken;
|
|
2636
3276
|
exports.validateEmail = validateEmail;
|
|
2637
3277
|
exports.validateFile = validateFile;
|
|
2638
3278
|
exports.validateRedirect = validateRedirect;
|