@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
@@ -5,6 +5,8 @@
5
5
  export { validate, createValidator } from './schema';
6
6
  export { validateFile, sanitizeFilename, isDangerousExtension } from './file';
7
7
  export { validateUrl, isUrlSafe } from './url';
8
+ export { validateUrlAsync, pinnedDnsLookup, safeFollowRedirect } from './url-async';
9
+ export type { ValidateUrlAsyncOptions, ValidateUrlAsyncResult, DnsLookup, LookupAddress, } from './url-async';
8
10
  export { validateRedirect, isRedirectSafe } from './redirect';
9
11
  export { validateEmail, verifyEmailMx, isValidEmailSyntax } from './email';
10
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/validation/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,QAAQ,CAAC;AAC9E,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/validation/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,QAAQ,CAAC;AAC9E,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACpF,YAAY,EACV,uBAAuB,EACvB,sBAAsB,EACtB,SAAS,EACT,aAAa,GACd,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC"}
@@ -2,6 +2,26 @@
2
2
 
3
3
  var dns = require('dns');
4
4
 
5
+ function _interopNamespace(e) {
6
+ if (e && e.__esModule) return e;
7
+ var n = Object.create(null);
8
+ if (e) {
9
+ Object.keys(e).forEach(function (k) {
10
+ if (k !== 'default') {
11
+ var d = Object.getOwnPropertyDescriptor(e, k);
12
+ Object.defineProperty(n, k, d.get ? d : {
13
+ enumerable: true,
14
+ get: function () { return e[k]; }
15
+ });
16
+ }
17
+ });
18
+ }
19
+ n.default = e;
20
+ return Object.freeze(n);
21
+ }
22
+
23
+ var dns__namespace = /*#__PURE__*/_interopNamespace(dns);
24
+
5
25
  // src/core/constants.ts
