@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
JavaScript
/**
* 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