{"version":3,"sources":["../../src/data/patterns.json","../../src/core/patterns-loader.ts","../../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":["lookup","host","dns"],"mappings":"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","file":"index.mjs","sourcesContent":["{\n  \"version\": \"1.1.0\",\n  \"description\": \"Arcis Core Security Patterns - Language Agnostic\",\n  \"_pattern_convention\": {\n    \"description\": \"Pattern field naming convention for SDK implementers\",\n    \"pattern\": \"The primary regex pattern. May contain ReDoS-vulnerable constructs.\",\n    \"pattern_safe\": \"ReDoS-safe alternative pattern. SDKs MUST prefer this when available.\",\n    \"redos_safe\": \"Boolean indicating if 'pattern' is ReDoS-safe. If false, use 'pattern_safe'.\",\n    \"usage\": \"pattern_str = rule.get('pattern_safe') or rule.get('pattern')\"\n  },\n  \"config\": {\n    \"max_input_size\": 1000000,\n    \"max_recursion_depth\": 10,\n    \"enable_redos_protection\": true\n  },\n  \"patterns\": {\n    \"xss\": {\n      \"name\": \"Cross-Site Scripting (XSS)\",\n      \"severity\": \"critical\",\n      \"owasp\": \"A03:2021\",\n      \"rules\": [\n        {\n          \"id\": \"xss-script-block\",\n          \"pattern\": \"<script[^>]*>[\\\\s\\\\S]*?</script>\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects full script blocks (open tag through close tag)\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"xss-script-tag\",\n          \"pattern\": \"<script[^>]*>\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects standalone or unclosed script tags\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"xss-javascript-protocol\",\n          \"pattern\": \"javascript\\\\s*:[^\\\\s]\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects javascript: protocol (allows whitespace around colon)\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"xss-event-handler-quoted\",\n          \"pattern\": \"(?:[\\\\s/])on\\\\w+\\\\s*=\\\\s*[\\\"'][^\\\"']*[\\\"']\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects inline event handlers with quoted values (e.g. onclick=\\\"alert(1)\\\")\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"xss-event-handler-unquoted\",\n          \"pattern\": \"(?:[\\\\s/])on\\\\w+\\\\s*=\\\\s*[^\\\\s>]*\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects inline event handlers with unquoted values (e.g. onload=value)\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"xss-iframe-block\",\n          \"pattern\": \"<iframe[^>]*>[\\\\s\\\\S]*?</iframe>\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects full iframe blocks\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"xss-iframe\",\n          \"pattern\": \"<iframe[^>]*\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects iframe tags (partial or unclosed)\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"xss-style-block\",\n          \"pattern\": \"<style[^>]*>[\\\\s\\\\S]*?</style>\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects full style blocks (CSS injection, expression(), behavior: attacks)\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"xss-style-tag\",\n          \"pattern\": \"<style[\\\\s>][^>]*\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects style tags (partial or unclosed). Requires whitespace or > after `style` so `<styled>` / `<StyledButton>` text is not flagged.\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"xss-object-block\",\n          \"pattern\": \"<object[^>]*>[\\\\s\\\\S]*?</object>\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects full object blocks\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"xss-object\",\n          \"pattern\": \"<object[^>]*\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects object tags (partial or unclosed)\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"xss-embed\",\n          \"pattern\": \"<embed[^>]*\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects embed tags\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"xss-data-uri\",\n          \"pattern\": \"data\\\\s*:\\\\s*(?:text/html|image/svg)[^>\\\\s]*\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects data: URIs with HTML or SVG content (avoids 'metadata' false positives)\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"xss-vbscript\",\n          \"pattern\": \"vbscript\\\\s*:\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects vbscript: protocol\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"xss-url-encoded\",\n          \"pattern\": \"%3Cscript\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects URL-encoded script tags\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"xss-svg-onload\",\n          \"pattern\": \"<svg[^>]*onload[^>]*>\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects SVG with inline event handlers\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"xss-form-injection\",\n          \"pattern\": \"<form[\\\\s>][^>]*\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects form tags used for phishing/credential harvesting via action= redirection\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"xss-meta-injection\",\n          \"pattern\": \"<meta[\\\\s>][^>]*\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects meta tags used for http-equiv refresh redirects or CSP bypass\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"xss-base-injection\",\n          \"pattern\": \"<base[\\\\s>][^>]*\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects base href hijacking — redirects all relative URLs to attacker domain\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"xss-link-injection\",\n          \"pattern\": \"<link[\\\\s>][^>]*\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects link tag injection used for stylesheet or preload CSRF attacks\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"xss-css-expression\",\n          \"pattern\": \"expression\\\\s*\\\\(\",\n          \"flags\": \"gi\",\n          \"description\": \"Legacy IE CSS expression() in a style context, executes JS. Benchmark xss-style-expression.\",\n          \"redos_safe\": true\n        }\n      ],\n      \"encoding\": {\n        \"&\": \"&amp;\",\n        \"<\": \"&lt;\",\n        \">\": \"&gt;\",\n        \"\\\"\": \"&quot;\",\n        \"'\": \"&#x27;\"\n      }\n    },\n    \"sql_injection\": {\n      \"name\": \"SQL Injection\",\n      \"severity\": \"critical\",\n      \"owasp\": \"A03:2021\",\n      \"rules\": [\n        {\n          \"id\": \"sqli-keywords\",\n          \"pattern\": \"(\\\\bUNION\\\\s+(?:ALL\\\\s+)?SELECT\\\\b)|(\\\\b(?:DROP|TRUNCATE)\\\\s+(?:TABLE|DATABASE|INDEX|VIEW|SCHEMA)\\\\b)|(\\\\bINTO\\\\s+(?:OUTFILE|DUMPFILE)\\\\b)|(\\\\bATTACH\\\\s+DATABASE\\\\b)|(\\\\bCREATE\\\\s+(?:USER|FUNCTION|TRIGGER|PROCEDURE)\\\\b)|(\\\\bGRANT\\\\s+(?:ALL|SELECT|INSERT|UPDATE|DELETE)\\\\b)|(\\\\bSHUTDOWN\\\\b)|(\\\\bxp_cmdshell\\\\b)|(\\\\bsp_executesql\\\\b)\",\n          \"flags\": \"gi\",\n          \"description\": \"Multi-token SQL attack shapes (UNION SELECT, DROP TABLE, INTO OUTFILE, etc.) that real attackers use and benign users essentially never type. Replaces older bare-keyword rule that false-positived on 'please select an option', 'I'll update you tomorrow'. Benchmark FP class B3, 2026-06-07.\",\n          \"redos_safe\": true,\n          \"request_boundary_safe\": true\n        },\n        {\n          \"id\": \"sqli-comments\",\n          \"pattern\": \"((?:^|[^!])--(?:[\\\\s-]|$)|/\\\\*|\\\\*/)\",\n          \"flags\": \"g\",\n          \"description\": \"SQL comments. ANSI -- requires a following space / dash / end-of-string so it matches a real trailing comment (1=1--, -- -) but not CLI flags (--max-retries). C-style /* */ kept. MySQL # excluded (see sqli-comment-quote). FPR pass 2026-06-08. The -- must not be immediately preceded by '!' so HTML comments (<!-- -->) are not flagged as SQL.\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"sqli-boolean-numeric\",\n          \"pattern\": \"\\\\bOR\\\\s+\\\\d+\\\\s*=\\\\s*\\\\d+\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects OR 1=1 style injection\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"sqli-boolean-string\",\n          \"pattern\": \"\\\\bOR\\\\s+['\\\"][^'\\\"]*['\\\"]\\\\s*=\\\\s*['\\\"][^'\\\"]*['\\\"]?\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects OR 'a'='a' style injection\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"sqli-and-numeric\",\n          \"pattern\": \"\\\\bAND\\\\s+\\\\d+\\\\s*=\\\\s*\\\\d+\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects AND 1=1 style injection\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"sqli-and-string\",\n          \"pattern\": \"\\\\bAND\\\\s+['\\\"][^'\\\"]*['\\\"]\\\\s*=\\\\s*['\\\"][^'\\\"]*['\\\"]?\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects AND 'a'='a' style injection\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"sqli-sleep\",\n          \"pattern\": \"\\\\bSLEEP\\\\s*\\\\(\\\\s*\\\\d+\\\\s*\\\\)\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects time-based blind SQLi via SLEEP\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"sqli-benchmark\",\n          \"pattern\": \"\\\\bBENCHMARK\\\\s*\\\\(\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects time-based blind SQLi via BENCHMARK\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"sqli-pg-sleep\",\n          \"pattern\": \"\\\\bpg_sleep\\\\s*\\\\(\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects time-based blind SQLi via PostgreSQL pg_sleep\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"sqli-waitfor-delay\",\n          \"pattern\": \"\\\\bWAITFOR\\\\s+DELAY\\\\b\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects time-based blind SQLi via MSSQL WAITFOR DELAY\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"sqli-oracle-dbms-packages\",\n          \"pattern\": \"\\\\bDBMS_(?:LOCK|PIPE|UTILITY|XSLPROCESSOR|JAVA|OUTPUT|SCHEDULER)\\\\b\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects Oracle DBMS_* package references used in time-based blind SQLi (DBMS_LOCK.SLEEP, DBMS_PIPE.RECEIVE_MESSAGE) and other Oracle stdlib abuse paths. Broad match — no legitimate user input contains these (improvements.md §1.1.e Q3).\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"sqli-hex-blob\",\n          \"pattern\": \"\\\\b0x[0-9a-fA-F]{16,}\\\\b\",\n          \"flags\": \"gi\",\n          \"description\": \"Long hex-encoded blob (8+ bytes) smuggling SQL past keyword filters; far longer than any hex color. Benchmark sql-hex-encoded.\",\n          \"redos_safe\": true,\n          \"request_boundary_safe\": true\n        },\n        {\n          \"id\": \"sqli-comment-quote\",\n          \"pattern\": \"'\\\\s*#\",\n          \"flags\": \"g\",\n          \"description\": \"Single-quote immediately followed by a MySQL # line comment (admin' #). Quote anchor avoids FP on bare # (hex colors, hashtags). Benchmark sql-comment-hash.\",\n          \"redos_safe\": true,\n          \"request_boundary_safe\": true\n        }\n      ]\n    },\n    \"nosql_injection\": {\n      \"name\": \"NoSQL Injection\",\n      \"severity\": \"critical\",\n      \"owasp\": \"A03:2021\",\n      \"rules\": [\n        {\n          \"id\": \"nosql-operators\",\n          \"pattern\": \"\\\\$(?:gt|gte|lt|lte|ne|eq|in|nin|and|or|not|nor|exists|type|regex|where|expr|mod|text|jsonSchema|function|accumulator|elemMatch|all|size|lookup|match|project|group|sort|limit|skip|unwind|addFields|replaceRoot)\\\\b\",\n          \"flags\": \"i\",\n          \"description\": \"Detects MongoDB operators\",\n          \"redos_safe\": true\n        }\n      ],\n      \"dangerous_keys\": [\n        \"$gt\",\n        \"$gte\",\n        \"$lt\",\n        \"$lte\",\n        \"$ne\",\n        \"$eq\",\n        \"$in\",\n        \"$nin\",\n        \"$and\",\n        \"$or\",\n        \"$not\",\n        \"$nor\",\n        \"$exists\",\n        \"$type\",\n        \"$regex\",\n        \"$where\",\n        \"$expr\",\n        \"$mod\",\n        \"$text\",\n        \"$jsonSchema\",\n        \"$function\",\n        \"$accumulator\",\n        \"$elemMatch\",\n        \"$all\",\n        \"$size\",\n        \"$lookup\",\n        \"$match\",\n        \"$project\",\n        \"$group\",\n        \"$sort\",\n        \"$limit\",\n        \"$skip\",\n        \"$unwind\",\n        \"$addFields\",\n        \"$replaceRoot\"\n      ]\n    },\n    \"command_injection\": {\n      \"name\": \"Command Injection\",\n      \"severity\": \"critical\",\n      \"owasp\": \"A03:2021\",\n      \"rules\": [\n        {\n          \"id\": \"cmdi-shell-chars\",\n          \"pattern\": \"[;&|]\\\\s*(?:(?:cat|ls|dir|rm|cp|mv|wget|curl|nc|ncat|bash|sh|zsh|ksh|chmod|chown|kill|ps|id|touch|ping|dig|su|head|tail|php|sed|whoami|nslookup|nmap|python3?|perl|ruby|node|eval|exec|sudo|telnet|ssh|ftp|tftp|scp|awk|xxd)\\\\b|base64(?:\\\\s|$))\",\n          \"flags\": \"g\",\n          \"description\": \"Shell command chained after a metacharacter (; cat, | nc, & curl). Replaces the bare [;&|`] rule, which flagged every semicolon / ampersand in code, URLs and prose. FPR pass 2026-06-08. base64 requires a trailing space or end-of-input so `;base64 -d` is caught but the data-URI `;base64,` MIME parameter is not (FP on inline images).\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"cmdi-command-substitution\",\n          \"pattern\": \"\\\\$\\\\(\",\n          \"flags\": \"g\",\n          \"description\": \"Detects command substitution syntax $( — paired form, fewer false positives than bare parens\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"cmdi-ifs-bypass\",\n          \"pattern\": \"\\\\$\\\\{IFS(?:%[^}]*)?\\\\}\",\n          \"flags\": \"g\",\n          \"description\": \"Detects POSIX shell IFS substitution like ${IFS} or ${IFS%??}. Attackers use this to inject spaces past metacharacter filters in commands like `;cat${IFS}/etc/passwd` (improvements.md §1.1.e Q5).\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"cmdi-control-chars\",\n          \"pattern\": \"%0[0-9a-fA-F]\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects URL-encoded control characters (%00-%0F) including null, tab, vertical tab, form feed, LF, CR\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"cmdi-redirection\",\n          \"pattern\": \"(>>|<<<?)\",\n          \"flags\": \"g\",\n          \"description\": \"Append (>>) and here-doc (<<, <<<) redirection. The bare [<>] space form was dropped (matched 'a > b', 'RAM > 16GB'); redirect-to-file is covered by cmdi-redirect-syspath. FPR pass 2026-06-08.\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"cmdi-env-assign\",\n          \"pattern\": \"\\\\b(?:LD_PRELOAD|LD_LIBRARY_PATH|BASH_ENV|PYTHONPATH|PERL5LIB|DYLD_INSERT_LIBRARIES)\\\\s*=\",\n          \"flags\": \"gi\",\n          \"description\": \"Dangerous env-var assignment to smuggle code into a spawned process (LD_PRELOAD=). Benchmark cmd-env-var-smuggling.\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"cmdi-redirect-syspath\",\n          \"pattern\": \">\\\\s*/(?:etc|var|tmp|usr|bin|sbin|root|home|dev|proc|opt)\\\\b\",\n          \"flags\": \"g\",\n          \"description\": \"Shell output redirect to a system directory (> /var/www/shell.php). Anchored to system dirs to avoid math/text FP. Benchmark cmd-redirect-overwrite.\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"cmdi-newline-command\",\n          \"pattern\": \"[\\\\n\\\\r]\\\\s*(?:cat|ls|dir|rm|cp|mv|wget|curl|nc|ncat|bash|sh|zsh|ksh|chmod|chown|kill|ps|id|touch|ping|dig|su|head|tail|php|sed|whoami|nslookup|nmap|python3?|perl|ruby|node|eval|exec|sudo|telnet|ssh|ftp|tftp|scp|awk|xxd|base64)\\\\b\",\n          \"flags\": \"gi\",\n          \"description\": \"Newline followed by a shell command (host\\\\ncat /etc/passwd). Benchmark cmd-newline-injection.\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"cmdi-backtick-command\",\n          \"pattern\": \"`\\\\s*(?:cat|ls|dir|rm|cp|mv|wget|curl|nc|ncat|bash|sh|zsh|ksh|chmod|chown|kill|ps|id|touch|ping|dig|su|head|tail|php|sed|whoami|nslookup|nmap|python3?|perl|ruby|node|eval|exec|sudo|telnet|ssh|ftp|tftp|scp|awk|xxd|base64)\\\\b\",\n          \"flags\": \"gi\",\n          \"description\": \"Backtick command substitution wrapping a known command (`whoami`). Avoids FP on markdown inline code like `npm install`. FPR pass 2026-06-08.\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"cmdi-jndi-lookup\",\n          \"pattern\": \"\\\\$\\\\{jndi:\",\n          \"flags\": \"gi\",\n          \"description\": \"JNDI lookup used by Log4Shell, e.g. ${jndi:ldap://host/a}\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"cmdi-ssi-directive\",\n          \"pattern\": \"<!--\\\\s*#\\\\s*(?:exec|include|echo|config|fsize|flastmod|printenv)\\\\b\",\n          \"flags\": \"gi\",\n          \"description\": \"Server-side include (SSI) directive, e.g. <!--#exec cmd=...-->\",\n          \"redos_safe\": true\n        }\n      ]\n    },\n    \"path_traversal\": {\n      \"name\": \"Path Traversal\",\n      \"severity\": \"high\",\n      \"owasp\": \"A01:2021\",\n      \"rules\": [\n        {\n          \"id\": \"path-dotdot\",\n          \"pattern\": \"\\\\.\\\\./\",\n          \"flags\": \"g\",\n          \"description\": \"Detects ../ sequences\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"path-dotdot-backslash\",\n          \"pattern\": \"\\\\.\\\\.\\\\\\\\\",\n          \"flags\": \"g\",\n          \"description\": \"Detects ..\\\\ sequences\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"path-encoded\",\n          \"pattern\": \"%2e%2e\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects URL-encoded traversal\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"path-double-encoded\",\n          \"pattern\": \"%252e\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects double URL-encoded traversal\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"path-double-encoded-slash\",\n          \"pattern\": \"%252f\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects double URL-encoded forward slash\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"path-dotdotslash-bypass\",\n          \"pattern\": \"\\\\.{2,}[/\\\\\\\\]{2,}\",\n          \"flags\": \"g\",\n          \"description\": \"Detects ....// and ....\\\\\\\\ bypass variants\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"path-null-byte\",\n          \"pattern\": \"%00\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects URL-encoded null byte injection\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"path-null-byte-literal\",\n          \"pattern\": \"\\\\x00\",\n          \"flags\": \"g\",\n          \"description\": \"Detects literal NUL byte in path inputs\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"path-mixed-encoded\",\n          \"pattern\": \"\\\\.\\\\.%2[fF]\",\n          \"flags\": \"g\",\n          \"description\": \"Detects mixed encoding: literal `..` followed by URL-encoded slash (%2F). Benchmark B6 — Python had no coverage; Node had this pattern. 2026-06-07.\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"path-mixed-dot-encoded\",\n          \"pattern\": \"%2e\\\\.[\\\\\\\\/]\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects half-encoded traversal: URL-encoded dot (%2e) followed by a literal dot and a slash (`%2e./`). Node caught this; Python/Go did not. Migration 2026-06-13.\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"path-mixed-dot-encoded-rev\",\n          \"pattern\": \"\\\\.%2e[\\\\\\\\/]\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects half-encoded traversal: literal dot followed by URL-encoded dot (%2e) and a slash (`.%2e/`). Node caught this; Python/Go did not. Migration 2026-06-13.\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"path-overlong-utf8-dot\",\n          \"pattern\": \"%[Cc]0%[Aa][Ee]\",\n          \"flags\": \"g\",\n          \"description\": \"Detects overlong UTF-8 encoding of '.' (%C0%AE). Historic IIS/Apache decoder bypass. Real char `.` is %2E; overlong encoding only appears in evasion attempts. Benchmark B6 gap — neither SDK caught this before. 2026-06-07.\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"path-overlong-utf8-slash\",\n          \"pattern\": \"%[Cc]0%[Aa][Ff]\",\n          \"flags\": \"g\",\n          \"description\": \"Detects overlong UTF-8 encoding of forward slash (%C0%AF). Same IIS/Apache decoder bypass class as path-overlong-utf8-dot. Real slash is %2F; overlong form only appears in evasion. 2026-06-13.\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"path-windows-unc\",\n          \"pattern\": \"\\\\\\\\\\\\\\\\[A-Za-z0-9_.-]+\\\\\\\\\",\n          \"flags\": \"g\",\n          \"description\": \"Detects Windows UNC paths (\\\\\\\\\\\\\\\\server\\\\\\\\share) in user input. Legitimate web-app inputs never contain UNC references; attacker UNC payloads leak SMB auth or pull remote payloads. Benchmark B6. 2026-06-07.\",\n          \"redos_safe\": true\n        }\n      ]\n    },\n    \"prototype_pollution\": {\n      \"name\": \"Prototype Pollution\",\n      \"severity\": \"high\",\n      \"owasp\": \"A03:2021\",\n      \"languages\": [\n        \"javascript\",\n        \"typescript\"\n      ],\n      \"dangerous_keys\": [\n        \"__proto__\",\n        \"constructor\",\n        \"prototype\",\n        \"__definegetter__\",\n        \"__definesetter__\",\n        \"__lookupgetter__\",\n        \"__lookupsetter__\"\n      ]\n    },\n    \"ldap_injection\": {\n      \"name\": \"LDAP Injection\",\n      \"severity\": \"high\",\n      \"owasp\": \"A03:2021\",\n      \"rules\": [\n        {\n          \"id\": \"ldap-special\",\n          \"pattern\": \"[()\\\\\\\\*]\",\n          \"flags\": \"g\",\n          \"description\": \"Detects LDAP special characters. Broad rule for sanitization context only; NOT safe for request-boundary scanning (every parenthesised string trips it). Use 'ldap-injection-strict' below at request boundary.\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"ldap-injection-strict\",\n          \"pattern\": \"\\\\)\\\\s*\\\\(|\\\\*\\\\s*\\\\)\\\\s*\\\\(\",\n          \"flags\": \"g\",\n          \"description\": \"Detects attack-specific LDAP filter-break shapes ')(' and '*)('. Safe to wire into request-boundary scanners. Catches '*)(uid=*))(|(uid=*' style payloads.\",\n          \"redos_safe\": true,\n          \"request_boundary_safe\": true\n        },\n        {\n          \"id\": \"ldap-not-bypass\",\n          \"pattern\": \"\\\\)\\\\s*\\\\(\\\\s*!|&\\\\s*\\\\(\\\\s*!|\\\\|\\\\s*\\\\(\\\\s*!\",\n          \"flags\": \"g\",\n          \"description\": \"Detects LDAP NOT-operator bypass shapes ')(!', '&(!', and '|(!'. Catches injections where attacker appends a NOT clause to enumerate or exclude specific entries, e.g. '*)(uid=*)(!(uid=admin))'. Companion to ldap-injection-strict; together they cover the OR-then-NOT enumeration corpus that legitimate LDAP queries never produce. (improvements.md Q8.)\",\n          \"redos_safe\": true,\n          \"request_boundary_safe\": true\n        },\n        {\n          \"id\": \"ldap-null-byte\",\n          \"pattern\": \"\\\\\\\\00|\\\\x00\",\n          \"flags\": \"g\",\n          \"description\": \"LDAP null-byte truncation (admin\\\\00) used to terminate a filter early and bypass appended constraints. Benchmark ldap-null-byte-truncate.\",\n          \"redos_safe\": true,\n          \"request_boundary_safe\": true\n        }\n      ]\n    },\n    \"xpath_injection\": {\n      \"name\": \"XPath Injection\",\n      \"severity\": \"high\",\n      \"owasp\": \"A03:2021\",\n      \"rules\": [\n        {\n          \"id\": \"xpath-injection-strict\",\n          \"pattern\": \"('\\\\s*(or|and)\\\\s*'|\\\"\\\\s*(or|and)\\\\s*\\\"|\\\\)\\\\s*(or|and)\\\\s*\\\\(|\\\\|\\\\s*/)\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects XPath injection patterns: boolean-injection (' or ' / \\\" or \\\"), function-arity tampering with closing+opening parens around or/and, and union operator before path step. Request-boundary safe.\",\n          \"redos_safe\": true,\n          \"request_boundary_safe\": true\n        },\n        {\n          \"id\": \"xpath-blind-substring\",\n          \"pattern\": \"\\\\bsubstring\\\\s*\\\\(\\\\s*name\\\\s*\\\\(|\\\\bstring-length\\\\s*\\\\(|\\\\bcount\\\\s*\\\\(\\\\s*/\",\n          \"flags\": \"gi\",\n          \"description\": \"Blind XPath extraction via substring(name()), string-length(), count(/...). Benchmark xpath-blind-substring.\",\n          \"redos_safe\": true,\n          \"request_boundary_safe\": true\n        }\n      ]\n    },\n    \"email_header_injection\": {\n      \"name\": \"Email Header Injection (SMTP CRLF)\",\n      \"severity\": \"high\",\n      \"owasp\": \"A03:2021\",\n      \"rules\": [\n        {\n          \"id\": \"email-header-smtp-keyword\",\n          \"pattern\": \"(\\\\r\\\\n|\\\\r|\\\\n)\\\\s*(bcc|cc|to|from|subject|reply-to|return-path|content-type|x-mailer)\\\\s*:\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects SMTP header injection: CR/LF followed by a known SMTP header keyword. Narrow enough for request-boundary scanning because legitimate user input rarely contains '\\\\nBcc:' verbatim.\",\n          \"redos_safe\": true,\n          \"request_boundary_safe\": true\n        },\n        {\n          \"id\": \"email-header-bare-newline\",\n          \"pattern\": \"(\\\\r\\\\n|\\\\r|\\\\n)\\\\s*[a-z][a-z0-9-]{0,40}\\\\s*:\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects bare CR/LF followed by ANY plausible header keyword (not just the SMTP allowlist). Catches MTAs that accept bare LF or bare CR as a header separator (qmail, sendmail variants), where attacker bypasses the strict keyword list by injecting a non-SMTP-keyword header (X-Custom, Authentication-Results, DKIM-Signature, ...). Companion to email-header-smtp-keyword; together they cover the bypass class. (improvements.md Q10.)\",\n          \"redos_safe\": true,\n          \"request_boundary_safe\": true\n        }\n      ]\n    },\n    \"ssti\": {\n      \"name\": \"Server-Side Template Injection (SSTI)\",\n      \"severity\": \"critical\",\n      \"owasp\": \"A03:2021\",\n      \"rules\": [\n        {\n          \"id\": \"ssti-jinja2-twig\",\n          \"pattern\": \"\\\\{\\\\{.*?\\\\}\\\\}\",\n          \"flags\": \"g\",\n          \"description\": \"Detects Jinja2/Twig/Nunjucks template expressions ({{ ... }})\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"ssti-freemarker-spring\",\n          \"pattern\": \"\\\\$\\\\{.*?\\\\}\",\n          \"pattern_safe\": \"\\\\$\\\\{[^}]*[?!()*+\\\\-/][^}]*\\\\}\",\n          \"flags\": \"g\",\n          \"description\": \"Detects Freemarker/Thymeleaf/Spring EL expressions. Detection uses broad pattern; sanitization uses pattern_safe which requires operators/method-calls to avoid false-positives on JS template literals like ${name}.\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"ssti-erb-ejs\",\n          \"pattern\": \"<%[=\\\\-]?.*?%>\",\n          \"flags\": \"gs\",\n          \"description\": \"Detects ERB/EJS template tags (<%= ... %>, <% ... %>)\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"ssti-pug-jade\",\n          \"pattern\": \"#\\\\{.*?\\\\}\",\n          \"pattern_safe\": \"#\\\\{[^}]*[?!()*+\\\\-/][^}]*\\\\}\",\n          \"flags\": \"g\",\n          \"description\": \"Detects Pug/Jade/Slim template expressions. Detection uses broad pattern; sanitization uses pattern_safe to avoid false-positives on Ruby/Pug output like #{name}.\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"ssti-python-class-chain\",\n          \"pattern\": \"__(?:class|mro|subclasses|globals|builtins|import)__\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects Python sandbox escape via dunder attributes\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"ssti-config-leak\",\n          \"pattern\": \"\\\\{\\\\{\\\\s*config[.\\\\[]\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects Jinja2 config/settings leak attempts\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"ssti-jinja2-functions\",\n          \"pattern\": \"\\\\{\\\\{\\\\s*(?:self|request|lipsum|cycler|joiner|namespace|range)\\\\b\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects Jinja2 built-in object/function access\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"ssti-velocity-ognl\",\n          \"pattern\": \"#set\\\\s*\\\\(\\\\s*\\\\$|#foreach\\\\s*\\\\(\\\\s*\\\\$|\\\\$\\\\w+\\\\.(?:exec|getClass|getRuntime|getMethod|invoke)\\\\b\",\n          \"flags\": \"gi\",\n          \"description\": \"Velocity #set/#foreach directives and OGNL/Velocity method calls. Benchmark ssti-velocity-runtime.\",\n          \"redos_safe\": true\n        }\n      ]\n    },\n    \"xml_injection\": {\n      \"name\": \"XML/XXE Injection\",\n      \"severity\": \"critical\",\n      \"owasp\": \"A03:2021\",\n      \"rules\": [\n        {\n          \"id\": \"xxe-doctype\",\n          \"pattern\": \"<!DOCTYPE\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects DOCTYPE declarations\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"xxe-entity\",\n          \"pattern\": \"<!ENTITY\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects ENTITY declarations\",\n          \"redos_safe\": true\n        },\n        {\n          \"id\": \"xxe-system\",\n          \"pattern\": \"SYSTEM\\\\s+[\\\"']\",\n          \"flags\": \"gi\",\n          \"description\": \"Detects SYSTEM references\",\n          \"redos_safe\": true\n        }\n      ]\n    }\n  },\n  \"security_headers\": {\n    \"Content-Security-Policy\": \"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; object-src 'none'; frame-ancestors 'none';\",\n    \"X-Content-Type-Options\": \"nosniff\",\n    \"X-Frame-Options\": \"DENY\",\n    \"X-XSS-Protection\": \"0\",\n    \"Strict-Transport-Security\": \"max-age=31536000; includeSubDomains\",\n    \"Referrer-Policy\": \"strict-origin-when-cross-origin\",\n    \"Permissions-Policy\": \"geolocation=(), microphone=(), camera=()\",\n    \"X-Permitted-Cross-Domain-Policies\": \"none\",\n    \"Cache-Control\": \"no-store, no-cache, must-revalidate, proxy-revalidate\",\n    \"Pragma\": \"no-cache\",\n    \"Expires\": \"0\"\n  },\n  \"rate_limiting\": {\n    \"default_max\": 100,\n    \"default_window_ms\": 60000,\n    \"headers\": {\n      \"limit\": \"X-RateLimit-Limit\",\n      \"remaining\": \"X-RateLimit-Remaining\",\n      \"reset\": \"X-RateLimit-Reset\"\n    }\n  },\n  \"sensitive_keys\": [\n    \"password\",\n    \"passwd\",\n    \"pwd\",\n    \"secret\",\n    \"token\",\n    \"apikey\",\n    \"api_key\",\n    \"apiKey\",\n    \"auth\",\n    \"authorization\",\n    \"credit_card\",\n    \"creditcard\",\n    \"cc\",\n    \"ssn\",\n    \"social_security\",\n    \"private_key\",\n    \"privateKey\",\n    \"access_token\",\n    \"accessToken\",\n    \"refresh_token\",\n    \"refreshToken\",\n    \"bearer\",\n    \"jwt\",\n    \"session\",\n    \"cookie\",\n    \"x-api-key\",\n    \"x-auth-token\",\n    \"credentials\"\n  ],\n  \"validation\": {\n    \"email\": \"^[^\\\\s@]+@[^\\\\s@]+\\\\.[^\\\\s@]+$\",\n    \"url\": \"^https?://[^\\\\s/$.?#].[^\\\\s]*$\",\n    \"uuid\": \"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$\",\n    \"ipv4\": \"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$\"\n  }\n}\n","/**\n * @module @arcis/node/core/patterns-loader\n *\n * Loads the shared security regex patterns from the bundled\n * `data/patterns.json` (a copy of the canonical `packages/core/patterns.json`,\n * kept byte-identical by the CI data-sync-check). This is the Node counterpart\n * of Go's `sanitizers/loader.go` and Python's `core/constants.load_patterns` +\n * `_compile_rules`.\n *\n * Why this exists: pre-migration, Node hardcoded every regex in `constants.ts`,\n * so each pattern edit was a manual dual-write against patterns.json (which\n * Python + Go already load at runtime). That is a Pattern 2 violation\n * (Shared Pattern Repository). Sourcing the arrays from patterns.json here\n * makes patterns.json the single source for all three SDKs.\n *\n * The JSON is a static `import`, so tsup inlines it into the CJS + ESM bundles\n * at build time (same as `bot-detection.ts` does with `bot-patterns.json`); no\n * runtime filesystem access. resolveJsonModule is already enabled in tsconfig.\n */\n\nimport patternsData from '../data/patterns.json';\n\ninterface PatternRule {\n  id: string;\n  pattern: string;\n  pattern_safe?: string;\n  flags?: string;\n}\n\ninterface PatternCategory {\n  rules?: PatternRule[];\n  dangerous_keys?: string[];\n}\n\ninterface PatternsSpec {\n  version: string;\n  patterns: Record<string, PatternCategory>;\n}\n\nconst spec = patternsData as unknown as PatternsSpec;\n\n/** Valid JavaScript RegExp flags. patterns.json uses g/i/m; anything else is dropped. */\nconst VALID_JS_FLAGS = new Set(['g', 'i', 'm', 's', 'u', 'y']);\n\nfunction normalizeFlags(flags: string | undefined): string {\n  if (!flags) return '';\n  let out = '';\n  for (const ch of flags) {\n    if (VALID_JS_FLAGS.has(ch) && !out.includes(ch)) out += ch;\n  }\n  return out;\n}\n\n/**\n * Compile every rule in a patterns.json category into a RegExp array, in file\n * order. Prefers the ReDoS-safe `pattern_safe` variant when present (parity\n * with Go's compileCategory / Python's _compile_rules). A category with no\n * rules returns an empty array.\n */\nexport function compileCategory(category: string): RegExp[] {\n  const cat = spec.patterns[category];\n  if (!cat?.rules) return [];\n  const out: RegExp[] = [];\n  for (const rule of cat.rules) {\n    const raw = rule.pattern_safe || rule.pattern;\n    if (!raw) continue;\n    out.push(new RegExp(raw, normalizeFlags(rule.flags)));\n  }\n  return out;\n}\n\n/**\n * Compile a single named rule from a category into one RegExp, or undefined if\n * the rule id is absent. Used where Node consumes a single pattern rather than\n * a list (e.g. the string-form NoSQL operator check).\n */\nexport function compileRule(category: string, id: string, flags?: string): RegExp | undefined {\n  const rule = spec.patterns[category]?.rules?.find((r) => r.id === id);\n  if (!rule) return undefined;\n  const raw = rule.pattern_safe || rule.pattern;\n  if (!raw) return undefined;\n  return new RegExp(raw, normalizeFlags(flags ?? rule.flags));\n}\n\n/**\n * Return the `dangerous_keys` list for a category (e.g. `nosql_injection`,\n * `prototype_pollution`), or an empty array if the category has none.\n */\nexport function dangerousKeysFor(category: string): string[] {\n  return spec.patterns[category]?.dangerous_keys ?? [];\n}\n\n/** The version string from the bundled patterns.json. Used by tests/diagnostics. */\nexport const PATTERNS_VERSION = spec.version;\n","/**\n * @module @arcis/node/core/constants\n * Named constants for Arcis - no magic numbers\n */\n\nimport { compileCategory, compileRule, dangerousKeysFor } from './patterns-loader';\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 (sourced from patterns.json)\n// =============================================================================\n\n/**\n * XSS patterns, compiled from the shared `patterns.json` `xss` category.\n *\n * Both detection (detectXss) and removal (sanitizeXss) iterate the same rule\n * list: the rules are precise capture patterns (full tag / attribute / protocol)\n * so they double as removal targets, and patterns.json file order keeps every\n * block rule (e.g. `<script>...</script>`) ahead of its bare-tag counterpart so\n * removal strips the larger match first. Pre-migration these were two hardcoded\n * arrays (a broad detect set + a precise remove set); single-sourcing them here\n * ends the dual-write against patterns.json and converges Node onto the same\n * detection Python + Go already ship (Pattern 2 + Pattern 7).\n */\nexport const XSS_PATTERNS = compileCategory('xss');\n\n/** Removal patterns for sanitizeXss(): the same patterns.json `xss` rules.\n *  A separate compiled array so its RegExp lastIndex state is independent of\n *  the detection pass. */\nexport const XSS_REMOVE_PATTERNS = compileCategory('xss');\n\n// =============================================================================\n// SQL INJECTION PATTERNS (sourced from patterns.json)\n// =============================================================================\n/** Compiled from the shared `patterns.json` `sql_injection` category. The\n *  quoted-boolean rules use RE2-safe `['\"]...['\"]` (no backreference) since\n *  patterns.json is also consumed by Go's RE2 engine; validated equivalent to\n *  the former backreference forms on the O'Brien / tautology corpus. */\nexport const SQL_PATTERNS = compileCategory('sql_injection');\n\n// =============================================================================\n// PATH TRAVERSAL PATTERNS (sourced from patterns.json)\n// =============================================================================\n/** Compiled from the shared `patterns.json` `path_traversal` category. */\nexport const PATH_PATTERNS = compileCategory('path_traversal');\n\n// =============================================================================\n// COMMAND INJECTION PATTERNS (sourced from patterns.json)\n// =============================================================================\n/** Compiled from the shared `patterns.json` `command_injection` category. */\nexport const COMMAND_PATTERNS = compileCategory('command_injection');\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(dangerousKeysFor('prototype_pollution'));\n\n/** MongoDB operators to block, from `patterns.json` nosql_injection.dangerous_keys. */\nexport const NOSQL_DANGEROUS_KEYS = new Set(dangerousKeysFor('nosql_injection'));\n\n/**\n * String-form NoSQL operator detection (block-mode scanThreats).\n *\n * NOSQL_DANGEROUS_KEYS catches operators that arrive as OBJECT KEYS\n * (`{\"$gt\": \"\"}`). But MongoDB operators also bypass as STRING VALUES —\n * query params like `?username[$ne]=1` arrive as the literal string\n * `$ne` before the body parser ever builds an object, and mongo-shell\n * payloads (`$where: '1==1'`) are plain strings. Node previously had no\n * string-level NoSQL check, so GoTestWAF scored NoSQL at 0% while Python\n * (which loads the shared `nosql-operators` rule) caught these. This\n * closes that Pattern-7 parity gap.\n *\n * Sourced from the `nosql-operators` rule in patterns.json. The trailing\n * `\\b` word boundary keeps `$invoice`/`$order`/`$index` from matching\n * `$in`/`$or` (a false-positive class the un-bounded rule had).\n */\nconst nosqlStringRule = compileRule('nosql_injection', 'nosql-operators');\nif (!nosqlStringRule) {\n  throw new Error('arcis: nosql-operators rule missing from patterns.json');\n}\nexport const NOSQL_STRING_PATTERN = nosqlStringRule;\n\n/**\n * Identity/auth field names that must hold a scalar value. A field here\n * carrying an array or object is a NoSQL type-juggling operator-injection\n * shape (e.g. {\"username\":[\"admin\"]}). v1.7 nosql-type-juggle.\n */\nexport const AUTH_FIELDS = new Set([\n  'username', 'user', 'userid', 'user_id', 'login', 'email',\n  'password', 'pass', 'passwd', 'pwd', 'token', 'apikey', 'api_key',\n  'secret', 'otp', 'pin',\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  // All XSS detection now flows through the shared patterns.json `xss` rules\n  // (event handlers, javascript:/vbscript:/data: protocols, tags). The former\n  // inline fast-path checks were a strict subset of these rules, so they were\n  // removed when the patterns moved to patterns.json (single source).\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, AUTH_FIELDS } 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 { detectHeaderInjectionStrict } from './headers';\nimport { detectDeserialization } from './deserialization';\nimport { detectNoSqlString } from './nosql';\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 */\n/**\n * Decode URL + HTML entity layers until the string is stable.\n *\n * improvements.md §1.1.b — closes the encoding-stack bypass class.\n * A payload like `%2526%2523x3c%253bscript%2526%2523x3e%253b` is a\n * triple-encoded `<script>`: pass 1 URL-decodes to\n * `%26%23x3c%3bscript%26%23x3e%3b`, pass 2 URL-decodes to\n * `&#x3c;script&#x3e;`, pass 3 HTML-decodes to `<script>`. Without\n * this helper the literal ASCII `<script>` never appears in the\n * string, so the XSS regex never fires.\n *\n * Bounded at 4 passes to prevent pathological-input loops. Base64\n * decoding is intentionally NOT in the chain — false-positive rate\n * on arbitrary text would be high.\n */\nfunction multiDecode(value: string, maxPasses = 4): string {\n  for (let i = 0; i < maxPasses; i++) {\n    const prev = value;\n\n    // URL-decode. decodeURIComponent throws on malformed sequences\n    // (lone `%` with no hex pair); treat that as \"no further\n    // URL-decoding possible\" and continue with the current value.\n    try {\n      value = decodeURIComponent(value);\n    } catch {\n      // leave value as-is\n    }\n\n    // HTML entity decode. No built-in in Node, so inline the common\n    // entities here. Numeric (`&#NN;`, `&#xHH;`) covers the bulk of\n    // XSS-encoding tricks; the five named entities below cover the\n    // rest of the encoding-bypass test corpus.\n    value = htmlEntityDecode(value);\n\n    if (value === prev) break;\n  }\n  return value;\n}\n\n/** Decode HTML entities — numeric (decimal + hex) plus the five core\n * named entities that XSS payloads use. Keeps the dep-free zero-dep\n * footprint of `@arcis/node`. */\nfunction htmlEntityDecode(s: string): string {\n  // &#NN; decimal numeric\n  s = s.replace(/&#(\\d+);/g, (_m, n) => {\n    const code = parseInt(n, 10);\n    return Number.isFinite(code) && code >= 0 && code <= 0x10ffff\n      ? String.fromCodePoint(code)\n      : _m;\n  });\n  // &#xHH; or &#XHH; hex numeric\n  s = s.replace(/&#x([0-9a-fA-F]+);/g, (_m, h) => {\n    const code = parseInt(h, 16);\n    return Number.isFinite(code) && code >= 0 && code <= 0x10ffff\n      ? String.fromCodePoint(code)\n      : _m;\n  });\n  // The five named entities that matter for XSS detection.\n  const named: Record<string, string> = {\n    '&lt;': '<',\n    '&gt;': '>',\n    '&amp;': '&',\n    '&quot;': '\"',\n    '&apos;': \"'\",\n    '&nbsp;': ' ',\n  };\n  for (const [entity, ch] of Object.entries(named)) {\n    s = s.split(entity).join(ch);\n  }\n  return s;\n}\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\n  // SECURITY: Normalize Unicode to NFKC BEFORE every detector runs.\n  // Fullwidth glyphs (`＜script＞`, `１+１＝２`) collapse to their ASCII\n  // equivalents, closing the entire fullwidth-bypass class for XSS,\n  // SQL, command-injection, and path-traversal in a single pass.\n  // improvements.md §1.1.a. Bypass example closed:\n  //   `＜script＞alert(1)＜/script＞`  →  `<script>alert(1)</script>`\n  let result = value.normalize('NFKC');\n\n  // SECURITY: Multi-pass URL + HTML decode (improvements.md §1.1.b).\n  // Closes the encoding-stack bypass class. After NFKC,\n  // `%2526%2523x3c%253bscript%2526%2523x3e%253b` (triple-encoded\n  // `<script>`) decodes all the way to `<script>` and hits the\n  // normal XSS strip below. Bounded at 4 passes.\n  result = multiDecode(result);\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    | 'deserialization';\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 value = (data as Record<string, unknown>)[key];\n      // NoSQL type-juggling: an auth/identity field whose value is an\n      // ARRAY instead of a scalar (e.g. {\"username\":[\"admin\"]}). MongoDB\n      // turns the array into an $in-style operator, bypassing string\n      // equality. Only arrays are flagged: operator-objects ({\"$ne\":...})\n      // are caught by NOSQL_DANGEROUS_KEYS, and a plain nested object under\n      // an auth field is legitimate. Benchmark nosql-mongo-type-juggle.\n      if (AUTH_FIELDS.has(lower) && Array.isArray(value)) {\n        return { vector: 'nosql', rule: 'nosql/type-juggle', matchedPattern: key };\n      }\n      const inner = scanThreats(value, 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  // SECURITY: normalize the SAME way sanitizeString does before any\n  // detector runs, so block-mode detection honors the NFKC + multi-decode\n  // bypass protection. Without this, block mode missed fullwidth\n  // `<script>` (U+FF1C), `%3Cscript%3E`, and HTML-entity-encoded payloads\n  // that sanitize-mode already caught. (v1.7 parity fix 2026-06-08.)\n  const norm = multiDecode(data.normalize('NFKC'));\n\n  const sample = norm.slice(0, 80);\n  // __proto__ as a STRING VALUE (e.g. [\"__proto__\",\"isAdmin\"] gadget-chain\n  // path array). The key-based check above only sees object keys; a dunder\n  // proto token as a value is the array-form signal. Only the dunder forms\n  // (never legit prose) are flagged, so the words \"constructor\"/\"prototype\"\n  // in normal text don't trip it. Benchmark proto-pollution-array-index.\n  if (norm.includes('__proto__')) {\n    return { vector: 'prototype', rule: 'prototype/match', matchedPattern: sample };\n  }\n  if (detectXss(norm)) {\n    return { vector: 'xss', rule: 'xss/match', matchedPattern: sample };\n  }\n  if (detectSsti(norm)) {\n    return { vector: 'ssti', rule: 'ssti/match', matchedPattern: sample };\n  }\n  if (detectXxe(norm)) {\n    return { vector: 'xxe', rule: 'xxe/match', matchedPattern: sample };\n  }\n  // Deserialization markers (pickle incl. base64, Ruby Marshal, .NET,\n  // FastJSON, PHP). Wired into block mode in v1.7 (was detection-only).\n  const deser = detectDeserialization(norm);\n  if (deser) {\n    return { vector: 'deserialization', rule: `deserialization/${deser}`, matchedPattern: sample };\n  }\n  if (detectSql(norm)) {\n    return { vector: 'sql', rule: 'sql/match', matchedPattern: sample };\n  }\n  // Path detection runs on the RAW string so the encoded-form patterns\n  // (%C0%AE overlong UTF-8, %2e%2e, %252f) still fire; multiDecode would\n  // otherwise strip them. The decoded `../` form survives in raw too.\n  if (detectPathTraversal(data) || detectPathTraversal(norm)) {\n    return { vector: 'path', rule: 'path/match', matchedPattern: sample };\n  }\n  if (detectCommandInjection(norm)) {\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(norm)) {\n    return { vector: 'ldap', rule: 'ldap/match', matchedPattern: sample };\n  }\n  if (detectXpathInjection(norm)) {\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 (detectHeaderInjectionStrict(norm)) {\n    return { vector: 'header', rule: 'header/match', matchedPattern: sample };\n  }\n  // String-form NoSQL operator (`$where`, `$ne`, `[$gt]`) carried in a\n  // string value rather than an object key. Last in the chain so the\n  // more-specific detectors above win; this only catches operator tokens\n  // that would otherwise pass through. Closes the Node-vs-Python NoSQL\n  // string parity gap (GoTestWAF NoSQL was 0% on Node).\n  if (detectNoSqlString(norm)) {\n    return { vector: 'nosql', rule: 'nosql/string', 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 * Matches a string that begins with a URL scheme + `://` (or `scheme:/`\n * for the `file:///path` form). Used by the body walker to decide which\n * string values are worth running through `validateUrl`. Anchored at\n * start, case-insensitive, scheme charset per RFC 3986.\n */\nconst URL_SHAPED = /^[a-z][a-z0-9+.-]*:\\/\\//i;\n\n/** Result of scanning a request body for SSRF-shaped URL values. */\nexport interface SsrfScanResult {\n  /** True if any URL-shaped string value failed validation. */\n  detected: boolean;\n  /** The offending URL string, or null. */\n  url: string | null;\n  /** Why it was blocked (from validateUrl), or null. */\n  reason: string | null;\n}\n\n/**\n * Body-scan profile (v1.7 W5). When a request body is walked for URLs,\n * the threat is private/internal targets and file-read schemes, not the\n * `localhost` hostname or non-http fetch schemes that show up constantly\n * in legitimate config payloads. So the body scan:\n *\n * - Allows `http` / `https` / `ftp` / `ftps`. Blocks `file:` / `gopher:` /\n *   `dict:` and other SSRF-amplifying schemes.\n * - Allows the literal `localhost` hostname (and `*.localhost`), which is\n *   ubiquitous in dev/staging webhook + health-check fields, while STILL\n *   blocking loopback expressed as an IP (`127.x`, decimal, hex) since\n *   that is the encoding an attacker reaches for.\n *\n * Apps that fetch a user-supplied URL directly should call the strict\n * `validateUrl` / `Guards.checkUrl` on that one field instead — the body\n * scan is the broad default, not a replacement for a focused guard.\n */\nconst SSRF_BODY_SCAN_PROTOCOLS = ['http:', 'https:', 'ftp:', 'ftps:'];\n\n// Schemes the body scan evaluates: the fetchable ones (http/https/ftp/ftps)\n// plus the classic SSRF-amplifying ones (file, gopher, dict, ...). A URL-shaped\n// string with any other scheme is a typo or a custom app scheme (lhttps://,\n// myapp://) that no server-side fetch would act on, so it is skipped rather\n// than flagged as SSRF. Keeps the dangerous schemes here so they stay blocked.\nconst SSRF_RELEVANT_SCHEMES = new Set([\n  'http', 'https', 'ftp', 'ftps', 'file', 'gopher', 'dict',\n  'ldap', 'ldaps', 'tftp', 'sftp', 'ssh', 'smb', 'jar', 'netdoc',\n]);\n\nfunction isLocalhostHostname(url: string): boolean {\n  try {\n    const host = new URL(url).hostname.toLowerCase();\n    return host === 'localhost' || host.endsWith('.localhost');\n  } catch {\n    return false;\n  }\n}\n\n/**\n * Recursively walk a parsed request body and run `validateUrl` on every\n * string value that looks like a URL (`scheme://...`). Returns the first\n * unsafe URL found. v1.7 W5 wire-up.\n *\n * Public URLs pass, so a body carrying `{ website: \"https://example.com\" }`\n * is not blocked; only private / loopback-IP / link-local / metadata /\n * file-read-scheme URLs trip it. See `SSRF_BODY_SCAN_PROTOCOLS` for the\n * scheme + localhost policy.\n *\n * @param body - Parsed request body (object, array, or scalar).\n * @param options - Forwarded to `validateUrl`. When `allowedProtocols`\n *   is not set, the body-scan profile (http/https/ftp/ftps) is used.\n * @param maxDepth - Max recursion depth. Default 8.\n */\nexport function scanForSsrf(\n  body: unknown,\n  options: ValidateUrlOptions = {},\n  maxDepth = 8,\n): SsrfScanResult {\n  const scanOptions: ValidateUrlOptions = {\n    ...options,\n    allowedProtocols: options.allowedProtocols ?? SSRF_BODY_SCAN_PROTOCOLS,\n  };\n  function walk(value: unknown, depth: number): SsrfScanResult | null {\n    if (depth > maxDepth) return null;\n    if (typeof value === 'string') {\n      const trimmed = value.trim();\n      if (URL_SHAPED.test(trimmed)) {\n        // Skip schemes a server-side fetch would never act on (typos like\n        // lhttps://, custom app schemes). They are not an SSRF vector.\n        const scheme = trimmed.slice(0, trimmed.indexOf(':')).toLowerCase();\n        if (!SSRF_RELEVANT_SCHEMES.has(scheme)) {\n          return null;\n        }\n        // The localhost hostname is allowed (dev config); loopback IPs\n        // are not (attacker evasion). Both would otherwise be flagged.\n        if (isLocalhostHostname(trimmed)) {\n          return null;\n        }\n        const result = validateUrl(trimmed, scanOptions);\n        if (!result.safe) {\n          return { detected: true, url: value, reason: result.reason ?? 'unsafe URL' };\n        }\n      }\n      return null;\n    }\n    if (value === null || typeof value !== 'object') return null;\n    if (Array.isArray(value)) {\n      for (const item of value) {\n        const hit = walk(item, depth + 1);\n        if (hit) return hit;\n      }\n      return null;\n    }\n    for (const key of Object.keys(value as Record<string, unknown>)) {\n      const hit = walk((value as Record<string, unknown>)[key], depth + 1);\n      if (hit) return hit;\n    }\n    return null;\n  }\n\n  return walk(body, 0) ?? { detected: false, url: null, reason: null };\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"]}