@arcis/node 1.4.0 → 1.4.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.
Files changed (42) hide show
  1. package/README.md +1 -1
  2. package/dist/core/constants.d.ts +2 -2
  3. package/dist/core/constants.d.ts.map +1 -1
  4. package/dist/core/index.js +11 -3
  5. package/dist/core/index.js.map +1 -1
  6. package/dist/core/index.mjs +11 -3
  7. package/dist/core/index.mjs.map +1 -1
  8. package/dist/index.js +125 -46
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.mjs +126 -47
  11. package/dist/index.mjs.map +1 -1
  12. package/dist/logging/index.js.map +1 -1
  13. package/dist/logging/index.mjs.map +1 -1
  14. package/dist/middleware/csrf.d.ts.map +1 -1
  15. package/dist/middleware/index.js +62 -30
  16. package/dist/middleware/index.js.map +1 -1
  17. package/dist/middleware/index.mjs +63 -31
  18. package/dist/middleware/index.mjs.map +1 -1
  19. package/dist/middleware/rate-limit.d.ts.map +1 -1
  20. package/dist/sanitizers/encode.d.ts.map +1 -1
  21. package/dist/sanitizers/index.d.ts +1 -0
  22. package/dist/sanitizers/index.d.ts.map +1 -1
  23. package/dist/sanitizers/index.js +90 -32
  24. package/dist/sanitizers/index.js.map +1 -1
  25. package/dist/sanitizers/index.mjs +88 -33
  26. package/dist/sanitizers/index.mjs.map +1 -1
  27. package/dist/sanitizers/ldap.d.ts +42 -0
  28. package/dist/sanitizers/ldap.d.ts.map +1 -0
  29. package/dist/sanitizers/path.d.ts.map +1 -1
  30. package/dist/sanitizers/sanitize.d.ts.map +1 -1
  31. package/dist/sanitizers/ssti.d.ts.map +1 -1
  32. package/dist/stores/index.js +21 -1
  33. package/dist/stores/index.js.map +1 -1
  34. package/dist/stores/index.mjs +21 -1
  35. package/dist/stores/index.mjs.map +1 -1
  36. package/dist/stores/memory.d.ts +4 -10
  37. package/dist/stores/memory.d.ts.map +1 -1
  38. package/dist/validation/index.js +38 -21
  39. package/dist/validation/index.js.map +1 -1
  40. package/dist/validation/index.mjs +38 -21
  41. package/dist/validation/index.mjs.map +1 -1
  42. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/middleware/rate-limit.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAkB,MAAM,eAAe,CAAC;AAO7F;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,gBAAqB,GAAG,qBAAqB,CAiHvF;AAED;;;GAGG;AACH,eAAO,MAAM,SAAS,0BAAoB,CAAC"}
