@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.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { promises } from 'dns';
|
|
2
|
-
import { createHash } from 'crypto';
|
|
2
|
+
import { randomBytes, createHash } from 'crypto';
|
|
3
3
|
|
|
4
4
|
// src/core/constants.ts
|
|
5
5
|
var INPUT = {
|
|
@@ -115,7 +115,11 @@ var SQL_PATTERNS = [
|
|
|
115
115
|
/** Time-based blind: SLEEP() */
|
|
116
116
|
/\bSLEEP\s*\(\s*\d+\s*\)/gi,
|
|
117
117
|
/** Time-based blind: BENCHMARK() */
|
|
118
|
-
/\bBENCHMARK\s*\(/gi
|
|
118
|
+
/\bBENCHMARK\s*\(/gi,
|
|
119
|
+
/** Time-based blind: PostgreSQL pg_sleep() */
|
|
120
|
+
/\bpg_sleep\s*\(/gi,
|
|
121
|
+
/** Time-based blind: MSSQL WAITFOR DELAY */
|
|
122
|
+
/\bWAITFOR\s+DELAY\b/gi
|
|
119
123
|
];
|
|
120
124
|
var PATH_PATTERNS = [
|
|
121
125
|
/** Unix path traversal */
|
|
@@ -133,6 +137,10 @@ var PATH_PATTERNS = [
|
|
|
133
137
|
/\.%2e[\\/]/gi,
|
|
134
138
|
/** Fully URL-encoded: %2e%2e%2f */
|
|
135
139
|
/%2e%2e%2f/gi,
|
|
140
|
+
/** Double URL-encoded forward slash: %252f */
|
|
141
|
+
/%252f/gi,
|
|
142
|
+
/** Dotdotslash bypass: ....// or ....\\ */
|
|
143
|
+
/\.{2,}[/\\]{2,}/g,
|
|
136
144
|
/** Null byte injection in paths */
|
|
137
145
|
/\0/g
|
|
138
146
|
];
|
|
@@ -148,7 +156,9 @@ var COMMAND_PATTERNS = [
|
|
|
148
156
|
*/
|
|
149
157
|
/[;&|`]/g,
|
|
150
158
|
/** Command substitution: $( ... ) — matched as a pair to reduce false positives */
|
|
151
|
-
/\$\(/g
|
|
159
|
+
/\$\(/g,
|
|
160
|
+
/** URL-encoded newline/carriage-return injection (%0a, %0d) */
|
|
161
|
+
/%0[ad]/gi
|
|
152
162
|
];
|
|
153
163
|
var DANGEROUS_PROTO_KEYS = /* @__PURE__ */ new Set([
|
|
154
164
|
"__proto__",
|
|
@@ -182,6 +192,7 @@ var NOSQL_DANGEROUS_KEYS = /* @__PURE__ */ new Set([
|
|
|
182
192
|
"$expr",
|
|
183
193
|
"$mod",
|
|
184
194
|
"$text",
|
|
195
|
+
"$jsonSchema",
|
|
185
196
|
// Array
|
|
186
197
|
"$elemMatch",
|
|
187
198
|
"$all",
|
|
@@ -292,7 +303,12 @@ function createHeaders(options = {}) {
|
|
|
292
303
|
hsts = true,
|
|
293
304
|
referrerPolicy = HEADERS.REFERRER_POLICY,
|
|
294
305
|
permissionsPolicy = HEADERS.PERMISSIONS_POLICY,
|
|
295
|
-
cacheControl = true
|
|
306
|
+
cacheControl = true,
|
|
307
|
+
crossOriginOpenerPolicy = "same-origin",
|
|
308
|
+
crossOriginResourcePolicy = "same-origin",
|
|
309
|
+
crossOriginEmbedderPolicy = "require-corp",
|
|
310
|
+
originAgentCluster = true,
|
|
311
|
+
dnsPrefetchControl = true
|
|
296
312
|
} = options;
|
|
297
313
|
return (req, res, next) => {
|
|
298
314
|
if (contentSecurityPolicy) {
|
|
@@ -300,7 +316,7 @@ function createHeaders(options = {}) {
|
|
|
300
316
|
res.setHeader("Content-Security-Policy", csp);
|
|
301
317
|
}
|
|
302
318
|
if (xssFilter) {
|
|
303
|
-
res.setHeader("X-XSS-Protection", "
|
|
319
|
+
res.setHeader("X-XSS-Protection", "0");
|
|
304
320
|
}
|
|
305
321
|
if (noSniff) {
|
|
306
322
|
res.setHeader("X-Content-Type-Options", HEADERS.CONTENT_TYPE_OPTIONS);
|
|
@@ -327,6 +343,21 @@ function createHeaders(options = {}) {
|
|
|
327
343
|
if (permissionsPolicy) {
|
|
328
344
|
res.setHeader("Permissions-Policy", permissionsPolicy);
|
|
329
345
|
}
|
|
346
|
+
if (crossOriginOpenerPolicy) {
|
|
347
|
+
res.setHeader("Cross-Origin-Opener-Policy", crossOriginOpenerPolicy);
|
|
348
|
+
}
|
|
349
|
+
if (crossOriginResourcePolicy) {
|
|
350
|
+
res.setHeader("Cross-Origin-Resource-Policy", crossOriginResourcePolicy);
|
|
351
|
+
}
|
|
352
|
+
if (crossOriginEmbedderPolicy) {
|
|
353
|
+
res.setHeader("Cross-Origin-Embedder-Policy", crossOriginEmbedderPolicy);
|
|
354
|
+
}
|
|
355
|
+
if (originAgentCluster) {
|
|
356
|
+
res.setHeader("Origin-Agent-Cluster", "?1");
|
|
357
|
+
}
|
|
358
|
+
if (dnsPrefetchControl) {
|
|
359
|
+
res.setHeader("X-DNS-Prefetch-Control", "off");
|
|
360
|
+
}
|
|
330
361
|
res.setHeader("X-Permitted-Cross-Domain-Policies", "none");
|
|
331
362
|
if (cacheControl) {
|
|
332
363
|
const cacheControlValue = typeof cacheControl === "string" ? cacheControl : HEADERS.CACHE_CONTROL;
|
|
@@ -782,7 +813,8 @@ function sanitizeObject(obj, options = {}) {
|
|
|
782
813
|
if (typeof obj === "string") return sanitizeString(obj, options);
|
|
783
814
|
if (typeof obj !== "object") return obj;
|
|
784
815
|
if (Array.isArray(obj)) return obj.map((item) => sanitizeObject(item, options));
|
|
785
|
-
|
|
816
|
+
const result = sanitizeObjectDepth(obj, options, 0);
|
|
817
|
+
return options.freeze ? Object.freeze(result) : result;
|
|
786
818
|
}
|
|
787
819
|
function sanitizeObjectDepth(obj, options, depth) {
|
|
788
820
|
if (depth >= INPUT.MAX_RECURSION_DEPTH) return obj;
|
|
@@ -879,6 +911,179 @@ function detectPrototypePollution(obj, maxDepth = 10) {
|
|
|
879
911
|
return false;
|
|
880
912
|
}
|
|
881
913
|
|
|
914
|
+
// src/sanitizers/ssti.ts
|
|
915
|
+
var SSTI_DETECT_PATTERNS = [
|
|
916
|
+
/** Jinja2 / Twig / Nunjucks: {{ ... }} */
|
|
917
|
+
/\{\{.*?\}\}/g,
|
|
918
|
+
/** Freemarker / Thymeleaf / Spring EL: ${ ... } */
|
|
919
|
+
/\$\{.*?\}/g,
|
|
920
|
+
/** ERB / EJS: <%= ... %> or <% ... %> */
|
|
921
|
+
/<%[=\-]?.*?%>/gs,
|
|
922
|
+
/** Pug / Jade / Slim: #{ ... } */
|
|
923
|
+
/#\{.*?\}/g,
|
|
924
|
+
/** Python dunder sandbox escape */
|
|
925
|
+
/__(?:class|mro|subclasses|globals|builtins|import)__/gi,
|
|
926
|
+
/** Jinja2 config leak: {{config.X}} or {{config['X']}} */
|
|
927
|
+
/\{\{\s*config[.\[]/gi,
|
|
928
|
+
/** Jinja2 built-in objects */
|
|
929
|
+
/\{\{\s*(?:self|request|lipsum|cycler|joiner|namespace|range)\b/gi
|
|
930
|
+
];
|
|
931
|
+
var SSTI_REMOVE_PATTERNS = [
|
|
932
|
+
/\{\{.*?\}\}/g,
|
|
933
|
+
/\$\{.*?\}/g,
|
|
934
|
+
/<%[=\-]?.*?%>/gs,
|
|
935
|
+
/#\{.*?\}/g,
|
|
936
|
+
/__(?:class|mro|subclasses|globals|builtins|import)__/gi
|
|
937
|
+
];
|
|
938
|
+
function sanitizeSsti(input, collectThreats = false) {
|
|
939
|
+
if (typeof input !== "string") {
|
|
940
|
+
return collectThreats ? { value: String(input), wasSanitized: false, threats: [] } : String(input);
|
|
941
|
+
}
|
|
942
|
+
const threats = [];
|
|
943
|
+
let value = input;
|
|
944
|
+
let wasSanitized = false;
|
|
945
|
+
for (const pattern of SSTI_REMOVE_PATTERNS) {
|
|
946
|
+
pattern.lastIndex = 0;
|
|
947
|
+
if (pattern.test(value)) {
|
|
948
|
+
pattern.lastIndex = 0;
|
|
949
|
+
if (collectThreats) {
|
|
950
|
+
const matches = value.match(pattern);
|
|
951
|
+
if (matches) {
|
|
952
|
+
for (const match of matches) {
|
|
953
|
+
threats.push({
|
|
954
|
+
type: "ssti",
|
|
955
|
+
pattern: pattern.source,
|
|
956
|
+
original: match
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
value = value.replace(pattern, "");
|
|
962
|
+
wasSanitized = true;
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
if (collectThreats) {
|
|
966
|
+
return { value, wasSanitized, threats };
|
|
967
|
+
}
|
|
968
|
+
return value;
|
|
969
|
+
}
|
|
970
|
+
function detectSsti(input) {
|
|
971
|
+
if (typeof input !== "string") return false;
|
|
972
|
+
for (const pattern of SSTI_DETECT_PATTERNS) {
|
|
973
|
+
pattern.lastIndex = 0;
|
|
974
|
+
if (pattern.test(input)) {
|
|
975
|
+
return true;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
return false;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
// src/sanitizers/xxe.ts
|
|
982
|
+
var XXE_DETECT_PATTERNS = [
|
|
983
|
+
/** DOCTYPE declaration */
|
|
984
|
+
/<!DOCTYPE\b/gi,
|
|
985
|
+
/** ENTITY declaration */
|
|
986
|
+
/<!ENTITY\b/gi,
|
|
987
|
+
/** SYSTEM keyword with URI */
|
|
988
|
+
/\bSYSTEM\s+["']/gi,
|
|
989
|
+
/** PUBLIC keyword with URI */
|
|
990
|
+
/\bPUBLIC\s+["']/gi,
|
|
991
|
+
/** Parameter entity reference (%entity;) */
|
|
992
|
+
/%\s*\w+\s*;/g,
|
|
993
|
+
/** CDATA section (often used to smuggle payloads) */
|
|
994
|
+
/<!\[CDATA\[/gi
|
|
995
|
+
];
|
|
996
|
+
var XXE_REMOVE_PATTERNS = [
|
|
997
|
+
/** Full DOCTYPE block with optional internal subset: <!DOCTYPE ... [...]> */
|
|
998
|
+
/<!DOCTYPE\s[^[>]*(?:\[[^\]]*\]\s*)?>|<!DOCTYPE\s[^>]*>/gi,
|
|
999
|
+
/** Full ENTITY declaration: <!ENTITY ... > */
|
|
1000
|
+
/<!ENTITY[^>]*>/gi,
|
|
1001
|
+
/** CDATA sections: <![CDATA[ ... ]]> */
|
|
1002
|
+
/<!\[CDATA\[[\s\S]*?\]\]>/gi
|
|
1003
|
+
];
|
|
1004
|
+
function sanitizeXxe(input, collectThreats = false) {
|
|
1005
|
+
if (typeof input !== "string") {
|
|
1006
|
+
return collectThreats ? { value: String(input), wasSanitized: false, threats: [] } : String(input);
|
|
1007
|
+
}
|
|
1008
|
+
const threats = [];
|
|
1009
|
+
let value = input;
|
|
1010
|
+
let wasSanitized = false;
|
|
1011
|
+
for (const pattern of XXE_REMOVE_PATTERNS) {
|
|
1012
|
+
pattern.lastIndex = 0;
|
|
1013
|
+
if (pattern.test(value)) {
|
|
1014
|
+
pattern.lastIndex = 0;
|
|
1015
|
+
if (collectThreats) {
|
|
1016
|
+
const matches = value.match(pattern);
|
|
1017
|
+
if (matches) {
|
|
1018
|
+
for (const match of matches) {
|
|
1019
|
+
threats.push({
|
|
1020
|
+
type: "xxe",
|
|
1021
|
+
pattern: pattern.source,
|
|
1022
|
+
original: match
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
value = value.replace(pattern, "");
|
|
1028
|
+
wasSanitized = true;
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
if (collectThreats) {
|
|
1032
|
+
return { value, wasSanitized, threats };
|
|
1033
|
+
}
|
|
1034
|
+
return value;
|
|
1035
|
+
}
|
|
1036
|
+
function detectXxe(input) {
|
|
1037
|
+
if (typeof input !== "string") return false;
|
|
1038
|
+
for (const pattern of XXE_DETECT_PATTERNS) {
|
|
1039
|
+
pattern.lastIndex = 0;
|
|
1040
|
+
if (pattern.test(input)) {
|
|
1041
|
+
return true;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
return false;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
// src/sanitizers/jsonp.ts
|
|
1048
|
+
var SAFE_CALLBACK_PATTERN = /^[a-zA-Z_$][a-zA-Z0-9_$.[\]]*$/;
|
|
1049
|
+
var DANGEROUS_CALLBACK_PATTERNS = [
|
|
1050
|
+
/\.\./,
|
|
1051
|
+
// prototype chain traversal
|
|
1052
|
+
/\[\s*\]/
|
|
1053
|
+
// empty bracket access
|
|
1054
|
+
];
|
|
1055
|
+
function sanitizeJsonpCallback(callback, maxLength = 128) {
|
|
1056
|
+
if (typeof callback !== "string" || callback.length === 0) {
|
|
1057
|
+
return null;
|
|
1058
|
+
}
|
|
1059
|
+
if (callback.length > maxLength) {
|
|
1060
|
+
return null;
|
|
1061
|
+
}
|
|
1062
|
+
if (!SAFE_CALLBACK_PATTERN.test(callback)) {
|
|
1063
|
+
return null;
|
|
1064
|
+
}
|
|
1065
|
+
for (const pattern of DANGEROUS_CALLBACK_PATTERNS) {
|
|
1066
|
+
if (pattern.test(callback)) {
|
|
1067
|
+
return null;
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
return callback;
|
|
1071
|
+
}
|
|
1072
|
+
function detectJsonpInjection(callback) {
|
|
1073
|
+
if (typeof callback !== "string" || callback.length === 0) {
|
|
1074
|
+
return false;
|
|
1075
|
+
}
|
|
1076
|
+
if (!SAFE_CALLBACK_PATTERN.test(callback)) {
|
|
1077
|
+
return true;
|
|
1078
|
+
}
|
|
1079
|
+
for (const pattern of DANGEROUS_CALLBACK_PATTERNS) {
|
|
1080
|
+
if (pattern.test(callback)) {
|
|
1081
|
+
return true;
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
return false;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
882
1087
|
// src/sanitizers/headers.ts
|
|
883
1088
|
var HEADER_INJECTION_PATTERN = /\r\n|\r|\n|\0/g;
|
|
884
1089
|
function sanitizeHeaderValue(input, collectThreats = false) {
|
|
@@ -928,6 +1133,205 @@ function detectHeaderInjection(input) {
|
|
|
928
1133
|
return HEADER_INJECTION_PATTERN.test(input);
|
|
929
1134
|
}
|
|
930
1135
|
|
|
1136
|
+
// src/sanitizers/pii.ts
|
|
1137
|
+
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;
|
|
1138
|
+
var PHONE_RE = /(?:\+?1[-.\s]?)?\(?[2-9]\d{2}\)?[-.\s]?\d{3}[-.\s]?\d{4}/g;
|
|
1139
|
+
var CREDIT_CARD_RE = /\b(?:\d[ -]*?){13,19}\b/g;
|
|
1140
|
+
var SSN_RE = /\b\d{3}[-\s]\d{2}[-\s]\d{4}\b/g;
|
|
1141
|
+
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;
|
|
1142
|
+
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;
|
|
1143
|
+
var PATTERN_MAP = {
|
|
1144
|
+
email: [EMAIL_RE],
|
|
1145
|
+
phone: [PHONE_RE],
|
|
1146
|
+
credit_card: [CREDIT_CARD_RE],
|
|
1147
|
+
ssn: [SSN_RE],
|
|
1148
|
+
ip_address: [IPV4_RE, IPV6_RE]
|
|
1149
|
+
};
|
|
1150
|
+
var ALL_TYPES = ["email", "phone", "credit_card", "ssn", "ip_address"];
|
|
1151
|
+
var TYPE_LABELS = {
|
|
1152
|
+
email: "[EMAIL]",
|
|
1153
|
+
phone: "[PHONE]",
|
|
1154
|
+
credit_card: "[CREDIT_CARD]",
|
|
1155
|
+
ssn: "[SSN]",
|
|
1156
|
+
ip_address: "[IP_ADDRESS]"
|
|
1157
|
+
};
|
|
1158
|
+
function luhnCheck(value) {
|
|
1159
|
+
const digits = value.replace(/[\s-]/g, "");
|
|
1160
|
+
if (!/^\d{13,19}$/.test(digits)) return false;
|
|
1161
|
+
let sum = 0;
|
|
1162
|
+
let alternate = false;
|
|
1163
|
+
for (let i = digits.length - 1; i >= 0; i--) {
|
|
1164
|
+
let n = parseInt(digits[i], 10);
|
|
1165
|
+
if (alternate) {
|
|
1166
|
+
n *= 2;
|
|
1167
|
+
if (n > 9) n -= 9;
|
|
1168
|
+
}
|
|
1169
|
+
sum += n;
|
|
1170
|
+
alternate = !alternate;
|
|
1171
|
+
}
|
|
1172
|
+
return sum % 10 === 0;
|
|
1173
|
+
}
|
|
1174
|
+
function scanPii(input, options = {}) {
|
|
1175
|
+
if (!input || typeof input !== "string") return [];
|
|
1176
|
+
const types = options.types ?? ALL_TYPES;
|
|
1177
|
+
const matches = [];
|
|
1178
|
+
for (const type of types) {
|
|
1179
|
+
const patterns = PATTERN_MAP[type];
|
|
1180
|
+
if (!patterns) continue;
|
|
1181
|
+
for (const pattern of patterns) {
|
|
1182
|
+
const re = new RegExp(pattern.source, pattern.flags);
|
|
1183
|
+
let match;
|
|
1184
|
+
while ((match = re.exec(input)) !== null) {
|
|
1185
|
+
const value = match[0];
|
|
1186
|
+
if (type === "credit_card" && !luhnCheck(value)) continue;
|
|
1187
|
+
if (type === "ssn") {
|
|
1188
|
+
const area = parseInt(value.substring(0, 3), 10);
|
|
1189
|
+
if (area === 0 || area === 666 || area >= 900) continue;
|
|
1190
|
+
}
|
|
1191
|
+
matches.push({
|
|
1192
|
+
type,
|
|
1193
|
+
value,
|
|
1194
|
+
start: match.index,
|
|
1195
|
+
end: match.index + value.length
|
|
1196
|
+
});
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
matches.sort((a, b) => a.start - b.start);
|
|
1201
|
+
return matches;
|
|
1202
|
+
}
|
|
1203
|
+
function detectPii(input, options = {}) {
|
|
1204
|
+
return scanPii(input, options).length > 0;
|
|
1205
|
+
}
|
|
1206
|
+
function redactPii(input, options = {}) {
|
|
1207
|
+
if (!input || typeof input !== "string") return input;
|
|
1208
|
+
const matches = scanPii(input, options);
|
|
1209
|
+
if (matches.length === 0) return input;
|
|
1210
|
+
const replacement = options.replacement ?? "[REDACTED]";
|
|
1211
|
+
let result = input;
|
|
1212
|
+
for (let i = matches.length - 1; i >= 0; i--) {
|
|
1213
|
+
const m = matches[i];
|
|
1214
|
+
const label = options.typeLabels ? TYPE_LABELS[m.type] : replacement;
|
|
1215
|
+
result = result.substring(0, m.start) + label + result.substring(m.end);
|
|
1216
|
+
}
|
|
1217
|
+
return result;
|
|
1218
|
+
}
|
|
1219
|
+
function scanObjectPii(obj, options = {}, path = "") {
|
|
1220
|
+
const results = [];
|
|
1221
|
+
if (!obj || typeof obj !== "object") return results;
|
|
1222
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
1223
|
+
const fieldPath = path ? `${path}.${key}` : key;
|
|
1224
|
+
if (typeof value === "string") {
|
|
1225
|
+
const matches = scanPii(value, options);
|
|
1226
|
+
for (const m of matches) {
|
|
1227
|
+
results.push({ ...m, field: fieldPath });
|
|
1228
|
+
}
|
|
1229
|
+
} else if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
1230
|
+
results.push(...scanObjectPii(value, options, fieldPath));
|
|
1231
|
+
} else if (Array.isArray(value)) {
|
|
1232
|
+
for (let i = 0; i < value.length; i++) {
|
|
1233
|
+
const item = value[i];
|
|
1234
|
+
if (typeof item === "string") {
|
|
1235
|
+
const matches = scanPii(item, options);
|
|
1236
|
+
for (const m of matches) {
|
|
1237
|
+
results.push({ ...m, field: `${fieldPath}[${i}]` });
|
|
1238
|
+
}
|
|
1239
|
+
} else if (item && typeof item === "object") {
|
|
1240
|
+
results.push(...scanObjectPii(item, options, `${fieldPath}[${i}]`));
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
return results;
|
|
1246
|
+
}
|
|
1247
|
+
function redactObjectPii(obj, options = {}) {
|
|
1248
|
+
if (!obj || typeof obj !== "object") return obj;
|
|
1249
|
+
const result = {};
|
|
1250
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
1251
|
+
if (typeof value === "string") {
|
|
1252
|
+
result[key] = redactPii(value, options);
|
|
1253
|
+
} else if (Array.isArray(value)) {
|
|
1254
|
+
result[key] = value.map((item) => {
|
|
1255
|
+
if (typeof item === "string") return redactPii(item, options);
|
|
1256
|
+
if (item && typeof item === "object") return redactObjectPii(item, options);
|
|
1257
|
+
return item;
|
|
1258
|
+
});
|
|
1259
|
+
} else if (value && typeof value === "object") {
|
|
1260
|
+
result[key] = redactObjectPii(value, options);
|
|
1261
|
+
} else {
|
|
1262
|
+
result[key] = value;
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
return result;
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
// src/sanitizers/encode.ts
|
|
1269
|
+
var HTML_ENTITIES = {
|
|
1270
|
+
"&": "&",
|
|
1271
|
+
"<": "<",
|
|
1272
|
+
">": ">",
|
|
1273
|
+
'"': """,
|
|
1274
|
+
"'": "'"
|
|
1275
|
+
};
|
|
1276
|
+
var HTML_ENCODE_RE = /[&<>"']/g;
|
|
1277
|
+
function encodeForHtml(value) {
|
|
1278
|
+
if (!value) return "";
|
|
1279
|
+
return value.replace(HTML_ENCODE_RE, (ch) => HTML_ENTITIES[ch]);
|
|
1280
|
+
}
|
|
1281
|
+
function encodeForAttribute(value) {
|
|
1282
|
+
if (!value) return "";
|
|
1283
|
+
let result = "";
|
|
1284
|
+
for (let i = 0; i < value.length; i++) {
|
|
1285
|
+
const ch = value.charCodeAt(i);
|
|
1286
|
+
if (ch >= 48 && ch <= 57 || // 0-9
|
|
1287
|
+
ch >= 65 && ch <= 90 || // A-Z
|
|
1288
|
+
ch >= 97 && ch <= 122) {
|
|
1289
|
+
result += value[i];
|
|
1290
|
+
} else {
|
|
1291
|
+
result += `&#x${ch.toString(16).toUpperCase()};`;
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
return result;
|
|
1295
|
+
}
|
|
1296
|
+
function encodeForJs(value) {
|
|
1297
|
+
if (!value) return "";
|
|
1298
|
+
let result = "";
|
|
1299
|
+
for (let i = 0; i < value.length; i++) {
|
|
1300
|
+
const ch = value.charCodeAt(i);
|
|
1301
|
+
if (ch >= 48 && ch <= 57 || // 0-9
|
|
1302
|
+
ch >= 65 && ch <= 90 || // A-Z
|
|
1303
|
+
ch >= 97 && ch <= 122) {
|
|
1304
|
+
result += value[i];
|
|
1305
|
+
} else if (ch < 256) {
|
|
1306
|
+
result += `\\x${ch.toString(16).toUpperCase().padStart(2, "0")}`;
|
|
1307
|
+
} else {
|
|
1308
|
+
result += `\\u${ch.toString(16).toUpperCase().padStart(4, "0")}`;
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
return result;
|
|
1312
|
+
}
|
|
1313
|
+
function encodeForUrl(value) {
|
|
1314
|
+
if (!value) return "";
|
|
1315
|
+
return encodeURIComponent(value).replace(/[!'()*]/g, (ch) => {
|
|
1316
|
+
return `%${ch.charCodeAt(0).toString(16).toUpperCase()}`;
|
|
1317
|
+
});
|
|
1318
|
+
}
|
|
1319
|
+
function encodeForCss(value) {
|
|
1320
|
+
if (!value) return "";
|
|
1321
|
+
let result = "";
|
|
1322
|
+
for (let i = 0; i < value.length; i++) {
|
|
1323
|
+
const ch = value.charCodeAt(i);
|
|
1324
|
+
if (ch >= 48 && ch <= 57 || // 0-9
|
|
1325
|
+
ch >= 65 && ch <= 90 || // A-Z
|
|
1326
|
+
ch >= 97 && ch <= 122) {
|
|
1327
|
+
result += value[i];
|
|
1328
|
+
} else {
|
|
1329
|
+
result += `\\${ch.toString(16).toUpperCase()} `;
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
return result;
|
|
1333
|
+
}
|
|
1334
|
+
|
|
931
1335
|
// src/validation/schema.ts
|
|
932
1336
|
function validate(schema, source = "body") {
|
|
933
1337
|
return (req, res, next) => {
|
|
@@ -1308,6 +1712,18 @@ function validateUrl(url, options = {}) {
|
|
|
1308
1712
|
return { safe: false, reason: "loopback address" };
|
|
1309
1713
|
}
|
|
1310
1714
|
}
|
|
1715
|
+
if (!allowLocalhost || !allowPrivate) {
|
|
1716
|
+
const decimalCheck = checkDecimalIp(hostname, allowLocalhost, allowPrivate);
|
|
1717
|
+
if (decimalCheck) {
|
|
1718
|
+
return { safe: false, reason: decimalCheck };
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
if (!allowLocalhost || !allowPrivate) {
|
|
1722
|
+
const octalCheck = checkOctalIp(hostname, allowLocalhost, allowPrivate);
|
|
1723
|
+
if (octalCheck) {
|
|
1724
|
+
return { safe: false, reason: octalCheck };
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1311
1727
|
if (!allowPrivate) {
|
|
1312
1728
|
const privateCheck = checkPrivateIp(hostname);
|
|
1313
1729
|
if (privateCheck) {
|
|
@@ -1339,13 +1755,93 @@ function checkPrivateIp(hostname) {
|
|
|
1339
1755
|
if (/^0\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(hostname)) {
|
|
1340
1756
|
return "current network address (0.0.0.0/8)";
|
|
1341
1757
|
}
|
|
1342
|
-
if (hostname === "metadata.google.internal" || hostname === "metadata.internal") {
|
|
1758
|
+
if (hostname === "metadata.google.internal" || hostname === "metadata.internal" || hostname === "metadata.azure.internal") {
|
|
1343
1759
|
return "cloud metadata endpoint";
|
|
1344
1760
|
}
|
|
1345
1761
|
const ipv6 = hostname.replace(/^\[|\]$/g, "");
|
|
1346
1762
|
if (ipv6 === "::1" || ipv6 === "::" || ipv6.startsWith("fc") || ipv6.startsWith("fd") || ipv6.startsWith("fe80")) {
|
|
1347
1763
|
return "private IPv6 address";
|
|
1348
1764
|
}
|
|
1765
|
+
const mappedDotted = ipv6.match(/^::ffff:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/i);
|
|
1766
|
+
if (mappedDotted) {
|
|
1767
|
+
const mappedIp = mappedDotted[1];
|
|
1768
|
+
if (/^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(mappedIp)) {
|
|
1769
|
+
return "IPv6-mapped loopback address";
|
|
1770
|
+
}
|
|
1771
|
+
const mappedCheck = checkPrivateIp(mappedIp);
|
|
1772
|
+
if (mappedCheck) {
|
|
1773
|
+
return `IPv6-mapped ${mappedCheck}`;
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
const mappedHex = ipv6.match(/^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i);
|
|
1777
|
+
if (mappedHex) {
|
|
1778
|
+
const hi = parseInt(mappedHex[1], 16);
|
|
1779
|
+
const lo = parseInt(mappedHex[2], 16);
|
|
1780
|
+
const a = hi >> 8 & 255;
|
|
1781
|
+
const b = hi & 255;
|
|
1782
|
+
const c = lo >> 8 & 255;
|
|
1783
|
+
const d = lo & 255;
|
|
1784
|
+
const dotted = `${a}.${b}.${c}.${d}`;
|
|
1785
|
+
if (a === 127) {
|
|
1786
|
+
return "IPv6-mapped loopback address";
|
|
1787
|
+
}
|
|
1788
|
+
const hexCheck = checkPrivateIp(dotted);
|
|
1789
|
+
if (hexCheck) {
|
|
1790
|
+
return `IPv6-mapped ${hexCheck}`;
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
return null;
|
|
1794
|
+
}
|
|
1795
|
+
function checkDecimalIp(hostname, allowLocalhost, allowPrivate) {
|
|
1796
|
+
if (!/^\d+$/.test(hostname)) return null;
|
|
1797
|
+
const num = parseInt(hostname, 10);
|
|
1798
|
+
if (isNaN(num) || num < 0 || num > 4294967295) return null;
|
|
1799
|
+
const a = num >>> 24 & 255;
|
|
1800
|
+
const b = num >>> 16 & 255;
|
|
1801
|
+
const c = num >>> 8 & 255;
|
|
1802
|
+
const d = num & 255;
|
|
1803
|
+
const dotted = `${a}.${b}.${c}.${d}`;
|
|
1804
|
+
if (!allowLocalhost && a === 127) {
|
|
1805
|
+
return `loopback address (decimal IP: ${dotted})`;
|
|
1806
|
+
}
|
|
1807
|
+
if (!allowPrivate) {
|
|
1808
|
+
const privateCheck = checkPrivateIp(dotted);
|
|
1809
|
+
if (privateCheck) {
|
|
1810
|
+
return `${privateCheck} (decimal IP: ${dotted})`;
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
return null;
|
|
1814
|
+
}
|
|
1815
|
+
function checkOctalIp(hostname, allowLocalhost, allowPrivate) {
|
|
1816
|
+
const parts = hostname.split(".");
|
|
1817
|
+
if (parts.length !== 4) return null;
|
|
1818
|
+
const hasAlternateNotation = parts.some((p) => /^0[0-7]+$/.test(p) || /^0x[0-9a-fA-F]+$/i.test(p));
|
|
1819
|
+
if (!hasAlternateNotation) return null;
|
|
1820
|
+
const octets = [];
|
|
1821
|
+
for (const part of parts) {
|
|
1822
|
+
let val;
|
|
1823
|
+
if (/^0x[0-9a-fA-F]+$/i.test(part)) {
|
|
1824
|
+
val = parseInt(part, 16);
|
|
1825
|
+
} else if (/^0[0-7]*$/.test(part)) {
|
|
1826
|
+
val = parseInt(part, 8);
|
|
1827
|
+
} else if (/^\d+$/.test(part)) {
|
|
1828
|
+
val = parseInt(part, 10);
|
|
1829
|
+
} else {
|
|
1830
|
+
return null;
|
|
1831
|
+
}
|
|
1832
|
+
if (val < 0 || val > 255) return null;
|
|
1833
|
+
octets.push(val);
|
|
1834
|
+
}
|
|
1835
|
+
const dotted = octets.join(".");
|
|
1836
|
+
if (!allowLocalhost && octets[0] === 127) {
|
|
1837
|
+
return `loopback address (octal IP: ${dotted})`;
|
|
1838
|
+
}
|
|
1839
|
+
if (!allowPrivate) {
|
|
1840
|
+
const privateCheck = checkPrivateIp(dotted);
|
|
1841
|
+
if (privateCheck) {
|
|
1842
|
+
return `${privateCheck} (octal IP: ${dotted})`;
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1349
1845
|
return null;
|
|
1350
1846
|
}
|
|
1351
1847
|
|
|
@@ -1661,12 +2157,21 @@ function isValidEmailSyntax(email) {
|
|
|
1661
2157
|
}
|
|
1662
2158
|
|
|
1663
2159
|
// src/logging/redactor.ts
|
|
2160
|
+
var LOG_LEVELS = {
|
|
2161
|
+
debug: 0,
|
|
2162
|
+
info: 1,
|
|
2163
|
+
warn: 2,
|
|
2164
|
+
error: 3,
|
|
2165
|
+
silent: 4
|
|
2166
|
+
};
|
|
1664
2167
|
function createSafeLogger(options = {}) {
|
|
1665
2168
|
const {
|
|
1666
2169
|
redactKeys = [],
|
|
1667
2170
|
maxLength = REDACTION.DEFAULT_MAX_LENGTH,
|
|
1668
|
-
redactPatterns = []
|
|
2171
|
+
redactPatterns = [],
|
|
2172
|
+
level: minLevel = "debug"
|
|
1669
2173
|
} = options;
|
|
2174
|
+
const minLevelNum = LOG_LEVELS[minLevel] ?? 0;
|
|
1670
2175
|
const allRedactKeys = /* @__PURE__ */ new Set([
|
|
1671
2176
|
...Array.from(REDACTION.SENSITIVE_KEYS),
|
|
1672
2177
|
...redactKeys.map((k) => k.toLowerCase())
|
|
@@ -1692,6 +2197,8 @@ function createSafeLogger(options = {}) {
|
|
|
1692
2197
|
return result;
|
|
1693
2198
|
}
|
|
1694
2199
|
function log(level, message, data) {
|
|
2200
|
+
const levelNum = LOG_LEVELS[level] ?? 0;
|
|
2201
|
+
if (levelNum < minLevelNum) return;
|
|
1695
2202
|
const entry = {
|
|
1696
2203
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1697
2204
|
level,
|
|
@@ -2298,6 +2805,119 @@ function botProtection(options = {}) {
|
|
|
2298
2805
|
next();
|
|
2299
2806
|
};
|
|
2300
2807
|
}
|
|
2808
|
+
var DEFAULTS = {
|
|
2809
|
+
cookieName: "_csrf",
|
|
2810
|
+
headerName: "x-csrf-token",
|
|
2811
|
+
fieldName: "_csrf",
|
|
2812
|
+
tokenLength: 32,
|
|
2813
|
+
protectedMethods: ["POST", "PUT", "PATCH", "DELETE"]
|
|
2814
|
+
};
|
|
2815
|
+
function generateCsrfToken(length = 32) {
|
|
2816
|
+
return randomBytes(length).toString("hex");
|
|
2817
|
+
}
|
|
2818
|
+
function validateCsrfToken(cookieToken, requestToken) {
|
|
2819
|
+
if (!cookieToken || !requestToken) return false;
|
|
2820
|
+
if (cookieToken.length !== requestToken.length) return false;
|
|
2821
|
+
let result = 0;
|
|
2822
|
+
for (let i = 0; i < cookieToken.length; i++) {
|
|
2823
|
+
result |= cookieToken.charCodeAt(i) ^ requestToken.charCodeAt(i);
|
|
2824
|
+
}
|
|
2825
|
+
return result === 0;
|
|
2826
|
+
}
|
|
2827
|
+
function getRequestToken(req, headerName, fieldName) {
|
|
2828
|
+
const headerToken = req.headers[headerName.toLowerCase()];
|
|
2829
|
+
if (typeof headerToken === "string" && headerToken) return headerToken;
|
|
2830
|
+
if (req.body && typeof req.body === "object" && fieldName in req.body) {
|
|
2831
|
+
const bodyToken = req.body[fieldName];
|
|
2832
|
+
if (typeof bodyToken === "string" && bodyToken) return bodyToken;
|
|
2833
|
+
}
|
|
2834
|
+
if (req.query && fieldName in req.query) {
|
|
2835
|
+
const queryToken = req.query[fieldName];
|
|
2836
|
+
if (typeof queryToken === "string" && queryToken) return queryToken;
|
|
2837
|
+
}
|
|
2838
|
+
return void 0;
|
|
2839
|
+
}
|
|
2840
|
+
function csrfProtection(options = {}) {
|
|
2841
|
+
const cookieName = options.cookieName ?? DEFAULTS.cookieName;
|
|
2842
|
+
const headerName = options.headerName ?? DEFAULTS.headerName;
|
|
2843
|
+
const fieldName = options.fieldName ?? DEFAULTS.fieldName;
|
|
2844
|
+
const tokenLength = options.tokenLength ?? DEFAULTS.tokenLength;
|
|
2845
|
+
const protectedMethods = options.protectedMethods ?? [...DEFAULTS.protectedMethods];
|
|
2846
|
+
const excludePaths = options.excludePaths ?? [];
|
|
2847
|
+
const isProduction = process.env.NODE_ENV === "production";
|
|
2848
|
+
const cookieOpts = {
|
|
2849
|
+
path: options.cookie?.path ?? "/",
|
|
2850
|
+
httpOnly: options.cookie?.httpOnly ?? false,
|
|
2851
|
+
// Must be readable by client JS
|
|
2852
|
+
secure: options.cookie?.secure ?? isProduction,
|
|
2853
|
+
sameSite: options.cookie?.sameSite ?? "Lax",
|
|
2854
|
+
domain: options.cookie?.domain
|
|
2855
|
+
};
|
|
2856
|
+
const defaultOnError = (_req, res, _next) => {
|
|
2857
|
+
res.status(403).json({
|
|
2858
|
+
error: "CSRF token validation failed",
|
|
2859
|
+
message: "Invalid or missing CSRF token. Include the token from the cookie in the X-CSRF-Token header."
|
|
2860
|
+
});
|
|
2861
|
+
};
|
|
2862
|
+
const onError = options.onError ?? defaultOnError;
|
|
2863
|
+
const protectedSet = new Set(protectedMethods.map((m) => m.toUpperCase()));
|
|
2864
|
+
return (req, res, next) => {
|
|
2865
|
+
const method = req.method.toUpperCase();
|
|
2866
|
+
const requestPath = req.path || req.url;
|
|
2867
|
+
if (excludePaths.some((p) => requestPath === p || requestPath.startsWith(p + "/"))) {
|
|
2868
|
+
return next();
|
|
2869
|
+
}
|
|
2870
|
+
req.csrfToken = () => {
|
|
2871
|
+
const existing = getCookieValue(req, cookieName);
|
|
2872
|
+
if (existing) return existing;
|
|
2873
|
+
const token = generateCsrfToken(tokenLength);
|
|
2874
|
+
setCsrfCookie(res, cookieName, token, cookieOpts);
|
|
2875
|
+
return token;
|
|
2876
|
+
};
|
|
2877
|
+
if (!protectedSet.has(method)) {
|
|
2878
|
+
const existing = getCookieValue(req, cookieName);
|
|
2879
|
+
if (!existing) {
|
|
2880
|
+
const token = generateCsrfToken(tokenLength);
|
|
2881
|
+
setCsrfCookie(res, cookieName, token, cookieOpts);
|
|
2882
|
+
}
|
|
2883
|
+
return next();
|
|
2884
|
+
}
|
|
2885
|
+
const cookieToken = getCookieValue(req, cookieName);
|
|
2886
|
+
if (!cookieToken) {
|
|
2887
|
+
return onError(req, res, next);
|
|
2888
|
+
}
|
|
2889
|
+
const requestToken = getRequestToken(req, headerName, fieldName);
|
|
2890
|
+
if (!requestToken) {
|
|
2891
|
+
return onError(req, res, next);
|
|
2892
|
+
}
|
|
2893
|
+
if (!validateCsrfToken(cookieToken, requestToken)) {
|
|
2894
|
+
return onError(req, res, next);
|
|
2895
|
+
}
|
|
2896
|
+
next();
|
|
2897
|
+
};
|
|
2898
|
+
}
|
|
2899
|
+
function getCookieValue(req, name) {
|
|
2900
|
+
if (req.cookies && typeof req.cookies === "object" && name in req.cookies) {
|
|
2901
|
+
return req.cookies[name];
|
|
2902
|
+
}
|
|
2903
|
+
const cookieHeader = req.headers.cookie;
|
|
2904
|
+
if (!cookieHeader) return void 0;
|
|
2905
|
+
const match = cookieHeader.match(new RegExp(`(?:^|;\\s*)${escapeRegex(name)}=([^;]*)`));
|
|
2906
|
+
return match ? decodeURIComponent(match[1]) : void 0;
|
|
2907
|
+
}
|
|
2908
|
+
function setCsrfCookie(res, name, token, opts) {
|
|
2909
|
+
const parts = [`${name}=${token}`];
|
|
2910
|
+
parts.push(`Path=${opts.path}`);
|
|
2911
|
+
if (opts.httpOnly) parts.push("HttpOnly");
|
|
2912
|
+
if (opts.secure) parts.push("Secure");
|
|
2913
|
+
parts.push(`SameSite=${opts.sameSite}`);
|
|
2914
|
+
if (opts.domain) parts.push(`Domain=${opts.domain}`);
|
|
2915
|
+
res.setHeader("Set-Cookie", parts.join("; "));
|
|
2916
|
+
}
|
|
2917
|
+
function escapeRegex(str) {
|
|
2918
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2919
|
+
}
|
|
2920
|
+
var createCsrf = csrfProtection;
|
|
2301
2921
|
|
|
2302
2922
|
// src/utils/ip.ts
|
|
2303
2923
|
var PLATFORM_HEADERS = {
|
|
@@ -2562,6 +3182,6 @@ function createRedisStore(options) {
|
|
|
2562
3182
|
return new RedisStore(options);
|
|
2563
3183
|
}
|
|
2564
3184
|
|
|
2565
|
-
export { ArcisError, ValidationError as ArcisValidationError, BLOCKED, ERRORS, HEADERS, INPUT, InputTooLargeError, MemoryStore, RATE_LIMIT, REDACTION, RateLimitError, RedisStore, SanitizationError, SecurityThreatError, VALIDATION, arcis, arcisWithMethods as arcisFunction, botProtection, createCors, createErrorHandler, createHeaders, createRateLimiter, createRedactor, createRedisStore, createSafeLogger, createSanitizer, createSecureCookies, createSlidingWindowLimiter, createTokenBucketLimiter, createValidator, main_default as default, detectBot, detectClientIp, detectCommandInjection, detectHeaderInjection, detectNoSqlInjection, detectPathTraversal, detectPrototypePollution, detectSql, detectXss, enforceSecureCookie, errorHandler, fingerprint, formatDuration, isDangerousExtension, isDangerousNoSqlKey, isDangerousProtoKey, isPrivateIp, isRedirectSafe, isUrlSafe, isValidEmailSyntax, parseDuration, rateLimit, safeCors, safeLog, sanitizeCommand, sanitizeFilename, sanitizeHeaderValue, sanitizeHeaders, sanitizeObject, sanitizePath, sanitizeSql, sanitizeString, sanitizeXss, secureCookieDefaults, securityHeaders, validate, validateEmail, validateFile, validateRedirect, validateUrl, verifyEmailMx };
|
|
3185
|
+
export { ArcisError, ValidationError as ArcisValidationError, BLOCKED, ERRORS, HEADERS, INPUT, InputTooLargeError, MemoryStore, RATE_LIMIT, REDACTION, RateLimitError, RedisStore, SanitizationError, SecurityThreatError, VALIDATION, arcis, arcisWithMethods as arcisFunction, botProtection, createCors, createCsrf, createErrorHandler, createHeaders, createRateLimiter, createRedactor, createRedisStore, createSafeLogger, createSanitizer, createSecureCookies, createSlidingWindowLimiter, createTokenBucketLimiter, createValidator, csrfProtection, main_default as default, detectBot, detectClientIp, detectCommandInjection, detectHeaderInjection, detectJsonpInjection, detectNoSqlInjection, detectPathTraversal, detectPii, detectPrototypePollution, detectSql, detectSsti, detectXss, detectXxe, encodeForAttribute, encodeForCss, encodeForHtml, encodeForJs, encodeForUrl, enforceSecureCookie, errorHandler, fingerprint, formatDuration, generateCsrfToken, isDangerousExtension, isDangerousNoSqlKey, isDangerousProtoKey, isPrivateIp, isRedirectSafe, isUrlSafe, isValidEmailSyntax, parseDuration, rateLimit, redactObjectPii, redactPii, safeCors, safeLog, sanitizeCommand, sanitizeFilename, sanitizeHeaderValue, sanitizeHeaders, sanitizeJsonpCallback, sanitizeObject, sanitizePath, sanitizeSql, sanitizeSsti, sanitizeString, sanitizeXss, sanitizeXxe, scanObjectPii, scanPii, secureCookieDefaults, securityHeaders, validate, validateCsrfToken, validateEmail, validateFile, validateRedirect, validateUrl, verifyEmailMx };
|
|
2566
3186
|
//# sourceMappingURL=index.mjs.map
|
|
2567
3187
|
//# sourceMappingURL=index.mjs.map
|