@arcis/node 1.4.4 → 1.5.1

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 (144) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +36 -6
  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 +3 -2
  12. package/dist/core/constants.d.ts.map +1 -1
  13. package/dist/core/index.js +4 -3
  14. package/dist/core/index.js.map +1 -1
  15. package/dist/core/index.mjs +4 -3
  16. package/dist/core/index.mjs.map +1 -1
  17. package/dist/core/types.d.ts +32 -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 +7126 -178
  32. package/dist/index.js.map +1 -1
  33. package/dist/index.mjs +7088 -179
  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 +6469 -119
  58. package/dist/middleware/index.js.map +1 -1
  59. package/dist/middleware/index.mjs +6459 -120
  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 +4 -1
  106. package/dist/sanitizers/index.d.ts.map +1 -1
  107. package/dist/sanitizers/index.js +140 -66
  108. package/dist/sanitizers/index.js.map +1 -1
  109. package/dist/sanitizers/index.mjs +135 -67
  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 +1 -1
  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/validation/index.d.ts +2 -0
  128. package/dist/validation/index.d.ts.map +1 -1
  129. package/dist/validation/index.js +137 -12
  130. package/dist/validation/index.js.map +1 -1
  131. package/dist/validation/index.mjs +116 -13
  132. package/dist/validation/index.mjs.map +1 -1
  133. package/dist/validation/redirect.d.ts.map +1 -1
  134. package/dist/validation/schema.d.ts.map +1 -1
  135. package/dist/validation/url-async.d.ts +137 -0
  136. package/dist/validation/url-async.d.ts.map +1 -0
  137. package/package.json +57 -12
  138. package/scripts/postinstall.cjs +26 -0
  139. package/dist/cli/arcis.d.ts +0 -23
  140. package/dist/cli/arcis.d.ts.map +0 -1
  141. package/dist/cli/arcis.js +0 -312
  142. package/dist/cli/arcis.js.map +0 -1
  143. package/dist/cli/arcis.mjs +0 -309
  144. package/dist/cli/arcis.mjs.map +0 -1
@@ -1,3 +1,4 @@
1
+ import * as dns from 'dns';
1
2
  import { promises } from 'dns';
2
3
 
3
4
  // src/core/constants.ts
@@ -31,8 +32,8 @@ var XSS_REMOVE_PATTERNS = [
31
32
  /** javascript: and vbscript: protocols (allow optional spaces before colon) */
32
33
  /javascript\s*:/gi,
33
34
  /vbscript\s*:/gi,
34
- /** data: URIs with HTML/script content */
35
- /data\s*:\s*text\/html[^>\s]*/gi,
35
+ /** data: URIs with HTML or SVG content (SVG can run JS via inline event handlers) */
36
+ /data\s*:\s*(?:text\/html|image\/svg)[^>\s]*/gi,
36
37
  /** form tag injection — phishing via action= redirection */
37
38
  /<form[\s>][^>]*/gi,
38
39
  /** meta tag injection — http-equiv refresh or CSP bypass */
@@ -116,10 +117,11 @@ var VALIDATION = {
116
117
  EMAIL: /^[^\s@.][^\s@]*(?:\.[^\s@.][^\s@]*)*@[^\s@]+\.[^\s@]+$/,
117
118
  /**
118
119
  * URL regex pattern.
119
- * Only allows http:// and https:// explicitly rejects javascript:,
120
- * data:, vbscript:, and other dangerous URI schemes.
120
+ * Only allows http:// and https:// (case-insensitive scheme per
121
+ * RFC 3986); explicitly rejects javascript:, data:, vbscript:, and
122
+ * other dangerous URI schemes.
121
123
  */
122
- URL: /^https?:\/\/[^\s/$.?#][^\s]*$/,
124
+ URL: /^https?:\/\/[^\s/$.?#][^\s]*$/i,
123
125
  /** UUID regex pattern (v4) */
124
126
  UUID: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
125
127
  };
@@ -400,7 +402,12 @@ function validate(schema, source = "body") {
400
402
  res.status(400).json({ errors });
401
403
  return;
402
404
  }
403
- req[source] = validated;
405
+ Object.defineProperty(req, source, {
406
+ value: validated,
407
+ writable: true,
408
+ configurable: true,
409
+ enumerable: true
410
+ });
404
411
  next();
405
412
  };
406
413
  }
@@ -897,6 +904,99 @@ function checkOctalIp(hostname, allowLocalhost, allowPrivate) {
897
904
  }
898
905
  return null;
899
906
  }
