cloudformation-stack-drift-detector 0.1.1

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,13 @@
1
+ import * as lambda from 'aws-cdk-lib/aws-lambda';
2
+ import { Construct } from 'constructs';
3
+ /**
4
+ * Props for DetectorFunction
5
+ */
6
+ export interface DetectorFunctionProps extends lambda.FunctionOptions {
7
+ }
8
+ /**
9
+ * An AWS Lambda function which executes src/funcs/detector.
10
+ */
11
+ export declare class DetectorFunction extends lambda.Function {
12
+ constructor(scope: Construct, id: string, props?: DetectorFunctionProps);
13
+ }
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.DetectorFunction = void 0;
37
+ // ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen".
38
+ const path = __importStar(require("path"));
39
+ const lambda = __importStar(require("aws-cdk-lib/aws-lambda"));
40
+ /**
41
+ * An AWS Lambda function which executes src/funcs/detector.
42
+ */
43
+ class DetectorFunction extends lambda.Function {
44
+ constructor(scope, id, props) {
45
+ super(scope, id, {
46
+ description: 'src/funcs/detector.lambda.ts',
47
+ ...props,
48
+ runtime: new lambda.Runtime('nodejs22.x', lambda.RuntimeFamily.NODEJS),
49
+ handler: 'index.handler',
50
+ code: lambda.Code.fromAsset(path.join(__dirname, '../../assets/funcs/detector.lambda')),
51
+ });
52
+ this.addEnvironment('AWS_NODEJS_CONNECTION_REUSE_ENABLED', '1', { removeInEdge: true });
53
+ }
54
+ }
55
+ exports.DetectorFunction = DetectorFunction;
56
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGV0ZWN0b3ItZnVuY3Rpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZnVuY3MvZGV0ZWN0b3ItZnVuY3Rpb24udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUEsNkVBQTZFO0FBQzdFLDJDQUE2QjtBQUM3QiwrREFBaUQ7QUFTakQ7O0dBRUc7QUFDSCxNQUFhLGdCQUFpQixTQUFRLE1BQU0sQ0FBQyxRQUFRO0lBQ25ELFlBQVksS0FBZ0IsRUFBRSxFQUFVLEVBQUUsS0FBNkI7UUFDckUsS0FBSyxDQUFDLEtBQUssRUFBRSxFQUFFLEVBQUU7WUFDZixXQUFXLEVBQUUsOEJBQThCO1lBQzNDLEdBQUcsS0FBSztZQUNSLE9BQU8sRUFBRSxJQUFJLE1BQU0sQ0FBQyxPQUFPLENBQUMsWUFBWSxFQUFFLE1BQU0sQ0FBQyxhQUFhLENBQUMsTUFBTSxDQUFDO1lBQ3RFLE9BQU8sRUFBRSxlQUFlO1lBQ3hCLElBQUksRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxvQ0FBb0MsQ0FBQyxDQUFDO1NBQ3hGLENBQUMsQ0FBQztRQUNILElBQUksQ0FBQyxjQUFjLENBQUMscUNBQXFDLEVBQUUsR0FBRyxFQUFFLEVBQUUsWUFBWSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7SUFDMUYsQ0FBQztDQUNGO0FBWEQsNENBV0MiLCJzb3VyY2VzQ29udGVudCI6WyIvLyB+fiBHZW5lcmF0ZWQgYnkgcHJvamVuLiBUbyBtb2RpZnksIGVkaXQgLnByb2plbnJjLnRzIGFuZCBydW4gXCJucHggcHJvamVuXCIuXG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0ICogYXMgbGFtYmRhIGZyb20gJ2F3cy1jZGstbGliL2F3cy1sYW1iZGEnO1xuaW1wb3J0IHsgQ29uc3RydWN0IH0gZnJvbSAnY29uc3RydWN0cyc7XG5cbi8qKlxuICogUHJvcHMgZm9yIERldGVjdG9yRnVuY3Rpb25cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBEZXRlY3RvckZ1bmN0aW9uUHJvcHMgZXh0ZW5kcyBsYW1iZGEuRnVuY3Rpb25PcHRpb25zIHtcbn1cblxuLyoqXG4gKiBBbiBBV1MgTGFtYmRhIGZ1bmN0aW9uIHdoaWNoIGV4ZWN1dGVzIHNyYy9mdW5jcy9kZXRlY3Rvci5cbiAqL1xuZXhwb3J0IGNsYXNzIERldGVjdG9yRnVuY3Rpb24gZXh0ZW5kcyBsYW1iZGEuRnVuY3Rpb24ge1xuICBjb25zdHJ1Y3RvcihzY29wZTogQ29uc3RydWN0LCBpZDogc3RyaW5nLCBwcm9wcz86IERldGVjdG9yRnVuY3Rpb25Qcm9wcykge1xuICAgIHN1cGVyKHNjb3BlLCBpZCwge1xuICAgICAgZGVzY3JpcHRpb246ICdzcmMvZnVuY3MvZGV0ZWN0b3IubGFtYmRhLnRzJyxcbiAgICAgIC4uLnByb3BzLFxuICAgICAgcnVudGltZTogbmV3IGxhbWJkYS5SdW50aW1lKCdub2RlanMyMi54JywgbGFtYmRhLlJ1bnRpbWVGYW1pbHkuTk9ERUpTKSxcbiAgICAgIGhhbmRsZXI6ICdpbmRleC5oYW5kbGVyJyxcbiAgICAgIGNvZGU6IGxhbWJkYS5Db2RlLmZyb21Bc3NldChwYXRoLmpvaW4oX19kaXJuYW1lLCAnLi4vLi4vYXNzZXRzL2Z1bmNzL2RldGVjdG9yLmxhbWJkYScpKSxcbiAgICB9KTtcbiAgICB0aGlzLmFkZEVudmlyb25tZW50KCdBV1NfTk9ERUpTX0NPTk5FQ1RJT05fUkVVU0VfRU5BQkxFRCcsICcxJywgeyByZW1vdmVJbkVkZ2U6IHRydWUgfSk7XG4gIH1cbn0iXX0=
@@ -0,0 +1,23 @@
1
+ import { DurableContext } from '@aws/durable-execution-sdk-js';
2
+ /**
3
+ * EventBridge input that selects stacks for drift detection.
4
+ * When `tagKey` is omitted, all stable stacks are inspected.
5
+ */
6
+ export interface DriftDetectionEvent {
7
+ /** Tag key used to discover CloudFormation stacks. */
8
+ readonly tagKey?: string;
9
+ /**
10
+ * Tag values to match for {@link DriftDetectionEvent.tagKey}.
11
+ * If omitted, any value for the key is accepted.
12
+ */
13
+ readonly tagValues?: string[];
14
+ }
15
+ /**
16
+ * Discovers target stacks and detects drift sequentially.
17
+ *
18
+ * @param event - Optional tag filter used to select stacks.
19
+ * @param context - Durable execution context for steps and waits.
20
+ */
21
+ export declare function processDriftDetection(event: DriftDetectionEvent, context: DurableContext): Promise<void>;
22
+ /** Durable Lambda handler wrapping {@link processDriftDetection}. */
23
+ export declare const handler: import("@aws/durable-execution-sdk-js").DurableLambdaHandler;
@@ -0,0 +1,226 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.handler = void 0;
4
+ exports.processDriftDetection = processDriftDetection;
5
+ const durable_execution_sdk_js_1 = require("@aws/durable-execution-sdk-js");
6
+ const client_cloudformation_1 = require("@aws-sdk/client-cloudformation");
7
+ const client_resource_groups_tagging_api_1 = require("@aws-sdk/client-resource-groups-tagging-api");
8
+ const client_sns_1 = require("@aws-sdk/client-sns");
9
+ /** Interval between DescribeStackDriftDetectionStatus polls. */
10
+ const DETECTION_POLL_INTERVAL_SECONDS = 30;
11
+ /** Environment variable that holds the SNS topic ARN for drift notifications. */
12
+ const NOTIFICATION_TOPIC_ARN_ENV = 'NOTIFICATION_TOPIC_ARN';
13
+ /** Resource Groups Tagging API type used to discover CloudFormation stacks. */
14
+ const CLOUDFORMATION_STACK_RESOURCE_TYPE = 'cloudformation:stack';
15
+ /** Stack statuses that are eligible for drift detection. */
16
+ const STABLE_STACK_STATUSES = [
17
+ client_cloudformation_1.StackStatus.CREATE_COMPLETE,
18
+ client_cloudformation_1.StackStatus.UPDATE_COMPLETE,
19
+ client_cloudformation_1.StackStatus.UPDATE_ROLLBACK_COMPLETE,
20
+ client_cloudformation_1.StackStatus.IMPORT_COMPLETE,
21
+ client_cloudformation_1.StackStatus.IMPORT_ROLLBACK_COMPLETE,
22
+ ];
23
+ /** Shared CloudFormation client for drift detection APIs. */
24
+ const cloudFormation = new client_cloudformation_1.CloudFormationClient({});
25
+ /** Shared tagging client used to resolve stacks by tag. */
26
+ const resourceGroupsTagging = new client_resource_groups_tagging_api_1.ResourceGroupsTaggingAPIClient({});
27
+ /** Shared SNS client used to publish drift notifications. */
28
+ const sns = new client_sns_1.SNSClient({});
29
+ /**
30
+ * Reads the SNS topic ARN from the Lambda environment.
31
+ *
32
+ * @returns The notification topic ARN.
33
+ * @throws When {@link NOTIFICATION_TOPIC_ARN_ENV} is not set.
34
+ */
35
+ function getNotificationTopicArn() {
36
+ const topicArn = process.env[NOTIFICATION_TOPIC_ARN_ENV];
37
+ if (!topicArn) {
38
+ throw new Error(`${NOTIFICATION_TOPIC_ARN_ENV} environment variable is not set`);
39
+ }
40
+ return topicArn;
41
+ }
42
+ /**
43
+ * Extracts a CloudFormation stack name from a stack ARN.
44
+ *
45
+ * @param resourceArn - Stack ARN in `arn:...:stack/{name}/{id}` form.
46
+ * @returns The stack name.
47
+ * @throws When the ARN does not contain a stack name.
48
+ */
49
+ function getStackNameFromArn(resourceArn) {
50
+ const resource = resourceArn.split(':').pop();
51
+ const stackName = resource?.split('/')[1];
52
+ if (!stackName) {
53
+ throw new Error(`Unable to get stack name from ARN: ${resourceArn}`);
54
+ }
55
+ return stackName;
56
+ }
57
+ /**
58
+ * Lists CloudFormation stacks that match the given tag filter.
59
+ *
60
+ * @param tagKey - Tag key to match.
61
+ * @param tagValues - Optional values for `tagKey`. An empty or omitted list matches any value.
62
+ * @returns Stack names discovered by the Resource Groups Tagging API.
63
+ */
64
+ async function getTaggedStackNames(tagKey, tagValues) {
65
+ const stackNames = [];
66
+ let paginationToken;
67
+ do {
68
+ const response = await resourceGroupsTagging.send(new client_resource_groups_tagging_api_1.GetResourcesCommand({
69
+ ResourceTypeFilters: [CLOUDFORMATION_STACK_RESOURCE_TYPE],
70
+ TagFilters: [
71
+ {
72
+ Key: tagKey,
73
+ Values: tagValues && tagValues.length > 0 ? tagValues : undefined,
74
+ },
75
+ ],
76
+ PaginationToken: paginationToken,
77
+ }));
78
+ for (const mapping of response.ResourceTagMappingList ?? []) {
79
+ if (mapping.ResourceARN) {
80
+ stackNames.push(getStackNameFromArn(mapping.ResourceARN));
81
+ }
82
+ }
83
+ paginationToken = response.PaginationToken;
84
+ } while (paginationToken);
85
+ return stackNames;
86
+ }
87
+ /**
88
+ * Lists all stable CloudFormation stacks in the current account and region.
89
+ *
90
+ * @returns Stack names in a complete, non-transitional status.
91
+ */
92
+ async function getAllStackNames() {
93
+ const stackNames = [];
94
+ let nextToken;
95
+ do {
96
+ const response = await cloudFormation.send(new client_cloudformation_1.ListStacksCommand({
97
+ StackStatusFilter: STABLE_STACK_STATUSES,
98
+ NextToken: nextToken,
99
+ }));
100
+ for (const summary of response.StackSummaries ?? []) {
101
+ if (summary.StackName) {
102
+ stackNames.push(summary.StackName);
103
+ }
104
+ }
105
+ nextToken = response.NextToken;
106
+ } while (nextToken);
107
+ return stackNames;
108
+ }
109
+ /**
110
+ * Resolves target stack names from the detector event.
111
+ *
112
+ * @param event - Tag filter from EventBridge, or an empty object for all stacks.
113
+ * @returns Stack names to inspect for drift.
114
+ */
115
+ async function getTargetStackNames(event) {
116
+ if (event.tagKey) {
117
+ return getTaggedStackNames(event.tagKey, event.tagValues);
118
+ }
119
+ return getAllStackNames();
120
+ }
121
+ /**
122
+ * Returns modified or deleted resource drifts for a stack.
123
+ *
124
+ * @param stackName - Stack to describe.
125
+ * @returns Resource drift records filtered to `MODIFIED` and `DELETED`.
126
+ */
127
+ async function getResourceDrifts(stackName) {
128
+ const resourceDrifts = [];
129
+ let nextToken;
130
+ do {
131
+ const response = await cloudFormation.send(new client_cloudformation_1.DescribeStackResourceDriftsCommand({
132
+ StackName: stackName,
133
+ StackResourceDriftStatusFilters: ['MODIFIED', 'DELETED'],
134
+ NextToken: nextToken,
135
+ }));
136
+ resourceDrifts.push(...(response.StackResourceDrifts ?? []));
137
+ nextToken = response.NextToken;
138
+ } while (nextToken);
139
+ return resourceDrifts;
140
+ }
141
+ /**
142
+ * Polls DescribeStackDriftDetectionStatus until detection is no longer in progress.
143
+ *
144
+ * @param stackName - Stack used in durable step and wait names.
145
+ * @param detectionId - ID returned by DetectStackDrift.
146
+ * @param context - Durable execution context.
147
+ * @returns The completed detection status.
148
+ */
149
+ async function getDriftDetectionStatus(stackName, detectionId, context) {
150
+ let attempt = 0;
151
+ let status;
152
+ do {
153
+ await context.wait(`wait-drift-detection-${stackName}-${attempt}`, {
154
+ seconds: DETECTION_POLL_INTERVAL_SECONDS,
155
+ });
156
+ status = await context.step(`describe-drift-detection-status-${stackName}-${attempt}`, async () => {
157
+ const response = await cloudFormation.send(new client_cloudformation_1.DescribeStackDriftDetectionStatusCommand({
158
+ StackDriftDetectionId: detectionId,
159
+ }));
160
+ return {
161
+ detectionStatus: response.DetectionStatus,
162
+ stackDriftStatus: response.StackDriftStatus,
163
+ detectionStatusReason: response.DetectionStatusReason,
164
+ };
165
+ });
166
+ attempt += 1;
167
+ } while (status.detectionStatus === 'DETECTION_IN_PROGRESS');
168
+ return status;
169
+ }
170
+ /**
171
+ * Detects drift for one stack and publishes an SNS notification when the stack has drifted.
172
+ *
173
+ * @param stackName - Stack to inspect.
174
+ * @param topicArn - SNS topic that receives drift notifications.
175
+ * @param context - Durable execution context.
176
+ * @throws When DetectStackDrift returns no ID, or detection finishes with `DETECTION_FAILED`.
177
+ */
178
+ async function processStackDrift(stackName, topicArn, context) {
179
+ const detectionId = await context.step(`detect-stack-drift-${stackName}`, async () => {
180
+ const response = await cloudFormation.send(new client_cloudformation_1.DetectStackDriftCommand({
181
+ StackName: stackName,
182
+ }));
183
+ if (!response.StackDriftDetectionId) {
184
+ throw new Error(`DetectStackDrift did not return a detection ID for stack ${stackName}`);
185
+ }
186
+ return response.StackDriftDetectionId;
187
+ });
188
+ const status = await getDriftDetectionStatus(stackName, detectionId, context);
189
+ if (status.detectionStatus === 'DETECTION_FAILED') {
190
+ throw new Error(`Drift detection failed for stack ${stackName}: ${status.detectionStatusReason ?? 'unknown reason'}`);
191
+ }
192
+ if (status.stackDriftStatus !== 'DRIFTED') {
193
+ return;
194
+ }
195
+ const resourceDrifts = await context.step(`describe-resource-drifts-${stackName}`, async () => getResourceDrifts(stackName));
196
+ await context.step(`publish-notification-${stackName}`, async () => {
197
+ const subject = `Stack drift detected: ${stackName}`.slice(0, 100);
198
+ await sns.send(new client_sns_1.PublishCommand({
199
+ TopicArn: topicArn,
200
+ Subject: subject,
201
+ Message: JSON.stringify({
202
+ stackName,
203
+ stackDriftStatus: status.stackDriftStatus,
204
+ driftedResources: resourceDrifts,
205
+ }),
206
+ }));
207
+ });
208
+ }
209
+ /**
210
+ * Discovers target stacks and detects drift sequentially.
211
+ *
212
+ * @param event - Optional tag filter used to select stacks.
213
+ * @param context - Durable execution context for steps and waits.
214
+ */
215
+ async function processDriftDetection(event, context) {
216
+ const topicArn = getNotificationTopicArn();
217
+ const stackNames = await context.step('get-target-stack-names', async () => {
218
+ return getTargetStackNames(event ?? {});
219
+ });
220
+ for (const stackName of stackNames) {
221
+ await processStackDrift(stackName, topicArn, context);
222
+ }
223
+ }
224
+ /** Durable Lambda handler wrapping {@link processDriftDetection}. */
225
+ exports.handler = (0, durable_execution_sdk_js_1.withDurableExecution)(processDriftDetection);
226
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGV0ZWN0b3IubGFtYmRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2Z1bmNzL2RldGVjdG9yLmxhbWJkYS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFtU0Esc0RBWUM7QUEvU0QsNEVBQXFGO0FBQ3JGLDBFQVF3QztBQUN4QyxvR0FHcUQ7QUFDckQsb0RBQWdFO0FBRWhFLGdFQUFnRTtBQUNoRSxNQUFNLCtCQUErQixHQUFHLEVBQUUsQ0FBQztBQUMzQyxpRkFBaUY7QUFDakYsTUFBTSwwQkFBMEIsR0FBRyx3QkFBd0IsQ0FBQztBQUM1RCwrRUFBK0U7QUFDL0UsTUFBTSxrQ0FBa0MsR0FBRyxzQkFBc0IsQ0FBQztBQUNsRSw0REFBNEQ7QUFDNUQsTUFBTSxxQkFBcUIsR0FBRztJQUM1QixtQ0FBVyxDQUFDLGVBQWU7SUFDM0IsbUNBQVcsQ0FBQyxlQUFlO0lBQzNCLG1DQUFXLENBQUMsd0JBQXdCO0lBQ3BDLG1DQUFXLENBQUMsZUFBZTtJQUMzQixtQ0FBVyxDQUFDLHdCQUF3QjtDQUNyQyxDQUFDO0FBRUYsNkRBQTZEO0FBQzdELE1BQU0sY0FBYyxHQUFHLElBQUksNENBQW9CLENBQUMsRUFBRSxDQUFDLENBQUM7QUFDcEQsMkRBQTJEO0FBQzNELE1BQU0scUJBQXFCLEdBQUcsSUFBSSxtRUFBOEIsQ0FBQyxFQUFFLENBQUMsQ0FBQztBQUNyRSw2REFBNkQ7QUFDN0QsTUFBTSxHQUFHLEdBQUcsSUFBSSxzQkFBUyxDQUFDLEVBQUUsQ0FBQyxDQUFDO0FBMEI5Qjs7Ozs7R0FLRztBQUNILFNBQVMsdUJBQXVCO0lBQzlCLE1BQU0sUUFBUSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsMEJBQTBCLENBQUMsQ0FBQztJQUN6RCxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUM7UUFDZCxNQUFNLElBQUksS0FBSyxDQUFDLEdBQUcsMEJBQTBCLGtDQUFrQyxDQUFDLENBQUM7SUFDbkYsQ0FBQztJQUNELE9BQU8sUUFBUSxD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QUFxQixDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBEdXJhYmxlQ29udGV4dCwgd2l0aER1cmFibGVFeGVjdXRpb24gfSBmcm9tICdAYXdzL2R1cmFibGUtZXhlY3V0aW9uLXNkay1qcyc7XG5pbXBvcnQge1xuICBDbG91ZEZvcm1hdGlvbkNsaWVudCxcbiAgRGVzY3JpYmVTdGFja0RyaWZ0RGV0ZWN0aW9uU3RhdHVzQ29tbWFuZCxcbiAgRGVzY3JpYmVTdGFja1Jlc291cmNlRHJpZnRzQ29tbWFuZCxcbiAgRGV0ZWN0U3RhY2tEcmlmdENvbW1hbmQsXG4gIExpc3RTdGFja3NDb21tYW5kLFxuICBTdGFja1Jlc291cmNlRHJpZnQsXG4gIFN0YWNrU3RhdHVzLFxufSBmcm9tICdAYXdzLXNkay9jbGllbnQtY2xvdWRmb3JtYXRpb24nO1xuaW1wb3J0IHtcbiAgR2V0UmVzb3VyY2VzQ29tbWFuZCxcbiAgUmVzb3VyY2VHcm91cHNUYWdnaW5nQVBJQ2xpZW50LFxufSBmcm9tICdAYXdzLXNkay9jbGllbnQtcmVzb3VyY2UtZ3JvdXBzLXRhZ2dpbmctYXBpJztcbmltcG9ydCB7IFB1Ymxpc2hDb21tYW5kLCBTTlNDbGllbnQgfSBmcm9tICdAYXdzLXNkay9jbGllbnQtc25zJztcblxuLyoqIEludGVydmFsIGJldHdlZW4gRGVzY3JpYmVTdGFja0RyaWZ0RGV0ZWN0aW9uU3RhdHVzIHBvbGxzLiAqL1xuY29uc3QgREVURUNUSU9OX1BPTExfSU5URVJWQUxfU0VDT05EUyA9IDMwO1xuLyoqIEVudmlyb25tZW50IHZhcmlhYmxlIHRoYXQgaG9sZHMgdGhlIFNOUyB0b3BpYyBBUk4gZm9yIGRyaWZ0IG5vdGlmaWNhdGlvbnMuICovXG5jb25zdCBOT1RJRklDQVRJT05fVE9QSUNfQVJOX0VOViA9ICdOT1RJRklDQVRJT05fVE9QSUNfQVJOJztcbi8qKiBSZXNvdXJjZSBHcm91cHMgVGFnZ2luZyBBUEkgdHlwZSB1c2VkIHRvIGRpc2NvdmVyIENsb3VkRm9ybWF0aW9uIHN0YWNrcy4gKi9cbmNvbnN0IENMT1VERk9STUFUSU9OX1NUQUNLX1JFU09VUkNFX1RZUEUgPSAnY2xvdWRmb3JtYXRpb246c3RhY2snO1xuLyoqIFN0YWNrIHN0YXR1c2VzIHRoYXQgYXJlIGVsaWdpYmxlIGZvciBkcmlmdCBkZXRlY3Rpb24uICovXG5jb25zdCBTVEFCTEVfU1RBQ0tfU1RBVFVTRVMgPSBbXG4gIFN0YWNrU3RhdHVzLkNSRUFURV9DT01QTEVURSxcbiAgU3RhY2tTdGF0dXMuVVBEQVRFX0NPTVBMRVRFLFxuICBTdGFja1N0YXR1cy5VUERBVEVfUk9MTEJBQ0tfQ09NUExFVEUsXG4gIFN0YWNrU3RhdHVzLklNUE9SVF9DT01QTEVURSxcbiAgU3RhY2tTdGF0dXMuSU1QT1JUX1JPTExCQUNLX0NPTVBMRVRFLFxuXTtcblxuLyoqIFNoYXJlZCBDbG91ZEZvcm1hdGlvbiBjbGllbnQgZm9yIGRyaWZ0IGRldGVjdGlvbiBBUElzLiAqL1xuY29uc3QgY2xvdWRGb3JtYXRpb24gPSBuZXcgQ2xvdWRGb3JtYXRpb25DbGllbnQoe30pO1xuLyoqIFNoYXJlZCB0YWdnaW5nIGNsaWVudCB1c2VkIHRvIHJlc29sdmUgc3RhY2tzIGJ5IHRhZy4gKi9cbmNvbnN0IHJlc291cmNlR3JvdXBzVGFnZ2luZyA9IG5ldyBSZXNvdXJjZUdyb3Vwc1RhZ2dpbmdBUElDbGllbnQoe30pO1xuLyoqIFNoYXJlZCBTTlMgY2xpZW50IHVzZWQgdG8gcHVibGlzaCBkcmlmdCBub3RpZmljYXRpb25zLiAqL1xuY29uc3Qgc25zID0gbmV3IFNOU0NsaWVudCh7fSk7XG5cbi8qKlxuICogRXZlbnRCcmlkZ2UgaW5wdXQgdGhhdCBzZWxlY3RzIHN0YWNrcyBmb3IgZHJpZnQgZGV0ZWN0aW9uLlxuICogV2hlbiBgdGFnS2V5YCBpcyBvbWl0dGVkLCBhbGwgc3RhYmxlIHN0YWNrcyBhcmUgaW5zcGVjdGVkLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIERyaWZ0RGV0ZWN0aW9uRXZlbnQge1xuICAvKiogVGFnIGtleSB1c2VkIHRvIGRpc2NvdmVyIENsb3VkRm9ybWF0aW9uIHN0YWNrcy4gKi9cbiAgcmVhZG9ubHkgdGFnS2V5Pzogc3RyaW5nO1xuICAvKipcbiAgICogVGFnIHZhbHVlcyB0byBtYXRjaCBmb3Ige0BsaW5rIERyaWZ0RGV0ZWN0aW9uRXZlbnQudGFnS2V5fS5cbiAgICogSWYgb21pdHRlZCwgYW55IHZhbHVlIGZvciB0aGUga2V5IGlzIGFjY2VwdGVkLlxuICAgKi9cbiAgcmVhZG9ubHkgdGFnVmFsdWVzPzogc3RyaW5nW107XG59XG5cbi8qKiBDaGVja3BvaW50ZWQgcmVzdWx0IG9mIERlc2NyaWJlU3RhY2tEcmlmdERldGVjdGlvblN0YXR1cy4gKi9cbmludGVyZmFjZSBEcmlmdERldGVjdGlvblN0YXR1cyB7XG4gIC8qKiBTdGF0dXMgb2YgdGhlIGRyaWZ0IGRldGVjdGlvbiBvcGVyYXRpb24uICovXG4gIHJlYWRvbmx5IGRldGVjdGlvblN0YXR1czogc3RyaW5nIHwgdW5kZWZpbmVkO1xuICAvKiogRHJpZnQgc3RhdHVzIG9mIHRoZSBzdGFjayBhZnRlciBkZXRlY3Rpb24gY29tcGxldGVzLiAqL1xuICByZWFkb25seSBzdGFja0RyaWZ0U3RhdHVzOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gIC8qKiBSZWFzb24gcmV0dXJuZWQgd2hlbiBkZXRlY3Rpb24gZmFpbHMuICovXG4gIHJlYWRvbmx5IGRldGVjdGlvblN0YXR1c1JlYXNvbjogc3RyaW5nIHwgdW5kZWZpbmVkO1xufVxuXG4vKipcbiAqIFJlYWRzIHRoZSBTTlMgdG9waWMgQVJOIGZyb20gdGhlIExhbWJkYSBlbnZpcm9ubWVudC5cbiAqXG4gKiBAcmV0dXJucyBUaGUgbm90aWZpY2F0aW9uIHRvcGljIEFSTi5cbiAqIEB0aHJvd3MgV2hlbiB7QGxpbmsgTk9USUZJQ0FUSU9OX1RPUElDX0FSTl9FTlZ9IGlzIG5vdCBzZXQuXG4gKi9cbmZ1bmN0aW9uIGdldE5vdGlmaWNhdGlvblRvcGljQXJuKCk6IHN0cmluZyB7XG4gIGNvbnN0IHRvcGljQXJuID0gcHJvY2Vzcy5lbnZbTk9USUZJQ0FUSU9OX1RPUElDX0FSTl9FTlZdO1xuICBpZiAoIXRvcGljQXJuKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGAke05PVElGSUNBVElPTl9UT1BJQ19BUk5fRU5WfSBlbnZpcm9ubWVudCB2YXJpYWJsZSBpcyBub3Qgc2V0YCk7XG4gIH1cbiAgcmV0dXJuIHRvcGljQXJuO1xufVxuXG4vKipcbiAqIEV4dHJhY3RzIGEgQ2xvdWRGb3JtYXRpb24gc3RhY2sgbmFtZSBmcm9tIGEgc3RhY2sgQVJOLlxuICpcbiAqIEBwYXJhbSByZXNvdXJjZUFybiAtIFN0YWNrIEFSTiBpbiBgYXJuOi4uLjpzdGFjay97bmFtZX0ve2lkfWAgZm9ybS5cbiAqIEByZXR1cm5zIFRoZSBzdGFjayBuYW1lLlxuICogQHRocm93cyBXaGVuIHRoZSBBUk4gZG9lcyBub3QgY29udGFpbiBhIHN0YWNrIG5hbWUuXG4gKi9cbmZ1bmN0aW9uIGdldFN0YWNrTmFtZUZyb21Bcm4ocmVzb3VyY2VBcm46IHN0cmluZyk6IHN0cmluZyB7XG4gIGNvbnN0IHJlc291cmNlID0gcmVzb3VyY2VBcm4uc3BsaXQoJzonKS5wb3AoKTtcbiAgY29uc3Qgc3RhY2tOYW1lID0gcmVzb3VyY2U/LnNwbGl0KCcvJylbMV07XG4gIGlmICghc3RhY2tOYW1lKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBVbmFibGUgdG8gZ2V0IHN0YWNrIG5hbWUgZnJvbSBBUk46ICR7cmVzb3VyY2VBcm59YCk7XG4gIH1cbiAgcmV0dXJuIHN0YWNrTmFtZTtcbn1cblxuLyoqXG4gKiBMaXN0cyBDbG91ZEZvcm1hdGlvbiBzdGFja3MgdGhhdCBtYXRjaCB0aGUgZ2l2ZW4gdGFnIGZpbHRlci5cbiAqXG4gKiBAcGFyYW0gdGFnS2V5IC0gVGFnIGtleSB0byBtYXRjaC5cbiAqIEBwYXJhbSB0YWdWYWx1ZXMgLSBPcHRpb25hbCB2YWx1ZXMgZm9yIGB0YWdLZXlgLiBBbiBlbXB0eSBvciBvbWl0dGVkIGxpc3QgbWF0Y2hlcyBhbnkgdmFsdWUuXG4gKiBAcmV0dXJucyBTdGFjayBuYW1lcyBkaXNjb3ZlcmVkIGJ5IHRoZSBSZXNvdXJjZSBHcm91cHMgVGFnZ2luZyBBUEkuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGdldFRhZ2dlZFN0YWNrTmFtZXModGFnS2V5OiBzdHJpbmcsIHRhZ1ZhbHVlcz86IHN0cmluZ1tdKTogUHJvbWlzZTxzdHJpbmdbXT4ge1xuICBjb25zdCBzdGFja05hbWVzOiBzdHJpbmdbXSA9IFtdO1xuICBsZXQgcGFnaW5hdGlvblRva2VuOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG5cbiAgZG8ge1xuICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgcmVzb3VyY2VHcm91cHNUYWdnaW5nLnNlbmQobmV3IEdldFJlc291cmNlc0NvbW1hbmQoe1xuICAgICAgUmVzb3VyY2VUeXBlRmlsdGVyczogW0NMT1VERk9STUFUSU9OX1NUQUNLX1JFU09VUkNFX1RZUEVdLFxuICAgICAgVGFnRmlsdGVyczogW1xuICAgICAgICB7XG4gICAgICAgICAgS2V5OiB0YWdLZXksXG4gICAgICAgICAgVmFsdWVzOiB0YWdWYWx1ZXMgJiYgdGFnVmFsdWVzLmxlbmd0aCA+IDAgPyB0YWdWYWx1ZXMgOiB1bmRlZmluZWQsXG4gICAgICAgIH0sXG4gICAgICBdLFxuICAgICAgUGFnaW5hdGlvblRva2VuOiBwYWdpbmF0aW9uVG9rZW4sXG4gICAgfSkpO1xuXG4gICAgZm9yIChjb25zdCBtYXBwaW5nIG9mIHJlc3BvbnNlLlJlc291cmNlVGFnTWFwcGluZ0xpc3QgPz8gW10pIHtcbiAgICAgIGlmIChtYXBwaW5nLlJlc291cmNlQVJOKSB7XG4gICAgICAgIHN0YWNrTmFtZXMucHVzaChnZXRTdGFja05hbWVGcm9tQXJuKG1hcHBpbmcuUmVzb3VyY2VBUk4pKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBwYWdpbmF0aW9uVG9rZW4gPSByZXNwb25zZS5QYWdpbmF0aW9uVG9rZW47XG4gIH0gd2hpbGUgKHBhZ2luYXRpb25Ub2tlbik7XG5cbiAgcmV0dXJuIHN0YWNrTmFtZXM7XG59XG5cbi8qKlxuICogTGlzdHMgYWxsIHN0YWJsZSBDbG91ZEZvcm1hdGlvbiBzdGFja3MgaW4gdGhlIGN1cnJlbnQgYWNjb3VudCBhbmQgcmVnaW9uLlxuICpcbiAqIEByZXR1cm5zIFN0YWNrIG5hbWVzIGluIGEgY29tcGxldGUsIG5vbi10cmFuc2l0aW9uYWwgc3RhdHVzLlxuICovXG5hc3luYyBmdW5jdGlvbiBnZXRBbGxTdGFja05hbWVzKCk6IFByb21pc2U8c3RyaW5nW10+IHtcbiAgY29uc3Qgc3RhY2tOYW1lczogc3RyaW5nW10gPSBbXTtcbiAgbGV0IG5leHRUb2tlbjogc3RyaW5nIHwgdW5kZWZpbmVkO1xuXG4gIGRvIHtcbiAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGNsb3VkRm9ybWF0aW9uLnNlbmQobmV3IExpc3RTdGFja3NDb21tYW5kKHtcbiAgICAgIFN0YWNrU3RhdHVzRmlsdGVyOiBTVEFCTEVfU1RBQ0tfU1RBVFVTRVMsXG4gICAgICBOZXh0VG9rZW46IG5leHRUb2tlbixcbiAgICB9KSk7XG5cbiAgICBmb3IgKGNvbnN0IHN1bW1hcnkgb2YgcmVzcG9uc2UuU3RhY2tTdW1tYXJpZXMgPz8gW10pIHtcbiAgICAgIGlmIChzdW1tYXJ5LlN0YWNrTmFtZSkge1xuICAgICAgICBzdGFja05hbWVzLnB1c2goc3VtbWFyeS5TdGFja05hbWUpO1xuICAgICAgfVxuICAgIH1cblxuICAgIG5leHRUb2tlbiA9IHJlc3BvbnNlLk5leHRUb2tlbjtcbiAgfSB3aGlsZSAobmV4dFRva2VuKTtcblxuICByZXR1cm4gc3RhY2tOYW1lcztcbn1cblxuLyoqXG4gKiBSZXNvbHZlcyB0YXJnZXQgc3RhY2sgbmFtZXMgZnJvbSB0aGUgZGV0ZWN0b3IgZXZlbnQuXG4gKlxuICogQHBhcmFtIGV2ZW50IC0gVGFnIGZpbHRlciBmcm9tIEV2ZW50QnJpZGdlLCBvciBhbiBlbXB0eSBvYmplY3QgZm9yIGFsbCBzdGFja3MuXG4gKiBAcmV0dXJucyBTdGFjayBuYW1lcyB0byBpbnNwZWN0IGZvciBkcmlmdC5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gZ2V0VGFyZ2V0U3RhY2tOYW1lcyhldmVudDogRHJpZnREZXRlY3Rpb25FdmVudCk6IFByb21pc2U8c3RyaW5nW10+IHtcbiAgaWYgKGV2ZW50LnRhZ0tleSkge1xuICAgIHJldHVybiBnZXRUYWdnZWRTdGFja05hbWVzKGV2ZW50LnRhZ0tleSwgZXZlbnQudGFnVmFsdWVzKTtcbiAgfVxuICByZXR1cm4gZ2V0QWxsU3RhY2tOYW1lcygpO1xufVxuXG4vKipcbiAqIFJldHVybnMgbW9kaWZpZWQgb3IgZGVsZXRlZCByZXNvdXJjZSBkcmlmdHMgZm9yIGEgc3RhY2suXG4gKlxuICogQHBhcmFtIHN0YWNrTmFtZSAtIFN0YWNrIHRvIGRlc2NyaWJlLlxuICogQHJldHVybnMgUmVzb3VyY2UgZHJpZnQgcmVjb3JkcyBmaWx0ZXJlZCB0byBgTU9ESUZJRURgIGFuZCBgREVMRVRFRGAuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGdldFJlc291cmNlRHJpZnRzKHN0YWNrTmFtZTogc3RyaW5nKTogUHJvbWlzZTxTdGFja1Jlc291cmNlRHJpZnRbXT4ge1xuICBjb25zdCByZXNvdXJjZURyaWZ0czogU3RhY2tSZXNvdXJjZURyaWZ0W10gPSBbXTtcbiAgbGV0IG5leHRUb2tlbjogc3RyaW5nIHwgdW5kZWZpbmVkO1xuXG4gIGRvIHtcbiAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGNsb3VkRm9ybWF0aW9uLnNlbmQobmV3IERlc2NyaWJlU3RhY2tSZXNvdXJjZURyaWZ0c0NvbW1hbmQoe1xuICAgICAgU3RhY2tOYW1lOiBzdGFja05hbWUsXG4gICAgICBTdGFja1Jlc291cmNlRHJpZnRTdGF0dXNGaWx0ZXJzOiBbJ01PRElGSUVEJywgJ0RFTEVURUQnXSxcbiAgICAgIE5leHRUb2tlbjogbmV4dFRva2VuLFxuICAgIH0pKTtcbiAgICByZXNvdXJjZURyaWZ0cy5wdXNoKC4uLihyZXNwb25zZS5TdGFja1Jlc291cmNlRHJpZnRzID8/IFtdKSk7XG4gICAgbmV4dFRva2VuID0gcmVzcG9uc2UuTmV4dFRva2VuO1xuICB9IHdoaWxlIChuZXh0VG9rZW4pO1xuXG4gIHJldHVybiByZXNvdXJjZURyaWZ0cztcbn1cblxuLyoqXG4gKiBQb2xscyBEZXNjcmliZVN0YWNrRHJpZnREZXRlY3Rpb25TdGF0dXMgdW50aWwgZGV0ZWN0aW9uIGlzIG5vIGxvbmdlciBpbiBwcm9ncmVzcy5cbiAqXG4gKiBAcGFyYW0gc3RhY2tOYW1lIC0gU3RhY2sgdXNlZCBpbiBkdXJhYmxlIHN0ZXAgYW5kIHdhaXQgbmFtZXMuXG4gKiBAcGFyYW0gZGV0ZWN0aW9uSWQgLSBJRCByZXR1cm5lZCBieSBEZXRlY3RTdGFja0RyaWZ0LlxuICogQHBhcmFtIGNvbnRleHQgLSBEdXJhYmxlIGV4ZWN1dGlvbiBjb250ZXh0LlxuICogQHJldHVybnMgVGhlIGNvbXBsZXRlZCBkZXRlY3Rpb24gc3RhdHVzLlxuICovXG5hc3luYyBmdW5jdGlvbiBnZXREcmlmdERldGVjdGlvblN0YXR1cyhcbiAgc3RhY2tOYW1lOiBzdHJpbmcsXG4gIGRldGVjdGlvbklkOiBzdHJpbmcsXG4gIGNvbnRleHQ6IER1cmFibGVDb250ZXh0LFxuKTogUHJvbWlzZTxEcmlmdERldGVjdGlvblN0YXR1cz4ge1xuICBsZXQgYXR0ZW1wdCA9IDA7XG4gIGxldCBzdGF0dXM6IERyaWZ0RGV0ZWN0aW9uU3RhdHVzO1xuXG4gIGRvIHtcbiAgICBhd2FpdCBjb250ZXh0LndhaXQoYHdhaXQtZHJpZnQtZGV0ZWN0aW9uLSR7c3RhY2tOYW1lfS0ke2F0dGVtcHR9YCwge1xuICAgICAgc2Vjb25kczogREVURUNUSU9OX1BPTExfSU5URVJWQUxfU0VDT05EUyxcbiAgICB9KTtcblxuICAgIHN0YXR1cyA9IGF3YWl0IGNvbnRleHQuc3RlcChcbiAgICAgIGBkZXNjcmliZS1kcmlmdC1kZXRlY3Rpb24tc3RhdHVzLSR7c3RhY2tOYW1lfS0ke2F0dGVtcHR9YCxcbiAgICAgIGFzeW5jICgpID0+IHtcbiAgICAgICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBjbG91ZEZvcm1hdGlvbi5zZW5kKG5ldyBEZXNjcmliZVN0YWNrRHJpZnREZXRlY3Rpb25TdGF0dXNDb21tYW5kKHtcbiAgICAgICAgICBTdGFja0RyaWZ0RGV0ZWN0aW9uSWQ6IGRldGVjdGlvbklkLFxuICAgICAgICB9KSk7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgZGV0ZWN0aW9uU3RhdHVzOiByZXNwb25zZS5EZXRlY3Rpb25TdGF0dXMsXG4gICAgICAgICAgc3RhY2tEcmlmdFN0YXR1czogcmVzcG9uc2UuU3RhY2tEcmlmdFN0YXR1cyxcbiAgICAgICAgICBkZXRlY3Rpb25TdGF0dXNSZWFzb246IHJlc3BvbnNlLkRldGVjdGlvblN0YXR1c1JlYXNvbixcbiAgICAgICAgfTtcbiAgICAgIH0sXG4gICAgKTtcblxuICAgIGF0dGVtcHQgKz0gMTtcbiAgfSB3aGlsZSAoc3RhdHVzLmRldGVjdGlvblN0YXR1cyA9PT0gJ0RFVEVDVElPTl9JTl9QUk9HUkVTUycpO1xuXG4gIHJldHVybiBzdGF0dXM7XG59XG5cbi8qKlxuICogRGV0ZWN0cyBkcmlmdCBmb3Igb25lIHN0YWNrIGFuZCBwdWJsaXNoZXMgYW4gU05TIG5vdGlmaWNhdGlvbiB3aGVuIHRoZSBzdGFjayBoYXMgZHJpZnRlZC5cbiAqXG4gKiBAcGFyYW0gc3RhY2tOYW1lIC0gU3RhY2sgdG8gaW5zcGVjdC5cbiAqIEBwYXJhbSB0b3BpY0FybiAtIFNOUyB0b3BpYyB0aGF0IHJlY2VpdmVzIGRyaWZ0IG5vdGlmaWNhdGlvbnMuXG4gKiBAcGFyYW0gY29udGV4dCAtIER1cmFibGUgZXhlY3V0aW9uIGNvbnRleHQuXG4gKiBAdGhyb3dzIFdoZW4gRGV0ZWN0U3RhY2tEcmlmdCByZXR1cm5zIG5vIElELCBvciBkZXRlY3Rpb24gZmluaXNoZXMgd2l0aCBgREVURUNUSU9OX0ZBSUxFRGAuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHByb2Nlc3NTdGFja0RyaWZ0KFxuICBzdGFja05hbWU6IHN0cmluZyxcbiAgdG9waWNBcm46IHN0cmluZyxcbiAgY29udGV4dDogRHVyYWJsZUNvbnRleHQsXG4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgY29uc3QgZGV0ZWN0aW9uSWQgPSBhd2FpdCBjb250ZXh0LnN0ZXAoYGRldGVjdC1zdGFjay1kcmlmdC0ke3N0YWNrTmFtZX1gLCBhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBjbG91ZEZvcm1hdGlvbi5zZW5kKG5ldyBEZXRlY3RTdGFja0RyaWZ0Q29tbWFuZCh7XG4gICAgICBTdGFja05hbWU6IHN0YWNrTmFtZSxcbiAgICB9KSk7XG4gICAgaWYgKCFyZXNwb25zZS5TdGFja0RyaWZ0RGV0ZWN0aW9uSWQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgRGV0ZWN0U3RhY2tEcmlmdCBkaWQgbm90IHJldHVybiBhIGRldGVjdGlvbiBJRCBmb3Igc3RhY2sgJHtzdGFja05hbWV9YCk7XG4gICAgfVxuICAgIHJldHVybiByZXNwb25zZS5TdGFja0RyaWZ0RGV0ZWN0aW9uSWQ7XG4gIH0pO1xuXG4gIGNvbnN0IHN0YXR1cyA9IGF3YWl0IGdldERyaWZ0RGV0ZWN0aW9uU3RhdHVzKHN0YWNrTmFtZSwgZGV0ZWN0aW9uSWQsIGNvbnRleHQpO1xuXG4gIGlmIChzdGF0dXMuZGV0ZWN0aW9uU3RhdHVzID09PSAnREVURUNUSU9OX0ZBSUxFRCcpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICBgRHJpZnQgZGV0ZWN0aW9uIGZhaWxlZCBmb3Igc3RhY2sgJHtzdGFja05hbWV9OiAke3N0YXR1cy5kZXRlY3Rpb25TdGF0dXNSZWFzb24gPz8gJ3Vua25vd24gcmVhc29uJ31gLFxuICAgICk7XG4gIH1cblxuICBpZiAoc3RhdHVzLnN0YWNrRHJpZnRTdGF0dXMgIT09ICdEUklGVEVEJykge1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGNvbnN0IHJlc291cmNlRHJpZnRzID0gYXdhaXQgY29udGV4dC5zdGVwKFxuICAgIGBkZXNjcmliZS1yZXNvdXJjZS1kcmlmdHMtJHtzdGFja05hbWV9YCxcbiAgICBhc3luYyAoKSA9PiBnZXRSZXNvdXJjZURyaWZ0cyhzdGFja05hbWUpLFxuICApO1xuXG4gIGF3YWl0IGNvbnRleHQuc3RlcChgcHVibGlzaC1ub3RpZmljYXRpb24tJHtzdGFja05hbWV9YCwgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IHN1YmplY3QgPSBgU3RhY2sgZHJpZnQgZGV0ZWN0ZWQ6ICR7c3RhY2tOYW1lfWAuc2xpY2UoMCwgMTAwKTtcbiAgICBhd2FpdCBzbnMuc2VuZChuZXcgUHVibGlzaENvbW1hbmQoe1xuICAgICAgVG9waWNBcm46IHRvcGljQXJuLFxuICAgICAgU3ViamVjdDogc3ViamVjdCxcbiAgICAgIE1lc3NhZ2U6IEpTT04uc3RyaW5naWZ5KHtcbiAgICAgICAgc3RhY2tOYW1lLFxuICAgICAgICBzdGFja0RyaWZ0U3RhdHVzOiBzdGF0dXMuc3RhY2tEcmlmdFN0YXR1cyxcbiAgICAgICAgZHJpZnRlZFJlc291cmNlczogcmVzb3VyY2VEcmlmdHMsXG4gICAgICB9KSxcbiAgICB9KSk7XG4gIH0pO1xufVxuXG4vKipcbiAqIERpc2NvdmVycyB0YXJnZXQgc3RhY2tzIGFuZCBkZXRlY3RzIGRyaWZ0IHNlcXVlbnRpYWxseS5cbiAqXG4gKiBAcGFyYW0gZXZlbnQgLSBPcHRpb25hbCB0YWcgZmlsdGVyIHVzZWQgdG8gc2VsZWN0IHN0YWNrcy5cbiAqIEBwYXJhbSBjb250ZXh0IC0gRHVyYWJsZSBleGVjdXRpb24gY29udGV4dCBmb3Igc3RlcHMgYW5kIHdhaXRzLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcHJvY2Vzc0RyaWZ0RGV0ZWN0aW9uKFxuICBldmVudDogRHJpZnREZXRlY3Rpb25FdmVudCxcbiAgY29udGV4dDogRHVyYWJsZUNvbnRleHQsXG4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgY29uc3QgdG9waWNBcm4gPSBnZXROb3RpZmljYXRpb25Ub3BpY0FybigpO1xuICBjb25zdCBzdGFja05hbWVzID0gYXdhaXQgY29udGV4dC5zdGVwKCdnZXQtdGFyZ2V0LXN0YWNrLW5hbWVzJywgYXN5bmMgKCkgPT4ge1xuICAgIHJldHVybiBnZXRUYXJnZXRTdGFja05hbWVzKGV2ZW50ID8/IHt9KTtcbiAgfSk7XG5cbiAgZm9yIChjb25zdCBzdGFja05hbWUgb2Ygc3RhY2tOYW1lcykge1xuICAgIGF3YWl0IHByb2Nlc3NTdGFja0RyaWZ0KHN0YWNrTmFtZSwgdG9waWNBcm4sIGNvbnRleHQpO1xuICB9XG59XG5cbi8qKiBEdXJhYmxlIExhbWJkYSBoYW5kbGVyIHdyYXBwaW5nIHtAbGluayBwcm9jZXNzRHJpZnREZXRlY3Rpb259LiAqL1xuZXhwb3J0IGNvbnN0IGhhbmRsZXIgPSB3aXRoRHVyYWJsZUV4ZWN1dGlvbihwcm9jZXNzRHJpZnREZXRlY3Rpb24pO1xuIl19
package/lib/index.d.ts ADDED
@@ -0,0 +1,77 @@
1
+ import { Duration } from 'aws-cdk-lib';
2
+ import * as sns from 'aws-cdk-lib/aws-sns';
3
+ import { Construct } from 'constructs';
4
+ /**
5
+ * Tag filter used to select CloudFormation stacks for drift detection.
6
+ * When `tagValues` is omitted, all stacks that have `tagKey` are selected.
7
+ */
8
+ export interface TargetResource {
9
+ /**
10
+ * Tag key used for stack discovery.
11
+ */
12
+ readonly tagKey: string;
13
+ /**
14
+ * Tag values to match. If omitted, any value for {@link TargetResource.tagKey} is accepted.
15
+ */
16
+ readonly tagValues?: string[];
17
+ }
18
+ /**
19
+ * Properties for {@link CloudformationStackDriftDetector}.
20
+ */
21
+ export interface CloudformationStackDriftDetectorProps {
22
+ /**
23
+ * SNS topic used to notify when a stack has drifted.
24
+ */
25
+ readonly notificationTopic: sns.ITopic;
26
+ /**
27
+ * Tag filter used to select target stacks.
28
+ * If omitted, all stacks in the account and region are inspected.
29
+ */
30
+ readonly targetResource?: TargetResource;
31
+ /**
32
+ * Maximum duration of a durable execution.
33
+ *
34
+ * @default Duration.hours(1)
35
+ */
36
+ readonly executionTimeout?: Duration;
37
+ /**
38
+ * How long durable execution history is retained after completion.
39
+ *
40
+ * @default Duration.days(30)
41
+ */
42
+ readonly retentionPeriod?: Duration;
43
+ }
44
+ /**
45
+ * CDK construct that runs CloudFormation stack drift detection daily and publishes
46
+ * drifted stacks to SNS.
47
+ *
48
+ * Target stacks are selected by {@link TargetResource} when provided. When omitted,
49
+ * every stable stack in the account and region is inspected.
50
+ */
51
+ export declare class CloudformationStackDriftDetector extends Construct {
52
+ /**
53
+ * SNS topic that receives drift notifications.
54
+ */
55
+ readonly notificationTopic: sns.ITopic;
56
+ /**
57
+ * Creates the durable detector Lambda, IAM policies, and daily EventBridge rule.
58
+ *
59
+ * @param scope - Parent construct.
60
+ * @param id - Construct id.
61
+ * @param props - Notification topic, optional tag filter, and durable execution settings.
62
+ */
63
+ constructor(scope: Construct, id: string, props: CloudformationStackDriftDetectorProps);
64
+ /**
65
+ * Returns the CloudFormation stack ARN pattern used for drift-detection IAM grants.
66
+ *
67
+ * @returns A single ARN that matches all stacks in the current account and region.
68
+ */
69
+ private getStackArns;
70
+ /**
71
+ * Builds the EventBridge target payload passed to the detector Lambda.
72
+ *
73
+ * @param targetResource - Optional tag filter. When omitted, an empty payload selects all stacks.
74
+ * @returns Tag key and values for the Lambda event, or an empty object.
75
+ */
76
+ private getEventInput;
77
+ }
package/lib/index.js ADDED
@@ -0,0 +1,151 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.CloudformationStackDriftDetector = void 0;
37
+ const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
38
+ const aws_cdk_lib_1 = require("aws-cdk-lib");
39
+ const events = __importStar(require("aws-cdk-lib/aws-events"));
40
+ const targets = __importStar(require("aws-cdk-lib/aws-events-targets"));
41
+ const iam = __importStar(require("aws-cdk-lib/aws-iam"));
42
+ const lambda = __importStar(require("aws-cdk-lib/aws-lambda"));
43
+ const constructs_1 = require("constructs");
44
+ const detector_function_1 = require("./funcs/detector-function");
45
+ /**
46
+ * CDK construct that runs CloudFormation stack drift detection daily and publishes
47
+ * drifted stacks to SNS.
48
+ *
49
+ * Target stacks are selected by {@link TargetResource} when provided. When omitted,
50
+ * every stable stack in the account and region is inspected.
51
+ */
52
+ class CloudformationStackDriftDetector extends constructs_1.Construct {
53
+ static [JSII_RTTI_SYMBOL_1] = { fqn: "cloudformation-stack-drift-detector.CloudformationStackDriftDetector", version: "0.1.1" };
54
+ /**
55
+ * SNS topic that receives drift notifications.
56
+ */
57
+ notificationTopic;
58
+ /**
59
+ * Creates the durable detector Lambda, IAM policies, and daily EventBridge rule.
60
+ *
61
+ * @param scope - Parent construct.
62
+ * @param id - Construct id.
63
+ * @param props - Notification topic, optional tag filter, and durable execution settings.
64
+ */
65
+ constructor(scope, id, props) {
66
+ super(scope, id);
67
+ this.notificationTopic = props.notificationTopic;
68
+ const durableFunction = new detector_function_1.DetectorFunction(this, 'Function', {
69
+ environment: {
70
+ NOTIFICATION_TOPIC_ARN: this.notificationTopic.topicArn,
71
+ },
72
+ durableConfig: {
73
+ executionTimeout: props.executionTimeout ?? aws_cdk_lib_1.Duration.hours(1),
74
+ retentionPeriod: props.retentionPeriod ?? aws_cdk_lib_1.Duration.days(30),
75
+ },
76
+ timeout: aws_cdk_lib_1.Duration.minutes(15),
77
+ });
78
+ durableFunction.role?.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicDurableExecutionRolePolicy'));
79
+ durableFunction.addToRolePolicy(new iam.PolicyStatement({
80
+ actions: [
81
+ 'cloudformation:DetectStackDrift',
82
+ 'cloudformation:DescribeStackResourceDrifts',
83
+ ],
84
+ resources: this.getStackArns(),
85
+ }));
86
+ durableFunction.addToRolePolicy(new iam.PolicyStatement({
87
+ actions: [
88
+ 'cloudformation:DescribeStackDriftDetectionStatus',
89
+ 'cloudformation:ListStacks',
90
+ ],
91
+ resources: ['*'],
92
+ }));
93
+ durableFunction.addToRolePolicy(new iam.PolicyStatement({
94
+ actions: [
95
+ 'tag:GetResources',
96
+ ],
97
+ resources: ['*'],
98
+ }));
99
+ this.notificationTopic.grantPublish(durableFunction);
100
+ const alias = new lambda.Alias(this, 'Live', {
101
+ aliasName: 'live',
102
+ version: durableFunction.currentVersion,
103
+ });
104
+ new events.Rule(this, 'Schedule', {
105
+ schedule: events.Schedule.rate(aws_cdk_lib_1.Duration.days(1)),
106
+ targets: [
107
+ new targets.LambdaFunction(alias, {
108
+ event: events.RuleTargetInput.fromObject(this.getEventInput(props.targetResource)),
109
+ }),
110
+ ],
111
+ });
112
+ }
113
+ /**
114
+ * Returns the CloudFormation stack ARN pattern used for drift-detection IAM grants.
115
+ *
116
+ * @returns A single ARN that matches all stacks in the current account and region.
117
+ */
118
+ getStackArns() {
119
+ const stack = aws_cdk_lib_1.Stack.of(this);
120
+ return [
121
+ stack.formatArn({
122
+ service: 'cloudformation',
123
+ resource: 'stack',
124
+ resourceName: '*/*',
125
+ arnFormat: aws_cdk_lib_1.ArnFormat.SLASH_RESOURCE_NAME,
126
+ }),
127
+ ];
128
+ }
129
+ /**
130
+ * Builds the EventBridge target payload passed to the detector Lambda.
131
+ *
132
+ * @param targetResource - Optional tag filter. When omitted, an empty payload selects all stacks.
133
+ * @returns Tag key and values for the Lambda event, or an empty object.
134
+ */
135
+ getEventInput(targetResource) {
136
+ if (!targetResource) {
137
+ return {};
138
+ }
139
+ if (targetResource.tagValues && targetResource.tagValues.length > 0) {
140
+ return {
141
+ tagKey: targetResource.tagKey,
142
+ tagValues: targetResource.tagValues,
143
+ };
144
+ }
145
+ return {
146
+ tagKey: targetResource.tagKey,
147
+ };
148
+ }
149
+ }
150
+ exports.CloudformationStackDriftDetector = CloudformationStackDriftDetector;
151
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLDZDQUF5RDtBQUN6RCwrREFBaUQ7QUFDakQsd0VBQTBEO0FBQzFELHlEQUEyQztBQUMzQywrREFBaUQ7QUFFakQsMkNBQXVDO0FBQ3ZDLGlFQUE2RDtBQTRDN0Q7Ozs7OztHQU1HO0FBQ0gsTUFBYSxnQ0FBaUMsU0FBUSxzQkFBUzs7SUFDN0Q7O09BRUc7SUFDTSxpQkFBaUIsQ0FBYTtJQUV2Qzs7Ozs7O09BTUc7SUFDSCxZQUFZLEtBQWdCLEVBQUUsRUFBVSxFQUFFLEtBQTRDO1FBQ3BGLEtBQUssQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFFakIsSUFBSSxDQUFDLGlCQUFpQixHQUFHLEtBQUssQ0FBQyxpQkFBaUIsQ0FBQztRQUVqRCxNQUFNLGVBQWUsR0FBRyxJQUFJLG9DQUFnQixDQUFDLElBQUksRUFBRSxVQUFVLEVBQUU7WUFDN0QsV0FBVyxFQUFFO2dCQUNYLHNCQUFzQixFQUFFLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxRQUFRO2FBQ3hEO1lBQ0QsYUFBYSxFQUFFO2dCQUNiLGdCQUFnQixFQUFFLEtBQUssQ0FBQyxnQkFBZ0IsSUFBSSxzQkFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7Z0JBQzdELGVBQWUsRUFBRSxLQUFLLENBQUMsZUFBZSxJQUFJLHNCQUFRLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQzthQUM1RDtZQUNELE9BQU8sRUFBRSxzQkFBUSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7U0FDOUIsQ0FBQyxDQUFDO1FBRUgsZUFBZSxDQUFDLElBQUksRUFBRSxnQkFBZ0IsQ0FDcEMsR0FBRyxDQUFDLGFBQWEsQ0FBQyx3QkFBd0IsQ0FBQyx1REFBdUQsQ0FBQyxDQUNwRyxDQUFDO1FBRUYsZUFBZSxDQUFDLGVBQWUsQ0FBQyxJQUFJLEdBQUcsQ0FBQyxlQUFlLENBQUM7WUFDdEQsT0FBTyxFQUFFO2dCQUNQLGlDQUFpQztnQkFDakMsNENBQTRDO2FBQzdDO1lBQ0QsU0FBUyxFQUFFLElBQUksQ0FBQyxZQUFZLEVBQUU7U0FDL0IsQ0FBQyxDQUFDLENBQUM7UUFFSixlQUFlLENBQUMsZUFBZSxDQUFDLElBQUksR0FBRyxDQUFDLGVBQWUsQ0FBQztZQUN0RCxPQUFPLEVBQUU7Z0JBQ1Asa0RBQWtEO2dCQUNsRCwyQkFBMkI7YUFDNUI7WUFDRCxTQUFTLEVBQUUsQ0FBQyxHQUFHLENBQUM7U0FDakIsQ0FBQyxDQUFDLENBQUM7UUFFSixlQUFlLENBQUMsZUFBZSxDQUFDLElBQUksR0FBRyxDQUFDLGVBQWUsQ0FBQztZQUN0RCxPQUFPLEVBQUU7Z0JBQ1Asa0JBQWtCO2FBQ25CO1lBQ0QsU0FBUyxFQUFFLENBQUMsR0FBRyxDQUFDO1NBQ2pCLENBQUMsQ0FBQyxDQUFDO1FBRUosSUFBSSxDQUFDLGlCQUFpQixDQUFDLFlBQVksQ0FBQyxlQUFlLENBQUMsQ0FBQztRQUVyRCxNQUFNLEtBQUssR0FBRyxJQUFJLE1BQU0sQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFLE1BQU0sRUFBRTtZQUMzQyxTQUFTLEVBQUUsTUFBTTtZQUNqQixPQUFPLEVBQUUsZUFBZSxDQUFDLGNBQWM7U0FDeEMsQ0FBQyxDQUFDO1FBRUgsSUFBSSxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxVQUFVLEVBQUU7WUFDaEMsUUFBUSxFQUFFLE1BQU0sQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLHNCQUFRLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ2hELE9BQU8sRUFBRTtnQkFDUCxJQUFJLE9BQU8sQ0FBQyxjQUFjLENBQUMsS0FBSyxFQUFFO29CQUNoQyxLQUFLLEVBQUUsTUFBTSxDQUFDLGVBQWUsQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQUMsY0FBYyxDQUFDLENBQUM7aUJBQ25GLENBQUM7YUFDSDtTQUNGLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRDs7OztPQUlHO0lBQ0ssWUFBWTtRQUNsQixNQUFNLEtBQUssR0FBRyxtQkFBSyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUM3QixPQUFPO1lBQ0wsS0FBSyxDQUFDLFNBQVMsQ0FBQztnQkFDZCxPQUFPLEVBQUUsZ0JBQWdCO2dCQUN6QixRQUFRLEVBQUUsT0FBTztnQkFDakIsWUFBWSxFQUFFLEtBQUs7Z0JBQ25CLFNBQVMsRUFBRSx1QkFBUyxDQUFDLG1CQUFtQjthQUN6QyxDQUFDO1NBQ0gsQ0FBQztJQUNKLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNLLGFBQWEsQ0FBQyxjQUErQjtRQUNuRCxJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7WUFDcEIsT0FBTyxFQUFFLENBQUM7UUFDWixDQUFDO1FBQ0QsSUFBSSxjQUFjLENBQUMsU0FBUyxJQUFJLGNBQWMsQ0FBQyxTQUFTLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO1lBQ3BFLE9BQU87Z0JBQ0wsTUFBTSxFQUFFLGNBQWMsQ0FBQyxNQUFNO2dCQUM3QixTQUFTLEVBQUUsY0FBYyxDQUFDLFNBQVM7YUFDcEMsQ0FBQztRQUNKLENBQUM7UUFDRCxPQUFPO1lBQ0wsTUFBTSxFQUFFLGNBQWMsQ0FBQyxNQUFNO1NBQzlCLENBQUM7SUFDSixDQUFDOztBQTdHSCw0RUE4R0MiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBBcm5Gb3JtYXQsIER1cmF0aW9uLCBTdGFjayB9IGZyb20gJ2F3cy1jZGstbGliJztcbmltcG9ydCAqIGFzIGV2ZW50cyBmcm9tICdhd3MtY2RrLWxpYi9hd3MtZXZlbnRzJztcbmltcG9ydCAqIGFzIHRhcmdldHMgZnJvbSAnYXdzLWNkay1saWIvYXdzLWV2ZW50cy10YXJnZXRzJztcbmltcG9ydCAqIGFzIGlhbSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtaWFtJztcbmltcG9ydCAqIGFzIGxhbWJkYSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtbGFtYmRhJztcbmltcG9ydCAqIGFzIHNucyBmcm9tICdhd3MtY2RrLWxpYi9hd3Mtc25zJztcbmltcG9ydCB7IENvbnN0cnVjdCB9IGZyb20gJ2NvbnN0cnVjdHMnO1xuaW1wb3J0IHsgRGV0ZWN0b3JGdW5jdGlvbiB9IGZyb20gJy4vZnVuY3MvZGV0ZWN0b3ItZnVuY3Rpb24nO1xuXG4vKipcbiAqIFRhZyBmaWx0ZXIgdXNlZCB0byBzZWxlY3QgQ2xvdWRGb3JtYXRpb24gc3RhY2tzIGZvciBkcmlmdCBkZXRlY3Rpb24uXG4gKiBXaGVuIGB0YWdWYWx1ZXNgIGlzIG9taXR0ZWQsIGFsbCBzdGFja3MgdGhhdCBoYXZlIGB0YWdLZXlgIGFyZSBzZWxlY3RlZC5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBUYXJnZXRSZXNvdXJjZSB7XG4gIC8qKlxuICAgKiBUYWcga2V5IHVzZWQgZm9yIHN0YWNrIGRpc2NvdmVyeS5cbiAgICovXG4gIHJlYWRvbmx5IHRhZ0tleTogc3RyaW5nO1xuICAvKipcbiAgICogVGFnIHZhbHVlcyB0byBtYXRjaC4gSWYgb21pdHRlZCwgYW55IHZhbHVlIGZvciB7QGxpbmsgVGFyZ2V0UmVzb3VyY2UudGFnS2V5fSBpcyBhY2NlcHRlZC5cbiAgICovXG4gIHJlYWRvbmx5IHRhZ1ZhbHVlcz86IHN0cmluZ1tdO1xufVxuXG4vKipcbiAqIFByb3BlcnRpZXMgZm9yIHtAbGluayBDbG91ZGZvcm1hdGlvblN0YWNrRHJpZnREZXRlY3Rvcn0uXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQ2xvdWRmb3JtYXRpb25TdGFja0RyaWZ0RGV0ZWN0b3JQcm9wcyB7XG4gIC8qKlxuICAgKiBTTlMgdG9waWMgdXNlZCB0byBub3RpZnkgd2hlbiBhIHN0YWNrIGhhcyBkcmlmdGVkLlxuICAgKi9cbiAgcmVhZG9ubHkgbm90aWZpY2F0aW9uVG9waWM6IHNucy5JVG9waWM7XG4gIC8qKlxuICAgKiBUYWcgZmlsdGVyIHVzZWQgdG8gc2VsZWN0IHRhcmdldCBzdGFja3MuXG4gICAqIElmIG9taXR0ZWQsIGFsbCBzdGFja3MgaW4gdGhlIGFjY291bnQgYW5kIHJlZ2lvbiBhcmUgaW5zcGVjdGVkLlxuICAgKi9cbiAgcmVhZG9ubHkgdGFyZ2V0UmVzb3VyY2U/OiBUYXJnZXRSZXNvdXJjZTtcbiAgLyoqXG4gICAqIE1heGltdW0gZHVyYXRpb24gb2YgYSBkdXJhYmxlIGV4ZWN1dGlvbi5cbiAgICpcbiAgICogQGRlZmF1bHQgRHVyYXRpb24uaG91cnMoMSlcbiAgICovXG4gIHJlYWRvbmx5IGV4ZWN1dGlvblRpbWVvdXQ/OiBEdXJhdGlvbjtcbiAgLyoqXG4gICAqIEhvdyBsb25nIGR1cmFibGUgZXhlY3V0aW9uIGhpc3RvcnkgaXMgcmV0YWluZWQgYWZ0ZXIgY29tcGxldGlvbi5cbiAgICpcbiAgICogQGRlZmF1bHQgRHVyYXRpb24uZGF5cygzMClcbiAgICovXG4gIHJlYWRvbmx5IHJldGVudGlvblBlcmlvZD86IER1cmF0aW9uO1xufVxuXG4vKipcbiAqIENESyBjb25zdHJ1Y3QgdGhhdCBydW5zIENsb3VkRm9ybWF0aW9uIHN0YWNrIGRyaWZ0IGRldGVjdGlvbiBkYWlseSBhbmQgcHVibGlzaGVzXG4gKiBkcmlmdGVkIHN0YWNrcyB0byBTTlMuXG4gKlxuICogVGFyZ2V0IHN0YWNrcyBhcmUgc2VsZWN0ZWQgYnkge0BsaW5rIFRhcmdldFJlc291cmNlfSB3aGVuIHByb3ZpZGVkLiBXaGVuIG9taXR0ZWQsXG4gKiBldmVyeSBzdGFibGUgc3RhY2sgaW4gdGhlIGFjY291bnQgYW5kIHJlZ2lvbiBpcyBpbnNwZWN0ZWQuXG4gKi9cbmV4cG9ydCBjbGFzcyBDbG91ZGZvcm1hdGlvblN0YWNrRHJpZnREZXRlY3RvciBleHRlbmRzIENvbnN0cnVjdCB7XG4gIC8qKlxuICAgKiBTTlMgdG9waWMgdGhhdCByZWNlaXZlcyBkcmlmdCBub3RpZmljYXRpb25zLlxuICAgKi9cbiAgcmVhZG9ubHkgbm90aWZpY2F0aW9uVG9waWM6IHNucy5JVG9waWM7XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgdGhlIGR1cmFibGUgZGV0ZWN0b3IgTGFtYmRhLCBJQU0gcG9saWNpZXMsIGFuZCBkYWlseSBFdmVudEJyaWRnZSBydWxlLlxuICAgKlxuICAgKiBAcGFyYW0gc2NvcGUgLSBQYXJlbnQgY29uc3RydWN0LlxuICAgKiBAcGFyYW0gaWQgLSBDb25zdHJ1Y3QgaWQuXG4gICAqIEBwYXJhbSBwcm9wcyAtIE5vdGlmaWNhdGlvbiB0b3BpYywgb3B0aW9uYWwgdGFnIGZpbHRlciwgYW5kIGR1cmFibGUgZXhlY3V0aW9uIHNldHRpbmdzLlxuICAgKi9cbiAgY29uc3RydWN0b3Ioc2NvcGU6IENvbnN0cnVjdCwgaWQ6IHN0cmluZywgcHJvcHM6IENsb3VkZm9ybWF0aW9uU3RhY2tEcmlmdERldGVjdG9yUHJvcHMpIHtcbiAgICBzdXBlcihzY29wZSwgaWQpO1xuXG4gICAgdGhpcy5ub3RpZmljYXRpb25Ub3BpYyA9IHByb3BzLm5vdGlmaWNhdGlvblRvcGljO1xuXG4gICAgY29uc3QgZHVyYWJsZUZ1bmN0aW9uID0gbmV3IERldGVjdG9yRnVuY3Rpb24odGhpcywgJ0Z1bmN0aW9uJywge1xuICAgICAgZW52aXJvbm1lbnQ6IHtcbiAgICAgICAgTk9USUZJQ0FUSU9OX1RPUElDX0FSTjogdGhpcy5ub3RpZmljYXRpb25Ub3BpYy50b3BpY0FybixcbiAgICAgIH0sXG4gICAgICBkdXJhYmxlQ29uZmlnOiB7XG4gICAgICAgIGV4ZWN1dGlvblRpbWVvdXQ6IHByb3BzLmV4ZWN1dGlvblRpbWVvdXQgPz8gRHVyYXRpb24uaG91cnMoMSksXG4gICAgICAgIHJldGVudGlvblBlcmlvZDogcHJvcHMucmV0ZW50aW9uUGVyaW9kID8/IER1cmF0aW9uLmRheXMoMzApLFxuICAgICAgfSxcbiAgICAgIHRpbWVvdXQ6IER1cmF0aW9uLm1pbnV0ZXMoMTUpLFxuICAgIH0pO1xuXG4gICAgZHVyYWJsZUZ1bmN0aW9uLnJvbGU/LmFkZE1hbmFnZWRQb2xpY3koXG4gICAgICBpYW0uTWFuYWdlZFBvbGljeS5mcm9tQXdzTWFuYWdlZFBvbGljeU5hbWUoJ3NlcnZpY2Utcm9sZS9BV1NMYW1iZGFCYXNpY0R1cmFibGVFeGVjdXRpb25Sb2xlUG9saWN5JyksXG4gICAgKTtcblxuICAgIGR1cmFibGVGdW5jdGlvbi5hZGRUb1JvbGVQb2xpY3kobmV3IGlhbS5Qb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgYWN0aW9uczogW1xuICAgICAgICAnY2xvdWRmb3JtYXRpb246RGV0ZWN0U3RhY2tEcmlmdCcsXG4gICAgICAgICdjbG91ZGZvcm1hdGlvbjpEZXNjcmliZVN0YWNrUmVzb3VyY2VEcmlmdHMnLFxuICAgICAgXSxcbiAgICAgIHJlc291cmNlczogdGhpcy5nZXRTdGFja0FybnMoKSxcbiAgICB9KSk7XG5cbiAgICBkdXJhYmxlRnVuY3Rpb24uYWRkVG9Sb2xlUG9saWN5KG5ldyBpYW0uUG9saWN5U3RhdGVtZW50KHtcbiAgICAgIGFjdGlvbnM6IFtcbiAgICAgICAgJ2Nsb3VkZm9ybWF0aW9uOkRlc2NyaWJlU3RhY2tEcmlmdERldGVjdGlvblN0YXR1cycsXG4gICAgICAgICdjbG91ZGZvcm1hdGlvbjpMaXN0U3RhY2tzJyxcbiAgICAgIF0sXG4gICAgICByZXNvdXJjZXM6IFsnKiddLFxuICAgIH0pKTtcblxuICAgIGR1cmFibGVGdW5jdGlvbi5hZGRUb1JvbGVQb2xpY3kobmV3IGlhbS5Qb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgYWN0aW9uczogW1xuICAgICAgICAndGFnOkdldFJlc291cmNlcycsXG4gICAgICBdLFxuICAgICAgcmVzb3VyY2VzOiBbJyonXSxcbiAgICB9KSk7XG5cbiAgICB0aGlzLm5vdGlmaWNhdGlvblRvcGljLmdyYW50UHVibGlzaChkdXJhYmxlRnVuY3Rpb24pO1xuXG4gICAgY29uc3QgYWxpYXMgPSBuZXcgbGFtYmRhLkFsaWFzKHRoaXMsICdMaXZlJywge1xuICAgICAgYWxpYXNOYW1lOiAnbGl2ZScsXG4gICAgICB2ZXJzaW9uOiBkdXJhYmxlRnVuY3Rpb24uY3VycmVudFZlcnNpb24sXG4gICAgfSk7XG5cbiAgICBuZXcgZXZlbnRzLlJ1bGUodGhpcywgJ1NjaGVkdWxlJywge1xuICAgICAgc2NoZWR1bGU6IGV2ZW50cy5TY2hlZHVsZS5yYXRlKER1cmF0aW9uLmRheXMoMSkpLFxuICAgICAgdGFyZ2V0czogW1xuICAgICAgICBuZXcgdGFyZ2V0cy5MYW1iZGFGdW5jdGlvbihhbGlhcywge1xuICAgICAgICAgIGV2ZW50OiBldmVudHMuUnVsZVRhcmdldElucHV0LmZyb21PYmplY3QodGhpcy5nZXRFdmVudElucHV0KHByb3BzLnRhcmdldFJlc291cmNlKSksXG4gICAgICAgIH0pLFxuICAgICAgXSxcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHRoZSBDbG91ZEZvcm1hdGlvbiBzdGFjayBBUk4gcGF0dGVybiB1c2VkIGZvciBkcmlmdC1kZXRlY3Rpb24gSUFNIGdyYW50cy5cbiAgICpcbiAgICogQHJldHVybnMgQSBzaW5nbGUgQVJOIHRoYXQgbWF0Y2hlcyBhbGwgc3RhY2tzIGluIHRoZSBjdXJyZW50IGFjY291bnQgYW5kIHJlZ2lvbi5cbiAgICovXG4gIHByaXZhdGUgZ2V0U3RhY2tBcm5zKCk6IHN0cmluZ1tdIHtcbiAgICBjb25zdCBzdGFjayA9IFN0YWNrLm9mKHRoaXMpO1xuICAgIHJldHVybiBbXG4gICAgICBzdGFjay5mb3JtYXRBcm4oe1xuICAgICAgICBzZXJ2aWNlOiAnY2xvdWRmb3JtYXRpb24nLFxuICAgICAgICByZXNvdXJjZTogJ3N0YWNrJyxcbiAgICAgICAgcmVzb3VyY2VOYW1lOiAnKi8qJyxcbiAgICAgICAgYXJuRm9ybWF0OiBBcm5Gb3JtYXQuU0xBU0hfUkVTT1VSQ0VfTkFNRSxcbiAgICAgIH0pLFxuICAgIF07XG4gIH1cblxuICAvKipcbiAgICogQnVpbGRzIHRoZSBFdmVudEJyaWRnZSB0YXJnZXQgcGF5bG9hZCBwYXNzZWQgdG8gdGhlIGRldGVjdG9yIExhbWJkYS5cbiAgICpcbiAgICogQHBhcmFtIHRhcmdldFJlc291cmNlIC0gT3B0aW9uYWwgdGFnIGZpbHRlci4gV2hlbiBvbWl0dGVkLCBhbiBlbXB0eSBwYXlsb2FkIHNlbGVjdHMgYWxsIHN0YWNrcy5cbiAgICogQHJldHVybnMgVGFnIGtleSBhbmQgdmFsdWVzIGZvciB0aGUgTGFtYmRhIGV2ZW50LCBvciBhbiBlbXB0eSBvYmplY3QuXG4gICAqL1xuICBwcml2YXRlIGdldEV2ZW50SW5wdXQodGFyZ2V0UmVzb3VyY2U/OiBUYXJnZXRSZXNvdXJjZSk6IHsgdGFnS2V5Pzogc3RyaW5nOyB0YWdWYWx1ZXM/OiBzdHJpbmdbXSB9IHtcbiAgICBpZiAoIXRhcmdldFJlc291cmNlKSB7XG4gICAgICByZXR1cm4ge307XG4gICAgfVxuICAgIGlmICh0YXJnZXRSZXNvdXJjZS50YWdWYWx1ZXMgJiYgdGFyZ2V0UmVzb3VyY2UudGFnVmFsdWVzLmxlbmd0aCA+IDApIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHRhZ0tleTogdGFyZ2V0UmVzb3VyY2UudGFnS2V5LFxuICAgICAgICB0YWdWYWx1ZXM6IHRhcmdldFJlc291cmNlLnRhZ1ZhbHVlcyxcbiAgICAgIH07XG4gICAgfVxuICAgIHJldHVybiB7XG4gICAgICB0YWdLZXk6IHRhcmdldFJlc291cmNlLnRhZ0tleSxcbiAgICB9O1xuICB9XG59XG4iXX0=