@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.
213 lines • 22.5 kB
JavaScript
/**
* Pattern Decryption Service with LLM Context Protection
*
* Part of Issue #1321 Phase 2: Memory Security Architecture
*
* PURPOSE:
* Provides secure access to decrypt patterns while preventing
* decryption in LLM request contexts. All decryption attempts
* are audited for security monitoring.
*
* SECURITY:
* - Prevents decryption in LLM contexts (blocks pattern leaks to LLM)
* - Requires explicit authorization
* - Audits all decryption attempts
* - Uses ContextTracker to detect execution context
*
* REFACTOR NOTE:
* Converted from static class to instance-based for DI architecture compatibility.
* PatternDecryptor now requires PatternEncryptor and ContextTracker dependencies
* to be injected via constructor for proper DI lifecycle management.
*
* @module PatternDecryptor
*/
import { logger } from '../../utils/logger.js';
/**
* Decryption audit log
* Maintains a record of all decryption attempts for security monitoring
*/
class DecryptionAuditLog {
attempts = [];
MAX_LOG_SIZE = 1000;
/**
* Log a decryption attempt
*
* @param attempt - Decryption attempt metadata
*/
log(attempt) {
this.attempts.push(attempt);
// Trim log if it exceeds max size
if (this.attempts.length > this.MAX_LOG_SIZE) {
this.attempts = this.attempts.slice(-this.MAX_LOG_SIZE);
}
// Always log to system logger for persistence
if (attempt.success) {
logger.info('Pattern decrypted', {
patternRef: attempt.patternRef,
contextType: attempt.contextType,
requestId: attempt.requestId,
});
}
else {
logger.warn('Pattern decryption denied', {
patternRef: attempt.patternRef,
contextType: attempt.contextType,
reason: attempt.denialReason,
error: attempt.error,
requestId: attempt.requestId,
});
}
}
/**
* Get all decryption attempts
*
* @returns Array of decryption attempts
*/
getAttempts() {
return [...this.attempts];
}
/**
* Get recent decryption attempts
*
* @param limit - Maximum number of attempts to return
* @returns Array of recent attempts
*/
getRecentAttempts(limit = 100) {
return this.attempts.slice(-limit);
}
/**
* Clear the audit log (useful for testing)
*/
clear() {
this.attempts = [];
logger.debug('Decryption audit log cleared');
}
}
/**
* PatternDecryptor service
*
* Provides controlled access to decrypt encrypted patterns with
* LLM context protection and audit logging.
*
* DI-COMPATIBLE: Instance-based service for dependency injection.
*/
export class PatternDecryptor {
encryptor;
contextTracker;
auditLog;
/**
* Create a new PatternDecryptor instance
*
* @param encryptor - PatternEncryptor instance for decryption operations
* @param contextTracker - ContextTracker instance for LLM context detection
*/
constructor(encryptor, contextTracker) {
this.encryptor = encryptor;
this.contextTracker = contextTracker;
this.auditLog = new DecryptionAuditLog();
}
/**
* Decrypt a sanitized pattern with security checks
*
* This method:
* 1. Checks if in LLM context (denies if true)
* 2. Validates pattern structure
* 3. Audits the decryption attempt
* 4. Decrypts the pattern using PatternEncryptor
*
* @param pattern - Sanitized pattern to decrypt
* @returns Decrypted pattern text
* @throws Error if decryption is not allowed or fails
*/
decryptPattern(pattern) {
const context = this.contextTracker.getContext();
const patternRef = pattern.ref;
// Security check: Prevent decryption in LLM context
if (this.contextTracker.isLLMContext()) {
const attempt = {
patternRef,
success: false,
timestamp: Date.now(),
contextType: context?.type || 'unknown',
requestId: context?.requestId,
denialReason: 'Decryption not allowed in LLM request context',
};
this.auditLog.log(attempt);
throw new Error('Pattern decryption blocked: Cannot decrypt patterns in LLM request context');
}
// Validate pattern structure
if (!pattern.encryptedPattern || !pattern.iv || !pattern.authTag) {
const attempt = {
patternRef,
success: false,
timestamp: Date.now(),
contextType: context?.type || 'unknown',
requestId: context?.requestId,
denialReason: 'Pattern not encrypted or missing required fields',
};
this.auditLog.log(attempt);
throw new Error('Pattern decryption failed: Pattern is not encrypted or missing required fields');
}
try {
// Build encrypted pattern structure
const encryptedPattern = {
encryptedData: pattern.encryptedPattern,
algorithm: pattern.algorithm || 'aes-256-gcm',
iv: pattern.iv,
authTag: pattern.authTag,
};
// Decrypt using PatternEncryptor
const decrypted = this.encryptor.decrypt(encryptedPattern);
// Log successful decryption
const attempt = {
patternRef,
success: true,
timestamp: Date.now(),
contextType: context?.type || 'unknown',
requestId: context?.requestId,
};
this.auditLog.log(attempt);
return decrypted;
}
catch (error) {
// Log failed decryption
const attempt = {
patternRef,
success: false,
timestamp: Date.now(),
contextType: context?.type || 'unknown',
requestId: context?.requestId,
error: error instanceof Error ? error.message : 'Unknown error',
};
this.auditLog.log(attempt);
throw error;
}
}
/**
* Get decryption audit log
*
* @param limit - Maximum number of attempts to return
* @returns Array of recent decryption attempts
*/
getAuditLog(limit) {
if (limit !== undefined) {
return this.auditLog.getRecentAttempts(limit);
}
return this.auditLog.getAttempts();
}
/**
* Clear the audit log (useful for testing)
*/
clearAuditLog() {
this.auditLog.clear();
}
/**
* Dispose of the decryptor and clean up resources
* Implements cleanup for proper DI lifecycle management
*/
async dispose() {
this.auditLog.clear();
logger.debug('PatternDecryptor disposed');
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUGF0dGVybkRlY3J5cHRvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9zZWN1cml0eS9lbmNyeXB0aW9uL1BhdHRlcm5EZWNyeXB0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FzQkc7QUFFSCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFnQy9DOzs7R0FHRztBQUNILE1BQU0sa0JBQWtCO0lBQ2QsUUFBUSxHQUF3QixFQUFFLENBQUM7SUFDMUIsWUFBWSxHQUFHLElBQUksQ0FBQztJQUVyQzs7OztPQUlHO0lBQ0gsR0FBRyxDQUFDLE9BQTBCO1FBQzVCLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBRTVCLGtDQUFrQztRQUNsQyxJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQztZQUM3QyxJQUFJLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDO1FBQzFELENBQUM7UUFFRCw4Q0FBOEM7UUFDOUMsSUFBSSxPQUFPLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDcEIsTUFBTSxDQUFDLElBQUksQ0FBQyxtQkFBbUIsRUFBRTtnQkFDL0IsVUFBVSxFQUFFLE9BQU8sQ0FBQyxVQUFVO2dCQUM5QixXQUFXLEVBQUUsT0FBTyxDQUFDLFdBQVc7Z0JBQ2hDLFNBQVMsRUFBRSxPQUFPLENBQUMsU0FBUzthQUM3QixDQUFDLENBQUM7UUFDTCxDQUFDO2FBQU0sQ0FBQztZQUNOLE1BQU0sQ0FBQyxJQUFJLENBQUMsMkJBQTJCLEVBQUU7Z0JBQ3ZDLFVBQVUsRUFBRSxPQUFPLENBQUMsVUFBVTtnQkFDOUIsV0FBVyxFQUFFLE9BQU8sQ0FBQyxXQUFXO2dCQUNoQyxNQUFNLEVB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RUQ7O09BRUc7SUFDSCxhQUFhO1FBQ1gsSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUN4QixDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsS0FBSyxDQUFDLE9BQU87UUFDWCxJQUFJLENBQUMsUUFBUSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ3RCLE1BQU0sQ0FBQyxLQUFLLENBQUMsMkJBQTJCLENBQUMsQ0FBQztJQUM1QyxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFBhdHRlcm4gRGVjcnlwdGlvbiBTZXJ2aWNlIHdpdGggTExNIENvbnRleHQgUHJvdGVjdGlvblxuICpcbiAqIFBhcnQgb2YgSXNzdWUgIzEzMjEgUGhhc2UgMjogTWVtb3J5IFNlY3VyaXR5IEFyY2hpdGVjdHVyZVxuICpcbiAqIFBVUlBPU0U6XG4gKiBQcm92aWRlcyBzZWN1cmUgYWNjZXNzIHRvIGRlY3J5cHQgcGF0dGVybnMgd2hpbGUgcHJldmVudGluZ1xuICogZGVjcnlwdGlvbiBpbiBMTE0gcmVxdWVzdCBjb250ZXh0cy4gQWxsIGRlY3J5cHRpb24gYXR0ZW1wdHNcbiAqIGFyZSBhdWRpdGVkIGZvciBzZWN1cml0eSBtb25pdG9yaW5nLlxuICpcbiAqIFNFQ1VSSVRZOlxuICogLSBQcmV2ZW50cyBkZWNyeXB0aW9uIGluIExMTSBjb250ZXh0cyAoYmxvY2tzIHBhdHRlcm4gbGVha3MgdG8gTExNKVxuICogLSBSZXF1aXJlcyBleHBsaWNpdCBhdXRob3JpemF0aW9uXG4gKiAtIEF1ZGl0cyBhbGwgZGVjcnlwdGlvbiBhdHRlbXB0c1xuICogLSBVc2VzIENvbnRleHRUcmFja2VyIHRvIGRldGVjdCBleGVjdXRpb24gY29udGV4dFxuICpcbiAqIFJFRkFDVE9SIE5PVEU6XG4gKiBDb252ZXJ0ZWQgZnJvbSBzdGF0aWMgY2xhc3MgdG8gaW5zdGFuY2UtYmFzZWQgZm9yIERJIGFyY2hpdGVjdHVyZSBjb21wYXRpYmlsaXR5LlxuICogUGF0dGVybkRlY3J5cHRvciBub3cgcmVxdWlyZXMgUGF0dGVybkVuY3J5cHRvciBhbmQgQ29udGV4dFRyYWNrZXIgZGVwZW5kZW5jaWVzXG4gKiB0byBiZSBpbmplY3RlZCB2aWEgY29uc3RydWN0b3IgZm9yIHByb3BlciBESSBsaWZlY3ljbGUgbWFuYWdlbWVudC5cbiAqXG4gKiBAbW9kdWxlIFBhdHRlcm5EZWNyeXB0b3JcbiAqL1xuXG5pbXBvcnQgeyBsb2dnZXIgfSBmcm9tICcuLi8uLi91dGlscy9sb2dnZXIuanMnO1xuaW1wb3J0IHsgUGF0dGVybkVuY3J5cHRvciB9IGZyb20gJy4vUGF0dGVybkVuY3J5cHRvci5qcyc7XG5pbXBvcnQgeyBDb250ZXh0VHJhY2tlciB9IGZyb20gJy4vQ29udGV4dFRyYWNrZXIuanMnO1xuaW1wb3J0IHR5cGUgeyBTYW5pdGl6ZWRQYXR0ZXJuIH0gZnJvbSAnLi4vdmFsaWRhdGlvbi9CYWNrZ3JvdW5kVmFsaWRhdG9yLmpzJztcbmltcG9ydCB0eXBlIHsgRW5jcnlwdGVkUGF0dGVybiB9IGZyb20gJy4vUGF0dGVybkVuY3J5cHRvci5qcyc7XG5cbi8qKlxuICogRGVjcnlwdGlvbiBhdHRlbXB0IG1ldGFkYXRhIGZvciBhdWRpdCBsb2dnaW5nXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRGVjcnlwdGlvbkF0dGVtcHQge1xuICAvKiogUGF0dGVybiByZWZlcmVuY2UgSUQgKi9cbiAgcGF0dGVyblJlZjogc3RyaW5nO1xuXG4gIC8qKiBXaGV0aGVyIGRlY3J5cHRpb24gd2FzIHN1Y2Nlc3NmdWwgKi9cbiAgc3VjY2VzczogYm9vbGVhbjtcblxuICAvKiogVGltZXN0YW1wIG9mIHRoZSBhdHRlbXB0ICovXG4gIHRpbWVzdGFtcDogbnVtYmVyO1xuXG4gIC8qKiBFeGVjdXRpb24gY29udGV4dCB0eXBlICovXG4gIGNvbnRleHRUeXBlOiBzdHJpbmc7XG5cbiAgLyoqIFJlcXVlc3QgSUQgZm9yIGNvcnJlbGF0aW9uICovXG4gIHJlcXVlc3RJZD86IHN0cmluZztcblxuICAvKiogUmVhc29uIGZvciBkZW5pYWwgKGlmIHVuc3VjY2Vzc2Z1bCkgKi9cbiAgZGVuaWFsUmVhc29uPzogc3RyaW5nO1xuXG4gIC8qKiBFcnJvciBtZXNzYWdlIChpZiBmYWlsZWQpICovXG4gIGVycm9yPzogc3RyaW5nO1xufVxuXG4vKipcbiAqIERlY3J5cHRpb24gYXVkaXQgbG9nXG4gKiBNYWludGFpbnMgYSByZWNvcmQgb2YgYWxsIGRlY3J5cHRpb24gYXR0ZW1wdHMgZm9yIHNlY3VyaXR5IG1vbml0b3JpbmdcbiAqL1xuY2xhc3MgRGVjcnlwdGlvbkF1ZGl0TG9nIHtcbiAgcHJpdmF0ZSBhdHRlbXB0czogRGVjcnlwdGlvbkF0dGVtcHRbXSA9IFtdO1xuICBwcml2YXRlIHJlYWRvbmx5IE1BWF9MT0dfU0laRSA9IDEwMDA7XG5cbiAgLyoqXG4gICAqIExvZyBhIGRlY3J5cHRpb24gYXR0ZW1wdFxuICAgKlxuICAgKiBAcGFyYW0gYXR0ZW1wdCAtIERlY3J5cHRpb24gYXR0ZW1wdCBtZXRhZGF0YVxuICAgKi9cbiAgbG9nKGF0dGVtcHQ6IERlY3J5cHRpb25BdHRlbXB0KTogdm9pZCB7XG4gICAgdGhpcy5hdHRlbXB0cy5wdXNoKGF0dGVtcHQpO1xuXG4gICAgLy8gVHJpbSBsb2cgaWYgaXQgZXhjZWVkcyBtYXggc2l6ZVxuICAgIGlmICh0aGlzLmF0dGVtcHRzLmxlbmd0aCA+IHRoaXMuTUFYX0xPR19TSVpFKSB7XG4gICAgICB0aGlzLmF0dGVtcHRzID0gdGhpcy5hdHRlbXB0cy5zbGljZSgtdGhpcy5NQVhfTE9HX1NJWkUpO1xuICAgIH1cblxuICAgIC8vIEFsd2F5cyBsb2cgdG8gc3lzdGVtIGxvZ2dlciBmb3IgcGVyc2lzdGVuY2VcbiAgICBpZiAoYXR0ZW1wdC5zdWNjZXNzKSB7XG4gICAgICBsb2dnZXIuaW5mbygnUGF0dGVybiBkZWNyeXB0ZWQnLCB7XG4gICAgICAgIHBhdHRlcm5SZWY6IGF0dGVtcHQucGF0dGVyblJlZixcbiAgICAgICAgY29udGV4dFR5cGU6IGF0dGVtcHQuY29udGV4dFR5cGUsXG4gICAgICAgIHJlcXVlc3RJZDogYXR0ZW1wdC5yZXF1ZXN0SWQsXG4gICAgICB9KTtcbiAgICB9IGVsc2Uge1xuICAgICAgbG9nZ2VyLndhcm4oJ1BhdHRlcm4gZGVjcnlwdGlvbiBkZW5pZWQnLCB7XG4gICAgICAgIHBhdHRlcm5SZWY6IGF0dGVtcHQucGF0dGVyblJlZixcbiAgICAgICAgY29udGV4dFR5cGU6IGF0dGVtcHQuY29udGV4dFR5cGUsXG4gICAgICAgIHJlYXNvbjogYXR0ZW1wdC5kZW5pYWxSZWFzb24sXG4gICAgICAgIGVycm9yOiBhdHRlbXB0LmVycm9yLFxuICAgICAgICByZXF1ZXN0SWQ6IGF0dGVtcHQucmVxdWVzdElkLFxuICAgICAgfSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEdldCBhbGwgZGVjcnlwdGlvbiBhdHRlbXB0c1xuICAgKlxuICAgKiBAcmV0dXJucyBBcnJheSBvZiBkZWNyeXB0aW9uIGF0dGVtcHRzXG4gICAqL1xuICBnZXRBdHRlbXB0cygpOiBEZWNyeXB0aW9uQXR0ZW1wdFtdIHtcbiAgICByZXR1cm4gWy4uLnRoaXMuYXR0ZW1wdHNdO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCByZWNlbnQgZGVjcnlwdGlvbiBhdHRlbXB0c1xuICAgKlxuICAgKiBAcGFyYW0gbGltaXQgLSBNYXhpbXVtIG51bWJlciBvZiBhdHRlbXB0cyB0byByZXR1cm5cbiAgICogQHJldHVybnMgQXJyYXkgb2YgcmVjZW50IGF0dGVtcHRzXG4gICAqL1xuICBnZXRSZWNlbnRBdHRlbXB0cyhsaW1pdDogbnVtYmVyID0gMTAwKTogRGVjcnlwdGlvbkF0dGVtcHRbXSB7XG4gICAgcmV0dXJuIHRoaXMuYXR0ZW1wdHMuc2xpY2UoLWxpbWl0KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDbGVhciB0aGUgYXVkaXQgbG9nICh1c2VmdWwgZm9yIHRlc3RpbmcpXG4gICAqL1xuICBjbGVhcigpOiB2b2lkIHtcbiAgICB0aGlzLmF0dGVtcHRzID0gW107XG4gICAgbG9nZ2VyLmRlYnVnKCdEZWNyeXB0aW9uIGF1ZGl0IGxvZyBjbGVhcmVkJyk7XG4gIH1cbn1cblxuLyoqXG4gKiBQYXR0ZXJuRGVjcnlwdG9yIHNlcnZpY2VcbiAqXG4gKiBQcm92aWRlcyBjb250cm9sbGVkIGFjY2VzcyB0byBkZWNyeXB0IGVuY3J5cHRlZCBwYXR0ZXJucyB3aXRoXG4gKiBMTE0gY29udGV4dCBwcm90ZWN0aW9uIGFuZCBhdWRpdCBsb2dnaW5nLlxuICpcbiAqIERJLUNPTVBBVElCTEU6IEluc3RhbmNlLWJhc2VkIHNlcnZpY2UgZm9yIGRlcGVuZGVuY3kgaW5qZWN0aW9uLlxuICovXG5leHBvcnQgY2xhc3MgUGF0dGVybkRlY3J5cHRvciB7XG4gIHByaXZhdGUgcmVhZG9ubHkgYXVkaXRMb2c6IERlY3J5cHRpb25BdWRpdExvZztcblxuICAvKipcbiAgICogQ3JlYXRlIGEgbmV3IFBhdHRlcm5EZWNyeXB0b3IgaW5zdGFuY2VcbiAgICpcbiAgICogQHBhcmFtIGVuY3J5cHRvciAtIFBhdHRlcm5FbmNyeXB0b3IgaW5zdGFuY2UgZm9yIGRlY3J5cHRpb24gb3BlcmF0aW9uc1xuICAgKiBAcGFyYW0gY29udGV4dFRyYWNrZXIgLSBDb250ZXh0VHJhY2tlciBpbnN0YW5jZSBmb3IgTExNIGNvbnRleHQgZGV0ZWN0aW9uXG4gICAqL1xuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIHJlYWRvbmx5IGVuY3J5cHRvcjogUGF0dGVybkVuY3J5cHRvcixcbiAgICBwcml2YXRlIHJlYWRvbmx5IGNvbnRleHRUcmFja2VyOiBDb250ZXh0VHJhY2tlclxuICApIHtcbiAgICB0aGlzLmF1ZGl0TG9nID0gbmV3IERlY3J5cHRpb25BdWRpdExvZygpO1xuICB9XG5cbiAgLyoqXG4gICAqIERlY3J5cHQgYSBzYW5pdGl6ZWQgcGF0dGVybiB3aXRoIHNlY3VyaXR5IGNoZWNrc1xuICAgKlxuICAgKiBUaGlzIG1ldGhvZDpcbiAgICogMS4gQ2hlY2tzIGlmIGluIExMTSBjb250ZXh0IChkZW5pZXMgaWYgdHJ1ZSlcbiAgICogMi4gVmFsaWRhdGVzIHBhdHRlcm4gc3RydWN0dXJlXG4gICAqIDMuIEF1ZGl0cyB0aGUgZGVjcnlwdGlvbiBhdHRlbXB0XG4gICAqIDQuIERlY3J5cHRzIHRoZSBwYXR0ZXJuIHVzaW5nIFBhdHRlcm5FbmNyeXB0b3JcbiAgICpcbiAgICogQHBhcmFtIHBhdHRlcm4gLSBTYW5pdGl6ZWQgcGF0dGVybiB0byBkZWNyeXB0XG4gICAqIEByZXR1cm5zIERlY3J5cHRlZCBwYXR0ZXJuIHRleHRcbiAgICogQHRocm93cyBFcnJvciBpZiBkZWNyeXB0aW9uIGlzIG5vdCBhbGxvd2VkIG9yIGZhaWxzXG4gICAqL1xuICBkZWNyeXB0UGF0dGVybihwYXR0ZXJuOiBTYW5pdGl6ZWRQYXR0ZXJuKTogc3RyaW5nIHtcbiAgICBjb25zdCBjb250ZXh0ID0gdGhpcy5jb250ZXh0VHJhY2tlci5nZXRDb250ZXh0KCk7XG4gICAgY29uc3QgcGF0dGVyblJlZiA9IHBhdHRlcm4ucmVmO1xuXG4gICAgLy8gU2VjdXJpdHkgY2hlY2s6IFByZXZlbnQgZGVjcnlwdGlvbiBpbiBMTE0gY29udGV4dFxuICAgIGlmICh0aGlzLmNvbnRleHRUcmFja2VyLmlzTExNQ29udGV4dCgpKSB7XG4gICAgICBjb25zdCBhdHRlbXB0OiBEZWNyeXB0aW9uQXR0ZW1wdCA9IHtcbiAgICAgICAgcGF0dGVyblJlZixcbiAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgIHRpbWVzdGFtcDogRGF0ZS5ub3coKSxcbiAgICAgICAgY29udGV4dFR5cGU6IGNvbnRleHQ/LnR5cGUgfHwgJ3Vua25vd24nLFxuICAgICAgICByZXF1ZXN0SWQ6IGNvbnRleHQ/LnJlcXVlc3RJZCxcbiAgICAgICAgZGVuaWFsUmVhc29uOiAnRGVjcnlwdGlvbiBub3QgYWxsb3dlZCBpbiBMTE0gcmVxdWVzdCBjb250ZXh0JyxcbiAgICAgIH07XG5cbiAgICAgIHRoaXMuYXVkaXRMb2cubG9nKGF0dGVtcHQpO1xuXG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICdQYXR0ZXJuIGRlY3J5cHRpb24gYmxvY2tlZDogQ2Fubm90IGRlY3J5cHQgcGF0dGVybnMgaW4gTExNIHJlcXVlc3QgY29udGV4dCdcbiAgICAgICk7XG4gICAgfVxuXG4gICAgLy8gVmFsaWRhdGUgcGF0dGVybiBzdHJ1Y3R1cmVcbiAgICBpZiAoIXBhdHRlcm4uZW5jcnlwdGVkUGF0dGVybiB8fCAhcGF0dGVybi5pdiB8fCAhcGF0dGVybi5hdXRoVGFnKSB7XG4gICAgICBjb25zdCBhdHRlbXB0OiBEZWNyeXB0aW9uQXR0ZW1wdCA9IHtcbiAgICAgICAgcGF0dGVyblJlZixcbiAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgIHRpbWVzdGFtcDogRGF0ZS5ub3coKSxcbiAgICAgICAgY29udGV4dFR5cGU6IGNvbnRleHQ/LnR5cGUgfHwgJ3Vua25vd24nLFxuICAgICAgICByZXF1ZXN0SWQ6IGNvbnRleHQ/LnJlcXVlc3RJZCxcbiAgICAgICAgZGVuaWFsUmVhc29uOiAnUGF0dGVybiBub3QgZW5jcnlwdGVkIG9yIG1pc3NpbmcgcmVxdWlyZWQgZmllbGRzJyxcbiAgICAgIH07XG5cbiAgICAgIHRoaXMuYXVkaXRMb2cubG9nKGF0dGVtcHQpO1xuXG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICdQYXR0ZXJuIGRlY3J5cHRpb24gZmFpbGVkOiBQYXR0ZXJuIGlzIG5vdCBlbmNyeXB0ZWQgb3IgbWlzc2luZyByZXF1aXJlZCBmaWVsZHMnXG4gICAgICApO1xuICAgIH1cblxuICAgIHRyeSB7XG4gICAgICAvLyBCdWlsZCBlbmNyeXB0ZWQgcGF0dGVybiBzdHJ1Y3R1cmVcbiAgICAgIGNvbnN0IGVuY3J5cHRlZFBhdHRlcm46IEVuY3J5cHRlZFBhdHRlcm4gPSB7XG4gICAgICAgIGVuY3J5cHRlZERhdGE6IHBhdHRlcm4uZW5jcnlwdGVkUGF0dGVybixcbiAgICAgICAgYWxnb3JpdGhtOiAocGF0dGVybi5hbGdvcml0aG0gYXMgJ2Flcy0yNTYtZ2NtJykgfHwgJ2Flcy0yNTYtZ2NtJyxcbiAgICAgICAgaXY6IHBhdHRlcm4uaXYsXG4gICAgICAgIGF1dGhUYWc6IHBhdHRlcm4uYXV0aFRhZyxcbiAgICAgIH07XG5cbiAgICAgIC8vIERlY3J5cHQgdXNpbmcgUGF0dGVybkVuY3J5cHRvclxuICAgICAgY29uc3QgZGVjcnlwdGVkID0gdGhpcy5lbmNyeXB0b3IuZGVjcnlwdChlbmNyeXB0ZWRQYXR0ZXJuKTtcblxuICAgICAgLy8gTG9nIHN1Y2Nlc3NmdWwgZGVjcnlwdGlvblxuICAgICAgY29uc3QgYXR0ZW1wdDogRGVjcnlwdGlvbkF0dGVtcHQgPSB7XG4gICAgICAgIHBhdHRlcm5SZWYsXG4gICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgIHRpbWVzdGFtcDogRGF0ZS5ub3coKSxcbiAgICAgICAgY29udGV4dFR5cGU6IGNvbnRleHQ/LnR5cGUgfHwgJ3Vua25vd24nLFxuICAgICAgICByZXF1ZXN0SWQ6IGNvbnRleHQ/LnJlcXVlc3RJZCxcbiAgICAgIH07XG5cbiAgICAgIHRoaXMuYXVkaXRMb2cubG9nKGF0dGVtcHQpO1xuXG4gICAgICByZXR1cm4gZGVjcnlwdGVkO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAvLyBMb2cgZmFpbGVkIGRlY3J5cHRpb25cbiAgICAgIGNvbnN0IGF0dGVtcHQ6IERlY3J5cHRpb25BdHRlbXB0ID0ge1xuICAgICAgICBwYXR0ZXJuUmVmLFxuICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgdGltZXN0YW1wOiBEYXRlLm5vdygpLFxuICAgICAgICBjb250ZXh0VHlwZTogY29udGV4dD8udHlwZSB8fCAndW5rbm93bicsXG4gICAgICAgIHJlcXVlc3RJZDogY29udGV4dD8ucmVxdWVzdElkLFxuICAgICAgICBlcnJvcjogZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm1lc3NhZ2UgOiAnVW5rbm93biBlcnJvcicsXG4gICAgICB9O1xuXG4gICAgICB0aGlzLmF1ZGl0TG9nLmxvZyhhdHRlbXB0KTtcblxuICAgICAgdGhyb3cgZXJyb3I7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEdldCBkZWNyeXB0aW9uIGF1ZGl0IGxvZ1xuICAgKlxuICAgKiBAcGFyYW0gbGltaXQgLSBNYXhpbXVtIG51bWJlciBvZiBhdHRlbXB0cyB0byByZXR1cm5cbiAgICogQHJldHVybnMgQXJyYXkgb2YgcmVjZW50IGRlY3J5cHRpb24gYXR0ZW1wdHNcbiAgICovXG4gIGdldEF1ZGl0TG9nKGxpbWl0PzogbnVtYmVyKTogRGVjcnlwdGlvbkF0dGVtcHRbXSB7XG4gICAgaWYgKGxpbWl0ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIHJldHVybiB0aGlzLmF1ZGl0TG9nLmdldFJlY2VudEF0dGVtcHRzKGxpbWl0KTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuYXVkaXRMb2cuZ2V0QXR0ZW1wdHMoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDbGVhciB0aGUgYXVkaXQgbG9nICh1c2VmdWwgZm9yIHRlc3RpbmcpXG4gICAqL1xuICBjbGVhckF1ZGl0TG9nKCk6IHZvaWQge1xuICAgIHRoaXMuYXVkaXRMb2cuY2xlYXIoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBEaXNwb3NlIG9mIHRoZSBkZWNyeXB0b3IgYW5kIGNsZWFuIHVwIHJlc291cmNlc1xuICAgKiBJbXBsZW1lbnRzIGNsZWFudXAgZm9yIHByb3BlciBESSBsaWZlY3ljbGUgbWFuYWdlbWVudFxuICAgKi9cbiAgYXN5bmMgZGlzcG9zZSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aGlzLmF1ZGl0TG9nLmNsZWFyKCk7XG4gICAgbG9nZ2VyLmRlYnVnKCdQYXR0ZXJuRGVjcnlwdG9yIGRpc3Bvc2VkJyk7XG4gIH1cbn1cbiJdfQ==