907
+ var defaultLookup = (hostname) => new Promise((resolve, reject) => {
908
+ dns.lookup(hostname, { all: true }, (err, addresses) => {
909
+ if (err) reject(err);
910
+ else resolve(addresses);
911
+ });
912
+ });
913
+ function checkResolvedIp(ip, options) {
914
+ const isIpv6 = ip.includes(":");
915
+ const host = isIpv6 ? `[${ip}]` : ip;
916
+ const { allowedProtocols, allowLocalhost, allowPrivate } = options;
917
+ return validateUrl(`http://${host}/`, {
918
+ allowedProtocols,
919
+ allowLocalhost,
920
+ allowPrivate
921
+ });
922
+ }
923
+ async function validateUrlAsync(url, options = {}) {
924
+ const sync = validateUrl(url, options);
925
+ if (!sync.safe) return sync;
926
+ let parsed;
927
+ try {
928
+ parsed = new URL(url);
929
+ } catch {
930
+ return { safe: false, reason: "invalid URL: failed to parse" };
931
+ }
932
+ const host = parsed.hostname.replace(/^\[|\]$/g, "");
933
+ if (isLiteralIp(host)) return { safe: true };
934
+ if (options.allowedHosts?.some((h) => host.toLowerCase() === h.toLowerCase())) {
935
+ return { safe: true };
936
+ }
937
+ const lookup2 = options.lookup ?? defaultLookup;
938
+ let addresses;
939
+ try {
940
+ addresses = await lookup2(host);
941
+ } catch (err) {
942
+ const msg = err instanceof Error ? err.message : String(err);
943
+ return { safe: false, reason: `DNS lookup failed: ${msg}` };
944
+ }
945
+ if (addresses.length === 0) {
946
+ return { safe: false, reason: "DNS returned no addresses" };
947
+ }
948
+ const ips = addresses.map((a) => a.address);
949
+ const acceptFirstPublic = options.acceptFirstPublic === true;
950
+ let firstPublic;
951
+ for (const ip of ips) {
952
+ const ipResult = checkResolvedIp(ip, options);
953
+ if (ipResult.safe) {
954
+ if (firstPublic === void 0) firstPublic = ip;
955
+ } else if (!acceptFirstPublic) {
956
+ return {
957
+ safe: false,
958
+ reason: `resolved IP ${ip} is unsafe: ${ipResult.reason}`,
959
+ resolvedIps: ips
960
+ };
961
+ }
962
+ }
963
+ if (firstPublic === void 0) {
964
+ return {
965
+ safe: false,
966
+ reason: "all resolved IPs are private/loopback",
967
+ resolvedIps: ips
968
+ };
969
+ }
970
+ return { safe: true, resolvedIp: firstPublic, resolvedIps: ips };
971
+ }
972
+ function pinnedDnsLookup(ip) {
973
+ if (typeof ip !== "string" || ip.trim() === "") {
974
+ throw new TypeError("pinnedDnsLookup: ip must be a non-empty string");
975
+ }
976
+ const family = ip.includes(":") ? 6 : 4;
977
+ return (_hostname, _options, callback) => {
978
+ callback(null, ip, family);
979
+ };
980
+ }
981
+ async function safeFollowRedirect(prev, location, options = {}) {
982
+ if (typeof location !== "string" || location.trim() === "") {
983
+ return { safe: false, reason: "redirect Location is empty" };
984
+ }
985
+ let absolute;
986
+ try {
987
+ absolute = new URL(location, prev);
988
+ } catch {
989
+ return { safe: false, reason: "invalid redirect URL" };
990
+ }
991
+ return validateUrlAsync(absolute.toString(), options);
992
+ }
993
+ function isLiteralIp(host) {
994
+ if (/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(host)) return true;
995
+ if (/^\d+$/.test(host)) return true;
996
+ if (/^0[0-7x]/.test(host) && /^[0-9a-fx.]+$/i.test(host) && host.includes(".")) return true;
997
+ if (host.includes(":") && !/[/\s]/.test(host)) return true;
998
+ return false;
999
+ }
900
1000
 
