@arcis/node 1.4.3 → 1.5.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.
Files changed (142) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +43 -5
  3. package/dist/astro/index.js +6141 -0
  4. package/dist/astro/index.js.map +1 -0
  5. package/dist/astro/index.mjs +6136 -0
  6. package/dist/astro/index.mjs.map +1 -0
  7. package/dist/bun/index.js +6195 -0
  8. package/dist/bun/index.js.map +1 -0
  9. package/dist/bun/index.mjs +6189 -0
  10. package/dist/bun/index.mjs.map +1 -0
  11. package/dist/core/constants.d.ts +4 -3
  12. package/dist/core/constants.d.ts.map +1 -1
  13. package/dist/core/index.js +8 -4
  14. package/dist/core/index.js.map +1 -1
  15. package/dist/core/index.mjs +8 -4
  16. package/dist/core/index.mjs.map +1 -1
  17. package/dist/core/types.d.ts +43 -0
  18. package/dist/core/types.d.ts.map +1 -1
  19. package/dist/fastify/index.js +6160 -0
  20. package/dist/fastify/index.js.map +1 -0
  21. package/dist/fastify/index.mjs +6155 -0
  22. package/dist/fastify/index.mjs.map +1 -0
  23. package/dist/guards.d.ts +156 -0
  24. package/dist/guards.d.ts.map +1 -0
  25. package/dist/hono/index.js +6159 -0
  26. package/dist/hono/index.js.map +1 -0
  27. package/dist/hono/index.mjs +6154 -0
  28. package/dist/hono/index.mjs.map +1 -0
  29. package/dist/index.d.ts +23 -1
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +7365 -305
  32. package/dist/index.js.map +1 -1
  33. package/dist/index.mjs +7327 -306
  34. package/dist/index.mjs.map +1 -1
  35. package/dist/koa/index.js +6158 -0
  36. package/dist/koa/index.js.map +1 -0
  37. package/dist/koa/index.mjs +6153 -0
  38. package/dist/koa/index.mjs.map +1 -0
  39. package/dist/logging/index.js.map +1 -1
  40. package/dist/logging/index.mjs.map +1 -1
  41. package/dist/logging/redactor.d.ts.map +1 -1
  42. package/dist/middleware/astro.d.ts +64 -0
  43. package/dist/middleware/astro.d.ts.map +1 -0
  44. package/dist/middleware/bot-detection.d.ts.map +1 -1
  45. package/dist/middleware/bun.d.ts +75 -0
  46. package/dist/middleware/bun.d.ts.map +1 -0
  47. package/dist/middleware/csrf.d.ts.map +1 -1
  48. package/dist/middleware/error-handler.d.ts.map +1 -1
  49. package/dist/middleware/fastify.d.ts +89 -0
  50. package/dist/middleware/fastify.d.ts.map +1 -0
  51. package/dist/middleware/graphql.d.ts +35 -0
  52. package/dist/middleware/graphql.d.ts.map +1 -0
  53. package/dist/middleware/hono.d.ts +63 -0
  54. package/dist/middleware/hono.d.ts.map +1 -0
  55. package/dist/middleware/index.d.ts +12 -0
  56. package/dist/middleware/index.d.ts.map +1 -1
  57. package/dist/middleware/index.js +6693 -122
  58. package/dist/middleware/index.js.map +1 -1
  59. package/dist/middleware/index.mjs +6683 -123
  60. package/dist/middleware/index.mjs.map +1 -1
  61. package/dist/middleware/koa.d.ts +84 -0
  62. package/dist/middleware/koa.d.ts.map +1 -0
  63. package/dist/middleware/main.d.ts +0 -30
  64. package/dist/middleware/main.d.ts.map +1 -1
  65. package/dist/middleware/mass-assign.d.ts +81 -0
  66. package/dist/middleware/mass-assign.d.ts.map +1 -0
  67. package/dist/middleware/method-allowlist.d.ts +66 -0
  68. package/dist/middleware/method-allowlist.d.ts.map +1 -0
  69. package/dist/middleware/nestjs.d.ts +62 -0
  70. package/dist/middleware/nestjs.d.ts.map +1 -0
  71. package/dist/middleware/nextjs.d.ts +102 -0
  72. package/dist/middleware/nextjs.d.ts.map +1 -0
  73. package/dist/middleware/nuxt.d.ts +61 -0
  74. package/dist/middleware/nuxt.d.ts.map +1 -0
  75. package/dist/middleware/overload.d.ts +92 -0
  76. package/dist/middleware/overload.d.ts.map +1 -0
  77. package/dist/middleware/protect.d.ts +91 -0
  78. package/dist/middleware/protect.d.ts.map +1 -0
  79. package/dist/middleware/rate-limit-sliding.d.ts.map +1 -1
  80. package/dist/middleware/rate-limit-token.d.ts.map +1 -1
  81. package/dist/middleware/rate-limit.d.ts.map +1 -1
  82. package/dist/middleware/response-splitting.d.ts +83 -0
  83. package/dist/middleware/response-splitting.d.ts.map +1 -0
  84. package/dist/middleware/sveltekit.d.ts +68 -0
  85. package/dist/middleware/sveltekit.d.ts.map +1 -0
  86. package/dist/middleware/token-budget.d.ts +75 -0
  87. package/dist/middleware/token-budget.d.ts.map +1 -0
  88. package/dist/nestjs/index.js +1724 -0
  89. package/dist/nestjs/index.js.map +1 -0
  90. package/dist/nestjs/index.mjs +1717 -0
  91. package/dist/nestjs/index.mjs.map +1 -0
  92. package/dist/nextjs/index.js +6184 -0
  93. package/dist/nextjs/index.js.map +1 -0
  94. package/dist/nextjs/index.mjs +6178 -0
  95. package/dist/nextjs/index.mjs.map +1 -0
  96. package/dist/nuxt/index.js +6141 -0
  97. package/dist/nuxt/index.js.map +1 -0
  98. package/dist/nuxt/index.mjs +6136 -0
  99. package/dist/nuxt/index.mjs.map +1 -0
  100. package/dist/sanitizers/encode.d.ts.map +1 -1
  101. package/dist/sanitizers/graphql.d.ts +72 -0
  102. package/dist/sanitizers/graphql.d.ts.map +1 -0
  103. package/dist/sanitizers/headers.d.ts +18 -0
  104. package/dist/sanitizers/headers.d.ts.map +1 -1
  105. package/dist/sanitizers/index.d.ts +6 -2
  106. package/dist/sanitizers/index.d.ts.map +1 -1
  107. package/dist/sanitizers/index.js +339 -197
  108. package/dist/sanitizers/index.js.map +1 -1
  109. package/dist/sanitizers/index.mjs +333 -198
  110. package/dist/sanitizers/index.mjs.map +1 -1
  111. package/dist/sanitizers/prompt-injection.d.ts +62 -0
  112. package/dist/sanitizers/prompt-injection.d.ts.map +1 -0
  113. package/dist/sanitizers/sanitize.d.ts +13 -0
  114. package/dist/sanitizers/sanitize.d.ts.map +1 -1
  115. package/dist/sanitizers/xpath.d.ts +37 -0
  116. package/dist/sanitizers/xpath.d.ts.map +1 -0
  117. package/dist/stores/index.js +4 -4
  118. package/dist/stores/index.js.map +1 -1
  119. package/dist/stores/index.mjs +4 -4
  120. package/dist/stores/index.mjs.map +1 -1
  121. package/dist/stores/redis.d.ts +7 -1
  122. package/dist/stores/redis.d.ts.map +1 -1
  123. package/dist/sveltekit/index.js +6142 -0
  124. package/dist/sveltekit/index.js.map +1 -0
  125. package/dist/sveltekit/index.mjs +6137 -0
  126. package/dist/sveltekit/index.mjs.map +1 -0
  127. package/dist/telemetry/client.d.ts +3 -0
  128. package/dist/telemetry/client.d.ts.map +1 -1
  129. package/dist/telemetry/types.d.ts +12 -0
  130. package/dist/telemetry/types.d.ts.map +1 -1
  131. package/dist/validation/index.d.ts +2 -0
  132. package/dist/validation/index.d.ts.map +1 -1
  133. package/dist/validation/index.js +137 -12
  134. package/dist/validation/index.js.map +1 -1
  135. package/dist/validation/index.mjs +116 -13
  136. package/dist/validation/index.mjs.map +1 -1
  137. package/dist/validation/redirect.d.ts.map +1 -1
  138. package/dist/validation/schema.d.ts.map +1 -1
  139. package/dist/validation/url-async.d.ts +137 -0
  140. package/dist/validation/url-async.d.ts.map +1 -0
  141. package/package.json +52 -4
  142. package/scripts/postinstall.cjs +26 -0
