@arcis/node 1.3.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 (139) hide show
  1. package/README.md +1 -1
  2. package/dist/core/{index.d.mts → constants.d.ts} +21 -70
  3. package/dist/core/constants.d.ts.map +1 -0
  4. package/dist/core/errors.d.ts +53 -0
  5. package/dist/core/errors.d.ts.map +1 -0
  6. package/dist/core/index.d.ts +6 -168
  7. package/dist/core/index.d.ts.map +1 -0
  8. package/dist/core/index.js +11 -3
  9. package/dist/core/index.js.map +1 -1
  10. package/dist/core/index.mjs +11 -3
  11. package/dist/core/index.mjs.map +1 -1
  12. package/dist/{types-BOkx5YJc.d.mts → core/types.d.ts} +27 -30
  13. package/dist/core/types.d.ts.map +1 -0
  14. package/dist/index.d.ts +71 -166
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +182 -48
  17. package/dist/index.js.map +1 -1
  18. package/dist/index.mjs +182 -50
  19. package/dist/index.mjs.map +1 -1
  20. package/dist/logging/index.d.ts +4 -36
  21. package/dist/logging/index.d.ts.map +1 -0
  22. package/dist/logging/index.js.map +1 -1
  23. package/dist/logging/index.mjs.map +1 -1
  24. package/dist/logging/{index.d.mts → redactor.d.ts} +5 -9
  25. package/dist/logging/redactor.d.ts.map +1 -0
  26. package/dist/middleware/bot-detection.d.ts +86 -0
  27. package/dist/middleware/bot-detection.d.ts.map +1 -0
  28. package/dist/middleware/cookies.d.ts +48 -0
  29. package/dist/middleware/cookies.d.ts.map +1 -0
  30. package/dist/middleware/cors.d.ts +65 -0
  31. package/dist/middleware/cors.d.ts.map +1 -0
  32. package/dist/middleware/csrf.d.ts +109 -0
  33. package/dist/middleware/csrf.d.ts.map +1 -0
  34. package/dist/middleware/error-handler.d.ts +43 -0
  35. package/dist/middleware/error-handler.d.ts.map +1 -0
  36. package/dist/middleware/headers.d.ts +29 -0
  37. package/dist/middleware/headers.d.ts.map +1 -0
  38. package/dist/middleware/hpp.d.ts +56 -0
  39. package/dist/middleware/hpp.d.ts.map +1 -0
  40. package/dist/middleware/index.d.ts +16 -3
  41. package/dist/middleware/index.d.ts.map +1 -0
  42. package/dist/middleware/index.js +68 -31
  43. package/dist/middleware/index.js.map +1 -1
  44. package/dist/middleware/index.mjs +69 -32
  45. package/dist/middleware/index.mjs.map +1 -1
  46. package/dist/middleware/main.d.ts +40 -0
  47. package/dist/middleware/main.d.ts.map +1 -0
  48. package/dist/middleware/rate-limit-sliding.d.ts +46 -0
  49. package/dist/middleware/rate-limit-sliding.d.ts.map +1 -0
  50. package/dist/middleware/rate-limit-token.d.ts +51 -0
  51. package/dist/middleware/rate-limit-token.d.ts.map +1 -0
  52. package/dist/middleware/rate-limit.d.ts +34 -0
  53. package/dist/middleware/rate-limit.d.ts.map +1 -0
  54. package/dist/sanitizers/command.d.ts +28 -0
  55. package/dist/sanitizers/command.d.ts.map +1 -0
  56. package/dist/sanitizers/encode.d.ts +46 -0
  57. package/dist/sanitizers/encode.d.ts.map +1 -0
  58. package/dist/sanitizers/headers.d.ts +46 -0
  59. package/dist/sanitizers/headers.d.ts.map +1 -0
  60. package/dist/sanitizers/index.d.ts +18 -22
  61. package/dist/sanitizers/index.d.ts.map +1 -0
  62. package/dist/sanitizers/index.js +90 -32
  63. package/dist/sanitizers/index.js.map +1 -1
  64. package/dist/sanitizers/index.mjs +88 -33
  65. package/dist/sanitizers/index.mjs.map +1 -1
  66. package/dist/sanitizers/jsonp.d.ts +34 -0
  67. package/dist/sanitizers/jsonp.d.ts.map +1 -0
  68. package/dist/sanitizers/ldap.d.ts +42 -0
  69. package/dist/sanitizers/ldap.d.ts.map +1 -0
  70. package/dist/sanitizers/nosql.d.ts +31 -0
  71. package/dist/sanitizers/nosql.d.ts.map +1 -0
  72. package/dist/sanitizers/path.d.ts +28 -0
  73. package/dist/sanitizers/path.d.ts.map +1 -0
  74. package/dist/sanitizers/pii.d.ts +80 -0
  75. package/dist/sanitizers/pii.d.ts.map +1 -0
  76. package/dist/sanitizers/prototype.d.ts +34 -0
  77. package/dist/sanitizers/prototype.d.ts.map +1 -0
  78. package/dist/sanitizers/sanitize.d.ts +51 -0
  79. package/dist/sanitizers/sanitize.d.ts.map +1 -0
  80. package/dist/sanitizers/sql.d.ts +28 -0
  81. package/dist/sanitizers/sql.d.ts.map +1 -0
  82. package/dist/sanitizers/ssti.d.ts +20 -0
  83. package/dist/sanitizers/ssti.d.ts.map +1 -0
  84. package/dist/sanitizers/utils.d.ts +19 -0
  85. package/dist/sanitizers/utils.d.ts.map +1 -0
  86. package/dist/sanitizers/xss.d.ts +35 -0
  87. package/dist/sanitizers/xss.d.ts.map +1 -0
  88. package/dist/sanitizers/xxe.d.ts +20 -0
  89. package/dist/sanitizers/xxe.d.ts.map +1 -0
  90. package/dist/stores/index.d.ts +6 -104
  91. package/dist/stores/index.d.ts.map +1 -0
  92. package/dist/stores/index.js +21 -1
  93. package/dist/stores/index.js.map +1 -1
  94. package/dist/stores/index.mjs +21 -1
  95. package/dist/stores/index.mjs.map +1 -1
  96. package/dist/stores/memory.d.ts +29 -0
  97. package/dist/stores/memory.d.ts.map +1 -0
  98. package/dist/stores/{index.d.mts → redis.d.ts} +6 -45
  99. package/dist/stores/redis.d.ts.map +1 -0
  100. package/dist/utils/duration.d.ts +34 -0
  101. package/dist/utils/duration.d.ts.map +1 -0
  102. package/dist/utils/fingerprint.d.ts +64 -0
  103. package/dist/utils/fingerprint.d.ts.map +1 -0
  104. package/dist/utils/index.d.ts +10 -0
  105. package/dist/utils/index.d.ts.map +1 -0
  106. package/dist/utils/index.js +188 -0
  107. package/dist/utils/index.js.map +1 -0
  108. package/dist/utils/index.mjs +182 -0
  109. package/dist/utils/index.mjs.map +1 -0
  110. package/dist/utils/ip.d.ts +70 -0
  111. package/dist/utils/ip.d.ts.map +1 -0
  112. package/dist/validation/email.d.ts +82 -0
  113. package/dist/validation/email.d.ts.map +1 -0
  114. package/dist/validation/file.d.ts +90 -0
  115. package/dist/validation/file.d.ts.map +1 -0
  116. package/dist/validation/index.d.ts +10 -3
  117. package/dist/validation/index.d.ts.map +1 -0
  118. package/dist/validation/index.js +38 -21
  119. package/dist/validation/index.js.map +1 -1
  120. package/dist/validation/index.mjs +38 -21
  121. package/dist/validation/index.mjs.map +1 -1
  122. package/dist/validation/redirect.d.ts +64 -0
  123. package/dist/validation/redirect.d.ts.map +1 -0
  124. package/dist/validation/schema.d.ts +36 -0
  125. package/dist/validation/schema.d.ts.map +1 -0
  126. package/dist/validation/url.d.ts +65 -0
  127. package/dist/validation/url.d.ts.map +1 -0
  128. package/package.json +8 -6
  129. package/dist/encode-CrQCGlBq.d.mts +0 -484
  130. package/dist/encode-jl9sOwmA.d.ts +0 -484
  131. package/dist/index-BAhgn9V2.d.ts +0 -532
  132. package/dist/index-BGNKspqH.d.ts +0 -340
  133. package/dist/index-Cd02z-0j.d.mts +0 -340
  134. package/dist/index-DgJtWMSj.d.mts +0 -532
  135. package/dist/index.d.mts +0 -175
  136. package/dist/middleware/index.d.mts +0 -3
  137. package/dist/sanitizers/index.d.mts +0 -24
  138. package/dist/types-BOkx5YJc.d.ts +0 -279
  139. package/dist/validation/index.d.mts +0 -3