1
+ {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/middleware/rate-limit.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAkB,MAAM,eAAe,CAAC;AAO7F;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,gBAAqB,GAAG,qBAAqB,CAoIvF;AAED;;;GAGG;AACH,eAAO,MAAM,SAAS,0BAAoB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"encode.d.ts","sourceRoot":"","sources":["../../src/sanitizers/encode.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAaH;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGnD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAiBxD;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAmBjD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOlD;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAkBlD"}
1
+ {"version":3,"file":"encode.d.ts","sourceRoot":"","sources":["../../src/sanitizers/encode.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAaH;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGnD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAiBxD;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAwBjD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOlD;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAkBlD"}
@@ -15,5 +15,6 @@ export { sanitizeJsonpCallback, detectJsonpInjection } from './jsonp';
15
15
  export { sanitizeHeaderValue, sanitizeHeaders, detectHeaderInjection } from './headers';
16
16
  export { scanPii, detectPii, redactPii, scanObjectPii, redactObjectPii } from './pii';
17
17
  export { encodeForHtml, encodeForAttribute, encodeForJs, encodeForUrl, encodeForCss } from './encode';
18
+ export { sanitizeLdapFilter, sanitizeLdapDn, detectLdapInjection } from './ldap';
18
19
  export { encodeHtmlEntities, isPlainObject } from './utils';
19
20
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sanitizers/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAG7E,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAGpE,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAG3F,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAGnG,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAGlD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAG/C,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAGtE,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAGxF,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAGtF,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAGtG,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sanitizers/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAG7E,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAGpE,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAG3F,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAGnG,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAGlD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAG/C,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAGtE,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAGxF,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAGtF,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAGtG,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAGjF,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC"}
@@ -27,7 +27,15 @@ var XSS_PATTERNS = [
27
27
  /** URL-encoded script tags */
28
28
  /%3Cscript/gi,
29
29
  /** SVG with onload */
30
- /<svg[^>]*onload/gi
30
+ /<svg[^>]*onload/gi,
31
+ /** form tags — phishing/credential harvesting via action= redirection */
32
+ /<form[\s>]/gi,
33
+ /** meta tags — http-equiv refresh redirects or CSP bypass */
34
+ /<meta[\s>]/gi,
35
+ /** base href hijacking — redirects all relative URLs to attacker domain */
36
+ /<base[\s>]/gi,
37
+ /** link tag injection — stylesheet or preload CSRF attacks */
38
+ /<link[\s>]/gi
31
39
  ];
32
40
  var XSS_REMOVE_PATTERNS = [
33
41
  /** Full script blocks (content + tags) */
@@ -54,7 +62,15 @@ var XSS_REMOVE_PATTERNS = [
54
62
  /javascript\s*:/gi,
55
63
  /vbscript\s*:/gi,
56
64
  /** data: URIs with HTML/script content */
57
- /data\s*:\s*text\/html[^>\s]*/gi
65
+ /data\s*:\s*text\/html[^>\s]*/gi,
66
+ /** form tag injection — phishing via action= redirection */
67
+ /<form[\s>][^>]*/gi,
68
+ /** meta tag injection — http-equiv refresh or CSP bypass */
69
+ /<meta[\s>][^>]*/gi,
70
+ /** base href hijacking */
71
+ /<base[\s>][^>]*/gi,
72
+ /** link tag injection — stylesheet or preload attacks */
73
+ /<link[\s>][^>]*/gi
58
74
  ];
59
75
  var SQL_PATTERNS = [
60
76
  /** SQL keywords */
@@ -118,8 +134,8 @@ var COMMAND_PATTERNS = [
118
134
  /[;&|`]/g,
119
135
  /** Command substitution: $( ... ) — matched as a pair to reduce false positives */
120
136
  /\$\(/g,
121
- /** URL-encoded newline/carriage-return injection (%0a, %0d) */
122
- /%0[ad]/gi
137
+ /** URL-encoded control characters (%00-%0F): null, tab, vtab, formfeed, LF, CR */
138
+ /%0[0-9a-f]/gi
123
139
  ];
124
140
  var DANGEROUS_PROTO_KEYS = /* @__PURE__ */ new Set([
125
141
  "__proto__",
@@ -323,26 +339,31 @@ function sanitizePath(input, collectThreats = false) {
323
339
  const threats = [];
324
340
  let value = input;
325
341
  let wasSanitized = false;
326
- for (const pattern of PATH_PATTERNS) {
327
- pattern.lastIndex = 0;
328
- if (pattern.test(value)) {
342
+ value = value.normalize("NFKC");
343
+ let prev;
344
+ do {
345
+ prev = value;
346
+ for (const pattern of PATH_PATTERNS) {
329
347
  pattern.lastIndex = 0;
330
- if (collectThreats) {
331
- const matches = value.match(pattern);
332
- if (matches) {
333
- for (const match of matches) {
334
- threats.push({
335
- type: "path_traversal",
336
- pattern: pattern.source,
337
- original: match
338
- });
348
+ if (pattern.test(value)) {
349
+ pattern.lastIndex = 0;
350
+ if (collectThreats) {
351
+ const matches = value.match(pattern);
352
+ if (matches) {
353
+ for (const match of matches) {
354
+ threats.push({
355
+ type: "path_traversal",
356
+ pattern: pattern.source,
357
+ original: match
358
+ });
359
+ }
339
360
  }
340
361
  }
362
+ value = value.replace(pattern, "");
363
+ wasSanitized = true;
341
364
  }
342
- value = value.replace(pattern, "");
343
- wasSanitized = true;
344
365
  }
345
- }
366
+ } while (value !== prev);
346
367
  if (collectThreats) {
347
368
  return { value, wasSanitized, threats };
348
369
  }
@@ -350,9 +371,10 @@ function sanitizePath(input, collectThreats = false) {
350
371
  }
351
372
  function detectPathTraversal(input) {
352
373
  if (typeof input !== "string") return false;
374
+ const normalized = input.normalize("NFKC");
353
375
  for (const pattern of PATH_PATTERNS) {
354
376
  pattern.lastIndex = 0;
355
- if (pattern.test(input)) {
377
+ if (pattern.test(normalized)) {
356
378
  return true;
357
379
  }
358
380
  }
@@ -410,7 +432,7 @@ function sanitizeString(value, options = {}) {
410
432
  if (value.length > maxSize) {
411
433
  throw new InputTooLargeError(maxSize, value.length);
412
434
  }
413
- const reject = options.mode !== "sanitize";
435
+ const reject = options.mode === "reject";
414
436
  let result = value;
415
437
  if (options.sql !== false) {
416
438
  if (reject) {
@@ -565,10 +587,22 @@ var SSTI_DETECT_PATTERNS = [
565
587
  /\{\{\s*(?:self|request|lipsum|cycler|joiner|namespace|range)\b/gi
566
588
  ];
567
589
  var SSTI_REMOVE_PATTERNS = [
590
+ /** Jinja2 / Twig: {{ ... }} — always strip (not valid in any JS context) */
568
591
  /\{\{.*?\}\}/g,
569
- /\$\{.*?\}/g,
592
+ /**
593
+ * Freemarker / Spring EL: ${...} — only strip when the expression contains
594
+ * operators (?!*+-/), method calls (), or known-dangerous prefixes.
595
+ * Bare ${name} and ${user.name} are left intact (JS template literal syntax).
596
+ */
597
+ /\$\{[^}]*[?!()*+\-/][^}]*\}/g,
598
+ /** ERB / EJS: <%= ... %> */
570
599
  /<%[=\-]?.*?%>/gs,
571
- /#\{.*?\}/g,
600
+ /**
601
+ * Pug / Jade: #{...} — same narrowing as ${ above.
602
+ * #{name} output expressions are left intact.
603
+ */
604
+ /#\{[^}]*[?!()*+\-/][^}]*\}/g,
605
+ /** Python dunder sandbox escape — always strip */
572
606
  /__(?:class|mro|subclasses|globals|builtins|import)__/gi
573
607
  ];
574
608
  function sanitizeSsti(input, collectThreats = false) {
@@ -932,16 +966,18 @@ function encodeForAttribute(value) {
932
966
  function encodeForJs(value) {
933
967
  if (!value) return "";
934
968
  let result = "";
935
- for (let i = 0; i < value.length; i++) {
936
- const ch = value.charCodeAt(i);
937
- if (ch >= 48 && ch <= 57 || // 0-9
938
- ch >= 65 && ch <= 90 || // A-Z
939
- ch >= 97 && ch <= 122) {
940
- result += value[i];
941
- } else if (ch < 256) {
942
- result += `\\x${ch.toString(16).toUpperCase().padStart(2, "0")}`;
969
+ for (const char of value) {
970
+ const cp = char.codePointAt(0);
971
+ if (cp >= 48 && cp <= 57 || // 0-9
972
+ cp >= 65 && cp <= 90 || // A-Z
973
+ cp >= 97 && cp <= 122) {
974
+ result += char;
975
+ } else if (cp < 256) {
976
+ result += `\\x${cp.toString(16).toUpperCase().padStart(2, "0")}`;
977
+ } else if (cp <= 65535) {
978
+ result += `\\u${cp.toString(16).toUpperCase().padStart(4, "0")}`;
943
979
  } else {
944
- result += `\\u${ch.toString(16).toUpperCase().padStart(4, "0")}`;
980
+ result += `\\u{${cp.toString(16).toUpperCase()}}`;
945
981
  }
946
982
  }
947
983
  return result;
@@ -968,10 +1004,30 @@ function encodeForCss(value) {
968
1004
  return result;
969
1005
  }
970
1006
 
1007
+ // src/sanitizers/ldap.ts
1008
+ var LDAP_FILTER_CHARS = /[*()\\\x00]/g;
1009
+ var LDAP_DN_CHARS = /[,+<>;"=\/\\\x00*()\x00]/g;
1010
+ var LDAP_DETECT_PATTERN = /[*()\\\x00]/;
1011
+ var LDAP_INJECTION_PATTERN = /\)\s*\(|\*\s*\)\s*\(/;
1012
+ var escapeChar = (char) => "\\" + char.charCodeAt(0).toString(16).padStart(2, "0");
1013
+ function sanitizeLdapFilter(input) {
1014
+ if (typeof input !== "string") return String(input);
1015
+ return input.replace(LDAP_FILTER_CHARS, escapeChar);
1016
+ }
1017
+ function sanitizeLdapDn(input) {
1018
+ if (typeof input !== "string") return String(input);
1019
+ return input.replace(LDAP_DN_CHARS, escapeChar);
1020
+ }
1021
+ function detectLdapInjection(input) {
1022
+ if (typeof input !== "string") return false;
1023
+ return LDAP_DETECT_PATTERN.test(input) || LDAP_INJECTION_PATTERN.test(input);
1024
+ }
1025
+
971
1026
  exports.createSanitizer = createSanitizer;
972
1027
  exports.detectCommandInjection = detectCommandInjection;
973
1028
  exports.detectHeaderInjection = detectHeaderInjection;
974
1029
  exports.detectJsonpInjection = detectJsonpInjection;
1030
+ exports.detectLdapInjection = detectLdapInjection;
975
1031
  exports.detectNoSqlInjection = detectNoSqlInjection;
976
1032
  exports.detectPathTraversal = detectPathTraversal;
977
1033
  exports.detectPii = detectPii;
@@ -997,6 +1053,8 @@ exports.sanitizeCommand = sanitizeCommand;
997
1053
  exports.sanitizeHeaderValue = sanitizeHeaderValue;
998
1054
  exports.sanitizeHeaders = sanitizeHeaders;
999
1055
  exports.sanitizeJsonpCallback = sanitizeJsonpCallback;
1056
+ exports.sanitizeLdapDn = sanitizeLdapDn;
1057
+ exports.sanitizeLdapFilter = sanitizeLdapFilter;
1000
1058
  exports.sanitizeObject = sanitizeObject;
1001
1059
  exports.sanitizePath = sanitizePath;
1002
1060
  exports.sanitizeSql = sanitizeSql;