@aligent/cdk-header-change-detection 1.6.1 → 1.7.0

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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ # @aligent/cdk-header-change-detection
2
+
3
+ ## 1.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#1512](https://github.com/aligent/cdk-constructs/pull/1512) [`a9a6231`](https://github.com/aligent/cdk-constructs/commit/a9a62319e4528ac2d23f3af96e96cb2427f242f8) Thanks [@TheOrangePuff](https://github.com/TheOrangePuff)! - Adds changeset package to handle release management
package/README.md ADDED
@@ -0,0 +1,65 @@
1
+ # Aligent Header Change Detection Service
2
+
3
+ ## Overview
4
+
5
+ Creates a Lambda function that periodically scans security headers and sends the results to SNS.
6
+
7
+ ### Diagram
8
+
9
+ ![diagram](docs/diagram.jpg)
10
+
11
+ This service aims to comply with PCI DSS to cover the requirements outlined by section 11.6.1.
12
+
13
+ **11.6.1**: A change- and tamper-detection mechanism is deployed as follows:
14
+
15
+ > - To alert personnel to unauthorized modification (including indicators of compromise, changes, additions, and deletions) to the security-impacting HTTP headers and the script contents of payment pages as received by the consumer browser.
16
+ > - The mechanism is configured to evaluate the received HTTP headers and payment pages.
17
+ > - The mechanism functions are performed as follows:
18
+ > - At least weekly
19
+ > OR
20
+ > - Periodically (at the frequency defined in the entity’s targeted risk analysis, which is performed according to all elements specified in Requirement 12.3.1)
21
+
22
+ ## Default config
23
+
24
+ By default, the following headers are monitored:
25
+
26
+ - Content-Security-Policy
27
+ - Content-Security-Policy-Report-Only
28
+ - Reporting-Endpoints
29
+ - Strict-Transport-Security
30
+ - X-Frame-Options
31
+ - X-Content-Type-Options
32
+ - Cross-Origin-Opener-Policy
33
+ - Cross-Origin-Embedder-Policy
34
+ - Cross-Origin-Resource-Policy
35
+ - Referrer-Policy
36
+ - Permission-Policy
37
+ - Cache-Control
38
+
39
+ ## Usage
40
+
41
+ To include this in your CDK stack, add the following:
42
+
43
+ ```typescript
44
+ // Import required packages
45
+ import { SnsTopic } from "aws-cdk-lib/aws-events-targets";
46
+ import { Topic } from "aws-cdk-lib/aws-sns";
47
+ import { HeaderChangeDetection } from "@aligent/cdk-header-change-detection";
48
+
49
+ // Create a new SNS topic
50
+ const topic = new Topic(this, "Topic");
51
+ const snsTopic = new SnsTopic(topic);
52
+
53
+ // Pass the required props
54
+ new HeaderChangeDetection(this, "HeaderChangeDetection", { snsTopic });
55
+ ```
56
+
57
+ ## Local development
58
+
59
+ [NPM link](https://docs.npmjs.com/cli/v7/commands/npm-link) can be used to develop the module locally.
60
+
61
+ 1. Pull this repository locally
62
+ 2. `cd` into this repository
63
+ 3. run `npm link`
64
+ 4. `cd` into the downstream repo (target project, etc) and run `npm link '@aligent/cdk-header-change-detection'`
65
+ The downstream repository should now include a symlink to this module. Allowing local changes to be tested before pushing. You may want to update the version notation of the package in the downstream repository's `package.json`.
Binary file
package/index.ts ADDED
@@ -0,0 +1,6 @@
1
+ import {
2
+ HeaderChangeDetection,
3
+ HeaderChangeDetectionProps,
4
+ } from "./lib/header-change-detection";
5
+
6
+ export { HeaderChangeDetection, HeaderChangeDetectionProps };
package/jest.config.ts ADDED
@@ -0,0 +1,11 @@
1
+ /* eslint-disable */
2
+ export default {
3
+ displayName: "header-change-detection",
4
+ preset: "../../jest.preset.js",
5
+ testEnvironment: "node",
6
+ transform: {
7
+ "^.+\\.[tj]s$": ["ts-jest", { tsconfig: "<rootDir>/tsconfig.spec.json" }],
8
+ },
9
+ moduleFileExtensions: ["ts", "js", "html"],
10
+ coverageDirectory: "../../coverage/packages/header-change-detection",
11
+ };
@@ -0,0 +1,133 @@
1
+ import { DockerImage, Duration } from "aws-cdk-lib";
2
+ import { AttributeType, BillingMode, Table } from "aws-cdk-lib/aws-dynamodb";
3
+ import { Rule, RuleProps, Schedule } from "aws-cdk-lib/aws-events";
4
+ import { LambdaFunction, SnsTopic } from "aws-cdk-lib/aws-events-targets";
5
+ import { Architecture, Code, Function, Runtime } from "aws-cdk-lib/aws-lambda";
6
+ import { Construct } from "constructs";
7
+ import { join } from "path";
8
+ import { Esbuild } from "@aligent/cdk-esbuild";
9
+
10
+ export interface HeaderChangeDetectionProps {
11
+ /**
12
+ * List of URLs to monitor for header changes
13
+ */
14
+ urls: string[];
15
+
16
+ /**
17
+ * Optional list of additional headers to monitor
18
+ *
19
+ * @default []
20
+ */
21
+ additionalHeaders?: string[];
22
+
23
+ /**
24
+ * Optionally disable all the default headers
25
+ *
26
+ * @default false
27
+ */
28
+ disableDefaults?: boolean;
29
+
30
+ /**
31
+ * SNS Topic to send change detection notifications to
32
+ */
33
+ snsTopic: SnsTopic;
34
+
35
+ /**
36
+ * The schedule for performing the header check
37
+ *
38
+ * @default Schedule.rate(Duration.hours(1))
39
+ */
40
+ schedule?: Schedule;
41
+
42
+ /**
43
+ * Optionally pass any rule properties
44
+ */
45
+ ruleProps?: Partial<RuleProps>;
46
+
47
+ /**
48
+ * Optionally accept HTTP status codes other than 200
49
+ *
50
+ * @default ["200"]
51
+ */
52
+ acceptedHttpStatus?: string[];
53
+
54
+ /**
55
+ * For extended Lambda timeout. Default: 10 seconds
56
+ */
57
+ lambdaTimeout?: Duration;
58
+ }
59
+
60
+ const command = [
61
+ "sh",
62
+ "-c",
63
+ 'echo "Docker build not supported. Please install esbuild."',
64
+ ];
65
+
66
+ const defaultHeaders = [
67
+ "content-security-policy",
68
+ "content-security-policy-report-only",
69
+ "reporting-endpoints",
70
+ "strict-transport-security",
71
+ "x-frame-options",
72
+ "x-content-type-options",
73
+ "cross-origin-opener-policy",
74
+ "cross-origin-embedder-policy",
75
+ "cross-origin-resource-policy",
76
+ "referrer-policy",
77
+ "permission-policy",
78
+ "cache-control",
79
+ ];
80
+
81
+ export class HeaderChangeDetection extends Construct {
82
+ constructor(scope: Construct, id: string, props: HeaderChangeDetectionProps) {
83
+ super(scope, id);
84
+
85
+ const headers = props.disableDefaults ? [] : defaultHeaders;
86
+
87
+ headers.push(
88
+ ...(props.additionalHeaders?.map(header => header.toLowerCase()) || [])
89
+ );
90
+
91
+ const table = new Table(this, "Table", {
92
+ partitionKey: {
93
+ name: "Url",
94
+ type: AttributeType.STRING,
95
+ },
96
+ billingMode: BillingMode.PAY_PER_REQUEST,
97
+ });
98
+
99
+ const schedule = new Rule(this, "EventRule", {
100
+ schedule: props.schedule || Schedule.rate(Duration.hours(1)),
101
+ ...props.ruleProps,
102
+ });
103
+
104
+ const lambda = new Function(this, "HeaderCheck", {
105
+ architecture: Architecture.X86_64,
106
+ runtime: Runtime.NODEJS_22_X,
107
+ handler: "header-check.handler",
108
+ timeout: props.lambdaTimeout || Duration.seconds(10),
109
+ code: Code.fromAsset(join(__dirname, "lambda"), {
110
+ bundling: {
111
+ command,
112
+ image: DockerImage.fromRegistry("busybox"),
113
+ local: new Esbuild({
114
+ entryPoints: [join(__dirname, "lambda/header-check.ts")],
115
+ }),
116
+ },
117
+ }),
118
+ environment: {
119
+ URLS: props.urls.join(","),
120
+ HEADERS: headers.join(","),
121
+ TABLE: table.tableName,
122
+ TOPIC_ARN: props.snsTopic.topic.topicArn,
123
+ ACCEPTED_HTTP_STATUS: (props.acceptedHttpStatus || ["200"]).join(","),
124
+ },
125
+ });
126
+
127
+ schedule.addTarget(new LambdaFunction(lambda));
128
+
129
+ table.grantWriteData(lambda);
130
+ table.grantReadData(lambda);
131
+ props.snsTopic.topic.grantPublish(lambda);
132
+ }
133
+ }
package/package.json CHANGED
@@ -1,35 +1,39 @@
1
1
  {
2
2
  "name": "@aligent/cdk-header-change-detection",
3
+ "version": "1.7.0",
3
4
  "main": "index.js",
4
5
  "license": "MIT",
5
- "homepage": "https://github.com/aligent/cdk-constructs/packages/header-change-detection-stack#readme",
6
+ "homepage": "https://github.com/aligent/aws-cdk-constructs/tree/main/packages/header-change-detection#readme",
6
7
  "repository": {
7
8
  "type": "git",
8
- "url": "https://github.com/aligent/aws-cdk-header-change-detection-stack"
9
+ "url": "git+https://github.com/aligent/aws-cdk-constructs.git"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/aligent/aws-cdk-constructs/issues"
9
13
  },
10
14
  "types": "index.d.ts",
11
15
  "scripts": {
12
- "build": "tsc"
16
+ "build": "tsc",
17
+ "test": "npx nx test header-change-detection",
18
+ "lint": "npx nx lint header-change-detection"
13
19
  },
14
20
  "devDependencies": {
15
21
  "@types/jest": "^29.5.10",
16
22
  "@types/node": "^20.6.3",
17
- "aws-cdk": "^2.113.0",
23
+ "aws-cdk": "^2.1019.1",
18
24
  "jest": "^29.7.0",
19
25
  "ts-jest": "^29.1.1",
20
26
  "ts-node": "^10.9.1",
21
27
  "typescript": "^5.3.2"
22
28
  },
23
29
  "dependencies": {
24
- "@aws-sdk/client-dynamodb": "^3.726.1",
25
- "@aws-sdk/client-sns": "3.732.0",
30
+ "@aws-sdk/client-dynamodb": "^3.830.0",
31
+ "@aws-sdk/client-sns": "3.830.0",
26
32
  "axios": "^1.8.3",
27
33
  "source-map-support": "^0.5.21"
28
34
  },
29
35
  "peerDependencies": {
30
36
  "aws-cdk-lib": "^2.168.0",
31
37
  "constructs": "^10.4.2"
32
- },
33
- "type": "commonjs",
34
- "version": "1.6.1"
35
- }
38
+ }
39
+ }
package/project.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "header-change-detection",
3
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
4
+ "sourceRoot": "packages/header-change-detection/lib",
5
+ "projectType": "application",
6
+ "targets": {
7
+ "build": {
8
+ "executor": "@nx/js:tsc",
9
+ "options": {
10
+ "main": "packages/header-change-detection/index.ts",
11
+ "outputPath": "dist/header-change-detection",
12
+ "tsConfig": "packages/header-change-detection/tsconfig.app.json",
13
+ "assets": [
14
+ "packages/header-change-detection/lib/lambda/**",
15
+ "packages/header-change-detection/README.md",
16
+ "packages/header-change-detection/docs/**"
17
+ ]
18
+ },
19
+ "dependsOn": ["merge-gitignore"]
20
+ },
21
+ "lint": {
22
+ "executor": "@nx/eslint:lint",
23
+ "outputs": ["{options.outputFile}"]
24
+ },
25
+ "test": {
26
+ "executor": "@nx/jest:jest",
27
+ "outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
28
+ "options": {
29
+ "jestConfig": "packages/header-change-detection/jest.config.ts",
30
+ "passWithNoTests": true
31
+ }
32
+ },
33
+ "publish": {
34
+ "command": "node tools/scripts/publish.mjs header-change-detection {args.ver} {args.tag}",
35
+ "dependsOn": ["build"]
36
+ },
37
+ "merge-gitignore": {
38
+ "executor": "nx:run-commands",
39
+ "options": {
40
+ "command": "node tools/scripts/merge-gitignore.mjs header-change-detection"
41
+ }
42
+ }
43
+ },
44
+ "tags": []
45
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "../../dist/out-tsc",
5
+ "module": "commonjs",
6
+ "types": ["node"]
7
+ },
8
+ "exclude": ["./jest.config.ts", "lib/lambda/**/*.ts"],
9
+ "include": ["lib/**/*.ts", "index.ts"]
10
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "files": [],
4
+ "include": [],
5
+ "references": [
6
+ {
7
+ "path": "./tsconfig.app.json"
8
+ },
9
+ {
10
+ "path": "./tsconfig.spec.json"
11
+ }
12
+ ],
13
+ "compilerOptions": {
14
+ "esModuleInterop": true
15
+ }
16
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "../../dist/out-tsc",
5
+ "module": "commonjs",
6
+ "types": ["jest", "node"]
7
+ }
8
+ }
package/index.d.ts DELETED
@@ -1,2 +0,0 @@
1
- import { HeaderChangeDetection, HeaderChangeDetectionProps } from "./lib/header-change-detection";
2
- export { HeaderChangeDetection, HeaderChangeDetectionProps };
package/index.js DELETED
@@ -1,6 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.HeaderChangeDetection = void 0;
4
- const header_change_detection_1 = require("./lib/header-change-detection");
5
- Object.defineProperty(exports, "HeaderChangeDetection", { enumerable: true, get: function () { return header_change_detection_1.HeaderChangeDetection; } });
6
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9wYWNrYWdlcy9oZWFkZXItY2hhbmdlLWRldGVjdGlvbi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwyRUFHdUM7QUFFOUIsc0dBSlAsK0NBQXFCLE9BSU8iLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuICBIZWFkZXJDaGFuZ2VEZXRlY3Rpb24sXG4gIEhlYWRlckNoYW5nZURldGVjdGlvblByb3BzLFxufSBmcm9tIFwiLi9saWIvaGVhZGVyLWNoYW5nZS1kZXRlY3Rpb25cIjtcblxuZXhwb3J0IHsgSGVhZGVyQ2hhbmdlRGV0ZWN0aW9uLCBIZWFkZXJDaGFuZ2VEZXRlY3Rpb25Qcm9wcyB9O1xuIl19
@@ -1,49 +0,0 @@
1
- import { Duration } from "aws-cdk-lib";
2
- import { RuleProps, Schedule } from "aws-cdk-lib/aws-events";
3
- import { SnsTopic } from "aws-cdk-lib/aws-events-targets";
4
- import { Construct } from "constructs";
5
- export interface HeaderChangeDetectionProps {
6
- /**
7
- * List of URLs to monitor for header changes
8
- */
9
- urls: string[];
10
- /**
11
- * Optional list of additional headers to monitor
12
- *
13
- * @default []
14
- */
15
- additionalHeaders?: string[];
16
- /**
17
- * Optionally disable all the default headers
18
- *
19
- * @default false
20
- */
21
- disableDefaults?: boolean;
22
- /**
23
- * SNS Topic to send change detection notifications to
24
- */
25
- snsTopic: SnsTopic;
26
- /**
27
- * The schedule for performing the header check
28
- *
29
- * @default Schedule.rate(Duration.hours(1))
30
- */
31
- schedule?: Schedule;
32
- /**
33
- * Optionally pass any rule properties
34
- */
35
- ruleProps?: Partial<RuleProps>;
36
- /**
37
- * Optionally accept HTTP status codes other than 200
38
- *
39
- * @default ["200"]
40
- */
41
- acceptedHttpStatus?: string[];
42
- /**
43
- * For extended Lambda timeout. Default: 10 seconds
44
- */
45
- lambdaTimeout?: Duration;
46
- }
47
- export declare class HeaderChangeDetection extends Construct {
48
- constructor(scope: Construct, id: string, props: HeaderChangeDetectionProps);
49
- }
@@ -1,77 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.HeaderChangeDetection = void 0;
4
- const aws_cdk_lib_1 = require("aws-cdk-lib");
5
- const aws_dynamodb_1 = require("aws-cdk-lib/aws-dynamodb");
6
- const aws_events_1 = require("aws-cdk-lib/aws-events");
7
- const aws_events_targets_1 = require("aws-cdk-lib/aws-events-targets");
8
- const aws_lambda_1 = require("aws-cdk-lib/aws-lambda");
9
- const constructs_1 = require("constructs");
10
- const path_1 = require("path");
11
- const cdk_esbuild_1 = require("@aligent/cdk-esbuild");
12
- const command = [
13
- "sh",
14
- "-c",
15
- 'echo "Docker build not supported. Please install esbuild."',
16
- ];
17
- const defaultHeaders = [
18
- "content-security-policy",
19
- "content-security-policy-report-only",
20
- "reporting-endpoints",
21
- "strict-transport-security",
22
- "x-frame-options",
23
- "x-content-type-options",
24
- "cross-origin-opener-policy",
25
- "cross-origin-embedder-policy",
26
- "cross-origin-resource-policy",
27
- "referrer-policy",
28
- "permission-policy",
29
- "cache-control",
30
- ];
31
- class HeaderChangeDetection extends constructs_1.Construct {
32
- constructor(scope, id, props) {
33
- var _a;
34
- super(scope, id);
35
- const headers = props.disableDefaults ? [] : defaultHeaders;
36
- headers.push(...(((_a = props.additionalHeaders) === null || _a === void 0 ? void 0 : _a.map(header => header.toLowerCase())) || []));
37
- const table = new aws_dynamodb_1.Table(this, "Table", {
38
- partitionKey: {
39
- name: "Url",
40
- type: aws_dynamodb_1.AttributeType.STRING,
41
- },
42
- billingMode: aws_dynamodb_1.BillingMode.PAY_PER_REQUEST,
43
- });
44
- const schedule = new aws_events_1.Rule(this, "EventRule", {
45
- schedule: props.schedule || aws_events_1.Schedule.rate(aws_cdk_lib_1.Duration.hours(1)),
46
- ...props.ruleProps,
47
- });
48
- const lambda = new aws_lambda_1.Function(this, "HeaderCheck", {
49
- architecture: aws_lambda_1.Architecture.X86_64,
50
- runtime: aws_lambda_1.Runtime.NODEJS_22_X,
51
- handler: "header-check.handler",
52
- timeout: props.lambdaTimeout || aws_cdk_lib_1.Duration.seconds(10),
53
- code: aws_lambda_1.Code.fromAsset((0, path_1.join)(__dirname, "lambda"), {
54
- bundling: {
55
- command,
56
- image: aws_cdk_lib_1.DockerImage.fromRegistry("busybox"),
57
- local: new cdk_esbuild_1.Esbuild({
58
- entryPoints: [(0, path_1.join)(__dirname, "lambda/header-check.ts")],
59
- }),
60
- },
61
- }),
62
- environment: {
63
- URLS: props.urls.join(","),
64
- HEADERS: headers.join(","),
65
- TABLE: table.tableName,
66
- TOPIC_ARN: props.snsTopic.topic.topicArn,
67
- ACCEPTED_HTTP_STATUS: (props.acceptedHttpStatus || ["200"]).join(","),
68
- },
69
- });
70
- schedule.addTarget(new aws_events_targets_1.LambdaFunction(lambda));
71
- table.grantWriteData(lambda);
72
- table.grantReadData(lambda);
73
- props.snsTopic.topic.grantPublish(lambda);
74
- }
75
- }
76
- exports.HeaderChangeDetection = HeaderChangeDetection;
77
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVhZGVyLWNoYW5nZS1kZXRlY3Rpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9wYWNrYWdlcy9oZWFkZXItY2hhbmdlLWRldGVjdGlvbi9saWIvaGVhZGVyLWNoYW5nZS1kZXRlY3Rpb24udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsNkNBQW9EO0FBQ3BELDJEQUE2RTtBQUM3RSx1REFBbUU7QUFDbkUsdUVBQTBFO0FBQzFFLHVEQUErRTtBQUMvRSwyQ0FBdUM7QUFDdkMsK0JBQTRCO0FBQzVCLHNEQUErQztBQW9EL0MsTUFBTSxPQUFPLEdBQUc7SUFDZCxJQUFJO0lBQ0osSUFBSTtJQUNKLDREQUE0RDtDQUM3RCxDQUFDO0FBRUYsTUFBTSxjQUFjLEdBQUc7SUFDckIseUJBQXlCO0lBQ3pCLHFDQUFxQztJQUNyQyxxQkFBcUI7SUFDckIsMkJBQTJCO0lBQzNCLGlCQUFpQjtJQUNqQix3QkFBd0I7SUFDeEIsNEJBQTRCO0lBQzVCLDhCQUE4QjtJQUM5Qiw4QkFBOEI7SUFDOUIsaUJBQWlCO0lBQ2pCLG1CQUFtQjtJQUNuQixlQUFlO0NBQ2hCLENBQUM7QUFFRixNQUFhLHFCQUFzQixTQUFRLHNCQUFTO0lBQ2xELFlBQVksS0FBZ0IsRUFBRSxFQUFVLEVBQUUsS0FBaUM7O1FBQ3pFLEtBQUssQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFFakIsTUFBTSxPQUFPLEdBQUcsS0FBSyxDQUFDLGVBQWUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxjQUFjLENBQUM7UUFFNUQsT0FBTyxDQUFDLElBQUksQ0FDVixHQUFHLENBQUMsQ0FBQSxNQUFBLEtBQUssQ0FBQyxpQkFBaUIsMENBQUUsR0FBRyxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsTUFBTSxDQUFDLFdBQVcsRUFBRSxDQUFDLEtBQUksRUFBRSxDQUFDLENBQ3hFLENBQUM7UUFFRixNQUFNLEtBQUssR0FBRyxJQUFJLG9CQUFLLENBQUMsSUFBSSxFQUFFLE9BQU8sRUFBRTtZQUNyQyxZQUFZLEVBQUU7Z0JBQ1osSUFBSSxFQUFFLEtBQUs7Z0JBQ1gsSUFBSSxFQUFFLDRCQUFhLENBQUMsTUFBTTthQUMzQjtZQUNELFdBQVcsRUFBRSwwQkFBVyxDQUFDLGVBQWU7U0FDekMsQ0FBQyxDQUFDO1FBRUgsTUFBTSxRQUFRLEdBQUcsSUFBSSxpQkFBSSxDQUFDLElBQUksRUFBRSxXQUFXLEVBQUU7WUFDM0MsUUFBUSxFQUFFLEtBQUssQ0FBQyxRQUFRLElBQUkscUJBQVEsQ0FBQyxJQUFJLENBQUMsc0JBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDNUQsR0FBRyxLQUFLLENBQUMsU0FBUztTQUNuQixDQUFDLENBQUM7UUFFSCxNQUFNLE1BQU0sR0FBRyxJQUFJLHFCQUFRLENBQUMsSUFBSSxFQUFFLGFBQWEsRUFBRTtZQUMvQyxZQUFZLEVBQUUseUJBQVksQ0FBQyxNQUFNO1lBQ2pDLE9BQU8sRUFBRSxvQkFBTyxDQUFDLFdBQVc7WUFDNUIsT0FBTyxFQUFFLHNCQUFzQjtZQUMvQixPQUFPLEVBQUUsS0FBSyxDQUFDLGFBQWEsSUFBSSxzQkFBUSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7WUFDcEQsSUFBSSxFQUFFLGlCQUFJLENBQUMsU0FBUyxDQUFDLElBQUEsV0FBSSxFQUFDLFNBQVMsRUFBRSxRQUFRLENBQUMsRUFBRTtnQkFDOUMsUUFBUSxFQUFFO29CQUNSLE9BQU87b0JBQ1AsS0FBSyxFQUFFLHlCQUFXLENBQUMsWUFBWSxDQUFDLFNBQVMsQ0FBQztvQkFDMUMsS0FBSyxFQUFFLElBQUkscUJBQU8sQ0FBQzt3QkFDakIsV0FBVyxFQUFFLENBQUMsSUFBQSxXQUFJLEVBQUMsU0FBUyxFQUFFLHdCQUF3QixDQUFDLENBQUM7cUJBQ3pELENBQUM7aUJBQ0g7YUFDRixDQUFDO1lBQ0YsV0FBVyxFQUFFO2dCQUNYLElBQUksRUFBRSxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUM7Z0JBQzFCLE9BQU8sRUFBRSxPQUFPLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQztnQkFDMUIsS0FBSyxFQUFFLEtBQUssQ0FBQyxTQUFTO2dCQUN0QixTQUFTLEVBQUUsS0FBSyxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsUUFBUTtnQkFDeEMsb0JBQW9CLEVBQUUsQ0FBQyxLQUFLLENBQUMsa0JBQWtCLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUM7YUFDdEU7U0FDRixDQUFDLENBQUM7UUFFSCxRQUFRLENBQUMsU0FBUyxDQUFDLElBQUksbUNBQWMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO1FBRS9DLEtBQUssQ0FBQyxjQUFjLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDN0IsS0FBSyxDQUFDLGFBQWEsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUM1QixLQUFLLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxZQUFZLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDNUMsQ0FBQztDQUNGO0FBcERELHNEQW9EQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IERvY2tlckltYWdlLCBEdXJhdGlvbiB9IGZyb20gXCJhd3MtY2RrLWxpYlwiO1xuaW1wb3J0IHsgQXR0cmlidXRlVHlwZSwgQmlsbGluZ01vZGUsIFRhYmxlIH0gZnJvbSBcImF3cy1jZGstbGliL2F3cy1keW5hbW9kYlwiO1xuaW1wb3J0IHsgUnVsZSwgUnVsZVByb3BzLCBTY2hlZHVsZSB9IGZyb20gXCJhd3MtY2RrLWxpYi9hd3MtZXZlbnRzXCI7XG5pbXBvcnQgeyBMYW1iZGFGdW5jdGlvbiwgU25zVG9waWMgfSBmcm9tIFwiYXdzLWNkay1saWIvYXdzLWV2ZW50cy10YXJnZXRzXCI7XG5pbXBvcnQgeyBBcmNoaXRlY3R1cmUsIENvZGUsIEZ1bmN0aW9uLCBSdW50aW1lIH0gZnJvbSBcImF3cy1jZGstbGliL2F3cy1sYW1iZGFcIjtcbmltcG9ydCB7IENvbnN0cnVjdCB9IGZyb20gXCJjb25zdHJ1Y3RzXCI7XG5pbXBvcnQgeyBqb2luIH0gZnJvbSBcInBhdGhcIjtcbmltcG9ydCB7IEVzYnVpbGQgfSBmcm9tIFwiQGFsaWdlbnQvY2RrLWVzYnVpbGRcIjtcblxuZXhwb3J0IGludGVyZmFjZSBIZWFkZXJDaGFuZ2VEZXRlY3Rpb25Qcm9wcyB7XG4gIC8qKlxuICAgKiBMaXN0IG9mIFVSTHMgdG8gbW9uaXRvciBmb3IgaGVhZGVyIGNoYW5nZXNcbiAgICovXG4gIHVybHM6IHN0cmluZ1tdO1xuXG4gIC8qKlxuICAgKiBPcHRpb25hbCBsaXN0IG9mIGFkZGl0aW9uYWwgaGVhZGVycyB0byBtb25pdG9yXG4gICAqXG4gICAqIEBkZWZhdWx0IFtdXG4gICAqL1xuICBhZGRpdGlvbmFsSGVhZGVycz86IHN0cmluZ1tdO1xuXG4gIC8qKlxuICAgKiBPcHRpb25hbGx5IGRpc2FibGUgYWxsIHRoZSBkZWZhdWx0IGhlYWRlcnNcbiAgICpcbiAgICogQGRlZmF1bHQgZmFsc2VcbiAgICovXG4gIGRpc2FibGVEZWZhdWx0cz86IGJvb2xlYW47XG5cbiAgLyoqXG4gICAqIFNOUyBUb3BpYyB0byBzZW5kIGNoYW5nZSBkZXRlY3Rpb24gbm90aWZpY2F0aW9ucyB0b1xuICAgKi9cbiAgc25zVG9waWM6IFNuc1RvcGljO1xuXG4gIC8qKlxuICAgKiBUaGUgc2NoZWR1bGUgZm9yIHBlcmZvcm1pbmcgdGhlIGhlYWRlciBjaGVja1xuICAgKlxuICAgKiBAZGVmYXVsdCBTY2hlZHVsZS5yYXRlKER1cmF0aW9uLmhvdXJzKDEpKVxuICAgKi9cbiAgc2NoZWR1bGU/OiBTY2hlZHVsZTtcblxuICAvKipcbiAgICogT3B0aW9uYWxseSBwYXNzIGFueSBydWxlIHByb3BlcnRpZXNcbiAgICovXG4gIHJ1bGVQcm9wcz86IFBhcnRpYWw8UnVsZVByb3BzPjtcblxuICAvKipcbiAgICogT3B0aW9uYWxseSBhY2NlcHQgSFRUUCBzdGF0dXMgY29kZXMgb3RoZXIgdGhhbiAyMDBcbiAgICpcbiAgICogQGRlZmF1bHQgW1wiMjAwXCJdXG4gICAqL1xuICBhY2NlcHRlZEh0dHBTdGF0dXM/OiBzdHJpbmdbXTtcblxuICAvKipcbiAgICogRm9yIGV4dGVuZGVkIExhbWJkYSB0aW1lb3V0LiBEZWZhdWx0OiAxMCBzZWNvbmRzXG4gICAqL1xuICBsYW1iZGFUaW1lb3V0PzogRHVyYXRpb247XG59XG5cbmNvbnN0IGNvbW1hbmQgPSBbXG4gIFwic2hcIixcbiAgXCItY1wiLFxuICAnZWNobyBcIkRvY2tlciBidWlsZCBub3Qgc3VwcG9ydGVkLiBQbGVhc2UgaW5zdGFsbCBlc2J1aWxkLlwiJyxcbl07XG5cbmNvbnN0IGRlZmF1bHRIZWFkZXJzID0gW1xuICBcImNvbnRlbnQtc2VjdXJpdHktcG9saWN5XCIsXG4gIFwiY29udGVudC1zZWN1cml0eS1wb2xpY3ktcmVwb3J0LW9ubHlcIixcbiAgXCJyZXBvcnRpbmctZW5kcG9pbnRzXCIsXG4gIFwic3RyaWN0LXRyYW5zcG9ydC1zZWN1cml0eVwiLFxuICBcIngtZnJhbWUtb3B0aW9uc1wiLFxuICBcIngtY29udGVudC10eXBlLW9wdGlvbnNcIixcbiAgXCJjcm9zcy1vcmlnaW4tb3BlbmVyLXBvbGljeVwiLFxuICBcImNyb3NzLW9yaWdpbi1lbWJlZGRlci1wb2xpY3lcIixcbiAgXCJjcm9zcy1vcmlnaW4tcmVzb3VyY2UtcG9saWN5XCIsXG4gIFwicmVmZXJyZXItcG9saWN5XCIsXG4gIFwicGVybWlzc2lvbi1wb2xpY3lcIixcbiAgXCJjYWNoZS1jb250cm9sXCIsXG5dO1xuXG5leHBvcnQgY2xhc3MgSGVhZGVyQ2hhbmdlRGV0ZWN0aW9uIGV4dGVuZHMgQ29uc3RydWN0IHtcbiAgY29uc3RydWN0b3Ioc2NvcGU6IENvbnN0cnVjdCwgaWQ6IHN0cmluZywgcHJvcHM6IEhlYWRlckNoYW5nZURldGVjdGlvblByb3BzKSB7XG4gICAgc3VwZXIoc2NvcGUsIGlkKTtcblxuICAgIGNvbnN0IGhlYWRlcnMgPSBwcm9wcy5kaXNhYmxlRGVmYXVsdHMgPyBbXSA6IGRlZmF1bHRIZWFkZXJzO1xuXG4gICAgaGVhZGVycy5wdXNoKFxuICAgICAgLi4uKHByb3BzLmFkZGl0aW9uYWxIZWFkZXJzPy5tYXAoaGVhZGVyID0+IGhlYWRlci50b0xvd2VyQ2FzZSgpKSB8fCBbXSlcbiAgICApO1xuXG4gICAgY29uc3QgdGFibGUgPSBuZXcgVGFibGUodGhpcywgXCJUYWJsZVwiLCB7XG4gICAgICBwYXJ0aXRpb25LZXk6IHtcbiAgICAgICAgbmFtZTogXCJVcmxcIixcbiAgICAgICAgdHlwZTogQXR0cmlidXRlVHlwZS5TVFJJTkcsXG4gICAgICB9LFxuICAgICAgYmlsbGluZ01vZGU6IEJpbGxpbmdNb2RlLlBBWV9QRVJfUkVRVUVTVCxcbiAgICB9KTtcblxuICAgIGNvbnN0IHNjaGVkdWxlID0gbmV3IFJ1bGUodGhpcywgXCJFdmVudFJ1bGVcIiwge1xuICAgICAgc2NoZWR1bGU6IHByb3BzLnNjaGVkdWxlIHx8IFNjaGVkdWxlLnJhdGUoRHVyYXRpb24uaG91cnMoMSkpLFxuICAgICAgLi4ucHJvcHMucnVsZVByb3BzLFxuICAgIH0pO1xuXG4gICAgY29uc3QgbGFtYmRhID0gbmV3IEZ1bmN0aW9uKHRoaXMsIFwiSGVhZGVyQ2hlY2tcIiwge1xuICAgICAgYXJjaGl0ZWN0dXJlOiBBcmNoaXRlY3R1cmUuWDg2XzY0LFxuICAgICAgcnVudGltZTogUnVudGltZS5OT0RFSlNfMjJfWCxcbiAgICAgIGhhbmRsZXI6IFwiaGVhZGVyLWNoZWNrLmhhbmRsZXJcIixcbiAgICAgIHRpbWVvdXQ6IHByb3BzLmxhbWJkYVRpbWVvdXQgfHwgRHVyYXRpb24uc2Vjb25kcygxMCksXG4gICAgICBjb2RlOiBDb2RlLmZyb21Bc3NldChqb2luKF9fZGlybmFtZSwgXCJsYW1iZGFcIiksIHtcbiAgICAgICAgYnVuZGxpbmc6IHtcbiAgICAgICAgICBjb21tYW5kLFxuICAgICAgICAgIGltYWdlOiBEb2NrZXJJbWFnZS5mcm9tUmVnaXN0cnkoXCJidXN5Ym94XCIpLFxuICAgICAgICAgIGxvY2FsOiBuZXcgRXNidWlsZCh7XG4gICAgICAgICAgICBlbnRyeVBvaW50czogW2pvaW4oX19kaXJuYW1lLCBcImxhbWJkYS9oZWFkZXItY2hlY2sudHNcIildLFxuICAgICAgICAgIH0pLFxuICAgICAgICB9LFxuICAgICAgfSksXG4gICAgICBlbnZpcm9ubWVudDoge1xuICAgICAgICBVUkxTOiBwcm9wcy51cmxzLmpvaW4oXCIsXCIpLFxuICAgICAgICBIRUFERVJTOiBoZWFkZXJzLmpvaW4oXCIsXCIpLFxuICAgICAgICBUQUJMRTogdGFibGUudGFibGVOYW1lLFxuICAgICAgICBUT1BJQ19BUk46IHByb3BzLnNuc1RvcGljLnRvcGljLnRvcGljQXJuLFxuICAgICAgICBBQ0NFUFRFRF9IVFRQX1NUQVRVUzogKHByb3BzLmFjY2VwdGVkSHR0cFN0YXR1cyB8fCBbXCIyMDBcIl0pLmpvaW4oXCIsXCIpLFxuICAgICAgfSxcbiAgICB9KTtcblxuICAgIHNjaGVkdWxlLmFkZFRhcmdldChuZXcgTGFtYmRhRnVuY3Rpb24obGFtYmRhKSk7XG5cbiAgICB0YWJsZS5ncmFudFdyaXRlRGF0YShsYW1iZGEpO1xuICAgIHRhYmxlLmdyYW50UmVhZERhdGEobGFtYmRhKTtcbiAgICBwcm9wcy5zbnNUb3BpYy50b3BpYy5ncmFudFB1Ymxpc2gobGFtYmRhKTtcbiAgfVxufVxuIl19