@@ -1,4 +1,4 @@
1
- import { randomBytes } from 'crypto';
1
+ import { randomBytes, timingSafeEqual } from 'crypto';
2
2
 
3
3
  // src/core/constants.ts
4
4
  var INPUT = {
@@ -65,7 +65,15 @@ var XSS_REMOVE_PATTERNS = [
65
65
  /javascript\s*:/gi,
66
66
  /vbscript\s*:/gi,
67
67
  /** data: URIs with HTML/script content */
68
- /data\s*:\s*text\/html[^>\s]*/gi
68
+ /data\s*:\s*text\/html[^>\s]*/gi,
69
+ /** form tag injection — phishing via action= redirection */
70
+ /<form[\s>][^>]*/gi,
71
+ /** meta tag injection — http-equiv refresh or CSP bypass */
72
+ /<meta[\s>][^>]*/gi,
73
+ /** base href hijacking */
74
+ /<base[\s>][^>]*/gi,
75
+ /** link tag injection — stylesheet or preload attacks */
76
+ /<link[\s>][^>]*/gi
69
77
  ];
70
78
  var SQL_PATTERNS = [
71
79
  /** SQL keywords */
@@ -129,8 +137,8 @@ var COMMAND_PATTERNS = [
129
137
  /[;&|`]/g,
130
138
  /** Command substitution: $( ... ) — matched as a pair to reduce false positives */
131
139
  /\$\(/g,
132
- /** URL-encoded newline/carriage-return injection (%0a, %0d) */
133
- /%0[ad]/gi
140
+ /** URL-encoded control characters (%00-%0F): null, tab, vtab, formfeed, LF, CR */
141
+ /%0[0-9a-f]/gi
134
142
  ];
135
143
  var DANGEROUS_PROTO_KEYS = /* @__PURE__ */ new Set([
136
144
  "__proto__",
@@ -418,7 +426,24 @@ function createRateLimiter(options = {}) {
418
426
  }
419
427
  next();
420
428
  } catch (error) {
421
- console.error("[arcis] Rate limiter error:", error);
429
+ console.error("[arcis] Rate limiter store error, using in-memory fallback:", error);
430
+ try {
431
+ const key = keyGenerator(req);
432
+ const now = Date.now();
433
+ if (!inMemoryStore[key] || inMemoryStore[key].resetTime < now) {
434
+ inMemoryStore[key] = { count: 1, resetTime: now + windowMs };
435
+ } else {
436
+ inMemoryStore[key].count++;
437
+ }
438
+ const count = inMemoryStore[key].count;
439
+ if (count > max) {
440
+ const resetSeconds = Math.ceil((inMemoryStore[key].resetTime - now) / 1e3);
441
+ res.setHeader("Retry-After", resetSeconds.toString());
442
+ res.status(statusCode).json({ error: message, retryAfter: resetSeconds });
443
+ return;
444
+ }
445
+ } catch {
446
+ }
422
447
  next();
423
448
  }
424
449
  };
@@ -628,26 +653,31 @@ function sanitizePath(input, collectThreats = false) {
628
653
  const threats = [];
629
654
  let value = input;
630
655
  let wasSanitized = false;
631
- for (const pattern of PATH_PATTERNS) {
632
- pattern.lastIndex = 0;
633
- if (pattern.test(value)) {
656
+ value = value.normalize("NFKC");
657
+ let prev;
658
+ do {
659
+ prev = value;
660
+ for (const pattern of PATH_PATTERNS) {
634
661
  pattern.lastIndex = 0;
635
- if (collectThreats) {
636
- const matches = value.match(pattern);
637
- if (matches) {
638
- for (const match of matches) {
639
- threats.push({
640
- type: "path_traversal",
641
- pattern: pattern.source,
642
- original: match
643
- });
662
+ if (pattern.test(value)) {
663
+ pattern.lastIndex = 0;
664
+ if (collectThreats) {
665
+ const matches = value.match(pattern);
666
+ if (matches) {
667
+ for (const match of matches) {
668
+ threats.push({
669
+ type: "path_traversal",
670
+ pattern: pattern.source,
671
+ original: match
672
+ });
673
+ }
644
674
  }
645
675
  }
676
+ value = value.replace(pattern, "");
677
+ wasSanitized = true;
646
678
  }
647
- value = value.replace(pattern, "");
648
- wasSanitized = true;
649
679
  }
650
- }
680
+ } while (value !== prev);
651
681
  if (collectThreats) {
652
682
  return { value, wasSanitized, threats };
653
683
  }
@@ -705,7 +735,7 @@ function sanitizeString(value, options = {}) {
705
735
  if (value.length > maxSize) {
706
736
  throw new InputTooLargeError(maxSize, value.length);
707
737
  }
708
- const reject = options.mode !== "sanitize";
738
+ const reject = options.mode === "reject";
709
739
  let result = value;
710
740
  if (options.sql !== false) {
711
741
  if (reject) {
@@ -1582,11 +1612,9 @@ function generateCsrfToken(length = 32) {
1582
1612
  function validateCsrfToken(cookieToken, requestToken) {
1583
1613
  if (!cookieToken || !requestToken) return false;
1584
1614
  if (cookieToken.length !== requestToken.length) return false;
1585
- let result = 0;
1586
- for (let i = 0; i < cookieToken.length; i++) {
1587
- result |= cookieToken.charCodeAt(i) ^ requestToken.charCodeAt(i);
1588
- }
1589
- return result === 0;
1615
+ const a = Buffer.from(cookieToken);
1616
+ const b = Buffer.from(requestToken);
1617
+ return timingSafeEqual(a, b);
1590
1618
  }
1591
1619
  function getRequestToken(req, headerName, fieldName) {
1592
1620
  const headerToken = req.headers[headerName.toLowerCase()];
@@ -1595,19 +1623,17 @@ function getRequestToken(req, headerName, fieldName) {
1595
1623
  const bodyToken = req.body[fieldName];
1596
1624
  if (typeof bodyToken === "string" && bodyToken) return bodyToken;
1597
1625
  }
1598
- if (req.query && fieldName in req.query) {
1599
- const queryToken = req.query[fieldName];
1600
- if (typeof queryToken === "string" && queryToken) return queryToken;
1601
- }
1602
1626
  return void 0;
1603
1627
  }
1604
1628
  function csrfProtection(options = {}) {
1605
- const cookieName = options.cookieName ?? DEFAULTS.cookieName;
1629
+ const baseCookieName = options.cookieName ?? DEFAULTS.cookieName;
1630
+ const cookieName = options.useHostPrefix ? `__Host-${baseCookieName}` : baseCookieName;
1606
1631
  const headerName = options.headerName ?? DEFAULTS.headerName;
1607
1632
  const fieldName = options.fieldName ?? DEFAULTS.fieldName;
1608
1633
  const tokenLength = options.tokenLength ?? DEFAULTS.tokenLength;
1609
1634
  const protectedMethods = options.protectedMethods ?? [...DEFAULTS.protectedMethods];
1610
1635
  const excludePaths = options.excludePaths ?? [];
1636
+ const skipCsrf = options.skipCsrf;
1611
1637
  const isProduction = process.env.NODE_ENV === "production";
1612
1638
  const cookieOpts = {
1613
1639
  path: options.cookie?.path ?? "/",
@@ -1627,6 +1653,9 @@ function csrfProtection(options = {}) {
1627
1653
  const protectedSet = new Set(protectedMethods.map((m) => m.toUpperCase()));
1628
1654
  return (req, res, next) => {
1629
1655
  const method = req.method.toUpperCase();
1656
+ if (skipCsrf && skipCsrf(req)) {
1657
+ return next();
1658
+ }
1630
1659
  const requestPath = req.path || req.url;
1631
1660
  if (excludePaths.some((p) => requestPath === p || requestPath.startsWith(p + "/"))) {
1632
1661
  return next();
@@ -1676,7 +1705,15 @@ function setCsrfCookie(res, name, token, opts) {
1676
1705
  if (opts.secure) parts.push("Secure");
1677
1706
  parts.push(`SameSite=${opts.sameSite}`);
1678
1707
  if (opts.domain) parts.push(`Domain=${opts.domain}`);
1679
- res.setHeader("Set-Cookie", parts.join("; "));
1708
+ const newCookie = parts.join("; ");
1709
+ const existing = res.getHeader("Set-Cookie");
1710
+ if (existing === void 0) {
1711
+ res.setHeader("Set-Cookie", newCookie);
1712
+ } else if (Array.isArray(existing)) {
1713
+ res.setHeader("Set-Cookie", [...existing, newCookie]);
1714
+ } else {
1715
+ res.setHeader("Set-Cookie", [existing, newCookie]);
1716
+ }
1680
1717
  }
1681
1718
  function escapeRegex(str) {
1682
1719
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");