@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.
484 lines • 63.2 kB
JavaScript
/**
* GenericElementValidator - Default validator implementation for most element types
*
* Provides standard validation logic that works for any element type.
* Uses the existing ValidationService and TriggerValidationService for
* consistent security-first validation patterns.
*
* Element types that need specialized validation (like personas) can
* extend this class or implement ElementValidator directly.
*/
import { ElementType } from '../../portfolio/types.js';
import { SECURITY_LIMITS } from '../../security/constants.js';
import { InputNormalizer } from '../../security/InputNormalizer.js';
import { ValidatorHelpers } from './ElementValidator.js';
/**
* Default validator implementation for most element types
*/
export class GenericElementValidator {
validationService;
triggerValidationService;
metadataService;
elementType;
constructor(elementType, validationService, triggerValidationService, metadataService) {
this.validationService = validationService;
this.triggerValidationService = triggerValidationService;
this.metadataService = metadataService;
this.elementType = elementType;
}
/**
* Validate data for element creation
*
* ARCHITECTURE: Input Normalization at Boundary
* Step 1: Normalize ALL string fields (Unicode, confusables, direction overrides)
* Step 2: Validate the normalized data (field rules, lengths, patterns)
*
* This ensures we can't forget to normalize a field - it happens once at entry.
*/
async validateCreate(data, options) {
const errors = [];
const warnings = [];
const suggestions = [];
if (!data || typeof data !== 'object') {
return ValidatorHelpers.fail(['Data must be a non-null object']);
}
// STEP 1: NORMALIZE at the boundary (before any validation)
const normalized = InputNormalizer.normalize(data);
// Fail fast if high or critical Unicode issues detected
if (normalized.hasHighOrCriticalIssues) {
return ValidatorHelpers.fail(normalized.errors);
}
// Add normalization warnings to results
warnings.push(...normalized.warnings);
// STEP 2: VALIDATE the normalized data
const record = normalized.data;
// Validate name
const nameResult = this.validateName(record.name);
if (!nameResult.isValid) {
errors.push(...nameResult.errors);
}
warnings.push(...nameResult.warnings);
// Validate description
const descResult = this.validateDescription(record.description);
if (!descResult.isValid) {
errors.push(...descResult.errors);
}
warnings.push(...descResult.warnings);
// Validate content if present
if (record.content !== undefined && !options?.skipContentValidation) {
const contentResult = await this.validateContent(record.content, options?.maxContentLength);
if (!contentResult.isValid) {
errors.push(...contentResult.errors);
}
warnings.push(...contentResult.warnings);
}
// Fix #908: Validate instructions field — previously only content was checked,
// allowing injection payloads in instructions to reach disk unscanned.
if (record.instructions !== undefined && !options?.skipContentValidation) {
const instrResult = await this.validateContent(record.instructions, options?.maxContentLength);
if (!instrResult.isValid) {
errors.push(...instrResult.errors);
}
warnings.push(...instrResult.warnings);
}
// Validate triggers if present
if (record.triggers !== undefined) {
const triggerResult = this.validateTriggers(record.triggers, String(record.name || 'unknown'));
if (!triggerResult.isValid) {
errors.push(...triggerResult.errors);
}
warnings.push(...triggerResult.warnings);
}
// Add suggestions for missing optional fields
if (!record.triggers || (Array.isArray(record.triggers) && record.triggers.length === 0)) {
suggestions.push('Add trigger keywords to improve discoverability');
}
if (!record.author) {
suggestions.push('Add an author field for proper attribution');
}
if (!record.version) {
suggestions.push('Add a version number for tracking updates');
}
return {
isValid: errors.length === 0,
errors,
warnings,
suggestions: suggestions.length > 0 ? suggestions : undefined
};
}
/**
* Validate changes to an existing element
*
* ARCHITECTURE: Input Normalization at Boundary
* Step 1: Normalize ALL string fields in changes object
* Step 2: Validate the normalized changes
*/
async validateEdit(element, changes, options) {
const errors = [];
const warnings = [];
if (!element || typeof element !== 'object') {
return ValidatorHelpers.fail(['Element must be a non-null object']);
}
if (!changes || typeof changes !== 'object') {
return ValidatorHelpers.fail(['Changes must be a non-null object']);
}
// STEP 1: NORMALIZE changes at the boundary
const normalized = InputNormalizer.normalize(changes);
// Fail fast if high or critical Unicode issues detected
if (normalized.hasHighOrCriticalIssues) {
return ValidatorHelpers.fail(normalized.errors);
}
// Add normalization warnings to results
warnings.push(...normalized.warnings);
// STEP 2: VALIDATE the normalized changes
const changeRecord = normalized.data;
// Validate each changed field
if (changeRecord.name !== undefined) {
const nameResult = this.validateName(changeRecord.name);
if (!nameResult.isValid) {
errors.push(...nameResult.errors);
}
warnings.push(...nameResult.warnings);
}
if (changeRecord.description !== undefined) {
const descResult = this.validateDescription(changeRecord.description);
if (!descResult.isValid) {
errors.push(...descResult.errors);
}
warnings.push(...descResult.warnings);
}
if (changeRecord.content !== undefined && !options?.skipContentValidation) {
const contentResult = await this.validateContent(changeRecord.content, options?.maxContentLength);
if (!contentResult.isValid) {
errors.push(...contentResult.errors);
}
warnings.push(...contentResult.warnings);
}
// Fix #908: Validate instructions on edit path (same gap as validateCreate)
if (changeRecord.instructions !== undefined && !options?.skipContentValidation) {
const instrResult = await this.validateContent(changeRecord.instructions, options?.maxContentLength);
if (!instrResult.isValid) {
errors.push(...instrResult.errors);
}
warnings.push(...instrResult.warnings);
}
if (changeRecord.triggers !== undefined) {
const elementRecord = element;
const triggerResult = this.validateTriggers(changeRecord.triggers, String(elementRecord.name || changeRecord.name || 'unknown'));
if (!triggerResult.isValid) {
errors.push(...triggerResult.errors);
}
warnings.push(...triggerResult.warnings);
}
return {
isValid: errors.length === 0,
errors,
warnings
};
}
/**
* Validate element metadata
*/
async validateMetadata(metadata, options) {
const errors = [];
const warnings = [];
if (!metadata || typeof metadata !== 'object') {
return ValidatorHelpers.fail(['Metadata must be a non-null object']);
}
const record = metadata;
// Check required fields
const requiredFields = options?.requiredFields || ['name'];
for (const field of requiredFields) {
if (!record[field]) {
errors.push(`Required field '${field}' is missing or empty`);
}
}
// Validate field formats
if (options?.formatFields) {
for (const [field, pattern] of Object.entries(options.formatFields)) {
const value = record[field];
if (value && typeof value === 'string' && !pattern.test(value)) {
errors.push(`Field '${field}' has invalid format`);
}
}
}
// Validate max lengths
if (options?.maxLengths) {
for (const [field, maxLength] of Object.entries(options.maxLengths)) {
const value = record[field];
if (value && typeof value === 'string' && value.length > maxLength) {
errors.push(`Field '${field}' exceeds maximum length of ${maxLength} characters`);
}
}
}
// Standard field validations
if (record.name) {
const nameResult = this.validateName(record.name);
if (!nameResult.isValid) {
errors.push(...nameResult.errors);
}
warnings.push(...nameResult.warnings);
}
return {
isValid: errors.length === 0,
errors,
warnings
};
}
/**
* Generate a comprehensive validation report
*/
async generateReport(element) {
const details = [];
let status = 'pass';
if (!element || typeof element !== 'object') {
return {
status: 'fail',
summary: 'Invalid element: must be a non-null object',
details: ['Element validation failed - invalid input type'],
timestamp: new Date()
};
}
const record = element;
const metadata = (record.metadata || record);
const content = String(record.content || record.instructions || '');
// Validate all fields
const createResult = await this.validateCreate({
name: metadata.name,
description: metadata.description,
content,
triggers: metadata.triggers,
author: metadata.author,
version: metadata.version
});
// Process validation results
if (createResult.errors.length > 0) {
status = 'fail';
details.push('Errors:');
createResult.errors.forEach((error, i) => {
details.push(` ${i + 1}. ${error}`);
});
}
if (createResult.warnings.length > 0) {
if (status === 'pass') {
status = 'warning';
}
details.push('Warnings:');
createResult.warnings.forEach((warning, i) => {
details.push(` ${i + 1}. ${warning}`);
});
}
if (createResult.suggestions && createResult.suggestions.length > 0) {
details.push('Suggestions:');
createResult.suggestions.forEach((suggestion, i) => {
details.push(` ${i + 1}. ${suggestion}`);
});
}
// Calculate metrics
const triggerCount = Array.isArray(metadata.triggers) ? metadata.triggers.length : 0;
const contentLength = content.length;
// Generate summary
let summary;
if (status === 'pass') {
summary = `${this.getElementLabel()} validation passed with no issues`;
}
else if (status === 'warning') {
summary = `${this.getElementLabel()} is valid but has ${createResult.warnings.length} warning(s)`;
}
else {
summary = `${this.getElementLabel()} validation failed with ${createResult.errors.length} error(s)`;
}
return {
status,
summary,
details,
timestamp: new Date(),
metrics: {
contentLength,
triggerCount,
qualityScore: this.calculateQualityScore(metadata, content)
}
};
}
/**
* Validate element name
*/
validateName(name) {
if (!name || typeof name !== 'string') {
return ValidatorHelpers.fail(["Name is required and must be a string"]);
}
const result = this.validationService.validateAndSanitizeInput(name, {
maxLength: SECURITY_LIMITS.MAX_NAME_LENGTH,
allowSpaces: true,
fieldType: 'name'
});
if (!result.isValid) {
return ValidatorHelpers.fail(result.errors || ['Invalid name']);
}
const warnings = [];
if (name.length > 50) {
warnings.push('Name is very long - consider shortening for better display');
}
return {
isValid: true,
errors: [],
warnings
};
}
/**
* Validate element description
*/
validateDescription(description) {
const warnings = [];
if (!description) {
return ValidatorHelpers.fail(["Description is required"]);
}
if (typeof description !== 'string') {
return ValidatorHelpers.fail(["Description must be a string"]);
}
if (description.length > SECURITY_LIMITS.MAX_DESCRIPTION_LENGTH) {
return ValidatorHelpers.fail([
`Description exceeds maximum length of ${SECURITY_LIMITS.MAX_DESCRIPTION_LENGTH} characters`
]);
}
const result = this.sanitizeDescriptionInput(description);
if (!result.isValid) {
return ValidatorHelpers.fail(result.errors || ['Invalid description']);
}
if (description.length > 200) {
warnings.push('Description is very long - consider keeping it under 200 characters');
}
return {
isValid: true,
errors: [],
warnings
};
}
sanitizeDescriptionInput(description) {
return this.validationService.validateAndSanitizeInput(description, {
maxLength: SECURITY_LIMITS.MAX_DESCRIPTION_LENGTH,
allowSpaces: true,
fieldType: 'description'
});
}
/**
* Validate element content
*/
async validateContent(content, maxLength) {
const warnings = [];
const max = maxLength || SECURITY_LIMITS.MAX_CONTENT_LENGTH;
if (!content) {
return ValidatorHelpers.fail(['Content is required']);
}
if (typeof content !== 'string') {
return ValidatorHelpers.fail(['Content must be a string']);
}
// Check minimum length
if (content.trim().length < 10) {
return ValidatorHelpers.fail(['Content is too short (minimum 10 characters)']);
}
// Use ValidationService for content validation
const result = this.validationService.validateContent(content, {
maxLength: max
});
if (!result.isValid) {
return ValidatorHelpers.fail(result.detectedPatterns || ['Content validation failed']);
}
// Content quality warnings
if (content.length > 5000) {
warnings.push('Content is very long - consider breaking it into sections');
}
return {
isValid: true,
errors: [],
warnings
};
}
/**
* Validate triggers array
*/
validateTriggers(triggers, elementName) {
if (!Array.isArray(triggers)) {
return ValidatorHelpers.fail(['Triggers must be an array']);
}
const result = this.triggerValidationService.validateTriggers(triggers, this.elementType, elementName);
const errors = [];
const warnings = [...result.warnings];
if (result.hasRejections) {
result.rejectedTriggers.forEach(rejected => {
warnings.push(`Trigger "${rejected.original}" rejected: ${rejected.reason}`);
});
}
return {
isValid: errors.length === 0,
errors,
warnings
};
}
/**
* Calculate a quality score for the element (0-100)
*/
calculateQualityScore(metadata, content) {
let score = 0;
// Name quality (0-15 points)
if (metadata.name && typeof metadata.name === 'string') {
score += 10;
if (metadata.name.length >= 3 && metadata.name.length <= 50) {
score += 5;
}
}
// Description quality (0-15 points)
if (metadata.description && typeof metadata.description === 'string') {
score += 10;
const desc = metadata.description;
if (desc.length >= 20 && desc.length <= 200) {
score += 5;
}
}
// Content quality (0-30 points)
if (content) {
score += 15;
if (content.length >= 50) {
score += 10;
}
if (content.length <= 5000) {
score += 5;
}
}
// Metadata completeness (0-20 points)
if (metadata.author)
score += 5;
if (metadata.version)
score += 5;
if (metadata.category)
score += 5;
if (metadata.created)
score += 5;
// Triggers (0-20 points)
if (Array.isArray(metadata.triggers)) {
const triggers = metadata.triggers;
if (triggers.length > 0)
score += 10;
if (triggers.length >= 3)
score += 5;
if (triggers.length <= 10)
score += 5;
}
return Math.min(100, score);
}
/**
* Get human-readable label for this element type
*/
getElementLabel() {
const labels = {
[ElementType.PERSONA]: 'Persona',
[ElementType.SKILL]: 'Skill',
[ElementType.TEMPLATE]: 'Template',
[ElementType.AGENT]: 'Agent',
[ElementType.MEMORY]: 'Memory',
[ElementType.ENSEMBLE]: 'Ensemble'
};
return labels[this.elementType] || this.elementType;
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiR2VuZXJpY0VsZW1lbnRWYWxpZGF0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvc2VydmljZXMvdmFsaWRhdGlvbi9HZW5lcmljRWxlbWVudFZhbGlkYXRvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7O0dBU0c7QUFFSCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFJdkQsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBQzlELE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUNwRSxPQUFPLEVBTUwsZ0JBQWdCLEVBQ2pCLE1BQU0sdUJBQXVCLENBQUM7QUFFL0I7O0dBRUc7QUFDSCxNQUFNLE9BQU8sdUJBQXVCO0lBS3RCO0lBQ0E7SUFDQTtJQU5ILFdBQVcsQ0FBYztJQUVsQyxZQUNFLFdBQXdCLEVBQ2QsaUJBQW9DLEVBQ3BDLHdCQUFrRCxFQUNsRCxlQUFnQztRQUZoQyxzQkFBaUIsR0FBakIsaUJBQWlCLENBQW1CO1FBQ3BDLDZCQUF3QixHQUF4Qix3QkFBd0IsQ0FBMEI7UUFDbEQsb0JBQWUsR0FBZixlQUFlLENBQWlCO1FBRTFDLElBQUksQ0FBQyxXQUFXLEdBQUcsV0FBVyxDQUFDO0lBQ2pDLENBQUM7SUFFRDs7Ozs7Ozs7T0FRRztJQUNILEtBQUssQ0FBQyxjQUFjLENBQ2xCLElBQWEsRUFDYixPQUFrQztRQUVsQyxNQUFNLE1BQU0sR0FBYSxFQUFFLENBQUM7UUFDNUIsTUFBTSxRQUFRLEdBQWEsRUFBRSxDQUFDO1FBQzlCLE1BQU0sV0FBVyxHQUFhLEVBQUUsQ0FBQztRQUVqQyxJQUFJLENBQUMsSUFBSSxJQUFJLE9BQU8sSUFBSSxLQUFLLFFBQVEsRUFBRSxDQUFDO1lBQ3RDLE9BQU8sZ0JBQWdCLENBQUMsSUFBSSxDQUFDLENBQUMsZ0NBQWdDLENBQUMsQ0FBQyxDQUFDO1FBQ25FLENBQUM7UUFFRCw0REFB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LENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxDQUFDO0lBQzlCLENBQUM7SUFFRDs7T0FFRztJQUNPLGVBQWU7UUFDdkIsTUFBTSxNQUFNLEdBQWdDO1lBQzFDLENBQUMsV0FBVyxDQUFDLE9BQU8sQ0FBQyxFQUFFLFNBQVM7WUFDaEMsQ0FBQyxXQUFXLENBQUMsS0FBSyxDQUFDLEVBQUUsT0FBTztZQUM1QixDQUFDLFdBQVcsQ0FBQyxRQUFRLENBQUMsRUFBRSxVQUFVO1lBQ2xDLENBQUMsV0FBVyxDQUFDLEtBQUssQ0FBQyxFQUFFLE9BQU87WUFDNUIsQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDLEVBQUUsUUFBUTtZQUM5QixDQUFDLFdBQVcsQ0FBQyxRQUFRLENBQUMsRUFBRSxVQUFVO1NBQ25DLENBQUM7UUFDRixPQUFPLE1BQU0sQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksSUFBSSxDQUFDLFdBQVcsQ0FBQztJQUN0RCxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEdlbmVyaWNFbGVtZW50VmFsaWRhdG9yIC0gRGVmYXVsdCB2YWxpZGF0b3IgaW1wbGVtZW50YXRpb24gZm9yIG1vc3QgZWxlbWVudCB0eXBlc1xuICpcbiAqIFByb3ZpZGVzIHN0YW5kYXJkIHZhbGlkYXRpb24gbG9naWMgdGhhdCB3b3JrcyBmb3IgYW55IGVsZW1lbnQgdHlwZS5cbiAqIFVzZXMgdGhlIGV4aXN0aW5nIFZhbGlkYXRpb25TZXJ2aWNlIGFuZCBUcmlnZ2VyVmFsaWRhdGlvblNlcnZpY2UgZm9yXG4gKiBjb25zaXN0ZW50IHNlY3VyaXR5LWZpcnN0IHZhbGlkYXRpb24gcGF0dGVybnMuXG4gKlxuICogRWxlbWVudCB0eXBlcyB0aGF0IG5lZWQgc3BlY2lhbGl6ZWQgdmFsaWRhdGlvbiAobGlrZSBwZXJzb25hcykgY2FuXG4gKiBleHRlbmQgdGhpcyBjbGFzcyBvciBpbXBsZW1lbnQgRWxlbWVudFZhbGlkYXRvciBkaXJlY3RseS5cbiAqL1xuXG5pbXBvcnQgeyBFbGVtZW50VHlwZSB9IGZyb20gJy4uLy4uL3BvcnRmb2xpby90eXBlcy5qcyc7XG5pbXBvcnQgeyBWYWxpZGF0aW9uU2VydmljZSwgdHlwZSBWYWxpZGF0aW9uUmVzdWx0IGFzIElucHV0VmFsaWRhdGlvblJlc3VsdCB9IGZyb20gJy4vVmFsaWRhdGlvblNlcnZpY2UuanMnO1xuaW1wb3J0IHsgVHJpZ2dlclZhbGlkYXRpb25TZXJ2aWNlIH0gZnJvbSAnLi9UcmlnZ2VyVmFsaWRhdGlvblNlcnZpY2UuanMnO1xuaW1wb3J0IHsgTWV0YWRhdGFTZXJ2aWNlIH0gZnJvbSAnLi4vTWV0YWRhdGFTZXJ2aWNlLmpzJztcbmltcG9ydCB7IFNFQ1VSSVRZX0xJTUlUUyB9IGZyb20gJy4uLy4uL3NlY3VyaXR5L2NvbnN0YW50cy5qcyc7XG5pbXBvcnQgeyBJbnB1dE5vcm1hbGl6ZXIgfSBmcm9tICcuLi8uLi9zZWN1cml0eS9JbnB1dE5vcm1hbGl6ZXIuanMnO1xuaW1wb3J0IHtcbiAgRWxlbWVudFZhbGlkYXRvcixcbiAgVmFsaWRhdGlvblJlc3VsdCxcbiAgVmFsaWRhdGlvblJlcG9ydCxcbiAgRWxlbWVudFZhbGlkYXRpb25PcHRpb25zLFxuICBNZXRhZGF0YVZhbGlkYXRpb25PcHRpb25zLFxuICBWYWxpZGF0b3JIZWxwZXJzXG59IGZyb20gJy4vRWxlbWVudFZhbGlkYXRvci5qcyc7XG5cbi8qKlxuICogRGVmYXVsdCB2YWxpZGF0b3IgaW1wbGVtZW50YXRpb24gZm9yIG1vc3QgZWxlbWVudCB0eXBlc1xuICovXG5leHBvcnQgY2xhc3MgR2VuZXJpY0VsZW1lbnRWYWxpZGF0b3IgaW1wbGVtZW50cyBFbGVtZW50VmFsaWRhdG9yIHtcbiAgcmVhZG9ubHkgZWxlbWVudFR5cGU6IEVsZW1lbnRUeXBlO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIGVsZW1lbnRUeXBlOiBFbGVtZW50VHlwZSxcbiAgICBwcm90ZWN0ZWQgdmFsaWRhdGlvblNlcnZpY2U6IFZhbGlkYXRpb25TZXJ2aWNlLFxuICAgIHByb3RlY3RlZCB0cmlnZ2VyVmFsaWRhdGlvblNlcnZpY2U6IFRyaWdnZXJWYWxpZGF0aW9uU2VydmljZSxcbiAgICBwcm90ZWN0ZWQgbWV0YWRhdGFTZXJ2aWNlOiBNZXRhZGF0YVNlcnZpY2VcbiAgKSB7XG4gICAgdGhpcy5lbGVtZW50VHlwZSA9IGVsZW1lbnRUeXBlO1xuICB9XG5cbiAgLyoqXG4gICAqIFZhbGlkYXRlIGRhdGEgZm9yIGVsZW1lbnQgY3JlYXRpb25cbiAgICpcbiAgICogQVJDSElURUNUVVJFOiBJbnB1dCBOb3JtYWxpemF0aW9uIGF0IEJvdW5kYXJ5XG4gICAqIFN0ZXAgMTogTm9ybWFsaXplIEFMTCBzdHJpbmcgZmllbGRzIChVbmljb2RlLCBjb25mdXNhYmxlcywgZGlyZWN0aW9uIG92ZXJyaWRlcylcbiAgICogU3RlcCAyOiBWYWxpZGF0ZSB0aGUgbm9ybWFsaXplZCBkYXRhIChmaWVsZCBydWxlcywgbGVuZ3RocywgcGF0dGVybnMpXG4gICAqXG4gICAqIFRoaXMgZW5zdXJlcyB3ZSBjYW4ndCBmb3JnZXQgdG8gbm9ybWFsaXplIGEgZmllbGQgLSBpdCBoYXBwZW5zIG9uY2UgYXQgZW50cnkuXG4gICAqL1xuICBhc3luYyB2YWxpZGF0ZUNyZWF0ZShcbiAgICBkYXRhOiB1bmtub3duLFxuICAgIG9wdGlvbnM/OiBFbGVtZW50VmFsaWRhdGlvbk9wdGlvbnNcbiAgKTogUHJvbWlzZTxWYWxpZGF0aW9uUmVzdWx0PiB7XG4gICAgY29uc3QgZXJyb3JzOiBzdHJpbmdbXSA9IFtdO1xuICAgIGNvbnN0IHdhcm5pbmdzOiBzdHJpbmdbXSA9IFtdO1xuICAgIGNvbnN0IHN1Z2dlc3Rpb25zOiBzdHJpbmdbXSA9IFtdO1xuXG4gICAgaWYgKCFkYXRhIHx8IHR5cGVvZiBkYXRhICE9PSAnb2JqZWN0Jykge1xuICAgICAgcmV0dXJuIFZhbGlkYXRvckhlbHBlcnMuZmFpbChbJ0RhdGEgbXVzdCBiZSBhIG5vbi1udWxsIG9iamVjdCddKTtcbiAgICB9XG5cbiAgICAvLyBTVEVQIDE6IE5PUk1BTElaRSBhdCB0aGUgYm91bmRhcnkgKGJlZm9yZSBhbnkgdmFsaWRhdGlvbilcbiAgICBjb25zdCBub3JtYWxpemVkID0gSW5wdXROb3JtYWxpemVyLm5vcm1hbGl6ZShkYXRhKTtcblxuICAgIC8vIEZhaWwgZmFzdCBpZiBoaWdoIG9yIGNyaXRpY2FsIFVuaWNvZGUgaXNzdWVzIGRldGVjdGVkXG4gICAgaWYgKG5vcm1hbGl6ZWQuaGFzSGlnaE9yQ3JpdGljYWxJc3N1ZXMpIHtcbiAgICAgIHJldHVybiBWYWxpZGF0b3JIZWxwZXJzLmZhaWwobm9ybWFsaXplZC5lcnJvcnMpO1xuICAgIH1cblxuICAgIC8vIEFkZCBub3JtYWxpemF0aW9uIHdhcm5pbmdzIHRvIHJlc3VsdHNcbiAgICB3YXJuaW5ncy5wdXNoKC4uLm5vcm1hbGl6ZWQud2FybmluZ3MpO1xuXG4gICAgLy8gU1RFUCAyOiBWQUxJREFURSB0aGUgbm9ybWFsaXplZCBkYXRhXG4gICAgY29uc3QgcmVjb3JkID0gbm9ybWFsaXplZC5kYXRhIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuXG4gICAgLy8gVmFsaWRhdGUgbmFtZVxuICAgIGNvbnN0IG5hbWVSZXN1bHQgPSB0aGlzLnZhbGlkYXRlTmFtZShyZWNvcmQubmFtZSk7XG4gICAgaWYgKCFuYW1lUmVzdWx0LmlzVmFsaWQpIHtcbiAgICAgIGVycm9ycy5wdXNoKC4uLm5hbWVSZXN1bHQuZXJyb3JzKTtcbiAgICB9XG4gICAgd2FybmluZ3MucHVzaCguLi5uYW1lUmVzdWx0Lndhcm5pbmdzKTtcblxuICAgIC8vIFZhbGlkYXRlIGRlc2NyaXB0aW9uXG4gICAgY29uc3QgZGVzY1Jlc3VsdCA9IHRoaXMudmFsaWRhdGVEZXNjcmlwdGlvbihyZWNvcmQuZGVzY3JpcHRpb24pO1xuICAgIGlmICghZGVzY1Jlc3VsdC5pc1ZhbGlkKSB7XG4gICAgICBlcnJvcnMucHVzaCguLi5kZXNjUmVzdWx0LmVycm9ycyk7XG4gICAgfVxuICAgIHdhcm5pbmdzLnB1c2goLi4uZGVzY1Jlc3VsdC53YXJuaW5ncyk7XG5cbiAgICAvLyBWYWxpZGF0ZSBjb250ZW50IGlmIHByZXNlbnRcbiAgICBpZiAocmVjb3JkLmNvbnRlbnQgIT09IHVuZGVmaW5lZCAmJiAhb3B0aW9ucz8uc2tpcENvbnRlbnRWYWxpZGF0aW9uKSB7XG4gICAgICBjb25zdCBjb250ZW50UmVzdWx0ID0gYXdhaXQgdGhpcy52YWxpZGF0ZUNvbnRlbnQoXG4gICAgICAgIHJlY29yZC5jb250ZW50LFxuICAgICAgICBvcHRpb25zPy5tYXhDb250ZW50TGVuZ3RoXG4gICAgICApO1xuICAgICAgaWYgKCFjb250ZW50UmVzdWx0LmlzVmFsaWQpIHtcbiAgICAgICAgZXJyb3JzLnB1c2goLi4uY29udGVudFJlc3VsdC5lcnJvcnMpO1xuICAgICAgfVxuICAgICAgd2FybmluZ3MucHVzaCguLi5jb250ZW50UmVzdWx0Lndhcm5pbmdzKTtcbiAgICB9XG5cbiAgICAvLyBGaXggIzkwODogVmFsaWRhdGUgaW5zdHJ1Y3Rpb25zIGZpZWxkIOKAlCBwcmV2aW91c2x5IG9ubHkgY29udGVudCB3YXMgY2hlY2tlZCxcbiAgICAvLyBhbGxvd2luZyBpbmplY3Rpb24gcGF5bG9hZHMgaW4gaW5zdHJ1Y3Rpb25zIHRvIHJlYWNoIGRpc2sgdW5zY2FubmVkLlxuICAgIGlmIChyZWNvcmQuaW5zdHJ1Y3Rpb25zICE9PSB1bmRlZmluZWQgJiYgIW9wdGlvbnM/LnNraXBDb250ZW50VmFsaWRhdGlvbikge1xuICAgICAgY29uc3QgaW5zdHJSZXN1bHQgPSBhd2FpdCB0aGlzLnZhbGlkYXRlQ29udGVudChcbiAgICAgICAgcmVjb3JkLmluc3RydWN0aW9ucyxcbiAgICAgICAgb3B0aW9ucz8ubWF4Q29udGVudExlbmd0aFxuICAgICAgKTtcbiAgICAgIGlmICghaW5zdHJSZXN1bHQuaXNWYWxpZCkge1xuICAgICAgICBlcnJvcnMucHVzaCguLi5pbnN0clJlc3VsdC5lcnJvcnMpO1xuICAgICAgfVxuICAgICAgd2FybmluZ3MucHVzaCguLi5pbnN0clJlc3VsdC53YXJuaW5ncyk7XG4gICAgfVxuXG4gICAgLy8gVmFsaWRhdGUgdHJpZ2dlcnMgaWYgcHJlc2VudFxuICAgIGlmIChyZWNvcmQudHJpZ2dlcnMgIT09IHVuZGVmaW5lZCkge1xuICAgICAgY29uc3QgdHJpZ2dlclJlc3VsdCA9IHRoaXMudmFsaWRhdGVUcmlnZ2VycyhcbiAgICAgICAgcmVjb3JkLnRyaWdnZXJzLFxuICAgICAgICBTdHJpbmcocmVjb3JkLm5hbWUgfHwgJ3Vua25vd24nKVxuICAgICAgKTtcbiAgICAgIGlmICghdHJpZ2dlclJlc3VsdC5pc1ZhbGlkKSB7XG4gICAgICAgIGVycm9ycy5wdXNoKC4uLnRyaWdnZXJSZXN1bHQuZXJyb3JzKTtcbiAgICAgIH1cbiAgICAgIHdhcm5pbmdzLnB1c2goLi4udHJpZ2dlclJlc3VsdC53YXJuaW5ncyk7XG4gICAgfVxuXG4gICAgLy8gQWRkIHN1Z2dlc3Rpb25zIGZvciBtaXNzaW5nIG9wdGlvbmFsIGZpZWxkc1xuICAgIGlmICghcmVjb3JkLnRyaWdnZXJzIHx8IChBcnJheS5pc0FycmF5KHJlY29yZC50cmlnZ2VycykgJiYgcmVjb3JkLnRyaWdnZXJzLmxlbmd0aCA9PT0gMCkpIHtcbiAgICAgIHN1Z2dlc3Rpb25zLnB1c2goJ0FkZCB0cmlnZ2VyIGtleXdvcmRzIHRvIGltcHJvdmUgZGlzY292ZXJhYmlsaXR5Jyk7XG4gICAgfVxuICAgIGlmICghcmVjb3JkLmF1dGhvcikge1xuICAgICAgc3VnZ2VzdGlvbnMucHVzaCgnQWRkIGFuIGF1dGhvciBmaWVsZCBmb3IgcHJvcGVyIGF0dHJpYnV0aW9uJyk7XG4gICAgfVxuICAgIGlmICghcmVjb3JkLnZlcnNpb24pIHtcbiAgICAgIHN1Z2dlc3Rpb25zLnB1c2goJ0FkZCBhIHZlcnNpb24gbnVtYmVyIGZvciB0cmFja2luZyB1cGRhdGVzJyk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIGlzVmFsaWQ6IGVycm9ycy5sZW5ndGggPT09IDAsXG4gICAgICBlcnJvcnMsXG4gICAgICB3YXJuaW5ncyxcbiAgICAgIHN1Z2dlc3Rpb25zOiBzdWdnZXN0aW9ucy5sZW5ndGggPiAwID8gc3VnZ2VzdGlvbnMgOiB1bmRlZmluZWRcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIFZhbGlkYXRlIGNoYW5nZXMgdG8gYW4gZXhpc3RpbmcgZWxlbWVudFxuICAgKlxuICAgKiBBUkNISVRFQ1RVUkU6IElucHV0IE5vcm1hbGl6YXRpb24gYXQgQm91bmRhcnlcbiAgICogU3RlcCAxOiBOb3JtYWxpemUgQUxMIHN0cmluZyBmaWVsZHMgaW4gY2hhbmdlcyBvYmplY3RcbiAgICogU3RlcCAyOiBWYWxpZGF0ZSB0aGUgbm9ybWFsaXplZCBjaGFuZ2VzXG4gICAqL1xuICBhc3luYyB2YWxpZGF0ZUVkaXQoXG4gICAgZWxlbWVudDogdW5rbm93bixcbiAgICBjaGFuZ2VzOiB1bmtub3duLFxuICAgIG9wdGlvbnM/OiBFbGVtZW50VmFsaWRhdGlvbk9wdGlvbnNcbiAgKTogUHJvbWlzZTxWYWxpZGF0aW9uUmVzdWx0PiB7XG4gICAgY29uc3QgZXJyb3JzOiBzdHJpbmdbXSA9IFtdO1xuICAgIGNvbnN0IHdhcm5pbmdzOiBzdHJpbmdbXSA9IFtdO1xuXG4gICAgaWYgKCFlbGVtZW50IHx8IHR5cGVvZiBlbGVtZW50ICE9PSAnb2JqZWN0Jykge1xuICAgICAgcmV0dXJuIFZhbGlkYXRvckhlbHBlcnMuZmFpbChbJ0VsZW1lbnQgbXVzdCBiZSBhIG5vbi1udWxsIG9iamVjdCddKTtcbiAgICB9XG5cbiAgICBpZiAoIWNoYW5nZXMgfHwgdHlwZW9mIGNoYW5nZXMgIT09ICdvYmplY3QnKSB7XG4gICAgICByZXR1cm4gVmFsaWRhdG9ySGVscGVycy5mYWlsKFsnQ2hhbmdlcyBtdXN0IGJlIGEgbm9uLW51bGwgb2JqZWN0J10pO1xuICAgIH1cblxuICAgIC8vIFNURVAgMTogTk9STUFMSVpFIGNoYW5nZXMgYXQgdGhlIGJvdW5kYXJ5XG4gICAgY29uc3Qgbm9ybWFsaXplZCA9IElucHV0Tm9ybWFsaXplci5ub3JtYWxpemUoY2hhbmdlcyk7XG5cbiAgICAvLyBGYWlsIGZhc3QgaWYgaGlnaCBvciBjcml0aWNhbCBVbmljb2RlIGlzc3VlcyBkZXRlY3RlZFxuICAgIGlmIChub3JtYWxpemVkLmhhc0hpZ2hPckNyaXRpY2FsSXNzdWVzKSB7XG4gICAgICByZXR1cm4gVmFsaWRhdG9ySGVscGVycy5mYWlsKG5vcm1hbGl6ZWQuZXJyb3JzKTtcbiAgICB9XG5cbiAgICAvLyBBZGQgbm9ybWFsaXphdGlvbiB3YXJuaW5ncyB0byByZXN1bHRzXG4gICAgd2FybmluZ3MucHVzaCguLi5ub3JtYWxpemVkLndhcm5pbmdzKTtcblxuICAgIC8vIFNURVAgMjogVkFMSURBVEUgdGhlIG5vcm1hbGl6ZWQgY2hhbmdlc1xuICAgIGNvbnN0IGNoYW5nZVJlY29yZCA9IG5vcm1hbGl6ZWQuZGF0YSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcblxuICAgIC8vIFZhbGlkYXRlIGVhY2ggY2hhbmdlZCBmaWVsZFxuICAgIGlmIChjaGFuZ2VSZWNvcmQubmFtZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBjb25zdCBuYW1lUmVzdWx0ID0gdGhpcy52YWxpZGF0ZU5hbWUoY2hhbmdlUmVjb3JkLm5hbWUpO1xuICAgICAgaWYgKCFuYW1lUmVzdWx0LmlzVmFsaWQpIHtcbiAgICAgICAgZXJyb3JzLnB1c2goLi4ubmFtZVJlc3VsdC5lcnJvcnMpO1xuICAgICAgfVxuICAgICAgd2FybmluZ3MucHVzaCguLi5uYW1lUmVzdWx0Lndhcm5pbmdzKTtcbiAgICB9XG5cbiAgICBpZiAoY2hhbmdlUmVjb3JkLmRlc2NyaXB0aW9uICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIGNvbnN0IGRlc2NSZXN1bHQgPSB0aGlzLnZhbGlkYXRlRGVzY3JpcHRpb24oY2hhbmdlUmVjb3JkLmRlc2NyaXB0aW9uKTtcbiAgICAgIGlmICghZGVzY1Jlc3VsdC5pc1ZhbGlkKSB7XG4gICAgICAgIGVycm9ycy5wdXNoKC4uLmRlc2NSZXN1bHQuZXJyb3JzKTtcbiAgICAgIH1cbiAgICAgIHdhcm5pbmdzLnB1c2goLi4uZGVzY1Jlc3VsdC53YXJuaW5ncyk7XG4gICAgfVxuXG4gICAgaWYgKGNoYW5nZVJlY29yZC5jb250ZW50ICE9PSB1bmRlZmluZWQgJiYgIW9wdGlvbnM/LnNraXBDb250ZW50VmFsaWRhdGlvbikge1xuICAgICAgY29uc3QgY29udGVudFJlc3VsdCA9IGF3YWl0IHRoaXMudmFsaWRhdGVDb250ZW50KFxuICAgICAgICBjaGFuZ2VSZWNvcmQuY29udGVudCxcbiAgICAgICAgb3B0aW9ucz8ubWF4Q29udGVudExlbmd0aFxuICAgICAgKTtcbiAgICAgIGlmICghY29udGVudFJlc3VsdC5pc1ZhbGlkKSB7XG4gICAgICAgIGVycm9ycy5wdXNoKC4uLmNvbnRlbnRSZXN1bHQuZXJyb3JzKTtcbiAgICAgIH1cbiAgICAgIHdhcm5pbmdzLnB1c2goLi4uY29udGVudFJlc3VsdC53YXJuaW5ncyk7XG4gICAgfVxuXG4gICAgLy8gRml4ICM5MDg6IFZhbGlkYXRlIGluc3RydWN0aW9ucyBvbiBlZGl0IHBhdGggKHNhbWUgZ2FwIGFzIHZhbGlkYXRlQ3JlYXRlKVxuICAgIGlmIChjaGFuZ2VSZWNvcmQuaW5zdHJ1Y3Rpb25zICE9PSB1bmRlZmluZWQgJiYgIW9wdGlvbnM/LnNraXBDb250ZW50VmFsaWRhdGlvbikge1xuICAgICAgY29uc3QgaW5zdHJSZXN1bHQgPSBhd2FpdCB0aGlzLnZhbGlkYXRlQ29udGVudChcbiAgICAgICAgY2hhbmdlUmVjb3JkLmluc3RydWN0aW9ucyxcbiAgICAgICAgb3B0aW9ucz8ubWF4Q29udGVudExlbmd0aFxuICAgICAgKTtcbiAgICAgIGlmICghaW5zdHJSZXN1bHQuaXNWYWxpZCkge1xuICAgICAgICBlcnJvcnMucHVzaCguLi5pbnN0clJlc3VsdC5lcnJvcnMpO1xuICAgICAgfVxuICAgICAgd2FybmluZ3MucHVzaCguLi5pbnN0clJlc3VsdC53YXJuaW5ncyk7XG4gICAgfVxuXG4gICAgaWYgKGNoYW5nZVJlY29yZC50cmlnZ2VycyAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBjb25zdCBlbGVtZW50UmVjb3JkID0gZWxlbWVudCBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgICAgIGNvbnN0IHRyaWdnZXJSZXN1bHQgPSB0aGlzLnZhbGlkYXRlVHJpZ2dlcnMoXG4gICAgICAgIGNoYW5nZVJlY29yZC50cmlnZ2VycyxcbiAgICAgICAgU3RyaW5nKGVsZW1lbnRSZWNvcmQubmFtZSB8fCBjaGFuZ2VSZWNvcmQubmFtZSB8fCAndW5rbm93bicpXG4gICAgICApO1xuICAgICAgaWYgKCF0cmlnZ2VyUmVzdWx0LmlzVmFsaWQpIHtcbiAgICAgICAgZXJyb3JzLnB1c2goLi4udHJpZ2dlclJlc3VsdC5lcnJvcnMpO1xuICAgICAgfVxuICAgICAgd2FybmluZ3MucHVzaCguLi50cmlnZ2VyUmVzdWx0Lndhcm5pbmdzKTtcbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgaXNWYWxpZDogZXJyb3JzLmxlbmd0aCA9PT0gMCxcbiAgICAgIGVycm9ycyxcbiAgICAgIHdhcm5pbmdzXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBWYWxpZGF0ZSBlbGVtZW50IG1ldGFkYXRhXG4gICAqL1xuICBhc3luYyB2YWxpZGF0ZU1ldGFkYXRhKFxuICAgIG1ldGFkYXRhOiB1bmtub3duLFxuICAgIG9wdGlvbnM/OiBNZXRhZGF0YVZhbGlkYXRpb25PcHRpb25zXG4gICk6IFByb21pc2U8VmFsaWRhdGlvblJlc3VsdD4ge1xuICAgIGNvbnN0IGVycm9yczogc3RyaW5nW10gPSBbXTtcbiAgICBjb25zdCB3YXJuaW5nczogc3RyaW5nW10gPSBbXTtcblxuICAgIGlmICghbWV0YWRhdGEgfHwgdHlwZW9mIG1ldGFkYXRhICE9PSAnb2JqZWN0Jykge1xuICAgICAgcmV0dXJuIFZhbGlkYXRvckhlbHBlcnMuZmFpbChbJ01ldGFkYXRhIG11c3QgYmUgYSBub24tbnVsbCBvYmplY3QnXSk7XG4gICAgfVxuXG4gICAgY29uc3QgcmVjb3JkID0gbWV0YWRhdGEgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj47XG5cbiAgICAvLyBDaGVjayByZXF1aXJlZCBmaWVsZHNcbiAgICBjb25zdCByZXF1aXJlZEZpZWxkcyA9IG9wdGlvbnM/LnJlcXVpcm