UNPKG

@dollhousemcp/mcp-server

Version:

DollhouseMCP - A Model Context Protocol (MCP) server that enables dynamic AI persona management from markdown files, allowing Claude and other compatible AI assistants to activate and switch between different behavioral personas.

448 lines 63.1 kB
/** * Input validation and sanitization functions */ import * as path from 'path'; import { SECURITY_LIMITS, VALIDATION_PATTERNS } from './constants.js'; import { VALID_CATEGORIES } from '../config/constants.js'; import { RegexValidator } from './regexValidator.js'; // Pre-compiled regex patterns for better performance // These patterns are used repeatedly and benefit from pre-compilation const CONTROL_CHARS_REGEX = /[\x00-\x1F\x7F]/g; const HTML_DANGEROUS_REGEX = /[<>'"&]/g; const SHELL_METACHAR_REGEX = /[;&|`$()!\\~*?{}]/g; const RTL_ZEROWIDTH_REGEX = /[\u202E\uFEFF]/g; const COLLECTION_PATH_CHAR_REGEX = /[a-zA-Z0-9\/\-_.]/; const VALID_COLLECTION_PATH_REGEX = /^[a-zA-Z0-9\/\-_.]*$/; const IPV4_REGEX = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/; const DECIMAL_IP_REGEX = /^\d{8,10}$/; const HEX_IP_REGEX = /^0x[0-9a-f]{1,8}$/i; const OCTAL_IP_REGEX = /^0[0-7]{8,11}$/; const FILENAME_DANGEROUS_REGEX = /[\/\\:*?"<>|]/g; const FILENAME_LEADING_DOTS_REGEX = /^\.+/; const PATH_NORMALIZE_REGEX = /^\/{1,100}|\/{1,100}$/g; const PATH_MULTIPLE_SLASHES_REGEX = /\/{1,100}/g; const URL_PLUS_DECODE_REGEX = /\+/g; /** * Enhanced input validation for MCP tools */ export class MCPInputValidator { /** * Validate a persona identifier (name or filename) */ static validatePersonaIdentifier(identifier) { if (!identifier || typeof identifier !== 'string') { throw new Error('Persona identifier must be a non-empty string'); } if (identifier.length > 100) { throw new Error('Persona identifier too long (max 100 characters)'); } // Allow persona names and filenames const sanitized = sanitizeInput(identifier, 100); if (!sanitized) { throw new Error('Persona identifier contains only invalid characters'); } return sanitized; } /** * Validate search query for collection */ static validateSearchQuery(query) { if (!query || typeof query !== 'string') { throw new Error('Search query must be a non-empty string'); } if (query.length < 2) { throw new Error('Search query too short (minimum 2 characters)'); } if (query.length > 200) { throw new Error('Search query too long (max 200 characters)'); } // Sanitize but preserve spaces for search const sanitized = query .replace(CONTROL_CHARS_REGEX, '') // Remove control characters .replace(HTML_DANGEROUS_REGEX, '') // Remove HTML-dangerous characters .replace(SHELL_METACHAR_REGEX, '') // Remove shell metacharacters (expanded) .replace(RTL_ZEROWIDTH_REGEX, '') // Remove RTL override and zero-width chars .trim(); if (!sanitized) { throw new Error('Search query contains only invalid characters'); } return sanitized; } /** * Validate collection path */ static validateCollectionPath(path) { if (!path || typeof path !== 'string') { throw new Error('Collection path must be a non-empty string'); } if (path.length > 500) { throw new Error('Collection path too long (max 500 characters)'); } // GitHub API paths should be safe filename patterns // Use single regex test for better performance (avoids O(n) character-by-character check) if (!VALID_COLLECTION_PATH_REGEX.test(path)) { // Only do character-by-character check if validation fails, to provide detailed error message for (let i = 0; i < path.length; i++) { const char = path[i]; if (!COLLECTION_PATH_CHAR_REGEX.test(char)) { throw new Error(`Invalid character '${char}' in collection path at position ${i + 1}`); } } // Fallback error if we somehow don't find the invalid character throw new Error('Invalid characters in collection path'); } // Prevent path traversal in GitHub paths (comprehensive check) const pathLower = path.toLowerCase(); const encodedPath = decodeURIComponent(path.replace(URL_PLUS_DECODE_REGEX, ' ')); // Decode URL encoding // Check for various path traversal patterns const traversalPatterns = [ '..', // Basic traversal './', // Current directory '/../', // Directory traversal with slashes '\\', // Backslash (Windows-style) '%2e%2e', // URL-encoded .. '%2e%2e%2f', // URL-encoded ../ '%2e%2e%5c', // URL-encoded ..\ '%252e%252e', // Double URL-encoded .. '..%2f', // Mixed encoding '..%5c', // Mixed encoding with backslash '..../', // Dotdot bypass attempt '..;/', // Semicolon bypass attempt ]; for (const pattern of traversalPatterns) { if (pathLower.includes(pattern) || encodedPath.toLowerCase().includes(pattern)) { throw new Error('Path traversal not allowed in collection path'); } } return path; } /** * Validate URL for import operations */ static validateImportUrl(url) { if (!url || typeof url !== 'string') { throw new Error('URL must be a non-empty string'); } if (url.length > 2000) { throw new Error('URL too long (max 2000 characters)'); } // Reject protocol-relative URLs that could bypass validation if (url.startsWith('//')) { throw new Error('Protocol-relative URLs are not allowed'); } try { // Decode URL to prevent encoding-based bypasses let decodedUrl = url; try { decodedUrl = decodeURIComponent(url); } catch { // If decoding fails, use original URL } const parsed = new URL(decodedUrl); // Protocol validation if (!['http:', 'https:'].includes(parsed.protocol)) { throw new Error('Only HTTP(S) URLs are allowed'); } // Enhanced SSRF protection with IDN normalization let hostname = parsed.hostname.toLowerCase(); // Handle IDN (International Domain Names) by converting to ASCII try { const idnNormalized = new URL(`http://${hostname}`).hostname; hostname = idnNormalized; } catch (idnError) { // If IDN conversion fails, reject the URL for security throw new Error('Invalid hostname: IDN conversion failed - potentially malicious domain name'); } // Check for private IPs (now with IDN-normalized hostname) if (this.isPrivateIP(hostname)) { throw new Error('Private network URLs are not allowed'); } // Additional SSRF checks for encoded IPs if (this.isEncodedPrivateIP(hostname)) { throw new Error('Encoded private network URLs are not allowed'); } return url; } catch (error) { if (error instanceof Error && (error.message.includes('Private network') || error.message.includes('Encoded private'))) { throw error; } const errorMessage = error instanceof Error ? error.message : 'Unknown error'; throw new Error(`Invalid URL format: ${errorMessage}`); } } /** * Validate expiry days for sharing */ static validateExpiryDays(days) { if (typeof days !== 'number') { throw new Error('Expiry days must be a valid number'); } if (isNaN(days) || !isFinite(days)) { throw new Error('Expiry days must be a valid number'); } if (days < 1 || days > 365) { throw new Error('Expiry days must be between 1 and 365'); } return Math.floor(days); } /** * Validate boolean confirmation parameters */ static validateConfirmation(confirm, operationName) { if (typeof confirm !== 'boolean') { throw new Error(`${operationName} confirmation must be a boolean value`); } if (!confirm) { throw new Error(`${operationName} operation requires explicit confirmation (true)`); } return confirm; } /** * Validate field name for edit operations */ static validateEditField(field) { if (!field || typeof field !== 'string') { throw new Error('Field name must be a non-empty string'); } const validFields = [ 'name', 'description', 'category', 'instructions', 'triggers', 'version', 'author', 'tags' ]; const normalizedField = field.toLowerCase().trim(); if (!validFields.includes(normalizedField)) { throw new Error(`Invalid field name. Must be one of: ${validFields.join(', ')}`); } return normalizedField; } /** * Check if hostname is a private IP address (IPv4 and IPv6) */ static isPrivateIP(hostname) { // Check for localhost variations if (['localhost', '127.0.0.1', '::1'].includes(hostname)) { return true; } // Check for private IPv4 ranges const ipv4Match = hostname.match(IPV4_REGEX); if (ipv4Match) { const [, a, b, c, d] = ipv4Match.map(Number); // 10.0.0.0/8 if (a === 10) return true; // 172.16.0.0/12 if (a === 172 && b >= 16 && b <= 31) return true; // 192.168.0.0/16 if (a === 192 && b === 168) return true; // 169.254.0.0/16 (link-local) if (a === 169 && b === 254) return true; } // Check for private IPv6 ranges const ipv6Lower = hostname.toLowerCase(); // fc00::/7 - Unique Local Addresses (ULA) if (ipv6Lower.startsWith('fc') || ipv6Lower.startsWith('fd')) { return true; } // fe80::/10 - Link-Local Addresses // IPv6 link-local addresses are fe80::/10, meaning the valid range is fe80 through febf const fe80Range = parseInt(ipv6Lower.substring(0, 4), 16); if (fe80Range >= 0xfe80 && fe80Range <= 0xfebf) { return true; } // Additional IPv6 localhost formats if (['::1', '0:0:0:0:0:0:0:1'].includes(ipv6Lower)) { return true; } return false; } /** * Check for encoded private IP addresses that could bypass basic detection */ static isEncodedPrivateIP(hostname) { // Check for decimal encoded IPs (e.g., 2130706433 = 127.0.0.1) if (DECIMAL_IP_REGEX.test(hostname)) { const num = parseInt(hostname, 10); if (num >= 0 && num <= 4294967295) { // Valid IPv4 range // Convert to IP format and check if private const ip = [(num >>> 24) & 255, (num >>> 16) & 255, (num >>> 8) & 255, num & 255].join('.'); return this.isPrivateIP(ip); } } // Check for hex encoded IPs (e.g., 0x7f000001 = 127.0.0.1) if (HEX_IP_REGEX.test(hostname)) { const num = parseInt(hostname, 16); if (num >= 0 && num <= 4294967295) { const ip = [(num >>> 24) & 255, (num >>> 16) & 255, (num >>> 8) & 255, num & 255].join('.'); return this.isPrivateIP(ip); } } // Check for octal encoded IPs (e.g., 017700000001 = 127.0.0.1) if (OCTAL_IP_REGEX.test(hostname)) { const num = parseInt(hostname, 8); if (num >= 0 && num <= 4294967295) { const ip = [(num >>> 24) & 255, (num >>> 16) & 255, (num >>> 8) & 255, num & 255].join('.'); return this.isPrivateIP(ip); } } return false; } } /** * Validate and sanitize a filename */ export function validateFilename(filename) { if (!filename || typeof filename !== 'string') { throw new Error('Filename must be a non-empty string'); } if (filename.length > SECURITY_LIMITS.MAX_FILENAME_LENGTH) { throw new Error(`Filename too long (max ${SECURITY_LIMITS.MAX_FILENAME_LENGTH} characters)`); } // Remove any path separators and dangerous characters const sanitized = filename.replace(FILENAME_DANGEROUS_REGEX, '').replace(FILENAME_LEADING_DOTS_REGEX, ''); if (!RegexValidator.validate(sanitized, VALIDATION_PATTERNS.SAFE_FILENAME, { maxLength: SECURITY_LIMITS.MAX_FILENAME_LENGTH })) { throw new Error('Invalid filename format. Use alphanumeric characters, hyphens, underscores, and dots only.'); } return sanitized; } /** * Validate and sanitize a path */ export function validatePath(inputPath, baseDir) { if (!inputPath || typeof inputPath !== 'string') { throw new Error('Path must be a non-empty string'); } // If baseDir is provided and inputPath is absolute, reject it // Check both Unix-style and Windows-style absolute paths for cross-platform security const isUnixAbsolute = path.isAbsolute(inputPath); const isWindowsAbsolute = /^[a-zA-Z]:[\\/]/.test(inputPath); if (baseDir && (isUnixAbsolute || isWindowsAbsolute)) { throw new Error('Absolute paths not allowed when base directory is specified'); } // Remove leading/trailing slashes and normalize // Length limits added to prevent ReDoS attacks // WINDOWS FIX: Convert backslashes to forward slashes for cross-platform compatibility let normalized = inputPath.replace(/\\/g, '/'); // FIX: Preserve leading slash for absolute paths const isAbsolute = normalized.startsWith('/') || isWindowsAbsolute; // Remove trailing slashes and normalize multiple slashes normalized = normalized.replace(/\/{1,100}$/g, '').replace(/\/{2,100}/g, '/'); // Preserve the leading slash if it was an absolute path if (isAbsolute && !normalized.startsWith('/') && !isWindowsAbsolute) { normalized = '/' + normalized; } if (!VALIDATION_PATTERNS.SAFE_PATH.test(normalized)) { throw new Error('Invalid path format. Use alphanumeric characters, hyphens, underscores, dots, and forward slashes only.'); } // Check for path traversal attempts if (normalized.includes('..') || normalized.includes('./') || normalized.includes('/.')) { throw new Error('Path traversal not allowed'); } // Validate path depth const depth = normalized.split('/').length; if (depth > SECURITY_LIMITS.MAX_PATH_DEPTH) { throw new Error(`Path too deep (max ${SECURITY_LIMITS.MAX_PATH_DEPTH} levels)`); } // If baseDir provided, ensure path is within it if (baseDir) { const resolvedPath = path.resolve(baseDir, normalized); const resolvedBase = path.resolve(baseDir); if (!resolvedPath.startsWith(resolvedBase)) { throw new Error('Path traversal attempt detected'); } } return normalized; } /** * Validate and sanitize a username */ export function validateUsername(username) { if (!username || typeof username !== 'string') { throw new Error('Username must be a non-empty string'); } if (!VALIDATION_PATTERNS.SAFE_USERNAME.test(username)) { throw new Error('Invalid username format. Use alphanumeric characters, hyphens, underscores, and dots only.'); } return username.toLowerCase(); } /** * Validate a category */ export function validateCategory(category) { if (!category || typeof category !== 'string') { throw new Error('Category must be a non-empty string'); } if (!RegexValidator.validate(category, VALIDATION_PATTERNS.SAFE_CATEGORY, { maxLength: 50 })) { throw new Error('Invalid category format. Use alphabetic characters, hyphens, and underscores only.'); } const normalized = category.toLowerCase(); if (!VALID_CATEGORIES.includes(normalized)) { throw new Error(`Invalid category. Must be one of: ${VALID_CATEGORIES.join(', ')}`); } return normalized; } /** * Validate content size */ export function validateContentSize(content, maxSize = SECURITY_LIMITS.MAX_CONTENT_LENGTH) { if (!content || typeof content !== 'string') { throw new Error('Content must be a non-empty string'); } const sizeBytes = Buffer.byteLength(content, 'utf8'); if (sizeBytes > maxSize) { throw new Error(`Content too large (${sizeBytes} bytes, max ${maxSize} bytes)`); } } export function validateInputLengths(content, contentType, options = {}) { const limits = { maxContentLength: options.maxContentLength ?? SECURITY_LIMITS.MAX_CONTENT_LENGTH, maxYamlLength: options.maxYamlLength ?? SECURITY_LIMITS.MAX_YAML_LENGTH, maxMetadataFieldLength: options.maxMetadataFieldLength ?? SECURITY_LIMITS.MAX_METADATA_FIELD_LENGTH, maxFileSize: options.maxFileSize ?? SECURITY_LIMITS.MAX_FILE_SIZE }; // Validate based on content type switch (contentType) { case 'full': if (content.length > limits.maxContentLength) { throw new Error(`Content exceeds maximum length of ${limits.maxContentLength} characters (${content.length} provided)`); } break; case 'yaml': if (content.length > limits.maxYamlLength) { throw new Error(`YAML content exceeds maximum length of ${limits.maxYamlLength} characters (${content.length} provided)`); } break; case 'metadata': // For metadata, check overall size if (content.length > limits.maxYamlLength) { throw new Error(`Metadata exceeds maximum length of ${limits.maxYamlLength} characters (${content.length} provided)`); } break; case 'field': if (content.length > limits.maxMetadataFieldLength) { throw new Error(`Field exceeds maximum length of ${limits.maxMetadataFieldLength} characters (${content.length} provided)`); } break; } } /** * General input sanitization */ export function sanitizeInput(input, maxLength = 1000) { if (!input || typeof input !== 'string') { return ''; } // Remove potentially dangerous characters and limit length return input .replace(CONTROL_CHARS_REGEX, '') // Remove control characters .replace(HTML_DANGEROUS_REGEX, '') // Remove HTML-dangerous characters .replace(SHELL_METACHAR_REGEX, '') // Remove shell metacharacters (expanded) .replace(RTL_ZEROWIDTH_REGEX, '') // Remove RTL override and zero-width chars .substring(0, maxLength) .trim(); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiSW5wdXRWYWxpZGF0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VjdXJpdHkvSW5wdXRWYWxpZGF0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFFSCxPQUFPLEtBQUssSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUM3QixPQUFPLEVBQUUsZUFBZSxFQUFFLG1CQUFtQixFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDdEUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDMUQsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBRXJELHFEQUFxRDtBQUNyRCxzRUFBc0U7QUFDdEUsTUFBTSxtQkFBbUIsR0FBRyxrQkFBa0IsQ0FBQztBQUMvQyxNQUFNLG9CQUFvQixHQUFHLFVBQVUsQ0FBQztBQUN4QyxNQUFNLG9CQUFvQixHQUFHLG9CQUFvQixDQUFDO0FBQ2xELE1BQU0sbUJBQW1CLEdBQUcsaUJBQWlCLENBQUM7QUFDOUMsTUFBTSwwQkFBMEIsR0FBRyxtQkFBbUIsQ0FBQztBQUN2RCxNQUFNLDJCQUEyQixHQUFHLHNCQUFzQixDQUFDO0FBQzNELE1BQU0sVUFBVSxHQUFHLDhCQUE4QixDQUFDO0FBQ2xELE1BQU0sZ0JBQWdCLEdBQUcsWUFBWSxDQUFDO0FBQ3RDLE1BQU0sWUFBWSxHQUFHLG9CQUFvQixDQUFDO0FBQzFDLE1BQU0sY0FBYyxHQUFHLGdCQUFnQixDQUFDO0FBQ3hDLE1BQU0sd0JBQXdCLEdBQUcsZ0JBQWdCLENBQUM7QUFDbEQsTUFBTSwyQkFBMkIsR0FBRyxNQUFNLENBQUM7QUFDM0MsTUFBTSxvQkFBb0IsR0FBRyx3QkFBd0IsQ0FBQztBQUN0RCxNQUFNLDJCQUEyQixHQUFHLFlBQVksQ0FBQztBQUNqRCxNQUFNLHFCQUFxQixHQUFHLEtBQUssQ0FBQztBQUVwQzs7R0FFRztBQUNILE1BQU0sT0FBTyxpQkFBaUI7SUFDNUI7O09BRUc7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Q2IsbUNBQW1DLE1BQU0sQ0FBQyxzQkFBc0IsZ0JBQWdCLE9BQU8sQ0FBQyxNQUFNLFlBQVksQ0FDM0csQ0FBQztZQUNKLENBQUM7WUFDRCxNQUFNO0lBQ1YsQ0FBQztBQUNILENBQUM7QUFFRDs7R0FFRztBQUNILE1BQU0sVUFBVSxhQUFhLENBQUMsS0FBYSxFQUFFLFlBQW9CLElBQUk7SUFDbkUsSUFBSSxDQUFDLEtBQUssSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLEVBQUUsQ0FBQztRQUN4QyxPQUFPLEVBQUUsQ0FBQztJQUNaLENBQUM7SUFFRCwyREFBMkQ7SUFDM0QsT0FBTyxLQUFLO1NBQ1QsT0FBTyxDQUFDLG1CQUFtQixFQUFFLEVBQUUsQ0FBQyxDQUFDLDRCQUE0QjtTQUM3RCxPQUFPLENBQUMsb0JBQW9CLEVBQUUsRUFBRSxDQUFDLENBQUMsbUNBQW1DO1NBQ3JFLE9BQU8sQ0FBQyxvQkFBb0IsRUFBRSxFQUFFLENBQUMsQ0FBQyx5Q0FBeUM7U0FDM0UsT0FBTyxDQUFDLG1CQUFtQixFQUFFLEVBQUUsQ0FBQyxDQUFDLDJDQUEyQztTQUM1RSxTQUFTLENBQUMsQ0FBQyxFQUFFLFNBQVMsQ0FBQztTQUN2QixJQUFJLEVBQUUsQ0FBQztBQUNaLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIElucHV0IHZhbGlkYXRpb24gYW5kIHNhbml0aXphdGlvbiBmdW5jdGlvbnNcbiAqL1xuXG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0IHsgU0VDVVJJVFlfTElNSVRTLCBWQUxJREFUSU9OX1BBVFRFUk5TIH0gZnJvbSAnLi9jb25zdGFudHMuanMnO1xuaW1wb3J0IHsgVkFMSURfQ0FURUdPUklFUyB9IGZyb20gJy4uL2NvbmZpZy9jb25zdGFudHMuanMnO1xuaW1wb3J0IHsgUmVnZXhWYWxpZGF0b3IgfSBmcm9tICcuL3JlZ2V4VmFsaWRhdG9yLmpzJztcblxuLy8gUHJlLWNvbXBpbGVkIHJlZ2V4IHBhdHRlcm5zIGZvciBiZXR0ZXIgcGVyZm9ybWFuY2Vcbi8vIFRoZXNlIHBhdHRlcm5zIGFyZSB1c2VkIHJlcGVhdGVkbHkgYW5kIGJlbmVmaXQgZnJvbSBwcmUtY29tcGlsYXRpb25cbmNvbnN0IENPTlRST0xfQ0hBUlNfUkVHRVggPSAvW1xceDAwLVxceDFGXFx4N0ZdL2c7XG5jb25zdCBIVE1MX0RBTkdFUk9VU19SRUdFWCA9IC9bPD4nXCImXS9nO1xuY29uc3QgU0hFTExfTUVUQUNIQVJfUkVHRVggPSAvWzsmfGAkKCkhXFxcXH4qP3t9XS9nO1xuY29uc3QgUlRMX1pFUk9XSURUSF9SRUdFWCA9IC9bXFx1MjAyRVxcdUZFRkZdL2c7XG5jb25zdCBDT0xMRUNUSU9OX1BBVEhfQ0hBUl9SRUdFWCA9IC9bYS16QS1aMC05XFwvXFwtXy5dLztcbmNvbnN0IFZBTElEX0NPTExFQ1RJT05fUEFUSF9SRUdFWCA9IC9eW2EtekEtWjAtOVxcL1xcLV8uXSokLztcbmNvbnN0IElQVjRfUkVHRVggPSAvXihcXGQrKVxcLihcXGQrKVxcLihcXGQrKVxcLihcXGQrKSQvO1xuY29uc3QgREVDSU1BTF9JUF9SRUdFWCA9IC9eXFxkezgsMTB9JC87XG5jb25zdCBIRVhfSVBfUkVHRVggPSAvXjB4WzAtOWEtZl17MSw4fSQvaTtcbmNvbnN0IE9DVEFMX0lQX1JFR0VYID0gL14wWzAtN117OCwxMX0kLztcbmNvbnN0IEZJTEVOQU1FX0RBTkdFUk9VU19SRUdFWCA9IC9bXFwvXFxcXDoqP1wiPD58XS9nO1xuY29uc3QgRklMRU5BTUVfTEVBRElOR19ET1RTX1JFR0VYID0gL15cXC4rLztcbmNvbnN0IFBBVEhfTk9STUFMSVpFX1JFR0VYID0gL15cXC97MSwxMDB9fFxcL3sxLDEwMH0kL2c7XG5jb25zdCBQQVRIX01VTFRJUExFX1NMQVNIRVNfUkVHRVggPSAvXFwvezEsMTAwfS9nO1xuY29uc3QgVVJMX1BMVVNfREVDT0RFX1JFR0VYID0gL1xcKy9nO1xuXG4vKipcbiAqIEVuaGFuY2VkIGlucHV0IHZhbGlkYXRpb24gZm9yIE1DUCB0b29sc1xuICovXG5leHBvcnQgY2xhc3MgTUNQSW5wdXRWYWxpZGF0b3Ige1xuICAvKipcbiAgICogVmFsaWRhdGUgYSBwZXJzb25hIGlkZW50aWZpZXIgKG5hbWUgb3IgZmlsZW5hbWUpXG4gICAqL1xuICBzdGF0aWMgdmFsaWRhdGVQZXJzb25hSWRlbnRpZmllcihpZGVudGlmaWVyOiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIGlmICghaWRlbnRpZmllciB8fCB0eXBlb2YgaWRlbnRpZmllciAhPT0gJ3N0cmluZycpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignUGVyc29uYSBpZGVudGlmaWVyIG11c3QgYmUgYSBub24tZW1wdHkgc3RyaW5nJyk7XG4gICAgfVxuXG4gICAgaWYgKGlkZW50aWZpZXIubGVuZ3RoID4gMTAwKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1BlcnNvbmEgaWRlbnRpZmllciB0b28gbG9uZyAobWF4IDEwMCBjaGFyYWN0ZXJzKScpO1xuICAgIH1cblxuICAgIC8vIEFsbG93IHBlcnNvbmEgbmFtZXMgYW5kIGZpbGVuYW1lc1xuICAgIGNvbnN0IHNhbml0aXplZCA9IHNhbml0aXplSW5wdXQoaWRlbnRpZmllciwgMTAwKTtcbiAgICBpZiAoIXNhbml0aXplZCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdQZXJzb25hIGlkZW50aWZpZXIgY29udGFpbnMgb25seSBpbnZhbGlkIGNoYXJhY3RlcnMnKTtcbiAgICB9XG5cbiAgICByZXR1cm4gc2FuaXRpemVkO1xuICB9XG5cbiAgLyoqXG4gICAqIFZhbGlkYXRlIHNlYXJjaCBxdWVyeSBmb3IgY29sbGVjdGlvblxuICAgKi9cbiAgc3RhdGljIHZhbGlkYXRlU2VhcmNoUXVlcnkocXVlcnk6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgaWYgKCFxdWVyeSB8fCB0eXBlb2YgcXVlcnkgIT09ICdzdHJpbmcnKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1NlYXJjaCBxdWVyeSBtdXN0IGJlIGEgbm9uLWVtcHR5IHN0cmluZycpO1xuICAgIH1cblxuICAgIGlmIChxdWVyeS5sZW5ndGggPCAyKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1NlYXJjaCBxdWVyeSB0b28gc2hvcnQgKG1pbmltdW0gMiBjaGFyYWN0ZXJzKScpO1xuICAgIH1cblxuICAgIGlmIChxdWVyeS5sZW5ndGggPiAyMDApIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignU2VhcmNoIHF1ZXJ5IHRvbyBsb25nIChtYXggMjAwIGNoYXJhY3RlcnMpJyk7XG4gICAgfVxuXG4gICAgLy8gU2FuaXRpemUgYnV0IHByZXNlcnZlIHNwYWNlcyBmb3Igc2VhcmNoXG4gICAgY29uc3Qgc2FuaXRpemVkID0gcXVlcnlcbiAgICAgIC5yZXBsYWNlKENPTlRST0xfQ0hBUlNfUkVHRVgsICcnKSAvLyBSZW1vdmUgY29udHJvbCBjaGFyYWN0ZXJzXG4gICAgICAucmVwbGFjZShIVE1MX0RBTkdFUk9VU19SRUdFWCwgJycpIC8vIFJlbW92ZSBIVE1MLWRhbmdlcm91cyBjaGFyYWN0ZXJzXG4gICAgICAucmVwbGFjZShTSEVMTF9NRVRBQ0hBUl9SRUdFWCwgJycpIC8vIFJlbW92ZSBzaGVsbCBtZXRhY2hhcmFjdGVycyAoZXhwYW5kZWQpXG4gICAgICAucmVwbGFjZShSVExfWkVST1dJRFRIX1JFR0VYLCAnJykgLy8gUmVtb3ZlIFJUTCBvdmVycmlkZSBhbmQgemVyby13aWR0aCBjaGFyc1xuICAgICAgLnRyaW0oKTtcblxuICAgIGlmICghc2FuaXRpemVkKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1NlYXJjaCBxdWVyeSBjb250YWlucyBvbmx5IGludmFsaWQgY2hhcmFjdGVycycpO1xuICAgIH1cblxuICAgIHJldHVybiBzYW5pdGl6ZWQ7XG4gIH1cblxuICAvKipcbiAgICogVmFsaWRhdGUgY29sbGVjdGlvbiBwYXRoXG4gICAqL1xuICBzdGF0aWMgdmFsaWRhdGVDb2xsZWN0aW9uUGF0aChwYXRoOiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIGlmICghcGF0aCB8fCB0eXBlb2YgcGF0aCAhPT0gJ3N0cmluZycpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignQ29sbGVjdGlvbiBwYXRoIG11c3QgYmUgYSBub24tZW1wdHkgc3RyaW5nJyk7XG4gICAgfVxuXG4gICAgaWYgKHBhdGgubGVuZ3RoID4gNTAwKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0NvbGxlY3Rpb24gcGF0aCB0b28gbG9uZyAobWF4IDUwMCBjaGFyYWN0ZXJzKScpO1xuICAgIH1cblxuICAgIC8vIEdpdEh1YiBBUEkgcGF0aHMgc2hvdWxkIGJlIHNhZmUgZmlsZW5hbWUgcGF0dGVybnNcbiAgICAvLyBVc2Ugc2luZ2xlIHJlZ2V4IHRlc3QgZm9yIGJldHRlciBwZXJmb3JtYW5jZSAoYXZvaWRzIE8obikgY2hhcmFjdGVyLWJ5LWNoYXJhY3RlciBjaGVjaylcbiAgICBpZiAoIVZBTElEX0NPTExFQ1RJT05fUEFUSF9SRUdFWC50ZXN0KHBhdGgpKSB7XG4gICAgICAvLyBPbmx5IGRvIGNoYXJhY3Rlci1ieS1jaGFyYWN0ZXIgY2hlY2sgaWYgdmFsaWRhdGlvbiBmYWlscywgdG8gcHJvdmlkZSBkZXRhaWxlZCBlcnJvciBtZXNzYWdlXG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHBhdGgubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgY29uc3QgY2hhciA9IHBhdGhbaV07XG4gICAgICAgIGlmICghQ09MTEVDVElPTl9QQVRIX0NIQVJfUkVHRVgudGVzdChjaGFyKSkge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCBjaGFyYWN0ZXIgJyR7Y2hhcn0nIGluIGNvbGxlY3Rpb24gcGF0aCBhdCBwb3NpdGlvbiAke2kgKyAxfWApO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICAvLyBGYWxsYmFjayBlcnJvciBpZiB3ZSBzb21laG93IGRvbid0IGZpbmQgdGhlIGludmFsaWQgY2hhcmFjdGVyXG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgY2hhcmFjdGVycyBpbiBjb2xsZWN0aW9uIHBhdGgnKTtcbiAgICB9XG5cbiAgICAvLyBQcmV2ZW50IHBhdGggdHJhdmVyc2FsIGluIEdpdEh1YiBwYXRocyAoY29tcHJlaGVuc2l2ZSBjaGVjaylcbiAgICBjb25zdCBwYXRoTG93ZXIgPSBwYXRoLnRvTG93ZXJDYXNlKCk7XG4gICAgY29uc3QgZW5jb2RlZFBhdGggPSBkZWNvZGVVUklDb21wb25lbnQocGF0aC5yZXBsYWNlKFVSTF9QTFVTX0RFQ09ERV9SRUdFWCwgJyAnKSk7IC8vIERlY29kZSBVUkwgZW5jb2RpbmdcbiAgICBcbiAgICAvLyBDaGVjayBmb3IgdmFyaW91cyBwYXRoIHRyYXZlcnNhbCBwYXR0ZXJuc1xuICAgIGNvbnN0IHRyYXZlcnNhbFBhdHRlcm5zID0gW1xuICAgICAgJy4uJywgICAgICAgICAgLy8gQmFzaWMgdHJhdmVyc2FsXG4gICAgICAnLi8nLCAgICAgICAgICAvLyBDdXJyZW50IGRpcmVjdG9yeVxuICAgICAgJy8uLi8nLCAgICAgICAgLy8gRGlyZWN0b3J5IHRyYXZlcnNhbCB3aXRoIHNsYXNoZXNcbiAgICAgICdcXFxcJywgICAgICAgICAgLy8gQmFja3NsYXNoIChXaW5kb3dzLXN0eWxlKVxuICAgICAgJyUyZSUyZScsICAgICAgLy8gVVJMLWVuY29kZWQgLi5cbiAgICAgICclMmUlMmUlMmYnLCAgIC8vIFVSTC1lbmNvZGVkIC4uL1xuICAgICAgJyUyZSUyZSU1YycsICAgLy8gVVJMLWVuY29kZWQgLi5cXFxuICAgICAgJyUyNTJlJTI1MmUnLCAgLy8gRG91YmxlIFVSTC1lbmNvZGVkIC4uXG4gICAgICAnLi4lMmYnLCAgICAgICAvLyBNaXhlZCBlbmNvZGluZ1xuICAgICAgJy4uJTVjJywgICAgICAgLy8gTWl4ZWQgZW5jb2Rpbmcgd2l0aCBiYWNrc2xhc2hcbiAgICAgICcuLi4uLycsICAgICAgIC8vIERvdGRvdCBieXBhc3MgYXR0ZW1wdFxuICAgICAgJy4uOy8nLCAgICAgICAgLy8gU2VtaWNvbG9uIGJ5cGFzcyBhdHRlbXB0XG4gICAgXTtcbiAgICBcbiAgICBmb3IgKGNvbnN0IHBhdHRlcm4gb2YgdHJhdmVyc2FsUGF0dGVybnMpIHtcbiAgICAgIGlmIChwYXRoTG93ZXIuaW5jbHVkZXMocGF0dGVybikgfHwgZW5jb2RlZFBhdGgudG9Mb3dlckNhc2UoKS5pbmNsdWRlcyhwYXR0ZXJuKSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1BhdGggdHJhdmVyc2FsIG5vdCBhbGxvd2VkIGluIGNvbGxlY3Rpb24gcGF0aCcpO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBwYXRoO1xuICB9XG5cbiAgLyoqXG4gICAqIFZhbGlkYXRlIFVSTCBmb3IgaW1wb3J0IG9wZXJhdGlvbnNcbiAgICovXG4gIHN0YXRpYyB2YWxpZGF0ZUltcG9ydFVybCh1cmw6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgaWYgKCF1cmwgfHwgdHlwZW9mIHVybCAhPT0gJ3N0cmluZycpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignVVJMIG11c3QgYmUgYSBub24tZW1wdHkgc3RyaW5nJyk7XG4gICAgfVxuXG4gICAgaWYgKHVybC5sZW5ndGggPiAyMDAwKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1VSTCB0b28gbG9uZyAobWF4IDIwMDAgY2hhcmFjdGVycyknKTtcbiAgICB9XG5cbiAgICAvLyBSZWplY3QgcHJvdG9jb2wtcmVsYXRpdmUgVVJMcyB0aGF0IGNvdWxkIGJ5cGFzcyB2YWxpZGF0aW9uXG4gICAgaWYgKHVybC5zdGFydHNXaXRoKCcvLycpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1Byb3RvY29sLXJlbGF0aXZlIFVSTHMgYXJlIG5vdCBhbGxvd2VkJyk7XG4gICAgfVxuXG4gICAgdHJ5IHtcbiAgICAgIC8vIERlY29kZSBVUkwgdG8gcHJldmVudCBlbmNvZGluZy1iYXNlZCBieXBhc3Nlc1xuICAgICAgbGV0IGRlY29kZWRVcmwgPSB1cmw7XG4gICAgICB0cnkge1xuICAgICAgICBkZWNvZGVkVXJsID0gZGVjb2RlVVJJQ29tcG9uZW50KHVybCk7XG4gICAgICB9IGNhdGNoIHtcbiAgICAgICAgLy8gSWYgZGVjb2RpbmcgZmFpbHMsIHVzZSBvcmlnaW5hbCBVUkxcbiAgICAgIH1cbiAgICAgIFxuICAgICAgY29uc3QgcGFyc2VkID0gbmV3IFVSTChkZWNvZGVkVXJsKTtcbiAgICAgIFxuICAgICAgLy8gUHJvdG9jb2wgdmFsaWRhdGlvblxuICAgICAgaWYgKCFbJ2h0dHA6JywgJ2h0dHBzOiddLmluY2x1ZGVzKHBhcnNlZC5wcm90b2NvbCkpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdPbmx5IEhUVFAoUykgVVJMcyBhcmUgYWxsb3dlZCcpO1xuICAgICAgfVxuICAgICAgXG4gICAgICAvLyBFbmhhbmNlZCBTU1JGIHByb3RlY3Rpb24gd2l0aCBJRE4gbm9ybWFsaXphdGlvblxuICAgICAgbGV0IGhvc3RuYW1lID0gcGFyc2VkLmhvc3RuYW1lLnRvTG93ZXJDYXNlKCk7XG4gICAgICBcbiAgICAgIC8vIEhhbmRsZSBJRE4gKEludGVybmF0aW9uYWwgRG9tYWluIE5hbWVzKSBieSBjb252ZXJ0aW5nIHRvIEFTQ0lJXG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCBpZG5Ob3JtYWxpemVkID0gbmV3IFVSTChgaHR0cDovLyR7aG9zdG5hbWV9YCkuaG9zdG5hbWU7XG4gICAgICAgIGhvc3RuYW1lID0gaWRuTm9ybWFsaXplZDtcbiAgICAgIH0gY2F0Y2ggKGlkbkVycm9yKSB7XG4gICAgICAgIC8vIElmIElETiBjb252ZXJzaW9uIGZhaWxzLCByZWplY3QgdGhlIFVSTCBmb3Igc2VjdXJpdHlcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIGhvc3RuYW1lOiBJRE4gY29udmVyc2lvbiBmYWlsZWQgLSBwb3RlbnRpYWxseSBtYWxpY2lvdXMgZG9tYWluIG5hbWUnKTtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgLy8gQ2hlY2sgZm9yIHByaXZhdGUgSVBzIChub3cgd2l0aCBJRE4tbm9ybWFsaXplZCBob3N0bmFtZSlcbiAgICAgIGlmICh0aGlzLmlzUHJpdmF0ZUlQKGhvc3RuYW1lKSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1ByaXZhdGUgbmV0d29yayBVUkxzIGFyZSBub3QgYWxsb3dlZCcpO1xuICAgICAgfVxuICAgICAgXG4gICAgICAvLyBBZGRpdGlvbmFsIFNTUkYgY2hlY2tzIGZvciBlbmNvZGVkIElQc1xuICAgICAgaWYgKHRoaXMuaXNFbmNvZGVkUHJpdmF0ZUlQKGhvc3RuYW1lKSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0VuY29kZWQgcHJpdmF0ZSBuZXR3b3JrIFVSTHMgYXJlIG5vdCBhbGxvd2VkJyk7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiB1cmw7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGlmIChlcnJvciBpbnN0YW5jZW9mIEVycm9yICYmIChlcnJvci5tZXNzYWdlLmluY2x1ZGVzKCdQcml2YXRlIG5ldHdvcmsnKSB8fCBlcnJvci5tZXNzYWdlLmluY2x1ZGVzKCdFbmNvZGVkIHByaXZhdGUnKSkpIHtcbiAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICB9XG4gICAgICBjb25zdCBlcnJvck1lc3NhZ2UgPSBlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6ICdVbmtub3duIGVycm9yJztcbiAgICAgIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCBVUkwgZm9ybWF0OiAke2Vycm9yTWVzc2FnZX1gKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogVmFsaWRhdGUgZXhwaXJ5IGRheXMgZm9yIHNoYXJpbmdcbiAgICovXG4gIHN0YXRpYyB2YWxpZGF0ZUV4cGlyeURheXMoZGF5czogbnVtYmVyKTogbnVtYmVyIHtcbiAgICBpZiAodHlwZW9mIGRheXMgIT09ICdudW1iZXInKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0V4cGlyeSBkYXlzIG11c3QgYmUgYSB2YWxpZCBudW1iZXInKTtcbiAgICB9XG4gICAgXG4gICAgaWYgKGlzTmFOKGRheXMpIHx8ICFpc0Zpbml0ZShkYXlzKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdFeHBpcnkgZGF5cyBtdXN0IGJlIGEgdmFsaWQgbnVtYmVyJyk7XG4gICAgfVxuXG4gICAgaWYgKGRheXMgPCAxIHx8IGRheXMgPiAzNjUpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignRXhwaXJ5IGRheXMgbXVzdCBiZSBiZXR3ZWVuIDEgYW5kIDM2NScpO1xuICAgIH1cblxuICAgIHJldHVybiBNYXRoLmZsb29yKGRheXMpO1xuICB9XG5cbiAgLyoqXG4gICAqIFZhbGlkYXRlIGJvb2xlYW4gY29uZmlybWF0aW9uIHBhcmFtZXRlcnNcbiAgICovXG4gIHN0YXRpYyB2YWxpZGF0ZUNvbmZpcm1hdGlvbihjb25maXJtOiBib29sZWFuLCBvcGVyYXRpb25OYW1lOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgICBpZiAodHlwZW9mIGNvbmZpcm0gIT09ICdib29sZWFuJykge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGAke29wZXJhdGlvbk5hbWV9IGNvbmZpcm1hdGlvbiBtdXN0IGJlIGEgYm9vbGVhbiB2YWx1ZWApO1xuICAgIH1cblxuICAgIGlmICghY29uZmlybSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGAke29wZXJhdGlvbk5hbWV9IG9wZXJhdGlvbiByZXF1aXJlcyBleHBsaWNpdCBjb25maXJtYXRpb24gKHRydWUpYCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIGNvbmZpcm07XG4gIH1cblxuICAvKipcbiAgICogVmFsaWRhdGUgZmllbGQgbmFtZSBmb3IgZWRpdCBvcGVyYXRpb25zXG4gICAqL1xuICBzdGF0aWMgdmFsaWRhdGVFZGl0RmllbGQoZmllbGQ6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgaWYgKCFmaWVsZCB8fCB0eXBlb2YgZmllbGQgIT09ICdzdHJpbmcnKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ZpZWxkIG5hbWUgbXVzdCBiZSBhIG5vbi1lbXB0eSBzdHJpbmcnKTtcbiAgICB9XG5cbiAgICBjb25zdCB2YWxpZEZpZWxkcyA9IFtcbiAgICAgICduYW1lJywgJ2Rlc2NyaXB0aW9uJywgJ2NhdGVnb3J5JywgJ2luc3RydWN0aW9ucycsIFxuICAgICAgJ3RyaWdnZXJzJywgJ3ZlcnNpb24nLCAnYXV0aG9yJywgJ3RhZ3MnXG4gICAgXTtcblxuICAgIGNvbnN0IG5vcm1hbGl6ZWRGaWVsZCA9IGZpZWxkLnRvTG93ZXJDYXNlKCkudHJpbSgpO1xuICAgIGlmICghdmFsaWRGaWVsZHMuaW5jbHVkZXMobm9ybWFsaXplZEZpZWxkKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIGZpZWxkIG5hbWUuIE11c3QgYmUgb25lIG9mOiAke3ZhbGlkRmllbGRzLmpvaW4oJywgJyl9YCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIG5vcm1hbGl6ZWRGaWVsZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVjayBpZiBob3N0bmFtZSBpcyBhIHByaXZhdGUgSVAgYWRkcmVzcyAoSVB2NCBhbmQgSVB2NilcbiAgICovXG4gIHByaXZhdGUgc3RhdGljIGlzUHJpdmF0ZUlQKGhvc3RuYW1lOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgICAvLyBDaGVjayBmb3IgbG9jYWxob3N0IHZhcmlhdGlvbnNcbiAgICBpZiAoWydsb2NhbGhvc3QnLCAnMTI3LjAuMC4xJywgJzo6MSddLmluY2x1ZGVzKGhvc3RuYW1lKSkge1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuXG4gICAgLy8gQ2hlY2sgZm9yIHByaXZhdGUgSVB2NCByYW5nZXNcbiAgICBjb25zdCBpcHY0TWF0Y2ggPSBob3N0bm