UNPKG

llmverify

Version:

AI Output Verification Toolkit — Local-first LLM safety, hallucination detection, PII redaction, prompt injection defense, and runtime monitoring. Zero telemetry. OWASP LLM Top 10 aligned.

233 lines 25.1 kB
"use strict"; /** * Security Validators and Hardening * * Input validation, regex safety, and security utilities * * @module security/validators */ Object.defineProperty(exports, "__esModule", { value: true }); exports.RateLimiter = exports.SECURITY_LIMITS = void 0; exports.validateInput = validateInput; exports.safeRegexTest = safeRegexTest; exports.sanitizeForLogging = sanitizeForLogging; exports.validateArray = validateArray; exports.sanitizeObject = sanitizeObject; exports.validateUrl = validateUrl; exports.escapeHtml = escapeHtml; exports.detectInjection = detectInjection; const errors_1 = require("../errors"); const codes_1 = require("../errors/codes"); /** * Maximum input sizes */ exports.SECURITY_LIMITS = { MAX_CONTENT_LENGTH: 10 * 1024 * 1024, // 10MB absolute max MAX_REGEX_LENGTH: 1000, MAX_ARRAY_LENGTH: 10000, REGEX_TIMEOUT_MS: 100 }; /** * Validate and sanitize input string */ function validateInput(input, maxLength) { // Check for null/undefined if (input === null || input === undefined) { throw new errors_1.ValidationError('Input cannot be null or undefined', codes_1.ErrorCode.INVALID_INPUT); } // Convert to string if needed const str = String(input); // Check length const limit = maxLength || exports.SECURITY_LIMITS.MAX_CONTENT_LENGTH; if (str.length > limit) { throw new errors_1.ValidationError(`Input exceeds maximum length (${limit} characters)`, codes_1.ErrorCode.CONTENT_TOO_LARGE, { length: str.length, maxLength: limit }); } return str; } /** * Safe regex execution with timeout protection */ function safeRegexTest(pattern, text, timeoutMs) { const timeout = timeoutMs || exports.SECURITY_LIMITS.REGEX_TIMEOUT_MS; // Validate regex pattern length if (pattern.source.length > exports.SECURITY_LIMITS.MAX_REGEX_LENGTH) { throw new errors_1.ValidationError('Regex pattern too complex', codes_1.ErrorCode.INVALID_INPUT, { patternLength: pattern.source.length }); } // Use timeout for regex execution let result = false; let timedOut = false; const timer = setTimeout(() => { timedOut = true; }, timeout); try { if (!timedOut) { result = pattern.test(text); } } catch (error) { clearTimeout(timer); throw new errors_1.ValidationError('Regex execution failed', codes_1.ErrorCode.INVALID_INPUT, { error: error.message }); } clearTimeout(timer); if (timedOut) { throw new errors_1.ValidationError('Regex execution timeout', codes_1.ErrorCode.TIMEOUT, { timeoutMs: timeout }); } return result; } /** * Sanitize string for safe logging (remove PII) */ function sanitizeForLogging(text) { let sanitized = text; // Remove email addresses sanitized = sanitized.replace(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g, '[EMAIL]'); // Remove phone numbers (various formats) sanitized = sanitized.replace(/\b\d{3}[-.\s]?\d{3,4}[-.\s]?\d{4}\b/g, '[PHONE]'); // Remove SSN sanitized = sanitized.replace(/\b\d{3}-\d{2}-\d{4}\b/g, '[SSN]'); // Remove credit card numbers sanitized = sanitized.replace(/\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b/g, '[CARD]'); // Remove API keys (common patterns) sanitized = sanitized.replace(/\b[A-Za-z0-9]{32,}\b/g, '[KEY]'); // Remove IP addresses sanitized = sanitized.replace(/\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g, '[IP]'); return sanitized; } /** * Validate array input */ function validateArray(arr, maxLength) { if (!Array.isArray(arr)) { throw new errors_1.ValidationError('Input must be an array', codes_1.ErrorCode.INVALID_INPUT); } const limit = maxLength || exports.SECURITY_LIMITS.MAX_ARRAY_LENGTH; if (arr.length > limit) { throw new errors_1.ValidationError(`Array exceeds maximum length (${limit} items)`, codes_1.ErrorCode.INVALID_INPUT, { length: arr.length, maxLength: limit }); } return arr; } /** * Rate limiter class */ class RateLimiter { constructor(maxRequests = 100, windowMs = 60000) { this.requests = new Map(); this.maxRequests = maxRequests; this.windowMs = windowMs; } /** * Check if request is allowed */ isAllowed(key) { const now = Date.now(); const timestamps = this.requests.get(key) || []; // Remove old timestamps outside the window const validTimestamps = timestamps.filter(ts => now - ts < this.windowMs); if (validTimestamps.length >= this.maxRequests) { return false; } // Add current timestamp validTimestamps.push(now); this.requests.set(key, validTimestamps); return true; } /** * Get remaining requests */ getRemaining(key) { const now = Date.now(); const timestamps = this.requests.get(key) || []; const validTimestamps = timestamps.filter(ts => now - ts < this.windowMs); return Math.max(0, this.maxRequests - validTimestamps.length); } /** * Reset rate limit for key */ reset(key) { this.requests.delete(key); } /** * Clear all rate limits */ clear() { this.requests.clear(); } } exports.RateLimiter = RateLimiter; /** * Sanitize object for safe logging */ function sanitizeObject(obj) { if (typeof obj !== 'object' || obj === null) { return obj; } if (Array.isArray(obj)) { return obj.map(item => sanitizeObject(item)); } const sanitized = {}; for (const key in obj) { // Skip sensitive keys const lowerKey = key.toLowerCase(); if (lowerKey.includes('password') || lowerKey.includes('secret') || lowerKey.includes('token') || lowerKey.includes('apikey') || lowerKey.includes('api_key') || lowerKey.includes('authorization')) { sanitized[key] = '[REDACTED]'; } else if (typeof obj[key] === 'string') { sanitized[key] = sanitizeForLogging(obj[key]); } else if (typeof obj[key] === 'object') { sanitized[key] = sanitizeObject(obj[key]); } else { sanitized[key] = obj[key]; } } return sanitized; } /** * Validate URL */ function validateUrl(url) { try { const parsed = new URL(url); // Only allow http and https return parsed.protocol === 'http:' || parsed.protocol === 'https:'; } catch { return false; } } /** * Escape HTML to prevent XSS */ function escapeHtml(text) { const map = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#039;' }; return text.replace(/[&<>"']/g, char => map[char]); } /** * Check for potential injection patterns */ function detectInjection(text) { const injectionPatterns = [ /<script/i, /javascript:/i, /on\w+\s*=/i, // Event handlers /eval\(/i, /expression\(/i, /<iframe/i, /<object/i, /<embed/i ]; return injectionPatterns.some(pattern => pattern.test(text)); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmFsaWRhdG9ycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zZWN1cml0eS92YWxpZGF0b3JzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7Ozs7O0dBTUc7OztBQWtCSCxzQ0F1QkM7QUFLRCxzQ0E0Q0M7QUFLRCxnREF3Q0M7QUFLRCxzQ0FrQkM7QUFpRUQsd0NBaUNDO0FBS0Qsa0NBUUM7QUFLRCxnQ0FVQztBQUtELDBDQWFDO0FBNVNELHNDQUE0QztBQUM1QywyQ0FBNEM7QUFFNUM7O0dBRUc7QUFDVSxRQUFBLGVBQWUsR0FBRztJQUM3QixrQkFBa0IsRUFBRSxFQUFFLEdBQUcsSUFBSSxHQUFHLElBQUksRUFBRSxvQkFBb0I7SUFDMUQsZ0JBQWdCLEVBQUUsSUFBSTtJQUN0QixnQkFBZ0IsRUFBRSxLQUFLO0lBQ3ZCLGdCQUFnQixFQUFFLEdBQUc7Q0FDdEIsQ0FBQztBQUVGOztHQUVHO0FBQ0gsU0FBZ0IsYUFBYSxDQUFDLEtBQWEsRUFBRSxTQUFrQjtJQUM3RCwyQkFBMkI7SUFDM0IsSUFBSSxLQUFLLEtBQUssSUFBSSxJQUFJLEtBQUssS0FBSyxTQUFTLEVBQUUsQ0FBQztRQUMxQyxNQUFNLElBQUksd0JBQWUsQ0FDdkIsbUNBQW1DLEVBQ25DLGlCQUFTLENBQUMsYUFBYSxDQUN4QixDQUFDO0lBQ0osQ0FBQztJQUVELDhCQUE4QjtJQUM5QixNQUFNLEdBQUcsR0FBRyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7SUFFMUIsZUFBZTtJQUNmLE1BQU0sS0FBSyxHQUFHLFNBQVMsSUFBSSx1QkFBZSxDQUFDLGtCQUFrQixDQUFDO0lBQzlELElBQUksR0FBRyxDQUFDLE1BQU0sR0FBRyxLQUFLLEVBQUUsQ0FBQztRQUN2QixNQUFNLElBQUksd0JBQWUsQ0FDdkIsaUNBQWlDLEtBQUssY0FBYyxFQUNwRCxpQkFBUyxDQUFDLGlCQUFpQixFQUMzQixFQUFFLE1BQU0sRUFBRSxHQUFHLENBQUMsTUFBTSxFQUFF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QUFPLE1BQU0sQ0FBQyxRQUFRLEtBQUssT0FBTyxJQUFJLE1BQU0sQ0FBQyxRQUFRLEtBQUssUUFBUSxDQUFDO0lBQ3JFLENBQUM7SUFBQyxNQUFNLENBQUM7UUFDUCxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7QUFDSCxDQUFDO0FBRUQ7O0dBRUc7QUFDSCxTQUFnQixVQUFVLENBQUMsSUFBWTtJQUNyQyxNQUFNLEdBQUcsR0FBMkI7UUFDbEMsR0FBRyxFQUFFLE9BQU87UUFDWixHQUFHLEVBQUUsTUFBTTtRQUNYLEdBQUcsRUFBRSxNQUFNO1FBQ1gsR0FBRyxFQUFFLFFBQVE7UUFDYixHQUFHLEVBQUUsUUFBUTtLQUNkLENBQUM7SUFFRixPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsVUFBVSxFQUFFLElBQUksQ0FBQyxFQUFFLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7QUFDckQsQ0FBQztBQUVEOztHQUVHO0FBQ0gsU0FBZ0IsZUFBZSxDQUFDLElBQVk7SUFDMUMsTUFBTSxpQkFBaUIsR0FBRztRQUN4QixVQUFVO1FBQ1YsY0FBYztRQUNkLFlBQVksRUFBRSxpQkFBaUI7UUFDL0IsU0FBUztRQUNULGVBQWU7UUFDZixVQUFVO1FBQ1YsVUFBVTtRQUNWLFNBQVM7S0FDVixDQUFDO0lBRUYsT0FBTyxpQkFBaUIsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7QUFDL0QsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogU2VjdXJpdHkgVmFsaWRhdG9ycyBhbmQgSGFyZGVuaW5nXG4gKiBcbiAqIElucHV0IHZhbGlkYXRpb24sIHJlZ2V4IHNhZmV0eSwgYW5kIHNlY3VyaXR5IHV0aWxpdGllc1xuICogXG4gKiBAbW9kdWxlIHNlY3VyaXR5L3ZhbGlkYXRvcnNcbiAqL1xuXG5pbXBvcnQgeyBWYWxpZGF0aW9uRXJyb3IgfSBmcm9tICcuLi9lcnJvcnMnO1xuaW1wb3J0IHsgRXJyb3JDb2RlIH0gZnJvbSAnLi4vZXJyb3JzL2NvZGVzJztcblxuLyoqXG4gKiBNYXhpbXVtIGlucHV0IHNpemVzXG4gKi9cbmV4cG9ydCBjb25zdCBTRUNVUklUWV9MSU1JVFMgPSB7XG4gIE1BWF9DT05URU5UX0xFTkdUSDogMTAgKiAxMDI0ICogMTAyNCwgLy8gMTBNQiBhYnNvbHV0ZSBtYXhcbiAgTUFYX1JFR0VYX0xFTkdUSDogMTAwMCxcbiAgTUFYX0FSUkFZX0xFTkdUSDogMTAwMDAsXG4gIFJFR0VYX1RJTUVPVVRfTVM6IDEwMFxufTtcblxuLyoqXG4gKiBWYWxpZGF0ZSBhbmQgc2FuaXRpemUgaW5wdXQgc3RyaW5nXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB2YWxpZGF0ZUlucHV0KGlucHV0OiBzdHJpbmcsIG1heExlbmd0aD86IG51bWJlcik6IHN0cmluZyB7XG4gIC8vIENoZWNrIGZvciBudWxsL3VuZGVmaW5lZFxuICBpZiAoaW5wdXQgPT09IG51bGwgfHwgaW5wdXQgPT09IHVuZGVmaW5lZCkge1xuICAgIHRocm93IG5ldyBWYWxpZGF0aW9uRXJyb3IoXG4gICAgICAnSW5wdXQgY2Fubm90IGJlIG51bGwgb3IgdW5kZWZpbmVkJyxcbiAgICAgIEVycm9yQ29kZS5JTlZBTElEX0lOUFVUXG4gICAgKTtcbiAgfVxuICBcbiAgLy8gQ29udmVydCB0byBzdHJpbmcgaWYgbmVlZGVkXG4gIGNvbnN0IHN0ciA9IFN0cmluZyhpbnB1dCk7XG4gIFxuICAvLyBDaGVjayBsZW5ndGhcbiAgY29uc3QgbGltaXQgPSBtYXhMZW5ndGggfHwgU0VDVVJJVFlfTElNSVRTLk1BWF9DT05URU5UX0xFTkdUSDtcbiAgaWYgKHN0ci5sZW5ndGggPiBsaW1pdCkge1xuICAgIHRocm93IG5ldyBWYWxpZGF0aW9uRXJyb3IoXG4gICAgICBgSW5wdXQgZXhjZWVkcyBtYXhpbXVtIGxlbmd0aCAoJHtsaW1pdH0gY2hhcmFjdGVycylgLFxuICAgICAgRXJyb3JDb2RlLkNPTlRFTlRfVE9PX0xBUkdFLFxuICAgICAgeyBsZW5ndGg6IHN0ci5sZW5ndGgsIG1heExlbmd0aDogbGltaXQgfVxuICAgICk7XG4gIH1cbiAgXG4gIHJldHVybiBzdHI7XG59XG5cbi8qKlxuICogU2FmZSByZWdleCBleGVjdXRpb24gd2l0aCB0aW1lb3V0IHByb3RlY3Rpb25cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNhZmVSZWdleFRlc3QocGF0dGVybjogUmVnRXhwLCB0ZXh0OiBzdHJpbmcsIHRpbWVvdXRNcz86IG51bWJlcik6IGJvb2xlYW4ge1xuICBjb25zdCB0aW1lb3V0ID0gdGltZW91dE1zIHx8IFNFQ1VSSVRZX0xJTUlUUy5SRUdFWF9USU1FT1VUX01TO1xuICBcbiAgLy8gVmFsaWRhdGUgcmVnZXggcGF0dGVybiBsZW5ndGhcbiAgaWYgKHBhdHRlcm4uc291cmNlLmxlbmd0aCA+IFNFQ1VSSVRZX0xJTUlUUy5NQVhfUkVHRVhfTEVOR1RIKSB7XG4gICAgdGhyb3cgbmV3IFZhbGlkYXRpb25FcnJvcihcbiAgICAgICdSZWdleCBwYXR0ZXJuIHRvbyBjb21wbGV4JyxcbiAgICAgIEVycm9yQ29kZS5JTlZBTElEX0lOUFVULFxuICAgICAgeyBwYXR0ZXJuTGVuZ3RoOiBwYXR0ZXJuLnNvdXJjZS5sZW5ndGggfVxuICAgICk7XG4gIH1cbiAgXG4gIC8vIFVzZSB0aW1lb3V0IGZvciByZWdleCBleGVjdXRpb25cbiAgbGV0IHJlc3VsdCA9IGZhbHNlO1xuICBsZXQgdGltZWRPdXQgPSBmYWxzZTtcbiAgXG4gIGNvbnN0IHRpbWVyID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgdGltZWRPdXQgPSB0cnVlO1xuICB9LCB0aW1lb3V0KTtcbiAgXG4gIHRyeSB7XG4gICAgaWYgKCF0aW1lZE91dCkge1xuICAgICAgcmVzdWx0ID0gcGF0dGVybi50ZXN0KHRleHQpO1xuICAgIH1cbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjbGVhclRpbWVvdXQodGltZXIpO1xuICAgIHRocm93IG5ldyBWYWxpZGF0aW9uRXJyb3IoXG4gICAgICAnUmVnZXggZXhlY3V0aW9uIGZhaWxlZCcsXG4gICAgICBFcnJvckNvZGUuSU5WQUxJRF9JTlBVVCxcbiAgICAgIHsgZXJyb3I6IChlcnJvciBhcyBFcnJvcikubWVzc2FnZSB9XG4gICAgKTtcbiAgfVxuICBcbiAgY2xlYXJUaW1lb3V0KHRpbWVyKTtcbiAgXG4gIGlmICh0aW1lZE91dCkge1xuICAgIHRocm93IG5ldyBWYWxpZGF0aW9uRXJyb3IoXG4gICAgICAnUmVnZXggZXhlY3V0aW9uIHRpbWVvdXQnLFxuICAgICAgRXJyb3JDb2RlLlRJTUVPVVQsXG4gICAgICB7IHRpbWVvdXRNczogdGltZW91dCB9XG4gICAgKTtcbiAgfVxuICBcbiAgcmV0dXJuIHJlc3VsdDtcbn1cblxuLyoqXG4gKiBTYW5pdGl6ZSBzdHJpbmcgZm9yIHNhZmUgbG9nZ2luZyAocmVtb3ZlIFBJSSlcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNhbml0aXplRm9yTG9nZ2luZyh0ZXh0OiBzdHJpbmcpOiBzdHJpbmcge1xuICBsZXQgc2FuaXRpemVkID0gdGV4dDtcbiAgXG4gIC8vIFJlbW92ZSBlbWFpbCBhZGRyZXNzZXNcbiAgc2FuaXRpemVkID0gc2FuaXRpemVkLnJlcGxhY2UoXG4gICAgL1thLXpBLVowLTkuXyUrLV0rQFthLXpBLVowLTkuLV0rXFwuW2EtekEtWl17Mix9L2csXG4gICAgJ1tFTUFJTF0nXG4gICk7XG4gIFxuICAvLyBSZW1vdmUgcGhvbmUgbnVtYmVycyAodmFyaW91cyBmb3JtYXRzKVxuICBzYW5pdGl6ZWQgPSBzYW5pdGl6ZWQucmVwbGFjZShcbiAgICAvXFxiXFxkezN9Wy0uXFxzXT9cXGR7Myw0fVstLlxcc10/XFxkezR9XFxiL2csXG4gICAgJ1tQSE9ORV0nXG4gICk7XG4gIFxuICAvLyBSZW1vdmUgU1NOXG4gIHNhbml0aXplZCA9IHNhbml0aXplZC5yZXBsYWNlKFxuICAgIC9cXGJcXGR7M30tXFxkezJ9LVxcZHs0fVxcYi9nLFxuICAgICdbU1NOXSdcbiAgKTtcbiAgXG4gIC8vIFJlbW92ZSBjcmVkaXQgY2FyZCBudW1iZXJzXG4gIHNhbml0aXplZCA9IHNhbml0aXplZC5yZXBsYWNlKFxuICAgIC9cXGJcXGR7NH1bXFxzLV0/XFxkezR9W1xccy1dP1xcZHs0fVtcXHMtXT9cXGR7NH1cXGIvZyxcbiAgICAnW0NBUkRdJ1xuICApO1xuICBcbiAgLy8gUmVtb3ZlIEFQSSBrZXlzIChjb21tb24gcGF0dGVybnMpXG4gIHNhbml0aXplZCA9IHNhbml0aXplZC5yZXBsYWNlKFxuICAgIC9cXGJbQS1aYS16MC05XXszMix9XFxiL2csXG4gICAgJ1tLRVldJ1xuICApO1xuICBcbiAgLy8gUmVtb3ZlIElQIGFkZHJlc3Nlc1xuICBzYW5pdGl6ZWQgPSBzYW5pdGl6ZWQucmVwbGFjZShcbiAgICAvXFxiXFxkezEsM31cXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvZyxcbiAgICAnW0lQXSdcbiAgKTtcbiAgXG4gIHJldHVybiBzYW5pdGl6ZWQ7XG59XG5cbi8qKlxuICogVmFsaWRhdGUgYXJyYXkgaW5wdXRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHZhbGlkYXRlQXJyYXk8VD4oYXJyOiBUW10sIG1heExlbmd0aD86IG51bWJlcik6IFRbXSB7XG4gIGlmICghQXJyYXkuaXNBcnJheShhcnIpKSB7XG4gICAgdGhyb3cgbmV3IFZhbGlkYXRpb25FcnJvcihcbiAgICAgICdJbnB1dCBtdXN0IGJlIGFuIGFycmF5JyxcbiAgICAgIEVycm9yQ29kZS5JTlZBTElEX0lOUFVUXG4gICAgKTtcbiAgfVxuICBcbiAgY29uc3QgbGltaXQgPSBtYXhMZW5ndGggfHwgU0VDVVJJVFlfTElNSVRTLk1BWF9BUlJBWV9MRU5HVEg7XG4gIGlmIChhcnIubGVuZ3RoID4gbGltaXQpIHtcbiAgICB0aHJvdyBuZXcgVmFsaWRhdGlvbkVycm9yKFxuICAgICAgYEFycmF5IGV4Y2VlZHMgbWF4aW11bSBsZW5ndGggKCR7bGltaXR9IGl0ZW1zKWAsXG4gICAgICBFcnJvckNvZGUuSU5WQUxJRF9JTlBVVCxcbiAgICAgIHsgbGVuZ3RoOiBhcnIubGVuZ3RoLCBtYXhMZW5ndGg6IGxpbWl0IH1cbiAgICApO1xuICB9XG4gIFxuICByZXR1cm4gYXJyO1xufVxuXG4vKipcbiAqIFJhdGUgbGltaXRlciBjbGFzc1xuICovXG5leHBvcnQgY2xhc3MgUmF0ZUxpbWl0ZXIge1xuICBwcml2YXRlIHJlcXVlc3RzOiBNYXA8c3RyaW5nLCBudW1iZXJbXT4gPSBuZXcgTWFwKCk7XG4gIHByaXZhdGUgbWF4UmVxdWVzdHM6IG51bWJlcjtcbiAgcHJpdmF0ZSB3aW5kb3dNczogbnVtYmVyO1xuICBcbiAgY29uc3RydWN0b3IobWF4UmVxdWVzdHM6IG51bWJlciA9IDEwMCwgd2luZG93TXM6IG51bWJlciA9IDYwMDAwKSB7XG4gICAgdGhpcy5tYXhSZXF1ZXN0cyA9IG1heFJlcXVlc3RzO1xuICAgIHRoaXMud2luZG93TXMgPSB3aW5kb3dNcztcbiAgfVxuICBcbiAgLyoqXG4gICAqIENoZWNrIGlmIHJlcXVlc3QgaXMgYWxsb3dlZFxuICAgKi9cbiAgcHVibGljIGlzQWxsb3dlZChrZXk6IHN0cmluZyk6IGJvb2xlYW4ge1xuICAgIGNvbnN0IG5vdyA9IERhdGUubm93KCk7XG4gICAgY29uc3QgdGltZXN0YW1wcyA9IHRoaXMucmVxdWVzdHMuZ2V0KGtleSkgfHwgW107XG4gICAgXG4gICAgLy8gUmVtb3ZlIG9sZCB0aW1lc3RhbXBzIG91dHNpZGUgdGhlIHdpbmRvd1xuICAgIGNvbnN0IHZhbGlkVGltZXN0YW1wcyA9IHRpbWVzdGFtcHMuZmlsdGVyKHRzID0+IG5vdyAtIHRzIDwgdGhpcy53aW5kb3dNcyk7XG4gICAgXG4gICAgaWYgKHZhbGlkVGltZXN0YW1wcy5sZW5ndGggPj0gdGhpcy5tYXhSZXF1ZXN0cykge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICBcbiAgICAvLyBBZGQgY3VycmVudCB0aW1lc3RhbXBcbiAgICB2YWxpZFRpbWVzdGFtcHMucHVzaChub3cpO1xuICAgIHRoaXMucmVxdWVzdHMuc2V0KGtleSwgdmFsaWRUaW1lc3RhbXBzKTtcbiAgICBcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIEdldCByZW1haW5pbmcgcmVxdWVzdHNcbiAgICovXG4gIHB1YmxpYyBnZXRSZW1haW5pbmcoa2V5OiBzdHJpbmcpOiBudW1iZXIge1xuICAgIGNvbnN0IG5vdyA9IERhdGUubm93KCk7XG4gICAgY29uc3QgdGltZXN0YW1wcyA9IHRoaXMucmVxdWVzdHMuZ2V0KGtleSkgfHwgW107XG4gICAgY29uc3QgdmFsaWRUaW1lc3RhbXBzID0gdGltZXN0YW1wcy5maWx0ZXIodHMgPT4gbm93IC0gdHMgPCB0aGlzLndpbmRvd01zKTtcbiAgICBcbiAgICByZXR1cm4gTWF0aC5tYXgoMCwgdGhpcy5tYXhSZXF1ZXN0cyAtIHZhbGlkVGltZXN0YW1wcy5sZW5ndGgpO1xuICB9XG4gIFxuICAvKipcbiAgICogUmVzZXQgcmF0ZSBsaW1pdCBmb3Iga2V5XG4gICAqL1xuICBwdWJsaWMgcmVzZXQoa2V5OiBzdHJpbmcpOiB2b2lkIHtcbiAgICB0aGlzLnJlcXVlc3RzLmRlbGV0ZShrZXkpO1xuICB9XG4gIFxuICAvKipcbiAgICogQ2xlYXIgYWxsIHJhdGUgbGltaXRzXG4gICAqL1xuICBwdWJsaWMgY2xlYXIoKTogdm9pZCB7XG4gICAgdGhpcy5yZXF1ZXN0cy5jbGVhcigpO1xuICB9XG59XG5cbi8qKlxuICogU2FuaXRpemUgb2JqZWN0IGZvciBzYWZlIGxvZ2dpbmdcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNhbml0aXplT2JqZWN0KG9iajogYW55KTogYW55IHtcbiAgaWYgKHR5cGVvZiBvYmogIT09ICdvYmplY3QnIHx8IG9iaiA9PT0gbnVsbCkge1xuICAgIHJldHVybiBvYmo7XG4gIH1cbiAgXG4gIGlmIChBcnJheS5pc0FycmF5KG9iaikpIHtcbiAgICByZXR1cm4gb2JqLm1hcChpdGVtID0+IHNhbml0aXplT2JqZWN0KGl0ZW0pKTtcbiAgfVxuICBcbiAgY29uc3Qgc2FuaXRpemVkOiBhbnkgPSB7fTtcbiAgXG4gIGZvciAoY29uc3Qga2V5IGluIG9iaikge1xuICAgIC8vIFNraXAgc2Vuc2l0aXZlIGtleXNcbiAgICBjb25zdCBsb3dlcktleSA9IGtleS50b0xvd2VyQ2FzZSgpO1xuICAgIGlmIChcbiAgICAgIGxvd2VyS2V5LmluY2x1ZGVzKCdwYXNzd29yZCcpIHx8XG4gICAgICBsb3dlcktleS5pbmNsdWRlcygnc2VjcmV0JykgfHxcbiAgICAgIGxvd2VyS2V5LmluY2x1ZGVzKCd0b2tlbicpIHx8XG4gICAgICBsb3dlcktleS5pbmNsdWRlcygnYXBpa2V5JykgfHxcbiAgICAgIGxvd2VyS2V5LmluY2x1ZGVzKCdhcGlfa2V5JykgfHxcbiAgICAgIGxvd2VyS2V5LmluY2x1ZGVzKCdhdXRob3JpemF0aW9uJylcbiAgICApIHtcbiAgICAgIHNhbml0aXplZFtrZXldID0gJ1tSRURBQ1RFRF0nO1xuICAgIH0gZWxzZSBpZiAodHlwZW9mIG9ialtrZXldID09PSAnc3RyaW5nJykge1xuICAgICAgc2FuaXRpemVkW2tleV0gPSBzYW5pdGl6ZUZvckxvZ2dpbmcob2JqW2tleV0pO1xuICAgIH0gZWxzZSBpZiAodHlwZW9mIG9ialtrZXldID09PSAnb2JqZWN0Jykge1xuICAgICAgc2FuaXRpemVkW2tleV0gPSBzYW5pdGl6ZU9iamVjdChvYmpba2V5XSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHNhbml0aXplZFtrZXldID0gb2JqW2tleV07XG4gICAgfVxuICB9XG4gIFxuICByZXR1cm4gc2FuaXRpemVkO1xufVxuXG4vKipcbiAqIFZhbGlkYXRlIFVSTFxuICovXG5leHBvcnQgZnVuY3Rpb24gdmFsaWRhdGVVcmwodXJsOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgdHJ5IHtcbiAgICBjb25zdCBwYXJzZWQgPSBuZXcgVVJMKHVybCk7XG4gICAgLy8gT25seSBhbGxvdyBodHRwIGFuZCBodHRwc1xuICAgIHJldHVybiBwYXJzZWQucHJvdG9jb2wgPT09ICdodHRwOicgfHwgcGFyc2VkLnByb3RvY29sID09PSAnaHR0cHM6JztcbiAgfSBjYXRjaCB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG59XG5cbi8qKlxuICogRXNjYXBlIEhUTUwgdG8gcHJldmVudCBYU1NcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGVzY2FwZUh0bWwodGV4dDogc3RyaW5nKTogc3RyaW5nIHtcbiAgY29uc3QgbWFwOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge1xuICAgICcmJzogJyZhbXA7JyxcbiAgICAnPCc6ICcmbHQ7JyxcbiAgICAnPic6ICcmZ3Q7JyxcbiAgICAnXCInOiAnJnF1b3Q7JyxcbiAgICBcIidcIjogJyYjMDM5OydcbiAgfTtcbiAgXG4gIHJldHVybiB0ZXh0LnJlcGxhY2UoL1smPD5cIiddL2csIGNoYXIgPT4gbWFwW2NoYXJdKTtcbn1cblxuLyoqXG4gKiBDaGVjayBmb3IgcG90ZW50aWFsIGluamVjdGlvbiBwYXR0ZXJuc1xuICovXG5leHBvcnQgZnVuY3Rpb24gZGV0ZWN0SW5qZWN0aW9uKHRleHQ6IHN0cmluZyk6IGJvb2xlYW4ge1xuICBjb25zdCBpbmplY3Rpb25QYXR0ZXJucyA9IFtcbiAgICAvPHNjcmlwdC9pLFxuICAgIC9qYXZhc2NyaXB0Oi9pLFxuICAgIC9vblxcdytcXHMqPS9pLCAvLyBFdmVudCBoYW5kbGVyc1xuICAgIC9ldmFsXFwoL2ksXG4gICAgL2V4cHJlc3Npb25cXCgvaSxcbiAgICAvPGlmcmFtZS9pLFxuICAgIC88b2JqZWN0L2ksXG4gICAgLzxlbWJlZC9pXG4gIF07XG4gIFxuICByZXR1cm4gaW5qZWN0aW9uUGF0dGVybnMuc29tZShwYXR0ZXJuID0+IHBhdHRlcm4udGVzdCh0ZXh0KSk7XG59XG4iXX0=