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.

442 lines 64.2 kB
/** * Skill element class implementing IElement interface. * Represents a discrete capability for specific tasks. * * SECURITY FIXES IMPLEMENTED (PR #319): * 1. CRITICAL: Added comprehensive input validation for all skill parameters * 2. MEDIUM: Implemented Unicode normalization to prevent homograph attacks * 3. MEDIUM: Added audit logging for security events via SecurityMonitor * 4. MEDIUM: Implemented memory management to prevent unbounded growth * 5. MEDIUM: Added XSS protection through input sanitization */ import { BaseElement, normalizeVersion } from '../BaseElement.js'; import { ElementType } from '../../portfolio/types.js'; import { logger } from '../../utils/logger.js'; import { sanitizeInput } from '../../security/InputValidator.js'; import { UnicodeValidator } from '../../security/validators/unicodeValidator.js'; import { SecurityMonitor } from '../../security/securityMonitor.js'; import { ContentValidator } from '../../security/contentValidator.js'; import { SECURITY_LIMITS } from '../../security/constants.js'; export class Skill extends BaseElement { // instructions and content inherited from BaseElement (v2.0 dual-field architecture) parameters = new Map(); // SECURITY FIX #4: Memory management constants to prevent unbounded growth // Previously: No limits on parameter storage, could lead to memory exhaustion // Now: Enforced limits on both count and size MAX_PARAMETER_COUNT = 100; MAX_PARAMETER_SIZE = 10000; // Max size per parameter value // Store MetadataService for clone operation metadataService; constructor(metadata, instructions = '', metadataService, content = '') { // SECURITY FIX #2: Validate and sanitize ALL metadata fields // Previously: metadata was used directly without validation // Now: All string inputs are Unicode normalized and sanitized to prevent: // - XSS attacks through malicious metadata // - Unicode homograph attacks // - Buffer overflow attempts const sanitizedMetadata = { ...metadata, name: metadata.name ? sanitizeInput(UnicodeValidator.normalize(metadata.name).normalizedContent, 100) : undefined, description: metadata.description ? sanitizeInput(UnicodeValidator.normalize(metadata.description).normalizedContent, 500) : undefined }; super(ElementType.SKILL, sanitizedMetadata, metadataService); this.metadataService = metadataService; // Ensure instructions is always a string, even if empty // Use ContentValidator for multi-line content to preserve formatting (newlines, tabs) // while still detecting prompt injection attacks if (instructions && instructions.trim()) { const contentValidation = ContentValidator.validateAndSanitize(instructions, { maxLength: SECURITY_LIMITS.MAX_CONTENT_LENGTH, contentContext: 'skill' }); this.instructions = contentValidation.sanitizedContent || ''; } else { this.instructions = ''; } this.content = content; // Ensure skill-specific metadata this.metadata = { ...this.metadata, languages: metadata.languages || [], complexity: metadata.complexity || 'beginner', domains: metadata.domains || [], prerequisites: metadata.prerequisites || [], parameters: metadata.parameters || [], examples: metadata.examples || [], proficiency_level: metadata.proficiency_level || 0 }; // Validate parameter definitions if (this.metadata.parameters) { this.metadata.parameters = this.metadata.parameters.map(param => { const sanitized = { ...param, name: sanitizeInput(UnicodeValidator.normalize(param.name).normalizedContent, 50), description: sanitizeInput(UnicodeValidator.normalize(param.description).normalizedContent, SECURITY_LIMITS.MAX_DOCUMENTATION_FIELD_LENGTH) }; // Only include options if they exist (avoid undefined) if (param.options) { sanitized.options = param.options.map(opt => sanitizeInput(opt, 100)); } return sanitized; }); } // Initialize parameter values with defaults this.initializeParameters(); } /** * Initialize parameters with default values */ initializeParameters() { if (this.metadata.parameters) { this.metadata.parameters.forEach(param => { if (param.default !== undefined) { this.parameters.set(param.name, param.default); } }); } } /** * Set a parameter value * SECURITY FIX #1: Comprehensive input validation for all skill parameters * Previously: Parameters were set without validation, creating security risks * Now: Full validation pipeline including sanitization, type checking, and size limits */ setParameter(name, value) { // CRITICAL FIX: Sanitize parameter name to prevent injection attacks const sanitizedName = sanitizeInput(name, 50); const param = this.metadata.parameters?.find(p => p.name === sanitizedName); if (!param) { // SECURITY FIX #3: Log audit events for security monitoring // This helps detect potential attack patterns or misconfigurations SecurityMonitor.logSecurityEvent({ type: 'YAML_PARSING_WARNING', severity: 'MEDIUM', source: 'Skill.setParameter', details: `Attempt to set unknown parameter: ${sanitizedName} for skill: ${this.metadata.name}` }); throw new Error(`Parameter '${sanitizedName}' not found in skill definition`); } // CRITICAL FIX: Sanitize and validate the value based on type let sanitizedValue = value; if (param.type === 'string') { // SECURITY FIX #2 & #5: Normalize Unicode and sanitize string values // Previously: String values were used directly without validation // Now: Full validation pipeline: // 1. Unicode normalization prevents homograph attacks // 2. sanitizeInput() strips dangerous HTML/JS content // 3. Length limits prevent buffer overflow attacks const normalized = UnicodeValidator.normalize(String(value)); sanitizedValue = sanitizeInput(normalized.normalizedContent, param.max || 1000); // SECURITY FIX #5: Additional XSS protection // Check for common XSS patterns even after sanitization // This provides defense-in-depth against sophisticated attacks if (sanitizedValue.includes('<script') || sanitizedValue.includes('javascript:')) { // SECURITY FIX #3: Log high-severity security events SecurityMonitor.logSecurityEvent({ type: 'CONTENT_INJECTION_ATTEMPT', severity: 'HIGH', source: 'Skill.setParameter', details: `Potential XSS attempt in skill parameter: ${sanitizedName} for skill: ${this.metadata.name}` }); throw new Error('Invalid characters in parameter value'); } } // Type validation if (!this.validateParameterValue(param, sanitizedValue)) { throw new Error(`Invalid value for parameter '${sanitizedName}': expected ${param.type}`); } // SECURITY FIX #4: Memory management - check parameter count // Previously: No limits on parameter storage, could lead to memory exhaustion attacks // Now: Enforced limits prevent attackers from consuming unlimited memory if (this.parameters.size >= this.MAX_PARAMETER_COUNT && !this.parameters.has(sanitizedName)) { // SECURITY FIX #3: Log rate limit violations for security monitoring SecurityMonitor.logSecurityEvent({ type: 'RATE_LIMIT_EXCEEDED', severity: 'MEDIUM', source: 'Skill.setParameter', details: `Parameter limit exceeded for skill: ${this.metadata.name}. Max: ${this.MAX_PARAMETER_COUNT}` }); throw new Error(`Parameter limit exceeded. Maximum ${this.MAX_PARAMETER_COUNT} parameters allowed`); } // SECURITY FIX #4: Check parameter value size for strings // This prevents attackers from storing massive strings that could exhaust memory if (param.type === 'string' && sanitizedValue.length > this.MAX_PARAMETER_SIZE) { throw new Error(`Parameter value too large. Maximum ${this.MAX_PARAMETER_SIZE} characters allowed`); } this.parameters.set(sanitizedName, sanitizedValue); logger.debug(`Set parameter ${sanitizedName} = ${sanitizedValue} for skill ${this.metadata.name}`); } /** * Get a parameter value */ getParameter(name) { return this.parameters.get(name); } /** * Get all parameters as object */ getAllParameters() { return Object.fromEntries(this.parameters); } /** * Validate parameter value against its definition */ validateParameterValue(param, value) { switch (param.type) { case 'string': return typeof value === 'string'; case 'number': { const num = Number(value); if (Number.isNaN(num)) return false; if (param.min !== undefined && num < param.min) return false; if (param.max !== undefined && num > param.max) return false; return true; } case 'boolean': return typeof value === 'boolean'; case 'enum': return param.options?.includes(String(value)) || false; default: return true; } } /** * Execute the skill with current parameters */ async execute(input) { logger.info(`Executing skill: ${this.metadata.name}`); // Validate required parameters const missingRequired = this.metadata.parameters ?.filter(p => p.required && !this.parameters.has(p.name)) .map(p => p.name) || []; if (missingRequired.length > 0) { throw new Error(`Missing required parameters: ${missingRequired.join(', ')}`); } // Skills don't have built-in execution logic - this would be implemented // by specific skill types or through a plugin system logger.debug(`Skill ${this.metadata.name} executed with parameters:`, this.getAllParameters()); return { skill: this.metadata.name, parameters: this.getAllParameters(), input, executed_at: new Date().toISOString() }; } /** * Skill-specific validation */ validate() { const result = super.validate(); // Initialize arrays if not present if (!result.errors) result.errors = []; if (!result.warnings) result.warnings = []; // Instructions should not be empty if (!this.instructions || this.instructions.trim().length === 0) { result.errors.push({ field: 'instructions', message: 'Skill instructions cannot be empty', code: 'EMPTY_INSTRUCTIONS' }); } // Validate complexity level const validComplexity = ['beginner', 'intermediate', 'advanced', 'expert']; if (this.metadata.complexity && !validComplexity.includes(this.metadata.complexity)) { result.errors.push({ field: 'complexity', message: `Complexity must be one of: ${validComplexity.join(', ')}`, code: 'INVALID_COMPLEXITY' }); } // Validate proficiency level if (this.metadata.proficiency_level !== undefined) { if (this.metadata.proficiency_level < 0 || this.metadata.proficiency_level > 100) { result.errors.push({ field: 'proficiency_level', message: 'Proficiency level must be between 0 and 100', code: 'INVALID_PROFICIENCY' }); } } // Validate parameters if (this.metadata.parameters) { this.metadata.parameters.forEach((param, index) => { if (!param.name || param.name.trim() === '') { result.errors.push({ field: `parameters[${index}].name`, message: 'Parameter name is required', code: 'MISSING_PARAMETER_NAME' }); } if (param.type === 'enum' && (!param.options || param.options.length === 0)) { result.errors.push({ field: `parameters[${index}].options`, message: 'Enum parameter must have options defined', code: 'MISSING_ENUM_OPTIONS' }); } }); } // Warnings for best practices if (!this.metadata.domains || this.metadata.domains.length === 0) { result.warnings.push({ field: 'domains', message: 'Consider adding domain categories for better organization', severity: 'low' }); } if (!this.metadata.examples || this.metadata.examples.length === 0) { result.warnings.push({ field: 'examples', message: 'Adding usage examples improves skill usability', severity: 'medium' }); } // Update the valid flag based on final errors result.valid = (result.errors?.length || 0) === 0; return result; } /** * Serialize to JSON format for internal use and testing */ serializeToJSON() { const data = { id: this.id, type: this.type, version: this.version, metadata: this.metadata, instructions: this.instructions, content: this.content, parameters: this.getAllParameters(), references: this.references, extensions: this.extensions, ratings: this.ratings }; return JSON.stringify(data, null, 2); } /** * Get content for serialization */ getContent() { let content = `# ${this.metadata.name}\n\n`; content += `${this.metadata.description}\n\n`; if (this.instructions) { content += `## Instructions\n\n${this.instructions}\n\n`; } const params = this.getAllParameters(); if (params && Object.keys(params).length > 0) { content += `## Parameters\n\n`; for (const [key, value] of Object.entries(params)) { content += `- **${key}**: ${JSON.stringify(value)}\n`; } content += '\n'; } return content; } /** * Serialize skill to markdown format with YAML frontmatter * FIX: Changed from JSON to markdown for GitHub portfolio compatibility */ serialize() { // Add skill-specific data to extensions for frontmatter const originalExtensions = this.extensions; this.extensions = { ...originalExtensions, instructions: this.instructions, parameters: this.getAllParameters() }; // Use base class serialize which now outputs markdown const result = super.serialize(); // Restore original extensions this.extensions = originalExtensions; return result; } /** * Deserialize skill from JSON format */ deserialize(data) { try { const parsed = JSON.parse(data); // Update metadata this.metadata = { ...this.metadata, ...parsed.metadata }; // Update other properties this.instructions = parsed.instructions || ''; this.content = parsed.content || ''; this.references = parsed.references || []; this.extensions = parsed.extensions || {}; this.ratings = parsed.ratings || this.ratings; // Update ID and version if provided if (parsed.id) this.id = parsed.id; if (parsed.version != null) this.version = normalizeVersion(String(parsed.version)); // Restore parameters if (parsed.parameters) { this.parameters.clear(); Object.entries(parsed.parameters).forEach(([key, value]) => { this.parameters.set(key, value); }); } this._isDirty = true; logger.debug(`Deserialized skill: ${this.metadata.name}`); } catch (error) { logger.error(`Failed to deserialize skill: ${error}`); throw new Error(`Deserialization failed: ${error}`); } } /** * Skill activation lifecycle */ async activate() { logger.info(`Activating skill: ${this.metadata.name} (${this.id})`); // Validate that all required parameters are set const validation = this.validate(); if (!validation.valid) { throw new Error(`Cannot activate skill with validation errors: ${validation.errors?.map(e => e.message).join(', ')}`); } await super.activate?.(); } /** * Clone the skill with different parameters */ clone(newParameters) { const cloned = new Skill(this.metadata, this.instructions, this.metadataService, this.content); // Copy current parameters this.parameters.forEach((value, key) => { cloned.parameters.set(key, value); }); // Override with new parameters if provided if (newParameters) { Object.entries(newParameters).forEach(([key, value]) => { cloned.setParameter(key, value); }); } return cloned; } /** * Skill deactivation lifecycle * SECURITY FIX #4: Automatically clears parameters on deactivation * to prevent memory leaks in long-running processes */ async deactivate() { logger.info(`Deactivating skill: ${this.metadata.name} (${this.id})`); // SECURITY FIX #4: Clear parameters to free memory and prevent leaks // This ensures skills don't retain data after deactivation this.clearParameters(); await super.deactivate?.(); } /** * Clear all parameters (for memory management) * SECURITY FIX #4: Added method to clear parameters and prevent memory leaks * Previously: Parameters accumulated indefinitely in long-running processes * Now: Provides explicit cleanup mechanism called during deactivation */ clearParameters() { this.parameters.clear(); logger.debug(`Cleared all parameters for skill: ${this.metadata.name}`); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU2tpbGwuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvZWxlbWVudHMvc2tpbGxzL1NraWxsLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7O0dBVUc7QUFFSCxPQUFPLEVBQUUsV0FBVyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFFbEUsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ3ZELE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUMvQyxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sa0NBQWtDLENBQUM7QUFDakUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sK0NBQStDLENBQUM7QUFDakYsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLG1DQUFtQyxDQUFDO0FBQ3BFLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLG9DQUFvQyxDQUFDO0FBQ3RFLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQW9DOUQsTUFBTSxPQUFPLEtBQU0sU0FBUSxXQUFXO0lBRXBDLHFGQUFxRjtJQUM5RSxVQUFVLEdBQXFCLElBQUksR0FBRyxFQUFFLENBQUM7SUFFaEQsMkVBQTJFO0lBQzNFLDhFQUE4RTtJQUM5RSw4Q0FBOEM7SUFDN0IsbUJBQW1CLEdBQUcsR0FBRyxDQUFDO0lBQzFCLGtCQUFrQixHQUFHLEtBQUssQ0FBQyxDQUFDLCtCQUErQjtJQUU1RSw0Q0FBNEM7SUFDM0IsZUFBZSxDQUFrQjtJQUVsRCxZQUFZLFFBQWdDLEVBQUUsZUFBdUIsRUFBRSxFQUFFLGVBQWdDLEVBQUUsVUFBa0IsRUFBRTtRQUM3SCw2REFBNkQ7UUFDN0QsNERBQTREO1FBQzVELDBFQUEwRTtRQUMxRSwyQ0FBMkM7UUFDM0MsOEJBQThCO1FBQzlCLDZCQUE2QjtRQUM3QixNQUFNLGlCQUFpQixHQUFHO1lBQ3hCLEdBQUcsUUFBUTtZQUNY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QUFFO1lBQ3JDLE1BQU0sQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsQ0FBQztRQUNwQyxDQUFDLENBQUMsQ0FBQztRQUVILDJDQUEyQztRQUMzQyxJQUFJLGFBQWEsRUFBRSxDQUFDO1lBQ2xCLE1BQU0sQ0FBQyxPQUFPLENBQUMsYUFBYSxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLEVBQUUsRUFBRTtnQkFDckQsTUFBTSxDQUFDLFlBQVksQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLENBQUM7WUFDbEMsQ0FBQyxDQUFDLENBQUM7UUFDTCxDQUFDO1FBRUQsT0FBTyxNQUFNLENBQUM7SUFDaEIsQ0FBQztJQUVEOzs7O09BSUc7SUFDYSxLQUFLLENBQUMsVUFBVTtRQUM5QixNQUFNLENBQUMsSUFBSSxDQUFDLHVCQUF1QixJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksS0FBSyxJQUFJLENBQUMsRUFBRSxHQUFHLENBQUMsQ0FBQztRQUV0RSxxRUFBcUU7UUFDckUsMkRBQTJEO1FBQzNELElBQUksQ0FBQyxlQUFlLEVBQUUsQ0FBQztRQUV2QixNQUFNLEtBQUssQ0FBQyxVQUFVLEVBQUUsRUFBRSxDQUFDO0lBQzdCLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNILGVBQWU7UUFDYixJQUFJLENBQUMsVUFBVSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ3hCLE1BQU0sQ0FBQyxLQUFLLENBQUMscUNBQXFDLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUMxRSxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFNraWxsIGVsZW1lbnQgY2xhc3MgaW1wbGVtZW50aW5nIElFbGVtZW50IGludGVyZmFjZS5cbiAqIFJlcHJlc2VudHMgYSBkaXNjcmV0ZSBjYXBhYmlsaXR5IGZvciBzcGVjaWZpYyB0YXNrcy5cbiAqIFxuICogU0VDVVJJVFkgRklYRVMgSU1QTEVNRU5URUQgKFBSICMzMTkpOlxuICogMS4gQ1JJVElDQUw6IEFkZGVkIGNvbXByZWhlbnNpdmUgaW5wdXQgdmFsaWRhdGlvbiBmb3IgYWxsIHNraWxsIHBhcmFtZXRlcnNcbiAqIDIuIE1FRElVTTogSW1wbGVtZW50ZWQgVW5pY29kZSBub3JtYWxpemF0aW9uIHRvIHByZXZlbnQgaG9tb2dyYXBoIGF0dGFja3NcbiAqIDMuIE1FRElVTTogQWRkZWQgYXVkaXQgbG9nZ2luZyBmb3Igc2VjdXJpdHkgZXZlbnRzIHZpYSBTZWN1cml0eU1vbml0b3JcbiAqIDQuIE1FRElVTTogSW1wbGVtZW50ZWQgbWVtb3J5IG1hbmFnZW1lbnQgdG8gcHJldmVudCB1bmJvdW5kZWQgZ3Jvd3RoXG4gKiA1LiBNRURJVU06IEFkZGVkIFhTUyBwcm90ZWN0aW9uIHRocm91Z2ggaW5wdXQgc2FuaXRpemF0aW9uXG4gKi9cblxuaW1wb3J0IHsgQmFzZUVsZW1lbnQsIG5vcm1hbGl6ZVZlcnNpb24gfSBmcm9tICcuLi9CYXNlRWxlbWVudC5qcyc7XG5pbXBvcnQgeyBJRWxlbWVudCwgSUVsZW1lbnRNZXRhZGF0YSwgRWxlbWVudFZhbGlkYXRpb25SZXN1bHQgfSBmcm9tICcuLi8uLi90eXBlcy9lbGVtZW50cy9pbmRleC5qcyc7XG5pbXBvcnQgeyBFbGVtZW50VHlwZSB9IGZyb20gJy4uLy4uL3BvcnRmb2xpby90eXBlcy5qcyc7XG5pbXBvcnQgeyBsb2dnZXIgfSBmcm9tICcuLi8uLi91dGlscy9sb2dnZXIuanMnO1xuaW1wb3J0IHsgc2FuaXRpemVJbnB1dCB9IGZyb20gJy4uLy4uL3NlY3VyaXR5L0lucHV0VmFsaWRhdG9yLmpzJztcbmltcG9ydCB7IFVuaWNvZGVWYWxpZGF0b3IgfSBmcm9tICcuLi8uLi9zZWN1cml0eS92YWxpZGF0b3JzL3VuaWNvZGVWYWxpZGF0b3IuanMnO1xuaW1wb3J0IHsgU2VjdXJpdHlNb25pdG9yIH0gZnJvbSAnLi4vLi4vc2VjdXJpdHkvc2VjdXJpdHlNb25pdG9yLmpzJztcbmltcG9ydCB7IENvbnRlbnRWYWxpZGF0b3IgfSBmcm9tICcuLi8uLi9zZWN1cml0eS9jb250ZW50VmFsaWRhdG9yLmpzJztcbmltcG9ydCB7IFNFQ1VSSVRZX0xJTUlUUyB9IGZyb20gJy4uLy4uL3NlY3VyaXR5L2NvbnN0YW50cy5qcyc7XG5pbXBvcnQgeyBNZXRhZGF0YVNlcnZpY2UgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9NZXRhZGF0YVNlcnZpY2UuanMnO1xuXG4vLyBFeHRlbmQgSUVsZW1lbnRNZXRhZGF0YSB3aXRoIHNraWxsLXNwZWNpZmljIGZpZWxkc1xuZXhwb3J0IGludGVyZmFjZSBTa2lsbE1ldGFkYXRhIGV4dGVuZHMgSUVsZW1lbnRNZXRhZGF0YSB7XG4gIHR5cGU/OiBFbGVtZW50VHlwZS5TS0lMTDsgICAgICAgICAgICAgICAgLy8gU2tpbGwgdHlwZSBjb25zdHJhaW50IGZvciB0eXBlIHNhZmV0eVxuICBsYW5ndWFnZXM/OiBzdHJpbmdbXTsgICAgICAgICAgIC8vIFByb2dyYW1taW5nL3Nwb2tlbiBsYW5ndWFnZXMgdGhpcyBza2lsbCB3b3JrcyB3aXRoXG4gIGNvbXBsZXhpdHk/OiAnYmVnaW5uZXInIHwgJ2ludGVybWVkaWF0ZScgfCAnYWR2YW5jZWQnIHwgJ2V4cGVydCc7XG4gIGRvbWFpbnM/OiBzdHJpbmdbXTsgICAgICAgICAgICAgLy8gRG9tYWluIGNhdGVnb3JpZXMgKGUuZy4sIHdlYi1kZXYsIGRhdGEtc2NpZW5jZSwgd3JpdGluZylcbiAgcHJlcmVxdWlzaXRlcz86IHN0cmluZ1tdOyAgICAgICAvLyBSZXF1aXJlZCBrbm93bGVkZ2Ugb3Igb3RoZXIgc2tpbGxzXG4gIHBhcmFtZXRlcnM/OiBTa2lsbFBhcmFtZXRlcltdOyAgLy8gQ29uZmlndXJhYmxlIHBhcmFtZXRlcnNcbiAgZXhhbXBsZXM/OiBTa2lsbEV4YW1wbGVbXTsgICAgICAvLyBVc2FnZSBleGFtcGxlc1xuICBjZXJ0aWZpY2F0aW9uPzogc3RyaW5nOyAgICAgICAgIC8vIEV4dGVybmFsIGNlcnRpZmljYXRpb24gb3IgdmFsaWRhdGlvblxuICBwcm9maWNpZW5jeV9sZXZlbD86IG51bWJlcjsgICAgIC8vIDEtMTAwIHByb2ZpY2llbmN5IGxldmVsXG4gIHRyaWdnZXJzPzogc3RyaW5nW107ICAgICAgICAgICAgLy8gQWN0aW9uIHZlcmJzIHRoYXQgdHJpZ2dlciB0aGlzIHNraWxsIChlLmcuLCBcImFuYWx5emVcIiwgXCJ2YWxpZGF0ZVwiLCBcIm9wdGltaXplXCIpXG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgU2tpbGxQYXJhbWV0ZXIge1xuICBuYW1lOiBzdHJpbmc7XG4gIHR5cGU6ICdzdHJpbmcnIHwgJ251bWJlcicgfCAnYm9vbGVhbicgfCAnZW51bSc7XG4gIGRlc2NyaXB0aW9uOiBzdHJpbmc7XG4gIHJlcXVpcmVkPzogYm9vbGVhbjtcbiAgZGVmYXVsdD86IGFueTtcbiAgb3B0aW9ucz86IHN0cmluZ1tdOyAgLy8gRm9yIGVudW0gdHlwZVxuICBtaW4/OiBudW1iZXI7ICAgICAgICAvLyBGb3IgbnVtYmVyIHR5cGVcbiAgbWF4PzogbnVtYmVyOyAgICAgICAgLy8gRm9yIG51bWJlciB0eXBlXG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgU2tpbGxFeGFtcGxlIHtcbiAgdGl0bGU6IHN0cmluZztcbiAgZGVzY3JpcHRpb246IHN0cmluZztcbiAgaW5wdXQ/OiBhbnk7XG4gIG91dHB1dD86IGFueTtcbiAgY29kZT86IHN0cmluZztcbn1cblxuZXhwb3J0IGNsYXNzIFNraWxsIGV4dGVuZHMgQmFzZUVsZW1lbnQgaW1wbGVtZW50cyBJRWxlbWVudCB7XG4gIHB1YmxpYyBkZWNsYXJlIG1ldGFkYXRhOiBTa2lsbE1ldGFkYXRhO1xuICAvLyBpbnN0cnVjdGlvbnMgYW5kIGNvbnRlbnQgaW5oZXJpdGVkIGZyb20gQmFzZUVsZW1lbnQgKHYyLjAgZHVhbC1maWVsZCBhcmNoaXRlY3R1cmUpXG4gIHB1YmxpYyBwYXJhbWV0ZXJzOiBNYXA8c3RyaW5nLCBhbnk+ID0gbmV3IE1hcCgpO1xuXG4gIC8vIFNFQ1VSSVRZIEZJWCAjNDogTWVtb3J5IG1hbmFnZW1lbnQgY29uc3RhbnRzIHRvIHByZXZlbnQgdW5ib3VuZGVkIGdyb3d0aFxuICAvLyBQcmV2aW91c2x5OiBObyBsaW1pdHMgb24gcGFyYW1ldGVyIHN0b3JhZ2UsIGNvdWxkIGxlYWQgdG8gbWVtb3J5IGV4aGF1c3Rpb25cbiAgLy8gTm93OiBFbmZvcmNlZCBsaW1pdHMgb24gYm90aCBjb3VudCBhbmQgc2l6ZVxuICBwcml2YXRlIHJlYWRvbmx5IE1BWF9QQVJBTUVURVJfQ09VTlQgPSAxMDA7XG4gIHByaXZhdGUgcmVhZG9ubHkgTUFYX1BBUkFNRVRFUl9TSVpFID0gMTAwMDA7IC8vIE1heCBzaXplIHBlciBwYXJhbWV0ZXIgdmFsdWVcblxuICAvLyBTdG9yZSBNZXRhZGF0YVNlcnZpY2UgZm9yIGNsb25lIG9wZXJhdGlvblxuICBwcml2YXRlIHJlYWRvbmx5IG1ldGFkYXRhU2VydmljZTogTWV0YWRhdGFTZXJ2aWNlO1xuXG4gIGNvbnN0cnVjdG9yKG1ldGFkYXRhOiBQYXJ0aWFsPFNraWxsTWV0YWRhdGE+LCBpbnN0cnVjdGlvbnM6IHN0cmluZyA9ICcnLCBtZXRhZGF0YVNlcnZpY2U6IE1ldGFkYXRhU2VydmljZSwgY29udGVudDogc3RyaW5nID0gJycpIHtcbiAgICAvLyBTRUNVUklUWSBGSVggIzI6IFZhbGlkYXRlIGFuZCBzYW5pdGl6ZSBBTEwgbWV0YWRhdGEgZmllbGRzXG4gICAgLy8gUHJldmlvdXNseTogbWV0YWRhdGEgd2FzIHVzZWQgZGlyZWN0bHkgd2l0aG91dCB2YWxpZGF0aW9uXG4gICAgLy8gTm93OiBBbGwgc3RyaW5nIGlucHV0cyBhcmUgVW5pY29kZSBub3JtYWxpemVkIGFuZCBzYW5pdGl6ZWQgdG8gcHJldmVudDpcbiAgICAvLyAtIFhTUyBhdHRhY2tzIHRocm91Z2ggbWFsaWNpb3VzIG1ldGFkYXRhXG4gICAgLy8gLSBVbmljb2RlIGhvbW9ncmFwaCBhdHRhY2tzXG4gICAgLy8gLSBCdWZmZXIgb3ZlcmZsb3cgYXR0ZW1wdHNcbiAgICBjb25zdCBzYW5pdGl6ZWRNZXRhZGF0YSA9IHtcbiAgICAgIC4uLm1ldGFkYXRhLFxuICAgICAgbmFtZTogbWV0YWRhdGEubmFtZSA/IHNhbml0aXplSW5wdXQoVW5pY29kZVZhbGlkYXRvci5ub3JtYWxpemUobWV0YWRhdGEubmFtZSkubm9ybWFsaXplZENvbnRlbnQsIDEwMCkgOiB1bmRlZmluZWQsXG4gICAgICBkZXNjcmlwdGlvbjogbWV0YWRhdGEuZGVzY3JpcHRpb24gPyBzYW5pdGl6ZUlucHV0KFVuaWNvZGVWYWxpZGF0b3Iubm9ybWFsaXplKG1ldGFkYXRhLmRlc2NyaXB0aW9uKS5ub3JtYWxpemVkQ29udGVudCwgNTAwKSA6IHVuZGVmaW5lZFxuICAgIH07XG5cbiAgICBzdXBlcihFbGVtZW50VHlwZS5TS0lMTCwgc2FuaXRpemVkTWV0YWRhdGEsIG1ldGFkYXRhU2VydmljZSk7XG4gICAgdGhpcy5tZXRhZGF0YVNlcnZpY2UgPSBtZXRhZGF0YVNlcnZpY2U7XG4gICAgLy8gRW5zdXJlIGluc3RydWN0aW9ucyBpcyBhbHdheXMgYSBzdHJpbmcsIGV2ZW4gaWYgZW1wdHlcbiAgICAvLyBVc2UgQ29udGVudFZhbGlkYXRvciBmb3IgbXVsdGktbGluZSBjb250ZW50IHRvIHByZXNlcnZlIGZvcm1hdHRpbmcgKG5ld2xpbmVzLCB0YWJzKVxuICAgIC8vIHdoaWxlIHN0aWxsIGRldGVjdGluZyBwcm9tcHQgaW5qZWN0aW9uIGF0dGFja3NcbiAgICBpZiAoaW5zdHJ1Y3Rpb25zICYmIGluc3RydWN0aW9ucy50cmltKCkpIHtcbiAgICAgIGNvbnN0IGNvbnRlbnRWYWxpZGF0aW9uID0gQ29udGVudFZhbGlkYXRvci52YWxpZGF0ZUFuZFNhbml0aXplKGluc3RydWN0aW9ucywgeyBtYXhMZW5ndGg6IFNFQ1VSSVRZX0xJTUlUUy5NQVhfQ09OVEVOVF9MRU5HVEgsIGNvbnRlbnRDb250ZXh0OiAnc2tpbGwnIH0pO1xuICAgICAgdGhpcy5pbnN0cnVjdGlvbnMgPSBjb250ZW50VmFsaWRhdGlvbi5zYW5pdGl6ZWRDb250ZW50IHx8ICcnO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmluc3RydWN0aW9ucyA9ICcnO1xuICAgIH1cbiAgICB0aGlzLmNvbnRlbnQgPSBjb250ZW50O1xuXG4gICAgLy8gRW5zdXJlIHNraWxsLXNwZWNpZmljIG1ldGFkYXRhXG4gICAgdGhpcy5tZXRhZGF0YSA9IHtcbiAgICAgIC4uLnRoaXMubWV0YWRhdGEsXG4gICAgICBsYW5ndWFnZXM6IG1ldGFkYXRhLmxhbmd1YWdlcyB8fCBbXSxcbiAgICAgIGNvbXBsZXhpdHk6IG1ldGFkYXRhLmNvbXBsZXhpdHkgfHwgJ2JlZ2lubmVyJyxcbiAgICAgIGRvbWFpbnM6IG1ldGFkYXRhLmRvbWFpbnMgfHwgW10sXG4gICAgICBwcmVyZXF1aXNpdGVzOiBtZXRhZGF0YS5wcmVyZXF1aXNpdGVzIHx8IFtdLFxuICAgICAgcGFyYW1ldGVyczogbWV0YWRhdGEucGFyYW1ldGVycyB8fCBbXSxcbiAgICAgIGV4YW1wbGVzOiBtZXRhZGF0YS5leGFtcGxlcyB8fCBbXSxcbiAgICAgIHByb2ZpY2llbmN5X2xldmVsOiBtZXRhZGF0YS5wcm9maWNpZW5jeV9sZXZlbCB8fCAwXG4gICAgfTtcblxuICAgIC8vIFZhbGlkYXRlIHBhcmFtZXRlciBkZWZpbml0aW9uc1xuICAgIGlmICh0aGlzLm1ldGFkYXRhLnBhcmFtZXRlcnMpIHtcbiAgICAgIHRoaXMubWV0YWRhdGEucGFyYW1ldGVycyA9IHRoaXMubWV0YWRhdGEucGFyYW1ldGVycy5tYXAocGFyYW0gPT4ge1xuICAgICAgICBjb25zdCBzYW5pdGl6ZWQ6IFNraWxsUGFyYW1ldGVyID0ge1xuICAgICAgICAgIC4uLnBhcmFtLFxuICAgICAgICAgIG5hbWU6IHNhbml0aXplSW5wdXQoVW5pY29kZVZhbGlkYXRvci5ub3JtYWxpemUocGFyYW0ubmFtZSkubm9ybWFsaXplZENvbnRlbnQsIDUwKSxcbiAgICAgICAgICBkZXNjcmlwdGlvbjogc2FuaXRpemVJbnB1dChcbiAgICAgICAgICAgIFVuaWNvZGVWYWxpZGF0b3Iubm9ybWFsaXplKHBhcmFtLmRlc2NyaXB0aW9uKS5ub3JtYWxpemVkQ29udGVudCxcbiAgICAgICAgICAgIFNFQ1VSSVRZX0xJTUlUUy5NQVhfRE9DVU1FTlRBVElPTl9GSUVMRF9MRU5HVEhcbiAgICAgICAgICApXG4gICAgICAgIH07XG4gICAgICAgIC8vIE9ubHkgaW5jbHVkZSBvcHRpb25zIGlmIHRoZXkgZXhpc3QgKGF2b2lkIHVuZGVmaW5lZClcbiAgICAgICAgaWYgKHBhcmFtLm9wdGlvbnMpIHtcbiAgICAgICAgICBzYW5pdGl6ZWQub3B0aW9ucyA9IHBhcmFtLm9wdGlvbnMubWFwKG9wdCA9PiBzYW5pdGl6ZUlucHV0KG9wdCwgMTAwKSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHNhbml0aXplZDtcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIC8vIEluaXRpYWxpemUgcGFyYW1ldGVyIHZhbHVlcyB3aXRoIGRlZmF1bHRzXG4gICAgdGhpcy5pbml0aWFsaXplUGFyYW1ldGVycygpO1xuICB9XG5cbiAgLyoqXG4gICAqIEluaXRpYWxpemUgcGFyYW1ldGVycyB3aXRoIGRlZmF1bHQgdmFsdWVzXG4gICAqL1xuICBwcml2YXRlIGluaXRpYWxpemVQYXJhbWV0ZXJzKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLm1ldGFkYXRhLnBhcmFtZXRlcnMpIHtcbiAgICAgIHRoaXMubWV0YWRhdGEucGFyYW1ldGVycy5mb3JFYWNoKHBhcmFtID0+IHtcbiAgICAgICAgaWYgKHBhcmFtLmRlZmF1bHQgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgIHRoaXMucGFyYW1ldGVycy5zZXQocGFyYW0ubmFtZSwgcGFyYW0uZGVmYXVsdCk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBTZXQgYSBwYXJhbWV0ZXIgdmFsdWVcbiAgICogU0VDVVJJVFkgRklYICMxOiBDb21wcmVoZW5zaXZlIGlucHV0IHZhbGlkYXRpb24gZm9yIGFsbCBza2lsbCBwYXJhbWV0ZXJzXG4gICAqIFByZXZpb3VzbHk6IFBhcmFtZXRlcnMgd2VyZSBzZXQgd2l0aG91dCB2YWxpZGF0aW9uLCBjcmVhdGluZyBzZWN1cml0eSByaXNrc1xuICAgKiBOb3c6IEZ1bGwgdmFsaWRhdGlvbiBwaXBlbGluZSBpbmNsdWRpbmcgc2FuaXRpemF0aW9uLCB0eXBlIGNoZWNraW5nLCBhbmQgc2l6ZSBsaW1pdHNcbiAgICovXG4gIHNldFBhcmFtZXRlcihuYW1lOiBzdHJpbmcsIHZhbHVlOiBhbnkpOiB2b2lkIHtcbiAgICAvLyBDUklUSUNBTCBGSVg6IFNhbml0aXplIHBhcmFtZXRlciBuYW1lIHRvIHByZXZlbnQgaW5qZWN0aW9uIGF0dGFja3NcbiAgICBjb25zdCBzYW5pdGl6ZWROYW1lID0gc2FuaXRpemVJbnB1dChuYW1lLCA1MCk7XG4gICAgXG4gICAgY29uc3QgcGFyYW0gPSB0aGlzLm1ldGFkYXRhLnBhcmFtZXRlcnM/LmZpbmQocCA9PiBwLm5hbWUgPT09IHNhbml0aXplZE5hbWUpO1xuICAgIGlmICghcGFyYW0pIHtcbiAgICAgIC8vIFNFQ1VSSVRZIEZJWCAjMzogTG9nIGF1ZGl0IGV2ZW50cyBmb3Igc2VjdXJpdHkgbW9uaXRvcmluZ1xuICAgICAgLy8gVGhpcyBoZWxwcyBkZXRlY3QgcG90ZW50aWFsIGF0dGFjayBwYXR0ZXJucyBvciBtaXNjb25maWd1cmF0aW9uc1xuICAgICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgICB0eXBlOiAnWUFNTF9QQVJTSU5HX1dBUk5JTkcnLFxuICAgICAgICBzZXZlcml0eTogJ01FRElVTScsXG4gICAgICAgIHNvdXJjZTogJ1NraWxsLnNldFBhcmFtZXRlcicsXG4gICAgICAgIGRldGFpbHM6IGBBdHRlbXB0IHRvIHNldCB1bmtub3duIHBhcmFtZXRlcjogJHtzYW5pdGl6ZWROYW1lfSBmb3Igc2tpbGw6ICR7dGhpcy5tZXRhZGF0YS5uYW1lfWBcbiAgICAgIH0pO1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBQYXJhbWV0ZXIgJyR7c2FuaXRpemVkTmFtZX0nIG5vdCBmb3VuZCBpbiBza2lsbCBkZWZpbml0aW9uYCk7XG4gICAgfVxuXG4gICAgLy8gQ1JJVElDQUwgRklYOiBTYW5pdGl6ZSBhbmQgdmFsaWRhdGUgdGhlIHZhbHVlIGJhc2VkIG9uIHR5cGVcbiAgICBsZXQgc2FuaXRpemVkVmFsdWUgPSB2YWx1ZTtcbiAgICBcbiAgICBpZiAocGFyYW0udHlwZSA9PT0gJ3N0cmluZycpIHtcbiAgICAgIC8vIFNFQ1VSSVRZIEZJWCAjMiAmICM1OiBOb3JtYWxpemUgVW5pY29kZSBhbmQgc2FuaXRpemUgc3RyaW5nIHZhbHVlc1xuICAgICAgLy8gUHJldmlvdXNseTogU3RyaW5nIHZhbHVlcyB3ZXJlIHVzZWQgZGlyZWN0bHkgd2l0aG91dCB2YWxpZGF0aW9uXG4gICAgICAvLyBOb3c6IEZ1bGwgdmFsaWRhdGlvbiBwaXBlbGluZTpcbiAgICAgIC8vIDEuIFVuaWNvZGUgbm9ybWFsaXphdGlvbiBwcmV2ZW50cyBob21vZ3JhcGggYXR0YWNrc1xuICAgICAgLy8gMi4gc2FuaXRpemVJbnB1dCgpIHN0cmlwcyBkYW5nZXJvdXMgSFRNTC9KUyBjb250ZW50XG4gICAgICAvLyAzLiBMZW5ndGggbGltaXRzIHByZXZlbnQgYnVmZmVyIG92ZXJmbG93IGF0dGFja3NcbiAgICAgIGNvbnN0IG5vcm1hbGl6ZWQgPSBVbmljb2RlVmFsaWRhdG9yLm5vcm1hbGl6ZShTdHJpbmcodmFsdWUpKTtcbiAgICAgIHNhbml0aXplZFZhbHVlID0gc2FuaXRpemVJbnB1dChub3JtYWxpemVkLm5vcm1hbGl6ZWRDb250ZW50LCBwYXJhbS5tYXggfHwgMTAwMCk7XG4gICAgICBcbiAgICAgIC8vIFNFQ1VSSVRZIEZJWCAjNTogQWRkaXRpb25hbCBYU1MgcHJvdGVjdGlvblxuICAgICAgLy8gQ2hlY2sgZm9yIGNvbW1vbiBYU1MgcGF0dGVybnMgZXZlbiBhZnRlciBzYW5pdGl6YXRpb25cbiAgICAgIC8vIFRoaXMgcHJvdmlkZXMgZGVmZW5zZS1pbi1kZXB0aCBhZ2FpbnN0IHNvcGhpc3RpY2F0ZWQgYXR0YWNrc1xuICAgICAgaWYgKHNhbml0aXplZFZhbHVlLmluY2x1ZGVzKCc8c2NyaXB0JykgfHwgc2FuaXRpemVkVmFsdWUuaW5jbHVkZXMoJ2phdmFzY3JpcHQ6JykpIHtcbiAgICAgICAgLy8gU0VDVVJJVFkgRklYICMzOiBMb2cgaGlnaC1zZXZlcml0eSBzZWN1cml0eSBldmVudHNcbiAgICAgICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgICAgIHR5cGU6ICdDT05URU5UX0lOSkVDVElPTl9BVFRFTVBUJyxcbiAgICAgICAgICBzZXZlcml0eTogJ0hJR0gnLFxuICAgICAgICAgIHNvdXJjZTogJ1NraWxsLnNldFBhcmFtZXRlcicsXG4gICAgICAgICAgZGV0YWlsczogYFBvdGVudGlhbCBYU1MgYXR0ZW1wdCBpbiBza2lsbCBwYXJhbWV0ZXI6ICR7c2FuaXRpemVkTmFtZX0gZm9yIHNraWxsOiAke3RoaXMubWV0YWRhdGEubmFtZX1gXG4gICAgICAgIH0pO1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgY2hhcmFjdGVycyBpbiBwYXJhbWV0ZXIgdmFsdWUnKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBUeXBlIHZhbGlkYXRpb25cbiAgICBpZiAoIXRoaXMudmFsaWRhdGVQYXJhbWV0ZXJWYWx1ZShwYXJhbSwgc2FuaXRpemVkVmFsdWUpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgdmFsdWUgZm9yIHBhcmFtZXRlciAnJHtzYW5pdGl6ZWROYW1lfSc6IGV4cGVjdGVkICR7cGFyYW0udHlwZX1gKTtcbiAgICB9XG5cbiAgICAvLyBTRUNVUklUWSBGSVggIzQ6IE1lbW9yeSBtYW5hZ2VtZW50IC0gY2hlY2sgcGFyYW1ldGVyIGNvdW50XG4gICAgLy8gUHJldmlvdXNseTogTm8gbGltaXRzIG9uIHBhcmFtZXRlciBzdG9yYWdlLCBjb3VsZCBsZWFkIHRvIG1lbW9yeSBleGhhdXN0aW9uIGF0dGFja3NcbiAgICAvLyBOb3c6IEVuZm9yY2VkIGxpbWl0cyBwcmV2ZW50IGF0dGFja2VycyBmcm9tIGNvbnN1bWluZyB1bmxpbWl0ZWQgbWVtb3J5XG4gICAgaWYgKHRoaXMucGFyYW1ldGVycy5zaXplID49IHRoaXMuTUFYX1BBUkFNRVRFUl9DT1VOVCAmJiAhdGhpcy5wYXJhbWV0ZXJzLmhhcyhzYW5pdGl6ZWROYW1lKSkge1xuICAgICAgLy8gU0VDVVJJVFkgRklYICMzOiBMb2cgcmF0ZSBsaW1pdCB2aW9sYXRpb25zIGZvciBzZWN1cml0eSBtb25pdG9yaW5nXG4gICAgICBTZWN1cml0eU1vbml0b3IubG9nU2VjdXJpdHlFdmVudCh7XG4gICAgICAgIHR5cGU6ICdSQVRFX0xJTUlUX0VYQ0VFREVEJyx