@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.
214 lines • 26.5 kB
JavaScript
/**
* RegexValidator - Provides protection against ReDoS attacks
*
* This module implements safe regex execution by:
* 1. Pre-validating content length based on pattern complexity
* 2. Analyzing patterns for known ReDoS vulnerabilities
* 3. Limiting execution based on calculated risk
*/
import { SecurityError } from './errors.js';
import { SecurityMonitor } from './securityMonitor.js';
export class RegexValidator {
// Default limits based on pattern complexity
static COMPLEXITY_LIMITS = {
low: 100000, // 100KB for simple patterns
medium: 10000, // 10KB for moderate patterns
high: 1000 // 1KB for complex patterns
};
/**
* Validates content against a pattern with ReDoS protection
*
* Protection strategy:
* 1. Analyze pattern complexity
* 2. Enforce content length limits based on complexity
* 3. Reject known dangerous patterns
* 4. Execute regex only if safe
*/
static validate(content, pattern, options = {}) {
const { maxLength, rejectDangerousPatterns = true, logEvents = true } = options;
// Analyze pattern for ReDoS risks
const analysis = this.analyzePattern(pattern);
// Reject dangerous patterns if configured
if (rejectDangerousPatterns && !analysis.safe) {
if (logEvents) {
SecurityMonitor.logSecurityEvent({
type: 'UPDATE_SECURITY_VIOLATION',
severity: 'HIGH',
source: 'RegexValidator',
details: 'Dangerous regex pattern rejected',
additionalData: {
pattern: pattern.source,
risks: analysis.risks
}
});
}
throw new SecurityError(`Pattern rejected due to ReDoS risk: ${analysis.risks.join(', ')}`);
}
// Determine effective max length
const effectiveMaxLength = maxLength ?? analysis.maxSafeLength;
// Check content length
if (content.length > effectiveMaxLength) {
throw new SecurityError(`Content too large for validation: ${content.length} bytes (max: ${effectiveMaxLength} for ${analysis.complexity} complexity pattern)`);
}
// Create a copy of the regex to avoid modifying the original
const safeCopy = new RegExp(pattern.source, pattern.flags);
try {
// Track execution time for monitoring
const startTime = performance.now();
const result = safeCopy.test(content);
const elapsed = performance.now() - startTime;
// Log slow patterns
if (elapsed > 50 && logEvents) {
SecurityMonitor.logSecurityEvent({
type: 'RATE_LIMIT_WARNING',
severity: 'MEDIUM',
source: 'RegexValidator',
details: `Slow regex execution: ${elapsed.toFixed(2)}ms`,
additionalData: {
pattern: pattern.source,
contentLength: content.length,
elapsed
}
});
}
return result;
}
catch (error) {
// Handle any regex errors
if (logEvents) {
SecurityMonitor.logSecurityEvent({
type: 'UPDATE_SECURITY_VIOLATION',
severity: 'HIGH',
source: 'RegexValidator',
details: 'Regex execution error',
additionalData: {
error: error instanceof Error ? error.message : 'Unknown error'
}
});
}
return false;
}
}
/**
* Validates multiple patterns with shared risk assessment
*/
static validateAny(content, patterns, options = {}) {
for (const pattern of patterns) {
if (this.validate(content, pattern, options)) {
return true;
}
}
return false;
}
/**
* Validates all patterns must match
*/
static validateAll(content, patterns, options = {}) {
for (const pattern of patterns) {
if (!this.validate(content, pattern, options)) {
return false;
}
}
return true;
}
/**
* Analyzes a regex pattern for potential ReDoS vulnerabilities
*
* Detects patterns known to cause exponential backtracking:
* - Nested quantifiers: (a+)+, (a*)*
* - Alternation with overlap: (a|a)*
* - Quantified groups with alternation: (a|b)+
* - Catastrophic patterns: (.+)+$
*/
static analyzePattern(pattern) {
const source = pattern.source;
const risks = [];
// Nested quantifiers - extremely dangerous
if (/\([^)]+[+*]\)[+*]/.test(source) ||
/\([^)]+\{[^}]+\}\)[+*]/.test(source) ||
/\(\w+[+*]\)[+*]/.test(source)) {
risks.push('Nested quantifiers detected');
}
// Alternation with repetition
if (/\([^)]*\|[^)]*\)[+*]/.test(source)) {
risks.push('Quantified alternation detected');
}
// Alternation with overlap (e.g., (a|a)*)
const alternationMatch = source.match(/\(([^|)]+)\|([^)]+)\)/g);
if (alternationMatch) {
for (const match of alternationMatch) {
const parts = match.slice(1, -1).split('|');
if (parts.some((part, i) => parts.slice(i + 1).includes(part))) {
risks.push('Overlapping alternation detected');
break;
}
}
}
// Catastrophic backtracking patterns
// Check for patterns like (.+)+, (.*)+, etc. that can cause exponential backtracking
if (/\([^)]*\.\+[^)]*\)\+/.test(source) || /\([^)]*\.\*[^)]*\)\+/.test(source) || /\([^)]*\\w\+[^)]*\)\+/.test(source)) {
risks.push('Potential catastrophic backtracking');
}
// Unbounded lookahead/lookbehind with quantifiers
if (/\(\?[=!<].*[+*]/.test(source)) {
risks.push('Unbounded lookahead/lookbehind');
}
// Polynomial patterns (multiple quantifiers in sequence)
const quantifierCount = (source.match(/[+*?]|\{\d*,?\d*\}/g) || []).length;
if (quantifierCount > 3) {
risks.push('Multiple quantifiers detected');
}
// Determine complexity and safe content length
let complexity;
let maxSafeLength;
if (risks.length === 0) {
if (quantifierCount === 0) {
complexity = 'low';
maxSafeLength = this.COMPLEXITY_LIMITS.low;
}
else if (quantifierCount <= 3) {
complexity = 'medium';
maxSafeLength = this.COMPLEXITY_LIMITS.medium;
}
else {
complexity = 'high';
maxSafeLength = this.COMPLEXITY_LIMITS.high;
}
}
else if (risks.length === 1) {
complexity = 'high';
maxSafeLength = this.COMPLEXITY_LIMITS.high;
}
else {
complexity = 'high';
maxSafeLength = this.COMPLEXITY_LIMITS.high;
}
return {
safe: risks.length === 0,
risks,
complexity,
maxSafeLength
};
}
/**
* Creates a regex pattern with safety analysis
*/
static createSafePattern(pattern, flags) {
const regex = new RegExp(pattern, flags);
const analysis = this.analyzePattern(regex);
if (!analysis.safe) {
SecurityMonitor.logSecurityEvent({
type: 'UPDATE_SECURITY_VIOLATION',
severity: 'MEDIUM',
source: 'RegexValidator',
details: 'Potentially dangerous regex pattern created',
additionalData: {
pattern,
risks: analysis.risks
}
});
}
return regex;
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVnZXhWYWxpZGF0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VjdXJpdHkvcmVnZXhWYWxpZGF0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7R0FPRztBQUVILE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDNUMsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBa0J2RCxNQUFNLE9BQU8sY0FBYztJQUN6Qiw2Q0FBNkM7SUFDckMsTUFBTSxDQUFVLGlCQUFpQixHQUFHO1FBQzFDLEdBQUcsRUFBRSxNQUFNLEVBQUssNEJBQTRCO1FBQzVDLE1BQU0sRUFBRSxLQUFLLEVBQUcsNkJBQTZCO1FBQzdDLElBQUksRUFBRSxJQUFJLENBQU0sMkJBQTJCO0tBQzVDLENBQUM7SUFFRjs7Ozs7Ozs7T0FRRztJQUNILE1BQU0sQ0FBQyxRQUFRLENBQ2IsT0FBZSxFQUNmLE9BQWUsRUFDZixVQUFrQyxFQUFFO1FBRXBDLE1BQU0sRUFDSixTQUFTLEVBQ1QsdUJBQXVCLEdBQUcsSUFBSSxFQUM5QixTQUFTLEdBQUcsSUFBSSxFQUNqQixHQUFHLE9BQU8sQ0FBQztRQUVaLGtDQUFrQztRQUNsQyxNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsY0FBYyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBRTlDLDBDQUEwQztRQUMxQyxJQUFJLHVCQUF1QixJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSxDQUFDO1lBQzlDLElBQUksU0FBUyxFQUFFLENBQUM7Z0JBQ2QsZUFBZSxDQUFDLGdCQUFnQixDQUFDO29CQUMvQixJQUFJLEVBQUUsMkJBQTJCO29CQUNqQyxRQUFRLEVBQUUsTUFBTTtvQkFDaEIsTUFBTSxFQUFFLGdCQUFnQjtvQkFDeEIsT0FBTyxFQUFFLGtDQUFrQztvQkFDM0MsY0FBYyxFQUFFO3dCQUNkLE9BQU8sRUFBRSxPQUFPLENBQUMsTUFBTTt3QkFDdkIsS0FB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LEtBQUs7aUJBQ3RCO2FBQ0YsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztRQUVELE9BQU8sS0FBSyxDQUFDO0lBQ2YsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogUmVnZXhWYWxpZGF0b3IgLSBQcm92aWRlcyBwcm90ZWN0aW9uIGFnYWluc3QgUmVEb1MgYXR0YWNrc1xuICogXG4gKiBUaGlzIG1vZHVsZSBpbXBsZW1lbnRzIHNhZmUgcmVnZXggZXhlY3V0aW9uIGJ5OlxuICogMS4gUHJlLXZhbGlkYXRpbmcgY29udGVudCBsZW5ndGggYmFzZWQgb24gcGF0dGVybiBjb21wbGV4aXR5XG4gKiAyLiBBbmFseXppbmcgcGF0dGVybnMgZm9yIGtub3duIFJlRG9TIHZ1bG5lcmFiaWxpdGllc1xuICogMy4gTGltaXRpbmcgZXhlY3V0aW9uIGJhc2VkIG9uIGNhbGN1bGF0ZWQgcmlza1xuICovXG5cbmltcG9ydCB7IFNlY3VyaXR5RXJyb3IgfSBmcm9tICcuL2Vycm9ycy5qcyc7XG5pbXBvcnQgeyBTZWN1cml0eU1vbml0b3IgfSBmcm9tICcuL3NlY3VyaXR5TW9uaXRvci5qcyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgUmVnZXhWYWxpZGF0aW9uT3B0aW9ucyB7XG4gIC8qKiBNYXhpbXVtIGNvbnRlbnQgbGVuZ3RoIGFsbG93ZWQgKi9cbiAgbWF4TGVuZ3RoPzogbnVtYmVyO1xuICAvKiogUmVqZWN0IHBhdHRlcm5zIHdpdGggaGlnaCBSZURvUyByaXNrICovXG4gIHJlamVjdERhbmdlcm91c1BhdHRlcm5zPzogYm9vbGVhbjtcbiAgLyoqIExvZyBzZWN1cml0eSBldmVudHMgKi9cbiAgbG9nRXZlbnRzPzogYm9vbGVhbjtcbn1cblxuaW50ZXJmYWNlIFBhdHRlcm5BbmFseXNpcyB7XG4gIHNhZmU6IGJvb2xlYW47XG4gIHJpc2tzOiBzdHJpbmdbXTtcbiAgY29tcGxleGl0eTogJ2xvdycgfCAnbWVkaXVtJyB8ICdoaWdoJztcbiAgbWF4U2FmZUxlbmd0aDogbnVtYmVyO1xufVxuXG5leHBvcnQgY2xhc3MgUmVnZXhWYWxpZGF0b3Ige1xuICAvLyBEZWZhdWx0IGxpbWl0cyBiYXNlZCBvbiBwYXR0ZXJuIGNvbXBsZXhpdHlcbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgQ09NUExFWElUWV9MSU1JVFMgPSB7XG4gICAgbG93OiAxMDAwMDAsICAgIC8vIDEwMEtCIGZvciBzaW1wbGUgcGF0dGVybnNcbiAgICBtZWRpdW06IDEwMDAwLCAgLy8gMTBLQiBmb3IgbW9kZXJhdGUgcGF0dGVybnNcbiAgICBoaWdoOiAxMDAwICAgICAgLy8gMUtCIGZvciBjb21wbGV4IHBhdHRlcm5zXG4gIH07XG5cbiAgLyoqXG4gICAqIFZhbGlkYXRlcyBjb250ZW50IGFnYWluc3QgYSBwYXR0ZXJuIHdpdGggUmVEb1MgcHJvdGVjdGlvblxuICAgKiBcbiAgICogUHJvdGVjdGlvbiBzdHJhdGVneTpcbiAgICogMS4gQW5hbHl6ZSBwYXR0ZXJuIGNvbXBsZXhpdHlcbiAgICogMi4gRW5mb3JjZSBjb250ZW50IGxlbmd0aCBsaW1pdHMgYmFzZWQgb24gY29tcGxleGl0eVxuICAgKiAzLiBSZWplY3Qga25vd24gZGFuZ2Vyb3VzIHBhdHRlcm5zXG4gICAqIDQuIEV4ZWN1dGUgcmVnZXggb25seSBpZiBzYWZlXG4gICAqL1xuICBzdGF0aWMgdmFsaWRhdGUoXG4gICAgY29udGVudDogc3RyaW5nLFxuICAgIHBhdHRlcm46IFJlZ0V4cCxcbiAgICBvcHRpb25zOiBSZWdleFZhbGlkYXRpb25PcHRpb25zID0ge31cbiAgKTogYm9vbGVhbiB7XG4gICAgY29uc3Qge1xuICAgICAgbWF4TGVuZ3RoLFxuICAgICAgcmVqZWN0RGFuZ2Vyb3VzUGF0dGVybnMgPSB0cnVlLFxuICAgICAgbG9nRXZlbnRzID0gdHJ1ZVxuICAgIH0gPSBvcHRpb25zO1xuXG4gICAgLy8gQW5hbHl6ZSBwYXR0ZXJuIGZvciBSZURvUyByaXNrc1xuICAgIGNvbnN0IGFuYWx5c2lzID0gdGhpcy5hbmFseXplUGF0dGVybihwYXR0ZXJuKTtcbiAgICBcbiAgICAvLyBSZWplY3QgZGFuZ2Vyb3VzIHBhdHRlcm5zIGlmIGNvbmZpZ3VyZWRcbiAgICBpZiAocmVqZWN0RGFuZ2Vyb3VzUGF0dGVybnMgJiYgIWFuYWx5c2lzLnNhZmUpIHtcbiAgICAgIGlmIChsb2dFdmVudHMpIHtcbiAgICAgICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgICAgIHR5cGU6ICdVUERBVEVfU0VDVVJJVFlfVklPTEFUSU9OJyxcbiAgICAgICAgICBzZXZlcml0eTogJ0hJR0gnLFxuICAgICAgICAgIHNvdXJjZTogJ1JlZ2V4VmFsaWRhdG9yJyxcbiAgICAgICAgICBkZXRhaWxzOiAnRGFuZ2Vyb3VzIHJlZ2V4IHBhdHRlcm4gcmVqZWN0ZWQnLFxuICAgICAgICAgIGFkZGl0aW9uYWxEYXRhOiB7XG4gICAgICAgICAgICBwYXR0ZXJuOiBwYXR0ZXJuLnNvdXJjZSxcbiAgICAgICAgICAgIHJpc2tzOiBhbmFseXNpcy5yaXNrc1xuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgICB0aHJvdyBuZXcgU2VjdXJpdHlFcnJvcihcbiAgICAgICAgYFBhdHRlcm4gcmVqZWN0ZWQgZHVlIHRvIFJlRG9TIHJpc2s6ICR7YW5hbHlzaXMucmlza3Muam9pbignLCAnKX1gXG4gICAgICApO1xuICAgIH1cblxuICAgIC8vIERldGVybWluZSBlZmZlY3RpdmUgbWF4IGxlbmd0aFxuICAgIGNvbnN0IGVmZmVjdGl2ZU1heExlbmd0aCA9IG1heExlbmd0aCA/PyBhbmFseXNpcy5tYXhTYWZlTGVuZ3RoO1xuICAgIFxuICAgIC8vIENoZWNrIGNvbnRlbnQgbGVuZ3RoXG4gICAgaWYgKGNvbnRlbnQubGVuZ3RoID4gZWZmZWN0aXZlTWF4TGVuZ3RoKSB7XG4gICAgICB0aHJvdyBuZXcgU2VjdXJpdHlFcnJvcihcbiAgICAgICAgYENvbnRlbnQgdG9vIGxhcmdlIGZvciB2YWxpZGF0aW9uOiAke2NvbnRlbnQubGVuZ3RofSBieXRlcyAobWF4OiAke2VmZmVjdGl2ZU1heExlbmd0aH0gZm9yICR7YW5hbHlzaXMuY29tcGxleGl0eX0gY29tcGxleGl0eSBwYXR0ZXJuKWBcbiAgICAgICk7XG4gICAgfVxuXG4gICAgLy8gQ3JlYXRlIGEgY29weSBvZiB0aGUgcmVnZXggdG8gYXZvaWQgbW9kaWZ5aW5nIHRoZSBvcmlnaW5hbFxuICAgIGNvbnN0IHNhZmVDb3B5ID0gbmV3IFJlZ0V4cChwYXR0ZXJuLnNvdXJjZSwgcGF0dGVybi5mbGFncyk7XG4gICAgXG4gICAgdHJ5IHtcbiAgICAgIC8vIFRyYWNrIGV4ZWN1dGlvbiB0aW1lIGZvciBtb25pdG9yaW5nXG4gICAgICBjb25zdCBzdGFydFRpbWUgPSBwZXJmb3JtYW5jZS5ub3coKTtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IHNhZmVDb3B5LnRlc3QoY29udGVudCk7XG4gICAgICBjb25zdCBlbGFwc2VkID0gcGVyZm9ybWFuY2Uubm93KCkgLSBzdGFydFRpbWU7XG5cbiAgICAgIC8vIExvZyBzbG93IHBhdHRlcm5zXG4gICAgICBpZiAoZWxhcHNlZCA+IDUwICYmIGxvZ0V2ZW50cykge1xuICAgICAgICBTZWN1cml0eU1vbml0b3IubG9nU2VjdXJpdHlFdmVudCh7XG4gICAgICAgICAgdHlwZTogJ1JBVEVfTElNSVRfV0FSTklORycsXG4gICAgICAgICAgc2V2ZXJpdHk6ICdNRURJVU0nLFxuICAgICAgICAgIHNvdXJjZTogJ1JlZ2V4VmFsaWRhdG9yJyxcbiAgICAgICAgICBkZXRhaWxzOiBgU2xvdyByZWdleCBleGVjdXRpb246ICR7ZWxhcHNlZC50b0ZpeGVkKDIpfW1zYCxcbiAgICAgICAgICBhZGRpdGlvbmFsRGF0YToge1xuICAgICAgICAgICAgcGF0dGVybjogcGF0dGVybi5zb3VyY2UsXG4gICAgICAgICAgICBjb250ZW50TGVuZ3RoOiBjb250ZW50Lmxlbmd0aCxcbiAgICAgICAgICAgIGVsYXBzZWRcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgfVxuXG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAvLyBIYW5kbGUgYW55IHJlZ2V4IGVycm9yc1xuICAgICAgaWYgKGxvZ0V2ZW50cykge1xuICAgICAgICBTZWN1cml0eU1vbml0b3IubG9nU2VjdXJpdHlFdmVudCh7XG4gICAgICAgICAgdHlwZTogJ1VQREFURV9TRUNVUklUWV9WSU9MQVRJT04nLFxuICAgICAgICAgIHNldmVyaXR5OiAnSElHSCcsXG4gICAgICAgICAgc291cmNlOiAnUmVnZXhWYWxpZGF0b3InLFxuICAgICAgICAgIGRldGFpbHM6ICdSZWdleCBleGVjdXRpb24gZXJyb3InLFxuICAgICAgICAgIGFkZGl0aW9uYWxEYXRhOiB7XG4gICAgICAgICAgICBlcnJvcjogZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm1lc3NhZ2UgOiAnVW5rbm93biBlcnJvcidcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBWYWxpZGF0ZXMgbXVsdGlwbGUgcGF0dGVybnMgd2l0aCBzaGFyZWQgcmlzayBhc3Nlc3NtZW50XG4gICAqL1xuICBzdGF0aWMgdmFsaWRhdGVBbnkoXG4gICAgY29udGVudDogc3RyaW5nLFxuICAgIHBhdHRlcm5zOiBSZWdFeHBbXSxcbiAgICBvcHRpb25zOiBSZWdleFZhbGlkYXRpb25PcHRpb25zID0ge31cbiAgKTogYm9vbGVhbiB7XG4gICAgZm9yIChjb25zdCBwYXR0ZXJuIG9mIHBhdHRlcm5zKSB7XG4gICAgICBpZiAodGhpcy52YWxpZGF0ZShjb250ZW50LCBwYXR0ZXJuLCBvcHRpb25zKSkge1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG5cbiAgLyoqXG4gICAqIFZhbGlkYXRlcyBhbGwgcGF0dGVybnMgbXVzdCBtYXRjaFxuICAgKi9cbiAgc3RhdGljIHZhbGlkYXRlQWxsKFxuICAgIGNvbnRlbnQ6IHN0cmluZyxcbiAgICBwYXR0ZXJuczogUmVnRXhwW10sXG4gICAgb3B0aW9uczogUmVnZXhWYWxpZGF0aW9uT3B0aW9ucyA9IHt9XG4gICk6IGJvb2xlYW4ge1xuICAgIGZvciAoY29uc3QgcGF0dGVybiBvZiBwYXR0ZXJucykge1xuICAgICAgaWYgKCF0aGlzLnZhbGlkYXRlKGNvbnRlbnQsIHBhdHRlcm4sIG9wdGlvbnMpKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHRydWU7XG4gIH1cblxuICAvKipcbiAgICogQW5hbHl6ZXMgYSByZWdleCBwYXR0ZXJuIGZvciBwb3RlbnRpYWwgUmVEb1MgdnVsbmVyYWJpbGl0aWVzXG4gICAqIFxuICAgKiBEZXRlY3RzIHBhdHRlcm5zIGtub3duIHRvIGNhdXNlIGV4cG9uZW50aWFsIGJhY2t0cmFja2luZzpcbiAgICogLSBOZXN0ZWQgcXVhbnRpZmllcnM6IChhKykrLCAoYSopKlxuICAgKiAtIEFsdGVybmF0aW9uIHdpdGggb3ZlcmxhcDogKGF8YSkqXG4gICAqIC0gUXVhbnRpZmllZCBncm91cHMgd2l0aCBhbHRlcm5hdGlvbjogKGF8YikrXG4gICAqIC0gQ2F0YXN0cm9waGljIHBhdHRlcm5zOiAoLispKyRcbiAgICovXG4gIHN0YXRpYyBhbmFseXplUGF0dGVybihwYXR0ZXJuOiBSZWdFeHApOiBQYXR0ZXJuQW5hbHlzaXMge1xuICAgIGNvbnN0IHNvdXJjZSA9IHBhdHRlcm4uc291cmNlO1xuICAgIGNvbnN0IHJpc2tzOiBzdHJpbmdbXSA9IFtdO1xuICAgIFxuICAgIC8vIE5lc3RlZCBxdWFudGlmaWVycyAtIGV4dHJlbWVseSBkYW5nZXJvdXNcbiAgICBpZiAoL1xcKFteKV0rWysqXVxcKVsrKl0vLnRlc3Qoc291cmNlKSB8fCBcbiAgICAgICAgL1xcKFteKV0rXFx7W159XStcXH1cXClbKypdLy50ZXN0KHNvdXJjZSkgfHxcbiAgICAgICAgL1xcKFxcdytbKypdXFwpWysqXS8udGVzdChzb3VyY2UpKSB7XG4gICAgICByaXNrcy5wdXNoKCdOZXN0ZWQgcXVhbnRpZmllcnMgZGV0ZWN0ZWQnKTtcbiAgICB9XG5cbiAgICAvLyBBbHRlcm5hdGlvbiB3aXRoIHJlcGV0aXRpb25cbiAgICBpZiAoL1xcKFteKV0qXFx8W14pXSpcXClbKypdLy50ZXN0KHNvdXJjZSkpIHtcbiAgICAgIHJpc2tzLnB1c2goJ1F1YW50aWZpZWQgYWx0ZXJuYXRpb24gZGV0ZWN0ZWQnKTtcbiAgICB9XG5cbiAgICAvLyBBbHRlcm5hdGlvbiB3aXRoIG92ZXJsYXAgKGUuZy4sIChhfGEpKilcbiAgICBjb25zdCBhbHRlcm5hdGlvbk1hdGNoID0gc291cmNlLm1hdGNoKC9cXCgoW158KV0rKVxcfChbXildKylcXCkvZyk7XG4gICAgaWYgKGFsdGVybmF0aW9uTWF0Y2gpIHtcbiAgICAgIGZvciAoY29uc3QgbWF0Y2ggb2YgYWx0ZXJuYXRpb25NYXRjaCkge1xuICAgICAgICBjb25zdCBwYXJ0cyA9IG1hdGNoLnNsaWNlKDEsIC0xKS5zcGxpdCgnfCcpO1xuICAgICAgICBpZiAocGFydHMuc29tZSgocGFydCwgaSkgPT4gcGFydHMuc2xpY2UoaSArIDEpLmluY2x1ZGVzKHBhcnQpKSkge1xuICAgICAgICAgIHJpc2tzLnB1c2goJ092ZXJsYXBwaW5nIGFsdGVybmF0aW9uIGRldGVjdGVkJyk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBDYXRhc3Ryb3BoaWMgYmFja3RyYWNraW5nIHBhdHRlcm5zXG4gICAgLy8gQ2hlY2sgZm9yIHBhdHRlcm5zIGxpa2UgKC4rKSssICguKikrLCBldGMuIHRoYXQgY2FuIGNhdXNlIGV4cG9uZW50aWFsIGJhY2t0cmFja2luZ1xuICAgIGlmICgvXFwoW14pXSpcXC5cXCtbXildKlxcKVxcKy8udGVzdChzb3VyY2UpIHx8IC9cXChbXildKlxcLlxcKlteKV0qXFwpXFwrLy50ZXN0KHNvdXJjZSkgfHwgL1xcKFteKV0qXFxcXHdcXCtbXildKlxcKVxcKy8udGVzdChzb3VyY2UpKSB7XG4gICAgICByaXNrcy5wdXNoKCdQb3RlbnRpYWwgY2F0YXN0cm9waGljIGJhY2t0cmFja2luZycpO1xuICAgIH1cblxuICAgIC8vIFVuYm91bmRlZCBsb29rYWhlYWQvbG9va2JlaGluZCB3aXRoIHF1YW50aWZpZXJzXG4gICAgaWYgKC9cXChcXD9bPSE8XS4qWysqXS8udGVzdChzb3VyY2UpKSB7XG4gICAgICByaXNrcy5wdXNoKCdVbmJvdW5kZWQgbG9va2FoZWFkL2xvb2tiZWhpbmQnKTtcbiAgICB9XG5cbiAgICAvLyBQb2x5bm9taWFsIHBhdHRlcm5zIChtdWx0aXBsZSBxdWFudGlmaWVycyBpbiBzZXF1ZW5jZSlcbiAgICBjb25zdCBxdWFudGlmaWVyQ291bnQgPSAoc291cmNlLm1hdGNoKC9bKyo/XXxcXHtcXGQqLD9cXGQqXFx9L2cpIHx8IFtdKS5sZW5ndGg7XG4gICAgaWYgKHF1YW50aWZpZXJDb3VudCA+IDMpIHtcbiAgICAgIHJpc2tzLnB1c2goJ011bHRpcGxlIHF1YW50aWZpZXJzIGRldGVjdGVkJyk7XG4gICAgfVxuXG4gICAgLy8gRGV0ZXJtaW5lIGNvbXBsZXhpdHkgYW5kIHNhZmUgY29udGVudCBsZW5ndGhcbiAgICBsZXQgY29tcGxleGl0eTogJ2xvdycgfCAnbWVkaXVtJyB8ICdoaWdoJztcbiAgICBsZXQgbWF4U2FmZUxlbmd0aDogbnVtYmVyO1xuICAgIFxuICAgIGlmIChyaXNrcy5sZW5ndGggPT09IDApIHtcbiAgICAgIGlmIChxdWFudGlmaWVyQ291bnQgPT09IDApIHtcbiAgICAgICAgY29tcGxleGl0eSA9ICdsb3cnO1xuICAgICAgICBtYXhTYWZlTGVuZ3RoID0gdGhpcy5DT01QTEVYSVRZX0xJTUlUUy5sb3c7XG4gICAgICB9IGVsc2UgaWYgKHF1YW50aWZpZXJDb3VudCA8PSAzKSB7XG4gICAgICAgIGNvbXBsZXhpdHkgPSAnbWVkaXVtJztcbiAgICAgICAgbWF4U2FmZUxlbmd0aCA9IHRoaXMuQ09NUExFWElUWV9MSU1JVFMubWVkaXVtO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY29tcGxleGl0eSA9ICdoaWdoJztcbiAgICAgICAgbWF4U2FmZUxlbmd0aCA9IHRoaXMuQ09NUExFWElUWV9MSU1JVFMuaGlnaDtcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKHJpc2tzLmxlbmd0aCA9PT0gMSkge1xuICAgICAgY29tcGxleGl0eSA9ICdoaWdoJztcbiAgICAgIG1heFNhZmVMZW5ndGggPSB0aGlzLkNPTVBMRVhJVFlfTElNSVRTLmhpZ2g7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbXBsZXhpdHkgPSAnaGlnaCc7XG4gICAgICBtYXhTYWZlTGVuZ3RoID0gdGhpcy5DT01QTEVYSVRZX0xJTUlUUy5oaWdoO1xuICAgIH1cblxuICAgIHJldHVybiB7XG4gICAgICBzYWZlOiByaXNrcy5sZW5ndGggPT09IDAsXG4gICAgICByaXNrcyxcbiAgICAgIGNvbXBsZXhpdHksXG4gICAgICBtYXhTYWZlTGVuZ3RoXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgcmVnZXggcGF0dGVybiB3aXRoIHNhZmV0eSBhbmFseXNpc1xuICAgKi9cbiAgc3RhdGljIGNyZWF0ZVNhZmVQYXR0ZXJuKHBhdHRlcm46IHN0cmluZywgZmxhZ3M/OiBzdHJpbmcpOiBSZWdFeHAge1xuICAgIGNvbnN0IHJlZ2V4ID0gbmV3IFJlZ0V4cChwYXR0ZXJuLCBmbGFncyk7XG4gICAgY29uc3QgYW5hbHlzaXMgPSB0aGlzLmFuYWx5emVQYXR0ZXJuKHJlZ2V4KTtcbiAgICBcbiAgICBpZiAoIWFuYWx5c2lzLnNhZmUpIHtcbiAgICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgICAgdHlwZTogJ1VQREFURV9TRUNVUklUWV9WSU9MQVRJT04nLFxuICAgICAgICBzZXZlcml0eTogJ01FRElVTScsXG4gICAgICAgIHNvdXJjZTogJ1JlZ2V4VmFsaWRhdG9yJyxcbiAgICAgICAgZGV0YWlsczogJ1BvdGVudGlhbGx5IGRhbmdlcm91cyByZWdleCBwYXR0ZXJuIGNyZWF0ZWQnLFxuICAgICAgICBhZGRpdGlvbmFsRGF0YToge1xuICAgICAgICAgIHBhdHRlcm4sXG4gICAgICAgICAgcmlza3M6IGFuYWx5c2lzLnJpc2tzXG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH1cbiAgICBcbiAgICByZXR1cm4gcmVnZXg7XG4gIH1cbn0iXX0=