@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.
467 lines • 78.7 kB
JavaScript
/**
* Input validation and sanitization functions
*/
import * as path from 'path';
import { SECURITY_LIMITS, VALIDATION_PATTERNS } from './constants.js';
// VALID_CATEGORIES import removed — categories are deprecated (see constants.ts)
import { RegexValidator } from './regexValidator.js';
import { ErrorHandler, ErrorCategory } from '../utils/ErrorHandler.js';
import { ValidationErrorCodes } from '../utils/errorCodes.js';
// Pre-compiled regex patterns for better performance
// These patterns are used repeatedly and benefit from pre-compilation
// eslint-disable-next-line no-control-regex -- Intentionally matching control chars for sanitization
const CONTROL_CHARS_REGEX = /[\u0000-\u001F\u007F]/g; // NOSONAR - Removing control characters for security
const HTML_DANGEROUS_REGEX = /[<>'"&]/g;
const SHELL_METACHAR_REGEX = /[;&|`$()!\\~*?{}]/g;
const SHELL_METACHAR_DISPLAY_REGEX = /[;&|`$()]/g; // Core shell metacharacters for display sanitization
// Enhanced to include all common zero-width and directional override characters
// eslint-disable-next-line no-misleading-character-class -- Intentionally using Unicode characters for zero-width sanitization
const RTL_ZEROWIDTH_REGEX = /[\u200B\u200C\u200D\u2060\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 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 ErrorHandler.createError('Persona identifier must be a non-empty string', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_PERSONA_ID);
}
if (identifier.length > 100) {
throw ErrorHandler.createError('Persona identifier too long (max 100 characters)', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_LENGTH);
}
// Allow persona names and filenames
const sanitized = sanitizeInput(identifier, 100);
if (!sanitized) {
throw ErrorHandler.createError('Persona identifier contains only invalid characters', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_PERSONA_ID);
}
return sanitized;
}
/**
* Validate search query for collection
*/
static validateSearchQuery(query) {
if (!query || typeof query !== 'string') {
throw ErrorHandler.createError('Search query must be a non-empty string', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_SEARCH_QUERY);
}
if (query.length < 2) {
throw ErrorHandler.createError('Search query too short (minimum 2 characters)', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_LENGTH);
}
if (query.length > 200) {
throw ErrorHandler.createError('Search query too long (max 200 characters)', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_LENGTH);
}
// Sanitize but preserve spaces for search
const sanitized = query
.replaceAll(CONTROL_CHARS_REGEX, '') // Remove control characters
.replaceAll(HTML_DANGEROUS_REGEX, '') // Remove HTML-dangerous characters
.replaceAll(SHELL_METACHAR_REGEX, '') // Remove shell metacharacters (expanded)
.replaceAll(RTL_ZEROWIDTH_REGEX, '') // Remove RTL override and zero-width chars
.trim();
if (!sanitized) {
throw ErrorHandler.createError('Search query contains only invalid characters', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_SEARCH_QUERY);
}
return sanitized;
}
/**
* Validate collection path
*/
static validateCollectionPath(path) {
if (!path || typeof path !== 'string') {
throw ErrorHandler.createError('Collection path must be a non-empty string', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_COLLECTION_PATH);
}
if (path.length > 500) {
throw ErrorHandler.createError('Collection path too long (max 500 characters)', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_LENGTH);
}
// 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 ErrorHandler.createError(`Invalid character '${char}' in collection path at position ${i + 1}`, ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_CHARACTER);
}
}
// Fallback error if we somehow don't find the invalid character
throw ErrorHandler.createError('Invalid characters in collection path', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_COLLECTION_PATH);
}
// Prevent path traversal in GitHub paths (comprehensive check)
const pathLower = path.toLowerCase();
const encodedPath = decodeURIComponent(path.replaceAll(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 ErrorHandler.createError('Path traversal not allowed in collection path', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.PATH_TRAVERSAL);
}
}
return path;
}
/**
* Validate URL for import operations
*/
static validateImportUrl(url) {
if (!url || typeof url !== 'string') {
throw ErrorHandler.createError('URL must be a non-empty string', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_URL);
}
if (url.length > 2000) {
throw ErrorHandler.createError('URL too long (max 2000 characters)', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_LENGTH);
}
// Reject protocol-relative URLs that could bypass validation
if (url.startsWith('//')) {
throw ErrorHandler.createError('Protocol-relative URLs are not allowed', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_URL);
}
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 ErrorHandler.createError('Only HTTP(S) URLs are allowed', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_URL);
}
// 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 {
// If IDN conversion fails, reject the URL for security
throw ErrorHandler.createError('Invalid hostname: IDN conversion failed - potentially malicious domain name', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_URL);
}
// Check for private IPs (now with IDN-normalized hostname)
if (this.isPrivateIP(hostname)) {
throw ErrorHandler.createError('Private network URLs are not allowed', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_URL);
}
// Additional SSRF checks for encoded IPs
if (this.isEncodedPrivateIP(hostname)) {
throw ErrorHandler.createError('Encoded private network URLs are not allowed', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_URL);
}
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 ErrorHandler.createError(`Invalid URL format: ${errorMessage}`, ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_URL);
}
}
/**
* Validate expiry days for sharing
*/
static validateExpiryDays(days) {
if (typeof days !== 'number') {
throw ErrorHandler.createError('Expiry days must be a valid number', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_NUMBER);
}
if (Number.isNaN(days) || !Number.isFinite(days)) {
throw ErrorHandler.createError('Expiry days must be a valid number', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_NUMBER);
}
if (days < 1 || days > 365) {
throw ErrorHandler.createError('Expiry days must be between 1 and 365', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_RANGE);
}
return Math.floor(days);
}
/**
* Validate boolean confirmation parameters
*/
static validateConfirmation(confirm, operationName) {
if (typeof confirm !== 'boolean') {
throw ErrorHandler.createError(`${operationName} confirmation must be a boolean value`, ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_TYPE);
}
if (!confirm) {
throw ErrorHandler.createError(`${operationName} operation requires explicit confirmation (true)`, ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.CONFIRMATION_REQUIRED);
}
return confirm;
}
/**
* Validate field name for edit operations
*/
static validateEditField(field) {
if (!field || typeof field !== 'string') {
throw ErrorHandler.createError('Field name must be a non-empty string', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.REQUIRED_FIELD);
}
const validFields = [
'name', 'description', 'category', 'instructions',
'triggers', 'version', 'author', 'tags'
];
const normalizedField = field.toLowerCase().trim();
if (!validFields.includes(normalizedField)) {
throw ErrorHandler.createError(`Invalid field name. Must be one of: ${validFields.join(', ')}`, ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_INPUT);
}
return normalizedField;
}
/**
* Sanitize text for safe display output
* Removes core shell metacharacters to prevent command injection in displayed messages
*
* Uses a conservative subset of shell metacharacters (;, &, |, `, $, parentheses)
* that are most critical for command injection prevention while preserving
* common punctuation like !, ?, * for better user experience in display contexts.
*
* @param text - Text to sanitize for display
* @returns Sanitized text with core shell metacharacters removed
*/
static sanitizeForDisplay(text) {
if (!text || typeof text !== 'string') {
return '';
}
// Use pre-compiled regex for performance
// Removes: ; & | ` $ ( )
return text.replaceAll(SHELL_METACHAR_DISPLAY_REGEX, '');
}
/**
* 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] = 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 = Number.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 = Number.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 = Number.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 = Number.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 ErrorHandler.createError('Filename must be a non-empty string', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_FILENAME);
}
if (filename.length > SECURITY_LIMITS.MAX_FILENAME_LENGTH) {
throw ErrorHandler.createError(`Filename too long (max ${SECURITY_LIMITS.MAX_FILENAME_LENGTH} characters)`, ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_LENGTH);
}
// Remove any path separators and dangerous characters
const sanitized = filename.replaceAll(FILENAME_DANGEROUS_REGEX, '').replace(FILENAME_LEADING_DOTS_REGEX, '');
if (!RegexValidator.validate(sanitized, VALIDATION_PATTERNS.SAFE_FILENAME, { maxLength: SECURITY_LIMITS.MAX_FILENAME_LENGTH })) {
throw ErrorHandler.createError('Invalid filename format. Use alphanumeric characters, hyphens, underscores, and dots only.', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_FILENAME);
}
return sanitized;
}
/**
* Validate and sanitize a path
*/
export function validatePath(inputPath, baseDir) {
if (!inputPath || typeof inputPath !== 'string') {
throw ErrorHandler.createError('Path must be a non-empty string', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_PATH);
}
// 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 ErrorHandler.createError('Absolute paths not allowed when base directory is specified', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_PATH);
}
// 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.replaceAll('\\', '/');
// FIX: Preserve leading slash for absolute paths
const isAbsolute = normalized.startsWith('/') || isWindowsAbsolute;
// Remove trailing slashes and normalize multiple slashes
normalized = normalized.replaceAll(/\/{1,100}$/g, '').replaceAll(/\/{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 ErrorHandler.createError('Invalid path format. Use alphanumeric characters, hyphens, underscores, dots, and forward slashes only.', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_PATH);
}
// Check for path traversal attempts
if (normalized.includes('..') || normalized.includes('./') || normalized.includes('/.')) {
throw ErrorHandler.createError('Path traversal not allowed', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.PATH_TRAVERSAL);
}
// Validate path depth
const depth = normalized.split('/').length;
if (depth > SECURITY_LIMITS.MAX_PATH_DEPTH) {
throw ErrorHandler.createError(`Path too deep (max ${SECURITY_LIMITS.MAX_PATH_DEPTH} levels)`, ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_PATH);
}
// 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 ErrorHandler.createError('Path traversal attempt detected', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.PATH_TRAVERSAL);
}
}
return normalized;
}
/**
* Validate and sanitize a username
*/
export function validateUsername(username) {
if (!username || typeof username !== 'string') {
throw ErrorHandler.createError('Username must be a non-empty string', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.REQUIRED_FIELD);
}
if (!VALIDATION_PATTERNS.SAFE_USERNAME.test(username)) {
throw ErrorHandler.createError('Invalid username format. Use alphanumeric characters, hyphens, underscores, and dots only.', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_FORMAT);
}
return username.toLowerCase();
}
/**
* Validate a category
*/
export function validateCategory(category) {
if (!category || typeof category !== 'string') {
throw ErrorHandler.createError('Category must be a non-empty string', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_CATEGORY);
}
if (!RegexValidator.validate(category, VALIDATION_PATTERNS.SAFE_CATEGORY, { maxLength: SECURITY_LIMITS.MAX_TAG_LENGTH })) {
throw ErrorHandler.createError('Invalid category format. Must start with a letter, followed by letters, digits, hyphens, or underscores (max 21 chars). Example: "code-analysis".', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.INVALID_CATEGORY);
}
return category.toLowerCase();
}
/**
* Validate content size
*/
export function validateContentSize(content, maxSize = SECURITY_LIMITS.MAX_CONTENT_LENGTH) {
if (!content || typeof content !== 'string') {
throw ErrorHandler.createError('Content must be a non-empty string', ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.REQUIRED_FIELD);
}
const sizeBytes = Buffer.byteLength(content, 'utf8');
if (sizeBytes > maxSize) {
throw ErrorHandler.createError(`Content too large (${sizeBytes} bytes, max ${maxSize} bytes)`, ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.CONTENT_TOO_LARGE);
}
}
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 ErrorHandler.createError(`Content exceeds maximum length of ${limits.maxContentLength} characters (${content.length} provided)`, ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.CONTENT_TOO_LARGE);
}
break;
case 'yaml':
if (content.length > limits.maxYamlLength) {
throw ErrorHandler.createError(`YAML content exceeds maximum length of ${limits.maxYamlLength} characters (${content.length} provided)`, ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.CONTENT_TOO_LARGE);
}
break;
case 'metadata':
// For metadata, check overall size
if (content.length > limits.maxYamlLength) {
throw ErrorHandler.createError(`Metadata exceeds maximum length of ${limits.maxYamlLength} characters (${content.length} provided)`, ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.CONTENT_TOO_LARGE);
}
break;
case 'field':
if (content.length > limits.maxMetadataFieldLength) {
throw ErrorHandler.createError(`Field exceeds maximum length of ${limits.maxMetadataFieldLength} characters (${content.length} provided)`, ErrorCategory.VALIDATION_ERROR, ValidationErrorCodes.CONTENT_TOO_LARGE);
}
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
.replaceAll(CONTROL_CHARS_REGEX, '') // Remove control characters
.replaceAll(HTML_DANGEROUS_REGEX, '') // Remove HTML-dangerous characters
.replaceAll(SHELL_METACHAR_REGEX, '') // Remove shell metacharacters (expanded)
.replaceAll(RTL_ZEROWIDTH_REGEX, '') // Remove RTL override and zero-width chars
.substring(0, maxLength)
.trim();
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiSW5wdXRWYWxpZGF0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VjdXJpdHkvSW5wdXRWYWxpZGF0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFFSCxPQUFPLEtBQUssSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUM3QixPQUFPLEVBQUUsZUFBZSxFQUFFLG1CQUFtQixFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDdEUsaUZBQWlGO0FBQ2pGLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUNyRCxPQUFPLEVBQUUsWUFBWSxFQUFFLGFBQWEsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ3ZFLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBRTlELHFEQUFxRDtBQUNyRCxzRUFBc0U7QUFDdEUscUdBQXFHO0FBQ3JHLE1BQU0sbUJBQW1CLEdBQUcsd0JBQXdCLENBQUMsQ0FBQyxxREFBcUQ7QUFDM0csTUFBTSxvQkFBb0IsR0FBRyxVQUFVLENBQUM7QUFDeEMsTUFBTSxvQkFBb0IsR0FBRyxvQkFBb0IsQ0FBQztBQUNsRCxNQUFNLDRCQUE0QixHQUFHLFlBQVksQ0FBQyxDQUFDLHFEQUFxRDtBQUN4RyxnRkFBZ0Y7QUFDaEYsK0hBQStIO0FBQy9ILE1BQU0sbUJBQW1CLEdBQUcseUNBQXlDLENBQUM7QUFDdEUsTUFBTSwwQkFBMEIsR0FBRyxtQkFBbUIsQ0FBQztBQUN2RCxNQUFNLDJCQUEyQixHQUFHLHNCQUFzQixDQUFDO0FBQzNELE1BQU0sVUFBVSxHQUFHLDhCQUE4QixDQUFDO0FBQ2xELE1BQU0sZ0JBQWdCLEdBQUcsWUFBWSxDQUFDO0FBQ3RDLE1BQU0sWUFBWSxHQUFHLG9CQUFvQixDQUFDO0FBQzFDLE1BQU0sY0FBYyxHQUFHLGdCQUFnQixDQUFDO0FBQ3hDLE1BQU0sd0JBQXdCLEdBQUcsZ0JBQWdCLENBQUM7QUFDbEQsTUFBTSwyQkFBMkIs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QUFDO1FBQ3hDLE9BQU8sRUFBRSxDQUFDO0lBQ1osQ0FBQztJQUVELDJEQUEyRDtJQUMzRCxPQUFPLEtBQUs7U0FDVCxVQUFVLENBQUMsbUJBQW1CLEVBQUUsRUFBRSxDQUFDLENBQUMsNEJBQTRCO1NBQ2hFLFVBQVUsQ0FBQyxvQkFBb0IsRUFBRSxFQUFFLENBQUMsQ0FBQyxtQ0FBbUM7U0FDeEUsVUFBVSxDQUFDLG9CQUFvQixFQUFFLEVBQUUsQ0FBQyxDQUFDLHlDQUF5QztTQUM5RSxVQUFVLENBQUMsbUJBQW1CLEVBQUUsRUFBRSxDQUFDLENBQUMsMkNBQTJDO1NBQy9FLFNBQVMsQ0FBQyxDQUFDLEVBQUUsU0FBUyxDQUFDO1NBQ3ZCLElBQUksRUFBRSxDQUFDO0FBQ1osQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogSW5wdXQgdmFsaWRhdGlvbiBhbmQgc2FuaXRpemF0aW9uIGZ1bmN0aW9uc1xuICovXG5cbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgeyBTRUNVUklUWV9MSU1JVFMsIFZBTElEQVRJT05fUEFUVEVSTlMgfSBmcm9tICcuL2NvbnN0YW50cy5qcyc7XG4vLyBWQUxJRF9DQVRFR09SSUVTIGltcG9ydCByZW1vdmVkIOKAlCBjYXRlZ29yaWVzIGFyZSBkZXByZWNhdGVkIChzZWUgY29uc3RhbnRzLnRzKVxuaW1wb3J0IHsgUmVnZXhWYWxpZGF0b3IgfSBmcm9tICcuL3JlZ2V4VmFsaWRhdG9yLmpzJztcbmltcG9ydCB7IEVycm9ySGFuZGxlciwgRXJyb3JDYXRlZ29yeSB9IGZyb20gJy4uL3V0aWxzL0Vycm9ySGFuZGxlci5qcyc7XG5pbXBvcnQgeyBWYWxpZGF0aW9uRXJyb3JDb2RlcyB9IGZyb20gJy4uL3V0aWxzL2Vycm9yQ29kZXMuanMnO1xuXG4vLyBQcmUtY29tcGlsZWQgcmVnZXggcGF0dGVybnMgZm9yIGJldHRlciBwZXJmb3JtYW5jZVxuLy8gVGhlc2UgcGF0dGVybnMgYXJlIHVzZWQgcmVwZWF0ZWRseSBhbmQgYmVuZWZpdCBmcm9tIHByZS1jb21waWxhdGlvblxuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnRyb2wtcmVnZXggLS0gSW50ZW50aW9uYWxseSBtYXRjaGluZyBjb250cm9sIGNoYXJzIGZvciBzYW5pdGl6YXRpb25cbmNvbnN0IENPTlRST0xfQ0hBUlNfUkVHRVggPSAvW1xcdTAwMDAtXFx1MDAxRlxcdTAwN0ZdL2c7IC8vIE5PU09OQVIgLSBSZW1vdmluZyBjb250cm9sIGNoYXJhY3RlcnMgZm9yIHNlY3VyaXR5XG5jb25zdCBIVE1MX0RBTkdFUk9VU19SRUdFWCA9IC9bPD4nXCImXS9nO1xuY29uc3QgU0hFTExfTUVUQUNIQVJfUkVHRVggPSAvWzsmfGAkKCkhXFxcXH4qP3t9XS9nO1xuY29uc3QgU0hFTExfTUVUQUNIQVJfRElTUExBWV9SRUdFWCA9IC9bOyZ8YCQoKV0vZzsgLy8gQ29yZSBzaGVsbCBtZXRhY2hhcmFjdGVycyBmb3IgZGlzcGxheSBzYW5pdGl6YXRpb25cbi8vIEVuaGFuY2VkIHRvIGluY2x1ZGUgYWxsIGNvbW1vbiB6ZXJvLXdpZHRoIGFuZCBkaXJlY3Rpb25hbCBvdmVycmlkZSBjaGFyYWN0ZXJzXG4vLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tbWlzbGVhZGluZy1jaGFyYWN0ZXItY2xhc3MgLS0gSW50ZW50aW9uYWxseSB1c2luZyBVbmljb2RlIGNoYXJhY3RlcnMgZm9yIHplcm8td2lkdGggc2FuaXRpemF0aW9uXG5jb25zdCBSVExfWkVST1dJRFRIX1JFR0VYID0gL1tcXHUyMDBCXFx1MjAwQ1xcdTIwMERcXHUyMDYwXFx1MjAyRVxcdUZFRkZdL2c7XG5jb25zdCBDT0xMRUNUSU9OX1BBVEhfQ0hBUl9SRUdFWCA9IC9bYS16QS1aMC05XFwvXFwtXy5dLztcbmNvbnN0IFZBTElEX0NPTExFQ1RJT05fUEFUSF9SRUdFWCA9IC9eW2EtekEtWjAtOVxcL1xcLV8uXSokLztcbmNvbnN0IElQVjRfUkVHRVggPSAvXihcXGQrKVxcLihcXGQrKVxcLihcXGQrKVxcLihcXGQrKSQvO1xuY29uc3QgREVDSU1BTF9JUF9SRUdFWCA9IC9eXFxkezgsMTB9JC87XG5jb25zdCBIRVhfSVBfUkVHRVggPSAvXjB4WzAtOWEtZl17MSw4fSQvaTtcbmNvbnN0IE9DVEFMX0lQX1JFR0VYID0gL14wWzAtN117OCwxMX0kLztcbmNvbnN0IEZJTEVOQU1FX0RBTkdFUk9VU19SRUdFWCA9IC9bXFwvXFxcXDoqP1wiPD58XS9nO1xuY29uc3QgRklMRU5BTUVfTEVBRElOR19ET1RTX1JFR0VYID0gL15cXC4rLztcbmNvbnN0IFVSTF9QTFVTX0RFQ09ERV9SRUdFWCA9IC9cXCsvZztcblxuLyoqXG4gKiBFbmhhbmNlZCBpbnB1dCB2YWxpZGF0aW9uIGZvciBNQ1AgdG9vbHNcbiAqL1xuZXhwb3J0IGNsYXNzIE1DUElucHV0VmFsaWRhdG9yIHtcbiAgLyoqXG4gICAqIFZhbGlkYXRlIGEgcGVyc29uYSBpZGVudGlmaWVyIChuYW1lIG9yIGZpbGVuYW1lKVxuICAgKi9cbiAgc3RhdGljIHZhbGlkYXRlUGVyc29uYUlkZW50aWZpZXIoaWRlbnRpZmllcjogc3RyaW5nKTogc3RyaW5nIHtcbiAgICBpZiAoIWlkZW50aWZpZXIgfHwgdHlwZW9mIGlkZW50aWZpZXIgIT09ICdzdHJpbmcnKSB7XG4gICAgICB0aHJvdyBFcnJvckhhbmRsZXIuY3JlYXRlRXJyb3IoJ1BlcnNvbmEgaWRlbnRpZmllciBtdXN0IGJlIGEgbm9uLWVtcHR5IHN0cmluZycsIEVycm9yQ2F0ZWdvcnkuVkFMSURBVElPTl9FUlJPUiwgVmFsaWRhdGlvbkVycm9yQ29kZXMuSU5WQUxJRF9QRVJTT05BX0lEKTtcbiAgICB9XG5cbiAgICBpZiAoaWRlbnRpZmllci5sZW5ndGggPiAxMDApIHtcbiAgICAgIHRocm93IEVycm9ySGFuZGxlci5jcmVhdGVFcnJvcignUGVyc29uYSBpZGVudGlmaWVyIHRvbyBsb25nIChtYXggMTAwIGNoYXJhY3RlcnMpJywgRXJyb3JDYXRlZ29yeS5WQUxJREFUSU9OX0VSUk9SLCBWYWxpZGF0aW9uRXJyb3JDb2Rlcy5JTlZBTElEX0xFTkdUSCk7XG4gICAgfVxuXG4gICAgLy8gQWxsb3cgcGVyc29uYSBuYW1lcyBhbmQgZmlsZW5hbWVzXG4gICAgY29uc3Qgc2FuaXRpemVkID0gc2FuaXRpemVJbnB1dChpZGVudGlmaWVyLCAxMDApO1xuICAgIGlmICghc2FuaXRpemVkKSB7XG4gICAgICB0aHJvdyBFcnJvckhhbmRsZXIuY3JlYXRlRXJyb3IoJ1BlcnNvbmEgaWRlbnRpZmllciBjb250YWlucyBvbmx5IGludmFsaWQgY2hhcmFjdGVycycsIEVycm9yQ2F0ZWdvcnkuVkFMSURBVElPTl9FUlJPUiwgVmFsaWRhdGlvbkVycm9yQ29kZXMuSU5WQUxJRF9QRVJTT05BX0lEKTtcbiAgICB9XG5cbiAgICByZXR1cm4gc2FuaXRpemVkO1xuICB9XG5cbiAgLyoqXG4gICAqIFZhbGlkYXRl