@arcis/node 1.4.3 → 1.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -3
- package/dist/cli/arcis.d.ts +23 -0
- package/dist/cli/arcis.d.ts.map +1 -0
- package/dist/cli/arcis.js +312 -0
- package/dist/cli/arcis.js.map +1 -0
- package/dist/cli/arcis.mjs +309 -0
- package/dist/cli/arcis.mjs.map +1 -0
- package/dist/core/constants.d.ts +1 -1
- package/dist/core/constants.d.ts.map +1 -1
- package/dist/core/index.js +4 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +4 -1
- package/dist/core/index.mjs.map +1 -1
- package/dist/core/types.d.ts +11 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.js +253 -141
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +253 -141
- package/dist/index.mjs.map +1 -1
- package/dist/logging/index.js.map +1 -1
- package/dist/logging/index.mjs.map +1 -1
- package/dist/middleware/bot-detection.d.ts.map +1 -1
- package/dist/middleware/csrf.d.ts.map +1 -1
- package/dist/middleware/index.js +224 -3
- package/dist/middleware/index.js.map +1 -1
- package/dist/middleware/index.mjs +224 -3
- package/dist/middleware/index.mjs.map +1 -1
- package/dist/middleware/main.d.ts.map +1 -1
- package/dist/sanitizers/index.d.ts +2 -1
- package/dist/sanitizers/index.d.ts.map +1 -1
- package/dist/sanitizers/index.js +213 -145
- package/dist/sanitizers/index.js.map +1 -1
- package/dist/sanitizers/index.mjs +213 -146
- package/dist/sanitizers/index.mjs.map +1 -1
- package/dist/sanitizers/sanitize.d.ts +13 -0
- package/dist/sanitizers/sanitize.d.ts.map +1 -1
- package/dist/stores/index.js.map +1 -1
- package/dist/stores/index.mjs.map +1 -1
- package/dist/telemetry/client.d.ts +3 -0
- package/dist/telemetry/client.d.ts.map +1 -1
- package/dist/telemetry/types.d.ts +12 -0
- package/dist/telemetry/types.d.ts.map +1 -1
- package/dist/validation/index.js.map +1 -1
- package/dist/validation/index.mjs.map +1 -1
- package/package.json +4 -1
package/dist/core/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/constants.ts","../../src/core/errors.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * @module @arcis/node/core/constants\n * Named constants for Arcis - no magic numbers\n */\n\n// =============================================================================\n// INPUT LIMITS\n// =============================================================================\nexport const INPUT = {\n /** Default maximum input size (1MB) */\n DEFAULT_MAX_SIZE: 1_000_000,\n /** Maximum recursion depth for nested objects */\n MAX_RECURSION_DEPTH: 10,\n} as const;\n\n// =============================================================================\n// RATE LIMITING\n// =============================================================================\nexport const RATE_LIMIT = {\n /** Default window size (1 minute) */\n DEFAULT_WINDOW_MS: 60_000,\n /** Default max requests per window */\n DEFAULT_MAX_REQUESTS: 100,\n /** Default HTTP status code for rate limited responses */\n DEFAULT_STATUS_CODE: 429,\n /** Default error message */\n DEFAULT_MESSAGE: 'Too many requests, please try again later.',\n /** Minimum window size (1 second) */\n MIN_WINDOW_MS: 1_000,\n /** Maximum window size (24 hours) */\n MAX_WINDOW_MS: 86_400_000,\n} as const;\n\n// =============================================================================\n// SECURITY HEADERS\n// =============================================================================\nexport const HEADERS = {\n /** Default Content Security Policy */\n DEFAULT_CSP: [\n \"default-src 'self'\",\n \"script-src 'self'\",\n \"style-src 'self' 'unsafe-inline'\",\n \"img-src 'self' data: https:\",\n \"font-src 'self'\",\n \"object-src 'none'\",\n \"frame-ancestors 'none'\",\n ].join('; '),\n /** Default HSTS max age (1 year in seconds) */\n HSTS_MAX_AGE: 31_536_000,\n /** Default X-Frame-Options value */\n FRAME_OPTIONS: 'DENY' as const,\n /** Default X-Content-Type-Options value */\n CONTENT_TYPE_OPTIONS: 'nosniff',\n /** Default Referrer-Policy value */\n REFERRER_POLICY: 'strict-origin-when-cross-origin',\n /** Default Permissions-Policy value */\n PERMISSIONS_POLICY: 'geolocation=(), microphone=(), camera=()',\n /** Default Cache-Control value for security */\n CACHE_CONTROL: 'no-store, no-cache, must-revalidate, proxy-revalidate',\n} as const;\n\n// =============================================================================\n// XSS PATTERNS (ReDoS-safe)\n// =============================================================================\n\n/**\n * Detection patterns — used to flag whether a string contains XSS payloads.\n * Must stay in sync with XSS_REMOVE_PATTERNS below.\n */\nexport const XSS_PATTERNS = [\n /** Script tags (ReDoS-safe version) */\n /<script[^>]*>[\\s\\S]*?<\\/script>/gi,\n /** javascript: protocol (allow optional spaces before colon) */\n /javascript\\s*:/gi,\n /** vbscript: protocol */\n /vbscript\\s*:/gi,\n /** Event handlers (onclick, onerror, etc.) — any separator before attribute */\n /(?:[\\s/])on\\w+\\s*=/gi,\n /** iframe tags */\n /<iframe/gi,\n /** object tags */\n /<object/gi,\n /** embed tags */\n /<embed/gi,\n /** data: URIs (only dangerous ones, avoid false positives) */\n /(?:^|[\\s\"'=])data:/gi,\n /** URL-encoded script tags */\n /%3Cscript/gi,\n /** SVG with onload */\n /<svg[^>]*onload/gi,\n /** form tags — phishing/credential harvesting via action= redirection */\n /<form[\\s>]/gi,\n /** meta tags — http-equiv refresh redirects or CSP bypass */\n /<meta[\\s>]/gi,\n /** base href hijacking — redirects all relative URLs to attacker domain */\n /<base[\\s>]/gi,\n /** link tag injection — stylesheet or preload CSRF attacks */\n /<link[\\s>]/gi,\n] as const;\n\n/**\n * Removal patterns — used by sanitizeXss() to strip dangerous content.\n * More targeted than XSS_PATTERNS: each pattern captures the full dangerous\n * substring (tag, attribute + value, protocol) so it can be replaced safely.\n * Must stay in sync with XSS_PATTERNS above.\n */\nexport const XSS_REMOVE_PATTERNS = [\n /** Full script blocks (content + tags) */\n /<script[^>]*>[\\s\\S]*?<\\/script>/gi,\n /** Standalone/unclosed script tags */\n /<script[^>]*>/gi,\n /** style — CSS expression() and behavior: attacks (IE-era but still relevant) */\n /<style[^>]*>[\\s\\S]*?<\\/style>/gi,\n /<style[^>]*/gi,\n /** iframe — full block and partial/unclosed */\n /<iframe[^>]*>[\\s\\S]*?<\\/iframe>/gi,\n /<iframe[^>]*/gi,\n /** object — full block and partial/unclosed */\n /<object[^>]*>[\\s\\S]*?<\\/object>/gi,\n /<object[^>]*/gi,\n /** embed tags */\n /<embed[^>]*/gi,\n /** SVG with inline event handlers */\n /<svg[^>]*onload[^>]*>/gi,\n /** URL-encoded script tags */\n /%3Cscript/gi,\n /** Event handlers with quoted values: onclick=\"...\", onerror='...' */\n /(?:[\\s/])on\\w+\\s*=\\s*[\"'][^\"']*[\"']/gi,\n /** Event handlers with unquoted values: onload=value */\n /(?:[\\s/])on\\w+\\s*=\\s*[^\\s>]*/gi,\n /** javascript: and vbscript: protocols (allow optional spaces before colon) */\n /javascript\\s*:/gi,\n /vbscript\\s*:/gi,\n /** data: URIs with HTML/script content */\n /data\\s*:\\s*text\\/html[^>\\s]*/gi,\n /** form tag injection — phishing via action= redirection */\n /<form[\\s>][^>]*/gi,\n /** meta tag injection — http-equiv refresh or CSP bypass */\n /<meta[\\s>][^>]*/gi,\n /** base href hijacking */\n /<base[\\s>][^>]*/gi,\n /** link tag injection — stylesheet or preload attacks */\n /<link[\\s>][^>]*/gi,\n] as const;\n\n// =============================================================================\n// SQL INJECTION PATTERNS\n// =============================================================================\nexport const SQL_PATTERNS = [\n /** SQL keywords */\n /(\\b(SELECT|INSERT|UPDATE|DELETE|DROP|UNION|ALTER|CREATE|TRUNCATE|EXEC|EXECUTE)\\b)/gi,\n /** SQL comments: ANSI (--), C-style (slash-star ... star-slash), MySQL (#) */\n /(--|\\/\\*|\\*\\/|#)/g,\n /** SQL statement separators */\n /(;|\\|\\||&&)/g,\n /** Boolean injection: OR 1=1 */\n /\\bOR\\s+\\d+\\s*=\\s*\\d+/gi,\n /** Boolean injection: OR 'a'='a' or OR \"a\"=\"a\" (including mixed quotes) */\n /\\bOR\\s+(['\"])[^'\"]*\\1\\s*=\\s*(['\"])[^'\"]*\\2/gi,\n /\\bOR\\s+('[^']*'|\"[^\"]*\")\\s*=\\s*('[^']*'|\"[^\"]*\")/gi,\n /** Boolean injection: AND 1=1 */\n /\\bAND\\s+\\d+\\s*=\\s*\\d+/gi,\n /** Boolean injection: AND 'a'='a' or AND \"a\"=\"a\" (including mixed quotes) */\n /\\bAND\\s+(['\"])[^'\"]*\\1\\s*=\\s*(['\"])[^'\"]*\\2/gi,\n /\\bAND\\s+('[^']*'|\"[^\"]*\")\\s*=\\s*('[^']*'|\"[^\"]*\")/gi,\n /** Time-based blind: SLEEP() */\n /\\bSLEEP\\s*\\(\\s*\\d+\\s*\\)/gi,\n /** Time-based blind: BENCHMARK() */\n /\\bBENCHMARK\\s*\\(/gi,\n /** Time-based blind: PostgreSQL pg_sleep() */\n /\\bpg_sleep\\s*\\(/gi,\n /** Time-based blind: MSSQL WAITFOR DELAY */\n /\\bWAITFOR\\s+DELAY\\b/gi,\n] as const;\n\n// =============================================================================\n// PATH TRAVERSAL PATTERNS\n// =============================================================================\nexport const PATH_PATTERNS = [\n /** Unix path traversal */\n /\\.\\.\\//g,\n /** Windows path traversal */\n /\\.\\.\\\\/g,\n /** URL-encoded traversal (%2e%2e) */\n /%2e%2e/gi,\n /** Double URL-encoded traversal (%252e) */\n /%252e/gi,\n /** Mixed encoding: ..%2F */\n /\\.\\.%2F/gi,\n /** Mixed encoding: %2e./ and .%2e/ */\n /%2e\\.[\\\\/]/gi,\n /\\.%2e[\\\\/]/gi,\n /** Fully URL-encoded: %2e%2e%2f */\n /%2e%2e%2f/gi,\n /** Double URL-encoded forward slash: %252f */\n /%252f/gi,\n /** Dotdotslash bypass: ....// or ....\\\\ */\n /\\.{2,}[/\\\\]{2,}/g,\n /** Null byte injection in paths */\n /\\0/g,\n] as const;\n\n// =============================================================================\n// COMMAND INJECTION PATTERNS\n// =============================================================================\nexport const COMMAND_PATTERNS = [\n /**\n * Shell metacharacters that enable command chaining/substitution.\n * Bare ( and ) are excluded — they appear in common legitimate values\n * (function calls in code fields, math expressions, etc.).\n * Command substitution is caught by the $( combined pattern below.\n * NOTE: ';', '&', '|' may appear in legitimate URL query strings\n * and Markdown; consider disabling command checking (command: false)\n * for fields that intentionally allow those characters.\n */\n /[;&|`]/g,\n /** Command substitution: $( ... ) — matched as a pair to reduce false positives */\n /\\$\\(/g,\n /** URL-encoded control characters (%00-%0F): null, tab, vtab, formfeed, LF, CR */\n /%0[0-9a-f]/gi,\n] as const;\n\n// =============================================================================\n// DANGEROUS KEYS\n// =============================================================================\n\n/**\n * Prototype pollution keys to block.\n * Stored lowercase — always compare with key.toLowerCase().\n *\n * Includes:\n * - __proto__: direct prototype assignment\n * - constructor: access to constructor.prototype chain\n * - prototype: direct prototype property\n * - __defineGetter__/__defineSetter__: legacy property definition (can override getters/setters)\n * - __lookupGetter__/__lookupSetter__: legacy property introspection\n */\nexport const DANGEROUS_PROTO_KEYS = new Set([\n '__proto__',\n 'constructor',\n 'prototype',\n '__definegetter__',\n '__definesetter__',\n '__lookupgetter__',\n '__lookupsetter__',\n]);\n\n/** MongoDB operators to block */\nexport const NOSQL_DANGEROUS_KEYS = new Set([\n // Comparison\n '$gt', '$gte', '$lt', '$lte', '$ne', '$eq', '$in', '$nin',\n // Logical\n '$and', '$or', '$not', '$nor',\n // Element / evaluation\n '$exists', '$type', '$regex', '$where', '$expr', '$mod', '$text', '$jsonSchema',\n // Array\n '$elemMatch', '$all', '$size',\n // JavaScript execution (critical)\n '$function', '$accumulator',\n // Aggregation pipeline operators (injectable via $lookup etc.)\n '$lookup', '$match', '$project', '$group', '$sort', '$limit', '$skip',\n '$unwind', '$addFields', '$replaceRoot',\n]);\n\n// =============================================================================\n// REDACTION\n// =============================================================================\nexport const REDACTION = {\n /** Replacement text for redacted values */\n REPLACEMENT: '[REDACTED]',\n /** Truncation indicator */\n TRUNCATED: '[TRUNCATED]',\n /** Max depth indicator */\n MAX_DEPTH: '[MAX_DEPTH]',\n /** Default max message length */\n DEFAULT_MAX_LENGTH: 10_000,\n /** Default sensitive keys to redact */\n SENSITIVE_KEYS: new Set([\n 'password', 'passwd', 'pwd', 'secret', 'token', 'apikey',\n 'api_key', 'apiKey', 'auth', 'authorization', 'credit_card',\n 'creditcard', 'cc', 'ssn', 'social_security', 'private_key',\n 'privateKey', 'access_token', 'accessToken', 'refresh_token',\n 'refreshToken', 'bearer', 'jwt', 'session', 'cookie',\n 'credentials', 'x-api-key', 'x-auth-token',\n ]),\n} as const;\n\n// =============================================================================\n// VALIDATION PATTERNS\n// =============================================================================\nexport const VALIDATION = {\n /**\n * Email regex pattern.\n * Rejects consecutive dots in local part (e.g. test..foo@example.com),\n * leading/trailing dots, and other common invalid forms.\n */\n EMAIL: /^[^\\s@.][^\\s@]*(?:\\.[^\\s@.][^\\s@]*)*@[^\\s@]+\\.[^\\s@]+$/,\n /**\n * URL regex pattern.\n * Only allows http:// and https:// — explicitly rejects javascript:,\n * data:, vbscript:, and other dangerous URI schemes.\n */\n URL: /^https?:\\/\\/[^\\s/$.?#][^\\s]*$/,\n /** UUID regex pattern (v4) */\n UUID: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,\n} as const;\n\n// =============================================================================\n// ERROR MESSAGES\n// =============================================================================\nexport const ERRORS = {\n /** Generic error message (production) */\n INTERNAL_SERVER_ERROR: 'Internal Server Error',\n /** Input too large error */\n INPUT_TOO_LARGE: (maxSize: number) => `Input exceeds maximum size of ${maxSize} bytes`,\n /** Validation error messages */\n VALIDATION: {\n REQUIRED: (field: string) => `${field} is required`,\n INVALID_TYPE: (field: string, type: string) => `${field} must be a ${type}`,\n MIN_LENGTH: (field: string, min: number) => `${field} must be at least ${min} characters`,\n MAX_LENGTH: (field: string, max: number) => `${field} must be at most ${max} characters`,\n MIN_VALUE: (field: string, min: number) => `${field} must be at least ${min}`,\n MAX_VALUE: (field: string, max: number) => `${field} must be at most ${max}`,\n INVALID_FORMAT: (field: string) => `${field} format is invalid`,\n INVALID_EMAIL: (field: string) => `${field} must be a valid email`,\n INVALID_URL: (field: string) => `${field} must be a valid URL`,\n INVALID_UUID: (field: string) => `${field} must be a valid UUID`,\n INVALID_ENUM: (field: string, values: unknown[]) => `${field} must be one of: ${values.join(', ')}`,\n MIN_ITEMS: (field: string, min: number) => `${field} must have at least ${min} items`,\n MAX_ITEMS: (field: string, max: number) => `${field} must have at most ${max} items`,\n },\n} as const;\n\n// =============================================================================\n// BLOCKED TEXT (for sanitizer replacements)\n// =============================================================================\nexport const BLOCKED = '[BLOCKED]' as const;\n","/**\n * @module @arcis/node/core/errors\n * Custom error classes for Arcis\n */\n\n/**\n * Base class for all Arcis errors\n */\nexport class ArcisError extends Error {\n public readonly statusCode: number;\n public readonly code: string;\n /** Whether the error message is safe to expose to API clients. */\n public readonly expose: boolean;\n\n constructor(message: string, statusCode = 500, code = 'ARCIS_ERROR') {\n super(message);\n this.name = 'ArcisError';\n this.statusCode = statusCode;\n this.code = code;\n // Client errors (4xx) have controlled messages — safe to expose.\n // Server errors (5xx) may contain internal details — hide by default.\n this.expose = statusCode < 500;\n\n // Maintains proper stack trace for where error was thrown (V8 engines)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n }\n}\n\n/**\n * Error thrown when input validation fails\n */\nexport class ValidationError extends ArcisError {\n public readonly errors: string[];\n\n constructor(errors: string[]) {\n super('Validation failed', 400, 'VALIDATION_ERROR');\n this.name = 'ValidationError';\n this.errors = errors;\n }\n}\n\n/** Alias for ValidationError (backwards compatibility) */\nexport { ValidationError as ArcisValidationError };\n\n/**\n * Error thrown when rate limit is exceeded\n */\nexport class RateLimitError extends ArcisError {\n public readonly retryAfter: number;\n\n constructor(message: string, retryAfter: number) {\n super(message, 429, 'RATE_LIMIT_EXCEEDED');\n this.name = 'RateLimitError';\n this.retryAfter = retryAfter;\n }\n}\n\n/**\n * Error thrown when input is too large\n */\nexport class InputTooLargeError extends ArcisError {\n public readonly maxSize: number;\n public readonly actualSize: number;\n\n constructor(maxSize: number, actualSize: number) {\n super(`Input exceeds maximum size of ${maxSize} bytes`, 413, 'INPUT_TOO_LARGE');\n this.name = 'InputTooLargeError';\n this.maxSize = maxSize;\n this.actualSize = actualSize;\n }\n}\n\n/**\n * Error thrown when security threat is detected\n */\nexport class SecurityThreatError extends ArcisError {\n public readonly threatType: string;\n public readonly pattern: string;\n\n constructor(threatType: string, pattern: string) {\n super('Request blocked for security reasons', 400, 'SECURITY_THREAT');\n this.name = 'SecurityThreatError';\n this.threatType = threatType;\n this.pattern = pattern;\n }\n}\n\n/**\n * Error thrown when sanitization fails\n */\nexport class SanitizationError extends ArcisError {\n constructor(message: string) {\n super(message, 400, 'SANITIZATION_ERROR');\n this.name = 'SanitizationError';\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/core/constants.ts","../../src/core/errors.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * @module @arcis/node/core/constants\n * Named constants for Arcis - no magic numbers\n */\n\n// =============================================================================\n// INPUT LIMITS\n// =============================================================================\nexport const INPUT = {\n /** Default maximum input size (1MB) */\n DEFAULT_MAX_SIZE: 1_000_000,\n /** Maximum recursion depth for nested objects */\n MAX_RECURSION_DEPTH: 10,\n} as const;\n\n// =============================================================================\n// RATE LIMITING\n// =============================================================================\nexport const RATE_LIMIT = {\n /** Default window size (1 minute) */\n DEFAULT_WINDOW_MS: 60_000,\n /** Default max requests per window */\n DEFAULT_MAX_REQUESTS: 100,\n /** Default HTTP status code for rate limited responses */\n DEFAULT_STATUS_CODE: 429,\n /** Default error message */\n DEFAULT_MESSAGE: 'Too many requests, please try again later.',\n /** Minimum window size (1 second) */\n MIN_WINDOW_MS: 1_000,\n /** Maximum window size (24 hours) */\n MAX_WINDOW_MS: 86_400_000,\n} as const;\n\n// =============================================================================\n// SECURITY HEADERS\n// =============================================================================\nexport const HEADERS = {\n /** Default Content Security Policy */\n DEFAULT_CSP: [\n \"default-src 'self'\",\n \"script-src 'self'\",\n \"style-src 'self' 'unsafe-inline'\",\n \"img-src 'self' data: https:\",\n \"font-src 'self'\",\n \"object-src 'none'\",\n \"frame-ancestors 'none'\",\n ].join('; '),\n /** Default HSTS max age (1 year in seconds) */\n HSTS_MAX_AGE: 31_536_000,\n /** Default X-Frame-Options value */\n FRAME_OPTIONS: 'DENY' as const,\n /** Default X-Content-Type-Options value */\n CONTENT_TYPE_OPTIONS: 'nosniff',\n /** Default Referrer-Policy value */\n REFERRER_POLICY: 'strict-origin-when-cross-origin',\n /** Default Permissions-Policy value */\n PERMISSIONS_POLICY: 'geolocation=(), microphone=(), camera=()',\n /** Default Cache-Control value for security */\n CACHE_CONTROL: 'no-store, no-cache, must-revalidate, proxy-revalidate',\n} as const;\n\n// =============================================================================\n// XSS PATTERNS (ReDoS-safe)\n// =============================================================================\n\n/**\n * Detection patterns — used to flag whether a string contains XSS payloads.\n * Must stay in sync with XSS_REMOVE_PATTERNS below.\n */\nexport const XSS_PATTERNS = [\n /** Script tags (ReDoS-safe version) */\n /<script[^>]*>[\\s\\S]*?<\\/script>/gi,\n /** javascript: protocol (allow optional spaces before colon) */\n /javascript\\s*:/gi,\n /** vbscript: protocol */\n /vbscript\\s*:/gi,\n /** Event handlers (onclick, onerror, etc.) — any separator before attribute */\n /(?:[\\s/])on\\w+\\s*=/gi,\n /** iframe tags */\n /<iframe/gi,\n /** object tags */\n /<object/gi,\n /** embed tags */\n /<embed/gi,\n /** data: URIs (only dangerous ones, avoid false positives) */\n /(?:^|[\\s\"'=])data:/gi,\n /** URL-encoded script tags */\n /%3Cscript/gi,\n /** SVG with onload */\n /<svg[^>]*onload/gi,\n /** form tags — phishing/credential harvesting via action= redirection */\n /<form[\\s>]/gi,\n /** meta tags — http-equiv refresh redirects or CSP bypass */\n /<meta[\\s>]/gi,\n /** base href hijacking — redirects all relative URLs to attacker domain */\n /<base[\\s>]/gi,\n /** link tag injection — stylesheet or preload CSRF attacks */\n /<link[\\s>]/gi,\n /** style tag — CSS expression() / behavior: / IE-era attacks. Mirrors\n * Python's xss-style-tag from packages/core/patterns.json. */\n /<style[\\s>]/gi,\n] as const;\n\n/**\n * Removal patterns — used by sanitizeXss() to strip dangerous content.\n * More targeted than XSS_PATTERNS: each pattern captures the full dangerous\n * substring (tag, attribute + value, protocol) so it can be replaced safely.\n * Must stay in sync with XSS_PATTERNS above.\n */\nexport const XSS_REMOVE_PATTERNS = [\n /** Full script blocks (content + tags) */\n /<script[^>]*>[\\s\\S]*?<\\/script>/gi,\n /** Standalone/unclosed script tags */\n /<script[^>]*>/gi,\n /** style — CSS expression() and behavior: attacks (IE-era but still relevant) */\n /<style[^>]*>[\\s\\S]*?<\\/style>/gi,\n /<style[^>]*/gi,\n /** iframe — full block and partial/unclosed */\n /<iframe[^>]*>[\\s\\S]*?<\\/iframe>/gi,\n /<iframe[^>]*/gi,\n /** object — full block and partial/unclosed */\n /<object[^>]*>[\\s\\S]*?<\\/object>/gi,\n /<object[^>]*/gi,\n /** embed tags */\n /<embed[^>]*/gi,\n /** SVG with inline event handlers */\n /<svg[^>]*onload[^>]*>/gi,\n /** URL-encoded script tags */\n /%3Cscript/gi,\n /** Event handlers with quoted values: onclick=\"...\", onerror='...' */\n /(?:[\\s/])on\\w+\\s*=\\s*[\"'][^\"']*[\"']/gi,\n /** Event handlers with unquoted values: onload=value */\n /(?:[\\s/])on\\w+\\s*=\\s*[^\\s>]*/gi,\n /** javascript: and vbscript: protocols (allow optional spaces before colon) */\n /javascript\\s*:/gi,\n /vbscript\\s*:/gi,\n /** data: URIs with HTML/script content */\n /data\\s*:\\s*text\\/html[^>\\s]*/gi,\n /** form tag injection — phishing via action= redirection */\n /<form[\\s>][^>]*/gi,\n /** meta tag injection — http-equiv refresh or CSP bypass */\n /<meta[\\s>][^>]*/gi,\n /** base href hijacking */\n /<base[\\s>][^>]*/gi,\n /** link tag injection — stylesheet or preload attacks */\n /<link[\\s>][^>]*/gi,\n] as const;\n\n// =============================================================================\n// SQL INJECTION PATTERNS\n// =============================================================================\nexport const SQL_PATTERNS = [\n /** SQL keywords */\n /(\\b(SELECT|INSERT|UPDATE|DELETE|DROP|UNION|ALTER|CREATE|TRUNCATE|EXEC|EXECUTE)\\b)/gi,\n /** SQL comments: ANSI (--), C-style (slash-star ... star-slash), MySQL (#) */\n /(--|\\/\\*|\\*\\/|#)/g,\n /** SQL statement separators */\n /(;|\\|\\||&&)/g,\n /** Boolean injection: OR 1=1 */\n /\\bOR\\s+\\d+\\s*=\\s*\\d+/gi,\n /** Boolean injection: OR 'a'='a' or OR \"a\"=\"a\" (including mixed quotes) */\n /\\bOR\\s+(['\"])[^'\"]*\\1\\s*=\\s*(['\"])[^'\"]*\\2/gi,\n /\\bOR\\s+('[^']*'|\"[^\"]*\")\\s*=\\s*('[^']*'|\"[^\"]*\")/gi,\n /** Boolean injection: AND 1=1 */\n /\\bAND\\s+\\d+\\s*=\\s*\\d+/gi,\n /** Boolean injection: AND 'a'='a' or AND \"a\"=\"a\" (including mixed quotes) */\n /\\bAND\\s+(['\"])[^'\"]*\\1\\s*=\\s*(['\"])[^'\"]*\\2/gi,\n /\\bAND\\s+('[^']*'|\"[^\"]*\")\\s*=\\s*('[^']*'|\"[^\"]*\")/gi,\n /** Time-based blind: SLEEP() */\n /\\bSLEEP\\s*\\(\\s*\\d+\\s*\\)/gi,\n /** Time-based blind: BENCHMARK() */\n /\\bBENCHMARK\\s*\\(/gi,\n /** Time-based blind: PostgreSQL pg_sleep() */\n /\\bpg_sleep\\s*\\(/gi,\n /** Time-based blind: MSSQL WAITFOR DELAY */\n /\\bWAITFOR\\s+DELAY\\b/gi,\n] as const;\n\n// =============================================================================\n// PATH TRAVERSAL PATTERNS\n// =============================================================================\nexport const PATH_PATTERNS = [\n /** Unix path traversal */\n /\\.\\.\\//g,\n /** Windows path traversal */\n /\\.\\.\\\\/g,\n /** URL-encoded traversal (%2e%2e) */\n /%2e%2e/gi,\n /** Double URL-encoded traversal (%252e) */\n /%252e/gi,\n /** Mixed encoding: ..%2F */\n /\\.\\.%2F/gi,\n /** Mixed encoding: %2e./ and .%2e/ */\n /%2e\\.[\\\\/]/gi,\n /\\.%2e[\\\\/]/gi,\n /** Fully URL-encoded: %2e%2e%2f */\n /%2e%2e%2f/gi,\n /** Double URL-encoded forward slash: %252f */\n /%252f/gi,\n /** Dotdotslash bypass: ....// or ....\\\\ */\n /\\.{2,}[/\\\\]{2,}/g,\n /** Null byte injection in paths */\n /\\0/g,\n] as const;\n\n// =============================================================================\n// COMMAND INJECTION PATTERNS\n// =============================================================================\nexport const COMMAND_PATTERNS = [\n /**\n * Shell metacharacters that enable command chaining/substitution.\n * Bare ( and ) are excluded — they appear in common legitimate values\n * (function calls in code fields, math expressions, etc.).\n * Command substitution is caught by the $( combined pattern below.\n * NOTE: ';', '&', '|' may appear in legitimate URL query strings\n * and Markdown; consider disabling command checking (command: false)\n * for fields that intentionally allow those characters.\n */\n /[;&|`]/g,\n /** Command substitution: $( ... ) — matched as a pair to reduce false positives */\n /\\$\\(/g,\n /** URL-encoded control characters (%00-%0F): null, tab, vtab, formfeed, LF, CR */\n /%0[0-9a-f]/gi,\n] as const;\n\n// =============================================================================\n// DANGEROUS KEYS\n// =============================================================================\n\n/**\n * Prototype pollution keys to block.\n * Stored lowercase — always compare with key.toLowerCase().\n *\n * Includes:\n * - __proto__: direct prototype assignment\n * - constructor: access to constructor.prototype chain\n * - prototype: direct prototype property\n * - __defineGetter__/__defineSetter__: legacy property definition (can override getters/setters)\n * - __lookupGetter__/__lookupSetter__: legacy property introspection\n */\nexport const DANGEROUS_PROTO_KEYS = new Set([\n '__proto__',\n 'constructor',\n 'prototype',\n '__definegetter__',\n '__definesetter__',\n '__lookupgetter__',\n '__lookupsetter__',\n]);\n\n/** MongoDB operators to block */\nexport const NOSQL_DANGEROUS_KEYS = new Set([\n // Comparison\n '$gt', '$gte', '$lt', '$lte', '$ne', '$eq', '$in', '$nin',\n // Logical\n '$and', '$or', '$not', '$nor',\n // Element / evaluation\n '$exists', '$type', '$regex', '$where', '$expr', '$mod', '$text', '$jsonSchema',\n // Array\n '$elemMatch', '$all', '$size',\n // JavaScript execution (critical)\n '$function', '$accumulator',\n // Aggregation pipeline operators (injectable via $lookup etc.)\n '$lookup', '$match', '$project', '$group', '$sort', '$limit', '$skip',\n '$unwind', '$addFields', '$replaceRoot',\n]);\n\n// =============================================================================\n// REDACTION\n// =============================================================================\nexport const REDACTION = {\n /** Replacement text for redacted values */\n REPLACEMENT: '[REDACTED]',\n /** Truncation indicator */\n TRUNCATED: '[TRUNCATED]',\n /** Max depth indicator */\n MAX_DEPTH: '[MAX_DEPTH]',\n /** Default max message length */\n DEFAULT_MAX_LENGTH: 10_000,\n /** Default sensitive keys to redact */\n SENSITIVE_KEYS: new Set([\n 'password', 'passwd', 'pwd', 'secret', 'token', 'apikey',\n 'api_key', 'apiKey', 'auth', 'authorization', 'credit_card',\n 'creditcard', 'cc', 'ssn', 'social_security', 'private_key',\n 'privateKey', 'access_token', 'accessToken', 'refresh_token',\n 'refreshToken', 'bearer', 'jwt', 'session', 'cookie',\n 'credentials', 'x-api-key', 'x-auth-token',\n ]),\n} as const;\n\n// =============================================================================\n// VALIDATION PATTERNS\n// =============================================================================\nexport const VALIDATION = {\n /**\n * Email regex pattern.\n * Rejects consecutive dots in local part (e.g. test..foo@example.com),\n * leading/trailing dots, and other common invalid forms.\n */\n EMAIL: /^[^\\s@.][^\\s@]*(?:\\.[^\\s@.][^\\s@]*)*@[^\\s@]+\\.[^\\s@]+$/,\n /**\n * URL regex pattern.\n * Only allows http:// and https:// — explicitly rejects javascript:,\n * data:, vbscript:, and other dangerous URI schemes.\n */\n URL: /^https?:\\/\\/[^\\s/$.?#][^\\s]*$/,\n /** UUID regex pattern (v4) */\n UUID: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,\n} as const;\n\n// =============================================================================\n// ERROR MESSAGES\n// =============================================================================\nexport const ERRORS = {\n /** Generic error message (production) */\n INTERNAL_SERVER_ERROR: 'Internal Server Error',\n /** Input too large error */\n INPUT_TOO_LARGE: (maxSize: number) => `Input exceeds maximum size of ${maxSize} bytes`,\n /** Validation error messages */\n VALIDATION: {\n REQUIRED: (field: string) => `${field} is required`,\n INVALID_TYPE: (field: string, type: string) => `${field} must be a ${type}`,\n MIN_LENGTH: (field: string, min: number) => `${field} must be at least ${min} characters`,\n MAX_LENGTH: (field: string, max: number) => `${field} must be at most ${max} characters`,\n MIN_VALUE: (field: string, min: number) => `${field} must be at least ${min}`,\n MAX_VALUE: (field: string, max: number) => `${field} must be at most ${max}`,\n INVALID_FORMAT: (field: string) => `${field} format is invalid`,\n INVALID_EMAIL: (field: string) => `${field} must be a valid email`,\n INVALID_URL: (field: string) => `${field} must be a valid URL`,\n INVALID_UUID: (field: string) => `${field} must be a valid UUID`,\n INVALID_ENUM: (field: string, values: unknown[]) => `${field} must be one of: ${values.join(', ')}`,\n MIN_ITEMS: (field: string, min: number) => `${field} must have at least ${min} items`,\n MAX_ITEMS: (field: string, max: number) => `${field} must have at most ${max} items`,\n },\n} as const;\n\n// =============================================================================\n// BLOCKED TEXT (for sanitizer replacements)\n// =============================================================================\nexport const BLOCKED = '[BLOCKED]' as const;\n","/**\n * @module @arcis/node/core/errors\n * Custom error classes for Arcis\n */\n\n/**\n * Base class for all Arcis errors\n */\nexport class ArcisError extends Error {\n public readonly statusCode: number;\n public readonly code: string;\n /** Whether the error message is safe to expose to API clients. */\n public readonly expose: boolean;\n\n constructor(message: string, statusCode = 500, code = 'ARCIS_ERROR') {\n super(message);\n this.name = 'ArcisError';\n this.statusCode = statusCode;\n this.code = code;\n // Client errors (4xx) have controlled messages — safe to expose.\n // Server errors (5xx) may contain internal details — hide by default.\n this.expose = statusCode < 500;\n\n // Maintains proper stack trace for where error was thrown (V8 engines)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n }\n}\n\n/**\n * Error thrown when input validation fails\n */\nexport class ValidationError extends ArcisError {\n public readonly errors: string[];\n\n constructor(errors: string[]) {\n super('Validation failed', 400, 'VALIDATION_ERROR');\n this.name = 'ValidationError';\n this.errors = errors;\n }\n}\n\n/** Alias for ValidationError (backwards compatibility) */\nexport { ValidationError as ArcisValidationError };\n\n/**\n * Error thrown when rate limit is exceeded\n */\nexport class RateLimitError extends ArcisError {\n public readonly retryAfter: number;\n\n constructor(message: string, retryAfter: number) {\n super(message, 429, 'RATE_LIMIT_EXCEEDED');\n this.name = 'RateLimitError';\n this.retryAfter = retryAfter;\n }\n}\n\n/**\n * Error thrown when input is too large\n */\nexport class InputTooLargeError extends ArcisError {\n public readonly maxSize: number;\n public readonly actualSize: number;\n\n constructor(maxSize: number, actualSize: number) {\n super(`Input exceeds maximum size of ${maxSize} bytes`, 413, 'INPUT_TOO_LARGE');\n this.name = 'InputTooLargeError';\n this.maxSize = maxSize;\n this.actualSize = actualSize;\n }\n}\n\n/**\n * Error thrown when security threat is detected\n */\nexport class SecurityThreatError extends ArcisError {\n public readonly threatType: string;\n public readonly pattern: string;\n\n constructor(threatType: string, pattern: string) {\n super('Request blocked for security reasons', 400, 'SECURITY_THREAT');\n this.name = 'SecurityThreatError';\n this.threatType = threatType;\n this.pattern = pattern;\n }\n}\n\n/**\n * Error thrown when sanitization fails\n */\nexport class SanitizationError extends ArcisError {\n constructor(message: string) {\n super(message, 400, 'SANITIZATION_ERROR');\n this.name = 'SanitizationError';\n }\n}\n"]}
|
package/dist/core/types.d.ts
CHANGED
|
@@ -14,6 +14,11 @@ export interface ArcisOptions {
|
|
|
14
14
|
headers?: boolean | HeaderOptions;
|
|
15
15
|
/** Enable/configure safe logging. Default: true */
|
|
16
16
|
logging?: boolean | LogOptions;
|
|
17
|
+
/**
|
|
18
|
+
* When true, the sanitizer middleware blocks attack payloads (returns 403)
|
|
19
|
+
* instead of silently sanitizing. Forwards to SanitizeOptions.block. Opt-in.
|
|
20
|
+
*/
|
|
21
|
+
block?: boolean;
|
|
17
22
|
/**
|
|
18
23
|
* Stream decision events to a dashboard endpoint. Opt-in: zero overhead when omitted.
|
|
19
24
|
* See spec/API_SPEC.md §9.
|
|
@@ -50,6 +55,12 @@ export interface SanitizeOptions {
|
|
|
50
55
|
htmlEncode?: boolean;
|
|
51
56
|
/** Freeze sanitized objects with Object.freeze() to prevent mutation. Default: false */
|
|
52
57
|
freeze?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* When true, scan req.body, req.query, req.params for attack patterns and
|
|
60
|
+
* respond 403 with a SECURITY_THREAT payload instead of silently sanitizing.
|
|
61
|
+
* Writes the deny decision to the telemetry marker. Default: false (opt-in).
|
|
62
|
+
*/
|
|
63
|
+
block?: boolean;
|
|
53
64
|
}
|
|
54
65
|
/** Result of sanitizing a string */
|
|
55
66
|
export interface SanitizeResult {
|
package/dist/core/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC/E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAM3D,uCAAuC;AACvC,MAAM,WAAW,YAAY;IAC3B,yDAAyD;IACzD,QAAQ,CAAC,EAAE,OAAO,GAAG,eAAe,CAAC;IACrC,oDAAoD;IACpD,SAAS,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAC;IACvC,uDAAuD;IACvD,OAAO,CAAC,EAAE,OAAO,GAAG,aAAa,CAAC;IAClC,mDAAmD;IACnD,OAAO,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;IAC/B;;;OAGG;IACH,SAAS,CAAC,EAAE,gBAAgB,CAAC;CAC9B;AAMD,iCAAiC;AACjC,MAAM,WAAW,eAAe;IAC9B,2CAA2C;IAC3C,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,qDAAqD;IACrD,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,uDAAuD;IACvD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,sDAAsD;IACtD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,yDAAyD;IACzD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,yDAAyD;IACzD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0DAA0D;IAC1D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,IAAI,CAAC,EAAE,UAAU,GAAG,QAAQ,CAAC;IAC7B;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,wFAAwF;IACxF,MAAM,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -78,7 +78,10 @@ var XSS_PATTERNS = [
|
|
|
78
78
|
/** base href hijacking — redirects all relative URLs to attacker domain */
|
|
79
79
|
/<base[\s>]/gi,
|
|
80
80
|
/** link tag injection — stylesheet or preload CSRF attacks */
|
|
81
|
-
/<link[\s>]/gi
|
|
81
|
+
/<link[\s>]/gi,
|
|
82
|
+
/** style tag — CSS expression() / behavior: / IE-era attacks. Mirrors
|
|
83
|
+
* Python's xss-style-tag from packages/core/patterns.json. */
|
|
84
|
+
/<style[\s>]/gi
|
|
82
85
|
];
|
|
83
86
|
var XSS_REMOVE_PATTERNS = [
|
|
84
87
|
/** Full script blocks (content + tags) */
|
|
@@ -893,144 +896,6 @@ function detectCommandInjection(input) {
|
|
|
893
896
|
return false;
|
|
894
897
|
}
|
|
895
898
|
|
|
896
|
-
// src/sanitizers/sanitize.ts
|
|
897
|
-
function sanitizeString(value, options = {}) {
|
|
898
|
-
if (typeof value !== "string") return value;
|
|
899
|
-
const maxSize = options.maxSize ?? INPUT.DEFAULT_MAX_SIZE;
|
|
900
|
-
if (value.length > maxSize) {
|
|
901
|
-
throw new InputTooLargeError(maxSize, value.length);
|
|
902
|
-
}
|
|
903
|
-
const reject = options.mode === "reject";
|
|
904
|
-
let result = value;
|
|
905
|
-
if (options.sql !== false) {
|
|
906
|
-
if (reject) {
|
|
907
|
-
if (detectSql(result)) {
|
|
908
|
-
throw new SecurityThreatError("sql_injection", "SQL pattern detected in input");
|
|
909
|
-
}
|
|
910
|
-
} else {
|
|
911
|
-
result = sanitizeSql(result);
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
if (options.path !== false) {
|
|
915
|
-
result = sanitizePath(result);
|
|
916
|
-
}
|
|
917
|
-
if (options.command !== false) {
|
|
918
|
-
if (reject) {
|
|
919
|
-
if (detectCommandInjection(result)) {
|
|
920
|
-
throw new SecurityThreatError("command_injection", "Shell metacharacter detected in input");
|
|
921
|
-
}
|
|
922
|
-
} else {
|
|
923
|
-
result = sanitizeCommand(result);
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
if (options.xss !== false) {
|
|
927
|
-
result = sanitizeXss(result, false, options.htmlEncode ?? false);
|
|
928
|
-
}
|
|
929
|
-
return result;
|
|
930
|
-
}
|
|
931
|
-
function sanitizeObject(obj, options = {}) {
|
|
932
|
-
if (obj === null || obj === void 0) return obj;
|
|
933
|
-
if (typeof obj === "string") return sanitizeString(obj, options);
|
|
934
|
-
if (typeof obj !== "object") return obj;
|
|
935
|
-
if (Array.isArray(obj)) return obj.map((item) => sanitizeObject(item, options));
|
|
936
|
-
const result = sanitizeObjectDepth(obj, options, 0);
|
|
937
|
-
return options.freeze ? Object.freeze(result) : result;
|
|
938
|
-
}
|
|
939
|
-
function sanitizeObjectDepth(obj, options, depth) {
|
|
940
|
-
if (depth >= INPUT.MAX_RECURSION_DEPTH) return obj;
|
|
941
|
-
const result = {};
|
|
942
|
-
for (const key of Object.keys(obj)) {
|
|
943
|
-
if (options.proto !== false && DANGEROUS_PROTO_KEYS.has(key.toLowerCase())) {
|
|
944
|
-
continue;
|
|
945
|
-
}
|
|
946
|
-
if (options.nosql !== false && NOSQL_DANGEROUS_KEYS.has(key)) {
|
|
947
|
-
continue;
|
|
948
|
-
}
|
|
949
|
-
const sanitizedKey = sanitizeString(key, options);
|
|
950
|
-
const value = obj[key];
|
|
951
|
-
if (value === null || value === void 0) {
|
|
952
|
-
result[sanitizedKey] = value;
|
|
953
|
-
} else if (typeof value === "string") {
|
|
954
|
-
result[sanitizedKey] = sanitizeString(value, options);
|
|
955
|
-
} else if (Array.isArray(value)) {
|
|
956
|
-
result[sanitizedKey] = value.map((item) => sanitizeObject(item, options));
|
|
957
|
-
} else if (typeof value === "object") {
|
|
958
|
-
result[sanitizedKey] = sanitizeObjectDepth(value, options, depth + 1);
|
|
959
|
-
} else {
|
|
960
|
-
result[sanitizedKey] = value;
|
|
961
|
-
}
|
|
962
|
-
}
|
|
963
|
-
return result;
|
|
964
|
-
}
|
|
965
|
-
function createSanitizer(options = {}) {
|
|
966
|
-
return (req, _res, next) => {
|
|
967
|
-
try {
|
|
968
|
-
if (req.body && typeof req.body === "object") {
|
|
969
|
-
req.body = sanitizeObject(req.body, options);
|
|
970
|
-
}
|
|
971
|
-
if (req.query && typeof req.query === "object") {
|
|
972
|
-
const sanitizedQuery = sanitizeObject(req.query, options);
|
|
973
|
-
Object.defineProperty(req, "query", { value: sanitizedQuery, writable: true, configurable: true });
|
|
974
|
-
}
|
|
975
|
-
if (req.params && typeof req.params === "object") {
|
|
976
|
-
const sanitizedParams = sanitizeObject(req.params, options);
|
|
977
|
-
Object.defineProperty(req, "params", { value: sanitizedParams, writable: true, configurable: true });
|
|
978
|
-
}
|
|
979
|
-
next();
|
|
980
|
-
} catch (err) {
|
|
981
|
-
next(err);
|
|
982
|
-
}
|
|
983
|
-
};
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
// src/sanitizers/nosql.ts
|
|
987
|
-
function isDangerousNoSqlKey(key) {
|
|
988
|
-
return NOSQL_DANGEROUS_KEYS.has(key);
|
|
989
|
-
}
|
|
990
|
-
function detectNoSqlInjection(obj, maxDepth = 10) {
|
|
991
|
-
if (maxDepth <= 0) return false;
|
|
992
|
-
if (obj === null || typeof obj !== "object") return false;
|
|
993
|
-
if (Array.isArray(obj)) {
|
|
994
|
-
return obj.some((item) => detectNoSqlInjection(item, maxDepth - 1));
|
|
995
|
-
}
|
|
996
|
-
for (const key of Object.keys(obj)) {
|
|
997
|
-
if (isDangerousNoSqlKey(key)) {
|
|
998
|
-
return true;
|
|
999
|
-
}
|
|
1000
|
-
const value = obj[key];
|
|
1001
|
-
if (typeof value === "object" && value !== null) {
|
|
1002
|
-
if (detectNoSqlInjection(value, maxDepth - 1)) {
|
|
1003
|
-
return true;
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
return false;
|
|
1008
|
-
}
|
|
1009
|
-
|
|
1010
|
-
// src/sanitizers/prototype.ts
|
|
1011
|
-
function isDangerousProtoKey(key) {
|
|
1012
|
-
return DANGEROUS_PROTO_KEYS.has(key.toLowerCase());
|
|
1013
|
-
}
|
|
1014
|
-
function detectPrototypePollution(obj, maxDepth = 10) {
|
|
1015
|
-
if (maxDepth <= 0) return false;
|
|
1016
|
-
if (obj === null || typeof obj !== "object") return false;
|
|
1017
|
-
if (Array.isArray(obj)) {
|
|
1018
|
-
return obj.some((item) => detectPrototypePollution(item, maxDepth - 1));
|
|
1019
|
-
}
|
|
1020
|
-
for (const key of Object.keys(obj)) {
|
|
1021
|
-
if (DANGEROUS_PROTO_KEYS.has(key.toLowerCase())) {
|
|
1022
|
-
return true;
|
|
1023
|
-
}
|
|
1024
|
-
const value = obj[key];
|
|
1025
|
-
if (typeof value === "object" && value !== null) {
|
|
1026
|
-
if (detectPrototypePollution(value, maxDepth - 1)) {
|
|
1027
|
-
return true;
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
|
-
return false;
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
899
|
// src/sanitizers/ssti.ts
|
|
1035
900
|
var SSTI_DETECT_PATTERNS = [
|
|
1036
901
|
/** Jinja2 / Twig / Nunjucks: {{ ... }} */
|
|
@@ -1193,6 +1058,208 @@ function detectXxe(input) {
|
|
|
1193
1058
|
return false;
|
|
1194
1059
|
}
|
|
1195
1060
|
|
|
1061
|
+
// src/sanitizers/sanitize.ts
|
|
1062
|
+
function sanitizeString(value, options = {}) {
|
|
1063
|
+
if (typeof value !== "string") return value;
|
|
1064
|
+
const maxSize = options.maxSize ?? INPUT.DEFAULT_MAX_SIZE;
|
|
1065
|
+
if (value.length > maxSize) {
|
|
1066
|
+
throw new InputTooLargeError(maxSize, value.length);
|
|
1067
|
+
}
|
|
1068
|
+
const reject = options.mode === "reject";
|
|
1069
|
+
let result = value;
|
|
1070
|
+
if (options.sql !== false) {
|
|
1071
|
+
if (reject) {
|
|
1072
|
+
if (detectSql(result)) {
|
|
1073
|
+
throw new SecurityThreatError("sql_injection", "SQL pattern detected in input");
|
|
1074
|
+
}
|
|
1075
|
+
} else {
|
|
1076
|
+
result = sanitizeSql(result);
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
if (options.path !== false) {
|
|
1080
|
+
result = sanitizePath(result);
|
|
1081
|
+
}
|
|
1082
|
+
if (options.command !== false) {
|
|
1083
|
+
if (reject) {
|
|
1084
|
+
if (detectCommandInjection(result)) {
|
|
1085
|
+
throw new SecurityThreatError("command_injection", "Shell metacharacter detected in input");
|
|
1086
|
+
}
|
|
1087
|
+
} else {
|
|
1088
|
+
result = sanitizeCommand(result);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
if (options.xss !== false) {
|
|
1092
|
+
result = sanitizeXss(result, false, options.htmlEncode ?? false);
|
|
1093
|
+
}
|
|
1094
|
+
return result;
|
|
1095
|
+
}
|
|
1096
|
+
function sanitizeObject(obj, options = {}) {
|
|
1097
|
+
if (obj === null || obj === void 0) return obj;
|
|
1098
|
+
if (typeof obj === "string") return sanitizeString(obj, options);
|
|
1099
|
+
if (typeof obj !== "object") return obj;
|
|
1100
|
+
if (Array.isArray(obj)) return obj.map((item) => sanitizeObject(item, options));
|
|
1101
|
+
const result = sanitizeObjectDepth(obj, options, 0);
|
|
1102
|
+
return options.freeze ? Object.freeze(result) : result;
|
|
1103
|
+
}
|
|
1104
|
+
function sanitizeObjectDepth(obj, options, depth) {
|
|
1105
|
+
if (depth >= INPUT.MAX_RECURSION_DEPTH) return obj;
|
|
1106
|
+
const result = {};
|
|
1107
|
+
for (const key of Object.keys(obj)) {
|
|
1108
|
+
if (options.proto !== false && DANGEROUS_PROTO_KEYS.has(key.toLowerCase())) {
|
|
1109
|
+
continue;
|
|
1110
|
+
}
|
|
1111
|
+
if (options.nosql !== false && NOSQL_DANGEROUS_KEYS.has(key)) {
|
|
1112
|
+
continue;
|
|
1113
|
+
}
|
|
1114
|
+
const sanitizedKey = sanitizeString(key, options);
|
|
1115
|
+
const value = obj[key];
|
|
1116
|
+
if (value === null || value === void 0) {
|
|
1117
|
+
result[sanitizedKey] = value;
|
|
1118
|
+
} else if (typeof value === "string") {
|
|
1119
|
+
result[sanitizedKey] = sanitizeString(value, options);
|
|
1120
|
+
} else if (Array.isArray(value)) {
|
|
1121
|
+
result[sanitizedKey] = value.map((item) => sanitizeObject(item, options));
|
|
1122
|
+
} else if (typeof value === "object") {
|
|
1123
|
+
result[sanitizedKey] = sanitizeObjectDepth(value, options, depth + 1);
|
|
1124
|
+
} else {
|
|
1125
|
+
result[sanitizedKey] = value;
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
return result;
|
|
1129
|
+
}
|
|
1130
|
+
function scanThreats(data, depth = 0) {
|
|
1131
|
+
if (depth > INPUT.MAX_RECURSION_DEPTH) return null;
|
|
1132
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
1133
|
+
for (const key of Object.keys(data)) {
|
|
1134
|
+
const lower = key.toLowerCase();
|
|
1135
|
+
if (DANGEROUS_PROTO_KEYS.has(lower)) {
|
|
1136
|
+
return { vector: "prototype", rule: "prototype/match", matchedPattern: key };
|
|
1137
|
+
}
|
|
1138
|
+
if (NOSQL_DANGEROUS_KEYS.has(key)) {
|
|
1139
|
+
return { vector: "nosql", rule: "nosql/match", matchedPattern: key };
|
|
1140
|
+
}
|
|
1141
|
+
const inner = scanThreats(data[key], depth + 1);
|
|
1142
|
+
if (inner) return inner;
|
|
1143
|
+
}
|
|
1144
|
+
return null;
|
|
1145
|
+
}
|
|
1146
|
+
if (Array.isArray(data)) {
|
|
1147
|
+
for (const item of data) {
|
|
1148
|
+
const inner = scanThreats(item, depth + 1);
|
|
1149
|
+
if (inner) return inner;
|
|
1150
|
+
}
|
|
1151
|
+
return null;
|
|
1152
|
+
}
|
|
1153
|
+
if (typeof data !== "string") return null;
|
|
1154
|
+
const sample = data.slice(0, 80);
|
|
1155
|
+
if (detectXss(data)) {
|
|
1156
|
+
return { vector: "xss", rule: "xss/match", matchedPattern: sample };
|
|
1157
|
+
}
|
|
1158
|
+
if (detectSsti(data)) {
|
|
1159
|
+
return { vector: "ssti", rule: "ssti/match", matchedPattern: sample };
|
|
1160
|
+
}
|
|
1161
|
+
if (detectXxe(data)) {
|
|
1162
|
+
return { vector: "xxe", rule: "xxe/match", matchedPattern: sample };
|
|
1163
|
+
}
|
|
1164
|
+
if (detectSql(data)) {
|
|
1165
|
+
return { vector: "sql", rule: "sql/match", matchedPattern: sample };
|
|
1166
|
+
}
|
|
1167
|
+
if (detectPathTraversal(data)) {
|
|
1168
|
+
return { vector: "path", rule: "path/match", matchedPattern: sample };
|
|
1169
|
+
}
|
|
1170
|
+
if (detectCommandInjection(data)) {
|
|
1171
|
+
return { vector: "command", rule: "command/match", matchedPattern: sample };
|
|
1172
|
+
}
|
|
1173
|
+
return null;
|
|
1174
|
+
}
|
|
1175
|
+
function createSanitizer(options = {}) {
|
|
1176
|
+
return (req, res, next) => {
|
|
1177
|
+
try {
|
|
1178
|
+
if (options.block) {
|
|
1179
|
+
const hit = scanThreats(req.body) || scanThreats(req.query) || scanThreats(req.params) || scanThreats(req.path);
|
|
1180
|
+
if (hit) {
|
|
1181
|
+
req.__arcis = {
|
|
1182
|
+
vector: hit.vector,
|
|
1183
|
+
rule: hit.rule,
|
|
1184
|
+
severity: "high",
|
|
1185
|
+
matchedPattern: hit.matchedPattern,
|
|
1186
|
+
reason: `${hit.vector} pattern detected in request`,
|
|
1187
|
+
decision: "deny"
|
|
1188
|
+
};
|
|
1189
|
+
res.status(403).json({
|
|
1190
|
+
error: "Request blocked for security reasons",
|
|
1191
|
+
code: "SECURITY_THREAT",
|
|
1192
|
+
vector: hit.vector
|
|
1193
|
+
});
|
|
1194
|
+
return;
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
if (req.body && typeof req.body === "object") {
|
|
1198
|
+
req.body = sanitizeObject(req.body, options);
|
|
1199
|
+
}
|
|
1200
|
+
if (req.query && typeof req.query === "object") {
|
|
1201
|
+
const sanitizedQuery = sanitizeObject(req.query, options);
|
|
1202
|
+
Object.defineProperty(req, "query", { value: sanitizedQuery, writable: true, configurable: true });
|
|
1203
|
+
}
|
|
1204
|
+
if (req.params && typeof req.params === "object") {
|
|
1205
|
+
const sanitizedParams = sanitizeObject(req.params, options);
|
|
1206
|
+
Object.defineProperty(req, "params", { value: sanitizedParams, writable: true, configurable: true });
|
|
1207
|
+
}
|
|
1208
|
+
next();
|
|
1209
|
+
} catch (err) {
|
|
1210
|
+
next(err);
|
|
1211
|
+
}
|
|
1212
|
+
};
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
// src/sanitizers/nosql.ts
|
|
1216
|
+
function isDangerousNoSqlKey(key) {
|
|
1217
|
+
return NOSQL_DANGEROUS_KEYS.has(key);
|
|
1218
|
+
}
|
|
1219
|
+
function detectNoSqlInjection(obj, maxDepth = 10) {
|
|
1220
|
+
if (maxDepth <= 0) return false;
|
|
1221
|
+
if (obj === null || typeof obj !== "object") return false;
|
|
1222
|
+
if (Array.isArray(obj)) {
|
|
1223
|
+
return obj.some((item) => detectNoSqlInjection(item, maxDepth - 1));
|
|
1224
|
+
}
|
|
1225
|
+
for (const key of Object.keys(obj)) {
|
|
1226
|
+
if (isDangerousNoSqlKey(key)) {
|
|
1227
|
+
return true;
|
|
1228
|
+
}
|
|
1229
|
+
const value = obj[key];
|
|
1230
|
+
if (typeof value === "object" && value !== null) {
|
|
1231
|
+
if (detectNoSqlInjection(value, maxDepth - 1)) {
|
|
1232
|
+
return true;
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
return false;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
// src/sanitizers/prototype.ts
|
|
1240
|
+
function isDangerousProtoKey(key) {
|
|
1241
|
+
return DANGEROUS_PROTO_KEYS.has(key.toLowerCase());
|
|
1242
|
+
}
|
|
1243
|
+
function detectPrototypePollution(obj, maxDepth = 10) {
|
|
1244
|
+
if (maxDepth <= 0) return false;
|
|
1245
|
+
if (obj === null || typeof obj !== "object") return false;
|
|
1246
|
+
if (Array.isArray(obj)) {
|
|
1247
|
+
return obj.some((item) => detectPrototypePollution(item, maxDepth - 1));
|
|
1248
|
+
}
|
|
1249
|
+
for (const key of Object.keys(obj)) {
|
|
1250
|
+
if (DANGEROUS_PROTO_KEYS.has(key.toLowerCase())) {
|
|
1251
|
+
return true;
|
|
1252
|
+
}
|
|
1253
|
+
const value = obj[key];
|
|
1254
|
+
if (typeof value === "object" && value !== null) {
|
|
1255
|
+
if (detectPrototypePollution(value, maxDepth - 1)) {
|
|
1256
|
+
return true;
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
return false;
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1196
1263
|
// src/sanitizers/jsonp.ts
|
|
1197
1264
|
var SAFE_CALLBACK_PATTERN = /^[a-zA-Z_$][a-zA-Z0-9_$.]*$/;
|
|
1198
1265
|
var DANGEROUS_CALLBACK_PATTERNS = [
|
|
@@ -2412,11 +2479,16 @@ var MAX_BATCH_SIZE = 500;
|
|
|
2412
2479
|
var DEFAULT_FLUSH_INTERVAL_MS = 5e3;
|
|
2413
2480
|
var MIN_FLUSH_INTERVAL_MS = 500;
|
|
2414
2481
|
var FLUSH_TIMEOUT_MS = 1e4;
|
|
2482
|
+
var DEFAULT_MAX_QUEUE_SIZE = 1e4;
|
|
2415
2483
|
var TelemetryClient = class {
|
|
2416
2484
|
constructor(options) {
|
|
2417
2485
|
this.queue = [];
|
|
2418
2486
|
this.flushing = false;
|
|
2419
2487
|
this.closed = false;
|
|
2488
|
+
// Counts events dropped since the last successful flush. Resets to 0
|
|
2489
|
+
// each flush so onQueueOverflow callbacks see "drops in this window"
|
|
2490
|
+
// rather than a monotonic lifetime counter.
|
|
2491
|
+
this.droppedSinceLastFlush = 0;
|
|
2420
2492
|
if (!options.endpoint || typeof options.endpoint !== "string") {
|
|
2421
2493
|
throw new TypeError("TelemetryClient: `endpoint` is required");
|
|
2422
2494
|
}
|
|
@@ -2432,8 +2504,14 @@ var TelemetryClient = class {
|
|
|
2432
2504
|
options.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS,
|
|
2433
2505
|
MIN_FLUSH_INTERVAL_MS
|
|
2434
2506
|
);
|
|
2507
|
+
this.maxQueueSize = Math.max(
|
|
2508
|
+
options.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE,
|
|
2509
|
+
this.batchSize
|
|
2510
|
+
);
|
|
2435
2511
|
this.onError = options.onError ?? (() => {
|
|
2436
2512
|
});
|
|
2513
|
+
this.onQueueOverflow = options.onQueueOverflow ?? (() => {
|
|
2514
|
+
});
|
|
2437
2515
|
this.startTimer();
|
|
2438
2516
|
}
|
|
2439
2517
|
/**
|
|
@@ -2443,6 +2521,15 @@ var TelemetryClient = class {
|
|
|
2443
2521
|
record(event) {
|
|
2444
2522
|
if (this.closed) return;
|
|
2445
2523
|
this.queue.push(event);
|
|
2524
|
+
if (this.queue.length > this.maxQueueSize) {
|
|
2525
|
+
const drop = this.queue.length - this.maxQueueSize;
|
|
2526
|
+
this.queue.splice(0, drop);
|
|
2527
|
+
this.droppedSinceLastFlush += drop;
|
|
2528
|
+
try {
|
|
2529
|
+
this.onQueueOverflow(this.droppedSinceLastFlush);
|
|
2530
|
+
} catch {
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2446
2533
|
if (this.queue.length >= this.batchSize) {
|
|
2447
2534
|
void this.flush();
|
|
2448
2535
|
}
|
|
@@ -2459,6 +2546,7 @@ var TelemetryClient = class {
|
|
|
2459
2546
|
try {
|
|
2460
2547
|
const batch = this.queue.splice(0, this.batchSize);
|
|
2461
2548
|
await this.send(batch);
|
|
2549
|
+
this.droppedSinceLastFlush = 0;
|
|
2462
2550
|
} catch (err) {
|
|
2463
2551
|
this.safeNotify(err);
|
|
2464
2552
|
} finally {
|
|
@@ -2603,7 +2691,10 @@ function arcis(options = {}) {
|
|
|
2603
2691
|
cleanupFns.push(() => rateLimiter.close());
|
|
2604
2692
|
}
|
|
2605
2693
|
if (options.sanitize !== false) {
|
|
2606
|
-
const sanitizeOpts = typeof options.sanitize === "object" ? options.sanitize : {};
|
|
2694
|
+
const sanitizeOpts = typeof options.sanitize === "object" ? { ...options.sanitize } : {};
|
|
2695
|
+
if (options.block && sanitizeOpts.block === void 0) {
|
|
2696
|
+
sanitizeOpts.block = true;
|
|
2697
|
+
}
|
|
2607
2698
|
const sanitizer = createSanitizer(sanitizeOpts);
|
|
2608
2699
|
middlewares.push(telemetryClient ? tapSanitizerThreats(sanitizer) : sanitizer);
|
|
2609
2700
|
}
|
|
@@ -3144,6 +3235,13 @@ function botProtection(options = {}) {
|
|
|
3144
3235
|
return next();
|
|
3145
3236
|
}
|
|
3146
3237
|
if (denySet.has(result.category)) {
|
|
3238
|
+
req.__arcis = {
|
|
3239
|
+
vector: "bot",
|
|
3240
|
+
rule: `bot/${result.category.toLowerCase()}`,
|
|
3241
|
+
severity: "medium",
|
|
3242
|
+
reason: result.name ? `Bot detected: ${result.name}` : "Bot detected",
|
|
3243
|
+
decision: "deny"
|
|
3244
|
+
};
|
|
3147
3245
|
if (onDetected) {
|
|
3148
3246
|
return onDetected(req, res, result);
|
|
3149
3247
|
}
|
|
@@ -3151,6 +3249,13 @@ function botProtection(options = {}) {
|
|
|
3151
3249
|
return;
|
|
3152
3250
|
}
|
|
3153
3251
|
if (defaultAction === "deny") {
|
|
3252
|
+
req.__arcis = {
|
|
3253
|
+
vector: "bot",
|
|
3254
|
+
rule: "bot/uncategorized",
|
|
3255
|
+
severity: "medium",
|
|
3256
|
+
reason: "Uncategorized bot under defaultAction=deny",
|
|
3257
|
+
decision: "deny"
|
|
3258
|
+
};
|
|
3154
3259
|
if (onDetected) {
|
|
3155
3260
|
return onDetected(req, res, result);
|
|
3156
3261
|
}
|
|
@@ -3204,7 +3309,14 @@ function csrfProtection(options = {}) {
|
|
|
3204
3309
|
sameSite: options.cookie?.sameSite ?? "Lax",
|
|
3205
3310
|
domain: options.cookie?.domain
|
|
3206
3311
|
};
|
|
3207
|
-
const defaultOnError = (
|
|
3312
|
+
const defaultOnError = (req, res, _next) => {
|
|
3313
|
+
req.__arcis = {
|
|
3314
|
+
vector: "csrf",
|
|
3315
|
+
rule: "csrf/token-mismatch",
|
|
3316
|
+
severity: "high",
|
|
3317
|
+
reason: "CSRF token missing or invalid",
|
|
3318
|
+
decision: "deny"
|
|
3319
|
+
};
|
|
3208
3320
|
res.status(403).json({
|
|
3209
3321
|
error: "CSRF token validation failed",
|
|
3210
3322
|
message: "Invalid or missing CSRF token. Include the token from the cookie in the X-CSRF-Token header."
|