@@ -35,7 +35,10 @@ var XSS_PATTERNS = [
35
35
  /** base href hijacking — redirects all relative URLs to attacker domain */
36
36
  /<base[\s>]/gi,
37
37
  /** link tag injection — stylesheet or preload CSRF attacks */
38
- /<link[\s>]/gi
38
+ /<link[\s>]/gi,
39
+ /** style tag — CSS expression() / behavior: / IE-era attacks. Mirrors
40
+ * Python's xss-style-tag from packages/core/patterns.json. */
41
+ /<style[\s>]/gi
39
42
  ];
40
43
  var XSS_REMOVE_PATTERNS = [
41
44
  /** Full script blocks (content + tags) */
@@ -64,8 +67,8 @@ var XSS_REMOVE_PATTERNS = [
64
67
  /** javascript: and vbscript: protocols (allow optional spaces before colon) */
65
68
  /javascript\s*:/gi,
66
69
  /vbscript\s*:/gi,
67
- /** data: URIs with HTML/script content */
68
- /data\s*:\s*text\/html[^>\s]*/gi,
70
+ /** data: URIs with HTML or SVG content (SVG can run JS via inline event handlers) */
71
+ /data\s*:\s*(?:text\/html|image\/svg)[^>\s]*/gi,
69
72
  /** form tag injection — phishing via action= redirection */
70
73
  /<form[\s>][^>]*/gi,
71
74
  /** meta tag injection — http-equiv refresh or CSP bypass */
@@ -428,150 +431,6 @@ function detectCommandInjection(input) {
428
431
  return false;
429
432
  }
430
433
 
431
- // src/sanitizers/sanitize.ts
432
- function sanitizeString(value, options = {}) {
433
- if (typeof value !== "string") return value;
434
- const maxSize = options.maxSize ?? INPUT.DEFAULT_MAX_SIZE;
435
- if (value.length > maxSize) {
436
- throw new InputTooLargeError(maxSize, value.length);
437
- }
438
- const reject = options.mode === "reject";
439
- let result = value;
440
- if (options.sql !== false) {
441
- if (reject) {
442
- if (detectSql(result)) {
443
- throw new SecurityThreatError("sql_injection", "SQL pattern detected in input");
444
- }
445
- } else {
446
- result = sanitizeSql(result);
447
- }
448
- }
449
- if (options.path !== false) {
450
- result = sanitizePath(result);
451
- }
452
- if (options.command !== false) {
453
- if (reject) {
454
- if (detectCommandInjection(result)) {
455
- throw new SecurityThreatError("command_injection", "Shell metacharacter detected in input");
456
- }
457
- } else {
458
- result = sanitizeCommand(result);
459
- }
460
- }
461
- if (options.xss !== false) {
462
- result = sanitizeXss(result, false, options.htmlEncode ?? false);
463
- }
464
- return result;
465
- }
466
- function sanitizeObject(obj, options = {}) {
467
- if (obj === null || obj === void 0) return obj;
468
- if (typeof obj === "string") return sanitizeString(obj, options);
469
- if (typeof obj !== "object") return obj;
470
- if (Array.isArray(obj)) return obj.map((item) => sanitizeObject(item, options));
471
- const result = sanitizeObjectDepth(obj, options, 0);
472
- return options.freeze ? Object.freeze(result) : result;
473
- }
474
- function sanitizeObjectDepth(obj, options, depth) {
475
- if (depth >= INPUT.MAX_RECURSION_DEPTH) return obj;
476
- const result = {};
477
- for (const key of Object.keys(obj)) {
478
- if (options.proto !== false && DANGEROUS_PROTO_KEYS.has(key.toLowerCase())) {
479
- continue;
480
- }
481
- if (options.nosql !== false && NOSQL_DANGEROUS_KEYS.has(key)) {
482
- continue;
483
- }
484
- const sanitizedKey = sanitizeString(key, options);
485
- const value = obj[key];
486
- if (value === null || value === void 0) {
487
- result[sanitizedKey] = value;
488
- } else if (typeof value === "string") {
489
- result[sanitizedKey] = sanitizeString(value, options);
490
- } else if (Array.isArray(value)) {
491
- result[sanitizedKey] = value.map((item) => sanitizeObject(item, options));
492
- } else if (typeof value === "object") {
493
- result[sanitizedKey] = sanitizeObjectDepth(value, options, depth + 1);
494
- } else {
495
- result[sanitizedKey] = value;
496
- }
497
- }
498
- return result;
499
- }
500
- function createSanitizer(options = {}) {
501
- return (req, _res, next) => {
502
- try {
503
- if (req.body && typeof req.body === "object") {
504
- req.body = sanitizeObject(req.body, options);
505
- }
506
- if (req.query && typeof req.query === "object") {
507
- const sanitizedQuery = sanitizeObject(req.query, options);
508
- Object.defineProperty(req, "query", { value: sanitizedQuery, writable: true, configurable: true });
509
- }
510
- if (req.params && typeof req.params === "object") {
511
- const sanitizedParams = sanitizeObject(req.params, options);
512
- Object.defineProperty(req, "params", { value: sanitizedParams, writable: true, configurable: true });
513
- }
514
- next();
515
- } catch (err) {
516
- next(err);
517
- }
518
- };
519
- }
520
-
521
- // src/sanitizers/nosql.ts
522
- function isDangerousNoSqlKey(key) {
523
- return NOSQL_DANGEROUS_KEYS.has(key);
524
- }
525
- function detectNoSqlInjection(obj, maxDepth = 10) {
526
- if (maxDepth <= 0) return false;
527
- if (obj === null || typeof obj !== "object") return false;
528
- if (Array.isArray(obj)) {
529
- return obj.some((item) => detectNoSqlInjection(item, maxDepth - 1));
530
- }
531
- for (const key of Object.keys(obj)) {
532
- if (isDangerousNoSqlKey(key)) {
533
- return true;
534
- }
535
- const value = obj[key];
536
- if (typeof value === "object" && value !== null) {
537
- if (detectNoSqlInjection(value, maxDepth - 1)) {
538
- return true;
539
- }
540
- }
541
- }
542
- return false;
543
- }
544
- function getDangerousOperators() {
545
- return Array.from(NOSQL_DANGEROUS_KEYS);
546
- }
547
-
548
- // src/sanitizers/prototype.ts
549
- function isDangerousProtoKey(key) {
550
- return DANGEROUS_PROTO_KEYS.has(key.toLowerCase());
551
- }
552
- function detectPrototypePollution(obj, maxDepth = 10) {
553
- if (maxDepth <= 0) return false;
554
- if (obj === null || typeof obj !== "object") return false;
555
- if (Array.isArray(obj)) {
556
- return obj.some((item) => detectPrototypePollution(item, maxDepth - 1));
557
- }
558
- for (const key of Object.keys(obj)) {
559
- if (DANGEROUS_PROTO_KEYS.has(key.toLowerCase())) {
560
- return true;
561
- }
562
- const value = obj[key];
563
- if (typeof value === "object" && value !== null) {
564
- if (detectPrototypePollution(value, maxDepth - 1)) {
565
- return true;
566
- }
567
- }
568
- }
569
- return false;
570
- }
571
- function getDangerousProtoKeys() {
572
- return Array.from(DANGEROUS_PROTO_KEYS);
573
- }
574
-
575
434
  // src/sanitizers/ssti.ts
576
435
  var SSTI_DETECT_PATTERNS = [
577
436
  /** Jinja2 / Twig / Nunjucks: {{ ... }} */
@@ -734,42 +593,36 @@ function detectXxe(input) {
734
593
  return false;
735
594
  }
736
595
 
737
- // src/sanitizers/jsonp.ts
738
- var SAFE_CALLBACK_PATTERN = /^[a-zA-Z_$][a-zA-Z0-9_$.]*$/;
739
- var DANGEROUS_CALLBACK_PATTERNS = [
740
- /\.\./
741
- // prototype chain traversal
742
- ];
743
- function sanitizeJsonpCallback(callback, maxLength = 128) {
744
- if (typeof callback !== "string" || callback.length === 0) {
745
- return null;
746
- }
747
- if (callback.length > maxLength) {
748
- return null;
749
- }
750
- if (!SAFE_CALLBACK_PATTERN.test(callback)) {
751
- return null;
752
- }
753
- for (const pattern of DANGEROUS_CALLBACK_PATTERNS) {
754
- if (pattern.test(callback)) {
755
- return null;
756
- }
757
- }
758
- return callback;
596
+ // src/sanitizers/ldap.ts
597
+ var LDAP_FILTER_CHARS = /[*()\\\x00]/g;
598
+ var LDAP_DN_CHARS = /[,+<>;"=\/\\\x00*()\x00]/g;
599
+ var LDAP_DETECT_PATTERN = /[*()\\\x00]/;
600
+ var LDAP_INJECTION_PATTERN = /\)\s*\(|\*\s*\)\s*\(/;
601
+ var escapeChar = (char) => "\\" + char.charCodeAt(0).toString(16).padStart(2, "0");
602
+ function sanitizeLdapFilter(input) {
603
+ if (typeof input !== "string") return String(input);
604
+ return input.replace(LDAP_FILTER_CHARS, escapeChar);
759
605
  }
760
- function detectJsonpInjection(callback) {
761
- if (typeof callback !== "string" || callback.length === 0) {
762
- return false;
763
- }
764
- if (!SAFE_CALLBACK_PATTERN.test(callback)) {
765
- return true;
766
- }
767
- for (const pattern of DANGEROUS_CALLBACK_PATTERNS) {
768
- if (pattern.test(callback)) {
769
- return true;
770
- }
771
- }
772
- return false;
606
+ function sanitizeLdapDn(input) {
607
+ if (typeof input !== "string") return String(input);
608
+ return input.replace(LDAP_DN_CHARS, escapeChar);
609
+ }
610
+ function detectLdapInjection(input) {
611
+ if (typeof input !== "string") return false;
612
+ return LDAP_DETECT_PATTERN.test(input) || LDAP_INJECTION_PATTERN.test(input);
613
+ }
614
+
615
+ // src/sanitizers/xpath.ts
616
+ var XPATH_INJECTION_CHARS = /['"|,()]/;
617
+ var XPATH_INJECTION_PATTERN = /('\s*(or|and)\s*'|"\s*(or|and)\s*"|\)\s*(or|and)\s*\(|\|\s*\/)/i;
618
+ function detectXpathInjection(input) {
619
+ if (typeof input !== "string" || input.length === 0) return false;
620
+ if (!XPATH_INJECTION_CHARS.test(input)) return false;
621
+ return XPATH_INJECTION_PATTERN.test(input);
622
+ }
623
+ function sanitizeXpath(input) {
624
+ if (typeof input !== "string") return String(input);
625
+ return input.replace(/['"|,]/g, "");
773
626
  }
774
627
 
775
628
  // src/sanitizers/headers.ts
@@ -820,6 +673,263 @@ function detectHeaderInjection(input) {
820
673
  HEADER_INJECTION_PATTERN.lastIndex = 0;
821
674
  return HEADER_INJECTION_PATTERN.test(input);
822
675
  }
676
+ var sanitizeEmailHeader = sanitizeHeaderValue;
677
+ var detectEmailHeaderInjection = detectHeaderInjection;
678
+
679
+ // src/sanitizers/sanitize.ts
680
+ function sanitizeString(value, options = {}) {
681
+ if (typeof value !== "string") return value;
682
+ const maxSize = options.maxSize ?? INPUT.DEFAULT_MAX_SIZE;
683
+ if (value.length > maxSize) {
684
+ throw new InputTooLargeError(maxSize, value.length);
685
+ }
686
+ const reject = options.mode === "reject";
687
+ let result = value;
688
+ if (options.sql !== false) {
689
+ if (reject) {
690
+ if (detectSql(result)) {
691
+ throw new SecurityThreatError("sql_injection", "SQL pattern detected in input");
692
+ }
693
+ } else {
694
+ result = sanitizeSql(result);
695
+ }
696
+ }
697
+ if (options.path !== false) {
698
+ result = sanitizePath(result);
699
+ }
700
+ if (options.command !== false) {
701
+ if (reject) {
702
+ if (detectCommandInjection(result)) {
703
+ throw new SecurityThreatError("command_injection", "Shell metacharacter detected in input");
704
+ }
705
+ } else {
706
+ result = sanitizeCommand(result);
707
+ }
708
+ }
709
+ if (options.xss !== false) {
710
+ result = sanitizeXss(result, false, options.htmlEncode ?? false);
711
+ }
712
+ return result;
713
+ }
714
+ function sanitizeObject(obj, options = {}) {
715
+ if (obj === null || obj === void 0) return obj;
716
+ if (typeof obj === "string") return sanitizeString(obj, options);
717
+ if (typeof obj !== "object") return obj;
718
+ if (Array.isArray(obj)) return obj.map((item) => sanitizeObject(item, options));
719
+ const result = sanitizeObjectDepth(obj, options, 0);
720
+ return options.freeze ? Object.freeze(result) : result;
721
+ }
722
+ function sanitizeObjectDepth(obj, options, depth) {
723
+ if (depth >= INPUT.MAX_RECURSION_DEPTH) return obj;
724
+ const result = {};
725
+ for (const key of Object.keys(obj)) {
726
+ if (options.proto !== false && DANGEROUS_PROTO_KEYS.has(key.toLowerCase())) {
727
+ continue;
728
+ }
729
+ if (options.nosql !== false && NOSQL_DANGEROUS_KEYS.has(key)) {
730
+ continue;
731
+ }
732
+ const sanitizedKey = sanitizeString(key, options);
733
+ const value = obj[key];
734
+ if (value === null || value === void 0) {
735
+ result[sanitizedKey] = value;
736
+ } else if (typeof value === "string") {
737
+ result[sanitizedKey] = sanitizeString(value, options);
738
+ } else if (Array.isArray(value)) {
739
+ result[sanitizedKey] = value.map((item) => sanitizeObject(item, options));
740
+ } else if (typeof value === "object") {
741
+ result[sanitizedKey] = sanitizeObjectDepth(value, options, depth + 1);
742
+ } else {
743
+ result[sanitizedKey] = value;
744
+ }
745
+ }
746
+ return result;
747
+ }
748
+ function scanThreats(data, depth = 0) {
749
+ if (depth > INPUT.MAX_RECURSION_DEPTH) return null;
750
+ if (data && typeof data === "object" && !Array.isArray(data)) {
751
+ for (const key of Object.keys(data)) {
752
+ const lower = key.toLowerCase();
753
+ if (DANGEROUS_PROTO_KEYS.has(lower)) {
754
+ return { vector: "prototype", rule: "prototype/match", matchedPattern: key };
755
+ }
756
+ if (NOSQL_DANGEROUS_KEYS.has(key)) {
757
+ return { vector: "nosql", rule: "nosql/match", matchedPattern: key };
758
+ }
759
+ const inner = scanThreats(data[key], depth + 1);
760
+ if (inner) return inner;
761
+ }
762
+ return null;
763
+ }
764
+ if (Array.isArray(data)) {
765
+ for (const item of data) {
766
+ const inner = scanThreats(item, depth + 1);
767
+ if (inner) return inner;
768
+ }
769
+ return null;
770
+ }
771
+ if (typeof data !== "string") return null;
772
+ const sample = data.slice(0, 80);
773
+ if (detectXss(data)) {
774
+ return { vector: "xss", rule: "xss/match", matchedPattern: sample };
775
+ }
776
+ if (detectSsti(data)) {
777
+ return { vector: "ssti", rule: "ssti/match", matchedPattern: sample };
778
+ }
779
+ if (detectXxe(data)) {
780
+ return { vector: "xxe", rule: "xxe/match", matchedPattern: sample };
781
+ }
782
+ if (detectSql(data)) {
783
+ return { vector: "sql", rule: "sql/match", matchedPattern: sample };
784
+ }
785
+ if (detectPathTraversal(data)) {
786
+ return { vector: "path", rule: "path/match", matchedPattern: sample };
787
+ }
788
+ if (detectCommandInjection(data)) {
789
+ return { vector: "command", rule: "command/match", matchedPattern: sample };
790
+ }
791
+ if (detectLdapInjection(data)) {
792
+ return { vector: "ldap", rule: "ldap/match", matchedPattern: sample };
793
+ }
794
+ if (detectXpathInjection(data)) {
795
+ return { vector: "xpath", rule: "xpath/match", matchedPattern: sample };
796
+ }
797
+ if (detectHeaderInjection(data)) {
798
+ return { vector: "header", rule: "header/match", matchedPattern: sample };
799
+ }
800
+ return null;
801
+ }
802
+ function createSanitizer(options = {}) {
803
+ return (req, res, next) => {
804
+ try {
805
+ if (options.block) {
806
+ const hit = scanThreats(req.body) || scanThreats(req.query) || scanThreats(req.params) || scanThreats(req.path);
807
+ if (hit) {
808
+ req.__arcis = {
809
+ vector: hit.vector,
810
+ rule: hit.rule,
811
+ severity: "high",
812
+ matchedPattern: hit.matchedPattern,
813
+ reason: `${hit.vector} pattern detected in request`,
814
+ decision: "deny"
815
+ };
816
+ res.status(403).json({
817
+ error: "Request blocked for security reasons",
818
+ code: "SECURITY_THREAT",
819
+ vector: hit.vector
820
+ });
821
+ return;
822
+ }
823
+ }
824
+ if (req.body && typeof req.body === "object") {
825
+ req.body = sanitizeObject(req.body, options);
826
+ }
827
+ if (req.query && typeof req.query === "object") {
828
+ const sanitizedQuery = sanitizeObject(req.query, options);
829
+ Object.defineProperty(req, "query", { value: sanitizedQuery, writable: true, configurable: true });
830
+ }
831
+ if (req.params && typeof req.params === "object") {
832
+ const sanitizedParams = sanitizeObject(req.params, options);
833
+ Object.defineProperty(req, "params", { value: sanitizedParams, writable: true, configurable: true });
834
+ }
835
+ next();
836
+ } catch (err) {
837
+ next(err);
838
+ }
839
+ };
840
+ }
841
+
842
+ // src/sanitizers/nosql.ts
843
+ function isDangerousNoSqlKey(key) {
844
+ return NOSQL_DANGEROUS_KEYS.has(key);
845
+ }
846
+ function detectNoSqlInjection(obj, maxDepth = 10) {
847
+ if (maxDepth <= 0) return false;
848
+ if (obj === null || typeof obj !== "object") return false;
849
+ if (Array.isArray(obj)) {
850
+ return obj.some((item) => detectNoSqlInjection(item, maxDepth - 1));
851
+ }
852
+ for (const key of Object.keys(obj)) {
853
+ if (isDangerousNoSqlKey(key)) {
854
+ return true;
855
+ }
856
+ const value = obj[key];
857
+ if (typeof value === "object" && value !== null) {
858
+ if (detectNoSqlInjection(value, maxDepth - 1)) {
859
+ return true;
860
+ }
861
+ }
862
+ }
863
+ return false;
864
+ }
865
+ function getDangerousOperators() {
866
+ return Array.from(NOSQL_DANGEROUS_KEYS);
867
+ }
868
+
869
+ // src/sanitizers/prototype.ts
870
+ function isDangerousProtoKey(key) {
871
+ return DANGEROUS_PROTO_KEYS.has(key.toLowerCase());
872
+ }
873
+ function detectPrototypePollution(obj, maxDepth = 10) {
874
+ if (maxDepth <= 0) return false;
875
+ if (obj === null || typeof obj !== "object") return false;
876
+ if (Array.isArray(obj)) {
877
+ return obj.some((item) => detectPrototypePollution(item, maxDepth - 1));
878
+ }
879
+ for (const key of Object.keys(obj)) {
880
+ if (DANGEROUS_PROTO_KEYS.has(key.toLowerCase())) {
881
+ return true;
882
+ }
883
+ const value = obj[key];
884
+ if (typeof value === "object" && value !== null) {
885
+ if (detectPrototypePollution(value, maxDepth - 1)) {
886
+ return true;
887
+ }
888
+ }
889
+ }
890
+ return false;
891
+ }
892
+ function getDangerousProtoKeys() {
893
+ return Array.from(DANGEROUS_PROTO_KEYS);
894
+ }
895
+
896
+ // src/sanitizers/jsonp.ts
897
+ var SAFE_CALLBACK_PATTERN = /^[a-zA-Z_$][a-zA-Z0-9_$.]*$/;
898
+ var DANGEROUS_CALLBACK_PATTERNS = [
899
+ /\.\./
900
+ // prototype chain traversal
901
+ ];
902
+ function sanitizeJsonpCallback(callback, maxLength = 128) {
903
+ if (typeof callback !== "string" || callback.length === 0) {
904
+ return null;
905
+ }
906
+ if (callback.length > maxLength) {
907
+ return null;
908
+ }
909
+ if (!SAFE_CALLBACK_PATTERN.test(callback)) {
910
+ return null;
911
+ }
912
+ for (const pattern of DANGEROUS_CALLBACK_PATTERNS) {
913
+ if (pattern.test(callback)) {
914
+ return null;
915
+ }
916
+ }
917
+ return callback;
918
+ }
919
+ function detectJsonpInjection(callback) {
920
+ if (typeof callback !== "string" || callback.length === 0) {
921
+ return false;
922
+ }
923
+ if (!SAFE_CALLBACK_PATTERN.test(callback)) {
924
+ return true;
925
+ }
926
+ for (const pattern of DANGEROUS_CALLBACK_PATTERNS) {
927
+ if (pattern.test(callback)) {
928
+ return true;
929
+ }
930
+ }
931
+ return false;
932
+ }
823
933
 
824
934
  // src/sanitizers/pii.ts
825
935
  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;
@@ -986,6 +1096,7 @@ function encodeForJs(value) {
986
1096
  let result = "";
987
1097
  for (const char of value) {
988
1098
  const cp = char.codePointAt(0);
1099
+ if (cp === void 0) continue;
989
1100
  if (cp >= 48 && cp <= 57 || // 0-9
990
1101
  cp >= 65 && cp <= 90 || // A-Z
991
1102
  cp >= 97 && cp <= 122) {
@@ -1022,27 +1133,53 @@ function encodeForCss(value) {
1022
1133
  return result;
1023
1134
  }
1024
1135
 
1025
- // src/sanitizers/ldap.ts
1026
- var LDAP_FILTER_CHARS = /[*()\\\x00]/g;
1027
- var LDAP_DN_CHARS = /[,+<>;"=\/\\\x00*()\x00]/g;
1028
- var LDAP_DETECT_PATTERN = /[*()\\\x00]/;
1029
- var LDAP_INJECTION_PATTERN = /\)\s*\(|\*\s*\)\s*\(/;
1030
- var escapeChar = (char) => "\\" + char.charCodeAt(0).toString(16).padStart(2, "0");
1031
- function sanitizeLdapFilter(input) {
1032
- if (typeof input !== "string") return String(input);
1033
- return input.replace(LDAP_FILTER_CHARS, escapeChar);
1136
+ // src/sanitizers/graphql.ts
1137
+ var DEFAULTS = {
1138
+ maxDepth: 10,
1139
+ maxLength: 1e4,
1140
+ blockIntrospection: true
1141
+ };
1142
+ var INTROSPECTION_PATTERN = /\b__(schema|type|typeKind|directive)\b/;
1143
+ function computeDepth(query) {
1144
+ let depth = 0;
1145
+ let max = 0;
1146
+ for (let i = 0; i < query.length; i++) {
1147
+ const c = query.charCodeAt(i);
1148
+ if (c === 123) {
1149
+ depth++;
1150
+ if (depth > max) max = depth;
1151
+ } else if (c === 125) {
1152
+ if (depth > 0) depth--;
1153
+ }
1154
+ }
1155
+ return max;
1034
1156
  }
1035
- function sanitizeLdapDn(input) {
1036
- if (typeof input !== "string") return String(input);
1037
- return input.replace(LDAP_DN_CHARS, escapeChar);
1157
+ function inspectGraphqlQuery(query, options = {}) {
1158
+ const maxDepth = options.maxDepth ?? DEFAULTS.maxDepth;
1159
+ const maxLength = options.maxLength ?? DEFAULTS.maxLength;
1160
+ const blockIntrospection = options.blockIntrospection ?? DEFAULTS.blockIntrospection;
1161
+ const length = query.length;
1162
+ const depth = computeDepth(query);
1163
+ if (depth > maxDepth) {
1164
+ return { blocked: true, reason: "depth", depth, length };
1165
+ }
1166
+ if (blockIntrospection && INTROSPECTION_PATTERN.test(query)) {
1167
+ return { blocked: true, reason: "introspection", depth, length };
1168
+ }
1169
+ if (length > maxLength) {
1170
+ return { blocked: true, reason: "length", depth, length };
1171
+ }
1172
+ return { blocked: false, depth, length };
1038
1173
  }
1039
- function detectLdapInjection(input) {
1040
- if (typeof input !== "string") return false;
1041
- return LDAP_DETECT_PATTERN.test(input) || LDAP_INJECTION_PATTERN.test(input);
1174
+ function detectGraphqlAbuse(query, options) {
1175
+ if (typeof query !== "string" || query.length === 0) return false;
1176
+ return inspectGraphqlQuery(query, options).blocked;
1042
1177
  }
1043
1178
 
1044
1179
  exports.createSanitizer = createSanitizer;
1045
1180
  exports.detectCommandInjection = detectCommandInjection;
1181
+ exports.detectEmailHeaderInjection = detectEmailHeaderInjection;
1182
+ exports.detectGraphqlAbuse = detectGraphqlAbuse;
1046
1183
  exports.detectHeaderInjection = detectHeaderInjection;
1047
1184
  exports.detectJsonpInjection = detectJsonpInjection;
1048
1185
  exports.detectLdapInjection = detectLdapInjection;
@@ -1052,6 +1189,7 @@ exports.detectPii = detectPii;
1052
1189
  exports.detectPrototypePollution = detectPrototypePollution;
1053
1190
  exports.detectSql = detectSql;
1054
1191
  exports.detectSsti = detectSsti;
1192
+ exports.detectXpathInjection = detectXpathInjection;
1055
1193
  exports.detectXss = detectXss;
1056
1194
  exports.detectXxe = detectXxe;
1057
1195
  exports.encodeForAttribute = encodeForAttribute;
@@ -1062,12 +1200,14 @@ exports.encodeForUrl = encodeForUrl;
1062
1200
  exports.encodeHtmlEntities = encodeHtmlEntities;
1063
1201
  exports.getDangerousOperators = getDangerousOperators;
1064
1202
  exports.getDangerousProtoKeys = getDangerousProtoKeys;
1203
+ exports.inspectGraphqlQuery = inspectGraphqlQuery;
1065
1204
  exports.isDangerousNoSqlKey = isDangerousNoSqlKey;
1066
1205
  exports.isDangerousProtoKey = isDangerousProtoKey;
1067
1206
  exports.isPlainObject = isPlainObject;
1068
1207
  exports.redactObjectPii = redactObjectPii;
1069
1208
  exports.redactPii = redactPii;
1070
1209
  exports.sanitizeCommand = sanitizeCommand;
1210
+ exports.sanitizeEmailHeader = sanitizeEmailHeader;
1071
1211
  exports.sanitizeHeaderValue = sanitizeHeaderValue;
1072
1212
  exports.sanitizeHeaders = sanitizeHeaders;
1073
1213
  exports.sanitizeJsonpCallback = sanitizeJsonpCallback;
@@ -1078,9 +1218,11 @@ exports.sanitizePath = sanitizePath;
1078
1218
  exports.sanitizeSql = sanitizeSql;
1079
1219
  exports.sanitizeSsti = sanitizeSsti;
1080
1220
  exports.sanitizeString = sanitizeString;
1221
+ exports.sanitizeXpath = sanitizeXpath;
1081
1222
  exports.sanitizeXss = sanitizeXss;
1082
1223
  exports.sanitizeXxe = sanitizeXxe;
1083
1224
  exports.scanObjectPii = scanObjectPii;
1084
1225
  exports.scanPii = scanPii;
1226
+ exports.scanThreats = scanThreats;
1085
1227
  //# sourceMappingURL=index.js.map
1086
1228
  //# sourceMappingURL=index.js.map