@sureshgururajan/aws-console-private-access-validator 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,236 @@
1
+ export class ConsolePrivateAccessValidator {
2
+ template;
3
+ region;
4
+ checks = [];
5
+ constructor(template, region = 'us-east-1') {
6
+ this.template = template;
7
+ this.region = region;
8
+ }
9
+ validate() {
10
+ this.checks = [];
11
+ this.checkVpcEndpoints();
12
+ this.checkEndpointPolicies();
13
+ this.checkRoute53HostedZones();
14
+ this.checkSecurityGroups();
15
+ this.checkEc2Instance();
16
+ this.checkNatGateway();
17
+ this.checkNetworkConfiguration();
18
+ const failCount = this.checks.filter(c => c.status === 'fail').length;
19
+ const valid = failCount === 0;
20
+ return {
21
+ valid,
22
+ checks: this.checks,
23
+ summary: this.generateSummary(valid, failCount),
24
+ };
25
+ }
26
+ getServiceName(serviceName) {
27
+ if (typeof serviceName === 'string') {
28
+ return serviceName;
29
+ }
30
+ if (serviceName?.['Fn::Join']) {
31
+ const parts = serviceName['Fn::Join'][1];
32
+ if (Array.isArray(parts)) {
33
+ // Handle Ref to AWS::Region by replacing with actual region
34
+ return parts
35
+ .map((part) => {
36
+ if (typeof part === 'string') {
37
+ return part;
38
+ }
39
+ if (part?.Ref === 'AWS::Region') {
40
+ return this.region;
41
+ }
42
+ return '';
43
+ })
44
+ .join('');
45
+ }
46
+ }
47
+ return null;
48
+ }
49
+ checkVpcEndpoints() {
50
+ const requiredEndpoints = [
51
+ { name: 'console', service: `com.amazonaws.${this.region}.console` },
52
+ { name: 'signin', service: `com.amazonaws.${this.region}.signin` },
53
+ { name: 'ssm', service: `com.amazonaws.${this.region}.ssm` },
54
+ { name: 'ec2messages', service: `com.amazonaws.${this.region}.ec2messages` },
55
+ { name: 'ssmmessages', service: `com.amazonaws.${this.region}.ssmmessages` },
56
+ ];
57
+ const resources = this.template.Resources || {};
58
+ const interfaceEndpoints = Object.values(resources).filter((r) => r.Type === 'AWS::EC2::VPCEndpoint' && r.Properties?.VpcEndpointType === 'Interface');
59
+ for (const endpoint of requiredEndpoints) {
60
+ const found = interfaceEndpoints.some((e) => {
61
+ const serviceName = this.getServiceName(e.Properties?.ServiceName);
62
+ return serviceName && serviceName.includes(endpoint.service);
63
+ });
64
+ this.checks.push({
65
+ name: `VPC Endpoint: ${endpoint.name}`,
66
+ status: found ? 'pass' : 'fail',
67
+ message: found
68
+ ? `Interface VPC endpoint for ${endpoint.name} found`
69
+ : `Missing interface VPC endpoint for ${endpoint.name}`,
70
+ });
71
+ }
72
+ // Check for S3 Gateway endpoint
73
+ const s3Gateway = Object.values(resources).some((r) => {
74
+ const serviceName = this.getServiceName(r.Properties?.ServiceName);
75
+ return (r.Type === 'AWS::EC2::VPCEndpoint' &&
76
+ r.Properties?.VpcEndpointType === 'Gateway' &&
77
+ serviceName &&
78
+ serviceName.includes('s3'));
79
+ });
80
+ this.checks.push({
81
+ name: 'VPC Endpoint: S3 Gateway',
82
+ status: s3Gateway ? 'pass' : 'fail',
83
+ message: s3Gateway ? 'S3 Gateway VPC endpoint found' : 'Missing S3 Gateway VPC endpoint',
84
+ });
85
+ }
86
+ checkEndpointPolicies() {
87
+ const resources = this.template.Resources || {};
88
+ const consoleEndpoint = Object.entries(resources).find(([_, r]) => {
89
+ const serviceName = this.getServiceName(r.Properties?.ServiceName);
90
+ return (r.Type === 'AWS::EC2::VPCEndpoint' &&
91
+ serviceName &&
92
+ serviceName.includes('console'));
93
+ });
94
+ const signinEndpoint = Object.entries(resources).find(([_, r]) => {
95
+ const serviceName = this.getServiceName(r.Properties?.ServiceName);
96
+ return (r.Type === 'AWS::EC2::VPCEndpoint' &&
97
+ serviceName &&
98
+ serviceName.includes('signin'));
99
+ });
100
+ for (const [name, endpoint] of [
101
+ ['Console', consoleEndpoint],
102
+ ['Signin', signinEndpoint],
103
+ ]) {
104
+ if (!endpoint)
105
+ continue;
106
+ const [_, resource] = endpoint;
107
+ const hasPolicy = resource.Properties?.PolicyDocument;
108
+ this.checks.push({
109
+ name: `Endpoint Policy: ${name}`,
110
+ status: hasPolicy ? 'pass' : 'fail',
111
+ message: hasPolicy
112
+ ? `${name} endpoint has a policy attached`
113
+ : `${name} endpoint is missing a policy`,
114
+ details: hasPolicy
115
+ ? this.validatePolicyContent(resource.Properties.PolicyDocument)
116
+ : undefined,
117
+ });
118
+ }
119
+ }
120
+ validatePolicyContent(policy) {
121
+ if (!policy.Statement || policy.Statement.length === 0) {
122
+ return 'Policy has no statements';
123
+ }
124
+ const statement = policy.Statement[0];
125
+ const hasAccountCondition = statement.Condition?.StringEquals?.['aws:PrincipalAccount'];
126
+ if (hasAccountCondition) {
127
+ return 'Policy restricts access to specific account(s)';
128
+ }
129
+ return 'Policy does not restrict access by account';
130
+ }
131
+ checkRoute53HostedZones() {
132
+ const resources = this.template.Resources || {};
133
+ const hostedZones = Object.values(resources).filter((r) => r.Type === 'AWS::Route53::HostedZone');
134
+ const requiredZones = ['console.aws.amazon.com', 'signin.aws.amazon.com'];
135
+ for (const zone of requiredZones) {
136
+ const found = hostedZones.some((hz) => {
137
+ const zoneName = hz.Properties?.Name;
138
+ // Route53 zone names may have a trailing dot
139
+ return zoneName === zone || zoneName === `${zone}.`;
140
+ });
141
+ this.checks.push({
142
+ name: `Route53 Hosted Zone: ${zone}`,
143
+ status: found ? 'pass' : 'fail',
144
+ message: found
145
+ ? `Private hosted zone for ${zone} found`
146
+ : `Missing private hosted zone for ${zone}`,
147
+ });
148
+ }
149
+ // Check for Route53 records
150
+ const recordSets = Object.values(resources).filter((r) => r.Type === 'AWS::Route53::RecordSet');
151
+ this.checks.push({
152
+ name: 'Route53 Records',
153
+ status: recordSets.length > 0 ? 'pass' : 'warning',
154
+ message: recordSets.length > 0
155
+ ? `Found ${recordSets.length} Route53 records`
156
+ : 'No Route53 records found',
157
+ });
158
+ }
159
+ checkSecurityGroups() {
160
+ const resources = this.template.Resources || {};
161
+ const securityGroups = Object.values(resources).filter((r) => r.Type === 'AWS::EC2::SecurityGroup');
162
+ const hasHttpsIngress = securityGroups.some((sg) => {
163
+ const ingress = sg.Properties?.SecurityGroupIngress || [];
164
+ return ingress.some((rule) => (rule.FromPort === 443 || rule.IpProtocol === 'tcp') &&
165
+ (rule.ToPort === 443 || rule.IpProtocol === 'tcp'));
166
+ });
167
+ this.checks.push({
168
+ name: 'Security Group: HTTPS Access',
169
+ status: hasHttpsIngress ? 'pass' : 'warning',
170
+ message: hasHttpsIngress
171
+ ? 'Security group allows HTTPS (port 443) traffic'
172
+ : 'No security group rule found for HTTPS (port 443)',
173
+ });
174
+ }
175
+ checkEc2Instance() {
176
+ const resources = this.template.Resources || {};
177
+ const instance = Object.values(resources).find((r) => r.Type === 'AWS::EC2::Instance');
178
+ this.checks.push({
179
+ name: 'EC2 Instance',
180
+ status: instance ? 'pass' : 'warning',
181
+ message: instance ? 'EC2 instance found' : 'No EC2 instance found (optional)',
182
+ });
183
+ if (instance) {
184
+ const hasIamRole = instance.Properties?.IamInstanceProfile;
185
+ this.checks.push({
186
+ name: 'EC2 IAM Role',
187
+ status: hasIamRole ? 'pass' : 'warning',
188
+ message: hasIamRole
189
+ ? 'EC2 instance has IAM instance profile'
190
+ : 'EC2 instance missing IAM instance profile',
191
+ });
192
+ }
193
+ }
194
+ checkNatGateway() {
195
+ const resources = this.template.Resources || {};
196
+ const natGateway = Object.values(resources).find((r) => r.Type === 'AWS::EC2::NatGateway');
197
+ this.checks.push({
198
+ name: 'NAT Gateway',
199
+ status: natGateway ? 'pass' : 'warning',
200
+ message: natGateway
201
+ ? 'NAT Gateway found for private subnet egress'
202
+ : 'No NAT Gateway found (required for private subnet internet access)',
203
+ });
204
+ }
205
+ checkNetworkConfiguration() {
206
+ const resources = this.template.Resources || {};
207
+ // Check for private subnets
208
+ const privateSubnets = Object.values(resources).filter((r) => r.Type === 'AWS::EC2::Subnet' &&
209
+ !r.Properties?.MapPublicIpOnLaunch);
210
+ this.checks.push({
211
+ name: 'Private Subnets',
212
+ status: privateSubnets.length > 0 ? 'pass' : 'fail',
213
+ message: privateSubnets.length > 0
214
+ ? `Found ${privateSubnets.length} private subnet(s)`
215
+ : 'No private subnets found',
216
+ });
217
+ // Check for route tables
218
+ const routeTables = Object.values(resources).filter((r) => r.Type === 'AWS::EC2::RouteTable');
219
+ this.checks.push({
220
+ name: 'Route Tables',
221
+ status: routeTables.length > 0 ? 'pass' : 'warning',
222
+ message: routeTables.length > 0
223
+ ? `Found ${routeTables.length} route table(s)`
224
+ : 'No route tables found',
225
+ });
226
+ }
227
+ generateSummary(valid, failCount) {
228
+ const passCount = this.checks.filter(c => c.status === 'pass').length;
229
+ const warningCount = this.checks.filter(c => c.status === 'warning').length;
230
+ if (valid) {
231
+ return `✓ Validation passed. All required checks passed (${passCount} passed, ${warningCount} warnings).`;
232
+ }
233
+ return `✗ Validation failed. ${failCount} check(s) failed, ${passCount} passed, ${warningCount} warnings.`;
234
+ }
235
+ }
236
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmFsaWRhdG9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsidmFsaWRhdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE1BQU0sT0FBTyw2QkFBNkI7SUFDaEMsUUFBUSxDQUF5QjtJQUNqQyxNQUFNLENBQVM7SUFDZixNQUFNLEdBQXNCLEVBQUUsQ0FBQztJQUV2QyxZQUFZLFFBQWdDLEVBQUUsU0FBaUIsV0FBVztRQUN4RSxJQUFJLENBQUMsUUFBUSxHQUFHLFFBQVEsQ0FBQztRQUN6QixJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQztJQUN2QixDQUFDO0lBRUQsUUFBUTtRQUNOLElBQUksQ0FBQyxNQUFNLEdBQUcsRUFBRSxDQUFDO1FBRWpCLElBQUksQ0FBQyxpQkFBaUIsRUFBRSxDQUFDO1FBQ3pCLElBQUksQ0FBQyxxQkFBcUIsRUFBRSxDQUFDO1FBQzdCLElBQUksQ0FBQyx1QkFBdUIsRUFBRSxDQUFDO1FBQy9CLElBQUksQ0FBQyxtQkFBbUIsRUFBRSxDQUFDO1FBQzNCLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO1FBQ3hCLElBQUksQ0FBQyxlQUFlLEVBQUUsQ0FBQztRQUN2QixJQUFJLENBQUMseUJBQXlCLEVBQUUsQ0FBQztRQUVqQyxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxNQUFNLEtBQUssTUFBTSxDQUFDLENBQUMsTUFBTSxDQUFDO1FBQ3RFLE1BQU0sS0FBSyxHQUFHLFNBQVMsS0FBSyxDQUFDLENBQUM7UUFFOUIsT0FBTztZQUNMLEtBQUs7WUFDTCxNQUFNLEVBQUUsSUFBSSxDQUFDLE1BQU07WUFDbkIsT0FBTyxFQUFFLElBQUksQ0FBQyxlQUFlLENBQUMsS0FBSyxFQUFFLFNBQVMsQ0FBQztTQUNoRCxDQUFDO0lBQ0osQ0FBQztJQUVPLGNBQWMsQ0FBQyxXQUFnQjtRQUNyQyxJQUFJLE9BQU8sV0FBVyxL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QVksR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxNQUFNLEtBQUssU0FBUyxDQUFDLENBQUMsTUFBTSxDQUFDO1FBRTVFLElBQUksS0FBSyxFQUFFLENBQUM7WUFDVixPQUFPLG9EQUFvRCxTQUFTLFlBQVksWUFBWSxhQUFhLENBQUM7UUFDNUcsQ0FBQztRQUVELE9BQU8sd0JBQXdCLFNBQVMscUJBQXFCLFNBQVMsWUFBWSxZQUFZLFlBQVksQ0FBQztJQUM3RyxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBWYWxpZGF0aW9uQ2hlY2ssIFZhbGlkYXRpb25SZXN1bHQsIENsb3VkRm9ybWF0aW9uVGVtcGxhdGUgfSBmcm9tICcuL3R5cGVzJztcblxuZXhwb3J0IGNsYXNzIENvbnNvbGVQcml2YXRlQWNjZXNzVmFsaWRhdG9yIHtcbiAgcHJpdmF0ZSB0ZW1wbGF0ZTogQ2xvdWRGb3JtYXRpb25UZW1wbGF0ZTtcbiAgcHJpdmF0ZSByZWdpb246IHN0cmluZztcbiAgcHJpdmF0ZSBjaGVja3M6IFZhbGlkYXRpb25DaGVja1tdID0gW107XG5cbiAgY29uc3RydWN0b3IodGVtcGxhdGU6IENsb3VkRm9ybWF0aW9uVGVtcGxhdGUsIHJlZ2lvbjogc3RyaW5nID0gJ3VzLWVhc3QtMScpIHtcbiAgICB0aGlzLnRlbXBsYXRlID0gdGVtcGxhdGU7XG4gICAgdGhpcy5yZWdpb24gPSByZWdpb247XG4gIH1cblxuICB2YWxpZGF0ZSgpOiBWYWxpZGF0aW9uUmVzdWx0IHtcbiAgICB0aGlzLmNoZWNrcyA9IFtdO1xuXG4gICAgdGhpcy5jaGVja1ZwY0VuZHBvaW50cygpO1xuICAgIHRoaXMuY2hlY2tFbmRwb2ludFBvbGljaWVzKCk7XG4gICAgdGhpcy5jaGVja1JvdXRlNTNIb3N0ZWRab25lcygpO1xuICAgIHRoaXMuY2hlY2tTZWN1cml0eUdyb3VwcygpO1xuICAgIHRoaXMuY2hlY2tFYzJJbnN0YW5jZSgpO1xuICAgIHRoaXMuY2hlY2tOYXRHYXRld2F5KCk7XG4gICAgdGhpcy5jaGVja05ldHdvcmtDb25maWd1cmF0aW9uKCk7XG5cbiAgICBjb25zdCBmYWlsQ291bnQgPSB0aGlzLmNoZWNrcy5maWx0ZXIoYyA9PiBjLnN0YXR1cyA9PT0gJ2ZhaWwnKS5sZW5ndGg7XG4gICAgY29uc3QgdmFsaWQgPSBmYWlsQ291bnQgPT09IDA7XG5cbiAgICByZXR1cm4ge1xuICAgICAgdmFsaWQsXG4gICAgICBjaGVja3M6IHRoaXMuY2hlY2tzLFxuICAgICAgc3VtbWFyeTogdGhpcy5nZW5lcmF0ZVN1bW1hcnkodmFsaWQsIGZhaWxDb3VudCksXG4gICAgfTtcbiAgfVxuXG4gIHByaXZhdGUgZ2V0U2VydmljZU5hbWUoc2VydmljZU5hbWU6IGFueSk6IHN0cmluZyB8IG51bGwge1xuICAgIGlmICh0eXBlb2Ygc2VydmljZU5hbWUgPT09ICdzdHJpbmcnKSB7XG4gICAgICByZXR1cm4gc2VydmljZU5hbWU7XG4gICAgfVxuICAgIGlmIChzZXJ2aWNlTmFtZT8uWydGbjo6Sm9pbiddKSB7XG4gICAgICBjb25zdCBwYXJ0cyA9IHNlcnZpY2VOYW1lWydGbjo6Sm9pbiddWzFdO1xuICAgICAgaWYgKEFycmF5LmlzQXJyYXkocGFydHMpKSB7XG4gICAgICAgIC8vIEhhbmRsZSBSZWYgdG8gQVdTOjpSZWdpb24gYnkgcmVwbGFjaW5nIHdpdGggYWN0dWFsIHJlZ2lvblxuICAgICAgICByZXR1cm4gcGFydHNcbiAgICAgICAgICAubWFwKChwYXJ0OiBhbnkpID0+IHtcbiAgICAgICAgICAgIGlmICh0eXBlb2YgcGFydCA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAgICAgcmV0dXJuIHBhcnQ7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAocGFydD8uUmVmID09PSAnQVdTOjpSZWdpb24nKSB7XG4gICAgICAgICAgICAgIHJldHVybiB0aGlzLnJlZ2lvbjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiAnJztcbiAgICAgICAgICB9KVxuICAgICAgICAgIC5qb2luKCcnKTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICBwcml2YXRlIGNoZWNrVnBjRW5kcG9pbnRzKCk6IHZvaWQge1xuICAgIGNvbnN0IHJlcXVpcmVkRW5kcG9pbnRzID0gW1xuICAgICAgeyBuYW1lOiAnY29uc29sZScsIHNlcnZpY2U6IGBjb20uYW1hem9uYXdzLiR7dGhpcy5yZWdpb259LmNvbnNvbGVgIH0sXG4gICAgICB7IG5hbWU6ICdzaWduaW4nLCBzZXJ2aWNlOiBgY29tLmFtYXpvbmF3cy4ke3RoaXMucmVnaW9ufS5zaWduaW5gIH0sXG4gICAgICB7IG5hbWU6ICdzc20nLCBzZXJ2aWNlOiBgY29tLmFtYXpvbmF3cy4ke3RoaXMucmVnaW9ufS5zc21gIH0sXG4gICAgICB7IG5hbWU6ICdlYzJtZXNzYWdlcycsIHNlcnZpY2U6IGBjb20uYW1hem9uYXdzLiR7dGhpcy5yZWdpb259LmVjMm1lc3NhZ2VzYCB9LFxuICAgICAgeyBuYW1lOiAnc3NtbWVzc2FnZXMnLCBzZXJ2aWNlOiBgY29tLmFtYXpvbmF3cy4ke3RoaXMucmVnaW9ufS5zc21tZXNzYWdlc2AgfSxcbiAgICBdO1xuXG4gICAgY29uc3QgcmVzb3VyY2VzID0gdGhpcy50ZW1wbGF0ZS5SZXNvdXJjZXMgfHwge307XG4gICAgY29uc3QgaW50ZXJmYWNlRW5kcG9pbnRzID0gT2JqZWN0LnZhbHVlcyhyZXNvdXJjZXMpLmZpbHRlcihcbiAgICAgIChyOiBhbnkpID0+IHIuVHlwZSA9PT0gJ0FXUzo6RUMyOjpWUENFbmRwb2ludCcgJiYgci5Qcm9wZXJ0aWVzPy5WcGNFbmRwb2ludFR5cGUgPT09ICdJbnRlcmZhY2UnXG4gICAgKTtcblxuICAgIGZvciAoY29uc3QgZW5kcG9pbnQgb2YgcmVxdWlyZWRFbmRwb2ludHMpIHtcbiAgICAgIGNvbnN0IGZvdW5kID0gaW50ZXJmYWNlRW5kcG9pbnRzLnNvbWUoKGU6IGFueSkgPT4ge1xuICAgICAgICBjb25zdCBzZXJ2aWNlTmFtZSA9IHRoaXMuZ2V0U2VydmljZU5hbWUoZS5Qcm9wZXJ0aWVzPy5TZXJ2aWNlTmFtZSk7XG4gICAgICAgIHJldHVybiBzZXJ2aWNlTmFtZSAmJiBzZXJ2aWNlTmFtZS5pbmNsdWRlcyhlbmRwb2ludC5zZXJ2aWNlKTtcbiAgICAgIH0pO1xuXG4gICAgICB0aGlzLmNoZWNrcy5wdXNoKHtcbiAgICAgICAgbmFtZTogYFZQQyBFbmRwb2ludDogJHtlbmRwb2ludC5uYW1lfWAsXG4gICAgICAgIHN0YXR1czogZm91bmQgPyAncGFzcycgOiAnZmFpbCcsXG4gICAgICAgIG1lc3NhZ2U6IGZvdW5kXG4gICAgICAgICAgPyBgSW50ZXJmYWNlIFZQQyBlbmRwb2ludCBmb3IgJHtlbmRwb2ludC5uYW1lfSBmb3VuZGBcbiAgICAgICAgICA6IGBNaXNzaW5nIGludGVyZmFjZSBWUEMgZW5kcG9pbnQgZm9yICR7ZW5kcG9pbnQubmFtZX1gLFxuICAgICAgfSk7XG4gICAgfVxuXG4gICAgLy8gQ2hlY2sgZm9yIFMzIEdhdGV3YXkgZW5kcG9pbnRcbiAgICBjb25zdCBzM0dhdGV3YXkgPSBPYmplY3QudmFsdWVzKHJlc291cmNlcykuc29tZShcbiAgICAgIChyOiBhbnkpID0+IHtcbiAgICAgICAgY29uc3Qgc2VydmljZU5hbWUgPSB0aGlzLmdldFNlcnZpY2VOYW1lKHIuUHJvcGVydGllcz8uU2VydmljZU5hbWUpO1xuICAgICAgICByZXR1cm4gKFxuICAgICAgICAgIHIuVHlwZSA9PT0gJ0FXUzo6RUMyOjpWUENFbmRwb2ludCcgJiZcbiAgICAgICAgICByLlByb3BlcnRpZXM/LlZwY0VuZHBvaW50VHlwZSA9PT0gJ0dhdGV3YXknICYmXG4gICAgICAgICAgc2VydmljZU5hbWUgJiZcbiAgICAgICAgICBzZXJ2aWNlTmFtZS5pbmNsdWRlcygnczMnKVxuICAgICAgICApO1xuICAgICAgfVxuICAgICk7XG5cbiAgICB0aGlzLmNoZWNrcy5wdXNoKHtcbiAgICAgIG5hbWU6ICdWUEMgRW5kcG9pbnQ6IFMzIEdhdGV3YXknLFxuICAgICAgc3RhdHVzOiBzM0dhdGV3YXkgPyAncGFzcycgOiAnZmFpbCcsXG4gICAgICBtZXNzYWdlOiBzM0dhdGV3YXkgPyAnUzMgR2F0ZXdheSBWUEMgZW5kcG9pbnQgZm91bmQnIDogJ01pc3NpbmcgUzMgR2F0ZXdheSBWUEMgZW5kcG9pbnQnLFxuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBjaGVja0VuZHBvaW50UG9saWNpZXMoKTogdm9pZCB7XG4gICAgY29uc3QgcmVzb3VyY2VzID0gdGhpcy50ZW1wbGF0ZS5SZXNvdXJjZXMgfHwge307XG4gICAgY29uc3QgY29uc29sZUVuZHBvaW50ID0gT2JqZWN0LmVudHJpZXMocmVzb3VyY2VzKS5maW5kKFxuICAgICAgKFtfLCByXTogW3N0cmluZywgYW55XSkgPT4ge1xuICAgICAgICBjb25zdCBzZXJ2aWNlTmFtZSA9IHRoaXMuZ2V0U2VydmljZU5hbWUoci5Qcm9wZXJ0aWVzPy5TZXJ2aWNlTmFtZSk7XG4gICAgICAgIHJldHVybiAoXG4gICAgICAgICAgci5UeXBlID09PSAnQVdTOjpFQzI6OlZQQ0VuZHBvaW50JyAmJlxuICAgICAgICAgIHNlcnZpY2VOYW1lICYmXG4gICAgICAgICAgc2VydmljZU5hbWUuaW5jbHVkZXMoJ2NvbnNvbGUnKVxuICAgICAgICApO1xuICAgICAgfVxuICAgICk7XG5cbiAgICBjb25zdCBzaWduaW5FbmRwb2ludCA9IE9iamVjdC5lbnRyaWVzKHJlc291cmNlcykuZmluZChcbiAgICAgIChbXywgcl06IFtzdHJpbmcsIGFueV0pID0+IHtcbiAgICAgICAgY29uc3Qgc2VydmljZU5hbWUgPSB0aGlzLmdldFNlcnZpY2VOYW1lKHIuUHJvcGVydGllcz8uU2VydmljZU5hbWUpO1xuICAgICAgICByZXR1cm4gKFxuICAgICAgICAgIHIuVHlwZSA9PT0gJ0FXUzo6RUMyOjpWUENFbmRwb2ludCcgJiZcbiAgICAgICAgICBzZXJ2aWNlTmFtZSAmJlxuICAgICAgICAgIHNlcnZpY2VOYW1lLmluY2x1ZGVzKCdzaWduaW4nKVxuICAgICAgICApO1xuICAgICAgfVxuICAgICk7XG5cbiAgICBmb3IgKGNvbnN0IFtuYW1lLCBlbmRwb2ludF0gb2YgW1xuICAgICAgWydDb25zb2xlJywgY29uc29sZUVuZHBvaW50XSxcbiAgICAgIFsnU2lnbmluJywgc2lnbmluRW5kcG9pbnRdLFxuICAgIF0pIHtcbiAgICAgIGlmICghZW5kcG9pbnQpIGNvbnRpbnVlO1xuXG4gICAgICBjb25zdCBbXywgcmVzb3VyY2VdID0gZW5kcG9pbnQgYXMgW3N0cmluZywgYW55XTtcbiAgICAgIGNvbnN0IGhhc1BvbGljeSA9IHJlc291cmNlLlByb3BlcnRpZXM/LlBvbGljeURvY3VtZW50O1xuXG4gICAgICB0aGlzLmNoZWNrcy5wdXNoKHtcbiAgICAgICAgbmFtZTogYEVuZHBvaW50IFBvbGljeTogJHtuYW1lfWAsXG4gICAgICAgIHN0YXR1czogaGFzUG9saWN5ID8gJ3Bhc3MnIDogJ2ZhaWwnLFxuICAgICAgICBtZXNzYWdlOiBoYXNQb2xpY3lcbiAgICAgICAgICA/IGAke25hbWV9IGVuZHBvaW50IGhhcyBhIHBvbGljeSBhdHRhY2hlZGBcbiAgICAgICAgICA6IGAke25hbWV9IGVuZHBvaW50IGlzIG1pc3NpbmcgYSBwb2xpY3lgLFxuICAgICAgICBkZXRhaWxzOiBoYXNQb2xpY3lcbiAgICAgICAgICA/IHRoaXMudmFsaWRhdGVQb2xpY3lDb250ZW50KHJlc291cmNlLlByb3BlcnRpZXMuUG9saWN5RG9jdW1lbnQpXG4gICAgICAgICAgOiB1bmRlZmluZWQsXG4gICAgICB9KTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIHZhbGlkYXRlUG9saWN5Q29udGVudChwb2xpY3k6IGFueSk6IHN0cmluZyB7XG4gICAgaWYgKCFwb2xpY3kuU3RhdGVtZW50IHx8IHBvbGljeS5TdGF0ZW1lbnQubGVuZ3RoID09PSAwKSB7XG4gICAgICByZXR1cm4gJ1BvbGljeSBoYXMgbm8gc3RhdGVtZW50cyc7XG4gICAgfVxuXG4gICAgY29uc3Qgc3RhdGVtZW50ID0gcG9saWN5LlN0YXRlbWVudFswXTtcbiAgICBjb25zdCBoYXNBY2NvdW50Q29uZGl0aW9uID0gc3RhdGVtZW50LkNvbmRpdGlvbj8uU3RyaW5nRXF1YWxzPy5bJ2F3czpQcmluY2lwYWxBY2NvdW50J107XG5cbiAgICBpZiAoaGFzQWNjb3VudENvbmRpdGlvbikge1xuICAgICAgcmV0dXJuICdQb2xpY3kgcmVzdHJpY3RzIGFjY2VzcyB0byBzcGVjaWZpYyBhY2NvdW50KHMpJztcbiAgICB9XG5cbiAgICByZXR1cm4gJ1BvbGljeSBkb2VzIG5vdCByZXN0cmljdCBhY2Nlc3MgYnkgYWNjb3VudCc7XG4gIH1cblxuICBwcml2YXRlIGNoZWNrUm91dGU1M0hvc3RlZFpvbmVzKCk6IHZvaWQge1xuICAgIGNvbnN0IHJlc291cmNlcyA9IHRoaXMudGVtcGxhdGUuUmVzb3VyY2VzIHx8IHt9O1xuICAgIGNvbnN0IGhvc3RlZFpvbmVzID0gT2JqZWN0LnZhbHVlcyhyZXNvdXJjZXMpLmZpbHRlcihcbiAgICAgIChyOiBhbnkpID0+IHIuVHlwZSA9PT0gJ0FXUzo6Um91dGU1Mzo6SG9zdGVkWm9uZSdcbiAgICApO1xuXG4gICAgY29uc3QgcmVxdWlyZWRab25lcyA9IFsnY29uc29sZS5hd3MuYW1hem9uLmNvbScsICdzaWduaW4uYXdzLmFtYXpvbi5jb20nXTtcblxuICAgIGZvciAoY29uc3Qgem9uZSBvZiByZXF1aXJlZFpvbmVzKSB7XG4gICAgICBjb25zdCBmb3VuZCA9IGhvc3RlZFpvbmVzLnNvbWUoKGh6OiBhbnkpID0+IHtcbiAgICAgICAgY29uc3Qgem9uZU5hbWUgPSBoei5Qcm9wZXJ0aWVzPy5OYW1lO1xuICAgICAgICAvLyBSb3V0ZTUzIHpvbmUgbmFtZXMgbWF5IGhhdmUgYSB0cmFpbGluZyBkb3RcbiAgICAgICAgcmV0dXJuIHpvbmVOYW1lID09PSB6b25lIHx8IHpvbmVOYW1lID09PSBgJHt6b25lfS5gO1xuICAgICAgfSk7XG5cbiAgICAgIHRoaXMuY2hlY2tzLnB1c2goe1xuICAgICAgICBuYW1lOiBgUm91dGU1MyBIb3N0ZWQgWm9uZTogJHt6b25lfWAsXG4gICAgICAgIHN0YXR1czogZm91bmQgPyAncGFzcycgOiAnZmFpbCcsXG4gICAgICAgIG1lc3NhZ2U6IGZvdW5kXG4gICAgICAgICAgPyBgUHJpdmF0ZSBob3N0ZWQgem9uZSBmb3IgJHt6b25lfSBmb3VuZGBcbiAgICAgICAgICA6IGBNaXNzaW5nIHByaXZhdGUgaG9zdGVkIHpvbmUgZm9yICR7em9uZX1gLFxuICAgICAgfSk7XG4gICAgfVxuXG4gICAgLy8gQ2hlY2sgZm9yIFJvdXRlNTMgcmVjb3Jkc1xuICAgIGNvbnN0IHJlY29yZFNldHMgPSBPYmplY3QudmFsdWVzKHJlc291cmNlcykuZmlsdGVyKFxuICAgICAgKHI6IGFueSkgPT4gci5UeXBlID09PSAnQVdTOjpSb3V0ZTUzOjpSZWNvcmRTZXQnXG4gICAgKTtcblxuICAgIHRoaXMuY2hlY2tzLnB1c2goe1xuICAgICAgbmFtZTogJ1JvdXRlNTMgUmVjb3JkcycsXG4gICAgICBzdGF0dXM6IHJlY29yZFNldHMubGVuZ3RoID4gMCA/ICdwYXNzJyA6ICd3YXJuaW5nJyxcbiAgICAgIG1lc3NhZ2U6XG4gICAgICAgIHJlY29yZFNldHMubGVuZ3RoID4gMFxuICAgICAgICAgID8gYEZvdW5kICR7cmVjb3JkU2V0cy5sZW5ndGh9IFJvdXRlNTMgcmVjb3Jkc2BcbiAgICAgICAgICA6ICdObyBSb3V0ZTUzIHJlY29yZHMgZm91bmQnLFxuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBjaGVja1NlY3VyaXR5R3JvdXBzKCk6IHZvaWQge1xuICAgIGNvbnN0IHJlc291cmNlcyA9IHRoaXMudGVtcGxhdGUuUmVzb3VyY2VzIHx8IHt9O1xuICAgIGNvbnN0IHNlY3VyaXR5R3JvdXBzID0gT2JqZWN0LnZhbHVlcyhyZXNvdXJjZXMpLmZpbHRlcihcbiAgICAgIChyOiBhbnkpID0+IHIuVHlwZSA9PT0gJ0FXUzo6RUMyOjpTZWN1cml0eUdyb3VwJ1xuICAgICk7XG5cbiAgICBjb25zdCBoYXNIdHRwc0luZ3Jlc3MgPSBzZWN1cml0eUdyb3Vwcy5zb21lKChzZzogYW55KSA9PiB7XG4gICAgICBjb25zdCBpbmdyZXNzID0gc2cuUHJvcGVydGllcz8uU2VjdXJpdHlHcm91cEluZ3Jlc3MgfHwgW107XG4gICAgICByZXR1cm4gaW5ncmVzcy5zb21lKFxuICAgICAgICAocnVsZTogYW55KSA9PlxuICAgICAgICAgIChydWxlLkZyb21Qb3J0ID09PSA0NDMgfHwgcnVsZS5JcFByb3RvY29sID09PSAndGNwJykgJiZcbiAgICAgICAgICAocnVsZS5Ub1BvcnQgPT09IDQ0MyB8fCBydWxlLklwUHJvdG9jb2wgPT09ICd0Y3AnKVxuICAgICAgKTtcbiAgICB9KTtcblxuICAgIHRoaXMuY2hlY2tzLnB1c2goe1xuICAgICAgbmFtZTogJ1NlY3VyaXR5IEdyb3VwOiBIVFRQUyBBY2Nlc3MnLFxuICAgICAgc3RhdHVzOiBoYXNIdHRwc0luZ3Jlc3MgPyAncGFzcycgOiAnd2FybmluZycsXG4gICAgICBtZXNzYWdlOiBoYXNIdHRwc0luZ3Jlc3NcbiAgICAgICAgPyAnU2VjdXJpdHkgZ3JvdXAgYWxsb3dzIEhUVFBTIChwb3J0IDQ0MykgdHJhZmZpYydcbiAgICAgICAgOiAnTm8gc2VjdXJpdHkgZ3JvdXAgcnVsZSBmb3VuZCBmb3IgSFRUUFMgKHBvcnQgNDQzKScsXG4gICAgfSk7XG4gIH1cblxuICBwcml2YXRlIGNoZWNrRWMySW5zdGFuY2UoKTogdm9pZCB7XG4gICAgY29uc3QgcmVzb3VyY2VzID0gdGhpcy50ZW1wbGF0ZS5SZXNvdXJjZXMgfHwge307XG4gICAgY29uc3QgaW5zdGFuY2UgPSBPYmplY3QudmFsdWVzKHJlc291cmNlcykuZmluZCgocjogYW55KSA9PiByLlR5cGUgPT09ICdBV1M6OkVDMjo6SW5zdGFuY2UnKTtcblxuICAgIHRoaXMuY2hlY2tzLnB1c2goe1xuICAgICAgbmFtZTogJ0VDMiBJbnN0YW5jZScsXG4gICAgICBzdGF0dXM6IGluc3RhbmNlID8gJ3Bhc3MnIDogJ3dhcm5pbmcnLFxuICAgICAgbWVzc2FnZTogaW5zdGFuY2UgPyAnRUMyIGluc3RhbmNlIGZvdW5kJyA6ICdObyBFQzIgaW5zdGFuY2UgZm91bmQgKG9wdGlvbmFsKScsXG4gICAgfSk7XG5cbiAgICBpZiAoaW5zdGFuY2UpIHtcbiAgICAgIGNvbnN0IGhhc0lhbVJvbGUgPSAoaW5zdGFuY2UgYXMgYW55KS5Qcm9wZXJ0aWVzPy5JYW1JbnN0YW5jZVByb2ZpbGU7XG4gICAgICB0aGlzLmNoZWNrcy5wdXNoKHtcbiAgICAgICAgbmFtZTogJ0VDMiBJQU0gUm9sZScsXG4gICAgICAgIHN0YXR1czogaGFzSWFtUm9sZSA/ICdwYXNzJyA6ICd3YXJuaW5nJyxcbiAgICAgICAgbWVzc2FnZTogaGFzSWFtUm9sZVxuICAgICAgICAgID8gJ0VDMiBpbnN0YW5jZSBoYXMgSUFNIGluc3RhbmNlIHByb2ZpbGUnXG4gICAgICAgICAgOiAnRUMyIGluc3RhbmNlIG1pc3NpbmcgSUFNIGluc3RhbmNlIHByb2ZpbGUnLFxuICAgICAgfSk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBjaGVja05hdEdhdGV3YXkoKTogdm9pZCB7XG4gICAgY29uc3QgcmVzb3VyY2VzID0gdGhpcy50ZW1wbGF0ZS5SZXNvdXJjZXMgfHwge307XG4gICAgY29uc3QgbmF0R2F0ZXdheSA9IE9iamVjdC52YWx1ZXMocmVzb3VyY2VzKS5maW5kKChyOiBhbnkpID0+IHIuVHlwZSA9PT0gJ0FXUzo6RUMyOjpOYXRHYXRld2F5Jyk7XG5cbiAgICB0aGlzLmNoZWNrcy5wdXNoKHtcbiAgICAgIG5hbWU6ICdOQVQgR2F0ZXdheScsXG4gICAgICBzdGF0dXM6IG5hdEdhdGV3YXkgPyAncGFzcycgOiAnd2FybmluZycsXG4gICAgICBtZXNzYWdlOiBuYXRHYXRld2F5XG4gICAgICAgID8gJ05BVCBHYXRld2F5IGZvdW5kIGZvciBwcml2YXRlIHN1Ym5ldCBlZ3Jlc3MnXG4gICAgICAgIDogJ05vIE5BVCBHYXRld2F5IGZvdW5kIChyZXF1aXJlZCBmb3IgcHJpdmF0ZSBzdWJuZXQgaW50ZXJuZXQgYWNjZXNzKScsXG4gICAgfSk7XG4gIH1cblxuICBwcml2YXRlIGNoZWNrTmV0d29ya0NvbmZpZ3VyYXRpb24oKTogdm9pZCB7XG4gICAgY29uc3QgcmVzb3VyY2VzID0gdGhpcy50ZW1wbGF0ZS5SZXNvdXJjZXMgfHwge307XG5cbiAgICAvLyBDaGVjayBmb3IgcHJpdmF0ZSBzdWJuZXRzXG4gICAgY29uc3QgcHJpdmF0ZVN1Ym5ldHMgPSBPYmplY3QudmFsdWVzKHJlc291cmNlcykuZmlsdGVyKFxuICAgICAgKHI6IGFueSkgPT5cbiAgICAgICAgci5UeXBlID09PSAnQVdTOjpFQzI6OlN1Ym5ldCcgJiZcbiAgICAgICAgIXIuUHJvcGVydGllcz8uTWFwUHVibGljSXBPbkxhdW5jaFxuICAgICk7XG5cbiAgICB0aGlzLmNoZWNrcy5wdXNoKHtcbiAgICAgIG5hbWU6ICdQcml2YXRlIFN1Ym5ldHMnLFxuICAgICAgc3RhdHVzOiBwcml2YXRlU3VibmV0cy5sZW5ndGggPiAwID8gJ3Bhc3MnIDogJ2ZhaWwnLFxuICAgICAgbWVzc2FnZTpcbiAgICAgICAgcHJpdmF0ZVN1Ym5ldHMubGVuZ3RoID4gMFxuICAgICAgICAgID8gYEZvdW5kICR7cHJpdmF0ZVN1Ym5ldHMubGVuZ3RofSBwcml2YXRlIHN1Ym5ldChzKWBcbiAgICAgICAgICA6ICdObyBwcml2YXRlIHN1Ym5ldHMgZm91bmQnLFxuICAgIH0pO1xuXG4gICAgLy8gQ2hlY2sgZm9yIHJvdXRlIHRhYmxlc1xuICAgIGNvbnN0IHJvdXRlVGFibGVzID0gT2JqZWN0LnZhbHVlcyhyZXNvdXJjZXMpLmZpbHRlcihcbiAgICAgIChyOiBhbnkpID0+IHIuVHlwZSA9PT0gJ0FXUzo6RUMyOjpSb3V0ZVRhYmxlJ1xuICAgICk7XG5cbiAgICB0aGlzLmNoZWNrcy5wdXNoKHtcbiAgICAgIG5hbWU6ICdSb3V0ZSBUYWJsZXMnLFxuICAgICAgc3RhdHVzOiByb3V0ZVRhYmxlcy5sZW5ndGggPiAwID8gJ3Bhc3MnIDogJ3dhcm5pbmcnLFxuICAgICAgbWVzc2FnZTpcbiAgICAgICAgcm91dGVUYWJsZXMubGVuZ3RoID4gMFxuICAgICAgICAgID8gYEZvdW5kICR7cm91dGVUYWJsZXMubGVuZ3RofSByb3V0ZSB0YWJsZShzKWBcbiAgICAgICAgICA6ICdObyByb3V0ZSB0YWJsZXMgZm91bmQnLFxuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBnZW5lcmF0ZVN1bW1hcnkodmFsaWQ6IGJvb2xlYW4sIGZhaWxDb3VudDogbnVtYmVyKTogc3RyaW5nIHtcbiAgICBjb25zdCBwYXNzQ291bnQgPSB0aGlzLmNoZWNrcy5maWx0ZXIoYyA9PiBjLnN0YXR1cyA9PT0gJ3Bhc3MnKS5sZW5ndGg7XG4gICAgY29uc3Qgd2FybmluZ0NvdW50ID0gdGhpcy5jaGVja3MuZmlsdGVyKGMgPT4gYy5zdGF0dXMgPT09ICd3YXJuaW5nJykubGVuZ3RoO1xuXG4gICAgaWYgKHZhbGlkKSB7XG4gICAgICByZXR1cm4gYOKckyBWYWxpZGF0aW9uIHBhc3NlZC4gQWxsIHJlcXVpcmVkIGNoZWNrcyBwYXNzZWQgKCR7cGFzc0NvdW50fSBwYXNzZWQsICR7d2FybmluZ0NvdW50fSB3YXJuaW5ncykuYDtcbiAgICB9XG5cbiAgICByZXR1cm4gYOKclyBWYWxpZGF0aW9uIGZhaWxlZC4gJHtmYWlsQ291bnR9IGNoZWNrKHMpIGZhaWxlZCwgJHtwYXNzQ291bnR9IHBhc3NlZCwgJHt3YXJuaW5nQ291bnR9IHdhcm5pbmdzLmA7XG4gIH1cbn1cbiJdfQ==