@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
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/constants.ts","../../src/core/errors.ts","../../src/sanitizers/utils.ts","../../src/sanitizers/xss.ts","../../src/sanitizers/sql.ts","../../src/sanitizers/path.ts","../../src/sanitizers/command.ts","../../src/sanitizers/sanitize.ts","../../src/validation/schema.ts","../../src/validation/file.ts","../../src/validation/url.ts","../../src/validation/redirect.ts","../../src/validation/email.ts"],"names":["host","dns"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @module @arcis/node/core/constants\n * Named constants for Arcis - no magic numbers\n */\n\n// =============================================================================\n// INPUT LIMITS\n// =============================================================================\nexport const INPUT = {\n /** Default maximum input size (1MB) */\n DEFAULT_MAX_SIZE: 1_000_000,\n /** Maximum recursion depth for nested objects */\n MAX_RECURSION_DEPTH: 10,\n} as const;\n\n// =============================================================================\n// RATE LIMITING\n// =============================================================================\nexport const RATE_LIMIT = {\n /** Default window size (1 minute) */\n DEFAULT_WINDOW_MS: 60_000,\n /** Default max requests per window */\n DEFAULT_MAX_REQUESTS: 100,\n /** Default HTTP status code for rate limited responses */\n DEFAULT_STATUS_CODE: 429,\n /** Default error message */\n DEFAULT_MESSAGE: 'Too many requests, please try again later.',\n /** Minimum window size (1 second) */\n MIN_WINDOW_MS: 1_000,\n /** Maximum window size (24 hours) */\n MAX_WINDOW_MS: 86_400_000,\n} as const;\n\n// =============================================================================\n// SECURITY HEADERS\n// =============================================================================\nexport const HEADERS = {\n /** Default Content Security Policy */\n DEFAULT_CSP: [\n \"default-src 'self'\",\n \"script-src 'self'\",\n \"style-src 'self' 'unsafe-inline'\",\n \"img-src 'self' data: https:\",\n \"font-src 'self'\",\n \"object-src 'none'\",\n \"frame-ancestors 'none'\",\n ].join('; '),\n /** Default HSTS max age (1 year in seconds) */\n HSTS_MAX_AGE: 31_536_000,\n /** Default X-Frame-Options value */\n FRAME_OPTIONS: 'DENY' as const,\n /** Default X-Content-Type-Options value */\n CONTENT_TYPE_OPTIONS: 'nosniff',\n /** Default Referrer-Policy value */\n REFERRER_POLICY: 'strict-origin-when-cross-origin',\n /** Default Permissions-Policy value */\n PERMISSIONS_POLICY: 'geolocation=(), microphone=(), camera=()',\n /** Default Cache-Control value for security */\n CACHE_CONTROL: 'no-store, no-cache, must-revalidate, proxy-revalidate',\n} as const;\n\n// =============================================================================\n// XSS PATTERNS (ReDoS-safe)\n// =============================================================================\n\n/**\n * Detection patterns — used to flag whether a string contains XSS payloads.\n * Must stay in sync with XSS_REMOVE_PATTERNS below.\n */\nexport const XSS_PATTERNS = [\n /** Script tags (ReDoS-safe version) */\n /<script[^>]*>[\\s\\S]*?<\\/script>/gi,\n /** javascript: protocol (allow optional spaces before colon) */\n /javascript\\s*:/gi,\n /** vbscript: protocol */\n /vbscript\\s*:/gi,\n /** Event handlers (onclick, onerror, etc.) — any separator before attribute */\n /(?:[\\s/])on\\w+\\s*=/gi,\n /** iframe tags */\n /<iframe/gi,\n /** object tags */\n /<object/gi,\n /** embed tags */\n /<embed/gi,\n /** data: URIs (only dangerous ones, avoid false positives) */\n /(?:^|[\\s\"'=])data:/gi,\n /** URL-encoded script tags */\n /%3Cscript/gi,\n /** SVG with onload */\n /<svg[^>]*onload/gi,\n /** form tags — phishing/credential harvesting via action= redirection */\n /<form[\\s>]/gi,\n /** meta tags — http-equiv refresh redirects or CSP bypass */\n /<meta[\\s>]/gi,\n /** base href hijacking — redirects all relative URLs to attacker domain */\n /<base[\\s>]/gi,\n /** link tag injection — stylesheet or preload CSRF attacks */\n /<link[\\s>]/gi,\n] as const;\n\n/**\n * Removal patterns — used by sanitizeXss() to strip dangerous content.\n * More targeted than XSS_PATTERNS: each pattern captures the full dangerous\n * substring (tag, attribute + value, protocol) so it can be replaced safely.\n * Must stay in sync with XSS_PATTERNS above.\n */\nexport const XSS_REMOVE_PATTERNS = [\n /** Full script blocks (content + tags) */\n /<script[^>]*>[\\s\\S]*?<\\/script>/gi,\n /** Standalone/unclosed script tags */\n /<script[^>]*>/gi,\n /** style — CSS expression() and behavior: attacks (IE-era but still relevant) */\n /<style[^>]*>[\\s\\S]*?<\\/style>/gi,\n /<style[^>]*/gi,\n /** iframe — full block and partial/unclosed */\n /<iframe[^>]*>[\\s\\S]*?<\\/iframe>/gi,\n /<iframe[^>]*/gi,\n /** object — full block and partial/unclosed */\n /<object[^>]*>[\\s\\S]*?<\\/object>/gi,\n /<object[^>]*/gi,\n /** embed tags */\n /<embed[^>]*/gi,\n /** SVG with inline event handlers */\n /<svg[^>]*onload[^>]*>/gi,\n /** URL-encoded script tags */\n /%3Cscript/gi,\n /** Event handlers with quoted values: onclick=\"...\", onerror='...' */\n /(?:[\\s/])on\\w+\\s*=\\s*[\"'][^\"']*[\"']/gi,\n /** Event handlers with unquoted values: onload=value */\n /(?:[\\s/])on\\w+\\s*=\\s*[^\\s>]*/gi,\n /** javascript: and vbscript: protocols (allow optional spaces before colon) */\n /javascript\\s*:/gi,\n /vbscript\\s*:/gi,\n /** data: URIs with HTML/script content */\n /data\\s*:\\s*text\\/html[^>\\s]*/gi,\n /** form tag injection — phishing via action= redirection */\n /<form[\\s>][^>]*/gi,\n /** meta tag injection — http-equiv refresh or CSP bypass */\n /<meta[\\s>][^>]*/gi,\n /** base href hijacking */\n /<base[\\s>][^>]*/gi,\n /** link tag injection — stylesheet or preload attacks */\n /<link[\\s>][^>]*/gi,\n] as const;\n\n// =============================================================================\n// SQL INJECTION PATTERNS\n// =============================================================================\nexport const SQL_PATTERNS = [\n /** SQL keywords */\n /(\\b(SELECT|INSERT|UPDATE|DELETE|DROP|UNION|ALTER|CREATE|TRUNCATE|EXEC|EXECUTE)\\b)/gi,\n /** SQL comments: ANSI (--), C-style (slash-star ... star-slash), MySQL (#) */\n /(--|\\/\\*|\\*\\/|#)/g,\n /** SQL statement separators */\n /(;|\\|\\||&&)/g,\n /** Boolean injection: OR 1=1 */\n /\\bOR\\s+\\d+\\s*=\\s*\\d+/gi,\n /** Boolean injection: OR 'a'='a' or OR \"a\"=\"a\" (including mixed quotes) */\n /\\bOR\\s+(['\"])[^'\"]*\\1\\s*=\\s*(['\"])[^'\"]*\\2/gi,\n /\\bOR\\s+('[^']*'|\"[^\"]*\")\\s*=\\s*('[^']*'|\"[^\"]*\")/gi,\n /** Boolean injection: AND 1=1 */\n /\\bAND\\s+\\d+\\s*=\\s*\\d+/gi,\n /** Boolean injection: AND 'a'='a' or AND \"a\"=\"a\" (including mixed quotes) */\n /\\bAND\\s+(['\"])[^'\"]*\\1\\s*=\\s*(['\"])[^'\"]*\\2/gi,\n /\\bAND\\s+('[^']*'|\"[^\"]*\")\\s*=\\s*('[^']*'|\"[^\"]*\")/gi,\n /** Time-based blind: SLEEP() */\n /\\bSLEEP\\s*\\(\\s*\\d+\\s*\\)/gi,\n /** Time-based blind: BENCHMARK() */\n /\\bBENCHMARK\\s*\\(/gi,\n /** Time-based blind: PostgreSQL pg_sleep() */\n /\\bpg_sleep\\s*\\(/gi,\n /** Time-based blind: MSSQL WAITFOR DELAY */\n /\\bWAITFOR\\s+DELAY\\b/gi,\n] as const;\n\n// =============================================================================\n// PATH TRAVERSAL PATTERNS\n// =============================================================================\nexport const PATH_PATTERNS = [\n /** Unix path traversal */\n /\\.\\.\\//g,\n /** Windows path traversal */\n /\\.\\.\\\\/g,\n /** URL-encoded traversal (%2e%2e) */\n /%2e%2e/gi,\n /** Double URL-encoded traversal (%252e) */\n /%252e/gi,\n /** Mixed encoding: ..%2F */\n /\\.\\.%2F/gi,\n /** Mixed encoding: %2e./ and .%2e/ */\n /%2e\\.[\\\\/]/gi,\n /\\.%2e[\\\\/]/gi,\n /** Fully URL-encoded: %2e%2e%2f */\n /%2e%2e%2f/gi,\n /** Double URL-encoded forward slash: %252f */\n /%252f/gi,\n /** Dotdotslash bypass: ....// or ....\\\\ */\n /\\.{2,}[/\\\\]{2,}/g,\n /** Null byte injection in paths */\n /\\0/g,\n] as const;\n\n// =============================================================================\n// COMMAND INJECTION PATTERNS\n// =============================================================================\nexport const COMMAND_PATTERNS = [\n /**\n * Shell metacharacters that enable command chaining/substitution.\n * Bare ( and ) are excluded — they appear in common legitimate values\n * (function calls in code fields, math expressions, etc.).\n * Command substitution is caught by the $( combined pattern below.\n * NOTE: ';', '&', '|' may appear in legitimate URL query strings\n * and Markdown; consider disabling command checking (command: false)\n * for fields that intentionally allow those characters.\n */\n /[;&|`]/g,\n /** Command substitution: $( ... ) — matched as a pair to reduce false positives */\n /\\$\\(/g,\n /** URL-encoded control characters (%00-%0F): null, tab, vtab, formfeed, LF, CR */\n /%0[0-9a-f]/gi,\n] as const;\n\n// =============================================================================\n// DANGEROUS KEYS\n// =============================================================================\n\n/**\n * Prototype pollution keys to block.\n * Stored lowercase — always compare with key.toLowerCase().\n *\n * Includes:\n * - __proto__: direct prototype assignment\n * - constructor: access to constructor.prototype chain\n * - prototype: direct prototype property\n * - __defineGetter__/__defineSetter__: legacy property definition (can override getters/setters)\n * - __lookupGetter__/__lookupSetter__: legacy property introspection\n */\nexport const DANGEROUS_PROTO_KEYS = new Set([\n '__proto__',\n 'constructor',\n 'prototype',\n '__definegetter__',\n '__definesetter__',\n '__lookupgetter__',\n '__lookupsetter__',\n]);\n\n/** MongoDB operators to block */\nexport const NOSQL_DANGEROUS_KEYS = new Set([\n // Comparison\n '$gt', '$gte', '$lt', '$lte', '$ne', '$eq', '$in', '$nin',\n // Logical\n '$and', '$or', '$not', '$nor',\n // Element / evaluation\n '$exists', '$type', '$regex', '$where', '$expr', '$mod', '$text', '$jsonSchema',\n // Array\n '$elemMatch', '$all', '$size',\n // JavaScript execution (critical)\n '$function', '$accumulator',\n // Aggregation pipeline operators (injectable via $lookup etc.)\n '$lookup', '$match', '$project', '$group', '$sort', '$limit', '$skip',\n '$unwind', '$addFields', '$replaceRoot',\n]);\n\n// =============================================================================\n// REDACTION\n// =============================================================================\nexport const REDACTION = {\n /** Replacement text for redacted values */\n REPLACEMENT: '[REDACTED]',\n /** Truncation indicator */\n TRUNCATED: '[TRUNCATED]',\n /** Max depth indicator */\n MAX_DEPTH: '[MAX_DEPTH]',\n /** Default max message length */\n DEFAULT_MAX_LENGTH: 10_000,\n /** Default sensitive keys to redact */\n SENSITIVE_KEYS: new Set([\n 'password', 'passwd', 'pwd', 'secret', 'token', 'apikey',\n 'api_key', 'apiKey', 'auth', 'authorization', 'credit_card',\n 'creditcard', 'cc', 'ssn', 'social_security', 'private_key',\n 'privateKey', 'access_token', 'accessToken', 'refresh_token',\n 'refreshToken', 'bearer', 'jwt', 'session', 'cookie',\n 'credentials', 'x-api-key', 'x-auth-token',\n ]),\n} as const;\n\n// =============================================================================\n// VALIDATION PATTERNS\n// =============================================================================\nexport const VALIDATION = {\n /**\n * Email regex pattern.\n * Rejects consecutive dots in local part (e.g. test..foo@example.com),\n * leading/trailing dots, and other common invalid forms.\n */\n EMAIL: /^[^\\s@.][^\\s@]*(?:\\.[^\\s@.][^\\s@]*)*@[^\\s@]+\\.[^\\s@]+$/,\n /**\n * URL regex pattern.\n * Only allows http:// and https:// — explicitly rejects javascript:,\n * data:, vbscript:, and other dangerous URI schemes.\n */\n URL: /^https?:\\/\\/[^\\s/$.?#][^\\s]*$/,\n /** UUID regex pattern (v4) */\n UUID: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,\n} as const;\n\n// =============================================================================\n// ERROR MESSAGES\n// =============================================================================\nexport const ERRORS = {\n /** Generic error message (production) */\n INTERNAL_SERVER_ERROR: 'Internal Server Error',\n /** Input too large error */\n INPUT_TOO_LARGE: (maxSize: number) => `Input exceeds maximum size of ${maxSize} bytes`,\n /** Validation error messages */\n VALIDATION: {\n REQUIRED: (field: string) => `${field} is required`,\n INVALID_TYPE: (field: string, type: string) => `${field} must be a ${type}`,\n MIN_LENGTH: (field: string, min: number) => `${field} must be at least ${min} characters`,\n MAX_LENGTH: (field: string, max: number) => `${field} must be at most ${max} characters`,\n MIN_VALUE: (field: string, min: number) => `${field} must be at least ${min}`,\n MAX_VALUE: (field: string, max: number) => `${field} must be at most ${max}`,\n INVALID_FORMAT: (field: string) => `${field} format is invalid`,\n INVALID_EMAIL: (field: string) => `${field} must be a valid email`,\n INVALID_URL: (field: string) => `${field} must be a valid URL`,\n INVALID_UUID: (field: string) => `${field} must be a valid UUID`,\n INVALID_ENUM: (field: string, values: unknown[]) => `${field} must be one of: ${values.join(', ')}`,\n MIN_ITEMS: (field: string, min: number) => `${field} must have at least ${min} items`,\n MAX_ITEMS: (field: string, max: number) => `${field} must have at most ${max} items`,\n },\n} as const;\n\n// =============================================================================\n// BLOCKED TEXT (for sanitizer replacements)\n// =============================================================================\nexport const BLOCKED = '[BLOCKED]' as const;\n","/**\n * @module @arcis/node/core/errors\n * Custom error classes for Arcis\n */\n\n/**\n * Base class for all Arcis errors\n */\nexport class ArcisError extends Error {\n public readonly statusCode: number;\n public readonly code: string;\n /** Whether the error message is safe to expose to API clients. */\n public readonly expose: boolean;\n\n constructor(message: string, statusCode = 500, code = 'ARCIS_ERROR') {\n super(message);\n this.name = 'ArcisError';\n this.statusCode = statusCode;\n this.code = code;\n // Client errors (4xx) have controlled messages — safe to expose.\n // Server errors (5xx) may contain internal details — hide by default.\n this.expose = statusCode < 500;\n\n // Maintains proper stack trace for where error was thrown (V8 engines)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n }\n}\n\n/**\n * Error thrown when input validation fails\n */\nexport class ValidationError extends ArcisError {\n public readonly errors: string[];\n\n constructor(errors: string[]) {\n super('Validation failed', 400, 'VALIDATION_ERROR');\n this.name = 'ValidationError';\n this.errors = errors;\n }\n}\n\n/** Alias for ValidationError (backwards compatibility) */\nexport { ValidationError as ArcisValidationError };\n\n/**\n * Error thrown when rate limit is exceeded\n */\nexport class RateLimitError extends ArcisError {\n public readonly retryAfter: number;\n\n constructor(message: string, retryAfter: number) {\n super(message, 429, 'RATE_LIMIT_EXCEEDED');\n this.name = 'RateLimitError';\n this.retryAfter = retryAfter;\n }\n}\n\n/**\n * Error thrown when input is too large\n */\nexport class InputTooLargeError extends ArcisError {\n public readonly maxSize: number;\n public readonly actualSize: number;\n\n constructor(maxSize: number, actualSize: number) {\n super(`Input exceeds maximum size of ${maxSize} bytes`, 413, 'INPUT_TOO_LARGE');\n this.name = 'InputTooLargeError';\n this.maxSize = maxSize;\n this.actualSize = actualSize;\n }\n}\n\n/**\n * Error thrown when security threat is detected\n */\nexport class SecurityThreatError extends ArcisError {\n public readonly threatType: string;\n public readonly pattern: string;\n\n constructor(threatType: string, pattern: string) {\n super('Request blocked for security reasons', 400, 'SECURITY_THREAT');\n this.name = 'SecurityThreatError';\n this.threatType = threatType;\n this.pattern = pattern;\n }\n}\n\n/**\n * Error thrown when sanitization fails\n */\nexport class SanitizationError extends ArcisError {\n constructor(message: string) {\n super(message, 400, 'SANITIZATION_ERROR');\n this.name = 'SanitizationError';\n }\n}\n","/**\n * @module @arcis/node/sanitizers/utils\n * Shared utilities for sanitizers\n */\n\n/**\n * Encodes HTML entities to prevent interpretation as markup.\n * \n * @param str - The string to encode\n * @returns The encoded string\n */\nexport function encodeHtmlEntities(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#x27;');\n}\n\n/**\n * Checks if a value is a plain object (not null, array, Date, etc.)\n * \n * @param value - Value to check\n * @returns True if plain object\n */\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return false;\n }\n // Check the actual prototype chain rather than toString, which can be spoofed\n // via Symbol.toStringTag. Accepts both Object.prototype (plain {}) and null\n // prototype objects (Object.create(null)).\n const proto = Object.getPrototypeOf(value as object);\n return proto === Object.prototype || proto === null;\n}\n","/**\n * @module @arcis/node/sanitizers/xss\n * XSS (Cross-Site Scripting) prevention\n */\n\nimport { XSS_PATTERNS, XSS_REMOVE_PATTERNS } from '../core/constants';\nimport { encodeHtmlEntities } from './utils';\nimport type { SanitizeResult, ThreatInfo } from '../core/types';\n\n/**\n * Sanitizes a string to prevent XSS attacks.\n * \n * Strategy:\n * 1. Remove dangerous patterns (script tags, event handlers, etc.)\n * 2. HTML-encode the remaining content\n * \n * @param input - The string to sanitize\n * @param collectThreats - Whether to collect threat information (default: false for performance)\n * @returns Sanitized string or SanitizeResult if collectThreats is true\n * \n * @example\n * sanitizeXss(\"<script>alert('xss')</script>\")\n * // Returns: \"&lt;script&gt;alert(&#x27;xss&#x27;)&lt;/script&gt;\"\n * \n * @example\n * sanitizeXss(\"<img onerror='alert(1)'>\")\n * // Returns: \"&lt;img&gt;\" (event handler removed)\n */\nexport function sanitizeXss(input: string, collectThreats?: false, htmlEncode?: boolean): string;\nexport function sanitizeXss(input: string, collectThreats: true, htmlEncode?: boolean): SanitizeResult;\nexport function sanitizeXss(input: string, collectThreats = false, htmlEncode = false): string | SanitizeResult {\n if (typeof input !== 'string') {\n return collectThreats \n ? { value: String(input), wasSanitized: false, threats: [] }\n : String(input);\n }\n\n const threats: ThreatInfo[] = [];\n let value = input;\n let wasSanitized = false;\n\n // Remove dangerous patterns FIRST — XSS_REMOVE_PATTERNS is the single\n // source of truth (defined in constants.ts alongside XSS_PATTERNS).\n for (const pattern of XSS_REMOVE_PATTERNS) {\n pattern.lastIndex = 0;\n if (pattern.test(value)) {\n pattern.lastIndex = 0;\n \n if (collectThreats) {\n const matches = value.match(pattern);\n if (matches) {\n for (const match of matches) {\n threats.push({\n type: 'xss',\n pattern: pattern.source,\n original: match,\n });\n }\n }\n }\n \n value = value.replace(pattern, '');\n wasSanitized = true;\n }\n }\n\n // HTML-encode only when explicitly requested (SSR/template context).\n // Do NOT encode by default — this is a REST API middleware; encoding\n // here corrupts JSON data with HTML entities (&lt;, &amp;, etc.) that\n // consumers would receive verbatim.\n if (htmlEncode) {\n const encoded = encodeHtmlEntities(value);\n if (encoded !== value) {\n wasSanitized = true;\n }\n value = encoded;\n }\n\n if (collectThreats) {\n return { value, wasSanitized, threats };\n }\n \n return value;\n}\n\n/**\n * Checks if a string contains potential XSS patterns.\n * Does not sanitize — use sanitizeXss() for that.\n * \n * @param input - The string to check\n * @returns True if XSS patterns detected\n */\nexport function detectXss(input: string): boolean {\n if (typeof input !== 'string') return false;\n \n // Check for event handlers\n if (/\\s+on\\w+\\s*=/i.test(input)) return true;\n \n // Check for dangerous protocols\n if (/javascript\\s*:/i.test(input)) return true;\n if (/vbscript\\s*:/i.test(input)) return true;\n if (/data\\s*:\\s*text\\/html/i.test(input)) return true;\n \n // Check for patterns from constants\n for (const pattern of XSS_PATTERNS) {\n pattern.lastIndex = 0;\n if (pattern.test(input)) {\n return true;\n }\n }\n \n return false;\n}\n","/**\n * @module @arcis/node/sanitizers/sql\n * SQL injection prevention\n */\n\nimport { SQL_PATTERNS } from '../core/constants';\nimport type { SanitizeResult, ThreatInfo } from '../core/types';\n\n/**\n * Sanitizes a string to prevent SQL injection attacks.\n * Replaces dangerous SQL patterns with [BLOCKED].\n * \n * @param input - The string to sanitize\n * @param collectThreats - Whether to collect threat information (default: false for performance)\n * @returns Sanitized string or SanitizeResult if collectThreats is true\n * \n * @example\n * sanitizeSql(\"'; DROP TABLE users; --\")\n * // Returns: \"'; TABLE users \"\n */\nexport function sanitizeSql(input: string, collectThreats?: false): string;\nexport function sanitizeSql(input: string, collectThreats: true): SanitizeResult;\nexport function sanitizeSql(input: string, collectThreats = false): string | SanitizeResult {\n if (typeof input !== 'string') {\n return collectThreats \n ? { value: String(input), wasSanitized: false, threats: [] }\n : String(input);\n }\n\n const threats: ThreatInfo[] = [];\n let value = input;\n let wasSanitized = false;\n\n for (const pattern of SQL_PATTERNS) {\n // Reset regex lastIndex for global patterns\n pattern.lastIndex = 0;\n \n if (pattern.test(value)) {\n pattern.lastIndex = 0; // Reset again for replace\n \n if (collectThreats) {\n const matches = value.match(pattern);\n if (matches) {\n for (const match of matches) {\n threats.push({\n type: 'sql_injection',\n pattern: pattern.source,\n original: match,\n });\n }\n }\n }\n \n // Replace the matched content with a space to avoid concatenating surrounding\n // tokens into new dangerous strings (e.g. \"SELECTname\" after stripping \"SELECT\").\n value = value.replace(pattern, ' ');\n wasSanitized = true;\n }\n }\n\n if (collectThreats) {\n return { value, wasSanitized, threats };\n }\n \n return value;\n}\n\n/**\n * Checks if a string contains potential SQL injection patterns.\n * Does not sanitize — use sanitizeSql() for that.\n * \n * @param input - The string to check\n * @returns True if SQL injection patterns detected\n */\nexport function detectSql(input: string): boolean {\n if (typeof input !== 'string') return false;\n \n for (const pattern of SQL_PATTERNS) {\n pattern.lastIndex = 0;\n if (pattern.test(input)) {\n return true;\n }\n }\n \n return false;\n}\n","/**\n * @module @arcis/node/sanitizers/path\n * Path traversal prevention\n */\n\nimport { PATH_PATTERNS } from '../core/constants';\nimport type { SanitizeResult, ThreatInfo } from '../core/types';\n\n/**\n * Sanitizes a string to prevent path traversal attacks.\n * Removes ../ and ..\\ patterns (including URL-encoded variants).\n * \n * @param input - The string to sanitize\n * @param collectThreats - Whether to collect threat information (default: false for performance)\n * @returns Sanitized string or SanitizeResult if collectThreats is true\n * \n * @example\n * sanitizePath(\"../../etc/passwd\")\n * // Returns: \"etc/passwd\"\n */\nexport function sanitizePath(input: string, collectThreats?: false): string;\nexport function sanitizePath(input: string, collectThreats: true): SanitizeResult;\nexport function sanitizePath(input: string, collectThreats = false): string | SanitizeResult {\n if (typeof input !== 'string') {\n return collectThreats \n ? { value: String(input), wasSanitized: false, threats: [] }\n : String(input);\n }\n\n const threats: ThreatInfo[] = [];\n let value = input;\n let wasSanitized = false;\n\n // SECURITY: Normalize Unicode to NFKC before pattern matching.\n // Fullwidth dot U+FF0E normalizes to '.', preventing ../ bypass of ../ detection.\n value = value.normalize('NFKC');\n\n // Apply patterns repeatedly until the string stops changing.\n // Single-pass stripping is bypassable: \"....//\".replace(\"../\",\"\") → \"../\"\n let prev: string;\n do {\n prev = value;\n for (const pattern of PATH_PATTERNS) {\n pattern.lastIndex = 0;\n\n if (pattern.test(value)) {\n pattern.lastIndex = 0;\n\n if (collectThreats) {\n const matches = value.match(pattern);\n if (matches) {\n for (const match of matches) {\n threats.push({\n type: 'path_traversal',\n pattern: pattern.source,\n original: match,\n });\n }\n }\n }\n\n value = value.replace(pattern, '');\n wasSanitized = true;\n }\n }\n } while (value !== prev);\n\n if (collectThreats) {\n return { value, wasSanitized, threats };\n }\n \n return value;\n}\n\n/**\n * Checks if a string contains path traversal patterns.\n * Does not sanitize — use sanitizePath() for that.\n * \n * @param input - The string to check\n * @returns True if path traversal patterns detected\n */\nexport function detectPathTraversal(input: string): boolean {\n if (typeof input !== 'string') return false;\n\n // SECURITY: Normalize Unicode to NFKC — same as sanitizePath\n const normalized = input.normalize('NFKC');\n\n for (const pattern of PATH_PATTERNS) {\n pattern.lastIndex = 0;\n if (pattern.test(normalized)) {\n return true;\n }\n }\n \n return false;\n}\n","/**\n * @module @arcis/node/sanitizers/command\n * Command injection prevention\n */\n\nimport { COMMAND_PATTERNS } from '../core/constants';\nimport type { SanitizeResult, ThreatInfo } from '../core/types';\n\n/**\n * Sanitizes a string to prevent command injection attacks.\n * Replaces shell metacharacters and dangerous commands with [BLOCKED].\n * \n * @param input - The string to sanitize\n * @param collectThreats - Whether to collect threat information (default: false for performance)\n * @returns Sanitized string or SanitizeResult if collectThreats is true\n * \n * @example\n * sanitizeCommand(\"file.txt; rm -rf /\")\n * // Returns: \"file.txt rm -rf /\"\n */\nexport function sanitizeCommand(input: string, collectThreats?: false): string;\nexport function sanitizeCommand(input: string, collectThreats: true): SanitizeResult;\nexport function sanitizeCommand(input: string, collectThreats = false): string | SanitizeResult {\n if (typeof input !== 'string') {\n return collectThreats \n ? { value: String(input), wasSanitized: false, threats: [] }\n : String(input);\n }\n\n const threats: ThreatInfo[] = [];\n let value = input;\n let wasSanitized = false;\n\n for (const pattern of COMMAND_PATTERNS) {\n // Reset regex lastIndex for global patterns\n pattern.lastIndex = 0;\n \n if (pattern.test(value)) {\n pattern.lastIndex = 0; // Reset again for replace\n \n if (collectThreats) {\n const matches = value.match(pattern);\n if (matches) {\n for (const match of matches) {\n threats.push({\n type: 'command_injection',\n pattern: pattern.source,\n original: match,\n });\n }\n }\n }\n \n value = value.replace(pattern, ' ');\n wasSanitized = true;\n }\n }\n\n if (collectThreats) {\n return { value, wasSanitized, threats };\n }\n \n return value;\n}\n\n/**\n * Checks if a string contains command injection patterns.\n * Does not sanitize — use sanitizeCommand() for that.\n * \n * @param input - The string to check\n * @returns True if command injection patterns detected\n */\nexport function detectCommandInjection(input: string): boolean {\n if (typeof input !== 'string') return false;\n \n for (const pattern of COMMAND_PATTERNS) {\n pattern.lastIndex = 0;\n if (pattern.test(input)) {\n return true;\n }\n }\n \n return false;\n}\n","/**\n * @module @arcis/node/sanitizers/sanitize\n * Main sanitization functions that combine all sanitizers\n */\n\nimport type { Request, Response, NextFunction, RequestHandler } from 'express';\nimport { INPUT, DANGEROUS_PROTO_KEYS, NOSQL_DANGEROUS_KEYS } from '../core/constants';\nimport { InputTooLargeError, SecurityThreatError } from '../core/errors';\nimport type { SanitizeOptions } from '../core/types';\nimport { sanitizeXss } from './xss';\nimport { sanitizeSql, detectSql } from './sql';\nimport { sanitizePath } from './path';\nimport { sanitizeCommand, detectCommandInjection } from './command';\n\n/**\n * Sanitize a string value against multiple attack vectors.\n * \n * Order matters: We do XSS encoding LAST because:\n * 1. Other sanitizers need to see the original patterns (e.g., SQL keywords)\n * 2. HTML encoding is the final safe output transformation\n * 3. Encoded entities like &lt; shouldn't be treated as SQL/command threats\n * \n * @param value - The string to sanitize\n * @param options - Sanitization options\n * @returns The sanitized string\n * \n * @example\n * sanitizeString(\"<script>alert('xss')</script>\")\n * // Returns: \"&lt;script&gt;alert(&#x27;xss&#x27;)&lt;/script&gt;\"\n * \n * @example\n * sanitizeString(\"../../etc/passwd\")\n * // Returns: \"etc/passwd\"\n */\nexport function sanitizeString(value: string, options: SanitizeOptions = {}): string {\n if (typeof value !== 'string') return value;\n\n // Input size limit to prevent DoS\n const maxSize = options.maxSize ?? INPUT.DEFAULT_MAX_SIZE;\n if (value.length > maxSize) {\n throw new InputTooLargeError(maxSize, value.length);\n }\n\n // Default mode is 'sanitize' (strip threats and return cleaned string).\n // Pass mode: 'reject' to throw SecurityThreatError instead of stripping.\n const reject = options.mode === 'reject';\n let result = value;\n\n // 1. SQL injection\n if (options.sql !== false) {\n if (reject) {\n if (detectSql(result)) {\n throw new SecurityThreatError('sql_injection', 'SQL pattern detected in input');\n }\n } else {\n result = sanitizeSql(result);\n }\n }\n\n // 2. Path traversal prevention\n if (options.path !== false) {\n result = sanitizePath(result);\n }\n\n // 3. Command injection\n if (options.command !== false) {\n if (reject) {\n if (detectCommandInjection(result)) {\n throw new SecurityThreatError('command_injection', 'Shell metacharacter detected in input');\n }\n } else {\n result = sanitizeCommand(result);\n }\n }\n\n // 4. XSS stripping — always runs to remove dangerous patterns.\n // HTML encoding is opt-in via options.htmlEncode (for SSR contexts only).\n if (options.xss !== false) {\n result = sanitizeXss(result, false, options.htmlEncode ?? false);\n }\n\n return result;\n}\n\n/**\n * Sanitize an object recursively, including nested objects and arrays.\n * Also removes prototype pollution and NoSQL injection keys.\n * \n * @param obj - The object to sanitize\n * @param options - Sanitization options\n * @returns The sanitized object\n */\nexport function sanitizeObject(obj: unknown, options: SanitizeOptions = {}): unknown {\n if (obj === null || obj === undefined) return obj;\n if (typeof obj === 'string') return sanitizeString(obj, options);\n if (typeof obj !== 'object') return obj;\n if (Array.isArray(obj)) return obj.map(item => sanitizeObject(item, options));\n\n const result = sanitizeObjectDepth(obj as Record<string, unknown>, options, 0);\n return options.freeze ? Object.freeze(result) : result;\n}\n\n/**\n * Internal recursive sanitization with depth tracking.\n */\nfunction sanitizeObjectDepth(\n obj: Record<string, unknown>,\n options: SanitizeOptions,\n depth: number\n): Record<string, unknown> {\n if (depth >= INPUT.MAX_RECURSION_DEPTH) return obj;\n\n const result: Record<string, unknown> = {};\n\n for (const key of Object.keys(obj)) {\n // Prototype pollution protection - always block dangerous keys (case-insensitive)\n if (options.proto !== false && DANGEROUS_PROTO_KEYS.has(key.toLowerCase())) {\n continue;\n }\n\n // NoSQL injection - skip dangerous MongoDB operators in keys\n if (options.nosql !== false && NOSQL_DANGEROUS_KEYS.has(key)) {\n continue;\n }\n\n // Sanitize the key against all active threat vectors (not just XSS).\n // Keys can carry injection payloads that bubble into query builders or ORMs.\n const sanitizedKey = sanitizeString(key, options);\n\n // Recursively sanitize value\n const value = obj[key];\n if (value === null || value === undefined) {\n result[sanitizedKey] = value;\n } else if (typeof value === 'string') {\n result[sanitizedKey] = sanitizeString(value, options);\n } else if (Array.isArray(value)) {\n result[sanitizedKey] = value.map(item => sanitizeObject(item, options));\n } else if (typeof value === 'object') {\n result[sanitizedKey] = sanitizeObjectDepth(value as Record<string, unknown>, options, depth + 1);\n } else {\n result[sanitizedKey] = value;\n }\n }\n\n return result;\n}\n\n/**\n * Create Express middleware for request sanitization.\n * Sanitizes req.body, req.query, and req.params.\n * \n * @param options - Sanitization options\n * @returns Express middleware\n * \n * @example\n * app.use(createSanitizer());\n * \n * @example\n * app.use(createSanitizer({ xss: true, sql: true, nosql: true }));\n */\nexport function createSanitizer(options: SanitizeOptions = {}): RequestHandler {\n return (req: Request, _res: Response, next: NextFunction) => {\n try {\n if (req.body && typeof req.body === 'object') {\n req.body = sanitizeObject(req.body, options);\n }\n if (req.query && typeof req.query === 'object') {\n const sanitizedQuery = sanitizeObject(req.query, options);\n // Express 5: req.query is a getter with no setter — override on instance\n Object.defineProperty(req, 'query', { value: sanitizedQuery, writable: true, configurable: true });\n }\n if (req.params && typeof req.params === 'object') {\n const sanitizedParams = sanitizeObject(req.params, options);\n Object.defineProperty(req, 'params', { value: sanitizedParams, writable: true, configurable: true });\n }\n next();\n } catch (err) {\n next(err);\n }\n };\n}\n","/**\n * @module @arcis/node/validation/schema\n * Request validation middleware\n */\n\nimport type { Request, Response, NextFunction, RequestHandler } from 'express';\nimport { VALIDATION, ERRORS } from '../core/constants';\nimport type { ValidationSchema, FieldValidator } from '../core/types';\nimport { sanitizeString } from '../sanitizers';\n\n/**\n * Create Express middleware for request validation.\n * Prevents mass assignment by only allowing fields defined in the schema.\n * \n * @param schema - Validation schema defining expected fields\n * @param source - Request property to validate ('body', 'query', or 'params')\n * @returns Express middleware\n * \n * @example\n * app.post('/users', validate({\n * email: { type: 'email', required: true },\n * name: { type: 'string', min: 2, max: 50 },\n * age: { type: 'number', min: 0, max: 150 },\n * role: { type: 'string', enum: ['user', 'admin'] }\n * }), handler);\n * \n * @example\n * // Validate query params\n * app.get('/search', validate({\n * q: { type: 'string', required: true, min: 1 },\n * page: { type: 'number', min: 1 }\n * }, 'query'), handler);\n */\nexport function validate(\n schema: ValidationSchema,\n source: 'body' | 'query' | 'params' = 'body'\n): RequestHandler {\n return (req: Request, res: Response, next: NextFunction) => {\n const data = req[source] || {};\n const errors: string[] = [];\n const validated: Record<string, unknown> = {};\n\n for (const [field, rules] of Object.entries(schema)) {\n const value = data[field];\n const result = validateField(field, value, rules);\n \n if (result.errors.length > 0) {\n errors.push(...result.errors);\n } else if (result.value !== undefined) {\n validated[field] = result.value;\n }\n }\n\n if (errors.length > 0) {\n res.status(400).json({ errors });\n return;\n }\n\n // Replace with validated data (prevents mass assignment)\n req[source] = validated as typeof req[typeof source];\n next();\n };\n}\n\n/**\n * Validate a single field against its rules.\n */\nfunction validateField(\n field: string,\n value: unknown,\n rules: FieldValidator\n): { value?: unknown; errors: string[] } {\n const errors: string[] = [];\n\n // Required check\n if (rules.required && (value === undefined || value === null || value === '')) {\n errors.push(ERRORS.VALIDATION.REQUIRED(field));\n return { errors };\n }\n\n // Skip optional empty fields\n if (value === undefined || value === null) {\n return { errors: [] };\n }\n\n let typedValue: unknown = value;\n let isValid = true;\n\n // Type validation and coercion\n switch (rules.type) {\n case 'string':\n if (typeof value !== 'string') {\n errors.push(ERRORS.VALIDATION.INVALID_TYPE(field, 'string'));\n isValid = false;\n break;\n }\n if (rules.min !== undefined && value.length < rules.min) {\n errors.push(ERRORS.VALIDATION.MIN_LENGTH(field, rules.min));\n isValid = false;\n }\n if (rules.max !== undefined && value.length > rules.max) {\n errors.push(ERRORS.VALIDATION.MAX_LENGTH(field, rules.max));\n isValid = false;\n }\n if (rules.pattern && !rules.pattern.test(value)) {\n errors.push(ERRORS.VALIDATION.INVALID_FORMAT(field));\n isValid = false;\n }\n // Enum check runs before sanitization so the raw value is compared.\n // Sanitizing first could silently modify the value and cause a mismatch\n // with enum entries that contain characters the sanitizer would strip.\n if (isValid && rules.enum && !rules.enum.includes(value)) {\n errors.push(ERRORS.VALIDATION.INVALID_ENUM(field, rules.enum));\n isValid = false;\n }\n if (isValid && rules.sanitize !== false) {\n typedValue = sanitizeString(value);\n }\n break;\n\n case 'number':\n typedValue = Number(value);\n if (isNaN(typedValue as number)) {\n errors.push(ERRORS.VALIDATION.INVALID_TYPE(field, 'number'));\n isValid = false;\n break;\n }\n if (rules.min !== undefined && (typedValue as number) < rules.min) {\n errors.push(ERRORS.VALIDATION.MIN_VALUE(field, rules.min));\n isValid = false;\n }\n if (rules.max !== undefined && (typedValue as number) > rules.max) {\n errors.push(ERRORS.VALIDATION.MAX_VALUE(field, rules.max));\n isValid = false;\n }\n break;\n\n case 'boolean':\n if (value === 'true' || value === true || value === 1 || value === '1') {\n typedValue = true;\n } else if (value === 'false' || value === false || value === 0 || value === '0') {\n typedValue = false;\n } else {\n errors.push(ERRORS.VALIDATION.INVALID_TYPE(field, 'boolean'));\n isValid = false;\n }\n break;\n\n case 'email':\n if (!VALIDATION.EMAIL.test(String(value))) {\n errors.push(ERRORS.VALIDATION.INVALID_EMAIL(field));\n isValid = false;\n }\n if (isValid) {\n typedValue = sanitizeString(String(value).toLowerCase().trim());\n }\n break;\n\n case 'url':\n if (!VALIDATION.URL.test(String(value))) {\n errors.push(ERRORS.VALIDATION.INVALID_URL(field));\n isValid = false;\n }\n if (isValid) {\n typedValue = sanitizeString(String(value));\n }\n break;\n\n case 'uuid':\n if (!VALIDATION.UUID.test(String(value))) {\n errors.push(ERRORS.VALIDATION.INVALID_UUID(field));\n isValid = false;\n }\n break;\n\n case 'array':\n if (!Array.isArray(value)) {\n errors.push(ERRORS.VALIDATION.INVALID_TYPE(field, 'array'));\n isValid = false;\n break;\n }\n if (rules.min !== undefined && value.length < rules.min) {\n errors.push(ERRORS.VALIDATION.MIN_ITEMS(field, rules.min));\n isValid = false;\n }\n if (rules.max !== undefined && value.length > rules.max) {\n errors.push(ERRORS.VALIDATION.MAX_ITEMS(field, rules.max));\n isValid = false;\n }\n break;\n\n case 'object':\n if (typeof value !== 'object' || Array.isArray(value) || value === null) {\n errors.push(ERRORS.VALIDATION.INVALID_TYPE(field, 'object'));\n isValid = false;\n }\n break;\n }\n\n // Enum validation for non-string types (strings check enum before sanitizing above).\n if (isValid && rules.enum && rules.type !== 'string' && !rules.enum.includes(typedValue)) {\n errors.push(ERRORS.VALIDATION.INVALID_ENUM(field, rules.enum));\n isValid = false;\n }\n\n // Custom validation\n if (isValid && rules.custom) {\n const customResult = rules.custom(typedValue);\n if (customResult === undefined) {\n throw new TypeError(\n `Custom validator for field \"${field}\" returned undefined. ` +\n 'Return true to pass, false to fail, or a string error message.'\n );\n }\n if (customResult !== true) {\n errors.push(typeof customResult === 'string' && customResult.length > 0 ? customResult : `${field} is invalid`);\n isValid = false;\n }\n }\n\n return {\n value: isValid ? typedValue : undefined,\n errors,\n };\n}\n\n/**\n * Alias for validate\n * @see validate\n */\nexport const createValidator = validate;\n","/**\n * @module @arcis/node/validation/file\n * File upload validation and filename sanitization\n */\n\n// =============================================================================\n// MAGIC BYTES — first bytes of common file types\n// =============================================================================\n\nconst MAGIC_BYTES: Record<string, Buffer[]> = {\n // Images\n 'image/jpeg': [Buffer.from([0xFF, 0xD8, 0xFF])],\n 'image/png': [Buffer.from([0x89, 0x50, 0x4E, 0x47])],\n 'image/gif': [Buffer.from('GIF87a'), Buffer.from('GIF89a')],\n 'image/webp': [Buffer.from('RIFF')], // RIFF....WEBP\n 'image/bmp': [Buffer.from([0x42, 0x4D])],\n 'image/svg+xml': [], // text-based, check separately\n\n // Documents\n 'application/pdf': [Buffer.from('%PDF')],\n 'application/zip': [Buffer.from([0x50, 0x4B, 0x03, 0x04])],\n\n // Audio/Video\n 'audio/mpeg': [Buffer.from([0xFF, 0xFB]), Buffer.from([0xFF, 0xF3]), Buffer.from([0x49, 0x44, 0x33])],\n 'video/mp4': [], // ftyp at offset 4\n};\n\n// =============================================================================\n// DANGEROUS EXTENSIONS — files that can execute code\n// =============================================================================\n\nconst DANGEROUS_EXTENSIONS = new Set([\n // Scripts\n '.exe', '.bat', '.cmd', '.com', '.msi', '.scr', '.pif',\n '.vbs', '.vbe', '.js', '.jse', '.ws', '.wsf', '.wsc', '.wsh',\n '.ps1', '.ps1xml', '.ps2', '.ps2xml', '.psc1', '.psc2',\n '.sh', '.bash', '.csh', '.ksh',\n // Server-side\n '.php', '.php3', '.php4', '.php5', '.phtml', '.pht',\n '.asp', '.aspx', '.ashx', '.asmx', '.cer',\n '.jsp', '.jspx', '.jsw', '.jsv',\n '.cgi', '.pl', '.py', '.rb',\n // Java\n '.jar', '.war', '.ear', '.class',\n // Config that can execute\n '.htaccess', '.htpasswd',\n // Template engines\n '.ejs', '.pug', '.hbs', '.handlebars', '.njk', '.twig',\n // Shortcuts/links\n '.lnk', '.inf', '.reg', '.url',\n // Office macros\n '.docm', '.xlsm', '.pptm', '.dotm',\n]);\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\n/** File upload validation options */\nexport interface ValidateFileOptions {\n /** Maximum file size in bytes. Default: 5MB */\n maxSize?: number;\n /** Allowed MIME types (e.g., ['image/jpeg', 'image/png']) */\n allowedTypes?: string[];\n /** Allowed file extensions (e.g., ['.jpg', '.png']). Includes dot. */\n allowedExtensions?: string[];\n /** Block dangerous/executable extensions. Default: true */\n blockExecutables?: boolean;\n /** Validate magic bytes match the claimed MIME type. Default: true */\n validateMagicBytes?: boolean;\n /** Block files with no extension. Default: true */\n blockNoExtension?: boolean;\n /** Block double extensions (e.g., file.php.jpg). Default: true */\n blockDoubleExtensions?: boolean;\n}\n\n/** File metadata for validation */\nexport interface FileInput {\n /** Original filename */\n filename: string;\n /** MIME type (as claimed by client) */\n mimetype: string;\n /** File size in bytes */\n size: number;\n /** File content buffer (for magic byte validation) */\n buffer?: Buffer;\n}\n\n/** File validation result */\nexport interface ValidateFileResult {\n /** Whether the file passed validation */\n valid: boolean;\n /** Validation errors (empty if valid) */\n errors: string[];\n /** Sanitized filename (safe for storage) */\n sanitizedFilename: string;\n}\n\n// =============================================================================\n// DEFAULTS\n// =============================================================================\n\nconst DEFAULT_MAX_SIZE = 5 * 1024 * 1024; // 5MB\n\n// =============================================================================\n// FILENAME SANITIZATION\n// =============================================================================\n\n/**\n * Sanitize a filename for safe storage.\n *\n * Strips path traversal, null bytes, control characters, and special characters.\n * Preserves the extension and converts to a filesystem-safe name.\n *\n * @param filename - The original filename\n * @returns A sanitized filename safe for storage\n *\n * @example\n * sanitizeFilename('../../etc/passwd') // 'etc_passwd'\n * sanitizeFilename('file<name>.jpg') // 'filename.jpg'\n * sanitizeFilename('photo (1).jpg') // 'photo_1.jpg'\n * sanitizeFilename('.htaccess') // 'htaccess'\n */\nexport function sanitizeFilename(filename: string): string {\n let name = filename;\n\n // Strip null bytes\n name = name.replace(/\\0/g, '');\n\n // Strip path components (both Unix and Windows)\n name = name.replace(/^.*[/\\\\]/, '');\n\n // Strip control characters\n name = name.replace(/[\\x00-\\x1F\\x7F]/g, '');\n\n // Strip characters unsafe for filesystems\n name = name.replace(/[<>:\"/\\\\|?*]/g, '');\n\n // Replace spaces and parens with underscores\n name = name.replace(/[\\s()]+/g, '_');\n\n // Strip leading dots (hidden files / .htaccess)\n name = name.replace(/^\\.+/, '');\n\n // Collapse multiple underscores/dots\n name = name.replace(/_{2,}/g, '_');\n name = name.replace(/\\.{2,}/g, '.');\n\n // Trim underscores before dots (e.g., \"photo_1_.jpg\" → \"photo_1.jpg\")\n name = name.replace(/_+\\./g, '.');\n\n // Trim underscores from edges\n name = name.replace(/^_+|_+$/g, '');\n\n // Fallback for empty name\n if (!name || name === '.') {\n name = 'unnamed';\n }\n\n return name;\n}\n\n// =============================================================================\n// MAGIC BYTE VALIDATION\n// =============================================================================\n\n/**\n * Check if file content matches the claimed MIME type via magic bytes.\n */\nfunction matchesMagicBytes(buffer: Buffer, mimetype: string): boolean {\n const signatures = MAGIC_BYTES[mimetype];\n if (!signatures || signatures.length === 0) return true; // no signature to check\n\n return signatures.some(sig => {\n if (buffer.length < sig.length) return false;\n return buffer.subarray(0, sig.length).equals(sig);\n });\n}\n\n// =============================================================================\n// EXTENSION HELPERS\n// =============================================================================\n\n/**\n * Get the extension from a filename (lowercase, with dot).\n */\nfunction getExtension(filename: string): string {\n const lastDot = filename.lastIndexOf('.');\n if (lastDot < 1) return '';\n return filename.slice(lastDot).toLowerCase();\n}\n\n/**\n * Check if a filename has double extensions (e.g., file.php.jpg).\n */\nfunction hasDoubleExtension(filename: string): boolean {\n const parts = filename.split('.');\n if (parts.length < 3) return false;\n\n // Check if any non-final extension is dangerous\n for (let i = 1; i < parts.length - 1; i++) {\n const ext = '.' + parts[i].toLowerCase();\n if (DANGEROUS_EXTENSIONS.has(ext)) return true;\n }\n return false;\n}\n\n// =============================================================================\n// FILE VALIDATION\n// =============================================================================\n\n/**\n * Validate a file upload for security.\n *\n * Checks file size, MIME type, extension, magic bytes, and dangerous patterns.\n * Returns a result with validation errors and a sanitized filename.\n *\n * @param file - File metadata and optional content\n * @param options - Validation options\n * @returns Validation result\n *\n * @example\n * const result = validateFile(\n * { filename: 'photo.jpg', mimetype: 'image/jpeg', size: 1024, buffer },\n * { allowedTypes: ['image/jpeg', 'image/png'], maxSize: 2 * 1024 * 1024 }\n * );\n * if (!result.valid) {\n * return res.status(400).json({ errors: result.errors });\n * }\n * // Use result.sanitizedFilename for storage\n *\n * @example\n * // Block executables only (no whitelist)\n * const result = validateFile(file, { blockExecutables: true });\n */\nexport function validateFile(\n file: FileInput,\n options: ValidateFileOptions = {}\n): ValidateFileResult {\n const {\n maxSize = DEFAULT_MAX_SIZE,\n allowedTypes,\n allowedExtensions,\n blockExecutables = true,\n validateMagicBytes = true,\n blockNoExtension = true,\n blockDoubleExtensions = true,\n } = options;\n\n const errors: string[] = [];\n const sanitizedFilename = sanitizeFilename(file.filename);\n const extension = getExtension(sanitizedFilename);\n\n // Size check\n if (file.size > maxSize) {\n errors.push(`File size ${file.size} exceeds maximum ${maxSize} bytes`);\n }\n\n if (file.size === 0) {\n errors.push('File is empty');\n }\n\n // Extension checks\n if (blockNoExtension && !extension) {\n errors.push('File has no extension');\n }\n\n if (blockExecutables && extension && DANGEROUS_EXTENSIONS.has(extension)) {\n errors.push(`Executable extension \"${extension}\" is not allowed`);\n }\n\n if (blockDoubleExtensions && hasDoubleExtension(sanitizedFilename)) {\n errors.push('Double extensions with executable types are not allowed');\n }\n\n if (allowedExtensions && extension) {\n const normalizedAllowed = allowedExtensions.map(e => e.toLowerCase());\n if (!normalizedAllowed.includes(extension)) {\n errors.push(`Extension \"${extension}\" is not allowed. Allowed: ${normalizedAllowed.join(', ')}`);\n }\n }\n\n // MIME type check\n if (allowedTypes && !allowedTypes.includes(file.mimetype)) {\n errors.push(`MIME type \"${file.mimetype}\" is not allowed. Allowed: ${allowedTypes.join(', ')}`);\n }\n\n // Magic bytes validation\n if (validateMagicBytes && file.buffer && file.buffer.length > 0) {\n if (!matchesMagicBytes(file.buffer, file.mimetype)) {\n errors.push(`File content does not match claimed MIME type \"${file.mimetype}\"`);\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n sanitizedFilename,\n };\n}\n\n/**\n * Check if a file extension is considered dangerous/executable.\n *\n * @param filename - Filename or extension to check\n * @returns true if the extension is dangerous\n */\nexport function isDangerousExtension(filename: string): boolean {\n const ext = getExtension(filename);\n return ext !== '' && DANGEROUS_EXTENSIONS.has(ext);\n}\n","/**\n * @module @arcis/node/validation/url\n * SSRF (Server-Side Request Forgery) prevention\n *\n * Validates URLs to ensure they don't target private/internal networks,\n * localhost, cloud metadata endpoints, or use dangerous protocols.\n *\n * @example\n * import { validateUrl } from '@arcis/node';\n *\n * // Block SSRF attempts\n * validateUrl('http://169.254.169.254/latest/meta-data/') // { safe: false, reason: 'link-local address' }\n * validateUrl('http://10.0.0.1/admin') // { safe: false, reason: 'private address (10.0.0.0/8)' }\n * validateUrl('http://localhost/secret') // { safe: false, reason: 'loopback address' }\n * validateUrl('file:///etc/passwd') // { safe: false, reason: 'disallowed protocol: file:' }\n *\n * // Allow safe URLs\n * validateUrl('https://api.example.com/data') // { safe: true }\n */\n\n/** Options for URL validation */\nexport interface ValidateUrlOptions {\n /** Allowed protocols. Default: ['http:', 'https:'] */\n allowedProtocols?: string[];\n /** Additional hostnames to block (e.g., internal service names) */\n blockedHosts?: string[];\n /** Additional hostnames to always allow (bypass IP checks) */\n allowedHosts?: string[];\n /** Allow localhost/loopback. Default: false */\n allowLocalhost?: boolean;\n /** Allow private/internal IPs. Default: false */\n allowPrivate?: boolean;\n}\n\n/** Result of URL validation */\nexport interface ValidateUrlResult {\n /** Whether the URL is safe to fetch */\n safe: boolean;\n /** Reason the URL was blocked (only set when safe=false) */\n reason?: string;\n}\n\n/**\n * Validate a URL for SSRF safety.\n *\n * Checks:\n * 1. Valid URL format\n * 2. Allowed protocol (default: http, https only)\n * 3. Not localhost/loopback (127.x.x.x, ::1, localhost)\n * 4. Not private IP (10.x, 172.16-31.x, 192.168.x)\n * 5. Not link-local (169.254.x.x — includes AWS/GCP/Azure metadata)\n * 6. Not blocked hostname\n * 7. No credentials in URL (user:pass@host)\n *\n * @param url - The URL string to validate\n * @param options - Validation options\n * @returns Validation result with safe flag and optional reason\n */\nexport function validateUrl(url: string, options: ValidateUrlOptions = {}): ValidateUrlResult {\n const {\n allowedProtocols = ['http:', 'https:'],\n blockedHosts = [],\n allowedHosts = [],\n allowLocalhost = false,\n allowPrivate = false,\n } = options;\n\n if (typeof url !== 'string' || url.trim() === '') {\n return { safe: false, reason: 'invalid URL: empty or not a string' };\n }\n\n // Parse URL\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return { safe: false, reason: 'invalid URL: failed to parse' };\n }\n\n // Check protocol\n if (!allowedProtocols.includes(parsed.protocol)) {\n return { safe: false, reason: `disallowed protocol: ${parsed.protocol}` };\n }\n\n // Check for credentials in URL (user:pass@host)\n if (parsed.username || parsed.password) {\n return { safe: false, reason: 'URL contains credentials' };\n }\n\n const hostname = parsed.hostname.toLowerCase();\n\n // Check explicit allowlist first (bypass IP checks)\n if (allowedHosts.some(h => hostname === h.toLowerCase())) {\n return { safe: true };\n }\n\n // Check explicit blocklist\n if (blockedHosts.some(h => hostname === h.toLowerCase())) {\n return { safe: false, reason: `blocked host: ${hostname}` };\n }\n\n // Check localhost/loopback\n if (!allowLocalhost) {\n if (\n hostname === 'localhost' ||\n hostname === '127.0.0.1' ||\n hostname === '[::1]' ||\n hostname === '::1' ||\n hostname === '0.0.0.0' ||\n hostname.endsWith('.localhost')\n ) {\n return { safe: false, reason: 'loopback address' };\n }\n\n // Check 127.x.x.x range\n if (/^127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$/.test(hostname)) {\n return { safe: false, reason: 'loopback address' };\n }\n }\n\n // Check decimal IP (e.g., 2130706433 = 127.0.0.1)\n if (!allowLocalhost || !allowPrivate) {\n const decimalCheck = checkDecimalIp(hostname, allowLocalhost, allowPrivate);\n if (decimalCheck) {\n return { safe: false, reason: decimalCheck };\n }\n }\n\n // Check octal IP (e.g., 0177.0.0.1 = 127.0.0.1)\n if (!allowLocalhost || !allowPrivate) {\n const octalCheck = checkOctalIp(hostname, allowLocalhost, allowPrivate);\n if (octalCheck) {\n return { safe: false, reason: octalCheck };\n }\n }\n\n // Check private/internal IPs\n if (!allowPrivate) {\n const privateCheck = checkPrivateIp(hostname);\n if (privateCheck) {\n return { safe: false, reason: privateCheck };\n }\n }\n\n return { safe: true };\n}\n\n/**\n * Convenience wrapper that returns true/false.\n *\n * @param url - The URL to check\n * @param options - Validation options\n * @returns true if the URL is safe to fetch\n */\nexport function isUrlSafe(url: string, options: ValidateUrlOptions = {}): boolean {\n return validateUrl(url, options).safe;\n}\n\n/**\n * Check if a hostname is a private/internal IP address.\n * Returns the reason string if private, or null if not.\n */\nfunction checkPrivateIp(hostname: string): string | null {\n // 10.0.0.0/8\n if (/^10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$/.test(hostname)) {\n return 'private address (10.0.0.0/8)';\n }\n\n // 172.16.0.0/12 (172.16.x.x - 172.31.x.x)\n const match172 = hostname.match(/^172\\.(\\d{1,3})\\.\\d{1,3}\\.\\d{1,3}$/);\n if (match172) {\n const second = parseInt(match172[1], 10);\n if (second >= 16 && second <= 31) {\n return 'private address (172.16.0.0/12)';\n }\n }\n\n // 192.168.0.0/16\n if (/^192\\.168\\.\\d{1,3}\\.\\d{1,3}$/.test(hostname)) {\n return 'private address (192.168.0.0/16)';\n }\n\n // 169.254.0.0/16 — link-local, includes cloud metadata endpoints\n // AWS: 169.254.169.254, GCP: metadata.google.internal, Azure: 169.254.169.254\n if (/^169\\.254\\.\\d{1,3}\\.\\d{1,3}$/.test(hostname)) {\n return 'link-local address (169.254.0.0/16)';\n }\n\n // 0.0.0.0/8 (current network)\n if (/^0\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$/.test(hostname)) {\n return 'current network address (0.0.0.0/8)';\n }\n\n // Cloud metadata hostnames\n if (\n hostname === 'metadata.google.internal' ||\n hostname === 'metadata.internal' ||\n hostname === 'metadata.azure.internal'\n ) {\n return 'cloud metadata endpoint';\n }\n\n // IPv6 private ranges (bracket-wrapped in URLs)\n let ipv6 = hostname.replace(/^\\[|\\]$/g, '');\n // Strip zone ID (e.g., ::1%eth0 → ::1)\n const zoneIdx = ipv6.indexOf('%');\n if (zoneIdx !== -1) {\n ipv6 = ipv6.slice(0, zoneIdx);\n }\n if (\n ipv6 === '::1' ||\n ipv6 === '::' ||\n /^fc[0-9a-f]{2}:/i.test(ipv6) ||\n /^fd[0-9a-f]{2}:/i.test(ipv6) ||\n /^fe80:/i.test(ipv6) ||\n /^ff[0-9a-f]{2}:/i.test(ipv6) // IPv6 multicast (ff00::/8)\n ) {\n return 'private IPv6 address';\n }\n\n // IPv6-mapped IPv4 — dotted form (::ffff:127.0.0.1)\n const mappedDotted = ipv6.match(/^::ffff:(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})$/i);\n if (mappedDotted) {\n const mappedIp = mappedDotted[1];\n if (/^127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$/.test(mappedIp)) {\n return 'IPv6-mapped loopback address';\n }\n const mappedCheck = checkPrivateIp(mappedIp);\n if (mappedCheck) {\n return `IPv6-mapped ${mappedCheck}`;\n }\n }\n\n // IPv6-mapped IPv4 — hex form (::ffff:7f00:1 = 127.0.0.1)\n // Node's URL parser normalizes ::ffff:a.b.c.d to ::ffff:XXYY:ZZWW\n const mappedHex = ipv6.match(/^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i);\n if (mappedHex) {\n const hi = parseInt(mappedHex[1], 16);\n const lo = parseInt(mappedHex[2], 16);\n const a = (hi >> 8) & 0xFF;\n const b = hi & 0xFF;\n const c = (lo >> 8) & 0xFF;\n const d = lo & 0xFF;\n const dotted = `${a}.${b}.${c}.${d}`;\n if (a === 127) {\n return 'IPv6-mapped loopback address';\n }\n const hexCheck = checkPrivateIp(dotted);\n if (hexCheck) {\n return `IPv6-mapped ${hexCheck}`;\n }\n }\n\n return null;\n}\n\n/**\n * Parse a decimal integer as an IPv4 address and check if it's private/loopback.\n * e.g., 2130706433 = 127.0.0.1, 167772160 = 10.0.0.0\n */\nfunction checkDecimalIp(hostname: string, allowLocalhost: boolean, allowPrivate: boolean): string | null {\n // Must be a pure decimal integer\n if (!/^\\d+$/.test(hostname)) return null;\n\n const num = parseInt(hostname, 10);\n if (isNaN(num) || num < 0 || num > 0xFFFFFFFF) return null;\n\n const a = (num >>> 24) & 0xFF;\n const b = (num >>> 16) & 0xFF;\n const c = (num >>> 8) & 0xFF;\n const d = num & 0xFF;\n const dotted = `${a}.${b}.${c}.${d}`;\n\n // Check loopback\n if (!allowLocalhost && a === 127) {\n return `loopback address (decimal IP: ${dotted})`;\n }\n\n // Check private ranges\n if (!allowPrivate) {\n const privateCheck = checkPrivateIp(dotted);\n if (privateCheck) {\n return `${privateCheck} (decimal IP: ${dotted})`;\n }\n }\n\n return null;\n}\n\n/**\n * Parse octal-notation IPv4 address and check if it's private/loopback.\n * e.g., 0177.0.0.1 = 127.0.0.1, 0x7f.0.0.1 = 127.0.0.1\n */\nfunction checkOctalIp(hostname: string, allowLocalhost: boolean, allowPrivate: boolean): string | null {\n // Must look like a dotted quad where at least one octet has a leading zero or 0x prefix\n const parts = hostname.split('.');\n if (parts.length !== 4) return null;\n\n // Check if any part uses octal (leading 0) or hex (0x) notation\n const hasAlternateNotation = parts.some(p => /^0[0-7]+$/.test(p) || /^0x[0-9a-fA-F]+$/i.test(p));\n if (!hasAlternateNotation) return null;\n\n const octets: number[] = [];\n for (const part of parts) {\n let val: number;\n if (/^0x[0-9a-fA-F]+$/i.test(part)) {\n val = parseInt(part, 16);\n } else if (/^0[0-7]*$/.test(part)) {\n val = parseInt(part, 8);\n } else if (/^\\d+$/.test(part)) {\n val = parseInt(part, 10);\n } else {\n return null;\n }\n if (val < 0 || val > 255) return null;\n octets.push(val);\n }\n\n const dotted = octets.join('.');\n\n // Check loopback\n if (!allowLocalhost && octets[0] === 127) {\n return `loopback address (octal IP: ${dotted})`;\n }\n\n // Check private ranges\n if (!allowPrivate) {\n const privateCheck = checkPrivateIp(dotted);\n if (privateCheck) {\n return `${privateCheck} (octal IP: ${dotted})`;\n }\n }\n\n return null;\n}\n","/**\n * @module @arcis/node/validation/redirect\n * Open Redirect prevention\n *\n * Prevents attackers from using your app to redirect users to malicious sites\n * via manipulated query parameters like ?returnUrl=http://evil.com\n *\n * @example\n * import { validateRedirect, isRedirectSafe } from '@arcis/node';\n *\n * // Block open redirects\n * validateRedirect('http://evil.com') // { safe: false, reason: 'absolute URL not in allowed hosts' }\n * validateRedirect('//evil.com') // { safe: false, reason: 'protocol-relative URL not in allowed hosts' }\n * validateRedirect('javascript:alert(1)') // { safe: false, reason: 'dangerous protocol: javascript:' }\n *\n * // Allow safe redirects\n * validateRedirect('/dashboard') // { safe: true }\n * validateRedirect('/users?page=2') // { safe: true }\n * validateRedirect('https://myapp.com/home', { allowedHosts: ['myapp.com'] }) // { safe: true }\n */\n\n/** Options for redirect validation */\nexport interface ValidateRedirectOptions {\n /** Hostnames that are allowed for absolute URL redirects */\n allowedHosts?: string[];\n /** Allow protocol-relative URLs (//example.com). Default: false */\n allowProtocolRelative?: boolean;\n /** Allowed protocols for absolute URLs. Default: ['http:', 'https:'] */\n allowedProtocols?: string[];\n}\n\n/** Result of redirect validation */\nexport interface ValidateRedirectResult {\n /** Whether the redirect URL is safe */\n safe: boolean;\n /** Reason the redirect was blocked (only set when safe=false) */\n reason?: string;\n}\n\n/** Protocols that can execute code or exfiltrate data */\nconst DANGEROUS_PROTOCOLS = /^(javascript|data|vbscript|blob):/i;\n\n/** Characters used to disguise URLs (tabs, newlines inside scheme) */\nconst CONTROL_CHARS = /[\\t\\n\\r]/g;\n\n/**\n * Validate a redirect URL to prevent open redirect attacks.\n *\n * Safe redirects:\n * - Relative paths: /dashboard, /users?page=2, ../settings\n * - Absolute URLs to allowed hosts (when configured)\n *\n * Blocked redirects:\n * - Absolute URLs to unknown hosts\n * - Protocol-relative URLs (//evil.com)\n * - javascript:, data:, vbscript:, blob: protocols\n * - Backslash-prefixed paths (\\\\evil.com — browser treats as //)\n * - URLs with control characters that could disguise the target\n *\n * @param url - The redirect target URL to validate\n * @param options - Validation options\n * @returns Validation result with safe flag and optional reason\n */\nexport function validateRedirect(\n url: string,\n options: ValidateRedirectOptions = {},\n): ValidateRedirectResult {\n const {\n allowedHosts = [],\n allowProtocolRelative = false,\n allowedProtocols = ['http:', 'https:'],\n } = options;\n\n if (typeof url !== 'string' || url.trim() === '') {\n return { safe: false, reason: 'invalid redirect: empty or not a string' };\n }\n\n // Strip control characters that could disguise the URL\n const cleaned = url.replace(CONTROL_CHARS, '');\n\n // Block dangerous protocols (javascript:, data:, etc.)\n if (DANGEROUS_PROTOCOLS.test(cleaned)) {\n const proto = cleaned.match(DANGEROUS_PROTOCOLS);\n return { safe: false, reason: `dangerous protocol: ${proto![0]}` };\n }\n\n // Block backslash-prefixed paths — browsers treat \\ as / in URLs\n // so \\evil.com or \\/evil.com could redirect to //evil.com\n if (cleaned.startsWith('\\\\')) {\n return { safe: false, reason: 'backslash-prefixed URL (browser treats as protocol-relative)' };\n }\n\n // Check protocol-relative URLs (//evil.com)\n if (cleaned.startsWith('//')) {\n if (!allowProtocolRelative) {\n // Still check allowedHosts\n const host = extractHost(cleaned);\n if (host && allowedHosts.some(h => host === h.toLowerCase())) {\n return { safe: true };\n }\n return { safe: false, reason: 'protocol-relative URL not in allowed hosts' };\n }\n const host = extractHost(cleaned);\n if (host && allowedHosts.length > 0 && !allowedHosts.some(h => host === h.toLowerCase())) {\n return { safe: false, reason: 'protocol-relative URL not in allowed hosts' };\n }\n return { safe: true };\n }\n\n // Check if it's an absolute URL (has scheme)\n let parsed: URL;\n try {\n parsed = new URL(cleaned);\n } catch {\n // Not a valid absolute URL — treat as relative path (safe)\n return { safe: true };\n }\n\n // If we got here, it parsed as an absolute URL\n // Check protocol\n if (!allowedProtocols.includes(parsed.protocol)) {\n return { safe: false, reason: `disallowed protocol: ${parsed.protocol}` };\n }\n\n // Check if host is in allowed list\n const hostname = parsed.hostname.toLowerCase();\n if (allowedHosts.length === 0) {\n return { safe: false, reason: 'absolute URL not in allowed hosts' };\n }\n\n if (!allowedHosts.some(h => hostname === h.toLowerCase())) {\n return { safe: false, reason: `host not allowed: ${hostname}` };\n }\n\n return { safe: true };\n}\n\n/**\n * Convenience wrapper that returns true/false.\n *\n * @param url - The redirect URL to check\n * @param options - Validation options\n * @returns true if the redirect is safe\n */\nexport function isRedirectSafe(url: string, options: ValidateRedirectOptions = {}): boolean {\n return validateRedirect(url, options).safe;\n}\n\n/**\n * Extract hostname from a protocol-relative URL.\n */\nfunction extractHost(url: string): string | null {\n // //hostname/path or //hostname:port/path\n const match = url.match(/^\\/\\/([^/:?#]+)/);\n return match ? match[1].toLowerCase() : null;\n}\n","/**\n * @module @arcis/node/validation/email\n * Advanced email validation with disposable detection and typo suggestions.\n *\n * Three levels of validation:\n * 1. Syntax — RFC-compliant format checking\n * 2. Domain intelligence — disposable/free provider detection, typo correction\n * 3. MX verification — DNS MX record lookup (async, optional)\n *\n * @example\n * const result = validateEmail('user@tempmail.com');\n * // { valid: false, reason: 'disposable' }\n *\n * const result = validateEmail('user@gmial.com');\n * // { valid: true, reason: 'typo', suggestion: 'user@gmail.com' }\n */\n\nimport { promises as dns } from 'dns';\n\nexport interface EmailValidationOptions {\n /** Check for disposable email providers. Default: true */\n checkDisposable?: boolean;\n /** Suggest corrections for typos. Default: true */\n suggestTypoFix?: boolean;\n /** Verify MX records via DNS. Default: false */\n checkMx?: boolean;\n /** Additional blocked domains */\n blockedDomains?: string[];\n /** Additional allowed domains (bypasses disposable check) */\n allowedDomains?: string[];\n}\n\nexport interface EmailValidationResult {\n /** Whether the email is valid */\n valid: boolean;\n /** Reason for the result */\n reason: 'valid' | 'invalid_syntax' | 'disposable' | 'no_mx' | 'blocked' | 'typo';\n /** Suggested correction if a typo was detected */\n suggestion: string | null;\n /** Whether the domain is a free email provider */\n isFree: boolean;\n /** Whether the domain is a disposable email provider */\n isDisposable: boolean;\n /** The normalized email address */\n normalized: string;\n}\n\n// RFC 5321: local part max 64, domain max 255, total max 254\nconst MAX_EMAIL_LENGTH = 254;\nconst MAX_LOCAL_LENGTH = 64;\nconst MAX_DOMAIN_LENGTH = 255;\n\n/**\n * Strict email syntax regex.\n * - No consecutive dots in local part\n * - No leading/trailing dots in local part\n * - Domain must have at least one dot\n * - No spaces anywhere\n */\nconst EMAIL_SYNTAX = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\\.[a-zA-Z]{2,}$/;\n\n/** Common free email providers */\nconst FREE_PROVIDERS = new Set([\n 'gmail.com', 'yahoo.com', 'hotmail.com', 'outlook.com', 'aol.com',\n 'protonmail.com', 'proton.me', 'icloud.com', 'mail.com', 'zoho.com',\n 'yandex.com', 'gmx.com', 'gmx.net', 'live.com', 'msn.com',\n 'me.com', 'mac.com', 'fastmail.com', 'tutanota.com', 'hey.com',\n]);\n\n/** Common disposable email domains */\nconst DISPOSABLE_DOMAINS = new Set([\n // Popular disposable services\n 'guerrillamail.com', 'guerrillamail.net', 'guerrillamail.org',\n 'tempmail.com', 'temp-mail.org', 'temp-mail.io',\n 'throwaway.email', 'throwaway.com',\n 'mailinator.com', 'mailinator.net',\n 'yopmail.com', 'yopmail.fr', 'yopmail.net',\n 'sharklasers.com', 'grr.la', 'guerrillamail.info',\n 'guerrillamail.biz', 'guerrillamail.de',\n 'trashmail.com', 'trashmail.me', 'trashmail.net',\n 'dispostable.com', 'maildrop.cc',\n 'mailnesia.com', 'tempail.com',\n 'mohmal.com', 'getnada.com',\n 'emailondeck.com', 'discard.email',\n 'fakeinbox.com', 'mailcatch.com',\n 'mintemail.com', 'tempr.email',\n 'tempinbox.com', 'burnermail.io',\n 'mailsac.com', 'harakirimail.com',\n 'tempmailo.com', 'emailfake.com',\n 'crazymailing.com', 'armyspy.com',\n 'dayrep.com', 'einrot.com',\n 'fleckens.hu', 'gustr.com',\n 'jourrapide.com', 'rhyta.com',\n 'superrito.com', 'teleworm.us',\n '10minutemail.com', '10minutemail.net',\n 'minutemail.com', 'tempsky.com',\n 'spamgourmet.com', 'mytrashmail.com',\n 'mailexpire.com', 'safetymail.info',\n 'filzmail.com', 'trashymail.com',\n 'sharkmail.com', 'jetable.org',\n 'nospam.ze.tc', 'trash-me.com',\n 'dodgit.com', 'mailmoat.com',\n 'spamfree24.org', 'incognitomail.org',\n 'tempomail.fr', 'ephemail.net',\n 'hidemail.de', 'spaml.de',\n 'uggsrock.com', 'binkmail.com',\n 'suremail.info', 'bugmenot.com',\n]);\n\n/** Common typos and their corrections */\nconst DOMAIN_TYPOS: Record<string, string> = {\n 'gmial.com': 'gmail.com',\n 'gmaill.com': 'gmail.com',\n 'gmai.com': 'gmail.com',\n 'gamil.com': 'gmail.com',\n 'gnail.com': 'gmail.com',\n 'gmal.com': 'gmail.com',\n 'gmil.com': 'gmail.com',\n 'gmail.co': 'gmail.com',\n 'gmail.cm': 'gmail.com',\n 'gmail.om': 'gmail.com',\n 'gmail.con': 'gmail.com',\n 'gmail.cim': 'gmail.com',\n 'gmail.comm': 'gmail.com',\n 'yahooo.com': 'yahoo.com',\n 'yaho.com': 'yahoo.com',\n 'yahoo.co': 'yahoo.com',\n 'yahoo.cm': 'yahoo.com',\n 'yahoo.con': 'yahoo.com',\n 'yahho.com': 'yahoo.com',\n 'hotmial.com': 'hotmail.com',\n 'hotmal.com': 'hotmail.com',\n 'hotmai.com': 'hotmail.com',\n 'hotmil.com': 'hotmail.com',\n 'hotmail.co': 'hotmail.com',\n 'hotmail.cm': 'hotmail.com',\n 'hotmail.con': 'hotmail.com',\n 'outlok.com': 'outlook.com',\n 'outloo.com': 'outlook.com',\n 'outlook.co': 'outlook.com',\n 'outlook.cm': 'outlook.com',\n 'protonmal.com': 'protonmail.com',\n 'protonmail.co': 'protonmail.com',\n 'icloud.co': 'icloud.com',\n 'icloud.cm': 'icloud.com',\n 'icoud.com': 'icloud.com',\n};\n\nfunction invalidResult(reason: EmailValidationResult['reason'], email: string): EmailValidationResult {\n return {\n valid: false,\n reason,\n suggestion: null,\n isFree: false,\n isDisposable: false,\n normalized: email,\n };\n}\n\n/**\n * Validate an email address with syntax checking, disposable detection,\n * and typo suggestions.\n *\n * @param email - Email address to validate\n * @param options - Validation options\n * @returns Validation result\n *\n * @example\n * validateEmail('user@gmail.com')\n * // { valid: true, reason: 'valid', isFree: true }\n *\n * validateEmail('user@tempmail.com')\n * // { valid: false, reason: 'disposable' }\n *\n * validateEmail('user@gmial.com')\n * // { valid: true, reason: 'typo', suggestion: 'user@gmail.com' }\n */\nexport function validateEmail(\n email: string,\n options: EmailValidationOptions = {}\n): EmailValidationResult {\n const {\n checkDisposable = true,\n suggestTypoFix = true,\n blockedDomains = [],\n allowedDomains = [],\n } = options;\n\n // Normalize\n const normalized = email.trim().toLowerCase();\n\n // Basic checks\n if (!normalized || normalized.length > MAX_EMAIL_LENGTH) {\n return invalidResult('invalid_syntax', normalized);\n }\n\n const atIndex = normalized.lastIndexOf('@');\n if (atIndex === -1) {\n return invalidResult('invalid_syntax', normalized);\n }\n\n const localPart = normalized.slice(0, atIndex);\n const domain = normalized.slice(atIndex + 1);\n\n // Length checks\n if (localPart.length === 0 || localPart.length > MAX_LOCAL_LENGTH) {\n return invalidResult('invalid_syntax', normalized);\n }\n if (domain.length === 0 || domain.length > MAX_DOMAIN_LENGTH) {\n return invalidResult('invalid_syntax', normalized);\n }\n\n // Consecutive dots in local part\n if (localPart.includes('..')) {\n return invalidResult('invalid_syntax', normalized);\n }\n\n // Leading/trailing dots in local part\n if (localPart.startsWith('.') || localPart.endsWith('.')) {\n return invalidResult('invalid_syntax', normalized);\n }\n\n // Full regex validation\n if (!EMAIL_SYNTAX.test(normalized)) {\n return invalidResult('invalid_syntax', normalized);\n }\n\n // Check if domain is explicitly allowed (bypass other checks)\n const allowedSet = new Set(allowedDomains.map(d => d.toLowerCase()));\n if (allowedSet.has(domain)) {\n return {\n valid: true,\n reason: 'valid',\n suggestion: null,\n isFree: FREE_PROVIDERS.has(domain),\n isDisposable: false,\n normalized,\n };\n }\n\n // Check blocked domains\n const blockedSet = new Set(blockedDomains.map(d => d.toLowerCase()));\n if (blockedSet.has(domain)) {\n return invalidResult('blocked', normalized);\n }\n\n // Check disposable\n const isDisposable = DISPOSABLE_DOMAINS.has(domain);\n if (checkDisposable && isDisposable) {\n return {\n valid: false,\n reason: 'disposable',\n suggestion: null,\n isFree: false,\n isDisposable: true,\n normalized,\n };\n }\n\n // Check typos\n const isFree = FREE_PROVIDERS.has(domain);\n if (suggestTypoFix && DOMAIN_TYPOS[domain]) {\n const corrected = `${localPart}@${DOMAIN_TYPOS[domain]}`;\n return {\n valid: true,\n reason: 'typo',\n suggestion: corrected,\n isFree: FREE_PROVIDERS.has(DOMAIN_TYPOS[domain]),\n isDisposable: false,\n normalized,\n };\n }\n\n return {\n valid: true,\n reason: 'valid',\n suggestion: null,\n isFree,\n isDisposable,\n normalized,\n };\n}\n\n/**\n * Verify that the email domain has MX records (can receive email).\n *\n * This performs a DNS lookup and requires network access.\n * Use for registration flows where you need high confidence.\n *\n * @param email - Email address to verify\n * @returns True if the domain has MX records\n *\n * @example\n * if (await verifyEmailMx('user@example.com')) {\n * // Domain can receive email\n * }\n */\nexport async function verifyEmailMx(email: string): Promise<boolean> {\n if (!isValidEmailSyntax(email)) return false;\n\n const atIndex = email.lastIndexOf('@');\n const domain = email.slice(atIndex + 1).trim().toLowerCase();\n if (!domain) return false;\n\n try {\n const records = await dns.resolveMx(domain);\n return records.length > 0;\n } catch {\n return false;\n }\n}\n\n/**\n * Quick check if an email address has valid syntax.\n * Faster than validateEmail() — just syntax, no domain intelligence.\n */\nexport function isValidEmailSyntax(email: string): boolean {\n const normalized = email.trim().toLowerCase();\n if (!normalized || normalized.length > MAX_EMAIL_LENGTH) return false;\n\n const atIndex = normalized.lastIndexOf('@');\n if (atIndex === -1) return false;\n\n const localPart = normalized.slice(0, atIndex);\n if (localPart.includes('..') || localPart.startsWith('.') || localPart.endsWith('.')) return false;\n\n return EMAIL_SYNTAX.test(normalized);\n}\n"]}
1
+ {"version":3,"sources":["../../src/core/constants.ts","../../src/core/errors.ts","../../src/sanitizers/utils.ts","../../src/sanitizers/xss.ts","../../src/sanitizers/sql.ts","../../src/sanitizers/path.ts","../../src/sanitizers/command.ts","../../src/sanitizers/sanitize.ts","../../src/validation/schema.ts","../../src/validation/file.ts","../../src/validation/url.ts","../../src/validation/url-async.ts","../../src/validation/redirect.ts","../../src/validation/email.ts"],"names":["dns","lookup","host"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @module @arcis/node/core/constants\n * Named constants for Arcis - no magic numbers\n */\n\n// =============================================================================\n// INPUT LIMITS\n// =============================================================================\nexport const INPUT = {\n /** Default maximum input size (1MB) */\n DEFAULT_MAX_SIZE: 1_000_000,\n /** Maximum recursion depth for nested objects */\n MAX_RECURSION_DEPTH: 10,\n} as const;\n\n// =============================================================================\n// RATE LIMITING\n// =============================================================================\nexport const RATE_LIMIT = {\n /** Default window size (1 minute) */\n DEFAULT_WINDOW_MS: 60_000,\n /** Default max requests per window */\n DEFAULT_MAX_REQUESTS: 100,\n /** Default HTTP status code for rate limited responses */\n DEFAULT_STATUS_CODE: 429,\n /** Default error message */\n DEFAULT_MESSAGE: 'Too many requests, please try again later.',\n /** Minimum window size (1 second) */\n MIN_WINDOW_MS: 1_000,\n /** Maximum window size (24 hours) */\n MAX_WINDOW_MS: 86_400_000,\n} as const;\n\n// =============================================================================\n// SECURITY HEADERS\n// =============================================================================\nexport const HEADERS = {\n /** Default Content Security Policy */\n DEFAULT_CSP: [\n \"default-src 'self'\",\n \"script-src 'self'\",\n \"style-src 'self' 'unsafe-inline'\",\n \"img-src 'self' data: https:\",\n \"font-src 'self'\",\n \"object-src 'none'\",\n \"frame-ancestors 'none'\",\n ].join('; '),\n /** Default HSTS max age (1 year in seconds) */\n HSTS_MAX_AGE: 31_536_000,\n /** Default X-Frame-Options value */\n FRAME_OPTIONS: 'DENY' as const,\n /** Default X-Content-Type-Options value */\n CONTENT_TYPE_OPTIONS: 'nosniff',\n /** Default Referrer-Policy value */\n REFERRER_POLICY: 'strict-origin-when-cross-origin',\n /** Default Permissions-Policy value */\n PERMISSIONS_POLICY: 'geolocation=(), microphone=(), camera=()',\n /** Default Cache-Control value for security */\n CACHE_CONTROL: 'no-store, no-cache, must-revalidate, proxy-revalidate',\n} as const;\n\n// =============================================================================\n// XSS PATTERNS (ReDoS-safe)\n// =============================================================================\n\n/**\n * Detection patterns — used to flag whether a string contains XSS payloads.\n * Must stay in sync with XSS_REMOVE_PATTERNS below.\n */\nexport const XSS_PATTERNS = [\n /** Script tags (ReDoS-safe version) */\n /<script[^>]*>[\\s\\S]*?<\\/script>/gi,\n /** javascript: protocol (allow optional spaces before colon) */\n /javascript\\s*:/gi,\n /** vbscript: protocol */\n /vbscript\\s*:/gi,\n /** Event handlers (onclick, onerror, etc.) — any separator before attribute */\n /(?:[\\s/])on\\w+\\s*=/gi,\n /** iframe tags */\n /<iframe/gi,\n /** object tags */\n /<object/gi,\n /** embed tags */\n /<embed/gi,\n /** data: URIs (only dangerous ones, avoid false positives) */\n /(?:^|[\\s\"'=])data:/gi,\n /** URL-encoded script tags */\n /%3Cscript/gi,\n /** SVG with onload */\n /<svg[^>]*onload/gi,\n /** form tags — phishing/credential harvesting via action= redirection */\n /<form[\\s>]/gi,\n /** meta tags — http-equiv refresh redirects or CSP bypass */\n /<meta[\\s>]/gi,\n /** base href hijacking — redirects all relative URLs to attacker domain */\n /<base[\\s>]/gi,\n /** link tag injection — stylesheet or preload CSRF attacks */\n /<link[\\s>]/gi,\n /** style tag — CSS expression() / behavior: / IE-era attacks. Mirrors\n * Python's xss-style-tag from packages/core/patterns.json. */\n /<style[\\s>]/gi,\n] as const;\n\n/**\n * Removal patterns — used by sanitizeXss() to strip dangerous content.\n * More targeted than XSS_PATTERNS: each pattern captures the full dangerous\n * substring (tag, attribute + value, protocol) so it can be replaced safely.\n * Must stay in sync with XSS_PATTERNS above.\n */\nexport const XSS_REMOVE_PATTERNS = [\n /** Full script blocks (content + tags) */\n /<script[^>]*>[\\s\\S]*?<\\/script>/gi,\n /** Standalone/unclosed script tags */\n /<script[^>]*>/gi,\n /** style — CSS expression() and behavior: attacks (IE-era but still relevant) */\n /<style[^>]*>[\\s\\S]*?<\\/style>/gi,\n /<style[^>]*/gi,\n /** iframe — full block and partial/unclosed */\n /<iframe[^>]*>[\\s\\S]*?<\\/iframe>/gi,\n /<iframe[^>]*/gi,\n /** object — full block and partial/unclosed */\n /<object[^>]*>[\\s\\S]*?<\\/object>/gi,\n /<object[^>]*/gi,\n /** embed tags */\n /<embed[^>]*/gi,\n /** SVG with inline event handlers */\n /<svg[^>]*onload[^>]*>/gi,\n /** URL-encoded script tags */\n /%3Cscript/gi,\n /** Event handlers with quoted values: onclick=\"...\", onerror='...' */\n /(?:[\\s/])on\\w+\\s*=\\s*[\"'][^\"']*[\"']/gi,\n /** Event handlers with unquoted values: onload=value */\n /(?:[\\s/])on\\w+\\s*=\\s*[^\\s>]*/gi,\n /** javascript: and vbscript: protocols (allow optional spaces before colon) */\n /javascript\\s*:/gi,\n /vbscript\\s*:/gi,\n /** data: URIs with HTML or SVG content (SVG can run JS via inline event handlers) */\n /data\\s*:\\s*(?:text\\/html|image\\/svg)[^>\\s]*/gi,\n /** form tag injection — phishing via action= redirection */\n /<form[\\s>][^>]*/gi,\n /** meta tag injection — http-equiv refresh or CSP bypass */\n /<meta[\\s>][^>]*/gi,\n /** base href hijacking */\n /<base[\\s>][^>]*/gi,\n /** link tag injection — stylesheet or preload attacks */\n /<link[\\s>][^>]*/gi,\n] as const;\n\n// =============================================================================\n// SQL INJECTION PATTERNS\n// =============================================================================\nexport const SQL_PATTERNS = [\n /** SQL keywords */\n /(\\b(SELECT|INSERT|UPDATE|DELETE|DROP|UNION|ALTER|CREATE|TRUNCATE|EXEC|EXECUTE)\\b)/gi,\n /** SQL comments: ANSI (--), C-style (slash-star ... star-slash), MySQL (#) */\n /(--|\\/\\*|\\*\\/|#)/g,\n /** SQL statement separators */\n /(;|\\|\\||&&)/g,\n /** Boolean injection: OR 1=1 */\n /\\bOR\\s+\\d+\\s*=\\s*\\d+/gi,\n /** Boolean injection: OR 'a'='a' or OR \"a\"=\"a\" (including mixed quotes) */\n /\\bOR\\s+(['\"])[^'\"]*\\1\\s*=\\s*(['\"])[^'\"]*\\2/gi,\n /\\bOR\\s+('[^']*'|\"[^\"]*\")\\s*=\\s*('[^']*'|\"[^\"]*\")/gi,\n /** Boolean injection: AND 1=1 */\n /\\bAND\\s+\\d+\\s*=\\s*\\d+/gi,\n /** Boolean injection: AND 'a'='a' or AND \"a\"=\"a\" (including mixed quotes) */\n /\\bAND\\s+(['\"])[^'\"]*\\1\\s*=\\s*(['\"])[^'\"]*\\2/gi,\n /\\bAND\\s+('[^']*'|\"[^\"]*\")\\s*=\\s*('[^']*'|\"[^\"]*\")/gi,\n /** Time-based blind: SLEEP() */\n /\\bSLEEP\\s*\\(\\s*\\d+\\s*\\)/gi,\n /** Time-based blind: BENCHMARK() */\n /\\bBENCHMARK\\s*\\(/gi,\n /** Time-based blind: PostgreSQL pg_sleep() */\n /\\bpg_sleep\\s*\\(/gi,\n /** Time-based blind: MSSQL WAITFOR DELAY */\n /\\bWAITFOR\\s+DELAY\\b/gi,\n] as const;\n\n// =============================================================================\n// PATH TRAVERSAL PATTERNS\n// =============================================================================\nexport const PATH_PATTERNS = [\n /** Unix path traversal */\n /\\.\\.\\//g,\n /** Windows path traversal */\n /\\.\\.\\\\/g,\n /** URL-encoded traversal (%2e%2e) */\n /%2e%2e/gi,\n /** Double URL-encoded traversal (%252e) */\n /%252e/gi,\n /** Mixed encoding: ..%2F */\n /\\.\\.%2F/gi,\n /** Mixed encoding: %2e./ and .%2e/ */\n /%2e\\.[\\\\/]/gi,\n /\\.%2e[\\\\/]/gi,\n /** Fully URL-encoded: %2e%2e%2f */\n /%2e%2e%2f/gi,\n /** Double URL-encoded forward slash: %252f */\n /%252f/gi,\n /** Dotdotslash bypass: ....// or ....\\\\ */\n /\\.{2,}[/\\\\]{2,}/g,\n /** Null byte injection in paths */\n /\\0/g,\n] as const;\n\n// =============================================================================\n// COMMAND INJECTION PATTERNS\n// =============================================================================\nexport const COMMAND_PATTERNS = [\n /**\n * Shell metacharacters that enable command chaining/substitution.\n * Bare ( and ) are excluded — they appear in common legitimate values\n * (function calls in code fields, math expressions, etc.).\n * Command substitution is caught by the $( combined pattern below.\n * NOTE: ';', '&', '|' may appear in legitimate URL query strings\n * and Markdown; consider disabling command checking (command: false)\n * for fields that intentionally allow those characters.\n */\n /[;&|`]/g,\n /** Command substitution: $( ... ) — matched as a pair to reduce false positives */\n /\\$\\(/g,\n /** URL-encoded control characters (%00-%0F): null, tab, vtab, formfeed, LF, CR */\n /%0[0-9a-f]/gi,\n] as const;\n\n// =============================================================================\n// DANGEROUS KEYS\n// =============================================================================\n\n/**\n * Prototype pollution keys to block.\n * Stored lowercase — always compare with key.toLowerCase().\n *\n * Includes:\n * - __proto__: direct prototype assignment\n * - constructor: access to constructor.prototype chain\n * - prototype: direct prototype property\n * - __defineGetter__/__defineSetter__: legacy property definition (can override getters/setters)\n * - __lookupGetter__/__lookupSetter__: legacy property introspection\n */\nexport const DANGEROUS_PROTO_KEYS = new Set([\n '__proto__',\n 'constructor',\n 'prototype',\n '__definegetter__',\n '__definesetter__',\n '__lookupgetter__',\n '__lookupsetter__',\n]);\n\n/** MongoDB operators to block */\nexport const NOSQL_DANGEROUS_KEYS = new Set([\n // Comparison\n '$gt', '$gte', '$lt', '$lte', '$ne', '$eq', '$in', '$nin',\n // Logical\n '$and', '$or', '$not', '$nor',\n // Element / evaluation\n '$exists', '$type', '$regex', '$where', '$expr', '$mod', '$text', '$jsonSchema',\n // Array\n '$elemMatch', '$all', '$size',\n // JavaScript execution (critical)\n '$function', '$accumulator',\n // Aggregation pipeline operators (injectable via $lookup etc.)\n '$lookup', '$match', '$project', '$group', '$sort', '$limit', '$skip',\n '$unwind', '$addFields', '$replaceRoot',\n]);\n\n// =============================================================================\n// REDACTION\n// =============================================================================\nexport const REDACTION = {\n /** Replacement text for redacted values */\n REPLACEMENT: '[REDACTED]',\n /** Truncation indicator */\n TRUNCATED: '[TRUNCATED]',\n /** Max depth indicator */\n MAX_DEPTH: '[MAX_DEPTH]',\n /** Default max message length */\n DEFAULT_MAX_LENGTH: 10_000,\n /** Default sensitive keys to redact */\n SENSITIVE_KEYS: new Set([\n 'password', 'passwd', 'pwd', 'secret', 'token', 'apikey',\n 'api_key', 'apiKey', 'auth', 'authorization', 'credit_card',\n 'creditcard', 'cc', 'ssn', 'social_security', 'private_key',\n 'privateKey', 'access_token', 'accessToken', 'refresh_token',\n 'refreshToken', 'bearer', 'jwt', 'session', 'cookie',\n 'credentials', 'x-api-key', 'x-auth-token',\n ]),\n} as const;\n\n// =============================================================================\n// VALIDATION PATTERNS\n// =============================================================================\nexport const VALIDATION = {\n /**\n * Email regex pattern.\n * Rejects consecutive dots in local part (e.g. test..foo@example.com),\n * leading/trailing dots, and other common invalid forms.\n */\n EMAIL: /^[^\\s@.][^\\s@]*(?:\\.[^\\s@.][^\\s@]*)*@[^\\s@]+\\.[^\\s@]+$/,\n /**\n * URL regex pattern.\n * Only allows http:// and https:// (case-insensitive scheme per\n * RFC 3986); explicitly rejects javascript:, data:, vbscript:, and\n * other dangerous URI schemes.\n */\n URL: /^https?:\\/\\/[^\\s/$.?#][^\\s]*$/i,\n /** UUID regex pattern (v4) */\n UUID: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,\n} as const;\n\n// =============================================================================\n// ERROR MESSAGES\n// =============================================================================\nexport const ERRORS = {\n /** Generic error message (production) */\n INTERNAL_SERVER_ERROR: 'Internal Server Error',\n /** Input too large error */\n INPUT_TOO_LARGE: (maxSize: number) => `Input exceeds maximum size of ${maxSize} bytes`,\n /** Validation error messages */\n VALIDATION: {\n REQUIRED: (field: string) => `${field} is required`,\n INVALID_TYPE: (field: string, type: string) => `${field} must be a ${type}`,\n MIN_LENGTH: (field: string, min: number) => `${field} must be at least ${min} characters`,\n MAX_LENGTH: (field: string, max: number) => `${field} must be at most ${max} characters`,\n MIN_VALUE: (field: string, min: number) => `${field} must be at least ${min}`,\n MAX_VALUE: (field: string, max: number) => `${field} must be at most ${max}`,\n INVALID_FORMAT: (field: string) => `${field} format is invalid`,\n INVALID_EMAIL: (field: string) => `${field} must be a valid email`,\n INVALID_URL: (field: string) => `${field} must be a valid URL`,\n INVALID_UUID: (field: string) => `${field} must be a valid UUID`,\n INVALID_ENUM: (field: string, values: unknown[]) => `${field} must be one of: ${values.join(', ')}`,\n MIN_ITEMS: (field: string, min: number) => `${field} must have at least ${min} items`,\n MAX_ITEMS: (field: string, max: number) => `${field} must have at most ${max} items`,\n },\n} as const;\n\n// =============================================================================\n// BLOCKED TEXT (for sanitizer replacements)\n// =============================================================================\nexport const BLOCKED = '[BLOCKED]' as const;\n","/**\n * @module @arcis/node/core/errors\n * Custom error classes for Arcis\n */\n\n/**\n * Base class for all Arcis errors\n */\nexport class ArcisError extends Error {\n public readonly statusCode: number;\n public readonly code: string;\n /** Whether the error message is safe to expose to API clients. */\n public readonly expose: boolean;\n\n constructor(message: string, statusCode = 500, code = 'ARCIS_ERROR') {\n super(message);\n this.name = 'ArcisError';\n this.statusCode = statusCode;\n this.code = code;\n // Client errors (4xx) have controlled messages — safe to expose.\n // Server errors (5xx) may contain internal details — hide by default.\n this.expose = statusCode < 500;\n\n // Maintains proper stack trace for where error was thrown (V8 engines)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n }\n}\n\n/**\n * Error thrown when input validation fails\n */\nexport class ValidationError extends ArcisError {\n public readonly errors: string[];\n\n constructor(errors: string[]) {\n super('Validation failed', 400, 'VALIDATION_ERROR');\n this.name = 'ValidationError';\n this.errors = errors;\n }\n}\n\n/** Alias for ValidationError (backwards compatibility) */\nexport { ValidationError as ArcisValidationError };\n\n/**\n * Error thrown when rate limit is exceeded\n */\nexport class RateLimitError extends ArcisError {\n public readonly retryAfter: number;\n\n constructor(message: string, retryAfter: number) {\n super(message, 429, 'RATE_LIMIT_EXCEEDED');\n this.name = 'RateLimitError';\n this.retryAfter = retryAfter;\n }\n}\n\n/**\n * Error thrown when input is too large\n */\nexport class InputTooLargeError extends ArcisError {\n public readonly maxSize: number;\n public readonly actualSize: number;\n\n constructor(maxSize: number, actualSize: number) {\n super(`Input exceeds maximum size of ${maxSize} bytes`, 413, 'INPUT_TOO_LARGE');\n this.name = 'InputTooLargeError';\n this.maxSize = maxSize;\n this.actualSize = actualSize;\n }\n}\n\n/**\n * Error thrown when security threat is detected\n */\nexport class SecurityThreatError extends ArcisError {\n public readonly threatType: string;\n public readonly pattern: string;\n\n constructor(threatType: string, pattern: string) {\n super('Request blocked for security reasons', 400, 'SECURITY_THREAT');\n this.name = 'SecurityThreatError';\n this.threatType = threatType;\n this.pattern = pattern;\n }\n}\n\n/**\n * Error thrown when sanitization fails\n */\nexport class SanitizationError extends ArcisError {\n constructor(message: string) {\n super(message, 400, 'SANITIZATION_ERROR');\n this.name = 'SanitizationError';\n }\n}\n","/**\n * @module @arcis/node/sanitizers/utils\n * Shared utilities for sanitizers\n */\n\n/**\n * Encodes HTML entities to prevent interpretation as markup.\n * \n * @param str - The string to encode\n * @returns The encoded string\n */\nexport function encodeHtmlEntities(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#x27;');\n}\n\n/**\n * Checks if a value is a plain object (not null, array, Date, etc.)\n * \n * @param value - Value to check\n * @returns True if plain object\n */\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return false;\n }\n // Check the actual prototype chain rather than toString, which can be spoofed\n // via Symbol.toStringTag. Accepts both Object.prototype (plain {}) and null\n // prototype objects (Object.create(null)).\n const proto = Object.getPrototypeOf(value as object);\n return proto === Object.prototype || proto === null;\n}\n","/**\n * @module @arcis/node/sanitizers/xss\n * XSS (Cross-Site Scripting) prevention\n */\n\nimport { XSS_PATTERNS, XSS_REMOVE_PATTERNS } from '../core/constants';\nimport { encodeHtmlEntities } from './utils';\nimport type { SanitizeResult, ThreatInfo } from '../core/types';\n\n/**\n * Sanitizes a string to prevent XSS attacks.\n * \n * Strategy:\n * 1. Remove dangerous patterns (script tags, event handlers, etc.)\n * 2. HTML-encode the remaining content\n * \n * @param input - The string to sanitize\n * @param collectThreats - Whether to collect threat information (default: false for performance)\n * @returns Sanitized string or SanitizeResult if collectThreats is true\n * \n * @example\n * sanitizeXss(\"<script>alert('xss')</script>\")\n * // Returns: \"&lt;script&gt;alert(&#x27;xss&#x27;)&lt;/script&gt;\"\n * \n * @example\n * sanitizeXss(\"<img onerror='alert(1)'>\")\n * // Returns: \"&lt;img&gt;\" (event handler removed)\n */\nexport function sanitizeXss(input: string, collectThreats?: false, htmlEncode?: boolean): string;\nexport function sanitizeXss(input: string, collectThreats: true, htmlEncode?: boolean): SanitizeResult;\nexport function sanitizeXss(input: string, collectThreats = false, htmlEncode = false): string | SanitizeResult {\n if (typeof input !== 'string') {\n return collectThreats \n ? { value: String(input), wasSanitized: false, threats: [] }\n : String(input);\n }\n\n const threats: ThreatInfo[] = [];\n let value = input;\n let wasSanitized = false;\n\n // Remove dangerous patterns FIRST — XSS_REMOVE_PATTERNS is the single\n // source of truth (defined in constants.ts alongside XSS_PATTERNS).\n for (const pattern of XSS_REMOVE_PATTERNS) {\n pattern.lastIndex = 0;\n if (pattern.test(value)) {\n pattern.lastIndex = 0;\n \n if (collectThreats) {\n const matches = value.match(pattern);\n if (matches) {\n for (const match of matches) {\n threats.push({\n type: 'xss',\n pattern: pattern.source,\n original: match,\n });\n }\n }\n }\n \n value = value.replace(pattern, '');\n wasSanitized = true;\n }\n }\n\n // HTML-encode only when explicitly requested (SSR/template context).\n // Do NOT encode by default — this is a REST API middleware; encoding\n // here corrupts JSON data with HTML entities (&lt;, &amp;, etc.) that\n // consumers would receive verbatim.\n if (htmlEncode) {\n const encoded = encodeHtmlEntities(value);\n if (encoded !== value) {\n wasSanitized = true;\n }\n value = encoded;\n }\n\n if (collectThreats) {\n return { value, wasSanitized, threats };\n }\n \n return value;\n}\n\n/**\n * Checks if a string contains potential XSS patterns.\n * Does not sanitize — use sanitizeXss() for that.\n * \n * @param input - The string to check\n * @returns True if XSS patterns detected\n */\nexport function detectXss(input: string): boolean {\n if (typeof input !== 'string') return false;\n \n // Check for event handlers\n if (/\\s+on\\w+\\s*=/i.test(input)) return true;\n \n // Check for dangerous protocols\n if (/javascript\\s*:/i.test(input)) return true;\n if (/vbscript\\s*:/i.test(input)) return true;\n if (/data\\s*:\\s*text\\/html/i.test(input)) return true;\n \n // Check for patterns from constants\n for (const pattern of XSS_PATTERNS) {\n pattern.lastIndex = 0;\n if (pattern.test(input)) {\n return true;\n }\n }\n \n return false;\n}\n","/**\n * @module @arcis/node/sanitizers/sql\n * SQL injection prevention\n */\n\nimport { SQL_PATTERNS } from '../core/constants';\nimport type { SanitizeResult, ThreatInfo } from '../core/types';\n\n/**\n * Sanitizes a string to prevent SQL injection attacks.\n * Replaces dangerous SQL patterns with [BLOCKED].\n * \n * @param input - The string to sanitize\n * @param collectThreats - Whether to collect threat information (default: false for performance)\n * @returns Sanitized string or SanitizeResult if collectThreats is true\n * \n * @example\n * sanitizeSql(\"'; DROP TABLE users; --\")\n * // Returns: \"'; TABLE users \"\n */\nexport function sanitizeSql(input: string, collectThreats?: false): string;\nexport function sanitizeSql(input: string, collectThreats: true): SanitizeResult;\nexport function sanitizeSql(input: string, collectThreats = false): string | SanitizeResult {\n if (typeof input !== 'string') {\n return collectThreats \n ? { value: String(input), wasSanitized: false, threats: [] }\n : String(input);\n }\n\n const threats: ThreatInfo[] = [];\n let value = input;\n let wasSanitized = false;\n\n for (const pattern of SQL_PATTERNS) {\n // Reset regex lastIndex for global patterns\n pattern.lastIndex = 0;\n \n if (pattern.test(value)) {\n pattern.lastIndex = 0; // Reset again for replace\n \n if (collectThreats) {\n const matches = value.match(pattern);\n if (matches) {\n for (const match of matches) {\n threats.push({\n type: 'sql_injection',\n pattern: pattern.source,\n original: match,\n });\n }\n }\n }\n \n // Replace the matched content with a space to avoid concatenating surrounding\n // tokens into new dangerous strings (e.g. \"SELECTname\" after stripping \"SELECT\").\n value = value.replace(pattern, ' ');\n wasSanitized = true;\n }\n }\n\n if (collectThreats) {\n return { value, wasSanitized, threats };\n }\n \n return value;\n}\n\n/**\n * Checks if a string contains potential SQL injection patterns.\n * Does not sanitize — use sanitizeSql() for that.\n * \n * @param input - The string to check\n * @returns True if SQL injection patterns detected\n */\nexport function detectSql(input: string): boolean {\n if (typeof input !== 'string') return false;\n \n for (const pattern of SQL_PATTERNS) {\n pattern.lastIndex = 0;\n if (pattern.test(input)) {\n return true;\n }\n }\n \n return false;\n}\n","/**\n * @module @arcis/node/sanitizers/path\n * Path traversal prevention\n */\n\nimport { PATH_PATTERNS } from '../core/constants';\nimport type { SanitizeResult, ThreatInfo } from '../core/types';\n\n/**\n * Sanitizes a string to prevent path traversal attacks.\n * Removes ../ and ..\\ patterns (including URL-encoded variants).\n * \n * @param input - The string to sanitize\n * @param collectThreats - Whether to collect threat information (default: false for performance)\n * @returns Sanitized string or SanitizeResult if collectThreats is true\n * \n * @example\n * sanitizePath(\"../../etc/passwd\")\n * // Returns: \"etc/passwd\"\n */\nexport function sanitizePath(input: string, collectThreats?: false): string;\nexport function sanitizePath(input: string, collectThreats: true): SanitizeResult;\nexport function sanitizePath(input: string, collectThreats = false): string | SanitizeResult {\n if (typeof input !== 'string') {\n return collectThreats \n ? { value: String(input), wasSanitized: false, threats: [] }\n : String(input);\n }\n\n const threats: ThreatInfo[] = [];\n let value = input;\n let wasSanitized = false;\n\n // SECURITY: Normalize Unicode to NFKC before pattern matching.\n // Fullwidth dot U+FF0E normalizes to '.', preventing ../ bypass of ../ detection.\n value = value.normalize('NFKC');\n\n // Apply patterns repeatedly until the string stops changing.\n // Single-pass stripping is bypassable: \"....//\".replace(\"../\",\"\") → \"../\"\n let prev: string;\n do {\n prev = value;\n for (const pattern of PATH_PATTERNS) {\n pattern.lastIndex = 0;\n\n if (pattern.test(value)) {\n pattern.lastIndex = 0;\n\n if (collectThreats) {\n const matches = value.match(pattern);\n if (matches) {\n for (const match of matches) {\n threats.push({\n type: 'path_traversal',\n pattern: pattern.source,\n original: match,\n });\n }\n }\n }\n\n value = value.replace(pattern, '');\n wasSanitized = true;\n }\n }\n } while (value !== prev);\n\n if (collectThreats) {\n return { value, wasSanitized, threats };\n }\n \n return value;\n}\n\n/**\n * Checks if a string contains path traversal patterns.\n * Does not sanitize — use sanitizePath() for that.\n * \n * @param input - The string to check\n * @returns True if path traversal patterns detected\n */\nexport function detectPathTraversal(input: string): boolean {\n if (typeof input !== 'string') return false;\n\n // SECURITY: Normalize Unicode to NFKC — same as sanitizePath\n const normalized = input.normalize('NFKC');\n\n for (const pattern of PATH_PATTERNS) {\n pattern.lastIndex = 0;\n if (pattern.test(normalized)) {\n return true;\n }\n }\n \n return false;\n}\n","/**\n * @module @arcis/node/sanitizers/command\n * Command injection prevention\n */\n\nimport { COMMAND_PATTERNS } from '../core/constants';\nimport type { SanitizeResult, ThreatInfo } from '../core/types';\n\n/**\n * Sanitizes a string to prevent command injection attacks.\n * Replaces shell metacharacters and dangerous commands with [BLOCKED].\n * \n * @param input - The string to sanitize\n * @param collectThreats - Whether to collect threat information (default: false for performance)\n * @returns Sanitized string or SanitizeResult if collectThreats is true\n * \n * @example\n * sanitizeCommand(\"file.txt; rm -rf /\")\n * // Returns: \"file.txt rm -rf /\"\n */\nexport function sanitizeCommand(input: string, collectThreats?: false): string;\nexport function sanitizeCommand(input: string, collectThreats: true): SanitizeResult;\nexport function sanitizeCommand(input: string, collectThreats = false): string | SanitizeResult {\n if (typeof input !== 'string') {\n return collectThreats \n ? { value: String(input), wasSanitized: false, threats: [] }\n : String(input);\n }\n\n const threats: ThreatInfo[] = [];\n let value = input;\n let wasSanitized = false;\n\n for (const pattern of COMMAND_PATTERNS) {\n // Reset regex lastIndex for global patterns\n pattern.lastIndex = 0;\n \n if (pattern.test(value)) {\n pattern.lastIndex = 0; // Reset again for replace\n \n if (collectThreats) {\n const matches = value.match(pattern);\n if (matches) {\n for (const match of matches) {\n threats.push({\n type: 'command_injection',\n pattern: pattern.source,\n original: match,\n });\n }\n }\n }\n \n value = value.replace(pattern, ' ');\n wasSanitized = true;\n }\n }\n\n if (collectThreats) {\n return { value, wasSanitized, threats };\n }\n \n return value;\n}\n\n/**\n * Checks if a string contains command injection patterns.\n * Does not sanitize — use sanitizeCommand() for that.\n * \n * @param input - The string to check\n * @returns True if command injection patterns detected\n */\nexport function detectCommandInjection(input: string): boolean {\n if (typeof input !== 'string') return false;\n \n for (const pattern of COMMAND_PATTERNS) {\n pattern.lastIndex = 0;\n if (pattern.test(input)) {\n return true;\n }\n }\n \n return false;\n}\n","/**\n * @module @arcis/node/sanitizers/sanitize\n * Main sanitization functions that combine all sanitizers\n */\n\nimport type { Request, Response, NextFunction, RequestHandler } from 'express';\nimport { INPUT, DANGEROUS_PROTO_KEYS, NOSQL_DANGEROUS_KEYS } from '../core/constants';\nimport { InputTooLargeError, SecurityThreatError } from '../core/errors';\nimport type { SanitizeOptions } from '../core/types';\nimport { sanitizeXss, detectXss } from './xss';\nimport { sanitizeSql, detectSql } from './sql';\nimport { sanitizePath, detectPathTraversal } from './path';\nimport { sanitizeCommand, detectCommandInjection } from './command';\nimport { detectSsti } from './ssti';\nimport { detectXxe } from './xxe';\nimport { detectLdapInjection } from './ldap';\nimport { detectXpathInjection } from './xpath';\nimport { detectHeaderInjection } from './headers';\n\n/**\n * Sanitize a string value against multiple attack vectors.\n * \n * Order matters: We do XSS encoding LAST because:\n * 1. Other sanitizers need to see the original patterns (e.g., SQL keywords)\n * 2. HTML encoding is the final safe output transformation\n * 3. Encoded entities like &lt; shouldn't be treated as SQL/command threats\n * \n * @param value - The string to sanitize\n * @param options - Sanitization options\n * @returns The sanitized string\n * \n * @example\n * sanitizeString(\"<script>alert('xss')</script>\")\n * // Returns: \"&lt;script&gt;alert(&#x27;xss&#x27;)&lt;/script&gt;\"\n * \n * @example\n * sanitizeString(\"../../etc/passwd\")\n * // Returns: \"etc/passwd\"\n */\nexport function sanitizeString(value: string, options: SanitizeOptions = {}): string {\n if (typeof value !== 'string') return value;\n\n // Input size limit to prevent DoS\n const maxSize = options.maxSize ?? INPUT.DEFAULT_MAX_SIZE;\n if (value.length > maxSize) {\n throw new InputTooLargeError(maxSize, value.length);\n }\n\n // Default mode is 'sanitize' (strip threats and return cleaned string).\n // Pass mode: 'reject' to throw SecurityThreatError instead of stripping.\n const reject = options.mode === 'reject';\n let result = value;\n\n // 1. SQL injection\n if (options.sql !== false) {\n if (reject) {\n if (detectSql(result)) {\n throw new SecurityThreatError('sql_injection', 'SQL pattern detected in input');\n }\n } else {\n result = sanitizeSql(result);\n }\n }\n\n // 2. Path traversal prevention\n if (options.path !== false) {\n result = sanitizePath(result);\n }\n\n // 3. Command injection\n if (options.command !== false) {\n if (reject) {\n if (detectCommandInjection(result)) {\n throw new SecurityThreatError('command_injection', 'Shell metacharacter detected in input');\n }\n } else {\n result = sanitizeCommand(result);\n }\n }\n\n // 4. XSS stripping — always runs to remove dangerous patterns.\n // HTML encoding is opt-in via options.htmlEncode (for SSR contexts only).\n if (options.xss !== false) {\n result = sanitizeXss(result, false, options.htmlEncode ?? false);\n }\n\n return result;\n}\n\n/**\n * Sanitize an object recursively, including nested objects and arrays.\n * Also removes prototype pollution and NoSQL injection keys.\n * \n * @param obj - The object to sanitize\n * @param options - Sanitization options\n * @returns The sanitized object\n */\nexport function sanitizeObject(obj: unknown, options: SanitizeOptions = {}): unknown {\n if (obj === null || obj === undefined) return obj;\n if (typeof obj === 'string') return sanitizeString(obj, options);\n if (typeof obj !== 'object') return obj;\n if (Array.isArray(obj)) return obj.map(item => sanitizeObject(item, options));\n\n const result = sanitizeObjectDepth(obj as Record<string, unknown>, options, 0);\n return options.freeze ? Object.freeze(result) : result;\n}\n\n/**\n * Internal recursive sanitization with depth tracking.\n */\nfunction sanitizeObjectDepth(\n obj: Record<string, unknown>,\n options: SanitizeOptions,\n depth: number\n): Record<string, unknown> {\n if (depth >= INPUT.MAX_RECURSION_DEPTH) return obj;\n\n const result: Record<string, unknown> = {};\n\n for (const key of Object.keys(obj)) {\n // Prototype pollution protection - always block dangerous keys (case-insensitive)\n if (options.proto !== false && DANGEROUS_PROTO_KEYS.has(key.toLowerCase())) {\n continue;\n }\n\n // NoSQL injection - skip dangerous MongoDB operators in keys\n if (options.nosql !== false && NOSQL_DANGEROUS_KEYS.has(key)) {\n continue;\n }\n\n // Sanitize the key against all active threat vectors (not just XSS).\n // Keys can carry injection payloads that bubble into query builders or ORMs.\n const sanitizedKey = sanitizeString(key, options);\n\n // Recursively sanitize value\n const value = obj[key];\n if (value === null || value === undefined) {\n result[sanitizedKey] = value;\n } else if (typeof value === 'string') {\n result[sanitizedKey] = sanitizeString(value, options);\n } else if (Array.isArray(value)) {\n result[sanitizedKey] = value.map(item => sanitizeObject(item, options));\n } else if (typeof value === 'object') {\n result[sanitizedKey] = sanitizeObjectDepth(value as Record<string, unknown>, options, depth + 1);\n } else {\n result[sanitizedKey] = value;\n }\n }\n\n return result;\n}\n\n/** Threat triple returned from scanThreats. */\nexport interface ThreatHit {\n vector:\n | 'xss'\n | 'sql'\n | 'nosql'\n | 'path'\n | 'command'\n | 'prototype'\n | 'ssti'\n | 'xxe'\n | 'ldap'\n | 'xpath'\n | 'header';\n rule: string;\n matchedPattern: string;\n}\n\n/**\n * Walk a value (string, array, or object) and return the first threat hit\n * found. Used by block-mode middleware to attribute the deny decision.\n *\n * Vector ordering matches Python's scan_threats for cross-SDK parity.\n */\nexport function scanThreats(data: unknown, depth = 0): ThreatHit | null {\n if (depth > INPUT.MAX_RECURSION_DEPTH) return null;\n\n if (data && typeof data === 'object' && !Array.isArray(data)) {\n for (const key of Object.keys(data as Record<string, unknown>)) {\n const lower = key.toLowerCase();\n if (DANGEROUS_PROTO_KEYS.has(lower)) {\n return { vector: 'prototype', rule: 'prototype/match', matchedPattern: key };\n }\n if (NOSQL_DANGEROUS_KEYS.has(key)) {\n return { vector: 'nosql', rule: 'nosql/match', matchedPattern: key };\n }\n const inner = scanThreats((data as Record<string, unknown>)[key], depth + 1);\n if (inner) return inner;\n }\n return null;\n }\n\n if (Array.isArray(data)) {\n for (const item of data) {\n const inner = scanThreats(item, depth + 1);\n if (inner) return inner;\n }\n return null;\n }\n\n if (typeof data !== 'string') return null;\n\n const sample = data.slice(0, 80);\n if (detectXss(data)) {\n return { vector: 'xss', rule: 'xss/match', matchedPattern: sample };\n }\n if (detectSsti(data)) {\n return { vector: 'ssti', rule: 'ssti/match', matchedPattern: sample };\n }\n if (detectXxe(data)) {\n return { vector: 'xxe', rule: 'xxe/match', matchedPattern: sample };\n }\n if (detectSql(data)) {\n return { vector: 'sql', rule: 'sql/match', matchedPattern: sample };\n }\n if (detectPathTraversal(data)) {\n return { vector: 'path', rule: 'path/match', matchedPattern: sample };\n }\n if (detectCommandInjection(data)) {\n return { vector: 'command', rule: 'command/match', matchedPattern: sample };\n }\n // LDAP + XPath checks come AFTER command/path so a string that's\n // primarily a path-traversal payload (`../`) gets attributed to\n // path, not LDAP (the `\\` in `..\\..\\` would otherwise hit the LDAP\n // backslash filter).\n if (detectLdapInjection(data)) {\n return { vector: 'ldap', rule: 'ldap/match', matchedPattern: sample };\n }\n if (detectXpathInjection(data)) {\n return { vector: 'xpath', rule: 'xpath/match', matchedPattern: sample };\n }\n // Header injection (HTTP response splitting + email-header injection\n // share the same byte-level threat: CRLF in a value that gets\n // concatenated into a header). Last in the chain so the more-specific\n // detectors (xss / sql / etc.) win on input that's both — e.g. an XSS\n // payload with a stray newline still attributes to xss.\n if (detectHeaderInjection(data)) {\n return { vector: 'header', rule: 'header/match', matchedPattern: sample };\n }\n return null;\n}\n\n/**\n * Create Express middleware for request sanitization.\n * Sanitizes req.body, req.query, and req.params.\n * \n * @param options - Sanitization options\n * @returns Express middleware\n * \n * @example\n * app.use(createSanitizer());\n * \n * @example\n * app.use(createSanitizer({ xss: true, sql: true, nosql: true }));\n */\nexport function createSanitizer(options: SanitizeOptions = {}): RequestHandler {\n return (req: Request, res: Response, next: NextFunction) => {\n try {\n // Block mode: scan first, return 403 on threat. The telemetry emitter\n // reads the marker on res.finish to attribute the deny decision.\n if (options.block) {\n const hit =\n scanThreats(req.body) ||\n scanThreats(req.query) ||\n scanThreats(req.params) ||\n scanThreats(req.path);\n if (hit) {\n req.__arcis = {\n vector: hit.vector,\n rule: hit.rule,\n severity: 'high',\n matchedPattern: hit.matchedPattern,\n reason: `${hit.vector} pattern detected in request`,\n decision: 'deny',\n };\n res.status(403).json({\n error: 'Request blocked for security reasons',\n code: 'SECURITY_THREAT',\n vector: hit.vector,\n });\n return;\n }\n }\n\n if (req.body && typeof req.body === 'object') {\n req.body = sanitizeObject(req.body, options);\n }\n if (req.query && typeof req.query === 'object') {\n const sanitizedQuery = sanitizeObject(req.query, options);\n // Express 5: req.query is a getter with no setter — override on instance\n Object.defineProperty(req, 'query', { value: sanitizedQuery, writable: true, configurable: true });\n }\n if (req.params && typeof req.params === 'object') {\n const sanitizedParams = sanitizeObject(req.params, options);\n Object.defineProperty(req, 'params', { value: sanitizedParams, writable: true, configurable: true });\n }\n next();\n } catch (err) {\n next(err);\n }\n };\n}\n","/**\n * @module @arcis/node/validation/schema\n * Request validation middleware\n */\n\nimport type { Request, Response, NextFunction, RequestHandler } from 'express';\nimport { VALIDATION, ERRORS } from '../core/constants';\nimport type { ValidationSchema, FieldValidator } from '../core/types';\nimport { sanitizeString } from '../sanitizers';\n\n/**\n * Create Express middleware for request validation.\n * Prevents mass assignment by only allowing fields defined in the schema.\n * \n * @param schema - Validation schema defining expected fields\n * @param source - Request property to validate ('body', 'query', or 'params')\n * @returns Express middleware\n * \n * @example\n * app.post('/users', validate({\n * email: { type: 'email', required: true },\n * name: { type: 'string', min: 2, max: 50 },\n * age: { type: 'number', min: 0, max: 150 },\n * role: { type: 'string', enum: ['user', 'admin'] }\n * }), handler);\n * \n * @example\n * // Validate query params\n * app.get('/search', validate({\n * q: { type: 'string', required: true, min: 1 },\n * page: { type: 'number', min: 1 }\n * }, 'query'), handler);\n */\nexport function validate(\n schema: ValidationSchema,\n source: 'body' | 'query' | 'params' = 'body'\n): RequestHandler {\n return (req: Request, res: Response, next: NextFunction) => {\n const data = req[source] || {};\n const errors: string[] = [];\n const validated: Record<string, unknown> = {};\n\n for (const [field, rules] of Object.entries(schema)) {\n const value = data[field];\n const result = validateField(field, value, rules);\n \n if (result.errors.length > 0) {\n errors.push(...result.errors);\n } else if (result.value !== undefined) {\n validated[field] = result.value;\n }\n }\n\n if (errors.length > 0) {\n res.status(400).json({ errors });\n return;\n }\n\n // Replace with validated data (prevents mass assignment).\n // SECURITY: Express 5 makes req.body/query/params read-only. Use\n // defineProperty so this works on both Express 4 and 5; direct\n // assignment crashes Express 5 with TypeError.\n Object.defineProperty(req, source, {\n value: validated,\n writable: true,\n configurable: true,\n enumerable: true,\n });\n next();\n };\n}\n\n/**\n * Validate a single field against its rules.\n */\nfunction validateField(\n field: string,\n value: unknown,\n rules: FieldValidator\n): { value?: unknown; errors: string[] } {\n const errors: string[] = [];\n\n // Required check\n if (rules.required && (value === undefined || value === null || value === '')) {\n errors.push(ERRORS.VALIDATION.REQUIRED(field));\n return { errors };\n }\n\n // Skip optional empty fields\n if (value === undefined || value === null) {\n return { errors: [] };\n }\n\n let typedValue: unknown = value;\n let isValid = true;\n\n // Type validation and coercion\n switch (rules.type) {\n case 'string':\n if (typeof value !== 'string') {\n errors.push(ERRORS.VALIDATION.INVALID_TYPE(field, 'string'));\n isValid = false;\n break;\n }\n if (rules.min !== undefined && value.length < rules.min) {\n errors.push(ERRORS.VALIDATION.MIN_LENGTH(field, rules.min));\n isValid = false;\n }\n if (rules.max !== undefined && value.length > rules.max) {\n errors.push(ERRORS.VALIDATION.MAX_LENGTH(field, rules.max));\n isValid = false;\n }\n if (rules.pattern && !rules.pattern.test(value)) {\n errors.push(ERRORS.VALIDATION.INVALID_FORMAT(field));\n isValid = false;\n }\n // Enum check runs before sanitization so the raw value is compared.\n // Sanitizing first could silently modify the value and cause a mismatch\n // with enum entries that contain characters the sanitizer would strip.\n if (isValid && rules.enum && !rules.enum.includes(value)) {\n errors.push(ERRORS.VALIDATION.INVALID_ENUM(field, rules.enum));\n isValid = false;\n }\n if (isValid && rules.sanitize !== false) {\n typedValue = sanitizeString(value);\n }\n break;\n\n case 'number':\n typedValue = Number(value);\n if (isNaN(typedValue as number)) {\n errors.push(ERRORS.VALIDATION.INVALID_TYPE(field, 'number'));\n isValid = false;\n break;\n }\n if (rules.min !== undefined && (typedValue as number) < rules.min) {\n errors.push(ERRORS.VALIDATION.MIN_VALUE(field, rules.min));\n isValid = false;\n }\n if (rules.max !== undefined && (typedValue as number) > rules.max) {\n errors.push(ERRORS.VALIDATION.MAX_VALUE(field, rules.max));\n isValid = false;\n }\n break;\n\n case 'boolean':\n if (value === 'true' || value === true || value === 1 || value === '1') {\n typedValue = true;\n } else if (value === 'false' || value === false || value === 0 || value === '0') {\n typedValue = false;\n } else {\n errors.push(ERRORS.VALIDATION.INVALID_TYPE(field, 'boolean'));\n isValid = false;\n }\n break;\n\n case 'email':\n if (!VALIDATION.EMAIL.test(String(value))) {\n errors.push(ERRORS.VALIDATION.INVALID_EMAIL(field));\n isValid = false;\n }\n if (isValid) {\n typedValue = sanitizeString(String(value).toLowerCase().trim());\n }\n break;\n\n case 'url':\n if (!VALIDATION.URL.test(String(value))) {\n errors.push(ERRORS.VALIDATION.INVALID_URL(field));\n isValid = false;\n }\n if (isValid) {\n typedValue = sanitizeString(String(value));\n }\n break;\n\n case 'uuid':\n if (!VALIDATION.UUID.test(String(value))) {\n errors.push(ERRORS.VALIDATION.INVALID_UUID(field));\n isValid = false;\n }\n break;\n\n case 'array':\n if (!Array.isArray(value)) {\n errors.push(ERRORS.VALIDATION.INVALID_TYPE(field, 'array'));\n isValid = false;\n break;\n }\n if (rules.min !== undefined && value.length < rules.min) {\n errors.push(ERRORS.VALIDATION.MIN_ITEMS(field, rules.min));\n isValid = false;\n }\n if (rules.max !== undefined && value.length > rules.max) {\n errors.push(ERRORS.VALIDATION.MAX_ITEMS(field, rules.max));\n isValid = false;\n }\n break;\n\n case 'object':\n if (typeof value !== 'object' || Array.isArray(value) || value === null) {\n errors.push(ERRORS.VALIDATION.INVALID_TYPE(field, 'object'));\n isValid = false;\n }\n break;\n }\n\n // Enum validation for non-string types (strings check enum before sanitizing above).\n if (isValid && rules.enum && rules.type !== 'string' && !rules.enum.includes(typedValue)) {\n errors.push(ERRORS.VALIDATION.INVALID_ENUM(field, rules.enum));\n isValid = false;\n }\n\n // Custom validation\n if (isValid && rules.custom) {\n const customResult = rules.custom(typedValue);\n if (customResult === undefined) {\n throw new TypeError(\n `Custom validator for field \"${field}\" returned undefined. ` +\n 'Return true to pass, false to fail, or a string error message.'\n );\n }\n if (customResult !== true) {\n errors.push(typeof customResult === 'string' && customResult.length > 0 ? customResult : `${field} is invalid`);\n isValid = false;\n }\n }\n\n return {\n value: isValid ? typedValue : undefined,\n errors,\n };\n}\n\n/**\n * Alias for validate\n * @see validate\n */\nexport const createValidator = validate;\n","/**\n * @module @arcis/node/validation/file\n * File upload validation and filename sanitization\n */\n\n// =============================================================================\n// MAGIC BYTES — first bytes of common file types\n// =============================================================================\n\nconst MAGIC_BYTES: Record<string, Buffer[]> = {\n // Images\n 'image/jpeg': [Buffer.from([0xFF, 0xD8, 0xFF])],\n 'image/png': [Buffer.from([0x89, 0x50, 0x4E, 0x47])],\n 'image/gif': [Buffer.from('GIF87a'), Buffer.from('GIF89a')],\n 'image/webp': [Buffer.from('RIFF')], // RIFF....WEBP\n 'image/bmp': [Buffer.from([0x42, 0x4D])],\n 'image/svg+xml': [], // text-based, check separately\n\n // Documents\n 'application/pdf': [Buffer.from('%PDF')],\n 'application/zip': [Buffer.from([0x50, 0x4B, 0x03, 0x04])],\n\n // Audio/Video\n 'audio/mpeg': [Buffer.from([0xFF, 0xFB]), Buffer.from([0xFF, 0xF3]), Buffer.from([0x49, 0x44, 0x33])],\n 'video/mp4': [], // ftyp at offset 4\n};\n\n// =============================================================================\n// DANGEROUS EXTENSIONS — files that can execute code\n// =============================================================================\n\nconst DANGEROUS_EXTENSIONS = new Set([\n // Scripts\n '.exe', '.bat', '.cmd', '.com', '.msi', '.scr', '.pif',\n '.vbs', '.vbe', '.js', '.jse', '.ws', '.wsf', '.wsc', '.wsh',\n '.ps1', '.ps1xml', '.ps2', '.ps2xml', '.psc1', '.psc2',\n '.sh', '.bash', '.csh', '.ksh',\n // Server-side\n '.php', '.php3', '.php4', '.php5', '.phtml', '.pht',\n '.asp', '.aspx', '.ashx', '.asmx', '.cer',\n '.jsp', '.jspx', '.jsw', '.jsv',\n '.cgi', '.pl', '.py', '.rb',\n // Java\n '.jar', '.war', '.ear', '.class',\n // Config that can execute\n '.htaccess', '.htpasswd',\n // Template engines\n '.ejs', '.pug', '.hbs', '.handlebars', '.njk', '.twig',\n // Shortcuts/links\n '.lnk', '.inf', '.reg', '.url',\n // Office macros\n '.docm', '.xlsm', '.pptm', '.dotm',\n]);\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\n/** File upload validation options */\nexport interface ValidateFileOptions {\n /** Maximum file size in bytes. Default: 5MB */\n maxSize?: number;\n /** Allowed MIME types (e.g., ['image/jpeg', 'image/png']) */\n allowedTypes?: string[];\n /** Allowed file extensions (e.g., ['.jpg', '.png']). Includes dot. */\n allowedExtensions?: string[];\n /** Block dangerous/executable extensions. Default: true */\n blockExecutables?: boolean;\n /** Validate magic bytes match the claimed MIME type. Default: true */\n validateMagicBytes?: boolean;\n /** Block files with no extension. Default: true */\n blockNoExtension?: boolean;\n /** Block double extensions (e.g., file.php.jpg). Default: true */\n blockDoubleExtensions?: boolean;\n}\n\n/** File metadata for validation */\nexport interface FileInput {\n /** Original filename */\n filename: string;\n /** MIME type (as claimed by client) */\n mimetype: string;\n /** File size in bytes */\n size: number;\n /** File content buffer (for magic byte validation) */\n buffer?: Buffer;\n}\n\n/** File validation result */\nexport interface ValidateFileResult {\n /** Whether the file passed validation */\n valid: boolean;\n /** Validation errors (empty if valid) */\n errors: string[];\n /** Sanitized filename (safe for storage) */\n sanitizedFilename: string;\n}\n\n// =============================================================================\n// DEFAULTS\n// =============================================================================\n\nconst DEFAULT_MAX_SIZE = 5 * 1024 * 1024; // 5MB\n\n// =============================================================================\n// FILENAME SANITIZATION\n// =============================================================================\n\n/**\n * Sanitize a filename for safe storage.\n *\n * Strips path traversal, null bytes, control characters, and special characters.\n * Preserves the extension and converts to a filesystem-safe name.\n *\n * @param filename - The original filename\n * @returns A sanitized filename safe for storage\n *\n * @example\n * sanitizeFilename('../../etc/passwd') // 'etc_passwd'\n * sanitizeFilename('file<name>.jpg') // 'filename.jpg'\n * sanitizeFilename('photo (1).jpg') // 'photo_1.jpg'\n * sanitizeFilename('.htaccess') // 'htaccess'\n */\nexport function sanitizeFilename(filename: string): string {\n let name = filename;\n\n // Strip null bytes\n name = name.replace(/\\0/g, '');\n\n // Strip path components (both Unix and Windows)\n name = name.replace(/^.*[/\\\\]/, '');\n\n // Strip control characters\n name = name.replace(/[\\x00-\\x1F\\x7F]/g, '');\n\n // Strip characters unsafe for filesystems\n name = name.replace(/[<>:\"/\\\\|?*]/g, '');\n\n // Replace spaces and parens with underscores\n name = name.replace(/[\\s()]+/g, '_');\n\n // Strip leading dots (hidden files / .htaccess)\n name = name.replace(/^\\.+/, '');\n\n // Collapse multiple underscores/dots\n name = name.replace(/_{2,}/g, '_');\n name = name.replace(/\\.{2,}/g, '.');\n\n // Trim underscores before dots (e.g., \"photo_1_.jpg\" → \"photo_1.jpg\")\n name = name.replace(/_+\\./g, '.');\n\n // Trim underscores from edges\n name = name.replace(/^_+|_+$/g, '');\n\n // Fallback for empty name\n if (!name || name === '.') {\n name = 'unnamed';\n }\n\n return name;\n}\n\n// =============================================================================\n// MAGIC BYTE VALIDATION\n// =============================================================================\n\n/**\n * Check if file content matches the claimed MIME type via magic bytes.\n */\nfunction matchesMagicBytes(buffer: Buffer, mimetype: string): boolean {\n const signatures = MAGIC_BYTES[mimetype];\n if (!signatures || signatures.length === 0) return true; // no signature to check\n\n return signatures.some(sig => {\n if (buffer.length < sig.length) return false;\n return buffer.subarray(0, sig.length).equals(sig);\n });\n}\n\n// =============================================================================\n// EXTENSION HELPERS\n// =============================================================================\n\n/**\n * Get the extension from a filename (lowercase, with dot).\n */\nfunction getExtension(filename: string): string {\n const lastDot = filename.lastIndexOf('.');\n if (lastDot < 1) return '';\n return filename.slice(lastDot).toLowerCase();\n}\n\n/**\n * Check if a filename has double extensions (e.g., file.php.jpg).\n */\nfunction hasDoubleExtension(filename: string): boolean {\n const parts = filename.split('.');\n if (parts.length < 3) return false;\n\n // Check if any non-final extension is dangerous\n for (let i = 1; i < parts.length - 1; i++) {\n const ext = '.' + parts[i].toLowerCase();\n if (DANGEROUS_EXTENSIONS.has(ext)) return true;\n }\n return false;\n}\n\n// =============================================================================\n// FILE VALIDATION\n// =============================================================================\n\n/**\n * Validate a file upload for security.\n *\n * Checks file size, MIME type, extension, magic bytes, and dangerous patterns.\n * Returns a result with validation errors and a sanitized filename.\n *\n * @param file - File metadata and optional content\n * @param options - Validation options\n * @returns Validation result\n *\n * @example\n * const result = validateFile(\n * { filename: 'photo.jpg', mimetype: 'image/jpeg', size: 1024, buffer },\n * { allowedTypes: ['image/jpeg', 'image/png'], maxSize: 2 * 1024 * 1024 }\n * );\n * if (!result.valid) {\n * return res.status(400).json({ errors: result.errors });\n * }\n * // Use result.sanitizedFilename for storage\n *\n * @example\n * // Block executables only (no whitelist)\n * const result = validateFile(file, { blockExecutables: true });\n */\nexport function validateFile(\n file: FileInput,\n options: ValidateFileOptions = {}\n): ValidateFileResult {\n const {\n maxSize = DEFAULT_MAX_SIZE,\n allowedTypes,\n allowedExtensions,\n blockExecutables = true,\n validateMagicBytes = true,\n blockNoExtension = true,\n blockDoubleExtensions = true,\n } = options;\n\n const errors: string[] = [];\n const sanitizedFilename = sanitizeFilename(file.filename);\n const extension = getExtension(sanitizedFilename);\n\n // Size check\n if (file.size > maxSize) {\n errors.push(`File size ${file.size} exceeds maximum ${maxSize} bytes`);\n }\n\n if (file.size === 0) {\n errors.push('File is empty');\n }\n\n // Extension checks\n if (blockNoExtension && !extension) {\n errors.push('File has no extension');\n }\n\n if (blockExecutables && extension && DANGEROUS_EXTENSIONS.has(extension)) {\n errors.push(`Executable extension \"${extension}\" is not allowed`);\n }\n\n if (blockDoubleExtensions && hasDoubleExtension(sanitizedFilename)) {\n errors.push('Double extensions with executable types are not allowed');\n }\n\n if (allowedExtensions && extension) {\n const normalizedAllowed = allowedExtensions.map(e => e.toLowerCase());\n if (!normalizedAllowed.includes(extension)) {\n errors.push(`Extension \"${extension}\" is not allowed. Allowed: ${normalizedAllowed.join(', ')}`);\n }\n }\n\n // MIME type check\n if (allowedTypes && !allowedTypes.includes(file.mimetype)) {\n errors.push(`MIME type \"${file.mimetype}\" is not allowed. Allowed: ${allowedTypes.join(', ')}`);\n }\n\n // Magic bytes validation\n if (validateMagicBytes && file.buffer && file.buffer.length > 0) {\n if (!matchesMagicBytes(file.buffer, file.mimetype)) {\n errors.push(`File content does not match claimed MIME type \"${file.mimetype}\"`);\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n sanitizedFilename,\n };\n}\n\n/**\n * Check if a file extension is considered dangerous/executable.\n *\n * @param filename - Filename or extension to check\n * @returns true if the extension is dangerous\n */\nexport function isDangerousExtension(filename: string): boolean {\n const ext = getExtension(filename);\n return ext !== '' && DANGEROUS_EXTENSIONS.has(ext);\n}\n","/**\n * @module @arcis/node/validation/url\n * SSRF (Server-Side Request Forgery) prevention\n *\n * Validates URLs to ensure they don't target private/internal networks,\n * localhost, cloud metadata endpoints, or use dangerous protocols.\n *\n * @example\n * import { validateUrl } from '@arcis/node';\n *\n * // Block SSRF attempts\n * validateUrl('http://169.254.169.254/latest/meta-data/') // { safe: false, reason: 'link-local address' }\n * validateUrl('http://10.0.0.1/admin') // { safe: false, reason: 'private address (10.0.0.0/8)' }\n * validateUrl('http://localhost/secret') // { safe: false, reason: 'loopback address' }\n * validateUrl('file:///etc/passwd') // { safe: false, reason: 'disallowed protocol: file:' }\n *\n * // Allow safe URLs\n * validateUrl('https://api.example.com/data') // { safe: true }\n */\n\n/** Options for URL validation */\nexport interface ValidateUrlOptions {\n /** Allowed protocols. Default: ['http:', 'https:'] */\n allowedProtocols?: string[];\n /** Additional hostnames to block (e.g., internal service names) */\n blockedHosts?: string[];\n /** Additional hostnames to always allow (bypass IP checks) */\n allowedHosts?: string[];\n /** Allow localhost/loopback. Default: false */\n allowLocalhost?: boolean;\n /** Allow private/internal IPs. Default: false */\n allowPrivate?: boolean;\n}\n\n/** Result of URL validation */\nexport interface ValidateUrlResult {\n /** Whether the URL is safe to fetch */\n safe: boolean;\n /** Reason the URL was blocked (only set when safe=false) */\n reason?: string;\n}\n\n/**\n * Validate a URL for SSRF safety.\n *\n * Checks:\n * 1. Valid URL format\n * 2. Allowed protocol (default: http, https only)\n * 3. Not localhost/loopback (127.x.x.x, ::1, localhost)\n * 4. Not private IP (10.x, 172.16-31.x, 192.168.x)\n * 5. Not link-local (169.254.x.x — includes AWS/GCP/Azure metadata)\n * 6. Not blocked hostname\n * 7. No credentials in URL (user:pass@host)\n *\n * @param url - The URL string to validate\n * @param options - Validation options\n * @returns Validation result with safe flag and optional reason\n */\nexport function validateUrl(url: string, options: ValidateUrlOptions = {}): ValidateUrlResult {\n const {\n allowedProtocols = ['http:', 'https:'],\n blockedHosts = [],\n allowedHosts = [],\n allowLocalhost = false,\n allowPrivate = false,\n } = options;\n\n if (typeof url !== 'string' || url.trim() === '') {\n return { safe: false, reason: 'invalid URL: empty or not a string' };\n }\n\n // Parse URL\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return { safe: false, reason: 'invalid URL: failed to parse' };\n }\n\n // Check protocol\n if (!allowedProtocols.includes(parsed.protocol)) {\n return { safe: false, reason: `disallowed protocol: ${parsed.protocol}` };\n }\n\n // Check for credentials in URL (user:pass@host)\n if (parsed.username || parsed.password) {\n return { safe: false, reason: 'URL contains credentials' };\n }\n\n const hostname = parsed.hostname.toLowerCase();\n\n // Check explicit allowlist first (bypass IP checks)\n if (allowedHosts.some(h => hostname === h.toLowerCase())) {\n return { safe: true };\n }\n\n // Check explicit blocklist\n if (blockedHosts.some(h => hostname === h.toLowerCase())) {\n return { safe: false, reason: `blocked host: ${hostname}` };\n }\n\n // Check localhost/loopback\n if (!allowLocalhost) {\n if (\n hostname === 'localhost' ||\n hostname === '127.0.0.1' ||\n hostname === '[::1]' ||\n hostname === '::1' ||\n hostname === '0.0.0.0' ||\n hostname.endsWith('.localhost')\n ) {\n return { safe: false, reason: 'loopback address' };\n }\n\n // Check 127.x.x.x range\n if (/^127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$/.test(hostname)) {\n return { safe: false, reason: 'loopback address' };\n }\n }\n\n // Check decimal IP (e.g., 2130706433 = 127.0.0.1)\n if (!allowLocalhost || !allowPrivate) {\n const decimalCheck = checkDecimalIp(hostname, allowLocalhost, allowPrivate);\n if (decimalCheck) {\n return { safe: false, reason: decimalCheck };\n }\n }\n\n // Check octal IP (e.g., 0177.0.0.1 = 127.0.0.1)\n if (!allowLocalhost || !allowPrivate) {\n const octalCheck = checkOctalIp(hostname, allowLocalhost, allowPrivate);\n if (octalCheck) {\n return { safe: false, reason: octalCheck };\n }\n }\n\n // Check private/internal IPs\n if (!allowPrivate) {\n const privateCheck = checkPrivateIp(hostname);\n if (privateCheck) {\n return { safe: false, reason: privateCheck };\n }\n }\n\n return { safe: true };\n}\n\n/**\n * Convenience wrapper that returns true/false.\n *\n * @param url - The URL to check\n * @param options - Validation options\n * @returns true if the URL is safe to fetch\n */\nexport function isUrlSafe(url: string, options: ValidateUrlOptions = {}): boolean {\n return validateUrl(url, options).safe;\n}\n\n/**\n * Check if a hostname is a private/internal IP address.\n * Returns the reason string if private, or null if not.\n */\nfunction checkPrivateIp(hostname: string): string | null {\n // 10.0.0.0/8\n if (/^10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$/.test(hostname)) {\n return 'private address (10.0.0.0/8)';\n }\n\n // 172.16.0.0/12 (172.16.x.x - 172.31.x.x)\n const match172 = hostname.match(/^172\\.(\\d{1,3})\\.\\d{1,3}\\.\\d{1,3}$/);\n if (match172) {\n const second = parseInt(match172[1], 10);\n if (second >= 16 && second <= 31) {\n return 'private address (172.16.0.0/12)';\n }\n }\n\n // 192.168.0.0/16\n if (/^192\\.168\\.\\d{1,3}\\.\\d{1,3}$/.test(hostname)) {\n return 'private address (192.168.0.0/16)';\n }\n\n // 169.254.0.0/16 — link-local, includes cloud metadata endpoints\n // AWS: 169.254.169.254, GCP: metadata.google.internal, Azure: 169.254.169.254\n if (/^169\\.254\\.\\d{1,3}\\.\\d{1,3}$/.test(hostname)) {\n return 'link-local address (169.254.0.0/16)';\n }\n\n // 0.0.0.0/8 (current network)\n if (/^0\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$/.test(hostname)) {\n return 'current network address (0.0.0.0/8)';\n }\n\n // Cloud metadata hostnames\n if (\n hostname === 'metadata.google.internal' ||\n hostname === 'metadata.internal' ||\n hostname === 'metadata.azure.internal'\n ) {\n return 'cloud metadata endpoint';\n }\n\n // IPv6 private ranges (bracket-wrapped in URLs)\n let ipv6 = hostname.replace(/^\\[|\\]$/g, '');\n // Strip zone ID (e.g., ::1%eth0 → ::1)\n const zoneIdx = ipv6.indexOf('%');\n if (zoneIdx !== -1) {\n ipv6 = ipv6.slice(0, zoneIdx);\n }\n if (\n ipv6 === '::1' ||\n ipv6 === '::' ||\n /^fc[0-9a-f]{2}:/i.test(ipv6) ||\n /^fd[0-9a-f]{2}:/i.test(ipv6) ||\n /^fe80:/i.test(ipv6) ||\n /^ff[0-9a-f]{2}:/i.test(ipv6) // IPv6 multicast (ff00::/8)\n ) {\n return 'private IPv6 address';\n }\n\n // IPv6-mapped IPv4 — dotted form (::ffff:127.0.0.1)\n const mappedDotted = ipv6.match(/^::ffff:(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})$/i);\n if (mappedDotted) {\n const mappedIp = mappedDotted[1];\n if (/^127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$/.test(mappedIp)) {\n return 'IPv6-mapped loopback address';\n }\n const mappedCheck = checkPrivateIp(mappedIp);\n if (mappedCheck) {\n return `IPv6-mapped ${mappedCheck}`;\n }\n }\n\n // IPv6-mapped IPv4 — hex form (::ffff:7f00:1 = 127.0.0.1)\n // Node's URL parser normalizes ::ffff:a.b.c.d to ::ffff:XXYY:ZZWW\n const mappedHex = ipv6.match(/^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i);\n if (mappedHex) {\n const hi = parseInt(mappedHex[1], 16);\n const lo = parseInt(mappedHex[2], 16);\n const a = (hi >> 8) & 0xFF;\n const b = hi & 0xFF;\n const c = (lo >> 8) & 0xFF;\n const d = lo & 0xFF;\n const dotted = `${a}.${b}.${c}.${d}`;\n if (a === 127) {\n return 'IPv6-mapped loopback address';\n }\n const hexCheck = checkPrivateIp(dotted);\n if (hexCheck) {\n return `IPv6-mapped ${hexCheck}`;\n }\n }\n\n return null;\n}\n\n/**\n * Parse a decimal integer as an IPv4 address and check if it's private/loopback.\n * e.g., 2130706433 = 127.0.0.1, 167772160 = 10.0.0.0\n */\nfunction checkDecimalIp(hostname: string, allowLocalhost: boolean, allowPrivate: boolean): string | null {\n // Must be a pure decimal integer\n if (!/^\\d+$/.test(hostname)) return null;\n\n const num = parseInt(hostname, 10);\n if (isNaN(num) || num < 0 || num > 0xFFFFFFFF) return null;\n\n const a = (num >>> 24) & 0xFF;\n const b = (num >>> 16) & 0xFF;\n const c = (num >>> 8) & 0xFF;\n const d = num & 0xFF;\n const dotted = `${a}.${b}.${c}.${d}`;\n\n // Check loopback\n if (!allowLocalhost && a === 127) {\n return `loopback address (decimal IP: ${dotted})`;\n }\n\n // Check private ranges\n if (!allowPrivate) {\n const privateCheck = checkPrivateIp(dotted);\n if (privateCheck) {\n return `${privateCheck} (decimal IP: ${dotted})`;\n }\n }\n\n return null;\n}\n\n/**\n * Parse octal-notation IPv4 address and check if it's private/loopback.\n * e.g., 0177.0.0.1 = 127.0.0.1, 0x7f.0.0.1 = 127.0.0.1\n */\nfunction checkOctalIp(hostname: string, allowLocalhost: boolean, allowPrivate: boolean): string | null {\n // Must look like a dotted quad where at least one octet has a leading zero or 0x prefix\n const parts = hostname.split('.');\n if (parts.length !== 4) return null;\n\n // Check if any part uses octal (leading 0) or hex (0x) notation\n const hasAlternateNotation = parts.some(p => /^0[0-7]+$/.test(p) || /^0x[0-9a-fA-F]+$/i.test(p));\n if (!hasAlternateNotation) return null;\n\n const octets: number[] = [];\n for (const part of parts) {\n let val: number;\n if (/^0x[0-9a-fA-F]+$/i.test(part)) {\n val = parseInt(part, 16);\n } else if (/^0[0-7]*$/.test(part)) {\n val = parseInt(part, 8);\n } else if (/^\\d+$/.test(part)) {\n val = parseInt(part, 10);\n } else {\n return null;\n }\n if (val < 0 || val > 255) return null;\n octets.push(val);\n }\n\n const dotted = octets.join('.');\n\n // Check loopback\n if (!allowLocalhost && octets[0] === 127) {\n return `loopback address (octal IP: ${dotted})`;\n }\n\n // Check private ranges\n if (!allowPrivate) {\n const privateCheck = checkPrivateIp(dotted);\n if (privateCheck) {\n return `${privateCheck} (octal IP: ${dotted})`;\n }\n }\n\n return null;\n}\n","/**\n * @module @arcis/node/validation/url-async\n *\n * Async SSRF guard that closes the DNS-rebinding TOCTOU gap left open\n * by the synchronous `validateUrl` (sdk-vectors.md #31, issue #50).\n *\n * The synchronous `validateUrl` only checks the *string form* of the\n * hostname. It catches obvious cases — `127.0.0.1`, `10.0.0.1`,\n * `169.254.169.254` — but a hostname like `evil.com` passes through\n * even when its DNS A-record points to `10.0.0.1`, because resolution\n * happens later inside `fetch`. An attacker controlling the\n * `evil.com` zone can also rebind the answer between Arcis's check\n * and the actual TCP connect, which is the classic DNS TOCTOU.\n *\n * Two layers of fix shipped here:\n *\n * 1. **`validateUrlAsync(url, options)`** — runs the existing sync\n * `validateUrl` first, then `dns.lookup(hostname, { all: true })`,\n * then re-runs the same private-range check on every resolved\n * address. Returns the pinned IP list so callers can reuse it for\n * the actual connection (closing the TOCTOU window).\n *\n * 2. **`pinnedDnsLookup(ip)`** — returns a Node `lookup` callback\n * that resolves any hostname to the pre-validated IP. Wire this\n * into `https.request({ lookup })` / `http.request({ lookup })`\n * so the connection uses the IP Arcis already validated, not\n * whatever DNS returns at connect time. Pure stdlib — no undici,\n * no extra dep.\n *\n * 3. **`safeFollowRedirect(prev, location, options)`** — when the\n * server replies 30x, run the same async guard against the new\n * Location URL. Resolves the absolute URL using the previous\n * response URL as base. Caller decides whether to follow.\n *\n * The function signatures keep `lookup` injectable so tests can\n * substitute a fake resolver without monkey-patching `node:dns`.\n *\n * ```ts\n * import https from 'node:https';\n * import { validateUrlAsync, pinnedDnsLookup } from '@arcis/node';\n *\n * const result = await validateUrlAsync(url);\n * if (!result.safe) throw new Error(result.reason);\n *\n * https.get(url, { lookup: pinnedDnsLookup(result.resolvedIp!) }, (res) => {\n * // The TCP connect now goes to result.resolvedIp regardless of\n * // what DNS would say at this exact moment.\n * });\n * ```\n */\n\nimport * as dns from 'node:dns';\nimport { validateUrl, type ValidateUrlOptions, type ValidateUrlResult } from './url';\n\n/**\n * Subset of `dns.lookup`'s `{ all: true }` callback signature. Kept\n * narrow so a test fake can satisfy it without depending on Node's\n * full `LookupAddress` type.\n */\nexport type LookupAddress = { address: string; family: number };\n\n/**\n * Function shape compatible with `dns.lookup(hostname, { all: true })`.\n * Returns a list of resolved addresses. Tests inject a fake.\n */\nexport type DnsLookup = (hostname: string) => Promise<LookupAddress[]>;\n\nconst defaultLookup: DnsLookup = (hostname) =>\n new Promise((resolve, reject) => {\n dns.lookup(hostname, { all: true }, (err, addresses) => {\n if (err) reject(err);\n else resolve(addresses);\n });\n });\n\nexport interface ValidateUrlAsyncOptions extends ValidateUrlOptions {\n /**\n * DNS lookup function. Defaults to a Promise wrapper around\n * `dns.lookup(hostname, { all: true })`. Tests inject a stub.\n */\n lookup?: DnsLookup;\n /**\n * If true, accept the first non-private IP and ignore the rest.\n * Default false: every resolved IP must pass the private-range\n * check. Hosts with mixed-public/private answers (round-robin DNS\n * with one internal record) still fail-closed.\n */\n acceptFirstPublic?: boolean;\n}\n\nexport interface ValidateUrlAsyncResult extends ValidateUrlResult {\n /**\n * Single pinned IP (the first public address if all checks passed,\n * or undefined when the string-only synchronous validator already\n * decided — e.g., the hostname *was* a literal IP). Use this with\n * `pinnedDnsLookup()` to wire the actual fetch.\n */\n resolvedIp?: string;\n /** Every IP returned by DNS, in resolver order. */\n resolvedIps?: string[];\n}\n\n/**\n * Reuses the existing private-range check from the sync validator.\n * We re-run `validateUrl` with the resolved IP swapped in as the\n * URL host. That way every existing rule (link-local, decimal-IP,\n * cloud-metadata hostname, IPv6-mapped, etc.) applies post-DNS too,\n * without forking the rule list.\n */\nfunction checkResolvedIp(ip: string, options: ValidateUrlOptions): ValidateUrlResult {\n // Build a synthetic URL that hands the IP to validateUrl. The\n // protocol is irrelevant for the private-range check; pick http to\n // avoid IPv6 bracket questions.\n const isIpv6 = ip.includes(':');\n const host = isIpv6 ? `[${ip}]` : ip;\n // Strip credentials + allowedHosts/blockedHosts because they apply\n // to the original hostname, not the resolved IP. We're only\n // re-running the IP-range checks here.\n const { allowedProtocols, allowLocalhost, allowPrivate } = options;\n return validateUrl(`http://${host}/`, {\n allowedProtocols,\n allowLocalhost,\n allowPrivate,\n });\n}\n\n/**\n * Async SSRF guard with DNS resolution. Runs the sync validator\n * first, then resolves DNS and validates every returned IP against\n * the same private-range rules. Returns a pinned IP for the caller\n * to reuse.\n *\n * Failure modes (any returns `{ safe: false, reason }`):\n * - Sync validator already rejects (string-pattern fail).\n * - DNS lookup throws (NXDOMAIN, network error). Reason carries the\n * underlying error message.\n * - DNS returns no addresses.\n * - Any resolved address fails the private-range check (default) or\n * *all* fail it when `acceptFirstPublic` is true.\n */\nexport async function validateUrlAsync(\n url: string,\n options: ValidateUrlAsyncOptions = {},\n): Promise<ValidateUrlAsyncResult> {\n const sync = validateUrl(url, options);\n if (!sync.safe) return sync;\n\n // If the hostname is a literal IP, the sync validator has already\n // checked it. Skip the DNS round-trip.\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return { safe: false, reason: 'invalid URL: failed to parse' };\n }\n const host = parsed.hostname.replace(/^\\[|\\]$/g, '');\n if (isLiteralIp(host)) return { safe: true };\n\n // Allowed-hosts allowlist takes precedence in the sync validator\n // and also bypasses the IP check here. The contract: if you\n // explicitly allowed the hostname, you accept whatever DNS returns.\n if (options.allowedHosts?.some((h) => host.toLowerCase() === h.toLowerCase())) {\n return { safe: true };\n }\n\n const lookup = options.lookup ?? defaultLookup;\n let addresses: LookupAddress[];\n try {\n addresses = await lookup(host);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n return { safe: false, reason: `DNS lookup failed: ${msg}` };\n }\n\n if (addresses.length === 0) {\n return { safe: false, reason: 'DNS returned no addresses' };\n }\n\n const ips = addresses.map((a) => a.address);\n const acceptFirstPublic = options.acceptFirstPublic === true;\n let firstPublic: string | undefined;\n for (const ip of ips) {\n const ipResult = checkResolvedIp(ip, options);\n if (ipResult.safe) {\n if (firstPublic === undefined) firstPublic = ip;\n } else if (!acceptFirstPublic) {\n return {\n safe: false,\n reason: `resolved IP ${ip} is unsafe: ${ipResult.reason}`,\n resolvedIps: ips,\n };\n }\n }\n\n if (firstPublic === undefined) {\n return {\n safe: false,\n reason: 'all resolved IPs are private/loopback',\n resolvedIps: ips,\n };\n }\n\n return { safe: true, resolvedIp: firstPublic, resolvedIps: ips };\n}\n\n/**\n * Build a `lookup` callback that pins the resolution to a single\n * pre-validated IP, regardless of what DNS would say at connect time.\n * Drop into `https.request({ lookup })` / `http.request({ lookup })`.\n *\n * Closes the TOCTOU window between `validateUrlAsync` and the actual\n * TCP connect. The pinned IP must be one that already passed the\n * async validator — wiring it without that check defeats the purpose.\n *\n * The returned function matches Node's `dns.lookup` callback shape\n * for the `{ all: false, family: 0 }` case (single address). The\n * default Node http/https stack uses that shape.\n */\nexport function pinnedDnsLookup(\n ip: string,\n): (\n hostname: string,\n options: { family?: number; hints?: number; verbatim?: boolean },\n callback: (err: NodeJS.ErrnoException | null, address: string, family: number) => void,\n) => void {\n if (typeof ip !== 'string' || ip.trim() === '') {\n throw new TypeError('pinnedDnsLookup: ip must be a non-empty string');\n }\n const family = ip.includes(':') ? 6 : 4;\n return (_hostname, _options, callback) => {\n callback(null, ip, family);\n };\n}\n\n/**\n * Validate a redirect target with the same TOCTOU-aware pipeline.\n *\n * `prev` is the URL of the response that returned the 30x; `location`\n * is the raw `Location:` header value (which may be relative). The\n * function resolves `location` against `prev` per RFC 3986 then runs\n * `validateUrlAsync` on the absolute result.\n *\n * Use this on every hop of a redirect chain. Without it, a server\n * that you trust today can redirect tomorrow's request to\n * `http://169.254.169.254/`.\n */\nexport async function safeFollowRedirect(\n prev: string,\n location: string,\n options: ValidateUrlAsyncOptions = {},\n): Promise<ValidateUrlAsyncResult> {\n if (typeof location !== 'string' || location.trim() === '') {\n return { safe: false, reason: 'redirect Location is empty' };\n }\n let absolute: URL;\n try {\n absolute = new URL(location, prev);\n } catch {\n return { safe: false, reason: 'invalid redirect URL' };\n }\n return validateUrlAsync(absolute.toString(), options);\n}\n\n/**\n * Detect whether a hostname is a literal IPv4/IPv6 (with or without\n * IPv6 brackets stripped). Mirrors what the sync validator already\n * checks via dotted-quad / decimal / octal regexes; we only need it\n * to know whether to skip the DNS round-trip.\n */\nfunction isLiteralIp(host: string): boolean {\n // IPv4 dotted quad\n if (/^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$/.test(host)) return true;\n // Decimal IP (single integer)\n if (/^\\d+$/.test(host)) return true;\n // Octal/hex IP forms\n if (/^0[0-7x]/.test(host) && /^[0-9a-fx.]+$/i.test(host) && host.includes('.')) return true;\n // IPv6: contains a colon and no slashes/spaces\n if (host.includes(':') && !/[/\\s]/.test(host)) return true;\n return false;\n}\n","/**\n * @module @arcis/node/validation/redirect\n * Open Redirect prevention\n *\n * Prevents attackers from using your app to redirect users to malicious sites\n * via manipulated query parameters like ?returnUrl=http://evil.com\n *\n * @example\n * import { validateRedirect, isRedirectSafe } from '@arcis/node';\n *\n * // Block open redirects\n * validateRedirect('http://evil.com') // { safe: false, reason: 'absolute URL not in allowed hosts' }\n * validateRedirect('//evil.com') // { safe: false, reason: 'protocol-relative URL not in allowed hosts' }\n * validateRedirect('javascript:alert(1)') // { safe: false, reason: 'dangerous protocol: javascript:' }\n *\n * // Allow safe redirects\n * validateRedirect('/dashboard') // { safe: true }\n * validateRedirect('/users?page=2') // { safe: true }\n * validateRedirect('https://myapp.com/home', { allowedHosts: ['myapp.com'] }) // { safe: true }\n */\n\n/** Options for redirect validation */\nexport interface ValidateRedirectOptions {\n /** Hostnames that are allowed for absolute URL redirects */\n allowedHosts?: string[];\n /** Allow protocol-relative URLs (//example.com). Default: false */\n allowProtocolRelative?: boolean;\n /** Allowed protocols for absolute URLs. Default: ['http:', 'https:'] */\n allowedProtocols?: string[];\n}\n\n/** Result of redirect validation */\nexport interface ValidateRedirectResult {\n /** Whether the redirect URL is safe */\n safe: boolean;\n /** Reason the redirect was blocked (only set when safe=false) */\n reason?: string;\n}\n\n/** Protocols that can execute code or exfiltrate data */\nconst DANGEROUS_PROTOCOLS = /^(javascript|data|vbscript|blob):/i;\n\n/** Characters used to disguise URLs (tabs, newlines inside scheme) */\nconst CONTROL_CHARS = /[\\t\\n\\r]/g;\n\n/**\n * Validate a redirect URL to prevent open redirect attacks.\n *\n * Safe redirects:\n * - Relative paths: /dashboard, /users?page=2, ../settings\n * - Absolute URLs to allowed hosts (when configured)\n *\n * Blocked redirects:\n * - Absolute URLs to unknown hosts\n * - Protocol-relative URLs (//evil.com)\n * - javascript:, data:, vbscript:, blob: protocols\n * - Backslash-prefixed paths (\\\\evil.com — browser treats as //)\n * - URLs with control characters that could disguise the target\n *\n * @param url - The redirect target URL to validate\n * @param options - Validation options\n * @returns Validation result with safe flag and optional reason\n */\nexport function validateRedirect(\n url: string,\n options: ValidateRedirectOptions = {},\n): ValidateRedirectResult {\n const {\n allowedHosts = [],\n allowProtocolRelative = false,\n allowedProtocols = ['http:', 'https:'],\n } = options;\n\n if (typeof url !== 'string' || url.trim() === '') {\n return { safe: false, reason: 'invalid redirect: empty or not a string' };\n }\n\n // Strip control characters that could disguise the URL\n const cleaned = url.replace(CONTROL_CHARS, '');\n\n // Block dangerous protocols (javascript:, data:, etc.)\n const proto = cleaned.match(DANGEROUS_PROTOCOLS);\n if (proto) {\n return { safe: false, reason: `dangerous protocol: ${proto[0]}` };\n }\n\n // Block backslash-prefixed paths — browsers treat \\ as / in URLs\n // so \\evil.com or \\/evil.com could redirect to //evil.com\n if (cleaned.startsWith('\\\\')) {\n return { safe: false, reason: 'backslash-prefixed URL (browser treats as protocol-relative)' };\n }\n\n // Check protocol-relative URLs (//evil.com)\n if (cleaned.startsWith('//')) {\n if (!allowProtocolRelative) {\n // Still check allowedHosts\n const host = extractHost(cleaned);\n if (host && allowedHosts.some(h => host === h.toLowerCase())) {\n return { safe: true };\n }\n return { safe: false, reason: 'protocol-relative URL not in allowed hosts' };\n }\n const host = extractHost(cleaned);\n if (host && allowedHosts.length > 0 && !allowedHosts.some(h => host === h.toLowerCase())) {\n return { safe: false, reason: 'protocol-relative URL not in allowed hosts' };\n }\n return { safe: true };\n }\n\n // Check if it's an absolute URL (has scheme)\n let parsed: URL;\n try {\n parsed = new URL(cleaned);\n } catch {\n // Not a valid absolute URL — treat as relative path (safe)\n return { safe: true };\n }\n\n // If we got here, it parsed as an absolute URL\n // Check protocol\n if (!allowedProtocols.includes(parsed.protocol)) {\n return { safe: false, reason: `disallowed protocol: ${parsed.protocol}` };\n }\n\n // Check if host is in allowed list. Match against host:port form when\n // a non-default port is present, falling back to hostname only when\n // the URL uses a default port. parsed.port is \"\" for default ports\n // (80 for http, 443 for https), so hostWithPort equals hostname in\n // those cases. Non-default ports must be explicitly listed: a bare\n // 'myapp.com' entry does NOT permit redirects to 'myapp.com:9999'.\n const hostname = parsed.hostname.toLowerCase();\n const hostWithPort = parsed.port ? `${hostname}:${parsed.port}` : hostname;\n if (allowedHosts.length === 0) {\n return { safe: false, reason: 'absolute URL not in allowed hosts' };\n }\n\n if (!allowedHosts.some(h => h.toLowerCase() === hostWithPort)) {\n return { safe: false, reason: `host not allowed: ${hostWithPort}` };\n }\n\n return { safe: true };\n}\n\n/**\n * Convenience wrapper that returns true/false.\n *\n * @param url - The redirect URL to check\n * @param options - Validation options\n * @returns true if the redirect is safe\n */\nexport function isRedirectSafe(url: string, options: ValidateRedirectOptions = {}): boolean {\n return validateRedirect(url, options).safe;\n}\n\n/**\n * Extract host (with optional port) from a protocol-relative URL.\n * Returns hostname for default ports, \"hostname:port\" for non-default.\n * Optional userinfo (user:pass@) is stripped.\n */\nfunction extractHost(url: string): string | null {\n // //user:pass@hostname/path -> authority = \"user:pass@hostname\"\n // //hostname:port/path -> authority = \"hostname:port\"\n const match = url.match(/^\\/\\/([^/?#]+)/);\n if (!match) return null;\n // Strip userinfo if present\n const authority = match[1].includes('@')\n ? match[1].slice(match[1].indexOf('@') + 1)\n : match[1];\n return authority.toLowerCase();\n}\n","/**\n * @module @arcis/node/validation/email\n * Advanced email validation with disposable detection and typo suggestions.\n *\n * Three levels of validation:\n * 1. Syntax — RFC-compliant format checking\n * 2. Domain intelligence — disposable/free provider detection, typo correction\n * 3. MX verification — DNS MX record lookup (async, optional)\n *\n * @example\n * const result = validateEmail('user@tempmail.com');\n * // { valid: false, reason: 'disposable' }\n *\n * const result = validateEmail('user@gmial.com');\n * // { valid: true, reason: 'typo', suggestion: 'user@gmail.com' }\n */\n\nimport { promises as dns } from 'dns';\n\nexport interface EmailValidationOptions {\n /** Check for disposable email providers. Default: true */\n checkDisposable?: boolean;\n /** Suggest corrections for typos. Default: true */\n suggestTypoFix?: boolean;\n /** Verify MX records via DNS. Default: false */\n checkMx?: boolean;\n /** Additional blocked domains */\n blockedDomains?: string[];\n /** Additional allowed domains (bypasses disposable check) */\n allowedDomains?: string[];\n}\n\nexport interface EmailValidationResult {\n /** Whether the email is valid */\n valid: boolean;\n /** Reason for the result */\n reason: 'valid' | 'invalid_syntax' | 'disposable' | 'no_mx' | 'blocked' | 'typo';\n /** Suggested correction if a typo was detected */\n suggestion: string | null;\n /** Whether the domain is a free email provider */\n isFree: boolean;\n /** Whether the domain is a disposable email provider */\n isDisposable: boolean;\n /** The normalized email address */\n normalized: string;\n}\n\n// RFC 5321: local part max 64, domain max 255, total max 254\nconst MAX_EMAIL_LENGTH = 254;\nconst MAX_LOCAL_LENGTH = 64;\nconst MAX_DOMAIN_LENGTH = 255;\n\n/**\n * Strict email syntax regex.\n * - No consecutive dots in local part\n * - No leading/trailing dots in local part\n * - Domain must have at least one dot\n * - No spaces anywhere\n */\nconst EMAIL_SYNTAX = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\\.[a-zA-Z]{2,}$/;\n\n/** Common free email providers */\nconst FREE_PROVIDERS = new Set([\n 'gmail.com', 'yahoo.com', 'hotmail.com', 'outlook.com', 'aol.com',\n 'protonmail.com', 'proton.me', 'icloud.com', 'mail.com', 'zoho.com',\n 'yandex.com', 'gmx.com', 'gmx.net', 'live.com', 'msn.com',\n 'me.com', 'mac.com', 'fastmail.com', 'tutanota.com', 'hey.com',\n]);\n\n/** Common disposable email domains */\nconst DISPOSABLE_DOMAINS = new Set([\n // Popular disposable services\n 'guerrillamail.com', 'guerrillamail.net', 'guerrillamail.org',\n 'tempmail.com', 'temp-mail.org', 'temp-mail.io',\n 'throwaway.email', 'throwaway.com',\n 'mailinator.com', 'mailinator.net',\n 'yopmail.com', 'yopmail.fr', 'yopmail.net',\n 'sharklasers.com', 'grr.la', 'guerrillamail.info',\n 'guerrillamail.biz', 'guerrillamail.de',\n 'trashmail.com', 'trashmail.me', 'trashmail.net',\n 'dispostable.com', 'maildrop.cc',\n 'mailnesia.com', 'tempail.com',\n 'mohmal.com', 'getnada.com',\n 'emailondeck.com', 'discard.email',\n 'fakeinbox.com', 'mailcatch.com',\n 'mintemail.com', 'tempr.email',\n 'tempinbox.com', 'burnermail.io',\n 'mailsac.com', 'harakirimail.com',\n 'tempmailo.com', 'emailfake.com',\n 'crazymailing.com', 'armyspy.com',\n 'dayrep.com', 'einrot.com',\n 'fleckens.hu', 'gustr.com',\n 'jourrapide.com', 'rhyta.com',\n 'superrito.com', 'teleworm.us',\n '10minutemail.com', '10minutemail.net',\n 'minutemail.com', 'tempsky.com',\n 'spamgourmet.com', 'mytrashmail.com',\n 'mailexpire.com', 'safetymail.info',\n 'filzmail.com', 'trashymail.com',\n 'sharkmail.com', 'jetable.org',\n 'nospam.ze.tc', 'trash-me.com',\n 'dodgit.com', 'mailmoat.com',\n 'spamfree24.org', 'incognitomail.org',\n 'tempomail.fr', 'ephemail.net',\n 'hidemail.de', 'spaml.de',\n 'uggsrock.com', 'binkmail.com',\n 'suremail.info', 'bugmenot.com',\n]);\n\n/** Common typos and their corrections */\nconst DOMAIN_TYPOS: Record<string, string> = {\n 'gmial.com': 'gmail.com',\n 'gmaill.com': 'gmail.com',\n 'gmai.com': 'gmail.com',\n 'gamil.com': 'gmail.com',\n 'gnail.com': 'gmail.com',\n 'gmal.com': 'gmail.com',\n 'gmil.com': 'gmail.com',\n 'gmail.co': 'gmail.com',\n 'gmail.cm': 'gmail.com',\n 'gmail.om': 'gmail.com',\n 'gmail.con': 'gmail.com',\n 'gmail.cim': 'gmail.com',\n 'gmail.comm': 'gmail.com',\n 'yahooo.com': 'yahoo.com',\n 'yaho.com': 'yahoo.com',\n 'yahoo.co': 'yahoo.com',\n 'yahoo.cm': 'yahoo.com',\n 'yahoo.con': 'yahoo.com',\n 'yahho.com': 'yahoo.com',\n 'hotmial.com': 'hotmail.com',\n 'hotmal.com': 'hotmail.com',\n 'hotmai.com': 'hotmail.com',\n 'hotmil.com': 'hotmail.com',\n 'hotmail.co': 'hotmail.com',\n 'hotmail.cm': 'hotmail.com',\n 'hotmail.con': 'hotmail.com',\n 'outlok.com': 'outlook.com',\n 'outloo.com': 'outlook.com',\n 'outlook.co': 'outlook.com',\n 'outlook.cm': 'outlook.com',\n 'protonmal.com': 'protonmail.com',\n 'protonmail.co': 'protonmail.com',\n 'icloud.co': 'icloud.com',\n 'icloud.cm': 'icloud.com',\n 'icoud.com': 'icloud.com',\n};\n\nfunction invalidResult(reason: EmailValidationResult['reason'], email: string): EmailValidationResult {\n return {\n valid: false,\n reason,\n suggestion: null,\n isFree: false,\n isDisposable: false,\n normalized: email,\n };\n}\n\n/**\n * Validate an email address with syntax checking, disposable detection,\n * and typo suggestions.\n *\n * @param email - Email address to validate\n * @param options - Validation options\n * @returns Validation result\n *\n * @example\n * validateEmail('user@gmail.com')\n * // { valid: true, reason: 'valid', isFree: true }\n *\n * validateEmail('user@tempmail.com')\n * // { valid: false, reason: 'disposable' }\n *\n * validateEmail('user@gmial.com')\n * // { valid: true, reason: 'typo', suggestion: 'user@gmail.com' }\n */\nexport function validateEmail(\n email: string,\n options: EmailValidationOptions = {}\n): EmailValidationResult {\n const {\n checkDisposable = true,\n suggestTypoFix = true,\n blockedDomains = [],\n allowedDomains = [],\n } = options;\n\n // Normalize\n const normalized = email.trim().toLowerCase();\n\n // Basic checks\n if (!normalized || normalized.length > MAX_EMAIL_LENGTH) {\n return invalidResult('invalid_syntax', normalized);\n }\n\n const atIndex = normalized.lastIndexOf('@');\n if (atIndex === -1) {\n return invalidResult('invalid_syntax', normalized);\n }\n\n const localPart = normalized.slice(0, atIndex);\n const domain = normalized.slice(atIndex + 1);\n\n // Length checks\n if (localPart.length === 0 || localPart.length > MAX_LOCAL_LENGTH) {\n return invalidResult('invalid_syntax', normalized);\n }\n if (domain.length === 0 || domain.length > MAX_DOMAIN_LENGTH) {\n return invalidResult('invalid_syntax', normalized);\n }\n\n // Consecutive dots in local part\n if (localPart.includes('..')) {\n return invalidResult('invalid_syntax', normalized);\n }\n\n // Leading/trailing dots in local part\n if (localPart.startsWith('.') || localPart.endsWith('.')) {\n return invalidResult('invalid_syntax', normalized);\n }\n\n // Full regex validation\n if (!EMAIL_SYNTAX.test(normalized)) {\n return invalidResult('invalid_syntax', normalized);\n }\n\n // Check if domain is explicitly allowed (bypass other checks)\n const allowedSet = new Set(allowedDomains.map(d => d.toLowerCase()));\n if (allowedSet.has(domain)) {\n return {\n valid: true,\n reason: 'valid',\n suggestion: null,\n isFree: FREE_PROVIDERS.has(domain),\n isDisposable: false,\n normalized,\n };\n }\n\n // Check blocked domains\n const blockedSet = new Set(blockedDomains.map(d => d.toLowerCase()));\n if (blockedSet.has(domain)) {\n return invalidResult('blocked', normalized);\n }\n\n // Check disposable\n const isDisposable = DISPOSABLE_DOMAINS.has(domain);\n if (checkDisposable && isDisposable) {\n return {\n valid: false,\n reason: 'disposable',\n suggestion: null,\n isFree: false,\n isDisposable: true,\n normalized,\n };\n }\n\n // Check typos\n const isFree = FREE_PROVIDERS.has(domain);\n if (suggestTypoFix && DOMAIN_TYPOS[domain]) {\n const corrected = `${localPart}@${DOMAIN_TYPOS[domain]}`;\n return {\n valid: true,\n reason: 'typo',\n suggestion: corrected,\n isFree: FREE_PROVIDERS.has(DOMAIN_TYPOS[domain]),\n isDisposable: false,\n normalized,\n };\n }\n\n return {\n valid: true,\n reason: 'valid',\n suggestion: null,\n isFree,\n isDisposable,\n normalized,\n };\n}\n\n/**\n * Verify that the email domain has MX records (can receive email).\n *\n * This performs a DNS lookup and requires network access.\n * Use for registration flows where you need high confidence.\n *\n * @param email - Email address to verify\n * @returns True if the domain has MX records\n *\n * @example\n * if (await verifyEmailMx('user@example.com')) {\n * // Domain can receive email\n * }\n */\nexport async function verifyEmailMx(email: string): Promise<boolean> {\n if (!isValidEmailSyntax(email)) return false;\n\n const atIndex = email.lastIndexOf('@');\n const domain = email.slice(atIndex + 1).trim().toLowerCase();\n if (!domain) return false;\n\n try {\n const records = await dns.resolveMx(domain);\n return records.length > 0;\n } catch {\n return false;\n }\n}\n\n/**\n * Quick check if an email address has valid syntax.\n * Faster than validateEmail() — just syntax, no domain intelligence.\n */\nexport function isValidEmailSyntax(email: string): boolean {\n const normalized = email.trim().toLowerCase();\n if (!normalized || normalized.length > MAX_EMAIL_LENGTH) return false;\n\n const atIndex = normalized.lastIndexOf('@');\n if (atIndex === -1) return false;\n\n const localPart = normalized.slice(0, atIndex);\n if (localPart.includes('..') || localPart.startsWith('.') || localPart.endsWith('.')) return false;\n\n return EMAIL_SYNTAX.test(normalized);\n}\n"]}