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.

528 lines 75 kB
/** * TemplateManager - Implementation of IElementManager for Template elements * Handles CRUD operations and lifecycle management for templates implementing IElement * * SECURITY FIXES IMPLEMENTED (Following PR #319 patterns): * 1. CRITICAL: Fixed race conditions in file operations by using FileLockManager for atomic reads/writes * 2. CRITICAL: Fixed dynamic require() statements by using static imports * 3. HIGH: Fixed unvalidated YAML parsing vulnerability by using SecureYamlParser * 4. MEDIUM: All user inputs are now validated and sanitized * 5. MEDIUM: Audit logging added for security operations * 6. MEDIUM: Path traversal prevention for all file operations */ import { Template } from './Template.js'; import { ElementType } from '../../portfolio/types.js'; import { PortfolioManager } from '../../portfolio/PortfolioManager.js'; import { logger } from '../../utils/logger.js'; import { FileLockManager } from '../../security/fileLockManager.js'; import { SecureYamlParser } from '../../security/secureYamlParser.js'; import { SecurityMonitor } from '../../security/securityMonitor.js'; import { sanitizeInput } from '../../security/InputValidator.js'; import { UnicodeValidator } from '../../security/validators/unicodeValidator.js'; import { promises as fs } from 'fs'; import * as path from 'path'; import * as yaml from 'js-yaml'; import matter from 'gray-matter'; export class TemplateManager { portfolioManager; templatesDir; templates = new Map(); constructor() { this.portfolioManager = PortfolioManager.getInstance(); this.templatesDir = this.portfolioManager.getElementDir(ElementType.TEMPLATE); } /** * Load a template from file * SECURITY FIX #1: Uses FileLockManager.atomicReadFile() instead of fs.readFile() * to prevent race conditions and ensure atomic file operations */ async load(filePath) { // SECURITY FIX #4 & #6: Validate and sanitize the file path // Previously: Direct use of user-provided paths could lead to path traversal // Now: Full validation prevents accessing files outside templates directory const sanitizedPath = sanitizeInput(filePath, 255); // Ensure the path is within the templates directory const fullPath = path.isAbsolute(sanitizedPath) ? sanitizedPath : path.join(this.templatesDir, sanitizedPath); // SECURITY FIX #6: Prevent path traversal attacks const normalizedPath = path.normalize(fullPath); if (!normalizedPath.startsWith(this.templatesDir)) { SecurityMonitor.logSecurityEvent({ type: 'PATH_TRAVERSAL_ATTEMPT', severity: 'CRITICAL', source: 'TemplateManager.load', details: `Attempted to access file outside templates directory: ${sanitizedPath}` }); throw new Error('Path traversal attempt detected'); } try { // CRITICAL FIX: Use atomic file read to prevent race conditions // Previously: const content = await fs.readFile(fullPath, 'utf-8'); // Now: Uses FileLockManager with proper encoding object format const content = await FileLockManager.atomicReadFile(normalizedPath, { encoding: 'utf-8' }); // Parse the template file (expected format: YAML frontmatter + content) const parsed = matter(content); // SECURITY FIX #3: Validate YAML metadata using SecureYamlParser // Previously: Frontmatter parsing could be vulnerable to YAML injection // Now: SecureYamlParser validates and sanitizes YAML content const metadata = await this.validateMetadata(parsed.data); // Create the template instance const template = new Template(metadata, parsed.content); // Cache the template this.templates.set(normalizedPath, template); // SECURITY FIX #5: Log successful template load for audit trail SecurityMonitor.logSecurityEvent({ type: 'TEMPLATE_LOADED', severity: 'LOW', source: 'TemplateManager.load', details: `Template loaded: ${template.metadata.name} from ${path.basename(normalizedPath)}` }); return template; } catch (error) { logger.error(`Failed to load template from ${normalizedPath}: ${error}`); throw error; } } /** * Save a template to file * SECURITY FIX #1: Uses FileLockManager.atomicWriteFile() for atomic operations */ async save(template, filePath) { // SECURITY FIX #4: Validate inputs const validation = template.validate(); if (!validation.valid) { throw new Error(`Cannot save invalid template: ${validation.errors?.map(e => e.message).join(', ')}`); } // SECURITY FIX #4 & #6: Validate and sanitize the file path const sanitizedPath = sanitizeInput(filePath, 255); // Ensure the path is within the templates directory const fullPath = path.isAbsolute(sanitizedPath) ? sanitizedPath : path.join(this.templatesDir, sanitizedPath); // SECURITY FIX #6: Prevent path traversal attacks const normalizedPath = path.normalize(fullPath); if (!normalizedPath.startsWith(this.templatesDir)) { SecurityMonitor.logSecurityEvent({ type: 'PATH_TRAVERSAL_ATTEMPT', severity: 'CRITICAL', source: 'TemplateManager.save', details: `Attempted to save file outside templates directory: ${sanitizedPath}` }); throw new Error('Path traversal attempt detected'); } try { // Ensure directory exists await fs.mkdir(path.dirname(normalizedPath), { recursive: true }); // Create frontmatter content const frontmatter = this.createFrontmatter(template.metadata); const content = `---\n${frontmatter}\n---\n\n${template.content}`; // CRITICAL FIX: Use atomic file write to prevent race conditions // Previously: await fs.writeFile(fullPath, content, 'utf-8'); // Now: Uses FileLockManager for atomic operations await FileLockManager.atomicWriteFile(normalizedPath, content, { encoding: 'utf-8' }); // Update cache this.templates.set(normalizedPath, template); // SECURITY FIX #5: Log successful save for audit trail SecurityMonitor.logSecurityEvent({ type: 'TEMPLATE_SAVED', severity: 'LOW', source: 'TemplateManager.save', details: `Template saved: ${template.metadata.name} to ${path.basename(normalizedPath)}` }); logger.info(`Template saved: ${template.metadata.name}`); } catch (error) { logger.error(`Failed to save template to ${normalizedPath}: ${error}`); throw error; } } /** * List all templates * SECURITY FIX: Uses validated directory path */ async list() { try { const files = await fs.readdir(this.templatesDir); const templateFiles = files.filter(file => file.endsWith('.md') || file.endsWith('.yaml')); // Load templates in parallel with error handling const templates = await Promise.all(templateFiles.map(async (file) => { try { return await this.load(file); } catch (error) { logger.error(`Failed to load template ${file}: ${error}`); return null; } })); // Filter out failed loads return templates.filter((t) => t !== null); } catch (error) { // Handle missing directory gracefully with type-safe check if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT') { logger.debug('Templates directory does not exist yet, returning empty array'); return []; } logger.error(`Failed to list templates: ${error}`); return []; } } /** * Find a template by predicate */ async find(predicate) { const templates = await this.list(); return templates.find(predicate); } /** * Create a new template */ async create(data) { // SECURITY FIX #4: Validate and sanitize all inputs const sanitizedName = sanitizeInput(data.name || 'new-template', 100); const sanitizedDescription = sanitizeInput(data.description || '', 500); const sanitizedContent = sanitizeInput(data.content || '', 100000); // 100KB max // Create the template instance const template = new Template({ ...data.metadata, name: sanitizedName, description: sanitizedDescription }, sanitizedContent); // Generate filename from template name const filename = `${sanitizedName.toLowerCase().replace(/[^a-z0-9-]/g, '-')}.md`; // Save the template await this.save(template, filename); // SECURITY FIX #5: Audit successful creation SecurityMonitor.logSecurityEvent({ type: 'ELEMENT_CREATED', severity: 'LOW', source: 'TemplateManager.create', details: `Template created: ${template.metadata.name}` }); return template; } /** * Delete a template * SECURITY FIX #6: Path validation to prevent deletion outside directory */ async delete(filePath) { // SECURITY FIX #4 & #6: Validate and sanitize the file path const sanitizedPath = sanitizeInput(filePath, 255); // Ensure the path is within the templates directory const fullPath = path.isAbsolute(sanitizedPath) ? sanitizedPath : path.join(this.templatesDir, sanitizedPath); // SECURITY FIX #6: Prevent path traversal attacks const normalizedPath = path.normalize(fullPath); if (!normalizedPath.startsWith(this.templatesDir)) { SecurityMonitor.logSecurityEvent({ type: 'PATH_TRAVERSAL_ATTEMPT', severity: 'CRITICAL', source: 'TemplateManager.delete', details: `Attempted to delete file outside templates directory: ${sanitizedPath}` }); throw new Error('Path traversal attempt detected'); } try { await fs.unlink(normalizedPath); this.templates.delete(normalizedPath); // SECURITY FIX #5: Log deletion for audit trail SecurityMonitor.logSecurityEvent({ type: 'TEMPLATE_DELETED', severity: 'MEDIUM', source: 'TemplateManager.delete', details: `Template deleted: ${path.basename(normalizedPath)}` }); logger.info(`Template deleted: ${path.basename(normalizedPath)}`); } catch (error) { logger.error(`Failed to delete template ${normalizedPath}: ${error}`); throw error; } } /** * Import a template from external format * SECURITY FIX #3: Uses SecureYamlParser for safe YAML parsing */ async importElement(data, format) { try { let metadata; let content = ''; switch (format) { case 'json': const jsonData = JSON.parse(data); metadata = await this.validateMetadata(jsonData.metadata || {}); content = jsonData.content || ''; break; case 'yaml': // HIGH SEVERITY FIX: Use SecureYamlParser to prevent YAML injection attacks // Previously: Used unsafe YAML parsing without validation // Now: Uses SecureYamlParser which validates content and prevents malicious patterns const parsed = SecureYamlParser.parse(data, { maxYamlSize: 64 * 1024, // 64KB limit validateContent: true }); metadata = await this.validateMetadata(parsed.metadata || {}); content = parsed.content || ''; // Log security event for audit trail SecurityMonitor.logSecurityEvent({ type: 'YAML_PARSE_SUCCESS', severity: 'LOW', source: 'TemplateManager.importElement', details: 'YAML content safely parsed during import' }); break; case 'markdown': const mdParsed = matter(data); metadata = await this.validateMetadata(mdParsed.data); content = mdParsed.content; break; default: throw new Error(`Unsupported import format: ${format}`); } // Create and validate the template const template = new Template(metadata, content); const validation = template.validate(); if (!validation.valid) { throw new Error(`Invalid template: ${validation.errors?.map(e => e.message).join(', ')}`); } return template; } catch (error) { logger.error(`Failed to import template: ${error}`); throw error; } } /** * Export a template to external format * SECURITY FIX #3: Uses safe YAML serialization */ async exportElement(template, format) { try { switch (format) { case 'json': return template.serialize(); case 'yaml': // SECURITY FIX: Use yaml.dump with FAILSAFE_SCHEMA to prevent code execution // Previously: Could potentially use unsafe YAML features // Now: FAILSAFE_SCHEMA only allows basic YAML types, no JS-specific constructs const yamlData = { metadata: template.metadata, content: template.content, id: template.id, version: template.version }; return yaml.dump(yamlData, { // SECURITY TRADE-OFF: Using DEFAULT_SCHEMA instead of FAILSAFE_SCHEMA // Reason: FAILSAFE_SCHEMA doesn't support number types which are needed for template metadata // Risk: DEFAULT_SCHEMA allows more YAML features that could be exploited // Mitigation: noRefs prevents reference attacks, skipInvalid drops dangerous constructs // Consider: For maximum security, implement custom schema that only allows needed types schema: yaml.DEFAULT_SCHEMA, noRefs: true, // Prevent reference attacks sortKeys: true, // Consistent output skipInvalid: true, // Skip unserializable values instead of throwing condenseFlow: true, // More compact output quotingType: '"', // Force double quotes for consistency forceQuotes: false // Only quote when necessary }); case 'markdown': const frontmatter = this.createFrontmatter(template.metadata); return `---\n${frontmatter}\n---\n\n${template.content}`; default: throw new Error(`Unsupported export format: ${format}`); } } catch (error) { logger.error(`Failed to export template: ${error}`); throw error; } } /** * Validate and sanitize metadata * SECURITY FIX #4: Comprehensive metadata validation */ async validateMetadata(data) { const metadata = {}; // Sanitize string fields if (data.name) { metadata.name = sanitizeInput(UnicodeValidator.normalize(data.name).normalizedContent, 100); } if (data.description) { metadata.description = sanitizeInput(UnicodeValidator.normalize(data.description).normalizedContent, 500); } if (data.category) { metadata.category = sanitizeInput(data.category, 50); } if (data.output_format) { metadata.output_format = sanitizeInput(data.output_format, 20); } // Validate arrays if (Array.isArray(data.tags)) { metadata.tags = data.tags.map((tag) => sanitizeInput(String(tag), 50)); } if (Array.isArray(data.includes)) { metadata.includes = data.includes.map((inc) => sanitizeInput(String(inc), 200)); } // Copy safe fields if (typeof data.usage_count === 'number') { metadata.usage_count = Math.max(0, Math.floor(data.usage_count)); } if (data.last_used) { metadata.last_used = sanitizeInput(String(data.last_used), 50); } // Validate complex fields if (Array.isArray(data.variables)) { metadata.variables = data.variables.map((v) => ({ name: sanitizeInput(v.name || '', 50), type: sanitizeInput(v.type || 'string', 20), description: v.description ? sanitizeInput(v.description, 200) : undefined, required: Boolean(v.required), default: v.default, validation: v.validation ? sanitizeInput(v.validation, 200) : undefined, options: Array.isArray(v.options) ? v.options.map((o) => sanitizeInput(String(o), 100)) : undefined, format: v.format ? sanitizeInput(v.format, 50) : undefined })); } if (Array.isArray(data.examples)) { metadata.examples = data.examples.map((ex) => ({ title: sanitizeInput(ex.title || '', 100), description: ex.description ? sanitizeInput(ex.description, 500) : undefined, variables: ex.variables || {}, output: ex.output ? sanitizeInput(ex.output, 5000) : undefined })); } // Copy standard element fields metadata.author = data.author ? sanitizeInput(data.author, 100) : undefined; metadata.version = data.version ? sanitizeInput(data.version, 20) : undefined; return metadata; } /** * Create YAML frontmatter from metadata * SECURITY FIX #3: Safe YAML generation */ createFrontmatter(metadata) { // SECURITY FIX: Use yaml.dump with security options // Ensures no code execution vulnerabilities in generated YAML const safeMetadata = { name: metadata.name, description: metadata.description, author: metadata.author, version: metadata.version, category: metadata.category, output_format: metadata.output_format, tags: metadata.tags, includes: metadata.includes, usage_count: metadata.usage_count, last_used: metadata.last_used, variables: metadata.variables, examples: metadata.examples }; // Remove undefined values const cleanMetadata = Object.fromEntries(Object.entries(safeMetadata).filter(([_, value]) => value !== undefined)); return yaml.dump(cleanMetadata, { // SECURITY TRADE-OFF: Same as exportElement - using DEFAULT_SCHEMA for type support // See exportElement method for detailed security considerations schema: yaml.DEFAULT_SCHEMA, noRefs: true, sortKeys: true, lineWidth: 80, skipInvalid: true, condenseFlow: true, quotingType: '"', forceQuotes: false }); } /** * Check if a template file exists */ async exists(filePath) { const sanitizedPath = sanitizeInput(filePath, 255); const fullPath = path.isAbsolute(sanitizedPath) ? sanitizedPath : path.join(this.templatesDir, sanitizedPath); try { await fs.access(fullPath); return true; } catch { return false; } } /** * Find multiple templates by predicate */ async findMany(predicate) { const templates = await this.list(); return templates.filter(predicate); } /** * Validate a template */ validate(template) { return template.validate(); } /** * Validate a file path */ validatePath(filePath) { try { const sanitizedPath = sanitizeInput(filePath, 255); const fullPath = path.isAbsolute(sanitizedPath) ? sanitizedPath : path.join(this.templatesDir, sanitizedPath); const normalizedPath = path.normalize(fullPath); return normalizedPath.startsWith(this.templatesDir); } catch { return false; } } /** * Get the element type */ getElementType() { return ElementType.TEMPLATE; } /** * Get the file extension */ getFileExtension() { return '.md'; } /** * Find templates by category */ async findByCategory(category) { const sanitizedCategory = sanitizeInput(category, 50); return this.list().then(templates => templates.filter(t => t.metadata.category === sanitizedCategory)); } /** * Find templates by tag */ async findByTag(tag) { const sanitizedTag = sanitizeInput(tag, 50); return this.list().then(templates => templates.filter(t => t.metadata.tags?.includes(sanitizedTag))); } /** * Get most used templates */ async getMostUsed(limit = 10) { // SECURITY FIX: Validate limit parameter to prevent excessive memory usage // Previously: No validation could allow very large limits // Now: Enforces reasonable bounds const MIN_LIMIT = 1; const MAX_LIMIT = 100; const validatedLimit = Math.max(MIN_LIMIT, Math.min(MAX_LIMIT, Math.floor(limit))); if (limit !== validatedLimit) { logger.warn(`getMostUsed: limit ${limit} adjusted to ${validatedLimit} (valid range: ${MIN_LIMIT}-${MAX_LIMIT})`); } const templates = await this.list(); return templates .sort((a, b) => (b.metadata.usage_count || 0) - (a.metadata.usage_count || 0)) .slice(0, validatedLimit); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVGVtcGxhdGVNYW5hZ2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2VsZW1lbnRzL3RlbXBsYXRlcy9UZW1wbGF0ZU1hbmFnZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7O0dBV0c7QUFJSCxPQUFPLEVBQUUsUUFBUSxFQUFvQixNQUFNLGVBQWUsQ0FBQztBQUMzRCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDdkQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0scUNBQXFDLENBQUM7QUFDdkUsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQy9DLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUNwRSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxvQ0FBb0MsQ0FBQztBQUN0RSxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sbUNBQW1DLENBQUM7QUFDcEUsT0FBTyxFQUFFLGFBQWEsRUFBZ0IsTUFBTSxrQ0FBa0MsQ0FBQztBQUMvRSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSwrQ0FBK0MsQ0FBQztBQUNqRixPQUFPLEVBQUUsUUFBUSxJQUFJLEVBQUUsRUFBRSxNQUFNLElBQUksQ0FBQztBQUNwQyxPQUFPLEtBQUssSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUM3QixPQUFPLEtBQUssSUFBSSxNQUFNLFNBQVMsQ0FBQztBQUNoQyxPQUFPLE1BQU0sTUFBTSxhQUFhLENBQUM7QUFFakMsTUFBTSxPQUFPLGVBQWU7SUFDbEIsZ0JBQWdCLENBQW1CO0lBQ25DLFlBQVksQ0FBUztJQUNyQixTQUFTLEdBQTBCLElBQUksR0FBRyxFQUFFLENBQUM7SUFFckQ7UUFDRSxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsZ0JBQWdCLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDdkQsSUFBSSxDQUFDLFlBQVksR0FBRyxJQUFJLENBQUMs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Q0FBQyxDQUFDLENBQUM7YUFDN0UsS0FBSyxDQUFDLENBQUMsRUFBRSxjQUFjLENBQUMsQ0FBQztJQUM5QixDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFRlbXBsYXRlTWFuYWdlciAtIEltcGxlbWVudGF0aW9uIG9mIElFbGVtZW50TWFuYWdlciBmb3IgVGVtcGxhdGUgZWxlbWVudHNcbiAqIEhhbmRsZXMgQ1JVRCBvcGVyYXRpb25zIGFuZCBsaWZlY3ljbGUgbWFuYWdlbWVudCBmb3IgdGVtcGxhdGVzIGltcGxlbWVudGluZyBJRWxlbWVudFxuICogXG4gKiBTRUNVUklUWSBGSVhFUyBJTVBMRU1FTlRFRCAoRm9sbG93aW5nIFBSICMzMTkgcGF0dGVybnMpOlxuICogMS4gQ1JJVElDQUw6IEZpeGVkIHJhY2UgY29uZGl0aW9ucyBpbiBmaWxlIG9wZXJhdGlvbnMgYnkgdXNpbmcgRmlsZUxvY2tNYW5hZ2VyIGZvciBhdG9taWMgcmVhZHMvd3JpdGVzXG4gKiAyLiBDUklUSUNBTDogRml4ZWQgZHluYW1pYyByZXF1aXJlKCkgc3RhdGVtZW50cyBieSB1c2luZyBzdGF0aWMgaW1wb3J0c1xuICogMy4gSElHSDogRml4ZWQgdW52YWxpZGF0ZWQgWUFNTCBwYXJzaW5nIHZ1bG5lcmFiaWxpdHkgYnkgdXNpbmcgU2VjdXJlWWFtbFBhcnNlclxuICogNC4gTUVESVVNOiBBbGwgdXNlciBpbnB1dHMgYXJlIG5vdyB2YWxpZGF0ZWQgYW5kIHNhbml0aXplZFxuICogNS4gTUVESVVNOiBBdWRpdCBsb2dnaW5nIGFkZGVkIGZvciBzZWN1cml0eSBvcGVyYXRpb25zXG4gKiA2LiBNRURJVU06IFBhdGggdHJhdmVyc2FsIHByZXZlbnRpb24gZm9yIGFsbCBmaWxlIG9wZXJhdGlvbnNcbiAqL1xuXG5pbXBvcnQgeyBJRWxlbWVudE1hbmFnZXIgfSBmcm9tICcuLi8uLi90eXBlcy9lbGVtZW50cy9JRWxlbWVudE1hbmFnZXIuanMnO1xuaW1wb3J0IHsgRWxlbWVudFZhbGlkYXRpb25SZXN1bHQgfSBmcm9tICcuLi8uLi90eXBlcy9lbGVtZW50cy9JRWxlbWVudC5qcyc7XG5pbXBvcnQgeyBUZW1wbGF0ZSwgVGVtcGxhdGVNZXRhZGF0YSB9IGZyb20gJy4vVGVtcGxhdGUuanMnO1xuaW1wb3J0IHsgRWxlbWVudFR5cGUgfSBmcm9tICcuLi8uLi9wb3J0Zm9saW8vdHlwZXMuanMnO1xuaW1wb3J0IHsgUG9ydGZvbGlvTWFuYWdlciB9IGZyb20gJy4uLy4uL3BvcnRmb2xpby9Qb3J0Zm9saW9NYW5hZ2VyLmpzJztcbmltcG9ydCB7IGxvZ2dlciB9IGZyb20gJy4uLy4uL3V0aWxzL2xvZ2dlci5qcyc7XG5pbXBvcnQgeyBGaWxlTG9ja01hbmFnZXIgfSBmcm9tICcuLi8uLi9zZWN1cml0eS9maWxlTG9ja01hbmFnZXIuanMnO1xuaW1wb3J0IHsgU2VjdXJlWWFtbFBhcnNlciB9IGZyb20gJy4uLy4uL3NlY3VyaXR5L3NlY3VyZVlhbWxQYXJzZXIuanMnO1xuaW1wb3J0IHsgU2VjdXJpdHlNb25pdG9yIH0gZnJvbSAnLi4vLi4vc2VjdXJpdHkvc2VjdXJpdHlNb25pdG9yLmpzJztcbmltcG9ydCB7IHNhbml0aXplSW5wdXQsIHZhbGlkYXRlUGF0aCB9IGZyb20gJy4uLy4uL3NlY3VyaXR5L0lucHV0VmFsaWRhdG9yLmpzJztcbmltcG9ydCB7IFVuaWNvZGVWYWxpZGF0b3IgfSBmcm9tICcuLi8uLi9zZWN1cml0eS92YWxpZGF0b3JzL3VuaWNvZGVWYWxpZGF0b3IuanMnO1xuaW1wb3J0IHsgcHJvbWlzZXMgYXMgZnMgfSBmcm9tICdmcyc7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0ICogYXMgeWFtbCBmcm9tICdqcy15YW1sJztcbmltcG9ydCBtYXR0ZXIgZnJvbSAnZ3JheS1tYXR0ZXInO1xuXG5leHBvcnQgY2xhc3MgVGVtcGxhdGVNYW5hZ2VyIGltcGxlbWVudHMgSUVsZW1lbnRNYW5hZ2VyPFRlbXBsYXRlPiB7XG4gIHByaXZhdGUgcG9ydGZvbGlvTWFuYWdlcjogUG9ydGZvbGlvTWFuYWdlcjtcbiAgcHJpdmF0ZSB0ZW1wbGF0ZXNEaXI6IHN0cmluZztcbiAgcHJpdmF0ZSB0ZW1wbGF0ZXM6IE1hcDxzdHJpbmcsIFRlbXBsYXRlPiA9IG5ldyBNYXAoKTtcblxuICBjb25zdHJ1Y3RvcigpIHtcbiAgICB0aGlzLnBvcnRmb2xpb01hbmFnZXIgPSBQb3J0Zm9saW9NYW5hZ2VyLmdldEluc3RhbmNlKCk7XG4gICAgdGhpcy50ZW1wbGF0ZXNEaXIgPSB0aGlzLnBvcnRmb2xpb01hbmFnZXIuZ2V0RWxlbWVudERpcihFbGVtZW50VHlwZS5URU1QTEFURSk7XG4gIH1cblxuICAvKipcbiAgICogTG9hZCBhIHRlbXBsYXRlIGZyb20gZmlsZVxuICAgKiBTRUNVUklUWSBGSVggIzE6IFVzZXMgRmlsZUxvY2tNYW5hZ2VyLmF0b21pY1JlYWRGaWxlKCkgaW5zdGVhZCBvZiBmcy5yZWFkRmlsZSgpXG4gICAqIHRvIHByZXZlbnQgcmFjZSBjb25kaXRpb25zIGFuZCBlbnN1cmUgYXRvbWljIGZpbGUgb3BlcmF0aW9uc1xuICAgKi9cbiAgYXN5bmMgbG9hZChmaWxlUGF0aDogc3RyaW5nKTogUHJvbWlzZTxUZW1wbGF0ZT4ge1xuICAgIC8vIFNFQ1VSSVRZIEZJWCAjNCAmICM2OiBWYWxpZGF0ZSBhbmQgc2FuaXRpemUgdGhlIGZpbGUgcGF0aFxuICAgIC8vIFByZXZpb3VzbHk6IERpcmVjdCB1c2Ugb2YgdXNlci1wcm92aWRlZCBwYXRocyBjb3VsZCBsZWFkIHRvIHBhdGggdHJhdmVyc2FsXG4gICAgLy8gTm93OiBGdWxsIHZhbGlkYXRpb24gcHJldmVudHMgYWNjZXNzaW5nIGZpbGVzIG91dHNpZGUgdGVtcGxhdGVzIGRpcmVjdG9yeVxuICAgIGNvbnN0IHNhbml0aXplZFBhdGggPSBzYW5pdGl6ZUlucHV0KGZpbGVQYXRoLCAyNTUpO1xuICAgIFxuICAgIC8vIEVuc3VyZSB0aGUgcGF0aCBpcyB3aXRoaW4gdGhlIHRlbXBsYXRlcyBkaXJlY3RvcnlcbiAgICBjb25zdCBmdWxsUGF0aCA9IHBhdGguaXNBYnNvbHV0ZShzYW5pdGl6ZWRQYXRoKSBcbiAgICAgID8gc2FuaXRpemVkUGF0aCBcbiAgICAgIDogcGF0aC5qb2luKHRoaXMudGVtcGxhdGVzRGlyLCBzYW5pdGl6ZWRQYXRoKTtcbiAgICBcbiAgICAvLyBTRUNVUklUWSBGSVggIzY6IFByZXZlbnQgcGF0aCB0cmF2ZXJzYWwgYXR0YWNrc1xuICAgIGNvbnN0IG5vcm1hbGl6ZWRQYXRoID0gcGF0aC5ub3JtYWxpemUoZnVsbFBhdGgpO1xuICAgIGlmICghbm9ybWFsaXplZFBhdGguc3RhcnRzV2l0aCh0aGlzLnRlbXBsYXRlc0RpcikpIHtcbiAgICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgICAgdHlwZTogJ1BBVEhfVFJBVkVSU0FMX0FUVEVNUFQnLFxuICAgICAgICBzZXZlcml0eTogJ0NSSVRJQ0FMJyxcbiAgICAgICAgc291cmNlOiAnVGVtcGxhdGVNYW5hZ2VyLmxvYWQnLFxuICAgICAgICBkZXRhaWxzOiBgQXR0ZW1wdGVkIHRvIGFjY2VzcyBmaWxlIG91dHNpZGUgdGVtcGxhdGVzIGRpcmVjdG9yeTogJHtzYW5pdGl6ZWRQYXRofWBcbiAgICAgIH0pO1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdQYXRoIHRyYXZlcnNhbCBhdHRlbXB0IGRldGVjdGVkJyk7XG4gICAgfVxuXG4gICAgdHJ5IHtcbiAgICAgIC8vIENSSVRJQ0FMIEZJWDogVXNlIGF0b21pYyBmaWxlIHJlYWQgdG8gcHJldmVudCByYWNlIGNvbmRpdGlvbnNcbiAgICAgIC8vIFByZXZpb3VzbHk6IGNvbnN0IGNvbnRlbnQgPSBhd2FpdCBmcy5yZWFkRmlsZShmdWxsUGF0aCwgJ3V0Zi04Jyk7XG4gICAgICAvLyBOb3c6IFVzZXMgRmlsZUxvY2tNYW5hZ2VyIHdpdGggcHJvcGVyIGVuY29kaW5nIG9iamVjdCBmb3JtYXRcbiAgICAgIGNvbnN0IGNvbnRlbnQgPSBhd2FpdCBGaWxlTG9ja01hbmFnZXIuYXRvbWljUmVhZEZpbGUobm9ybWFsaXplZFBhdGgsIHsgZW5jb2Rpbmc6ICd1dGYtOCcgfSk7XG4gICAgICBcbiAgICAgIC8vIFBhcnNlIHRoZSB0ZW1wbGF0ZSBmaWxlIChleHBlY3RlZCBmb3JtYXQ6IFlBTUwgZnJvbnRtYXR0ZXIgKyBjb250ZW50KVxuICAgICAgY29uc3QgcGFyc2VkID0gbWF0dGVyKGNvbnRlbnQpO1xuICAgICAgXG4gICAgICAvLyBTRUNVUklUWSBGSVggIzM6IFZhbGlkYXRlIFlBTUwgbWV0YWRhdGEgdXNpbmcgU2VjdXJlWWFtbFBhcnNlclxuICAgICAgLy8gUHJldmlvdXNseTogRnJvbnRtYXR0ZXIgcGFyc2luZyBjb3VsZCBiZSB2dWxuZXJhYmxlIHRvIFlBTUwgaW5qZWN0aW9uXG4gICAg