@mickdarling/dollhousemcp
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.
108 lines • 13.2 kB
JavaScript
/**
* Security Monitor for DollhouseMCP
*
* Centralized security event logging and monitoring system
* for tracking and alerting on security-related events.
*/
import { logger } from '../utils/logger.js';
export class SecurityMonitor {
static eventCount = 0;
static events = [];
static MAX_EVENTS = 1000; // Keep last 1000 events in memory
/**
* Logs a security event
*/
static logSecurityEvent(event) {
const logEntry = {
...event,
timestamp: new Date().toISOString(),
id: `SEC-${Date.now()}-${++this.eventCount}`,
};
// Store in memory (circular buffer)
this.events.push(logEntry);
if (this.events.length > this.MAX_EVENTS) {
this.events.shift();
}
// In MCP servers, we cannot write to stderr/stdout as it breaks the JSON-RPC protocol
// Security events are stored in memory and can be retrieved via API
// Only send critical alerts via the proper channel
if (event.severity === 'CRITICAL') {
this.sendSecurityAlert(logEntry);
}
}
/**
* Sends security alerts for critical events
*/
static sendSecurityAlert(event) {
// In a production environment, this would integrate with:
// - Slack webhooks
// - Email alerts
// - PagerDuty
// - Security Information and Event Management (SIEM) systems
// Log critical security alerts with structured data
// DO NOT use console.error in MCP servers as it breaks the JSON-RPC protocol
logger.error('🚨 CRITICAL SECURITY ALERT 🚨', {
type: event.type,
details: event.details,
timestamp: event.timestamp,
id: event.id
});
// If in production mode with proper config, send actual alerts
if (process.env.DOLLHOUSE_SECURITY_ALERTS === 'true') {
// TODO: Implement actual alert mechanisms
}
}
/**
* Gets recent security events for analysis
*/
static getRecentEvents(count = 100) {
return this.events.slice(-count);
}
/**
* Gets events by severity
*/
static getEventsBySeverity(severity) {
return this.events.filter(event => event.severity === severity);
}
/**
* Gets events by type
*/
static getEventsByType(type) {
return this.events.filter(event => event.type === type);
}
/**
* Generates a security report
*/
static generateSecurityReport() {
const eventsBySeverity = {
CRITICAL: 0,
HIGH: 0,
MEDIUM: 0,
LOW: 0,
};
const eventsByType = {};
for (const event of this.events) {
eventsBySeverity[event.severity]++;
eventsByType[event.type] = (eventsByType[event.type] || 0) + 1;
}
return {
totalEvents: this.events.length,
eventsBySeverity,
eventsByType,
recentCriticalEvents: this.getEventsBySeverity('CRITICAL').slice(-10),
};
}
/**
* Clears old events (for memory management)
*/
static clearOldEvents(daysToKeep = 7) {
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - daysToKeep);
const cutoffTimestamp = cutoffDate.toISOString();
const index = this.events.findIndex(event => event.timestamp >= cutoffTimestamp);
if (index > 0) {
this.events.splice(0, index);
}
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VjdXJpdHlNb25pdG9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3NlY3VyaXR5L3NlY3VyaXR5TW9uaXRvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7R0FLRztBQUVILE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQW9CNUMsTUFBTSxPQUFPLGVBQWU7SUFDbEIsTUFBTSxDQUFDLFVBQVUsR0FBRyxDQUFDLENBQUM7SUFDdEIsTUFBTSxDQUFVLE1BQU0sR0FBdUIsRUFBRSxDQUFDO0lBQ2hELE1BQU0sQ0FBVSxVQUFVLEdBQUcsSUFBSSxDQUFDLENBQUMsa0NBQWtDO0lBRTdFOztPQUVHO0lBQ0gsTUFBTSxDQUFDLGdCQUFnQixDQUFDLEtBQW9CO1FBQzFDLE1BQU0sUUFBUSxHQUFxQjtZQUNqQyxHQUFHLEtBQUs7WUFDUixTQUFTLEVBQUUsSUFBSSxJQUFJLEVBQUUsQ0FBQyxXQUFXLEVBQUU7WUFDbkMsRUFBRSxFQUFFLE9BQU8sSUFBSSxDQUFDLEdBQUcsRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLFVBQVUsRUFBRTtTQUM3QyxDQUFDO1FBRUYsb0NBQW9DO1FBQ3BDLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQzNCLElBQUksSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO1lBQ3pDLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDdEIsQ0FBQztRQUVELHNGQUFzRjtRQUN0RixvRUFBb0U7UUFDcEUsbURBQW1EO1FBRW5ELElBQUksS0FBSyxDQUFDLFFBQVEsS0FBSyxVQUFVLEVBQUUsQ0FBQztZQUNsQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDbkMsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNLLE1BQU0sQ0FBQyxpQkFBaUIsQ0FBQyxLQUF1QjtRQUN0RCwwREFBMEQ7UUFDMUQsbUJBQW1CO1FBQ25CLGlCQUFpQjtRQUNqQixjQUFjO1FBQ2QsNkRBQTZEO1FBRTdELG9EQUFvRDtRQUNwRCw2RUFBNkU7UUFDN0UsTUFBTSxDQUFDLEtBQUssQ0FBQywrQkFBK0IsRUFBRTtZQUM1QyxJQUFJLEVBQUUsS0FBSyxDQUFDLElBQUk7WUFDaEIsT0FBTyxFQUFFLEtBQUssQ0FBQyxPQUFPO1lBQ3RCLFNBQVMsRUFBRSxLQUFLLENBQUMsU0FBUztZQUMxQixFQUFFLEVBQUUsS0FBSyxDQUFDLEVBQUU7U0FDYixDQUFDLENBQUM7UUFFSCwrREFBK0Q7UUFDL0QsSUFBSSxPQUFPLENBQUMsR0FBRyxDQUFDLHlCQUF5QixLQUFLLE1BQU0sRUFBRSxDQUFDO1lBQ3JELDBDQUEwQztRQUM1QyxDQUFDO0lBQ0gsQ0FBQztJQUVEOztPQUVHO0lBQ0gsTUFBTSxDQUFDLGVBQWUsQ0FBQyxRQUFnQixHQUFHO1FBQ3hDLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNuQyxDQUFDO0lBRUQ7O09BRUc7SUFDSCxNQUFNLENBQUMsbUJBQW1CLENBQUMsUUFBbUM7UUFDNUQsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLEtBQUssQ0FBQyxRQUFRLEtBQUssUUFBUSxDQUFDLENBQUM7SUFDbEUsQ0FBQztJQUVEOztPQUVHO0lBQ0gsTUFBTSxDQUFDLGVBQWUsQ0FBQyxJQUEyQjtRQUNoRCxPQUFPLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsS0FBSyxDQUFDLElBQUksS0FBSyxJQUFJLENBQUMsQ0FBQztJQUMxRCxDQUFDO0lBRUQ7O09BRUc7SUFDSCxNQUFNLENBQUMsc0JBQXNCO1FBTTNCLE1BQU0sZ0JBQWdCLEdBQTJCO1lBQy9DLFFBQVEsRUFBRSxDQUFDO1lBQ1gsSUFBSSxFQUFFLENBQUM7WUFDUCxNQUFNLEVBQUUsQ0FBQztZQUNULEdBQUcsRUFBRSxDQUFDO1NBQ1AsQ0FBQztRQUVGLE1BQU0sWUFBWSxHQUEyQixFQUFFLENBQUM7UUFFaEQsS0FBSyxNQUFNLEtBQUssSUFBSSxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDaEMsZ0JBQWdCLENBQUMsS0FBSyxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUM7WUFDbkMsWUFBWSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLFlBQVksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ2pFLENBQUM7UUFFRCxPQUFPO1lBQ0wsV0FBVyxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTTtZQUMvQixnQkFBZ0I7WUFDaEIsWUFBWTtZQUNaLG9CQUFvQixFQUFFLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxVQUFVLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUM7U0FDdEUsQ0FBQztJQUNKLENBQUM7SUFFRDs7T0FFRztJQUNILE1BQU0sQ0FBQyxjQUFjLENBQUMsYUFBcUIsQ0FBQztRQUMxQyxNQUFNLFVBQVUsR0FBRyxJQUFJLElBQUksRUFBRSxDQUFDO1FBQzlCLFVBQVUsQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLE9BQU8sRUFBRSxHQUFHLFVBQVUsQ0FBQyxDQUFDO1FBQ3RELE1BQU0sZUFBZSxHQUFHLFVBQVUsQ0FBQyxXQUFXLEVBQUUsQ0FBQztRQUVqRCxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLEtBQUssQ0FBQyxTQUFTLElBQUksZUFBZSxDQUFDLENBQUM7UUFDakYsSUFBSSxLQUFLLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDZCxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDL0IsQ0FBQztJQUNILENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFNlY3VyaXR5IE1vbml0b3IgZm9yIERvbGxob3VzZU1DUFxuICogXG4gKiBDZW50cmFsaXplZCBzZWN1cml0eSBldmVudCBsb2dnaW5nIGFuZCBtb25pdG9yaW5nIHN5c3RlbVxuICogZm9yIHRyYWNraW5nIGFuZCBhbGVydGluZyBvbiBzZWN1cml0eS1yZWxhdGVkIGV2ZW50cy5cbiAqL1xuXG5pbXBvcnQgeyBsb2dnZXIgfSBmcm9tICcuLi91dGlscy9sb2dnZXIuanMnO1xuXG5leHBvcnQgaW50ZXJmYWNlIFNlY3VyaXR5RXZlbnQge1xuICB0eXBlOiAnQ09OVEVOVF9JTkpFQ1RJT05fQVRURU1QVCcgfCAnWUFNTF9JTkpFQ1RJT05fQVRURU1QVCcgfCAnUEFUSF9UUkFWRVJTQUxfQVRURU1QVCcgfCBcbiAgICAgICAgJ1RPS0VOX1ZBTElEQVRJT05fRkFJTFVSRScgfCAnVVBEQVRFX1NFQ1VSSVRZX1ZJT0xBVElPTicgfCAnUkFURV9MSU1JVF9FWENFRURFRCcgfFxuICAgICAgICAnWUFNTF9QQVJTSU5HX1dBUk5JTkcnIHwgJ1lBTUxfUEFSU0VfU1VDQ0VTUycgfCAnVE9LRU5fVkFMSURBVElPTl9TVUNDRVNTJyB8XG4gICAgICAgICdSQVRFX0xJTUlUX1dBUk5JTkcnIHwgJ1RPS0VOX0NBQ0hFX0NMRUFSRUQnO1xuICBzZXZlcml0eTogJ0xPVycgfCAnTUVESVVNJyB8ICdISUdIJyB8ICdDUklUSUNBTCc7XG4gIHNvdXJjZTogc3RyaW5nO1xuICBkZXRhaWxzOiBzdHJpbmc7XG4gIHVzZXJBZ2VudD86IHN0cmluZztcbiAgaXA/OiBzdHJpbmc7XG4gIGFkZGl0aW9uYWxEYXRhPzogUmVjb3JkPHN0cmluZywgYW55Pjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBTZWN1cml0eUxvZ0VudHJ5IGV4dGVuZHMgU2VjdXJpdHlFdmVudCB7XG4gIHRpbWVzdGFtcDogc3RyaW5nO1xuICBpZDogc3RyaW5nO1xufVxuXG5leHBvcnQgY2xhc3MgU2VjdXJpdHlNb25pdG9yIHtcbiAgcHJpdmF0ZSBzdGF0aWMgZXZlbnRDb3VudCA9IDA7XG4gIHByaXZhdGUgc3RhdGljIHJlYWRvbmx5IGV2ZW50czogU2VjdXJpdHlMb2dFbnRyeVtdID0gW107XG4gIHByaXZhdGUgc3RhdGljIHJlYWRvbmx5IE1BWF9FVkVOVFMgPSAxMDAwOyAvLyBLZWVwIGxhc3QgMTAwMCBldmVudHMgaW4gbWVtb3J5XG5cbiAgLyoqXG4gICAqIExvZ3MgYSBzZWN1cml0eSBldmVudFxuICAgKi9cbiAgc3RhdGljIGxvZ1NlY3VyaXR5RXZlbnQoZXZlbnQ6IFNlY3VyaXR5RXZlbnQpOiB2b2lkIHtcbiAgICBjb25zdCBsb2dFbnRyeTogU2VjdXJpdHlMb2dFbnRyeSA9IHtcbiAgICAgIC4uLmV2ZW50LFxuICAgICAgdGltZXN0YW1wOiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXG4gICAgICBpZDogYFNFQy0ke0RhdGUubm93KCl9LSR7Kyt0aGlzLmV2ZW50Q291bnR9YCxcbiAgICB9O1xuXG4gICAgLy8gU3RvcmUgaW4gbWVtb3J5IChjaXJjdWxhciBidWZmZXIpXG4gICAgdGhpcy5ldmVudHMucHVzaChsb2dFbnRyeSk7XG4gICAgaWYgKHRoaXMuZXZlbnRzLmxlbmd0aCA+IHRoaXMuTUFYX0VWRU5UUykge1xuICAgICAgdGhpcy5ldmVudHMuc2hpZnQoKTtcbiAgICB9XG5cbiAgICAvLyBJbiBNQ1Agc2VydmVycywgd2UgY2Fubm90IHdyaXRlIHRvIHN0ZGVyci9zdGRvdXQgYXMgaXQgYnJlYWtzIHRoZSBKU09OLVJQQyBwcm90b2NvbFxuICAgIC8vIFNlY3VyaXR5IGV2ZW50cyBhcmUgc3RvcmVkIGluIG1lbW9yeSBhbmQgY2FuIGJlIHJldHJpZXZlZCB2aWEgQVBJXG4gICAgLy8gT25seSBzZW5kIGNyaXRpY2FsIGFsZXJ0cyB2aWEgdGhlIHByb3BlciBjaGFubmVsXG4gICAgXG4gICAgaWYgKGV2ZW50LnNldmVyaXR5ID09PSAnQ1JJVElDQUwnKSB7XG4gICAgICB0aGlzLnNlbmRTZWN1cml0eUFsZXJ0KGxvZ0VudHJ5KTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogU2VuZHMgc2VjdXJpdHkgYWxlcnRzIGZvciBjcml0aWNhbCBldmVudHNcbiAgICovXG4gIHByaXZhdGUgc3RhdGljIHNlbmRTZWN1cml0eUFsZXJ0KGV2ZW50OiBTZWN1cml0eUxvZ0VudHJ5KTogdm9pZCB7XG4gICAgLy8gSW4gYSBwcm9kdWN0aW9uIGVudmlyb25tZW50LCB0aGlzIHdvdWxkIGludGVncmF0ZSB3aXRoOlxuICAgIC8vIC0gU2xhY2sgd2ViaG9va3NcbiAgICAvLyAtIEVtYWlsIGFsZXJ0c1xuICAgIC8vIC0gUGFnZXJEdXR5XG4gICAgLy8gLSBTZWN1cml0eSBJbmZvcm1hdGlvbiBhbmQgRXZlbnQgTWFuYWdlbWVudCAoU0lFTSkgc3lzdGVtc1xuICAgIFxuICAgIC8vIExvZyBjcml0aWNhbCBzZWN1cml0eSBhbGVydHMgd2l0aCBzdHJ1Y3R1cmVkIGRhdGFcbiAgICAvLyBETyBOT1QgdXNlIGNvbnNvbGUuZXJyb3IgaW4gTUNQIHNlcnZlcnMgYXMgaXQgYnJlYWtzIHRoZSBKU09OLVJQQyBwcm90b2NvbFxuICAgIGxvZ2dlci5lcnJvcign8J+aqCBDUklUSUNBTCBTRUNVUklUWSBBTEVSVCDwn5qoJywge1xuICAgICAgdHlwZTogZXZlbnQudHlwZSxcbiAgICAgIGRldGFpbHM6IGV2ZW50LmRldGFpbHMsXG4gICAgICB0aW1lc3RhbXA6IGV2ZW50LnRpbWVzdGFtcCxcbiAgICAgIGlkOiBldmVudC5pZFxuICAgIH0pO1xuICAgIFxuICAgIC8vIElmIGluIHByb2R1Y3Rpb24gbW9kZSB3aXRoIHByb3BlciBjb25maWcsIHNlbmQgYWN0dWFsIGFsZXJ0c1xuICAgIGlmIChwcm9jZXNzLmVudi5ET0xMSE9VU0VfU0VDVVJJVFlfQUxFUlRTID09PSAndHJ1ZScpIHtcbiAgICAgIC8vIFRPRE86IEltcGxlbWVudCBhY3R1YWwgYWxlcnQgbWVjaGFuaXNtc1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBHZXRzIHJlY2VudCBzZWN1cml0eSBldmVudHMgZm9yIGFuYWx5c2lzXG4gICAqL1xuICBzdGF0aWMgZ2V0UmVjZW50RXZlbnRzKGNvdW50OiBudW1iZXIgPSAxMDApOiBTZWN1cml0eUxvZ0VudHJ5W10ge1xuICAgIHJldHVybiB0aGlzLmV2ZW50cy5zbGljZSgtY291bnQpO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldHMgZXZlbnRzIGJ5IHNldmVyaXR5XG4gICAqL1xuICBzdGF0aWMgZ2V0RXZlbnRzQnlTZXZlcml0eShzZXZlcml0eTogU2VjdXJpdHlFdmVudFsnc2V2ZXJpdHknXSk6IFNlY3VyaXR5TG9nRW50cnlbXSB7XG4gICAgcmV0dXJuIHRoaXMuZXZlbnRzLmZpbHRlcihldmVudCA9PiBldmVudC5zZXZlcml0eSA9PT0gc2V2ZXJpdHkpO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldHMgZXZlbnRzIGJ5IHR5cGVcbiAgICovXG4gIHN0YXRpYyBnZXRFdmVudHNCeVR5cGUodHlwZTogU2VjdXJpdHlFdmVudFsndHlwZSddKTogU2VjdXJpdHlMb2dFbnRyeVtdIHtcbiAgICByZXR1cm4gdGhpcy5ldmVudHMuZmlsdGVyKGV2ZW50ID0+IGV2ZW50LnR5cGUgPT09IHR5cGUpO1xuICB9XG5cbiAgLyoqXG4gICAqIEdlbmVyYXRlcyBhIHNlY3VyaXR5IHJlcG9ydFxuICAgKi9cbiAgc3RhdGljIGdlbmVyYXRlU2VjdXJpdHlSZXBvcnQoKToge1xuICAgIHRvdGFsRXZlbnRzOiBudW1iZXI7XG4gICAgZXZlbnRzQnlTZXZlcml0eTogUmVjb3JkPHN0cmluZywgbnVtYmVyPjtcbiAgICBldmVudHNCeVR5cGU6IFJlY29yZDxzdHJpbmcsIG51bWJlcj47XG4gICAgcmVjZW50Q3JpdGljYWxFdmVudHM6IFNlY3VyaXR5TG9nRW50cnlbXTtcbiAgfSB7XG4gICAgY29uc3QgZXZlbnRzQnlTZXZlcml0eTogUmVjb3JkPHN0cmluZywgbnVtYmVyPiA9IHtcbiAgICAgIENSSVRJQ0FMOiAwLFxuICAgICAgSElHSDogMCxcbiAgICAgIE1FRElVTTogMCxcbiAgICAgIExPVzogMCxcbiAgICB9O1xuXG4gICAgY29uc3QgZXZlbnRzQnlUeXBlOiBSZWNvcmQ8c3RyaW5nLCBudW1iZXI+ID0ge307XG5cbiAgICBmb3IgKGNvbnN0IGV2ZW50IG9mIHRoaXMuZXZlbnRzKSB7XG4gICAgICBldmVudHNCeVNldmVyaXR5W2V2ZW50LnNldmVyaXR5XSsrO1xuICAgICAgZXZlbnRzQnlUeXBlW2V2ZW50LnR5cGVdID0gKGV2ZW50c0J5VHlwZVtldmVudC50eXBlXSB8fCAwKSArIDE7XG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIHRvdGFsRXZlbnRzOiB0aGlzLmV2ZW50cy5sZW5ndGgsXG4gICAgICBldmVudHNCeVNldmVyaXR5LFxuICAgICAgZXZlbnRzQnlUeXBlLFxuICAgICAgcmVjZW50Q3JpdGljYWxFdmVudHM6IHRoaXMuZ2V0RXZlbnRzQnlTZXZlcml0eSgnQ1JJVElDQUwnKS5zbGljZSgtMTApLFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogQ2xlYXJzIG9sZCBldmVudHMgKGZvciBtZW1vcnkgbWFuYWdlbWVudClcbiAgICovXG4gIHN0YXRpYyBjbGVhck9sZEV2ZW50cyhkYXlzVG9LZWVwOiBudW1iZXIgPSA3KTogdm9pZCB7XG4gICAgY29uc3QgY3V0b2ZmRGF0ZSA9IG5ldyBEYXRlKCk7XG4gICAgY3V0b2ZmRGF0ZS5zZXREYXRlKGN1dG9mZkRhdGUuZ2V0RGF0ZSgpIC0gZGF5c1RvS2VlcCk7XG4gICAgY29uc3QgY3V0b2ZmVGltZXN0YW1wID0gY3V0b2ZmRGF0ZS50b0lTT1N0cmluZygpO1xuXG4gICAgY29uc3QgaW5kZXggPSB0aGlzLmV2ZW50cy5maW5kSW5kZXgoZXZlbnQgPT4gZXZlbnQudGltZXN0YW1wID49IGN1dG9mZlRpbWVzdGFtcCk7XG4gICAgaWYgKGluZGV4ID4gMCkge1xuICAgICAgdGhpcy5ldmVudHMuc3BsaWNlKDAsIGluZGV4KTtcbiAgICB9XG4gIH1cbn0iXX0=