@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.
411 lines • 56.3 kB
JavaScript
/**
* Gatekeeper Policy Engine
*
* Centralized access control enforcement for MCP-AQL operations.
* Implements a multi-layer defense-in-depth model:
*
* Layer 1: Route Validation (existing - validates endpoint/operation matching)
* Layer 2: Safety Tier Check (integrates @dollhousemcp/safety)
* Layer 3: Element Policy Check (active element restrictions)
* Layer 4: Permission Level Enforcement (AUTO_APPROVE, CONFIRM, DENY)
*
* Design Principles:
* - "LLM instructions are suggestions. Adapter policies are enforcement."
* - Session-scoped confirmations (no cross-session leakage)
* - Security-first (crash = fresh session)
* - Audit logging for all decisions
*/
import { getRoute } from './OperationRouter.js';
import { logger } from '../../utils/logger.js';
import { SecurityMonitor } from '../../security/securityMonitor.js';
import { GatekeeperSession } from './GatekeeperSession.js';
import { GatekeeperConfig } from './GatekeeperConfig.js';
import { PermissionLevel, GatekeeperErrorCode, } from './GatekeeperTypes.js';
import { getDefaultPermissionLevel, resolveElementPolicy, createDecisionFromPolicy, } from './policies/index.js';
/**
* Gatekeeper Policy Engine.
*
* Central enforcement point for MCP-AQL access control.
* Validates that operations are called correctly and enforces
* permission policies based on operation type and active elements.
*/
export class Gatekeeper {
session;
config;
/**
* Permission flags for each CRUDE endpoint.
* These define the security characteristics of each endpoint.
*/
static ENDPOINT_PERMISSIONS = {
CREATE: { readOnly: false, destructive: false },
READ: { readOnly: true, destructive: false },
UPDATE: { readOnly: false, destructive: true },
DELETE: { readOnly: false, destructive: true },
EXECUTE: { readOnly: false, destructive: true }, // Potentially destructive - agents can perform any action
};
constructor(clientInfo, configOptions) {
this.config = new GatekeeperConfig(configOptions);
this.session = new GatekeeperSession(clientInfo, this.config.maxSessionConfirmations);
}
/**
* Get the session ID for this Gatekeeper instance.
*/
get sessionId() {
return this.session.sessionId;
}
/**
* Get a summary of the current session.
*/
getSessionSummary() {
return this.session.getSummary();
}
/**
* Whether permission_prompt has been invoked this session (Issue #625 Phase 4).
*/
get isPermissionPromptActive() {
return this.session.isPermissionPromptActive;
}
/**
* Mark that permission_prompt has been invoked (Issue #625 Phase 4).
*/
markPermissionPromptActive() {
this.session.markPermissionPromptActive();
}
/**
* Validates that an operation is being called via the correct endpoint.
* Throws an error if the operation doesn't exist or is called via wrong endpoint.
*
* This is Layer 1: Route Validation (existing PermissionGuard behavior).
*
* @param operation - The operation being called (e.g., 'create_element')
* @param calledEndpoint - The endpoint it was called through (e.g., 'CREATE')
* @throws Error if operation unknown or endpoint mismatch
*/
validateRoute(operation, calledEndpoint) {
const route = getRoute(operation);
if (!route) {
this.logAuditEvent(operation, calledEndpoint, {
allowed: false,
permissionLevel: PermissionLevel.DENY,
errorCode: GatekeeperErrorCode.UNKNOWN_OPERATION,
reason: `Unknown operation: "${operation}"`,
});
SecurityMonitor.logSecurityEvent({
type: 'UPDATE_SECURITY_VIOLATION',
severity: 'MEDIUM',
source: 'Gatekeeper.validateRoute',
details: `Unknown operation: "${operation}"`,
additionalData: { operation, calledEndpoint, sessionId: this.session.sessionId },
});
throw new Error(`Unknown operation: "${operation}". See tool descriptions for available operations on each endpoint.`);
}
if (route.endpoint !== calledEndpoint) {
const decision = {
allowed: false,
permissionLevel: PermissionLevel.DENY,
errorCode: GatekeeperErrorCode.ENDPOINT_MISMATCH,
reason: `Operation "${operation}" called via wrong endpoint`,
suggestion: `Use mcp_aql_${route.endpoint.toLowerCase()} instead of mcp_aql_${calledEndpoint.toLowerCase()}`,
};
this.logAuditEvent(operation, calledEndpoint, decision);
SecurityMonitor.logSecurityEvent({
type: 'UPDATE_SECURITY_VIOLATION',
severity: 'HIGH',
source: 'Gatekeeper.validateRoute',
details: `Security violation: Operation "${operation}" called via wrong endpoint`,
additionalData: {
operation,
expectedEndpoint: route.endpoint,
actualEndpoint: calledEndpoint,
permissionReason: this.getPermissionReason(route.endpoint),
sessionId: this.session.sessionId,
},
});
throw new Error(`Security violation: Operation "${operation}" must be called via mcp_aql_${route.endpoint.toLowerCase()} endpoint, ` +
`not mcp_aql_${calledEndpoint.toLowerCase()}. ` +
`This operation is classified as ${route.endpoint} due to its ${this.getPermissionReason(route.endpoint)}.`);
}
}
/**
* Enforce access control for an operation.
* This is the main entry point for policy enforcement.
*
* Checks:
* 1. Route validation (operation exists and matches endpoint)
* 2. Element policies (active elements' allow/confirm/deny lists)
* 3. Session confirmations (cached approvals)
* 4. Default operation policies (permission levels)
*
* @param input - The enforcement input containing operation context
* @returns A GatekeeperDecision indicating whether the operation is allowed
*/
enforce(input) {
const { operation, endpoint, elementType, activeElements = [], skipElementPolicies = false } = input;
// Layer 1: Route validation (throws if invalid)
try {
this.validateRoute(operation, endpoint);
}
catch (error) {
// Convert thrown error to decision for consistent return type
return {
allowed: false,
permissionLevel: PermissionLevel.DENY,
errorCode: GatekeeperErrorCode.ENDPOINT_MISMATCH,
reason: error instanceof Error ? error.message : String(error),
};
}
// Layer 2: Element policy resolution
// Issue #679: allowElementPolicyOverrides=false bypasses this layer entirely (operator kill switch)
// Issue #758: skipElementPolicies=true bypasses for gatekeeper infrastructure operations
const shouldResolveElementPolicies = this.config.allowElementPolicyOverrides && !skipElementPolicies;
const policyStart = Date.now();
const policyResult = shouldResolveElementPolicies
? resolveElementPolicy(operation, activeElements, elementType)
: { permissionLevel: getDefaultPermissionLevel(operation), sourceElement: undefined, matchedPolicy: undefined };
const policyMs = Date.now() - policyStart;
// Only log policy resolution when it's not the default AUTO_APPROVE path
if (policyResult.permissionLevel !== PermissionLevel.AUTO_APPROVE || policyMs > 5) {
logger.debug('Gatekeeper policy resolution', {
operation,
activeElementCount: activeElements.length,
overridesEnabled: this.config.allowElementPolicyOverrides,
resultLevel: policyResult.permissionLevel,
sourceElement: policyResult.sourceElement,
matchedPolicy: policyResult.matchedPolicy,
durationMs: policyMs,
});
}
// If element policy denies, return immediately
if (policyResult.permissionLevel === PermissionLevel.DENY) {
const decision = createDecisionFromPolicy(operation, policyResult, elementType);
this.logAuditEvent(operation, endpoint, decision, elementType);
return decision;
}
// Layer 3: Check session confirmations
const confirmation = this.session.checkConfirmation(operation, elementType);
if (confirmation) {
const decision = {
allowed: true,
permissionLevel: policyResult.permissionLevel,
reason: `Operation "${operation}" approved via session confirmation`,
policySource: 'session_confirmation',
};
this.logAuditEvent(operation, endpoint, decision, elementType);
return decision;
}
// Layer 4: Apply default/element policy
const decision = createDecisionFromPolicy(operation, policyResult, elementType);
this.logAuditEvent(operation, endpoint, decision, elementType);
return decision;
}
/**
* Record a confirmation for an operation.
* Called when the user approves an operation that requires confirmation.
*
* @param operation - The operation being confirmed
* @param level - The permission level (CONFIRM_SESSION or CONFIRM_SINGLE_USE)
* @param elementType - Optional element type scope
*/
recordConfirmation(operation, level, elementType) {
this.session.recordConfirmation(operation, level, elementType);
SecurityMonitor.logSecurityEvent({
type: 'CONFIRMATION_RECORDED',
severity: 'LOW',
source: 'Gatekeeper.recordConfirmation',
details: `Confirmation recorded for operation "${operation}"`,
additionalData: {
operation,
permissionLevel: level,
elementType,
sessionId: this.session.sessionId,
},
});
}
/**
* Revoke a confirmation for an operation.
*
* @param operation - The operation to revoke
* @param elementType - Optional element type scope
* @returns true if a confirmation was revoked
*/
revokeConfirmation(operation, elementType) {
return this.session.revokeConfirmation(operation, elementType);
}
/**
* Revoke all confirmations for this session.
* Useful when security-sensitive changes occur.
*/
revokeAllConfirmations() {
this.session.revokeAllConfirmations();
}
// ── CLI Approval Delegation (Issue #625 Phase 3) ──────────────
/**
* Create a CLI approval request.
* Delegates to session and logs the event.
*/
createCliApprovalRequest(toolName, toolInput, riskLevel, riskScore, irreversible, denyReason, policySource, ttlMs) {
const requestId = this.session.createCliApprovalRequest(toolName, toolInput, riskLevel, riskScore, irreversible, denyReason, policySource, ttlMs);
SecurityMonitor.logSecurityEvent({
type: 'CLI_APPROVAL_REQUESTED',
severity: 'MEDIUM',
source: 'Gatekeeper.createCliApprovalRequest',
details: `CLI approval requested for ${toolName}: ${denyReason}`,
additionalData: { requestId, toolName, riskLevel, riskScore, irreversible, sessionId: this.session.sessionId },
});
return requestId;
}
/**
* Approve a pending CLI approval request.
*/
approveCliRequest(requestId, scope = 'single') {
const record = this.session.approveCliRequest(requestId, scope);
if (record) {
SecurityMonitor.logSecurityEvent({
type: 'CLI_APPROVAL_GRANTED',
severity: 'MEDIUM',
source: 'Gatekeeper.approveCliRequest',
details: `CLI approval granted for ${record.toolName} (scope: ${scope})`,
additionalData: { requestId, toolName: record.toolName, scope, sessionId: this.session.sessionId },
});
}
return record;
}
/**
* Check if a CLI tool call has a valid approval.
*/
checkCliApproval(toolName, toolInput) {
const record = this.session.checkCliApproval(toolName, toolInput);
if (record) {
SecurityMonitor.logSecurityEvent({
type: 'CLI_APPROVAL_CONSUMED',
severity: 'LOW',
source: 'Gatekeeper.checkCliApproval',
details: `CLI approval consumed for ${toolName} (scope: ${record.scope})`,
additionalData: { requestId: record.requestId, toolName, scope: record.scope, sessionId: this.session.sessionId },
});
}
return record;
}
/**
* Get all pending CLI approval requests.
*/
getPendingCliApprovals() {
return this.session.getPendingCliApprovals();
}
/**
* Gets the permission flags for an endpoint.
*
* @param endpoint - The CRUD endpoint to get permissions for
* @returns The permission flags for the endpoint
*/
static getPermissions(endpoint) {
return Gatekeeper.ENDPOINT_PERMISSIONS[endpoint];
}
/**
* Static validation method for backward compatibility with PermissionGuard.
* Validates that an operation is being called via the correct endpoint.
*
* @param operation - The operation being called
* @param calledEndpoint - The endpoint it was called through
* @throws Error if operation unknown or endpoint mismatch
*
* @deprecated Use instance method validateRoute() or enforce() instead
*/
static validate(operation, calledEndpoint) {
const route = getRoute(operation);
if (!route) {
SecurityMonitor.logSecurityEvent({
type: 'UPDATE_SECURITY_VIOLATION',
severity: 'MEDIUM',
source: 'Gatekeeper.validate',
details: `Unknown operation: "${operation}"`,
additionalData: { operation, calledEndpoint },
});
throw new Error(`Unknown operation: "${operation}". See tool descriptions for available operations on each endpoint.`);
}
if (route.endpoint !== calledEndpoint) {
SecurityMonitor.logSecurityEvent({
type: 'UPDATE_SECURITY_VIOLATION',
severity: 'HIGH',
source: 'Gatekeeper.validate',
details: `Security violation: Operation "${operation}" called via wrong endpoint`,
additionalData: {
operation,
expectedEndpoint: route.endpoint,
actualEndpoint: calledEndpoint,
permissionReason: Gatekeeper.getPermissionReasonStatic(route.endpoint),
},
});
throw new Error(`Security violation: Operation "${operation}" must be called via mcp_aql_${route.endpoint.toLowerCase()} endpoint, ` +
`not mcp_aql_${calledEndpoint.toLowerCase()}. ` +
`This operation is classified as ${route.endpoint} due to its ${Gatekeeper.getPermissionReasonStatic(route.endpoint)}.`);
}
}
/**
* Get the default permission level for an operation.
*
* @param operation - The operation name
* @returns The default permission level
*/
getDefaultPermissionLevel(operation) {
return getDefaultPermissionLevel(operation);
}
/**
* Returns a human-readable reason for the permission classification.
*/
getPermissionReason(endpoint) {
return Gatekeeper.getPermissionReasonStatic(endpoint);
}
/**
* Static version of getPermissionReason for backward compatibility.
*/
static getPermissionReasonStatic(endpoint) {
switch (endpoint) {
case 'CREATE':
return 'additive, non-destructive nature';
case 'READ':
return 'read-only, safe nature';
case 'UPDATE':
return 'data modification capabilities';
case 'DELETE':
return 'destructive potential';
case 'EXECUTE':
return 'runtime execution lifecycle (stateful, non-idempotent)';
default: {
// Exhaustive check - TypeScript will error if a case is missing
const _exhaustive = endpoint;
return _exhaustive;
}
}
}
/**
* Log an audit event for a Gatekeeper decision.
*/
logAuditEvent(operation, endpoint, decision, elementType) {
if (!this.config.enableAuditLogging) {
return;
}
const auditEntry = {
timestamp: new Date().toISOString(),
sessionId: this.session.sessionId,
operation,
endpoint,
elementType,
decision,
clientInfo: this.session.clientInfo,
};
// Only log security events for denied decisions — allowed ops are normal traffic
if (!decision.allowed) {
SecurityMonitor.logSecurityEvent({
type: 'UPDATE_SECURITY_VIOLATION',
severity: 'MEDIUM',
source: 'Gatekeeper.enforce',
details: `Gatekeeper decision: DENIED - ${decision.reason}`,
additionalData: auditEntry,
});
}
}
}
// Re-export types and utilities for convenience
export { PermissionLevel, GatekeeperErrorCode } from './GatekeeperTypes.js';
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiR2F0ZWtlZXBlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9oYW5kbGVycy9tY3AtYXFsL0dhdGVrZWVwZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7R0FnQkc7QUFFSCxPQUFPLEVBQWdCLFFBQVEsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQzlELE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUMvQyxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sbUNBQW1DLENBQUM7QUFDcEUsT0FBTyxFQUFFLGlCQUFpQixFQUFtQixNQUFNLHdCQUF3QixDQUFDO0FBQzVFLE9BQU8sRUFBRSxnQkFBZ0IsRUFBZ0MsTUFBTSx1QkFBdUIsQ0FBQztBQUN2RixPQUFPLEVBQ0wsZUFBZSxFQUNmLG1CQUFtQixHQU1wQixNQUFNLHNCQUFzQixDQUFDO0FBQzlCLE9BQU8sRUFDTCx5QkFBeUIsRUFDekIsb0JBQW9CLEVBQ3BCLHdCQUF3QixHQUd6QixNQUFNLHFCQUFxQixDQUFDO0FBdUI3Qjs7Ozs7O0dBTUc7QUFDSCxNQUFNLE9BQU8sVUFBVTtJQUNKLE9BQU8sQ0FBb0I7SUFDM0IsTUFBTSxDQUFtQjtJQUUxQzs7O09BR0c7SUFDSyxNQUFNLENBQVUsb0JBQW9CLEdBQThDO1FBQ3hGLE1BQU0sRUFBRSxFQUFFLFFBQVEsRUFBRSxLQUFLLEVBQUUsV0FBVyxFQUFFLEtBQUssRUFBRTtRQUMvQyxJQUFJLEVBQUUsRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLFdBQVcsRUFBRSxLQUFLLEVBQUU7UUFDNUMsTUFBTSxFQUFFLEVBQUUsUUFBUSxFQUFFLEtBQUssRUFBRSxXQUFXLEVBQUUsSUFBSSxFQUFFO1FBQzlDLE1BQU0sRUFBRSxFQUFFLFFBQVEsRUFBRSxLQUFLLEVBQUUsV0FBVyxFQUFFLElBQUksRUFBRTtRQUM5QyxPQUFPLEVBQUUsRUFBRSxRQUFRLEVBQUUsS0FBSyxF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RCxNQUFNLFVBQVUsR0FBeUI7WUFDdkMsU0FBUyxFQUFFLElBQUksSUFBSSxFQUFFLENBQUMsV0FBVyxFQUFFO1lBQ25DLFNBQVMsRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLFNBQVM7WUFDakMsU0FBUztZQUNULFFBQVE7WUFDUixXQUFXO1lBQ1gsUUFBUTtZQUNSLFVBQVUsRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLFVBQVU7U0FDcEMsQ0FBQztRQUVGLGlGQUFpRjtRQUNqRixJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ3RCLGVBQWUsQ0FBQyxnQkFBZ0IsQ0FBQztnQkFDL0IsSUFBSSxFQUFFLDJCQUEyQjtnQkFDakMsUUFBUSxFQUFFLFFBQVE7Z0JBQ2xCLE1BQU0sRUFBRSxvQkFBb0I7Z0JBQzVCLE9BQU8sRUFBRSxpQ0FBaUMsUUFBUSxDQUFDLE1BQU0sRUFBRTtnQkFDM0QsY0FBYyxFQUFFLFVBQVU7YUFDM0IsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztJQUNILENBQUM7O0FBR0gsZ0RBQWdEO0FBQ2hELE9BQU8sRUFBRSxlQUFlLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogR2F0ZWtlZXBlciBQb2xpY3kgRW5naW5lXG4gKlxuICogQ2VudHJhbGl6ZWQgYWNjZXNzIGNvbnRyb2wgZW5mb3JjZW1lbnQgZm9yIE1DUC1BUUwgb3BlcmF0aW9ucy5cbiAqIEltcGxlbWVudHMgYSBtdWx0aS1sYXllciBkZWZlbnNlLWluLWRlcHRoIG1vZGVsOlxuICpcbiAqIExheWVyIDE6IFJvdXRlIFZhbGlkYXRpb24gKGV4aXN0aW5nIC0gdmFsaWRhdGVzIGVuZHBvaW50L29wZXJhdGlvbiBtYXRjaGluZylcbiAqIExheWVyIDI6IFNhZmV0eSBUaWVyIENoZWNrIChpbnRlZ3JhdGVzIEBkb2xsaG91c2VtY3Avc2FmZXR5KVxuICogTGF5ZXIgMzogRWxlbWVudCBQb2xpY3kgQ2hlY2sgKGFjdGl2ZSBlbGVtZW50IHJlc3RyaWN0aW9ucylcbiAqIExheWVyIDQ6IFBlcm1pc3Npb24gTGV2ZWwgRW5mb3JjZW1lbnQgKEFVVE9fQVBQUk9WRSwgQ09ORklSTSwgREVOWSlcbiAqXG4gKiBEZXNpZ24gUHJpbmNpcGxlczpcbiAqIC0gXCJMTE0gaW5zdHJ1Y3Rpb25zIGFyZSBzdWdnZXN0aW9ucy4gQWRhcHRlciBwb2xpY2llcyBhcmUgZW5mb3JjZW1lbnQuXCJcbiAqIC0gU2Vzc2lvbi1zY29wZWQgY29uZmlybWF0aW9ucyAobm8gY3Jvc3Mtc2Vzc2lvbiBsZWFrYWdlKVxuICogLSBTZWN1cml0eS1maXJzdCAoY3Jhc2ggPSBmcmVzaCBzZXNzaW9uKVxuICogLSBBdWRpdCBsb2dnaW5nIGZvciBhbGwgZGVjaXNpb25zXG4gKi9cblxuaW1wb3J0IHsgQ1JVREVuZHBvaW50LCBnZXRSb3V0ZSB9IGZyb20gJy4vT3BlcmF0aW9uUm91dGVyLmpzJztcbmltcG9ydCB7IGxvZ2dlciB9IGZyb20gJy4uLy4uL3V0aWxzL2xvZ2dlci5qcyc7XG5pbXBvcnQgeyBTZWN1cml0eU1vbml0b3IgfSBmcm9tICcuLi8uLi9zZWN1cml0eS9zZWN1cml0eU1vbml0b3IuanMnO1xuaW1wb3J0IHsgR2F0ZWtlZXBlclNlc3Npb24sIHR5cGUgQ2xpZW50SW5mbyB9IGZyb20gJy4vR2F0ZWtlZXBlclNlc3Npb24uanMnO1xuaW1wb3J0IHsgR2F0ZWtlZXBlckNvbmZpZywgdHlwZSBHYXRla2VlcGVyQ29uZmlnT3B0aW9ucyB9IGZyb20gJy4vR2F0ZWtlZXBlckNvbmZpZy5qcyc7XG5pbXBvcnQge1xuICBQZXJtaXNzaW9uTGV2ZWwsXG4gIEdhdGVrZWVwZXJFcnJvckNvZGUsXG4gIHR5cGUgR2F0ZWtlZXBlckRlY2lzaW9uLFxuICB0eXBlIEVuZHBvaW50UGVybWlzc2lvbnMsXG4gIHR5cGUgR2F0ZWtlZXBlckF1ZGl0RW50cnksXG4gIHR5cGUgQ2xpQXBwcm92YWxSZWNvcmQsXG4gIHR5cGUgQ2xpQXBwcm92YWxTY29wZSxcbn0gZnJvbSAnLi9HYXRla2VlcGVyVHlwZXMuanMnO1xuaW1wb3J0IHtcbiAgZ2V0RGVmYXVsdFBlcm1pc3Npb25MZXZlbCxcbiAgcmVzb2x2ZUVsZW1lbnRQb2xpY3ksXG4gIGNyZWF0ZURlY2lzaW9uRnJvbVBvbGljeSxcbiAgdHlwZSBBY3RpdmVFbGVtZW50LFxuICB0eXBlIEVsZW1lbnRQb2xpY3lSZXN1bHQsXG59IGZyb20gJy4vcG9saWNpZXMvaW5kZXguanMnO1xuXG4vKipcbiAqIElucHV0IGZvciBHYXRla2VlcGVyIGVuZm9yY2VtZW50LlxuICogQ29udGFpbnMgYWxsIGNvbnRleHQgbmVlZGVkIHRvIG1ha2UgYW4gYWNjZXNzIGNvbnRyb2wgZGVjaXNpb24uXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRW5mb3JjZUlucHV0IHtcbiAgLyoqIFRoZSBvcGVyYXRpb24gYmVpbmcgcmVxdWVzdGVkICovXG4gIG9wZXJhdGlvbjogc3RyaW5nO1xuICAvKiogVGhlIENSVUQgZW5kcG9pbnQgYmVpbmcgY2FsbGVkICovXG4gIGVuZHBvaW50OiBDUlVERW5kcG9pbnQ7XG4gIC8qKiBFbGVtZW50IHR5cGUgYmVpbmcgb3BlcmF0ZWQgb24gKGlmIGFwcGxpY2FibGUpICovXG4gIGVsZW1lbnRUeXBlPzogc3RyaW5nO1xuICAvKiogQ3VycmVudGx5IGFjdGl2ZSBlbGVtZW50cyBmb3IgcG9saWN5IGV2YWx1YXRpb24gKi9cbiAgYWN0aXZlRWxlbWVudHM/OiBBY3RpdmVFbGVtZW50W107XG4gIC8qKlxuICAgKiBTa2lwIExheWVyIDIgKGVsZW1lbnQgcG9saWN5IHJlc29sdXRpb24pIOKAlCB1c2Ugcm91dGUtbGV2ZWwgZGVmYXVsdHMgb25seS5cbiAgICogVXNlZCBmb3IgZ2F0ZWtlZXBlciBpbmZyYXN0cnVjdHVyZSBvcGVyYXRpb25zIChjb25maXJtX29wZXJhdGlvbiwgdmVyaWZ5X2NoYWxsZW5nZSlcbiAgICogdG8gcHJldmVudCBjYXNjYWRpbmcgY29uZmlybWF0aW9uIGxvb3BzLiBJc3N1ZSAjNzU4LlxuICAgKi9cbiAgc2tpcEVsZW1lbnRQb2xpY2llcz86IGJvb2xlYW47XG59XG5cbi8qKlxuICogR2F0ZWtlZXBlciBQb2xpY3kgRW5naW5lLlxuICpcbiAqIENlbnRyYWwgZW5mb3JjZW1lbnQgcG9pbnQgZm9yIE1DUC1BUUwgYWNjZXNzIGNvbnRyb2wuXG4gKiBWYWxpZGF0ZXMgdGhhdCBvcGVyYXRpb25zIGFyZSBjYWxsZWQgY29ycmVjdGx5IGFuZCBlbmZvcmNlc1xuICogcGVybWlzc2lvbiBwb2xpY2llcyBiYXNlZCBvbiBvcGVyYXRpb24gdHlwZSBhbmQgYWN0aXZlIGVsZW1lbnRzLlxuICovXG5leHBvcnQgY2xhc3MgR2F0ZWtlZXBlciB7XG4gIHByaXZhdGUgcmVhZG9ubHkgc2Vzc2lvbjogR2F0ZWtlZXBlclNlc3Npb247XG4gIHByaXZhdGUgcmVhZG9ubHkgY29uZmlnOiBHYXRla2VlcGVyQ29uZmlnO1xuXG4gIC8qKlxuICAgKiBQZXJtaXNzaW9uIGZsYWdzIGZvciBlYWNoIENSVURFIGVuZHBvaW50LlxuICAgKiBUaGVzZSBkZWZpbmUgdGhlIHNlY3VyaXR5IGNoYXJhY3RlcmlzdGljcyBvZiBlYWNoIGVuZHBvaW50LlxuICAgKi9cbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgRU5EUE9JTlRfUEVSTUlTU0lPTlM6IFJlY29yZDxDUlVERW5kcG9pbnQsIEVuZHBvaW50UGVybWlzc2lvbnM+ID0ge1xuICAgIENSRUFURTogeyByZWFkT25seTogZmFsc2UsIGRlc3RydWN0aXZlOiBmYWxzZSB9LFxuICAgIFJFQUQ6IHsgcmVhZE9ubHk6IHRydWUsIGRlc3RydWN0aXZlOiBmYWxzZSB9LFxuICAgIFVQREFURTogeyByZWFkT25seTogZmFsc2UsIGRlc3RydWN0aXZlOiB0cnVlIH0sXG4gICAgREVMRVRFOiB7IHJlYWRPbmx5OiBmYWxzZSwgZGVzdHJ1Y3RpdmU6IHRydWUgfSxcbiAgICBFWEVDVVRFOiB7IHJlYWRPbmx5OiBmYWxzZSwgZGVzdHJ1Y3RpdmU6IHRydWUgfSwgIC8vIFBvdGVudGlhbGx5IGRlc3RydWN0aXZlIC0gYWdlbnRzIGNhbiBwZXJmb3JtIGFueSBhY3Rpb25cbiAgfTtcblxuICBjb25zdHJ1Y3RvcihcbiAgICBjbGllbnRJbmZvPzogQ2xpZW50SW5mbyxcbiAgICBjb25maWdPcHRpb25zPzogR2F0ZWtlZXBlckNvbmZpZ09wdGlvbnNcbiAgKSB7XG4gICAgdGhpcy5jb25maWcgPSBuZXcgR2F0ZWtlZXBlckNvbmZpZyhjb25maWdPcHRpb25zKTtcbiAgICB0aGlzLnNlc3Npb24gPSBuZXcgR2F0ZWtlZXBlclNlc3Npb24oY2xpZW50SW5mbywgdGhpcy5jb25maWcubWF4U2Vzc2lvbkNvbmZpcm1hdGlvbnMpO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCB0aGUgc2Vzc2lvbiBJRCBmb3IgdGhpcyBHYXRla2VlcGVyIGluc3RhbmNlLlxuICAgKi9cbiAgZ2V0IHNlc3Npb25JZCgpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLnNlc3Npb24uc2Vzc2lvbklkO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBhIHN1bW1hcnkgb2YgdGhlIGN1cnJlbnQgc2Vzc2lvbi5cbiAgICovXG4gIGdldFNlc3Npb25TdW1tYXJ5KCk6IFJldHVyblR5cGU8R2F0ZWtlZXBlclNlc3Npb25bJ2dldFN1bW1hcnknXT4ge1xuICAgIHJldHVybiB0aGlzLnNlc3Npb24uZ2V0U3VtbWFyeSgpO1xuICB9XG5cbiAgLyoqXG4gICAqIFdoZXRoZXIgcGVybWlzc2lvbl9wcm9tcHQgaGFzIGJlZW4gaW52b2tlZCB0aGlzIHNlc3Npb24gKElzc3VlICM2MjUgUGhhc2UgNCkuXG4gICAqL1xuICBnZXQgaXNQZXJtaXNzaW9uUHJvbXB0QWN0aXZlKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLnNlc3Npb24uaXNQZXJtaXNzaW9uUHJvbXB0QWN0aXZlO1xuICB9XG5cbiAgLyoqXG4gICAqIE1hcmsgdGhhdCBwZXJtaXNzaW9uX3Byb21wdCBoYXMgYmVlbiBpbnZva2VkIChJc3N1ZSAjNjI1IFBoYXNlIDQpLlxuICAgKi9cbiAgbWFya1Blcm1pc3Npb25Qcm9tcHRBY3RpdmUoKTogdm9pZCB7XG4gICAgdGhpcy5zZXNzaW9uLm1hcmtQZXJtaXNzaW9uUHJvbXB0QWN0aXZlKCk7XG4gIH1cblxuICAvKipcbiAgICogVmFsaWRhdGVzIHRoYXQgYW4gb3BlcmF0aW9uIGlzIGJlaW5nIGNhbGxlZCB2aWEgdGhlIGNvcnJlY3QgZW5kcG9pbnQuXG4gICAqIFRocm93cyBhbiBlcnJvciBpZiB0aGUgb3BlcmF0aW9uIGRvZXNuJ3QgZXhpc3Qgb3IgaXMgY2FsbGVkIHZpYSB3cm9uZyBlbmRwb2ludC5cbiAgICpcbiAgICogVGhpcyBpcyBMYXllciAxOiBSb3V0ZSBWYWxpZGF0aW9uIChleGlzdGluZyBQZXJtaXNzaW9uR3VhcmQgYmVoYXZpb3IpLlxuICAgKlxuICAgKiBAcGFyYW0gb3BlcmF0aW9uIC0gVGhlIG9wZXJhdGlvbiBiZWluZyBjYWxsZWQgKGUuZy4sICdjcmVhdGVfZWxlbWVudCcpXG4gICAqIEBwYXJhbSBjYWxsZWRFbmRwb2ludCAtIFRoZSBlbmRwb2ludCBpdCB3YXMgY2FsbGVkIHRocm91Z2ggKGUuZy4sICdDUkVBVEUnKVxuICAgKiBAdGhyb3dzIEVycm9yIGlmIG9wZXJhdGlvbiB1bmtub3duIG9yIGVuZHBvaW50IG1pc21hdGNoXG4gICAqL1xuICB2YWxpZGF0ZVJvdXRlKG9wZXJhdGlvbjogc3RyaW5nLCBjYWxsZWRFbmRwb2ludDogQ1JVREVuZHBvaW50KTogdm9pZCB7XG4gICAgY29uc3Qgcm91dGUgPSBnZXRSb3V0ZShvcGVyYXRpb24pO1xuXG4gICAgaWYgKCFyb3V0ZSkge1xuICAgICAgdGhpcy5sb2dBdWRpdEV2ZW50KG9wZXJhdGlvbiwgY2FsbGVkRW5kcG9pbnQsIHtcbiAgICAgICAgYWxsb3dlZDogZmFsc2UsXG4gICAgICAgIHBlcm1pc3Npb25MZXZlbDogUGVybWlzc2lvbkxldmVsLkRFTlksXG4gICAgICAgIGVycm9yQ29kZTogR2F0ZWtlZXBlckVycm9yQ29kZS5VTktOT1dOX09QRVJBVElPTixcbiAgICAgICAgcmVhc29uOiBgVW5rbm93biBvcGVyYXRpb246IFwiJHtvcGVyYXRpb259XCJgLFxuICAgICAgfSk7XG5cbiAgICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgICAgdHlwZTogJ1VQREFURV9TRUNVUklUWV9WSU9MQVRJT04nLFxuICAgICAgICBzZXZlcml0eTogJ01FRElVTScsXG4gICAgICAgIHNvdXJjZTogJ0dhdGVrZWVwZXIudmFsaWRhdGVSb3V0ZScsXG4gICAgICAgIGRldGFpbHM6IGBVbmtub3duIG9wZXJhdGlvbjogXCIke29wZXJhdGlvbn1cImAsXG4gICAgICAgIGFkZGl0aW9uYWxEYXRhOiB7IG9wZXJhdGlvbiwgY2FsbGVkRW5kcG9pbnQsIHNlc3Npb25JZDogdGhpcy5zZXNzaW9uLnNlc3Npb25JZCB9LFxuICAgICAgfSk7XG5cbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgYFVua25vd24gb3BlcmF0aW9uOiBcIiR7b3BlcmF0aW9ufVwiLiBTZWUgdG9vbCBkZXNjcmlwdGlvbnMgZm9yIGF2YWlsYWJsZSBvcGVyYXRpb25zIG9uIGVhY2ggZW5kcG9pbnQuYFxuICAgICAgKTtcbiAgICB9XG5cbiAgICBpZiAocm91dGUuZW5kcG9pbnQgIT09IGNhbGxlZEVuZHBvaW50KSB7XG4gICAgICBjb25zdCBkZWNpc2lvbjogR2F0ZWtlZXBlckRlY2lzaW9uID0ge1xuICAgICAgICBhbGxvd2VkOiBmYWxzZSxcbiAgICAgICAgcGVybWlzc2lvbkxldmVsOiBQZXJtaXNzaW9uTGV2ZWwuREVOWSxcbiAgICAgICAgZXJyb3JDb2RlOiBHYXRla2VlcGVyRXJyb3JDb2RlLkVORFBPSU5UX01JU01BVENILFxuICAgICAgICByZWFzb246IGBPcGVyYXRpb24gXCIke29wZXJhdGlvbn1cIiBjYWxsZWQgdmlhIHdyb25nIGVuZHBvaW50YCxcbiAgICAgICAgc3VnZ2VzdGlvbjogYFVzZSBtY3BfYXFsXyR7cm91dGUuZW5kcG9pbnQudG9Mb3dlckNhc2UoKX0gaW5zdGVhZCBvZiBtY3BfYXFsXyR7Y2FsbGVkRW5kcG9pbnQudG9Mb3dlckNhc2UoKX1gLFxuICAgICAgfTtcblxuICAgICAgdGhpcy5sb2dBdWRpdEV2ZW50KG9wZXJhdGlvbiwgY2FsbGVkRW5kcG9pbnQsIGRlY2lzaW9uKTtcblxuICAgICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgICB0eXBlOiAnVVBEQVRFX1NFQ1VSSVRZX1ZJT0xBVElPTicsXG4gICAgICAgIHNldmVyaXR5OiAnSElHSCcsXG4gICAgICAgIHNvdXJjZTogJ0dhdGVrZWVwZXIudmFsaWRhdGVSb3V0ZScsXG4gICAgICAgIGRldGFpbHM6IGBTZWN1cml0eSB2aW9sYXRpb246IE9wZXJhdGlvbiBcIiR7b3BlcmF0aW9ufVwiIGNhbGxlZCB2aWEgd3JvbmcgZW5kcG9pbnRgLFxuICAgICAgICBhZGRpdGlvbmFsRGF0YToge1xuICAgICAgICAgIG9wZXJhdGlvbixcbiAgICAgICAgICBleHBlY3RlZEVuZHBvaW50OiByb3V0ZS5lbmRwb2ludCxcbiAgICAgICAgICBhY3R1YWxFbmRwb2ludDogY2FsbGVkRW5kcG9pbnQsXG4gICAgICAgICAgcGVybWlzc2lvblJlYXNvbjogdGhpcy5nZXRQZXJtaXNzaW9uUmVhc29uKHJvdXRlLmVuZHBvaW50KSxcbiAgICAgICAgICBzZXNzaW9uSWQ6IHRoaXMuc2Vzc2lvbi5zZXNzaW9uSWQsXG4gICAgICAgIH0sXG4gICAgICB9KTtcblxuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICBgU2VjdXJpdHkgdmlvbGF0aW9uOiBPcGVyYXRpb24gXCIke29wZXJhdGlvbn1cIiBtdXN0IGJlIGNhbGxlZCB2aWEgbWNwX2FxbF8ke3JvdXRlLmVuZHBvaW50LnRvTG93ZXJDYXNlKCl9IGVuZHBvaW50LCBgICtcbiAgICAgICAgICBgbm90IG1jcF9hcWxfJHtjYWxsZWRFbmRwb2ludC50b0xvd2VyQ2FzZSgpfS4gYCArXG4gICAgICAgICAgYFRoaXMgb3BlcmF0aW9uIGlzIGNsYXNzaWZpZWQgYXMgJHtyb3V0ZS5lbmRwb2ludH0gZHVlIHRvIGl0cyAke3RoaXMuZ2V0UGVybWlzc2lvblJlYXNvbihyb3V0ZS5lbmRwb2ludCl9LmBcbiAgICAgICk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEVuZm9yY2UgYWNjZXNzIGNvbnRyb2wgZm9yIGFuIG9wZXJhdGlvbi5cbiAgICogVGhpcyBpcyB0aGUgbWFpbiBlbnRyeSBwb2ludCBmb3IgcG9saWN5IGVuZm9yY2VtZW50LlxuICAgKlxuICAgKiBDaGVja3M6XG4gICAqIDEuIFJvdXRlIHZhbGlkYXRpb24gKG9wZXJhdGlvbiBleGlzdHMgYW5kIG1hdGNoZXMgZW5kcG9pbnQpXG4gICAqIDIuIEVsZW1lbnQgcG9saWNpZXMgKGFjdGl2ZSBlbGVtZW50cycgYWxsb3cvY29uZmlybS9kZW55IGxpc3RzKVxuICAgKiAzLiBTZXNzaW9uIGNvbmZpcm1hdGlvbnMgKGNhY2hlZCBhcHByb3ZhbHMpXG4gICAqIDQuIERlZmF1bHQgb3BlcmF0aW9uIHBvbGljaWVzIChwZXJtaXNzaW9uIGxldmVscylcbiAgICpcbiAgICogQHBhcmFtIGlucHV0IC0gVGhlIGVuZm9yY2VtZW50IGlucHV0IGNvbnRhaW5pbmcgb3BlcmF0aW9uIGNvbnRleHRcbiAgICogQHJldHVybnMgQSBHYXRla2VlcGVyRGVjaXNpb24gaW5kaWNhdGluZyB3aGV0aGVyIHRoZSBvcGVyYXRpb24gaXMgYWxsb3dlZFxuICAgKi9cbiAgZW5mb3JjZShpbnB1dDogRW5mb3JjZUlucHV0KTogR2F0ZWtlZXBlckRlY2lzaW9uIHtcbiAgICBjb25zdCB7IG9wZXJhdGlvbiwgZW5kcG9pbnQsIGVsZW1lbnRUeXBlLCBhY3RpdmVFbGVtZW50cyA9IFtdLCBza2lwRWxlbWVudFBvbGljaWVzID0gZmFsc2UgfSA9IGlucHV0O1xuXG4gICAgLy8gTGF5ZXIgMTogUm91dGUgdmFsaWRhdGlvbiAodGhyb3dzIGlmIGludmFsaWQpXG4gICAgdHJ5IHtcbiAgICAgIHRoaXMudmFsaWRhdGVSb3V0ZShvcGVyYXRpb24sIGVuZHBvaW50KTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgLy8gQ29udmVydCB0aHJvd24gZXJyb3IgdG8gZGVjaXNpb24gZm9yIGNvbnNpc3RlbnQgcmV0dXJuIHR5cGVcbiAgICAgIHJldHVybiB7XG4gICAgICAgIGFsbG93ZWQ6IGZhbHNlLFxuICAgICAgICBwZXJtaXNzaW9uTGV2ZWw6IFBlcm1pc3Npb25MZXZlbC5ERU5ZLFxuICAgICAgICBlcnJvckNvZGU6IEdhdGVrZWVwZXJFcnJvckNvZGUuRU5EUE9JTlRfTUlTTUFUQ0gsXG4gICAgICAgIHJlYXNvbjogZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm1lc3NhZ2UgOiBTdHJpbmcoZXJyb3IpLFxuICAgICAgfTtcbiAgICB9XG5cbiAgICAvLyBMYXllciAyOiBFbGVtZW50IHBvbGljeSByZXNvbHV0aW9uXG4gICAgLy8gSXNzdWUgIzY3OTogYWxsb3dFbGVtZW50UG9saWN5T3ZlcnJpZGVzPWZhbHNlIGJ5cGFzc2VzIHRoaXMgbGF5ZXIgZW50aXJlbHkgKG9wZXJhdG9yIGtpbGwgc3dpdGNoKVxuICAgIC8vIElzc3VlICM3NTg6IHNraXBFbGVtZW50UG9saWNpZXM9dHJ1ZSBieXBhc3NlcyBmb3IgZ2F0ZWtlZXBlciBpbmZyYXN0cnVjdHVyZSBvcGVyYXRpb25zXG4gICAgY29uc3Qgc2hvdWxkUmVzb2x2ZUVsZW1lbnRQb2xpY2llcyA9IHRoaXMuY29uZmlnLmFsbG93RWxlbWVudFBvbGljeU92ZXJyaWRlcyAmJiAhc2tpcEVsZW1lbnRQb2xpY2llcztcbiAgICBjb25zdCBwb2xpY3lTdGFydCA9IERhdGUubm93KCk7XG4gICAgY29uc3QgcG9saWN5UmVzdWx0ID0gc2hvdWxkUmVzb2x2ZUVsZW1lbnRQb2xpY2llc1xuICAgICAgPyByZXNvbHZlRWxlbWVudFBvbGljeShvcGVyYXRpb24sIGFjdGl2ZUVsZW1lbnRzLCBlbGVtZW50VHlwZSlcbiAgICAgIDogeyBwZXJtaXNzaW9uTGV2ZWw6IGdldERlZmF1bHRQZXJtaXNzaW9uTGV2ZWwob3BlcmF0aW9uKSwgc291cmNlRWxlbWVudDogdW5kZWZpbmVkLCBtYXRjaGVkUG9saWN5OiB1bmRlZmluZWQgYXMgRWxlbWVudFBvbGljeVJlc3VsdFsnbWF0Y2hlZFBvbGljeSddIH07XG4gICAgY29uc3QgcG9saWN5TXMgPSBEYXRlLm5vdygpIC0gcG9saWN5U3RhcnQ7XG4gICAgLy8gT25seSBsb2cgcG9saWN5IHJlc29sdXRpb24gd2hlbiBpdCdzIG5vdCB0aGUgZGVmYXVsdCBBVVRPX0FQUFJPVkUgcGF0aFxuICAgIGlmIChwb2xpY3lSZXN1bHQucGVybWlzc2lvbkxldmVsICE9PSBQZXJtaXNzaW9uTGV2ZWwuQVVUT19BUFBST1ZFIHx8IHBvbGljeU1zID4gNSkge1xuICAgICAgbG9nZ2VyLmRlYnVnKCdHYXRla2VlcGVyIHBvbGljeSByZXNvbHV0aW9uJywge1xuICAgICAgICBvcGVyYXRpb24sXG4gICAgICAgIGFjdGl2ZUVsZW1lbnRDb3VudDogYWN0aXZlRWxlbWVudHMubGVuZ3RoLFxuICAgICAgICBvdmVycmlkZXNFbmFibGVkOiB0aGlzLmNvbmZpZy5hbGxvd0VsZW1lbnRQb2xpY3lPdmVycmlkZXMsXG4gICAgICAgIHJlc3VsdExldmVsOiBwb2xpY3lSZXN1bHQucGVybWlzc2lvbkxldmVsLFxuICAgICAgICBzb3VyY2VFbGVtZW50OiBwb2xpY3lSZXN1bHQuc291cmNlRWxlbWVudCxcbiAgICAgICAgbWF0Y2hlZFBvbGljeTogcG9saWN5UmVzdWx0Lm1hdGNoZWRQb2xpY3ksXG4gICAgICAgIGR1cmF0aW9uTXM6IHBvbGljeU1zLFxuICAgICAgfSk7XG4gICAgfVxuXG4gICAgLy8gSWYgZWxlbWVudCBwb2xpY3kgZGVuaWVzLCByZXR1cm4gaW1tZWRpYXRlbHlcbiAgICBpZiAocG9saWN5UmVzdWx0LnBlcm1pc3Npb25MZXZlbCA9PT0gUGVybWlzc2lvbkxldmVsLkRFTlkpIHtcbiAgICAgIGNvbnN0IGRlY2lzaW9uID0gY3JlYXRlRGVjaXNpb25Gcm9tUG9saWN5KG9wZXJhdGlvbiwgcG9saWN5UmVzdWx0LCBlbGVtZW50VHlwZSk7XG4gICAgICB0aGlzLmxvZ0F1ZGl0RXZlbnQob3BlcmF0aW9uLCBlbmRwb2ludCwgZGVjaXNpb24sIGVsZW1lbnRUeXBlKTtcbiAgICAgIHJldHVybiBkZWNpc2lvbjtcbiAgICB9XG5cbiAgICAvLyBMYXllciAzOiBDaGVjayBzZXNzaW9uIGNvbmZpcm1hdGlvbnNcbiAgICBjb25zdCBjb25maXJtYXRpb24gPSB0aGlzLnNlc3Npb24uY2hlY2tDb25maXJtYXRpb24ob3BlcmF0aW9uLCBlbGVtZW50VHlwZSk7XG4gICAgaWYgKGNvbmZpcm1hdGlvbikge1xuICAgICAgY29uc3QgZGVjaXNpb246IEdhdGVrZWVwZXJEZWNpc2lvbiA9IHtcbiAgICAgICAgYWxsb3dlZDogdHJ1ZSxcbiAgICAgICAgcGVybWlzc2lvbkxldmVsOiBwb2xpY3lSZXN1bHQucGVybWlzc2lvbkxldmVsLFxuICAgICAgICByZWFzb246IGBPcGVyYXRpb24gXCIke29wZXJhdGlvbn1cIiBhcHByb3ZlZCB2aWEgc2Vzc2lvbiBjb25maXJtYXRpb25gLFxuICAgICAgICBwb2xpY3lTb3VyY2U6ICdzZXNzaW9uX2NvbmZpcm1hdGlvbicsXG4gICAgICB9O1xuICAgICAgdGhpcy5sb2dBdWRpdEV2ZW50KG9wZXJhdGlvbiwgZW5kcG9pbnQsIGRlY2lzaW9uLCBlbGVtZW50VHlwZSk7XG4gICAgICByZXR1cm4gZGVjaXNpb247XG4gICAgfVxuXG4gICAgLy8gTGF5ZXIgNDogQXBwbHkgZGVmYXVsdC9lbGVtZW50IHBvbGljeVxuICAgIGNvbnN0IGRlY2lzaW9uID0gY3JlYXRlRGVjaXNpb25Gcm9tUG9saWN5KG9wZXJhdGlvbiwgcG9saWN5UmVzdWx0LCBlbGVtZW50VHlwZSk7XG4gICAgdGhpcy5sb2dBdWRpdEV2ZW50KG9wZXJhdGlvbiwgZW5kcG9pbnQsIGRlY2lzaW9uLCBlbGVtZW50VHlwZSk7XG4gICAgcmV0dXJuIGRlY2lzaW9uO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlY29yZCBhIGNvbmZpcm1hdGlvbiBmb3IgYW4gb3BlcmF0aW9uLlxuICAgKiBDYWxsZWQgd2hlbiB0aGUgdXNlciBhcHByb3ZlcyBhbiBvcGVyYXRpb24gdGhhdCByZXF1aXJlcyBjb25maXJtYXRpb24uXG4gICAqXG4gICAqIEBwYXJhbSBvcGVyYXRpb24gLSBUaGUgb3BlcmF0aW9uIGJlaW5nIGNvbmZpcm1lZFxuICAgKiBAcGFyYW0gbGV2ZWwgLSBUaGUgcGVybWlzc2lvbiBsZXZlbCAoQ09ORklSTV9TRVNTSU9OIG9yIENPTkZJUk1fU0lOR0xFX1VTRSlcbiAgICogQHBhcmFtIGVsZW1lbnRUeXBlIC0gT3B0aW9uYWwgZWxlbWVudCB0eXBlIHNjb3BlXG4gICAqL1xuICByZWNvcmRDb25maXJtYXRpb24oXG4gICAgb3BlcmF0aW9uOiBzdHJpbmcsXG4gICAgbGV2ZWw6IFBlcm1pc3Npb25MZXZlbC5DT05GSVJNX1NFU1NJT04gfCBQZXJtaXNzaW9uTGV2ZWwuQ09ORklSTV9TSU5HTEVfVVNFLFxuICAgIGVsZW1lbnRUeXBlPzogc3RyaW5nXG4gICk6IHZvaWQge1xuICAgIHRoaXMuc2Vzc2lvbi5yZWNvcmRDb25maXJtYXRpb24ob3BlcmF0aW9uLCBsZXZlbCwgZWxlbWVudFR5cGUpO1xuXG4gICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgdHlwZTogJ0NPTkZJUk1BVElPTl9SRUNPUkRFRCcsXG4gICAgICBzZXZlcml0eTogJ0xPVycsXG4gICAgICBzb3VyY2U6ICdHYXRla2VlcGVyLnJlY29yZENvbmZpcm1hdGlvbicsXG4gICAgICBkZXRhaWxzOiBgQ29uZmlybWF0aW9uIHJlY29yZGVkIGZvciBvcGVyYXRpb24gXCIke29wZXJhdGlvbn1cImAsXG4gICAgICBhZGRpdGlvbmFsRGF0YToge1xuICAgICAgICBvcGVyYXRpb24sXG4gICAgICAgIHBlcm1pc3Npb25MZXZlbDogbGV2ZWwsXG4gICAgICAgIGVsZW1lbnRUeXBlLFxuICAgICAgICBzZXNzaW9uSWQ6IHRoaXMuc2Vzc2lvbi5zZXNzaW9uSWQsXG4gICAgICB9LFxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldm9rZSBhIGNvbmZpcm1hdGlvbiBmb3IgYW4gb3BlcmF0aW9uLlxuICAgKlxuICAgKiBAcGFyYW0gb3BlcmF0aW9uIC0gVGhlIG9wZXJhdGlvbiB0byByZXZva2VcbiAgICogQHBhcmFtIGVsZW1lbnRUeXBlIC0gT3B0aW9uYWwgZWxlbWVudCB0eXBlIHNjb3BlXG4gICAqIEByZXR1cm5zIHRydWUgaWYgYSBjb25maXJtYXRpb24gd2FzIHJldm9rZWRcbiAgICovXG4gIHJldm9rZUNvbmZpcm1hdGlvbihvcGVyYXRpb246IHN0cmluZywgZWxlbWVudFR5cGU/OiBzdHJpbmcpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5zZXNzaW9uLnJldm9rZUNvbmZpcm1hdGlvbihvcGVyYXRpb24sIGVsZW1lbnRUeXBlKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXZva2UgYWxsIGNvbmZpcm1hdGlvbnMgZm9yIHRoaXMgc2Vzc2lvbi5cbiAgICogVXNlZnVsIHdoZW4gc2VjdXJpdHktc2Vuc2l0aXZlIGNoYW5nZXMgb2NjdXIuXG4gICAqL1xuICByZXZva2VBbGxDb25maXJtYXRpb25zKCk6IHZvaWQge1xuICAgIHRoaXMuc2Vzc2lvbi5yZXZva2VBbGxDb25maXJtYXRpb25zKCk7XG4gIH1cblxuICAvLyDilIDilIAgQ0xJIEFwcHJvdmFsIERlbGVnYXRpb24gKElzc3VlICM2MjUgUGhhc2UgMykg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbiAgLyoqXG4gICAqIENyZWF0ZSBhIENMSSBhcHByb3ZhbCByZXF1ZXN0LlxuICAgKiBEZWxlZ2F0ZXMgdG8gc2Vzc2lvbiBhbmQgbG9ncyB0aGUgZXZlbnQuXG4gICAqL1xuICBjcmVhdGVDbGlBcHByb3ZhbFJlcXVlc3QoXG4gICAgdG9vbE5hbWU6IHN0cmluZyxcbiAgICB0b29sSW5wdXQ6IFJlY29yZDxzdHJpbmcsIHVua25vd24+LFxuICAgIHJpc2tMZXZlbDogc3RyaW5nLFxuICAgIHJpc2tTY29yZTogbnVtYmVyLFxuICAgIGlycmV2ZXJzaWJsZTogYm9vbGVhbixcbiAgICBkZW55UmVhc29uOiBzdHJpbmcsXG4gICAgcG9saWN5U291cmNlPzogc3RyaW5nLFxuICAgIHR0bE1zPzogbnVtYmVyLFxuICApOiBzdHJpbmcge1xuICAgIGNvbnN0IHJlcXVlc3RJZCA9IHRoaXMuc2Vzc2lvbi5jcmVhdGVDbGlBcHByb3ZhbFJlcXVlc3QoXG4gICAgICB0b29sTmFtZSwgdG9vbElucHV0LCByaXNrTGV2ZWwsIHJpc2tTY29yZSwgaXJyZXZlcnNpYmxlLCBkZW55UmVhc29uLCBwb2xpY3lTb3VyY2UsIHR0bE1zXG4gICAgKTtcblxuICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgIHR5cGU6ICdDTElfQVBQUk9WQUxfUkVRVUVTVEVEJyxcbiAgICAgIHNldmVyaXR5OiAnTUVESVVNJyxcbiAgICAgIHNvdXJjZTogJ0dhdGVrZWVwZXIuY3JlYXRlQ2xpQXBwcm92YWxSZXF1ZXN0JyxcbiAgICAgIGRldGFpbHM6IGBDTEkgYXBwcm92YWwgcmVxdWVzdGVkIGZvciAke3Rvb2xOYW1lfTogJHtkZW55UmVhc29ufWAsXG4gICAgICBhZGRpdGlvbmFsRGF0YTogeyByZXF1ZXN0SWQsIHRvb2xOYW1lLCByaXNrTGV2ZWwsIHJpc2tTY29yZSwgaXJyZXZlcnNpYmxlLCBzZXNzaW9uSWQ6IHRoaXMuc2Vzc2lvbi5zZXNzaW9uSWQgfSxcbiAgICB9KTtcblxuICAgIHJldHVybiByZXF1ZXN0SWQ7XG4gIH1cblxuICAvKipcbiAgICogQXBwcm92ZSBhIHBlbmRpbmcgQ0xJIGFwcHJvdmFsIHJlcXVlc3QuXG4gICAqL1xuICBhcHByb3ZlQ2xpUmVxdWVzdChyZXF1ZXN0SWQ6IHN0cmluZywgc2NvcGU6IENsaUFwcHJvdmFsU2NvcGUgPSAnc2luZ2xlJyk6IENsaUFwcHJvdmFsUmVjb3JkIHwgdW5kZWZpbmVkIHtcbiAgICBjb25zdCByZWNvcmQgPSB0aGlzLnNlc3Npb24uYXBwcm92ZUNsaVJlcXVlc3QocmVxdWVzdElkLCBzY29wZSk7XG5cbiAgICBpZiAocmVjb3JkKSB7XG4gICAgICBTZWN1cml0eU1vbml0b3IubG9nU2VjdXJpdHlFdmVudCh7XG4gICAgICAgIHR5cGU6ICdDTElfQVBQUk9WQUxfR1JBTlRFRCcsXG4gICAgICAgIHNldmVyaXR5OiAnTUVESVVNJyxcbiAgICAgICAgc291cmNlOiAnR2F0ZWtlZXBlci5hcHByb3ZlQ2xpUmVxdWVzdCcsXG4gICAgICAgIGRldGFpbHM6IGBDTEkgYXBwcm92YWwgZ3JhbnRlZCBmb3IgJHtyZWNvcmQudG9vbE5hbWV9IChzY29wZTogJHtzY29wZX0pYCxcbiAgICAgICAgYWRkaXRpb25hbERhdGE6IHsgcmVxdWVzdElkLCB0b29sTmFtZTogcmVjb3JkLnRvb2xOYW1lLCBzY29wZSwgc2Vzc2lvbklkOiB0aGlzLnNlc3Npb24uc2Vzc2lvbklkIH0sXG4gICAgICB9KTtcbiAgICB9XG5cbiAgICByZXR1cm4gcmVjb3JkO1xuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrIGlmIGEgQ0xJIHRvb2wgY2FsbCBoYXMgYSB2YWxpZCBhcHByb3ZhbC5cbiAgICovXG4gIGNoZWNrQ2xpQXBwcm92YWwodG9vbE5hbWU6IHN0cmluZywgdG9vbElucHV0OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPik6IENsaUFwcHJvdmFsUmVjb3JkIHwgdW5kZWZpbmVkIHtcbiAgICBjb25zdCByZWNvcmQgPSB0aGlzLnNlc3Npb24uY2hlY2tDbGlBcHByb3ZhbCh0b29sTmFtZSwgdG9vbElucHV0KTtcblxuICAgIGlmIChyZWNvcmQpIHtcbiAgICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgICAgdHlwZTogJ0NMSV9BUFBST1ZBTF9DT05TVU1FRCcsXG4gICAgICAgIHNldmVyaXR5OiAnTE9XJyxcbiAgICAgICAgc291cmNlOiAnR2F0ZWtlZXBlci5jaGVja0NsaUFwcHJvdmFsJyxcbiAgICAgICAgZGV0YWlsczogYENMSSBhcHByb3ZhbCBjb25zdW1lZCBmb3IgJHt0b29sTmFtZX0gKHNjb3BlOiAke3JlY29yZC5zY29wZX0pYCxcbiAgICAgICAgYWRkaXRpb25hbERhdGE6IHsgcmVxdWVzdElkOiByZWNvcmQucmVxdWVzdElkLCB0b29sTmFtZSwgc2NvcGU6IHJlY29yZC5zY29wZSwgc2Vzc2lvbklkOiB0aGlzLnNlc3Npb24uc2Vzc2lvbklkIH0sXG4gICAgICB9KTtcbiAgICB9XG5cbiAgICByZXR1cm4gcmVjb3JkO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBhbGwgcGVuZGluZyBDTEkgYXBwcm92YWwgcmVxdWVzdHMuXG4gICAqL1xuICBnZXRQZW5kaW5nQ2xpQXBwcm92YWxzKCk6IENsaUFwcHJvdmFsUmVjb3JkW10ge1xuICAgIHJldHVybiB0aGlzLnNlc3Npb24uZ2V0UGVuZGluZ0NsaUFwcHJvdmFscygpO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldHMgdGhlIHBlcm1pc3Npb24gZmxhZ3MgZm9yIGFuIGVuZHBvaW50LlxuICAgKlxuICAgKiBAcGFyYW0gZW5kcG9pbnQgLSBUaGUgQ1JVRCBlbmRwb2ludCB0byBnZXQgcGVybWlzc2lvbnMgZm9yXG4gICAqIEByZXR1cm5zIFRoZSBwZXJtaXNzaW9uI