6
26
  var INPUT = {
7
27
  /** Default maximum input size (1MB) */
@@ -33,8 +53,8 @@ var XSS_REMOVE_PATTERNS = [
33
53
  /** javascript: and vbscript: protocols (allow optional spaces before colon) */
34
54
  /javascript\s*:/gi,
35
55
  /vbscript\s*:/gi,
36
- /** data: URIs with HTML/script content */
37
- /data\s*:\s*text\/html[^>\s]*/gi,
56
+ /** data: URIs with HTML or SVG content (SVG can run JS via inline event handlers) */
57
+ /data\s*:\s*(?:text\/html|image\/svg)[^>\s]*/gi,
38
58
  /** form tag injection — phishing via action= redirection */
39
59
  /<form[\s>][^>]*/gi,
40
60
  /** meta tag injection — http-equiv refresh or CSP bypass */
@@ -118,10 +138,11 @@ var VALIDATION = {
118
138
  EMAIL: /^[^\s@.][^\s@]*(?:\.[^\s@.][^\s@]*)*@[^\s@]+\.[^\s@]+$/,
119
139
  /**
120
140
  * URL regex pattern.
121
- * Only allows http:// and https:// explicitly rejects javascript:,
122
- * data:, vbscript:, and other dangerous URI schemes.
141
+ * Only allows http:// and https:// (case-insensitive scheme per
142
+ * RFC 3986); explicitly rejects javascript:, data:, vbscript:, and
143
+ * other dangerous URI schemes.
123
144
  */
124
- URL: /^https?:\/\/[^\s/$.?#][^\s]*$/,
145
+ URL: /^https?:\/\/[^\s/$.?#][^\s]*$/i,
125
146
  /** UUID regex pattern (v4) */
126
147
  UUID: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
127
148
  };
@@ -402,7 +423,12 @@ function validate(schema, source = "body") {
402
423
  res.status(400).json({ errors });
403
424
  return;
404
425
  }
405
- req[source] = validated;
426
+ Object.defineProperty(req, source, {
427
+ value: validated,
428
+ writable: true,
429
+ configurable: true,
430
+ enumerable: true
431
+ });
406
432
  next();
407
433
  };
408
434
  }
@@ -899,6 +925,99 @@ function checkOctalIp(hostname, allowLocalhost, allowPrivate) {
899
925
  }
900
926
  return null;
901
927
  }
928
+ var defaultLookup = (hostname) => new Promise((resolve, reject) => {
929
+ dns__namespace.lookup(hostname, { all: true }, (err, addresses) => {
930
+ if (err) reject(err);
931
+ else resolve(addresses);
932
+ });
933
+ });
934
+ function checkResolvedIp(ip, options) {
935
+ const isIpv6 = ip.includes(":");
936
+ const host = isIpv6 ? `[${ip}]` : ip;
937
+ const { allowedProtocols, allowLocalhost, allowPrivate } = options;
938
+ return validateUrl(`http://${host}/`, {
939
+ allowedProtocols,
940
+ allowLocalhost,
941
+ allowPrivate
942
+ });
943
+ }
944
+ async function validateUrlAsync(url, options = {}) {
945
+ const sync = validateUrl(url, options);
946
+ if (!sync.safe) return sync;
947
+ let parsed;
948
+ try {
949
+ parsed = new URL(url);
950
+ } catch {
951
+ return { safe: false, reason: "invalid URL: failed to parse" };
952
+ }
953
+ const host = parsed.hostname.replace(/^\[|\]$/g, "");
954
+ if (isLiteralIp(host)) return { safe: true };
955
+ if (options.allowedHosts?.some((h) => host.toLowerCase() === h.toLowerCase())) {
956
+ return { safe: true };
957
+ }
958
+ const lookup2 = options.lookup ?? defaultLookup;
959
+ let addresses;
960
+ try {
961
+ addresses = await lookup2(host);
962
+ } catch (err) {
963
+ const msg = err instanceof Error ? err.message : String(err);
964
+ return { safe: false, reason: `DNS lookup failed: ${msg}` };
965
+ }
966
+ if (addresses.length === 0) {
967
+ return { safe: false, reason: "DNS returned no addresses" };
968
+ }
969
+ const ips = addresses.map((a) => a.address);
970
+ const acceptFirstPublic = options.acceptFirstPublic === true;
971
+ let firstPublic;
972
+ for (const ip of ips) {
973
+ const ipResult = checkResolvedIp(ip, options);
974
+ if (ipResult.safe) {
975
+ if (firstPublic === void 0) firstPublic = ip;
976
+ } else if (!acceptFirstPublic) {
977
+ return {
978
+ safe: false,
979
+ reason: `resolved IP ${ip} is unsafe: ${ipResult.reason}`,
980
+ resolvedIps: ips
981
+ };
982
+ }
983
+ }
984
+ if (firstPublic === void 0) {
985
+ return {
986
+ safe: false,
987
+ reason: "all resolved IPs are private/loopback",
988
+ resolvedIps: ips
989
+ };
990
+ }
991
+ return { safe: true, resolvedIp: firstPublic, resolvedIps: ips };
992
+ }
993
+ function pinnedDnsLookup(ip) {
994
+ if (typeof ip !== "string" || ip.trim() === "") {
995
+ throw new TypeError("pinnedDnsLookup: ip must be a non-empty string");
996
+ }
997
+ const family = ip.includes(":") ? 6 : 4;
998
+ return (_hostname, _options, callback) => {
999
+ callback(null, ip, family);
1000
+ };
1001
+ }
1002
+ async function safeFollowRedirect(prev, location, options = {}) {
1003
+ if (typeof location !== "string" || location.trim() === "") {
1004
+ return { safe: false, reason: "redirect Location is empty" };
1005
+ }
1006
+ let absolute;
1007
+ try {
1008
+ absolute = new URL(location, prev);
1009
+ } catch {
1010
+ return { safe: false, reason: "invalid redirect URL" };
1011
+ }
1012
+ return validateUrlAsync(absolute.toString(), options);
1013
+ }
1014
+ function isLiteralIp(host) {
1015
+ if (/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(host)) return true;
1016
+ if (/^\d+$/.test(host)) return true;
1017
+ if (/^0[0-7x]/.test(host) && /^[0-9a-fx.]+$/i.test(host) && host.includes(".")) return true;
1018
+ if (host.includes(":") && !/[/\s]/.test(host)) return true;
1019
+ return false;
1020
+ }
902
1021
 
903
1022
  // src/validation/redirect.ts
904
1023
  var DANGEROUS_PROTOCOLS = /^(javascript|data|vbscript|blob):/i;
@@ -913,8 +1032,8 @@ function validateRedirect(url, options = {}) {
913
1032
  return { safe: false, reason: "invalid redirect: empty or not a string" };
914
1033
  }
915
1034
  const cleaned = url.replace(CONTROL_CHARS, "");
916
- if (DANGEROUS_PROTOCOLS.test(cleaned)) {
917
- const proto = cleaned.match(DANGEROUS_PROTOCOLS);
1035
+ const proto = cleaned.match(DANGEROUS_PROTOCOLS);
1036
+ if (proto) {
918
1037
  return { safe: false, reason: `dangerous protocol: ${proto[0]}` };
919
1038
  }
920
1039
  if (cleaned.startsWith("\\")) {
@@ -944,11 +1063,12 @@ function validateRedirect(url, options = {}) {
944
1063
  return { safe: false, reason: `disallowed protocol: ${parsed.protocol}` };
945
1064
  }
946
1065
  const hostname = parsed.hostname.toLowerCase();
1066
+ const hostWithPort = parsed.port ? `${hostname}:${parsed.port}` : hostname;
947
1067
  if (allowedHosts.length === 0) {
948
1068
  return { safe: false, reason: "absolute URL not in allowed hosts" };
949
1069
  }
950
- if (!allowedHosts.some((h) => hostname === h.toLowerCase())) {
951
- return { safe: false, reason: `host not allowed: ${hostname}` };
1070
+ if (!allowedHosts.some((h) => h.toLowerCase() === hostWithPort)) {
1071
+ return { safe: false, reason: `host not allowed: ${hostWithPort}` };
952
1072
  }
953
1073
  return { safe: true };
954
1074
  }
@@ -956,8 +1076,10 @@ function isRedirectSafe(url, options = {}) {
956
1076
  return validateRedirect(url, options).safe;
957
1077
  }
958
1078
  function extractHost(url) {
959
- const match = url.match(/^\/\/([^/:?#]+)/);
960
- return match ? match[1].toLowerCase() : null;
1079
+ const match = url.match(/^\/\/([^/?#]+)/);
1080
+ if (!match) return null;
1081
+ const authority = match[1].includes("@") ? match[1].slice(match[1].indexOf("@") + 1) : match[1];
1082
+ return authority.toLowerCase();
961
1083
  }
962
1084
  var MAX_EMAIL_LENGTH = 254;
963
1085
  var MAX_LOCAL_LENGTH = 64;
@@ -1216,12 +1338,15 @@ exports.isDangerousExtension = isDangerousExtension;
1216
1338
  exports.isRedirectSafe = isRedirectSafe;
1217
1339
  exports.isUrlSafe = isUrlSafe;
1218
1340
  exports.isValidEmailSyntax = isValidEmailSyntax;
1341
+ exports.pinnedDnsLookup = pinnedDnsLookup;
1342
+ exports.safeFollowRedirect = safeFollowRedirect;
1219
1343
  exports.sanitizeFilename = sanitizeFilename;
1220
1344
  exports.validate = validate;
1221
1345
  exports.validateEmail = validateEmail;
1222
1346
  exports.validateFile = validateFile;
1223
1347
  exports.validateRedirect = validateRedirect;
1224
1348
  exports.validateUrl = validateUrl;
1349
+ exports.validateUrlAsync = validateUrlAsync;
1225
1350
  exports.verifyEmailMx = verifyEmailMx;
1226
1351
  //# sourceMappingURL=index.js.map
1227
1352
  //# sourceMappingURL=index.js.map