@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
@@ -33,7 +33,10 @@ var XSS_PATTERNS = [
33
33
  /** base href hijacking — redirects all relative URLs to attacker domain */
34
34
  /<base[\s>]/gi,
35
35
  /** link tag injection — stylesheet or preload CSRF attacks */
36
- /<link[\s>]/gi
36
+ /<link[\s>]/gi,
37
+ /** style tag — CSS expression() / behavior: / IE-era attacks. Mirrors
38
+ * Python's xss-style-tag from packages/core/patterns.json. */
39
+ /<style[\s>]/gi
37
40
  ];
38
41
  var XSS_REMOVE_PATTERNS = [
39
42
  /** Full script blocks (content + tags) */
@@ -62,8 +65,8 @@ var XSS_REMOVE_PATTERNS = [
62
65
  /** javascript: and vbscript: protocols (allow optional spaces before colon) */
63
66
  /javascript\s*:/gi,
64
67
  /vbscript\s*:/gi,
65
- /** data: URIs with HTML/script content */
66
- /data\s*:\s*text\/html[^>\s]*/gi,
68
+ /** data: URIs with HTML or SVG content (SVG can run JS via inline event handlers) */
69
+ /data\s*:\s*(?:text\/html|image\/svg)[^>\s]*/gi,
67
70
  /** form tag injection — phishing via action= redirection */
68
71
  /<form[\s>][^>]*/gi,
69
72
  /** meta tag injection — http-equiv refresh or CSP bypass */
@@ -426,150 +429,6 @@ function detectCommandInjection(input) {
426
429
  return false;
427
430
  }
428
431
 
429
- // src/sanitizers/sanitize.ts
430
- function sanitizeString(value, options = {}) {
431
- if (typeof value !== "string") return value;
432
- const maxSize = options.maxSize ?? INPUT.DEFAULT_MAX_SIZE;
433
- if (value.length > maxSize) {
434
- throw new InputTooLargeError(maxSize, value.length);
435
- }
436
- const reject = options.mode === "reject";
437
- let result = value;
438
- if (options.sql !== false) {
439
- if (reject) {
440
- if (detectSql(result)) {
441
- throw new SecurityThreatError("sql_injection", "SQL pattern detected in input");
442
- }
443
- } else {
444
- result = sanitizeSql(result);
445
- }
446
- }
447
- if (options.path !== false) {
448
- result = sanitizePath(result);
449
- }
450
- if (options.command !== false) {
451
- if (reject) {
452
- if (detectCommandInjection(result)) {
453
- throw new SecurityThreatError("command_injection", "Shell metacharacter detected in input");
454
- }
455
- } else {
456
- result = sanitizeCommand(result);
457
- }
458
- }
459
- if (options.xss !== false) {
460
- result = sanitizeXss(result, false, options.htmlEncode ?? false);
461
- }
462
- return result;
463
- }
464
- function sanitizeObject(obj, options = {}) {
465
- if (obj === null || obj === void 0) return obj;
466
- if (typeof obj === "string") return sanitizeString(obj, options);
467
- if (typeof obj !== "object") return obj;
468
- if (Array.isArray(obj)) return obj.map((item) => sanitizeObject(item, options));
469
- const result = sanitizeObjectDepth(obj, options, 0);
470
- return options.freeze ? Object.freeze(result) : result;
471
- }
472
- function sanitizeObjectDepth(obj, options, depth) {
473
- if (depth >= INPUT.MAX_RECURSION_DEPTH) return obj;
474
- const result = {};
475
- for (const key of Object.keys(obj)) {
476
- if (options.proto !== false && DANGEROUS_PROTO_KEYS.has(key.toLowerCase())) {
477
- continue;
478
- }
479
- if (options.nosql !== false && NOSQL_DANGEROUS_KEYS.has(key)) {
480
- continue;
481
- }
482
- const sanitizedKey = sanitizeString(key, options);
483
- const value = obj[key];
484
- if (value === null || value === void 0) {
485
- result[sanitizedKey] = value;
486
- } else if (typeof value === "string") {
487
- result[sanitizedKey] = sanitizeString(value, options);
488
- } else if (Array.isArray(value)) {
489
- result[sanitizedKey] = value.map((item) => sanitizeObject(item, options));
490
- } else if (typeof value === "object") {
491
- result[sanitizedKey] = sanitizeObjectDepth(value, options, depth + 1);
492
- } else {
493
- result[sanitizedKey] = value;
494
- }
495
- }
496
- return result;
497
- }
498
- function createSanitizer(options = {}) {
499
- return (req, _res, next) => {
500
- try {
501
- if (req.body && typeof req.body === "object") {
502
- req.body = sanitizeObject(req.body, options);
503
- }
504
- if (req.query && typeof req.query === "object") {
505
- const sanitizedQuery = sanitizeObject(req.query, options);
506
- Object.defineProperty(req, "query", { value: sanitizedQuery, writable: true, configurable: true });
507
- }
508
- if (req.params && typeof req.params === "object") {
509
- const sanitizedParams = sanitizeObject(req.params, options);
510
- Object.defineProperty(req, "params", { value: sanitizedParams, writable: true, configurable: true });
511
- }
512
- next();
513
- } catch (err) {
514
- next(err);
515
- }
516
- };
517
- }
518
-
519
- // src/sanitizers/nosql.ts
520
- function isDangerousNoSqlKey(key) {
521
- return NOSQL_DANGEROUS_KEYS.has(key);
522
- }
523
- function detectNoSqlInjection(obj, maxDepth = 10) {
524
- if (maxDepth <= 0) return false;
525
- if (obj === null || typeof obj !== "object") return false;
526
- if (Array.isArray(obj)) {
527
- return obj.some((item) => detectNoSqlInjection(item, maxDepth - 1));
528
- }
529
- for (const key of Object.keys(obj)) {
530
- if (isDangerousNoSqlKey(key)) {
531
- return true;
532
- }
533
- const value = obj[key];
534
- if (typeof value === "object" && value !== null) {
535
- if (detectNoSqlInjection(value, maxDepth - 1)) {
536
- return true;
537
- }
538
- }
539
- }
540
- return false;
541
- }
542
- function getDangerousOperators() {
543
- return Array.from(NOSQL_DANGEROUS_KEYS);
544
- }
545
-
546
- // src/sanitizers/prototype.ts
547
- function isDangerousProtoKey(key) {
548
- return DANGEROUS_PROTO_KEYS.has(key.toLowerCase());
549
- }
550
- function detectPrototypePollution(obj, maxDepth = 10) {
551
- if (maxDepth <= 0) return false;
552
- if (obj === null || typeof obj !== "object") return false;
553
- if (Array.isArray(obj)) {
554
- return obj.some((item) => detectPrototypePollution(item, maxDepth - 1));
555
- }
556
- for (const key of Object.keys(obj)) {
557
- if (DANGEROUS_PROTO_KEYS.has(key.toLowerCase())) {
558
- return true;
559
- }
560
- const value = obj[key];
561
- if (typeof value === "object" && value !== null) {
562
- if (detectPrototypePollution(value, maxDepth - 1)) {
563
- return true;
564
- }
565
- }
566
- }
567
- return false;
568
- }
569
- function getDangerousProtoKeys() {
570
- return Array.from(DANGEROUS_PROTO_KEYS);
571
- }
572
-
573
432
  // src/sanitizers/ssti.ts
574
433
  var SSTI_DETECT_PATTERNS = [
575
434
  /** Jinja2 / Twig / Nunjucks: {{ ... }} */
@@ -732,42 +591,36 @@ function detectXxe(input) {
732
591
  return false;
733
592
  }
734
593
 
735
- // src/sanitizers/jsonp.ts
736
- var SAFE_CALLBACK_PATTERN = /^[a-zA-Z_$][a-zA-Z0-9_$.]*$/;
737
- var DANGEROUS_CALLBACK_PATTERNS = [
738
- /\.\./
739
- // prototype chain traversal
740
- ];
741
- function sanitizeJsonpCallback(callback, maxLength = 128) {
742
- if (typeof callback !== "string" || callback.length === 0) {
743
- return null;
744
- }
745
- if (callback.length > maxLength) {
746
- return null;
747
- }
748
- if (!SAFE_CALLBACK_PATTERN.test(callback)) {
749
- return null;
750
- }
751
- for (const pattern of DANGEROUS_CALLBACK_PATTERNS) {
752
- if (pattern.test(callback)) {
753
- return null;
754
- }
755
- }
756
- return callback;
594
+ // src/sanitizers/ldap.ts
595
+ var LDAP_FILTER_CHARS = /[*()\\\x00]/g;
596
+ var LDAP_DN_CHARS = /[,+<>;"=\/\\\x00*()\x00]/g;
597
+ var LDAP_DETECT_PATTERN = /[*()\\\x00]/;
598
+ var LDAP_INJECTION_PATTERN = /\)\s*\(|\*\s*\)\s*\(/;
599
+ var escapeChar = (char) => "\\" + char.charCodeAt(0).toString(16).padStart(2, "0");
600
+ function sanitizeLdapFilter(input) {
601
+ if (typeof input !== "string") return String(input);
602
+ return input.replace(LDAP_FILTER_CHARS, escapeChar);
757
603
  }
758
- function detectJsonpInjection(callback) {
759
- if (typeof callback !== "string" || callback.length === 0) {
760
- return false;
761
- }
762
- if (!SAFE_CALLBACK_PATTERN.test(callback)) {
763
- return true;
764
- }
765
- for (const pattern of DANGEROUS_CALLBACK_PATTERNS) {
766
- if (pattern.test(callback)) {
767
- return true;
768
- }
769
- }
770
- return false;
604
+ function sanitizeLdapDn(input) {
605
+ if (typeof input !== "string") return String(input);
606
+ return input.replace(LDAP_DN_CHARS, escapeChar);
607
+ }
608
+ function detectLdapInjection(input) {
609
+ if (typeof input !== "string") return false;
610
+ return LDAP_DETECT_PATTERN.test(input) || LDAP_INJECTION_PATTERN.test(input);
611
+ }
612
+
613
+ // src/sanitizers/xpath.ts
614
+ var XPATH_INJECTION_CHARS = /['"|,()]/;
615
+ var XPATH_INJECTION_PATTERN = /('\s*(or|and)\s*'|"\s*(or|and)\s*"|\)\s*(or|and)\s*\(|\|\s*\/)/i;
616
+ function detectXpathInjection(input) {
617
+ if (typeof input !== "string" || input.length === 0) return false;
618
+ if (!XPATH_INJECTION_CHARS.test(input)) return false;
619
+ return XPATH_INJECTION_PATTERN.test(input);
620
+ }
621
+ function sanitizeXpath(input) {
622
+ if (typeof input !== "string") return String(input);
623
+ return input.replace(/['"|,]/g, "");
771
624
  }
772
625
 
773
626
  // src/sanitizers/headers.ts
@@ -818,6 +671,263 @@ function detectHeaderInjection(input) {
818
671
  HEADER_INJECTION_PATTERN.lastIndex = 0;
819
672
  return HEADER_INJECTION_PATTERN.test(input);
820
673
  }
674
+ var sanitizeEmailHeader = sanitizeHeaderValue;
675
+ var detectEmailHeaderInjection = detectHeaderInjection;
676
+
677
+ // src/sanitizers/sanitize.ts
678
+ function sanitizeString(value, options = {}) {
679
+ if (typeof value !== "string") return value;
680
+ const maxSize = options.maxSize ?? INPUT.DEFAULT_MAX_SIZE;
681
+ if (value.length > maxSize) {
682
+ throw new InputTooLargeError(maxSize, value.length);
683
+ }
684
+ const reject = options.mode === "reject";
685
+ let result = value;
686
+ if (options.sql !== false) {
687
+ if (reject) {
688
+ if (detectSql(result)) {
689
+ throw new SecurityThreatError("sql_injection", "SQL pattern detected in input");
690
+ }
691
+ } else {
692
+ result = sanitizeSql(result);
693
+ }
694
+ }
695
+ if (options.path !== false) {
696
+ result = sanitizePath(result);
697
+ }
698
+ if (options.command !== false) {
699
+ if (reject) {
700
+ if (detectCommandInjection(result)) {
701
+ throw new SecurityThreatError("command_injection", "Shell metacharacter detected in input");
702
+ }
703
+ } else {
704
+ result = sanitizeCommand(result);
705
+ }
706
+ }
707
+ if (options.xss !== false) {
708
+ result = sanitizeXss(result, false, options.htmlEncode ?? false);
709
+ }
710
+ return result;
711
+ }
712
+ function sanitizeObject(obj, options = {}) {
713
+ if (obj === null || obj === void 0) return obj;
714
+ if (typeof obj === "string") return sanitizeString(obj, options);
715
+ if (typeof obj !== "object") return obj;
716
+ if (Array.isArray(obj)) return obj.map((item) => sanitizeObject(item, options));
717
+ const result = sanitizeObjectDepth(obj, options, 0);
718
+ return options.freeze ? Object.freeze(result) : result;
719
+ }
720
+ function sanitizeObjectDepth(obj, options, depth) {
721
+ if (depth >= INPUT.MAX_RECURSION_DEPTH) return obj;
722
+ const result = {};
723
+ for (const key of Object.keys(obj)) {
724
+ if (options.proto !== false && DANGEROUS_PROTO_KEYS.has(key.toLowerCase())) {
725
+ continue;
726
+ }
727
+ if (options.nosql !== false && NOSQL_DANGEROUS_KEYS.has(key)) {
728
+ continue;
729
+ }
730
+ const sanitizedKey = sanitizeString(key, options);
731
+ const value = obj[key];
732
+ if (value === null || value === void 0) {
733
+ result[sanitizedKey] = value;
734
+ } else if (typeof value === "string") {
735
+ result[sanitizedKey] = sanitizeString(value, options);
736
+ } else if (Array.isArray(value)) {
737
+ result[sanitizedKey] = value.map((item) => sanitizeObject(item, options));
738
+ } else if (typeof value === "object") {
739
+ result[sanitizedKey] = sanitizeObjectDepth(value, options, depth + 1);
740
+ } else {
741
+ result[sanitizedKey] = value;
742
+ }
743
+ }
744
+ return result;
745
+ }
746
+ function scanThreats(data, depth = 0) {
747
+ if (depth > INPUT.MAX_RECURSION_DEPTH) return null;
748
+ if (data && typeof data === "object" && !Array.isArray(data)) {
749
+ for (const key of Object.keys(data)) {
750
+ const lower = key.toLowerCase();
751
+ if (DANGEROUS_PROTO_KEYS.has(lower)) {
752
+ return { vector: "prototype", rule: "prototype/match", matchedPattern: key };
753
+ }
754
+ if (NOSQL_DANGEROUS_KEYS.has(key)) {
755
+ return { vector: "nosql", rule: "nosql/match", matchedPattern: key };
756
+ }
757
+ const inner = scanThreats(data[key], depth + 1);
758
+ if (inner) return inner;
759
+ }
760
+ return null;
761
+ }
762
+ if (Array.isArray(data)) {
763
+ for (const item of data) {
764
+ const inner = scanThreats(item, depth + 1);
765
+ if (inner) return inner;
766
+ }
767
+ return null;
768
+ }
769
+ if (typeof data !== "string") return null;
770
+ const sample = data.slice(0, 80);
771
+ if (detectXss(data)) {
772
+ return { vector: "xss", rule: "xss/match", matchedPattern: sample };
773
+ }
774
+ if (detectSsti(data)) {
775
+ return { vector: "ssti", rule: "ssti/match", matchedPattern: sample };
776
+ }
777
+ if (detectXxe(data)) {
778
+ return { vector: "xxe", rule: "xxe/match", matchedPattern: sample };
779
+ }
780
+ if (detectSql(data)) {
781
+ return { vector: "sql", rule: "sql/match", matchedPattern: sample };
782
+ }
783
+ if (detectPathTraversal(data)) {
784
+ return { vector: "path", rule: "path/match", matchedPattern: sample };
785
+ }
786
+ if (detectCommandInjection(data)) {
787
+ return { vector: "command", rule: "command/match", matchedPattern: sample };
788
+ }
789
+ if (detectLdapInjection(data)) {
790
+ return { vector: "ldap", rule: "ldap/match", matchedPattern: sample };
791
+ }
792
+ if (detectXpathInjection(data)) {
793
+ return { vector: "xpath", rule: "xpath/match", matchedPattern: sample };
794
+ }
795
+ if (detectHeaderInjection(data)) {
796
+ return { vector: "header", rule: "header/match", matchedPattern: sample };
797
+ }
798
+ return null;
799
+ }
800
+ function createSanitizer(options = {}) {
801
+ return (req, res, next) => {
802
+ try {
803
+ if (options.block) {
804
+ const hit = scanThreats(req.body) || scanThreats(req.query) || scanThreats(req.params) || scanThreats(req.path);
805
+ if (hit) {
806
+ req.__arcis = {
807
+ vector: hit.vector,
808
+ rule: hit.rule,
809
+ severity: "high",
810
+ matchedPattern: hit.matchedPattern,
811
+ reason: `${hit.vector} pattern detected in request`,
812
+ decision: "deny"
813
+ };
814
+ res.status(403).json({
815
+ error: "Request blocked for security reasons",
816
+ code: "SECURITY_THREAT",
817
+ vector: hit.vector
818
+ });
819
+ return;
820
+ }
821
+ }
822
+ if (req.body && typeof req.body === "object") {
823
+ req.body = sanitizeObject(req.body, options);
824
+ }
825
+ if (req.query && typeof req.query === "object") {
826
+ const sanitizedQuery = sanitizeObject(req.query, options);
827
+ Object.defineProperty(req, "query", { value: sanitizedQuery, writable: true, configurable: true });
828
+ }
829
+ if (req.params && typeof req.params === "object") {
830
+ const sanitizedParams = sanitizeObject(req.params, options);
831
+ Object.defineProperty(req, "params", { value: sanitizedParams, writable: true, configurable: true });
832
+ }
833
+ next();
834
+ } catch (err) {
835
+ next(err);
836
+ }
837
+ };
838
+ }
839
+
840
+ // src/sanitizers/nosql.ts
841
+ function isDangerousNoSqlKey(key) {
842
+ return NOSQL_DANGEROUS_KEYS.has(key);
843
+ }
844
+ function detectNoSqlInjection(obj, maxDepth = 10) {
845
+ if (maxDepth <= 0) return false;
846
+ if (obj === null || typeof obj !== "object") return false;
847
+ if (Array.isArray(obj)) {
848
+ return obj.some((item) => detectNoSqlInjection(item, maxDepth - 1));
849
+ }
850
+ for (const key of Object.keys(obj)) {
851
+ if (isDangerousNoSqlKey(key)) {
852
+ return true;
853
+ }
854
+ const value = obj[key];
855
+ if (typeof value === "object" && value !== null) {
856
+ if (detectNoSqlInjection(value, maxDepth - 1)) {
857
+ return true;
858
+ }
859
+ }
860
+ }
861
+ return false;
862
+ }
863
+ function getDangerousOperators() {
864
+ return Array.from(NOSQL_DANGEROUS_KEYS);
865
+ }
866
+
867
+ // src/sanitizers/prototype.ts
868
+ function isDangerousProtoKey(key) {
869
+ return DANGEROUS_PROTO_KEYS.has(key.toLowerCase());
870
+ }
871
+ function detectPrototypePollution(obj, maxDepth = 10) {
872
+ if (maxDepth <= 0) return false;
873
+ if (obj === null || typeof obj !== "object") return false;
874
+ if (Array.isArray(obj)) {
875
+ return obj.some((item) => detectPrototypePollution(item, maxDepth - 1));
876
+ }
877
+ for (const key of Object.keys(obj)) {
878
+ if (DANGEROUS_PROTO_KEYS.has(key.toLowerCase())) {
879
+ return true;
880
+ }
881
+ const value = obj[key];
882
+ if (typeof value === "object" && value !== null) {
883
+ if (detectPrototypePollution(value, maxDepth - 1)) {
884
+ return true;
885
+ }
886
+ }
887
+ }
888
+ return false;
889
+ }
890
+ function getDangerousProtoKeys() {
891
+ return Array.from(DANGEROUS_PROTO_KEYS);
892
+ }
893
+
894
+ // src/sanitizers/jsonp.ts
895
+ var SAFE_CALLBACK_PATTERN = /^[a-zA-Z_$][a-zA-Z0-9_$.]*$/;
896
+ var DANGEROUS_CALLBACK_PATTERNS = [
897
+ /\.\./
898
+ // prototype chain traversal
899
+ ];
900
+ function sanitizeJsonpCallback(callback, maxLength = 128) {
901
+ if (typeof callback !== "string" || callback.length === 0) {
902
+ return null;
903
+ }
904
+ if (callback.length > maxLength) {
905
+ return null;
906
+ }
907
+ if (!SAFE_CALLBACK_PATTERN.test(callback)) {
908
+ return null;
909
+ }
910
+ for (const pattern of DANGEROUS_CALLBACK_PATTERNS) {
911
+ if (pattern.test(callback)) {
912
+ return null;
913
+ }
914
+ }
915
+ return callback;
916
+ }
917
+ function detectJsonpInjection(callback) {
918
+ if (typeof callback !== "string" || callback.length === 0) {
919
+ return false;
920
+ }
921
+ if (!SAFE_CALLBACK_PATTERN.test(callback)) {
922
+ return true;
923
+ }
924
+ for (const pattern of DANGEROUS_CALLBACK_PATTERNS) {
925
+ if (pattern.test(callback)) {
926
+ return true;
927
+ }
928
+ }
929
+ return false;
930
+ }
821
931
 
822
932
  // src/sanitizers/pii.ts
823
933
  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;
@@ -984,6 +1094,7 @@ function encodeForJs(value) {
984
1094
  let result = "";
985
1095
  for (const char of value) {
986
1096
  const cp = char.codePointAt(0);
1097
+ if (cp === void 0) continue;
987
1098
  if (cp >= 48 && cp <= 57 || // 0-9
988
1099
  cp >= 65 && cp <= 90 || // A-Z
989
1100
  cp >= 97 && cp <= 122) {
@@ -1020,25 +1131,49 @@ function encodeForCss(value) {
1020
1131
  return result;
1021
1132
  }
1022
1133
 
1023
- // src/sanitizers/ldap.ts
1024
- var LDAP_FILTER_CHARS = /[*()\\\x00]/g;
1025
- var LDAP_DN_CHARS = /[,+<>;"=\/\\\x00*()\x00]/g;
1026
- var LDAP_DETECT_PATTERN = /[*()\\\x00]/;
1027
- var LDAP_INJECTION_PATTERN = /\)\s*\(|\*\s*\)\s*\(/;
1028
- var escapeChar = (char) => "\\" + char.charCodeAt(0).toString(16).padStart(2, "0");
1029
- function sanitizeLdapFilter(input) {
1030
- if (typeof input !== "string") return String(input);
1031
- return input.replace(LDAP_FILTER_CHARS, escapeChar);
1134
+ // src/sanitizers/graphql.ts
1135
+ var DEFAULTS = {
1136
+ maxDepth: 10,
1137
+ maxLength: 1e4,
1138
+ blockIntrospection: true
1139
+ };
1140
+ var INTROSPECTION_PATTERN = /\b__(schema|type|typeKind|directive)\b/;
1141
+ function computeDepth(query) {
1142
+ let depth = 0;
1143
+ let max = 0;
1144
+ for (let i = 0; i < query.length; i++) {
1145
+ const c = query.charCodeAt(i);
1146
+ if (c === 123) {
1147
+ depth++;
1148
+ if (depth > max) max = depth;
1149
+ } else if (c === 125) {
1150
+ if (depth > 0) depth--;
1151
+ }
1152
+ }
1153
+ return max;
1032
1154
  }
1033
- function sanitizeLdapDn(input) {
1034
- if (typeof input !== "string") return String(input);
1035
- return input.replace(LDAP_DN_CHARS, escapeChar);
1155
+ function inspectGraphqlQuery(query, options = {}) {
1156
+ const maxDepth = options.maxDepth ?? DEFAULTS.maxDepth;
1157
+ const maxLength = options.maxLength ?? DEFAULTS.maxLength;
1158
+ const blockIntrospection = options.blockIntrospection ?? DEFAULTS.blockIntrospection;
1159
+ const length = query.length;
1160
+ const depth = computeDepth(query);
1161
+ if (depth > maxDepth) {
1162
+ return { blocked: true, reason: "depth", depth, length };
1163
+ }
1164
+ if (blockIntrospection && INTROSPECTION_PATTERN.test(query)) {
1165
+ return { blocked: true, reason: "introspection", depth, length };
1166
+ }
1167
+ if (length > maxLength) {
1168
+ return { blocked: true, reason: "length", depth, length };
1169
+ }
1170
+ return { blocked: false, depth, length };
1036
1171
  }
1037
- function detectLdapInjection(input) {
1038
- if (typeof input !== "string") return false;
1039
- return LDAP_DETECT_PATTERN.test(input) || LDAP_INJECTION_PATTERN.test(input);
1172
+ function detectGraphqlAbuse(query, options) {
1173
+ if (typeof query !== "string" || query.length === 0) return false;
1174
+ return inspectGraphqlQuery(query, options).blocked;
1040
1175
  }
1041
1176
 
1042
- export { createSanitizer, detectCommandInjection, detectHeaderInjection, detectJsonpInjection, detectLdapInjection, detectNoSqlInjection, detectPathTraversal, detectPii, detectPrototypePollution, detectSql, detectSsti, detectXss, detectXxe, encodeForAttribute, encodeForCss, encodeForHtml, encodeForJs, encodeForUrl, encodeHtmlEntities, getDangerousOperators, getDangerousProtoKeys, isDangerousNoSqlKey, isDangerousProtoKey, isPlainObject, redactObjectPii, redactPii, sanitizeCommand, sanitizeHeaderValue, sanitizeHeaders, sanitizeJsonpCallback, sanitizeLdapDn, sanitizeLdapFilter, sanitizeObject, sanitizePath, sanitizeSql, sanitizeSsti, sanitizeString, sanitizeXss, sanitizeXxe, scanObjectPii, scanPii };
1177
+ export { createSanitizer, detectCommandInjection, detectEmailHeaderInjection, detectGraphqlAbuse, detectHeaderInjection, detectJsonpInjection, detectLdapInjection, detectNoSqlInjection, detectPathTraversal, detectPii, detectPrototypePollution, detectSql, detectSsti, detectXpathInjection, detectXss, detectXxe, encodeForAttribute, encodeForCss, encodeForHtml, encodeForJs, encodeForUrl, encodeHtmlEntities, getDangerousOperators, getDangerousProtoKeys, inspectGraphqlQuery, isDangerousNoSqlKey, isDangerousProtoKey, isPlainObject, redactObjectPii, redactPii, sanitizeCommand, sanitizeEmailHeader, sanitizeHeaderValue, sanitizeHeaders, sanitizeJsonpCallback, sanitizeLdapDn, sanitizeLdapFilter, sanitizeObject, sanitizePath, sanitizeSql, sanitizeSsti, sanitizeString, sanitizeXpath, sanitizeXss, sanitizeXxe, scanObjectPii, scanPii, scanThreats };
1043
1178
  //# sourceMappingURL=index.mjs.map
1044
1179
  //# sourceMappingURL=index.mjs.map