UNPKG

@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.

226 lines 29 kB
/** * Secure Token Manager for DollhouseMCP * * Provides secure GitHub token management with validation, * caching, and error sanitization. * * Security: SEC-004 - Token exposure vulnerability protection */ import { SecurityError } from '../errors/SecurityError.js'; import { SecurityMonitor } from './securityMonitor.js'; export var TokenScope; (function (TokenScope) { TokenScope["READ"] = "read"; TokenScope["WRITE"] = "write"; TokenScope["ADMIN"] = "admin"; })(TokenScope || (TokenScope = {})); export class SecureTokenManager { static tokenCache = new Map(); static TOKEN_ROTATION_INTERVAL = 3600000; // 1 hour // Token patterns to sanitize from errors static TOKEN_PATTERNS = [ /ghp_[a-zA-Z0-9]{36}/g, /gho_[a-zA-Z0-9]{36}/g, /github_pat_[a-zA-Z0-9_]{82}/g, /Bearer\s+[a-zA-Z0-9_-]+/g, ]; // Valid token formats static VALID_TOKEN_FORMATS = [ /^ghp_[a-zA-Z0-9]{36}$/, // Personal access token (classic) /^gho_[a-zA-Z0-9]{36}$/, // OAuth access token /^github_pat_[a-zA-Z0-9_]{82}$/ // Fine-grained personal access token ]; /** * Get a secure GitHub token for the specified scope * @param scope The required permission scope * @returns The validated token * @throws SecurityError if token is invalid or missing */ static async getSecureGitHubToken(scope) { try { // Check cache first const cached = this.tokenCache.get('github'); if (cached && this.isTokenFresh(cached)) { await this.validateTokenPermissions(cached.token, scope); cached.lastUsed = Date.now(); return cached.token; } // Get token from environment const token = process.env.GITHUB_TOKEN; if (!token) { throw new SecurityError('GitHub token not found in environment variables', 'TOKEN_NOT_FOUND', 'high', { scope }); } // Validate format this.validateTokenFormat(token); // Validate permissions await this.validateTokenPermissions(token, scope); // Cache the token const metadata = { token, scope, createdAt: Date.now(), lastUsed: Date.now() }; this.tokenCache.set('github', metadata); // Log security event SecurityMonitor.logSecurityEvent({ type: 'TOKEN_VALIDATION_SUCCESS', severity: 'LOW', source: 'SecureTokenManager', details: 'GitHub token validated successfully', additionalData: { scope } }); return token; } catch (error) { // Sanitize error before re-throwing const sanitizedError = this.sanitizeError(error); SecurityMonitor.logSecurityEvent({ type: 'TOKEN_VALIDATION_FAILURE', severity: 'HIGH', source: 'SecureTokenManager', details: 'Token validation failed', additionalData: { scope, error: sanitizedError.message } }); throw sanitizedError; } } /** * Validate token format * @param token The token to validate * @throws SecurityError if format is invalid */ static validateTokenFormat(token) { const isValid = this.VALID_TOKEN_FORMATS.some(pattern => pattern.test(token)); if (!isValid) { throw new SecurityError('Invalid GitHub token format', 'INVALID_TOKEN_FORMAT', 'high'); } // Additional security checks if (token.length < 40) { throw new SecurityError('GitHub token too short', 'TOKEN_TOO_SHORT', 'high'); } if (token.includes(' ') || token.includes('\n') || token.includes('\t')) { throw new SecurityError('GitHub token contains invalid characters', 'TOKEN_INVALID_CHARS', 'high'); } } /** * Validate token has required permissions * @param token The token to validate * @param scope The required scope * @throws SecurityError if permissions are insufficient */ static async validateTokenPermissions(token, scope) { try { // Make a test API call to verify token and permissions const response = await fetch('https://api.github.com/user', { headers: { 'Authorization': `Bearer ${token}`, 'Accept': 'application/vnd.github.v3+json', 'User-Agent': 'DollhouseMCP/1.2.0' } }); if (!response.ok) { if (response.status === 401) { throw new SecurityError('GitHub token is invalid or expired', 'TOKEN_INVALID_OR_EXPIRED', 'high'); } else if (response.status === 403) { throw new SecurityError('GitHub token lacks required permissions', 'INSUFFICIENT_PERMISSIONS', 'high', { scope }); } throw new SecurityError(`GitHub API error: ${response.status}`, 'GITHUB_API_ERROR', 'medium'); } // Check rate limit headers for token validity const remaining = response.headers.get('x-ratelimit-remaining'); if (remaining && parseInt(remaining) < 100) { SecurityMonitor.logSecurityEvent({ type: 'RATE_LIMIT_WARNING', severity: 'MEDIUM', source: 'SecureTokenManager', details: 'GitHub API rate limit low', additionalData: { remaining } }); } // For write/admin scopes, verify additional permissions if (scope === TokenScope.WRITE || scope === TokenScope.ADMIN) { const scopes = response.headers.get('x-oauth-scopes'); if (!scopes || !this.hasRequiredScopes(scopes, scope)) { throw new SecurityError(`Token lacks required ${scope} permissions`, 'INSUFFICIENT_SCOPES', 'high', { required: scope, actual: scopes }); } } } catch (error) { // Network errors should not expose token if (error instanceof SecurityError) { throw error; } throw new SecurityError('Failed to validate token permissions', 'PERMISSION_VALIDATION_FAILED', 'medium'); } } /** * Check if token has required OAuth scopes */ static hasRequiredScopes(scopes, requiredScope) { const scopeList = scopes.split(',').map(s => s.trim()); switch (requiredScope) { case TokenScope.READ: return scopeList.includes('repo') || scopeList.includes('public_repo'); case TokenScope.WRITE: return scopeList.includes('repo') || scopeList.includes('public_repo'); case TokenScope.ADMIN: return scopeList.includes('repo') && scopeList.includes('admin:org'); default: return false; } } /** * Check if cached token is still fresh */ static isTokenFresh(metadata) { const age = Date.now() - metadata.createdAt; return age < this.TOKEN_ROTATION_INTERVAL; } /** * Sanitize error messages to remove sensitive data * @param error The error to sanitize * @returns A safe error object */ static sanitizeError(error) { let message = error?.message || 'Unknown error'; let stack = error?.stack || ''; // Sanitize all token patterns for (const pattern of this.TOKEN_PATTERNS) { message = message.replace(pattern, '[REDACTED]'); stack = stack.replace(pattern, '[REDACTED]'); } // Remove any environment variable values message = message.replace(/GITHUB_TOKEN=\S+/g, 'GITHUB_TOKEN=[REDACTED]'); stack = stack.replace(/GITHUB_TOKEN=\S+/g, 'GITHUB_TOKEN=[REDACTED]'); const sanitizedError = new Error(message); sanitizedError.stack = stack; return sanitizedError; } /** * Clear cached tokens (useful for testing or forced rotation) */ static clearCache() { this.tokenCache.clear(); SecurityMonitor.logSecurityEvent({ type: 'TOKEN_CACHE_CLEARED', severity: 'LOW', source: 'SecureTokenManager', details: 'Token cache cleared' }); } /** * Get token cache statistics (for monitoring) */ static getCacheStats() { return { size: this.tokenCache.size, tokens: Array.from(this.tokenCache.keys()) }; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9rZW5NYW5hZ2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3NlY3VyaXR5L3Rva2VuTWFuYWdlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7OztHQU9HO0FBRUgsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQzNELE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUV2RCxNQUFNLENBQU4sSUFBWSxVQUlYO0FBSkQsV0FBWSxVQUFVO0lBQ3BCLDJCQUFhLENBQUE7SUFDYiw2QkFBZSxDQUFBO0lBQ2YsNkJBQWUsQ0FBQTtBQUNqQixDQUFDLEVBSlcsVUFBVSxLQUFWLFVBQVUsUUFJckI7QUFTRCxNQUFNLE9BQU8sa0JBQWtCO0lBQ3JCLE1BQU0sQ0FBQyxVQUFVLEdBQStCLElBQUksR0FBRyxFQUFFLENBQUM7SUFDMUQsTUFBTSxDQUFVLHVCQUF1QixHQUFHLE9BQU8sQ0FBQyxDQUFDLFNBQVM7SUFFcEUseUNBQXlDO0lBQ2pDLE1BQU0sQ0FBVSxjQUFjLEdBQUc7UUFDdkMsc0JBQXNCO1FBQ3RCLHNCQUFzQjtRQUN0Qiw4QkFBOEI7UUFDOUIsMEJBQTBCO0tBQzNCLENBQUM7SUFFRixzQkFBc0I7SUFDZCxNQUFNLENBQVUsbUJBQW1CLEdBQUc7UUFDNUMsdUJBQXVCLEVBQU8sa0NBQWtDO1FBQ2hFLHVCQUF1QixFQUFPLHFCQUFxQjtRQUNuRCwrQkFBK0IsQ0FBQyxxQ0FBcUM7S0FDdEUsQ0FBQztJQUVGOzs7OztPQUtHO0lBQ0gsTUFBTSxDQUFDLEtBQUssQ0FBQyxvQkFBb0IsQ0FBQyxLQUFpQjtRQUNqRCxJQUFJLENBQUM7WUFDSCxvQkFBb0I7WUFDcEIsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLENBQUM7WUFDN0MsSUFBSSxNQUFNLElBQUksSUFBSSxDQUFDLFlBQVksQ0FBQyxNQUFNLENBQUMs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QUM7UUFDL0MsQ0FBQztRQUVELHlDQUF5QztRQUN6QyxPQUFPLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQyxtQkFBbUIsRUFBRSx5QkFBeUIsQ0FBQyxDQUFDO1FBQzFFLEtBQUssR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDLG1CQUFtQixFQUFFLHlCQUF5QixDQUFDLENBQUM7UUFFdEUsTUFBTSxjQUFjLEdBQUcsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDMUMsY0FBYyxDQUFDLEtBQUssR0FBRyxLQUFLLENBQUM7UUFFN0IsT0FBTyxjQUFjLENBQUM7SUFDeEIsQ0FBQztJQUVEOztPQUVHO0lBQ0gsTUFBTSxDQUFDLFVBQVU7UUFDZixJQUFJLENBQUMsVUFBVSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBRXhCLGVBQWUsQ0FBQyxnQkFBZ0IsQ0FBQztZQUMvQixJQUFJLEVBQUUscUJBQXFCO1lBQzNCLFFBQVEsRUFBRSxLQUFLO1lBQ2YsTUFBTSxFQUFFLG9CQUFvQjtZQUM1QixPQUFPLEVBQUUscUJBQXFCO1NBQy9CLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRDs7T0FFRztJQUNILE1BQU0sQ0FBQyxhQUFhO1FBQ2xCLE9BQU87WUFDTCxJQUFJLEVBQUUsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJO1lBQzFCLE1BQU0sRUFBRSxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxFQUFFLENBQUM7U0FDM0MsQ0FBQztJQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFNlY3VyZSBUb2tlbiBNYW5hZ2VyIGZvciBEb2xsaG91c2VNQ1BcbiAqIFxuICogUHJvdmlkZXMgc2VjdXJlIEdpdEh1YiB0b2tlbiBtYW5hZ2VtZW50IHdpdGggdmFsaWRhdGlvbixcbiAqIGNhY2hpbmcsIGFuZCBlcnJvciBzYW5pdGl6YXRpb24uXG4gKiBcbiAqIFNlY3VyaXR5OiBTRUMtMDA0IC0gVG9rZW4gZXhwb3N1cmUgdnVsbmVyYWJpbGl0eSBwcm90ZWN0aW9uXG4gKi9cblxuaW1wb3J0IHsgU2VjdXJpdHlFcnJvciB9IGZyb20gJy4uL2Vycm9ycy9TZWN1cml0eUVycm9yLmpzJztcbmltcG9ydCB7IFNlY3VyaXR5TW9uaXRvciB9IGZyb20gJy4vc2VjdXJpdHlNb25pdG9yLmpzJztcblxuZXhwb3J0IGVudW0gVG9rZW5TY29wZSB7XG4gIFJFQUQgPSAncmVhZCcsXG4gIFdSSVRFID0gJ3dyaXRlJyxcbiAgQURNSU4gPSAnYWRtaW4nXG59XG5cbmludGVyZmFjZSBUb2tlbk1ldGFkYXRhIHtcbiAgdG9rZW46IHN0cmluZztcbiAgc2NvcGU6IFRva2VuU2NvcGU7XG4gIGNyZWF0ZWRBdDogbnVtYmVyO1xuICBsYXN0VXNlZDogbnVtYmVyO1xufVxuXG5leHBvcnQgY2xhc3MgU2VjdXJlVG9rZW5NYW5hZ2VyIHtcbiAgcHJpdmF0ZSBzdGF0aWMgdG9rZW5DYWNoZTogTWFwPHN0cmluZywgVG9rZW5NZXRhZGF0YT4gPSBuZXcgTWFwKCk7XG4gIHByaXZhdGUgc3RhdGljIHJlYWRvbmx5IFRPS0VOX1JPVEFUSU9OX0lOVEVSVkFMID0gMzYwMDAwMDsgLy8gMSBob3VyXG4gIFxuICAvLyBUb2tlbiBwYXR0ZXJucyB0byBzYW5pdGl6ZSBmcm9tIGVycm9yc1xuICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBUT0tFTl9QQVRURVJOUyA9IFtcbiAgICAvZ2hwX1thLXpBLVowLTldezM2fS9nLFxuICAgIC9naG9fW2EtekEtWjAtOV17MzZ9L2csXG4gICAgL2dpdGh1Yl9wYXRfW2EtekEtWjAtOV9dezgyfS9nLFxuICAgIC9CZWFyZXJcXHMrW2EtekEtWjAtOV8tXSsvZyxcbiAgXTtcblxuICAvLyBWYWxpZCB0b2tlbiBmb3JtYXRzXG4gIHByaXZhdGUgc3RhdGljIHJlYWRvbmx5IFZBTElEX1RPS0VOX0ZPUk1BVFMgPSBbXG4gICAgL15naHBfW2EtekEtWjAtOV17MzZ9JC8sICAgICAgLy8gUGVyc29uYWwgYWNjZXNzIHRva2VuIChjbGFzc2ljKVxuICAgIC9eZ2hvX1thLXpBLVowLTldezM2fSQvLCAgICAgIC8vIE9BdXRoIGFjY2VzcyB0b2tlblxuICAgIC9eZ2l0aHViX3BhdF9bYS16QS1aMC05X117ODJ9JC8gLy8gRmluZS1ncmFpbmVkIHBlcnNvbmFsIGFjY2VzcyB0b2tlblxuICBdO1xuXG4gIC8qKlxuICAgKiBHZXQgYSBzZWN1cmUgR2l0SHViIHRva2VuIGZvciB0aGUgc3BlY2lmaWVkIHNjb3BlXG4gICAqIEBwYXJhbSBzY29wZSBUaGUgcmVxdWlyZWQgcGVybWlzc2lvbiBzY29wZVxuICAgKiBAcmV0dXJucyBUaGUgdmFsaWRhdGVkIHRva2VuXG4gICAqIEB0aHJvd3MgU2VjdXJpdHlFcnJvciBpZiB0b2tlbiBpcyBpbnZhbGlkIG9yIG1pc3NpbmdcbiAgICovXG4gIHN0YXRpYyBhc3luYyBnZXRTZWN1cmVHaXRIdWJUb2tlbihzY29wZTogVG9rZW5TY29wZSk6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgdHJ5IHtcbiAgICAgIC8vIENoZWNrIGNhY2hlIGZpcnN0XG4gICAgICBjb25zdCBjYWNoZWQgPSB0aGlzLnRva2VuQ2FjaGUuZ2V0KCdnaXRodWInKTtcbiAgICAgIGlmIChjYWNoZWQgJiYgdGhpcy5pc1Rva2VuRnJlc2goY2FjaGVkKSkge1xuICAgICAgICBhd2FpdCB0aGlzLnZhbGlkYXRlVG9rZW5QZXJtaXNzaW9ucyhjYWNoZWQudG9rZW4sIHNjb3BlKTtcbiAgICAgICAgY2FjaGVkLmxhc3RVc2VkID0gRGF0ZS5ub3coKTtcbiAgICAgICAgcmV0dXJuIGNhY2hlZC50b2tlbjtcbiAgICAgIH1cblxuICAgICAgLy8gR2V0IHRva2VuIGZyb20gZW52aXJvbm1lbnRcbiAgICAgIGNvbnN0IHRva2VuID0gcHJvY2Vzcy5lbnYuR0lUSFVCX1RPS0VOO1xuICAgICAgaWYgKCF0b2tlbikge1xuICAgICAgICB0aHJvdyBuZXcgU2VjdXJpdHlFcnJvcihcbiAgICAgICAgICAnR2l0SHViIHRva2VuIG5vdCBmb3VuZCBpbiBlbnZpcm9ubWVudCB2YXJpYWJsZXMnLFxuICAgICAgICAgICdUT0tFTl9OT1RfRk9VTkQnLFxuICAgICAgICAgICdoaWdoJyxcbiAgICAgICAgICB7IHNjb3BlIH1cbiAgICAgICAgKTtcbiAgICAgIH1cblxuICAgICAgLy8gVmFsaWRhdGUgZm9ybWF0XG4gICAgICB0aGlzLnZhbGlkYXRlVG9rZW5Gb3JtYXQodG9rZW4pO1xuXG4gICAgICAvLyBWYWxpZGF0ZSBwZXJtaXNzaW9uc1xuICAgICAgYXdhaXQgdGhpcy52YWxpZGF0ZVRva2VuUGVybWlzc2lvbnModG9rZW4sIHNjb3BlKTtcblxuICAgICAgLy8gQ2FjaGUgdGhlIHRva2VuXG4gICAgICBjb25zdCBtZXRhZGF0YTogVG9rZW5NZXRhZGF0YSA9IHtcbiAgICAgICAgdG9rZW4sXG4gICAgICAgIHNjb3BlLFxuICAgICAgICBjcmVhdGVkQXQ6IERhdGUubm93KCksXG4gICAgICAgIGxhc3RVc2VkOiBEYXRlLm5vdygpXG4gICAgICB9O1xuICAgICAgdGhpcy50b2tlbkNhY2hlLnNldCgnZ2l0aHViJywgbWV0YWRhdGEpO1xuXG4gICAgICAvLyBMb2cgc2VjdXJpdHkgZXZlbnRcbiAgICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgICAgdHlwZTogJ1RPS0VOX1ZBTElEQVRJT05fU1VDQ0VTUycsXG4gICAgICAgIHNldmVyaXR5OiAnTE9XJyxcbiAgICAgICAgc291cmNlOiAnU2VjdXJlVG9rZW5NYW5hZ2VyJyxcbiAgICAgICAgZGV0YWlsczogJ0dpdEh1YiB0b2tlbiB2YWxpZGF0ZWQgc3VjY2Vzc2Z1bGx5JyxcbiAgICAgICAgYWRkaXRpb25hbERhdGE6IHsgc2NvcGUgfVxuICAgICAgfSk7XG5cbiAgICAgIHJldHVybiB0b2tlbjtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgLy8gU2FuaXRpemUgZXJyb3IgYmVmb3JlIHJlLXRocm93aW5nXG4gICAgICBjb25zdCBzYW5pdGl6ZWRFcnJvciA9IHRoaXMuc2FuaXRpemVFcnJvcihlcnJvcik7XG4gICAgICBcbiAgICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgICAgdHlwZTogJ1RPS0VOX1ZBTElEQVRJT05fRkFJTFVSRScsXG4gICAgICAgIHNldmVyaXR5OiAnSElHSCcsXG4gICAgICAgIHNvdXJjZTogJ1NlY3VyZVRva2VuTWFuYWdlcicsXG4gICAgICAgIGRldGFpbHM6ICdUb2tlbiB2YWxpZGF0aW9uIGZhaWxlZCcsXG4gICAgICAgIGFkZGl0aW9uYWxEYXRhOiB7IFxuICAgICAgICAgIHNjb3BlLFxuICAgICAgICAgIGVycm9yOiBzYW5pdGl6ZWRFcnJvci5tZXNzYWdlIFxuICAgICAgICB9XG4gICAgICB9KTtcblxuICAgICAgdGhyb3cgc2FuaXRpemVkRXJyb3I7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFZhbGlkYXRlIHRva2VuIGZvcm1hdFxuICAgKiBAcGFyYW0gdG9rZW4gVGhlIHRva2VuIHRvIHZhbGlkYXRlXG4gICAqIEB0aHJvd3MgU2VjdXJpdHlFcnJvciBpZiBmb3JtYXQgaXMgaW52YWxpZFxuICAgKi9cbiAgcHJpdmF0ZSBzdGF0aWMgdmFsaWRhdGVUb2tlbkZvcm1hdCh0b2tlbjogc3RyaW5nKTogdm9pZCB7XG4gICAgY29uc3QgaXNWYWxpZCA9IHRoaXMuVkFMSURfVE9LRU5fRk9STUFUUy5zb21lKHBhdHRlcm4gPT4gcGF0dGVybi50ZXN0KHRva2VuKSk7XG4gICAgXG4gICAgaWYgKCFpc1ZhbGlkKSB7XG4gICAgICB0aHJvdyBuZXcgU2VjdXJpdHlFcnJvcihcbiAgICAgICAgJ0ludmFsaWQgR2l0SHViIHRva2VuIGZvcm1hdCcsXG4gICAgICAgICdJTlZBTElEX1RPS0VOX0ZPUk1BVCcsXG4gICAgICAgICdoaWdoJ1xuICAgICAgKTtcbiAgICB9XG5cbiAgICAvLyBBZGRpdGlvbmFsIHNlY3VyaXR5IGNoZWNrc1xuICAgIGlmICh0b2tlbi5sZW5ndGggPCA0MCkge1xuICAgICAgdGhyb3cgbmV3IFNlY3VyaXR5RXJyb3IoXG4gICAgICAgICdHaXRIdWIgdG9rZW4gdG9vIHNob3J0JyxcbiAgICAgICAgJ1RPS0VOX1RPT19TSE9SVCcsXG4gICAgICAgICdoaWdoJ1xuICAgICAgKTtcbiAgICB9XG5cbiAgICBpZiAodG9rZW4uaW5jbHVkZXMoJyAnKSB8fCB0b2tlbi5pbmNsdWRlcygnXFxuJykgfHwgdG9rZW4uaW5jbHVkZXMoJ1xcdCcpKSB7XG4gICAgICB0aHJvdyBuZXcgU2VjdXJpdHlFcnJvcihcbiAgICAgICAgJ0dpdEh1YiB0b2tlbiBjb250YWlucyBpbnZhbGlkIGNoYXJhY3RlcnMnLFxuICAgICAgICAnVE9LRU5fSU5WQUxJRF9DSEFSUycsXG4gICAgICAgICdoaWdoJ1xuICAgICAgKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogVmFsaWRhdGUgdG9rZW4gaGFzIHJlcXVpcmVkIHBlcm1pc3Npb25zXG4gICAqIEBwYXJhbSB0b2tlbiBUaGUgdG9rZW4gdG8gdmFsaWRhdGVcbiAgICogQHBhcmFtIHNjb3BlIFRoZSByZXF1aXJlZCBzY29wZVxuICAgKiBAdGhyb3dzIFNlY3VyaXR5RXJyb3IgaWYgcGVybWlzc2lvbnMgYXJlIGluc3VmZmljaWVudFxuICAgKi9cbiAgcHJpdmF0ZSBzdGF0aWMgYXN5bmMgdmFsaWRhdGVUb2tlblBlcm1pc3Npb25zKHRva2VuOiBzdHJpbmcsIHNjb3BlOiBUb2tlblNjb3BlKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdHJ5IHtcbiAgICAgIC8vIE1ha2UgYSB0ZXN0IEFQSSBjYWxsIHRvIHZlcmlmeSB0b2tlbiBhbmQgcGVybWlzc2lvbnNcbiAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2goJ2h0dHBzOi8vYXBpLmdpdGh1Yi5jb20vdXNlcicsIHtcbiAgICAgICAgaGVhZGVyczoge1xuICAgICAgICAgICdBdXRob3JpemF0aW9uJzogYEJlYXJlciAke3Rva2VufWAsXG4gICAgICAgICAgJ0FjY2VwdCc6ICdhcHBsaWNhdGlvbi92bmQuZ2l0aHViLnYzK2pzb24nLFxuICAgICAgICAgICdVc2VyLUFnZW50JzogJ0RvbGxob3VzZU1DUC8xLjIuMCdcbiAgICAgICAgfVxuICAgICAgfSk7XG5cbiAgICAgIGlmICghcmVzcG9uc2Uub2spIHtcbiAgICAgICAgaWYgKHJlc3BvbnNlLnN0YXR1cyA9PT0gNDAxKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IFNlY3VyaXR5RXJyb3IoXG4gICAgICAgICAgICAnR2l0SHViIHRva2VuIGlzIGludmFsaWQgb3IgZXhwaXJlZCcsXG4gICAgICAgICAgICAnVE9LRU5fSU5WQUxJRF9PUl9FWFBJUkVEJyxcbiAgICAgICAgICAgICdoaWdoJ1xuICAgICAgICAgICk7XG4gICAgICAgIH0gZWxzZSBpZiAocmVzcG9uc2Uuc3RhdHVzID09PSA0MDMpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgU2VjdXJpdHlFcnJvcihcbiAgICAgICAgICAgICdHaXRIdWIgdG9rZW4gbGFja3MgcmVxdWlyZWQgcGVybWlzc2lvbnMnLFxuICAgICAgICAgICAgJ0lOU1VGRklDSUVOVF9QRVJNSVNTSU9OUycsXG4gICAgICAgICAgICAnaGlnaCcsXG4gICAgICAgICAgICB7IHNjb3BlIH1cbiAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICAgIHRocm93IG5ldyBTZWN1cml0eUVycm9yKFxuICAgICAgICAgIGBHaXRIdWIgQVBJIGVycm9yOiAke3Jlc3BvbnNlLnN0YXR1c31gLFxuICAgICAgICAgICdHSVRIVUJfQVBJX0VSUk9SJyxcbiAgICAgICAgICAnbWVkaXVtJ1xuICAgICAgICApO1xuICAgICAgfVxuXG4gICAgICAvLyBDaGVjayByYXRlIGxpbWl0IGhlYWRlcnMgZm9yIHRva2VuIHZhbGlkaXR5XG4gICAgICBjb25zdCByZW1haW5pbmcgPSByZXNwb25zZS5oZWFkZXJzLmdldCgneC1yYXRlbGltaXQtcmVtYWluaW5nJyk7XG4gICAgICBpZiAocmVtYWluaW5nICYmIHBhcnNlSW50KHJlbWFpbmluZykgPCAxMDApIHtcbiAgICAgICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgICAgIHR5cGU6ICdSQVRFX0xJTUlUX1dBUk5JTkcnLFxuICAgICAgICAgIHNldmVyaXR5OiAnTUVESVVNJyxcbiAgICAgICAgICBzb3VyY2U6ICdTZWN1cmVUb2tlbk1hbmFnZXInLFxuICAgICAgICAgIGRldGFpbHM6ICdHaXRIdWIgQVBJIHJhdGUgbGltaXQgbG93JyxcbiAgICAgICAgICBhZGRpdGlvbmFsRGF0YTogeyByZW1haW5pbmcgfVxuICAgICAgICB9KTtcbiAgICAgIH1cblxuICAgICAgLy8gRm9yIHdyaXRlL2FkbWluIHNjb3BlcywgdmVyaWZ5IGFkZGl0aW9uYWwgcGVybWlzc2lvbnNcbiAgICAgIGlmIChzY29wZSA9PT0gVG9rZW5TY29wZS5XUklURSB8fCBzY29wZSA9PT0gVG9rZW5TY29wZS5BRE1JTikge1xuICAgICAgICBjb25zdCBzY29wZXMgPSByZXNwb25zZS5oZWFkZXJzLmdldCgneC1vYXV0aC1zY29wZXMnKTtcbiAgICAgICAgaWYgKCFzY29wZXMgfHwgIXRoaXMuaGFzUmVxdWlyZWRTY29wZXMoc2NvcGVzLCBzY29wZSkpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgU2VjdXJpdHlFcnJvcihcbiAgICAgICAgICAgIGBUb2tlbiBsYWNrcyByZXF1aXJlZCAke3Njb3BlfSBwZXJtaXNzaW9uc2AsXG4gICAgICAgICAgICAnSU5TVUZGSUNJRU5UX1NDT1BFUycsXG4gICAgICAgICAgICAnaGlnaCcsXG4gICAgICAgICAgICB7IHJlcXVpcmVkOiBzY29wZSwgYWN0dWFsOiBzY29wZXMgfVxuICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgLy8gTmV0d29yayBlcnJvcnMgc2hvdWxkIG5vdCBleHBvc2UgdG9rZW5cbiAgICAgIGlmIChlcnJvciBpbnN0YW5jZW9mIFNlY3VyaXR5RXJyb3IpIHtcbiAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICB9XG4gICAgICB0aHJvdyBuZXcgU2VjdXJpdHlFcnJvcihcbiAgICAgICAgJ0ZhaWxlZCB0byB2YWxpZGF0ZSB0b2tlbiBwZXJtaXNzaW9ucycsXG4gICAgICAgICdQRVJNSVNTSU9OX1ZBTElEQVRJT05fRkFJTEVEJyxcbiAgICAgICAgJ21lZGl1bSdcbiAgICAgICk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrIGlmIHRva2VuIGhhcyByZXF1aXJlZCBPQXV0aCBzY29wZXNcbiAgICovXG4gIHByaXZhdGUgc3RhdGljIGhhc1JlcXVpcmVkU2NvcGVzKHNjb3Blczogc3RyaW5nLCByZXF1aXJlZFNjb3BlOiBUb2tlblNjb3BlKTogYm9vbGVhbiB7XG4gICAgY29uc3Qgc2NvcGVMaXN0ID0gc2NvcGVzLnNwbGl0KCcsJykubWFwKHMgPT4gcy50cmltKCkpO1xuICAgIFxuICAgIHN3aXRjaCAocmVxdWlyZWRTY29wZSkge1xuICAgICAgY2FzZSBUb2tlblNjb3BlLlJFQUQ6XG4gICAgICAgIHJldHVybiBzY29wZUxpc3QuaW5jbHVkZXMoJ3JlcG8nKSB8fCBzY29wZUxpc3QuaW5jbHVkZXMoJ3B1YmxpY19yZXBvJyk7XG4gICAgICBjYXNlIFRva2VuU2NvcGUuV1JJVEU6XG4gICAgICAgIHJldHVybiBzY29wZUxpc3QuaW5jbHVkZXMoJ3JlcG8nKSB8fCBzY29wZUxpc3QuaW5jbHVkZXMoJ3B1YmxpY19yZXBvJyk7XG4gICAgICBjYXNlIFRva2VuU2NvcGUuQURNSU46XG4gICAgICAgIHJldHVybiBzY29wZUxpc3QuaW5jbHVkZXMoJ3JlcG8nKSAmJiBzY29wZUxpc3QuaW5jbHVkZXMoJ2FkbWluOm9yZycpO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVjayBpZiBjYWNoZWQgdG9rZW4gaXMgc3RpbGwgZnJlc2hcbiAgICovXG4gIHByaXZhdGUgc3RhdGljIGlzVG9rZW5GcmVzaChtZXRhZGF0YTogVG9rZW5NZXRhZGF0YSk6IGJvb2xlYW4ge1xuICAgIGNvbnN0IGFnZSA9IERhdGUubm93KCkgLSBtZXRhZGF0YS5jcmVhdGVkQXQ7XG4gICAgcmV0dXJuIGFnZSA8IHRoaXMuVE9LRU5fUk9UQVRJT05fSU5URVJWQUw7XG4gIH1cblxuICAvKipcbiAgICogU2FuaXRpemUgZXJyb3IgbWVzc2FnZXMgdG8gcmVtb3ZlIHNlbnNpdGl2ZSBkYXRhXG4gICAqIEBwYXJhbSBlcnJvciBUaGUgZXJyb3IgdG8gc2FuaXRpemVcbiAgICogQHJldHVybnMgQSBzYWZlIGVycm9yIG9iamVjdFxuICAgKi9cbiAgcHJpdmF0ZSBzdGF0aWMgc2FuaXRpemVFcnJvcihlcnJvcjogYW55KTogRXJyb3Ige1xuICAgIGxldCBtZXNzYWdlID0gZXJyb3I/Lm1lc3NhZ2UgfHwgJ1Vua25vd24gZXJyb3InO1xuICAgIGxldCBzdGFjayA9IGVycm9yPy5zdGFjayB8fCAnJztcblxuICAgIC8vIFNhbml0aXplIGFsbCB0b2tlbiBwYXR0ZXJuc1xuICAgIGZvciAoY29uc3QgcGF0dGVybiBvZiB0aGlzLlRPS0VOX1BBVFRFUk5TKSB7XG4gICAgICBtZXNzYWdlID0gbWVzc2FnZS5yZXBsYWNlKHBhdHRlcm4sICdbUkVEQUNURURdJyk7XG4gICAgICBzdGFjayA9IHN0YWNrLnJlcGxhY2UocGF0dGVybiwgJ1tSRURBQ1RFRF0nKTtcbiAgICB9XG5cbiAgICAvLyBSZW1vdmUgYW55IGVudmlyb25tZW50IHZhcmlhYmxlIHZhbHVlc1xuICAgIG1lc3NhZ2UgPSBtZXNzYWdlLnJlcGxhY2UoL0dJVEhVQl9UT0tFTj1cXFMrL2csICdHSVRIVUJfVE9LRU49W1JFREFDVEVEXScpO1xuICAgIHN0YWNrID0gc3RhY2sucmVwbGFjZSgvR0lUSFVCX1RPS0VOPVxcUysvZywgJ0dJVEhVQl9UT0tFTj1bUkVEQUNURURdJyk7XG5cbiAgICBjb25zdCBzYW5pdGl6ZWRFcnJvciA9IG5ldyBFcnJvcihtZXNzYWdlKTtcbiAgICBzYW5pdGl6ZWRFcnJvci5zdGFjayA9IHN0YWNrO1xuICAgIFxuICAgIHJldHVybiBzYW5pdGl6ZWRFcnJvcjtcbiAgfVxuXG4gIC8qKlxuICAgKiBDbGVhciBjYWNoZWQgdG9rZW5zICh1c2VmdWwgZm9yIHRlc3Rpbmcgb3IgZm9yY2VkIHJvdGF0aW9uKVxuICAgKi9cbiAgc3RhdGljIGNsZWFyQ2FjaGUoKTogdm9pZCB7XG4gICAgdGhpcy50b2tlbkNhY2hlLmNsZWFyKCk7XG4gICAgXG4gICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgdHlwZTogJ1RPS0VOX0NBQ0hFX0NMRUFSRUQnLFxuICAgICAgc2V2ZXJpdHk6ICdMT1cnLFxuICAgICAgc291cmNlOiAnU2VjdXJlVG9rZW5NYW5hZ2VyJyxcbiAgICAgIGRldGFpbHM6ICdUb2tlbiBjYWNoZSBjbGVhcmVkJ1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCB0b2tlbiBjYWNoZSBzdGF0aXN0aWNzIChmb3IgbW9uaXRvcmluZylcbiAgICovXG4gIHN0YXRpYyBnZXRDYWNoZVN0YXRzKCk6IHsgc2l6ZTogbnVtYmVyOyB0b2tlbnM6IHN0cmluZ1tdIH0ge1xuICAgIHJldHVybiB7XG4gICAgICBzaXplOiB0aGlzLnRva2VuQ2FjaGUuc2l6ZSxcbiAgICAgIHRva2VuczogQXJyYXkuZnJvbSh0aGlzLnRva2VuQ2FjaGUua2V5cygpKVxuICAgIH07XG4gIH1cbn0iXX0=