901
1001
  // src/validation/redirect.ts
902
1002
  var DANGEROUS_PROTOCOLS = /^(javascript|data|vbscript|blob):/i;
@@ -911,8 +1011,8 @@ function validateRedirect(url, options = {}) {
911
1011
  return { safe: false, reason: "invalid redirect: empty or not a string" };
912
1012
  }
913
1013
  const cleaned = url.replace(CONTROL_CHARS, "");
914
- if (DANGEROUS_PROTOCOLS.test(cleaned)) {
915
- const proto = cleaned.match(DANGEROUS_PROTOCOLS);
1014
+ const proto = cleaned.match(DANGEROUS_PROTOCOLS);
1015
+ if (proto) {
916
1016
  return { safe: false, reason: `dangerous protocol: ${proto[0]}` };
917
1017
  }
918
1018
  if (cleaned.startsWith("\\")) {
@@ -942,11 +1042,12 @@ function validateRedirect(url, options = {}) {
942
1042
  return { safe: false, reason: `disallowed protocol: ${parsed.protocol}` };
943
1043
  }
944
1044
  const hostname = parsed.hostname.toLowerCase();
1045
+ const hostWithPort = parsed.port ? `${hostname}:${parsed.port}` : hostname;
945
1046
  if (allowedHosts.length === 0) {
946
1047
  return { safe: false, reason: "absolute URL not in allowed hosts" };
947
1048
  }
948
- if (!allowedHosts.some((h) => hostname === h.toLowerCase())) {
949
- return { safe: false, reason: `host not allowed: ${hostname}` };
1049
+ if (!allowedHosts.some((h) => h.toLowerCase() === hostWithPort)) {
1050
+ return { safe: false, reason: `host not allowed: ${hostWithPort}` };
950
1051
  }
951
1052
  return { safe: true };
952
1053
  }
@@ -954,8 +1055,10 @@ function isRedirectSafe(url, options = {}) {
954
1055
  return validateRedirect(url, options).safe;
955
1056
  }
956
1057
  function extractHost(url) {
957
- const match = url.match(/^\/\/([^/:?#]+)/);
958
- return match ? match[1].toLowerCase() : null;
1058
+ const match = url.match(/^\/\/([^/?#]+)/);
1059
+ if (!match) return null;
1060
+ const authority = match[1].includes("@") ? match[1].slice(match[1].indexOf("@") + 1) : match[1];
1061
+ return authority.toLowerCase();
959
1062
  }
960
1063
  var MAX_EMAIL_LENGTH = 254;
961
1064
  var MAX_LOCAL_LENGTH = 64;
@@ -1209,6 +1312,6 @@ function isValidEmailSyntax(email) {
1209
1312
  return EMAIL_SYNTAX.test(normalized);
1210
1313
  }
1211
1314
 
1212
- export { createValidator, isDangerousExtension, isRedirectSafe, isUrlSafe, isValidEmailSyntax, sanitizeFilename, validate, validateEmail, validateFile, validateRedirect, validateUrl, verifyEmailMx };
1315
+ export { createValidator, isDangerousExtension, isRedirectSafe, isUrlSafe, isValidEmailSyntax, pinnedDnsLookup, safeFollowRedirect, sanitizeFilename, validate, validateEmail, validateFile, validateRedirect, validateUrl, validateUrlAsync, verifyEmailMx };
1213
1316
  //# sourceMappingURL=index.mjs.map
1214
1317
  //# sourceMappingURL=index.mjs.map