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.

167 lines 21.5 kB
/** * InputNormalizer - Industry-standard input normalization at the boundary * * Architecture Pattern: Defense in Depth - Normalize Once at Entry Point * * This utility enforces the principle of "normalize at the boundary" for security: * - Recursively normalizes ALL string values in input objects * - Uses UnicodeValidator.normalize() as the single normalization implementation * - Returns normalized object + aggregated security issues * - Validators receive clean, pre-normalized data * * Security Benefits: * - Single normalization point (no scattered validateContent calls) * - Can't forget to normalize a field * - Separation of concerns (normalization vs validation) * - Easier to audit and maintain * * Usage: * ```typescript * // At the validation boundary (GenericElementValidator) * const normalized = InputNormalizer.normalize(data); * * if (normalized.hasHighOrCriticalIssues) { * return ValidatorHelpers.fail(normalized.errors); * } * * // Now validate the normalized data * const result = await this.validateFields(normalized.data); * ``` * * @see UnicodeValidator.normalize() - Core normalization implementation * @see GenericElementValidator - Uses this at the boundary */ import { UnicodeValidator } from './validators/unicodeValidator.js'; import { SecurityMonitor } from './securityMonitor.js'; import { escalateSeverity } from './constants.js'; /** * InputNormalizer - Recursively normalizes all string values in an object * * This class provides a centralized, recursive normalization utility that: * - Walks object/array structures recursively * - Normalizes every string value using UnicodeValidator * - Aggregates security issues across all fields * - Preserves non-string values unchanged * - Tracks normalization path for detailed error reporting */ export class InputNormalizer { /** * Normalize all string values in an object/array structure * * @param input - Input data to normalize (object, array, or primitive) * @param path - Current path in object tree (for error tracking) * @returns Normalization result with normalized data and detected issues * * @example * ```typescript * const input = { * name: 'Test\u200BName', // Zero-width space * description: 'Café', // Legitimate Unicode * nested: { * field: 'Val\u202Eue' // Direction override * } * }; * * const result = InputNormalizer.normalize(input); * console.log(result.data.name); // 'TestName' (cleaned) * console.log(result.warnings); // ['name: Zero-width characters detected', ...] * ``` */ static normalize(input, path = '$') { const errors = []; const warnings = []; const issuesByPath = new Map(); let maxSeverity; // Recursive normalization implementation const normalizeValue = (value, currentPath) => { // Handle null/undefined if (value === null || value === undefined) { return value; } // Handle strings - this is where normalization happens if (typeof value === 'string') { const unicodeResult = UnicodeValidator.normalize(value); // Track issues for this field if (unicodeResult.detectedIssues && unicodeResult.detectedIssues.length > 0) { const pathIssues = unicodeResult.detectedIssues.map(issue => `${currentPath}: ${issue}`); issuesByPath.set(currentPath, unicodeResult.detectedIssues); // Categorize by severity if (unicodeResult.severity) { maxSeverity = escalateSeverity(maxSeverity, unicodeResult.severity); if (unicodeResult.severity === 'critical' || unicodeResult.severity === 'high') { errors.push(...pathIssues); } else { warnings.push(...pathIssues); } } else { warnings.push(...pathIssues); } } return unicodeResult.normalizedContent; } // Handle arrays - recursively normalize each element if (Array.isArray(value)) { return value.map((item, index) => normalizeValue(item, `${currentPath}[${index}]`)); } // Handle objects - recursively normalize each property if (typeof value === 'object') { const normalized = {}; for (const [key, val] of Object.entries(value)) { normalized[key] = normalizeValue(val, `${currentPath}.${key}`); } return normalized; } // Preserve other types unchanged (numbers, booleans, etc.) return value; }; // Normalize the input const normalizedData = normalizeValue(input, path); // Calculate aggregate status (#1782-6: separate critical-only from high+critical) const hasIssues = errors.length > 0 || warnings.length > 0; const hasCriticalIssues = maxSeverity === 'critical'; const hasHighOrCriticalIssues = maxSeverity === 'critical' || maxSeverity === 'high'; // Log if high or critical issues detected if (hasHighOrCriticalIssues) { SecurityMonitor.logSecurityEvent({ type: 'UNICODE_VALIDATION_ERROR', severity: maxSeverity === 'critical' ? 'CRITICAL' : 'HIGH', source: 'InputNormalizer', details: `Critical Unicode issues detected during normalization: ${errors.join('; ')}` }); } return { data: normalizedData, hasIssues, hasCriticalIssues, hasHighOrCriticalIssues, errors, warnings, issuesByPath, maxSeverity }; } /** * Quick check if input needs normalization (has suspicious Unicode) * * This is an optimization - you can check if normalization is needed * before doing expensive recursive normalization. * * @param input - Input data to check * @returns True if input contains potentially dangerous Unicode */ static needsNormalization(input) { if (typeof input === 'string') { return UnicodeValidator.containsDangerousUnicode(input); } if (Array.isArray(input)) { return input.some(item => this.needsNormalization(item)); } if (input && typeof input === 'object') { return Object.values(input).some(value => this.needsNormalization(value)); } return false; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiSW5wdXROb3JtYWxpemVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3NlY3VyaXR5L0lucHV0Tm9ybWFsaXplci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FnQ0c7QUFFSCxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxrQ0FBa0MsQ0FBQztBQUNwRSxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDdkQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUF3QmxEOzs7Ozs7Ozs7R0FTRztBQUNILE1BQU0sT0FBTyxlQUFlO0lBQzFCOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7T0FxQkc7SUFDSCxNQUFNLENBQUMsU0FBUyxDQUFjLEtBQVEsRUFBRSxPQUFlLEdBQUc7UUFDeEQsTUFBTSxNQUFNLEdBQWEsRUFBRSxDQUFDO1FBQzVCLE1BQU0sUUFBUSxHQUFhLEVBQUUsQ0FBQztRQUM5QixNQUFNLFlBQVksR0FBRyxJQUFJLEdBQUcsRUFBb0IsQ0FBQztRQUNqRCxJQUFJLFdBQStELENBQUM7UUFFcEUseUNBQXlDO1FBQ3pDLE1BQU0sY0FBYyxHQUFHLENBQUMsS0FBYyxFQUFFLFdBQW1CLEVBQVcsRUFBRTtZQUN0RSx3QkFBd0I7WUFDeEIsSUFBSSxLQUFLLEtBQUssSUFBSSxJQUFJLEtBQUssS0FBSyxTQUFTLEVBQUUsQ0FBQztnQkFDMUMsT0FBTyxLQUFLLENBQUM7WUFDZixDQUFDO1lBRUQsdURBQXVEO1lBQ3ZELElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxFQUFFLENBQUM7Z0JBQzlCLE1BQU0sYUFBYSxHQUFHLGdCQUFnQixDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQztnQkFFeEQsOEJBQThCO2dCQUM5QixJQUFJLGFBQWEsQ0FBQyxjQUFjLElBQUksYUFBYSxDQUFDLGNBQWMsQ0FBQyxNQUFNLEdB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QkFBdUI7WUFDdkIsTUFBTTtZQUNOLFFBQVE7WUFDUixZQUFZO1lBQ1osV0FBVztTQUNaLENBQUM7SUFDSixDQUFDO0lBRUQ7Ozs7Ozs7O09BUUc7SUFDSCxNQUFNLENBQUMsa0JBQWtCLENBQUMsS0FBYztRQUN0QyxJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsRUFBRSxDQUFDO1lBQzlCLE9BQU8sZ0JBQWdCLENBQUMsd0JBQXdCLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDMUQsQ0FBQztRQUVELElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQ3pCLE9BQU8sS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBQzNELENBQUM7UUFFRCxJQUFJLEtBQUssSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLEVBQUUsQ0FBQztZQUN2QyxPQUFPLE1BQU0sQ0FBQyxNQUFNLENBQUMsS0FBZ0MsQ0FBQyxDQUFDLElBQUksQ0FDekQsS0FBSyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsa0JBQWtCLENBQUMsS0FBSyxDQUFDLENBQ3hDLENBQUM7UUFDSixDQUFDO1FBRUQsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIElucHV0Tm9ybWFsaXplciAtIEluZHVzdHJ5LXN0YW5kYXJkIGlucHV0IG5vcm1hbGl6YXRpb24gYXQgdGhlIGJvdW5kYXJ5XG4gKlxuICogQXJjaGl0ZWN0dXJlIFBhdHRlcm46IERlZmVuc2UgaW4gRGVwdGggLSBOb3JtYWxpemUgT25jZSBhdCBFbnRyeSBQb2ludFxuICpcbiAqIFRoaXMgdXRpbGl0eSBlbmZvcmNlcyB0aGUgcHJpbmNpcGxlIG9mIFwibm9ybWFsaXplIGF0IHRoZSBib3VuZGFyeVwiIGZvciBzZWN1cml0eTpcbiAqIC0gUmVjdXJzaXZlbHkgbm9ybWFsaXplcyBBTEwgc3RyaW5nIHZhbHVlcyBpbiBpbnB1dCBvYmplY3RzXG4gKiAtIFVzZXMgVW5pY29kZVZhbGlkYXRvci5ub3JtYWxpemUoKSBhcyB0aGUgc2luZ2xlIG5vcm1hbGl6YXRpb24gaW1wbGVtZW50YXRpb25cbiAqIC0gUmV0dXJucyBub3JtYWxpemVkIG9iamVjdCArIGFnZ3JlZ2F0ZWQgc2VjdXJpdHkgaXNzdWVzXG4gKiAtIFZhbGlkYXRvcnMgcmVjZWl2ZSBjbGVhbiwgcHJlLW5vcm1hbGl6ZWQgZGF0YVxuICpcbiAqIFNlY3VyaXR5IEJlbmVmaXRzOlxuICogLSBTaW5nbGUgbm9ybWFsaXphdGlvbiBwb2ludCAobm8gc2NhdHRlcmVkIHZhbGlkYXRlQ29udGVudCBjYWxscylcbiAqIC0gQ2FuJ3QgZm9yZ2V0IHRvIG5vcm1hbGl6ZSBhIGZpZWxkXG4gKiAtIFNlcGFyYXRpb24gb2YgY29uY2VybnMgKG5vcm1hbGl6YXRpb24gdnMgdmFsaWRhdGlvbilcbiAqIC0gRWFzaWVyIHRvIGF1ZGl0IGFuZCBtYWludGFpblxuICpcbiAqIFVzYWdlOlxuICogYGBgdHlwZXNjcmlwdFxuICogLy8gQXQgdGhlIHZhbGlkYXRpb24gYm91bmRhcnkgKEdlbmVyaWNFbGVtZW50VmFsaWRhdG9yKVxuICogY29uc3Qgbm9ybWFsaXplZCA9IElucHV0Tm9ybWFsaXplci5ub3JtYWxpemUoZGF0YSk7XG4gKlxuICogaWYgKG5vcm1hbGl6ZWQuaGFzSGlnaE9yQ3JpdGljYWxJc3N1ZXMpIHtcbiAqICAgcmV0dXJuIFZhbGlkYXRvckhlbHBlcnMuZmFpbChub3JtYWxpemVkLmVycm9ycyk7XG4gKiB9XG4gKlxuICogLy8gTm93IHZhbGlkYXRlIHRoZSBub3JtYWxpemVkIGRhdGFcbiAqIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHRoaXMudmFsaWRhdGVGaWVsZHMobm9ybWFsaXplZC5kYXRhKTtcbiAqIGBgYFxuICpcbiAqIEBzZWUgVW5pY29kZVZhbGlkYXRvci5ub3JtYWxpemUoKSAtIENvcmUgbm9ybWFsaXphdGlvbiBpbXBsZW1lbnRhdGlvblxuICogQHNlZSBHZW5lcmljRWxlbWVudFZhbGlkYXRvciAtIFVzZXMgdGhpcyBhdCB0aGUgYm91bmRhcnlcbiAqL1xuXG5pbXBvcnQgeyBVbmljb2RlVmFsaWRhdG9yIH0gZnJvbSAnLi92YWxpZGF0b3JzL3VuaWNvZGVWYWxpZGF0b3IuanMnO1xuaW1wb3J0IHsgU2VjdXJpdHlNb25pdG9yIH0gZnJvbSAnLi9zZWN1cml0eU1vbml0b3IuanMnO1xuaW1wb3J0IHsgZXNjYWxhdGVTZXZlcml0eSB9IGZyb20gJy4vY29uc3RhbnRzLmpzJztcblxuLyoqXG4gKiBSZXN1bHQgZnJvbSBpbnB1dCBub3JtYWxpemF0aW9uXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgTm9ybWFsaXphdGlvblJlc3VsdDxUID0gdW5rbm93bj4ge1xuICAvKiogVGhlIG5vcm1hbGl6ZWQgZGF0YSBzdHJ1Y3R1cmUgd2l0aCBhbGwgc3RyaW5ncyBub3JtYWxpemVkICovXG4gIGRhdGE6IFQ7XG4gIC8qKiBXaGV0aGVyIG5vcm1hbGl6YXRpb24gZGV0ZWN0ZWQgYW55IGlzc3VlcyAqL1xuICBoYXNJc3N1ZXM6IGJvb2xlYW47XG4gIC8qKiBXaGV0aGVyIGNyaXRpY2FsLXNldmVyaXR5IGlzc3VlcyB3ZXJlIGRldGVjdGVkICovXG4gIGhhc0NyaXRpY2FsSXNzdWVzOiBib29sZWFuO1xuICAvKiogV2hldGhlciBoaWdoLW9yLWNyaXRpY2FsIGlzc3VlcyB3ZXJlIGRldGVjdGVkIHRoYXQgc2hvdWxkIGZhaWwgdmFsaWRhdGlvbiAoIzE3ODItNikgKi9cbiAgaGFzSGlnaE9yQ3JpdGljYWxJc3N1ZXM6IGJvb2xlYW47XG4gIC8qKiBBbGwgZXJyb3JzIGRldGVjdGVkIGR1cmluZyBub3JtYWxpemF0aW9uIChjcml0aWNhbCBpc3N1ZXMpICovXG4gIGVycm9yczogc3RyaW5nW107XG4gIC8qKiBBbGwgd2FybmluZ3MgZGV0ZWN0ZWQgZHVyaW5nIG5vcm1hbGl6YXRpb24gKG5vbi1jcml0aWNhbCBpc3N1ZXMpICovXG4gIHdhcm5pbmdzOiBzdHJpbmdbXTtcbiAgLyoqIERldGFpbGVkIGlzc3VlcyBieSBwYXRoIChmb3IgZGVidWdnaW5nKSAqL1xuICBpc3N1ZXNCeVBhdGg6IE1hcDxzdHJpbmcsIHN0cmluZ1tdPjtcbiAgLyoqIEhpZ2hlc3Qgc2V2ZXJpdHkgbGV2ZWwgZGV0ZWN0ZWQgKi9cbiAgbWF4U2V2ZXJpdHk/OiAnbG93JyB8ICdtZWRpdW0nIHwgJ2hpZ2gnIHwgJ2NyaXRpY2FsJztcbn1cblxuLyoqXG4gKiBJbnB1dE5vcm1hbGl6ZXIgLSBSZWN1cnNpdmVseSBub3JtYWxpemVzIGFsbCBzdHJpbmcgdmFsdWVzIGluIGFuIG9iamVjdFxuICpcbiAqIFRoaXMgY2xhc3MgcHJvdmlkZXMgYSBjZW50cmFsaXplZCwgcmVjdXJzaXZlIG5vcm1hbGl6YXRpb24gdXRpbGl0eSB0aGF0OlxuICogLSBXYWxrcyBvYmplY3QvYXJyYXkgc3RydWN0dXJlcyByZWN1cnNpdmVseVxuICogLSBOb3JtYWxpemVzIGV2ZXJ5IHN0cmluZyB2YWx1ZSB1c2luZyBVbmljb2RlVmFsaWRhdG9yXG4gKiAtIEFnZ3JlZ2F0ZXMgc2VjdXJpdHkgaXNzdWVzIGFjcm9zcyBhbGwgZmllbGRzXG4gKiAtIFByZXNlcnZlcyBub24tc3RyaW5nIHZhbHVlcyB1bmNoYW5nZWRcbiAqIC0gVHJhY2tzIG5vcm1hbGl6YXRpb24gcGF0aCBmb3IgZGV0YWlsZWQgZXJyb3IgcmVwb3J0aW5nXG4gKi9cbmV4cG9ydCBjbGFzcyBJbnB1dE5vcm1hbGl6ZXIge1xuICAvKipcbiAgICogTm9ybWFsaXplIGFsbCBzdHJpbmcgdmFsdWVzIGluIGFuIG9iamVjdC9hcnJheSBzdHJ1Y3R1cmVcbiAgICpcbiAgICogQHBhcmFtIGlucHV0IC0gSW5wdXQgZGF0YSB0byBub3JtYWxpemUgKG9iamVjdCwgYXJyYXksIG9yIHByaW1pdGl2ZSlcbiAgICogQHBhcmFtIHBhdGggLSBDdXJyZW50IHBhdGggaW4gb2JqZWN0IHRyZWUgKGZvciBlcnJvciB0cmFja2luZylcbiAgICogQHJldHVybnMgTm9ybWFsaXphdGlvbiByZXN1bHQgd2l0aCBub3JtYWxpemVkIGRhdGEgYW5kIGRldGVjdGVkIGlzc3Vlc1xuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiBgYGB0eXBlc2NyaXB0XG4gICAqIGNvbnN0IGlucHV0ID0ge1xuICAgKiAgIG5hbWU6ICdUZXN0XFx1MjAwQk5hbWUnLCAgLy8gWmVyby13aWR0aCBzcGFjZVxuICAgKiAgIGRlc2NyaXB0aW9uOiAnQ2Fmw6knLCAgICAgIC8vIExlZ2l0aW1hdGUgVW5pY29kZVxuICAgKiAgIG5lc3RlZDoge1xuICAgKiAgICAgZmllbGQ6ICdWYWxcXHUyMDJFdWUnICAgIC8vIERpcmVjdGlvbiBvdmVycmlkZVxuICAgKiAgIH1cbiAgICogfTtcbiAgICpcbiAgICogY29uc3QgcmVzdWx0ID0gSW5wdXROb3JtYWxpemVyLm5vcm1hbGl6ZShpbnB1dCk7XG4gICAqIGNvbnNvbGUubG9nKHJlc3VsdC5kYXRhLm5hbWUpOyAgLy8gJ1Rlc3ROYW1lJyAoY2xlYW5lZClcbiAgICogY29uc29sZS5sb2cocmVzdWx0Lndhcm5pbmdzKTsgICAvLyBbJ25hbWU6IFplcm8td2lkdGggY2hhcmFjdGVycyBkZXRlY3RlZCcsIC4uLl1cbiAgICogYGBgXG4gICAqL1xuICBzdGF0aWMgbm9ybWFsaXplPFQgPSB1bmtub3duPihpbnB1dDogVCwgcGF0aDogc3RyaW5nID0gJyQnKTogTm9ybWFsaXphdGlvblJlc3VsdDxUPiB7XG4gICAgY29uc3QgZXJyb3JzOiBzdHJpbmdbXSA9IFtdO1xuICAgIGNvbnN0IHdhcm5pbmdzOiBzdHJpbmdbXSA9IFtdO1xuICAgIGNvbnN0IGlzc3Vlc0J5UGF0aCA9IG5ldyBNYXA8c3RyaW5nLCBzdHJpbmdbXT4oKTtcbiAgICBsZXQgbWF4U2V2ZXJpdHk6ICdsb3cnIHwgJ21lZGl1bScgfCAnaGlnaCcgfCAnY3JpdGljYWwnIHwgdW5kZWZpbmVkO1xuXG4gICAgLy8gUmVjdXJzaXZlIG5vcm1hbGl6YXRpb24gaW1wbGVtZW50YXRpb25cbiAgICBjb25zdCBub3JtYWxpemVWYWx1ZSA9ICh2YWx1ZTogdW5rbm93biwgY3VycmVudFBhdGg6IHN0cmluZyk6IHVua25vd24gPT4ge1xuICAgICAgLy8gSGFuZGxlIG51bGwvdW5kZWZpbmVkXG4gICAgICBpZiAodmFsdWUgPT09IG51bGwgfHwgdmFsdWUgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICByZXR1cm4gdmFsdWU7XG4gICAgICB9XG5cbiAgICAgIC8vIEhhbmRsZSBzdHJpbmdzIC0gdGhpcyBpcyB3aGVyZSBub3JtYWxpemF0aW9uIGhhcHBlbnNcbiAgICAgIGlmICh0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgIGNvbnN0IHVuaWNvZGVSZXN1bHQgPSBVbmljb2RlVmFsaWRhdG9yLm5vcm1hbGl6ZSh2YWx1ZSk7XG5cbiAgICAgICAgLy8gVHJhY2sgaXNzdWVzIGZvciB0aGlzIGZpZWxkXG4gICAgICAgIGlmICh1bmljb2RlUmVzdWx0LmRldGVjdGVkSXNzdWVzICYmIHVuaWNvZGVSZXN1bHQuZGV0ZWN0ZWRJc3N1ZXMubGVuZ3RoID4gMCkge1xuICAgICAgICAgIGNvbnN0IHBhdGhJc3N1ZXMgPSB1bmljb2RlUmVzdWx0LmRldGVjdGVkSXNzdWVzLm1hcChcbiAgICAgICAgICAgIGlzc3VlID0+IGAke2N1cnJlbnRQYXRofTogJHtpc3N1ZX1gXG4gICAgICAgICAgKTtcbiAgICAgICAgICBpc3N1ZXNCeVBhdGguc2V0KGN1cnJlbnRQYXRoLCB1bmljb2RlUmVzdWx0LmRldGVjdGVkSXNzdWVzKTtcblxuICAgICAgICAgIC8vIENhdGVnb3JpemUgYnkgc2V2ZXJpdHlcbiAgICAgICAgICBpZiAodW5pY29kZVJlc3VsdC5zZXZlcml0eSkge1xuICAgICAgICAgICAgbWF4U2V2ZXJpdHkgPSBlc2NhbGF0ZVNldmVyaXR5KG1heFNldmVyaXR5LCB1bmljb2RlUmVzdWx0LnNldmVyaXR5KTtcblxuICAgICAgICAgICAgaWYgKHVuaWNvZGVSZXN1bHQuc2V2ZXJpdHkgPT09ICdjcml0aWNhbCcgfHwgdW5pY29kZVJlc3VsdC5zZXZlcml0eSA9PT0gJ2hpZ2gnKSB7XG4gICAgICAgICAgICAgIGVycm9ycy5wdXNoKC4uLnBhdGhJc3N1ZXMpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgd2FybmluZ3MucHVzaCguLi5wYXRoSXNzdWVzKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgd2FybmluZ3MucHVzaCguLi5wYXRoSXNzdWVzKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gdW5pY29kZVJlc3VsdC5ub3JtYWxpemVkQ29udGVudDtcbiAgICAgIH1cblxuICAgICAgLy8gSGFuZGxlIGFycmF5cyAtIHJlY3Vyc2l2ZWx5IG5vcm1hbGl6ZSBlYWNoIGVsZW1lbnRcbiAgICAgIGlmIChBcnJheS5pc0FycmF5KHZhbHVlKSkge1xuICAgICAgICByZXR1cm4gdmFsdWUubWFwKChpdGVtLCBpbmRleCkgPT5cbiAgICAgICAgICBub3JtYWxpemVWYWx1ZShpdGVtLCBgJHtjdXJyZW50UGF0aH1bJHtpbmRleH1dYClcbiAgICAgICAgKTtcbiAgICAgIH1cblxuICAgICAgLy8gSGFuZGxlIG9iamVjdHMgLSByZWN1cnNpdmVseSBub3JtYWxpemUgZWFjaCBwcm9wZXJ0eVxuICAgICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ29iamVjdCcpIHtcbiAgICAgICAgY29uc3Qgbm9ybWFsaXplZDogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gPSB7fTtcbiAgICAgICAgZm9yIChjb25zdCBba2V5LCB2YWxdIG9mIE9iamVjdC5lbnRyaWVzKHZhbHVlIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+KSkge1xuICAgICAgICAgIG5vcm1hbGl6ZWRba2V5XSA9IG5vcm1hbGl6ZVZhbHVlKHZhbCwgYCR7Y3VycmVudFBhdGh9LiR7a2V5fWApO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBub3JtYWxpemVkO1xuICAgICAgfVxuXG4gICAgICAvLyBQcmVzZXJ2ZSBvdGhlciB0eXBlcyB1bmNoYW5nZWQgKG51bWJlcnMsIGJvb2xlYW5zLCBldGMuKVxuICAgICAgcmV0dXJuIHZhbHVlO1xuICAgIH07XG5cbiAgICAvLyBOb3JtYWxpemUgdGhlIGlucHV0XG4gICAgY29uc3Qgbm9ybWFsaXplZERhdGEgPSBub3JtYWxpemVWYWx1ZShpbnB1dCwgcGF0aCkgYXMgVDtcblxuICAgIC8vIENhbGN1bGF0ZSBhZ2dyZWdhdGUgc3RhdHVzICgjMTc4Mi02OiBzZXBhcmF0ZSBjcml0aWNhbC1vbmx5IGZyb20gaGlnaCtjcml0aWNhbClcbiAgICBjb25zdCBoYXNJc3N1ZXMgPSBlcnJvcnMubGVuZ3RoID4gMCB8fCB3YXJuaW5ncy5sZW5ndGggPiAwO1xuICAgIGNvbnN0IGhhc0NyaXRpY2FsSXNzdWVzID0gbWF4U2V2ZXJpdHkgPT09ICdjcml0aWNhbCc7XG4gICAgY29uc3QgaGFzSGlnaE9yQ3JpdGljYWxJc3N1ZXMgPSBtYXhTZXZlcml0eSA9PT0gJ2NyaXRpY2FsJyB8fCBtYXhTZXZlcml0eSA9PT0gJ2hpZ2gnO1xuXG4gICAgLy8gTG9nIGlmIGhpZ2ggb3IgY3JpdGljYWwgaXNzdWVzIGRldGVjdGVkXG4gICAgaWYgKGhhc0hpZ2hPckNyaXRpY2FsSXNzdWVzKSB7XG4gICAgICBTZWN1cml0eU1vbml0b3IubG9nU2VjdXJpdHlFdmVudCh7XG4gICAgICAgIHR5cGU6ICdVTklDT0RFX1ZBTElEQVRJT05fRVJST1InLFxuICAgICAgICBzZXZlcml0eTogbWF4U2V2ZXJpdHkgPT09ICdjcml0aWNhbCcgPyAnQ1JJVElDQUwnIDogJ0hJR0gnLFxuICAgICAgICBzb3VyY2U6ICdJbnB1dE5vcm1hbGl6ZXInLFxuICAgICAgICBkZXRhaWxzOiBgQ3JpdGljYWwgVW5pY29kZSBpc3N1ZXMgZGV0ZWN0ZWQgZHVyaW5nIG5vcm1hbGl6YXRpb246ICR7ZXJyb3JzLmpvaW4oJzsgJyl9YFxuICAgICAgfSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIGRhdGE6IG5vcm1hbGl6ZWREYXRhLFxuICAgICAgaGFzSXNzdWVzLFxuICAgICAgaGFzQ3JpdGljYWxJc3N1ZXMsXG4gICAgICBoYXNIaWdoT3JDcml0aWNhbElzc3VlcyxcbiAgICAgIGVycm9ycyxcbiAgICAgIHdhcm5pbmdzLFxuICAgICAgaXNzdWVzQnlQYXRoLFxuICAgICAgbWF4U2V2ZXJpdHlcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIFF1aWNrIGNoZWNrIGlmIGlucHV0IG5lZWRzIG5vcm1hbGl6YXRpb24gKGhhcyBzdXNwaWNpb3VzIFVuaWNvZGUpXG4gICAqXG4gICAqIFRoaXMgaXMgYW4gb3B0aW1pemF0aW9uIC0geW91IGNhbiBjaGVjayBpZiBub3JtYWxpemF0aW9uIGlzIG5lZWRlZFxuICAgKiBiZWZvcmUgZG9pbmcgZXhwZW5zaXZlIHJlY3Vyc2l2ZSBub3JtYWxpemF0aW9uLlxuICAgKlxuICAgKiBAcGFyYW0gaW5wdXQgLSBJbnB1dCBkYXRhIHRvIGNoZWNrXG4gICAqIEByZXR1cm5zIFRydWUgaWYgaW5wdXQgY29udGFpbnMgcG90ZW50aWFsbHkgZGFuZ2Vyb3VzIFVuaWNvZGVcbiAgICovXG4gIHN0YXRpYyBuZWVkc05vcm1hbGl6YXRpb24oaW5wdXQ6IHVua25vd24pOiBib29sZWFuIHtcbiAgICBpZiAodHlwZW9mIGlucHV0ID09PSAnc3RyaW5nJykge1xuICAgICAgcmV0dXJuIFVuaWNvZGVWYWxpZGF0b3IuY29udGFpbnNEYW5nZXJvdXNVbmljb2RlKGlucHV0KTtcbiAgICB9XG5cbiAgICBpZiAoQXJyYXkuaXNBcnJheShpbnB1dCkpIHtcbiAgICAgIHJldHVybiBpbnB1dC5zb21lKGl0ZW0gPT4gdGhpcy5uZWVkc05vcm1hbGl6YXRpb24oaXRlbSkpO1xuICAgIH1cblxuICAgIGlmIChpbnB1dCAmJiB0eXBlb2YgaW5wdXQgPT09ICdvYmplY3QnKSB7XG4gICAgICByZXR1cm4gT2JqZWN0LnZhbHVlcyhpbnB1dCBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPikuc29tZShcbiAgICAgICAgdmFsdWUgPT4gdGhpcy5uZWVkc05vcm1hbGl6YXRpb24odmFsdWUpXG4gICAgICApO1xuICAgIH1cblxuICAgIHJldHVybiBmYWxzZTtcbiAgfVxufVxuIl19