ecr-scan-verifier 0.0.2 → 0.0.4

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/.jsii CHANGED
@@ -3961,7 +3961,7 @@
3961
3961
  },
3962
3962
  "name": "ecr-scan-verifier",
3963
3963
  "readme": {
3964
- "markdown": "# ecr-scan-verifier\n\nAn AWS CDK Construct that **blocks deployments** to ECS, Lambda, and other services when **ECR Image Scanning detects vulnerabilities**.\n\nIt integrates both Basic and Enhanced (Amazon Inspector) scanning into your CDK deployment pipeline.\n\n- **Block any construct's deployment** — block ECS, Lambda, or any CDK construct on vulnerability detection via `blockConstructs`\n- **Notify without failing** — get alerts via SNS without blocking deployment. Great for gradual adoption\n- **Scan logs output** — results go to S3 or CloudWatch Logs\n- **SBOM generation** — output Software Bill of Materials in CycloneDX or SPDX format to S3 via Amazon Inspector\n\n## Scanning Modes\n\nThis construct supports two scanning modes. With Basic scanning, the construct starts a scan via API during deployment, or checks existing scan-on-push results. Enhanced scanning (Amazon Inspector) only supports scan-on-push, but additionally enables SBOM generation.\n\n| Feature | Basic Scanning | Enhanced Scanning |\n|---|---|---|\n| Start scan via API | ✅ (`startScan: true`) | — |\n| Check scan-on-push results | ✅ (`startScan: false`) | ✅ |\n| SBOM generation | — | ✅ |\n\n### Prerequisites\n\nWhen using `ScanConfig.basic({ startScan: true })` (the default), the construct starts a scan via the ECR `StartImageScan` API during deployment — no additional ECR configuration is required.\n\nFor all other modes, **scan-on-push must be enabled** on your ECR repository or account before deployment:\n\n- **`ScanConfig.basic({ startScan: false })`** — requires Basic scan-on-push to be enabled on the repository\n- **`ScanConfig.enhanced()`** — requires Enhanced scanning (Amazon Inspector) to be enabled on the account, with the repository included in Inspector's coverage\n\nIf scan-on-push is not configured and no prior scan results exist, the deployment will fail with an error.\n\n> **Tip**: `startScan: true` works even when scan-on-push is already enabled. If a scan has already been triggered, the construct simply uses the existing scan results.\n\n## Usage\n\n### Install\n\n```sh\nnpm install ecr-scan-verifier\n```\n\n### CDK Code\n\nThe following code is a minimal example that scans the image and blocks the ECS deployment if vulnerabilities are detected.\n\n```ts\nimport { EcrScanVerifier, ScanConfig } from 'ecr-scan-verifier';\n\n// Target image to scan\nconst image = new DockerImageAsset(this, 'DockerImage', {\n directory: resolve(__dirname, './'),\n});\n\n// Example of an ECS construct that uses the image\nconst ecs = new YourECSConstruct(this, 'YourECSConstruct', {\n dockerImage: image,\n});\n\n// Scan the image before deploying to ECS\nnew EcrScanVerifier(this, 'ImageScanner', {\n repository: image.repository,\n imageTag: image.assetHash,\n scanConfig: ScanConfig.basic(),\n // If vulnerabilities are detected, the ECS deployment will be blocked\n blockConstructs: [ecs],\n});\n```\n\n### Image Tag\n\nYou can specify which image to scan by tag or digest:\n\n```ts\n// Scan by tag (default: 'latest')\nnew EcrScanVerifier(this, 'Scanner', {\n repository,\n scanConfig: ScanConfig.basic(),\n imageTag: 'v1.0',\n});\n\n// Scan by digest (if the value starts with 'sha256:', it is treated as a digest)\nnew EcrScanVerifier(this, 'Scanner', {\n repository,\n scanConfig: ScanConfig.basic(),\n imageTag: 'sha256:abc123...',\n});\n```\n\n### Scan Configuration\n\nUse `ScanConfig` to choose between Basic and Enhanced scanning:\n\n```ts\nimport { ScanConfig } from 'ecr-scan-verifier';\n\n// Basic scanning (default) — starts a scan via StartImageScan API\nnew EcrScanVerifier(this, 'Scanner', {\n repository,\n scanConfig: ScanConfig.basic({ startScan: true }),\n});\n\n// Basic scanning — polls for existing scan results (useful when scan-on-push is configured)\nnew EcrScanVerifier(this, 'Scanner', {\n repository,\n scanConfig: ScanConfig.basic({ startScan: false }),\n});\n\n// Enhanced scanning — uses Amazon Inspector (scan-on-push only)\nnew EcrScanVerifier(this, 'Scanner', {\n repository,\n scanConfig: ScanConfig.enhanced(),\n});\n```\n\nSee [Prerequisites](#prerequisites) for the scan-on-push requirements of each mode.\n\n> **Important**: If Enhanced scanning (Amazon Inspector) is enabled on your account, you must use `ScanConfig.enhanced()`. Using `ScanConfig.basic()` with an Enhanced scanning account will result in a deployment error.\n\n### Severity\n\nYou can specify which severity levels trigger a failure:\n\n```ts\nimport { Severity } from 'ecr-scan-verifier';\n\nnew EcrScanVerifier(this, 'Scanner', {\n repository,\n scanConfig: ScanConfig.basic(),\n severity: [Severity.CRITICAL, Severity.HIGH],\n});\n```\n\nAvailable severity levels: `CRITICAL`, `HIGH`, `MEDIUM`, `LOW`, `INFORMATIONAL`, `UNDEFINED`.\n\n### Ignore Findings\n\nYou can ignore specific CVEs:\n\n```ts\nnew EcrScanVerifier(this, 'Scanner', {\n repository,\n scanConfig: ScanConfig.basic(),\n ignoreFindings: ['CVE-2023-37920', 'CVE-2024-12345'],\n});\n```\n\n### Scan Logs Output\n\nYou can choose where to output the scan logs using `ScanLogsOutput`: S3 or CloudWatch Logs. If not specified, scan logs are written to the Scanner Lambda function's default log group.\n\n#### S3\n\n```ts\nconst scanLogsBucket = new Bucket(this, 'ScanLogsBucket');\n\nnew EcrScanVerifier(this, 'Scanner', {\n repository,\n scanConfig: ScanConfig.basic(),\n scanLogsOutput: ScanLogsOutput.s3({\n bucket: scanLogsBucket,\n prefix: 'scan-logs/', // Optional\n }),\n});\n```\n\n#### CloudWatch Logs\n\n```ts\nimport { ScanLogsOutput } from 'ecr-scan-verifier';\n\nconst scanLogsLogGroup = new LogGroup(this, 'ScanLogsLogGroup');\n\nnew EcrScanVerifier(this, 'Scanner', {\n repository,\n scanConfig: ScanConfig.basic(),\n scanLogsOutput: ScanLogsOutput.cloudWatchLogs({ logGroup: scanLogsLogGroup }),\n});\n```\n\n#### Default Log Group\n\nYou can customize the Scanner Lambda function's log group with `defaultLogGroup`.\n\nIf you use `EcrScanVerifier` construct multiple times in the same stack, you have to set the same log group for `defaultLogGroup` for each construct. When you set different log groups for each construct, a warning message will be displayed.\n\n```ts\nconst logGroup = new LogGroup(this, 'LogGroup');\n\nnew EcrScanVerifier(this, 'Scanner1', {\n repository,\n scanConfig: ScanConfig.basic(),\n defaultLogGroup: logGroup,\n});\n\nnew EcrScanVerifier(this, 'Scanner2', {\n repository,\n scanConfig: ScanConfig.basic(),\n defaultLogGroup: new LogGroup(this, 'AnotherLogGroup'), // NG: different log group from Scanner1\n defaultLogGroup: logGroup, // OK: Use the same log group as Scanner1 to avoid warning\n});\n```\n\n### SBOM Output\n\nYou can generate SBOM (Software Bill of Materials) using Amazon Inspector's CreateSbomExport API. This is independent from scan logs output.\n\n**Note**: SBOM export is only available with Enhanced scanning. Using with Basic scanning will throw an error.\n\n```ts\nimport { SbomOutput, ScanConfig } from 'ecr-scan-verifier';\n\nconst sbomBucket = new Bucket(this, 'SbomBucket');\n\nnew EcrScanVerifier(this, 'Scanner', {\n repository,\n scanConfig: ScanConfig.enhanced(),\n sbomOutput: SbomOutput.cycloneDx14({\n bucket: sbomBucket,\n prefix: 'sbom/', // Optional\n kmsKeyArn: 'arn:aws:kms:...', // Optional: KMS key for encryption\n }),\n});\n```\n\nAvailable SBOM formats:\n\n- `SbomOutput.cycloneDx14()` — CycloneDX 1.4 JSON format\n- `SbomOutput.spdx23()` — SPDX 2.3 JSON format\n\n### SNS Notification for Vulnerabilities\n\nYou can configure an SNS topic via `vulnsNotificationTopic` to receive notifications when vulnerabilities are detected.\n\nBy default, the construct fails the deployment when vulnerabilities are found.\nYou can set `failOnVulnerability: false` to receive SNS notifications without blocking the deployment.\n\n```ts\nconst notificationTopic = new Topic(this, 'VulnerabilityNotificationTopic');\n\nnew EcrScanVerifier(this, 'Scanner', {\n repository,\n scanConfig: ScanConfig.basic(),\n vulnsNotificationTopic: notificationTopic,\n failOnVulnerability: false, // Notify but don't fail deployment\n});\n```\n"
3964
+ "markdown": "# ecr-scan-verifier\n\nAn AWS CDK Construct that **blocks deployments** to ECS, Lambda, and other services when **ECR Image Scanning detects vulnerabilities**.\n\nIt scans a specified container image during CDK deployment using Basic or Enhanced (Amazon Inspector) scanning.\n\n- **Block any construct's deployment** — block ECS, Lambda, or any CDK construct on vulnerability detection via `blockConstructs`\n- **Notify without failing** — get alerts via SNS without blocking deployment. Great for gradual adoption\n- **Scan logs output** — results go to S3 or CloudWatch Logs\n- **SBOM generation** — output Software Bill of Materials in CycloneDX or SPDX format to S3 via Amazon Inspector\n\n## Scanning Modes\n\nThis construct supports two scanning modes. With Basic scanning, the construct starts a scan via API during deployment, or checks existing scan-on-push results. Enhanced scanning (Amazon Inspector) only supports scan-on-push, but additionally enables SBOM generation.\n\n| Feature | Basic Scanning | Enhanced Scanning |\n|---|---|---|\n| Start scan via API | ✅ (`startScan: true`) | — |\n| Check scan-on-push results | ✅ (`startScan: false`) | ✅ |\n| SBOM generation | — | ✅ |\n\n### Prerequisites\n\nWhen using `ScanConfig.basic({ startScan: true })` (the default), the construct starts a scan via the ECR `StartImageScan` API during deployment — no additional ECR configuration is required.\n\nFor all other modes, **scan-on-push must be enabled** on your ECR repository or account before deployment:\n\n- **`ScanConfig.basic({ startScan: false })`** — requires Basic scan-on-push to be enabled on the repository\n- **`ScanConfig.enhanced()`** — requires Enhanced scanning (Amazon Inspector) to be enabled on the account, with the repository included in Inspector's coverage\n\nIf scan-on-push is not configured and no prior scan results exist, the deployment will fail with an error.\n\n> **Tip**: `startScan: true` works even when scan-on-push is already enabled. If a scan has already been triggered, the construct simply uses the existing scan results.\n\n## Usage\n\n### Install\n\n```sh\nnpm install ecr-scan-verifier\n```\n\n### CDK Code\n\nThe following code is a minimal example that scans the image and blocks the ECS deployment if vulnerabilities are detected.\n\n```ts\nimport { EcrScanVerifier, ScanConfig } from 'ecr-scan-verifier';\n\n// Target image to scan\nconst image = new DockerImageAsset(this, 'DockerImage', {\n directory: resolve(__dirname, './'),\n});\n\n// Example of an ECS construct that uses the image\nconst ecs = new YourECSConstruct(this, 'YourECSConstruct', {\n dockerImage: image,\n});\n\n// Scan the image before deploying to ECS\nnew EcrScanVerifier(this, 'ImageScanner', {\n repository: image.repository,\n imageTag: image.assetHash,\n scanConfig: ScanConfig.basic(),\n // If vulnerabilities are detected, the ECS deployment will be blocked\n blockConstructs: [ecs],\n});\n```\n\n### Image Tag\n\nYou can specify which image to scan by tag or digest:\n\n```ts\n// Scan by tag (default: 'latest')\nnew EcrScanVerifier(this, 'Scanner', {\n repository,\n scanConfig: ScanConfig.basic(),\n imageTag: 'v1.0',\n});\n\n// Scan by digest (if the value starts with 'sha256:', it is treated as a digest)\nnew EcrScanVerifier(this, 'Scanner', {\n repository,\n scanConfig: ScanConfig.basic(),\n imageTag: 'sha256:abc123...',\n});\n```\n\n### Scan Configuration\n\nUse `ScanConfig` to choose between Basic and Enhanced scanning:\n\n```ts\nimport { ScanConfig } from 'ecr-scan-verifier';\n\n// Basic scanning (default) — starts a scan via StartImageScan API\nnew EcrScanVerifier(this, 'Scanner', {\n repository,\n scanConfig: ScanConfig.basic({ startScan: true }),\n});\n\n// Basic scanning — polls for existing scan results (useful when scan-on-push is configured)\nnew EcrScanVerifier(this, 'Scanner', {\n repository,\n scanConfig: ScanConfig.basic({ startScan: false }),\n});\n\n// Enhanced scanning — uses Amazon Inspector (scan-on-push only)\nnew EcrScanVerifier(this, 'Scanner', {\n repository,\n scanConfig: ScanConfig.enhanced(),\n});\n```\n\nSee [Prerequisites](#prerequisites) for the scan-on-push requirements of each mode.\n\n> **Important**: If Enhanced scanning (Amazon Inspector) is enabled on your account, you must use `ScanConfig.enhanced()`. Using `ScanConfig.basic()` with an Enhanced scanning account will result in a deployment error.\n\n### Severity\n\nYou can specify which severity levels trigger a failure:\n\n```ts\nimport { Severity } from 'ecr-scan-verifier';\n\nnew EcrScanVerifier(this, 'Scanner', {\n repository,\n scanConfig: ScanConfig.basic(),\n severity: [Severity.CRITICAL, Severity.HIGH],\n});\n```\n\nAvailable severity levels: `CRITICAL`, `HIGH`, `MEDIUM`, `LOW`, `INFORMATIONAL`, `UNDEFINED`.\n\n### Ignore Findings\n\nYou can ignore specific CVEs:\n\n```ts\nnew EcrScanVerifier(this, 'Scanner', {\n repository,\n scanConfig: ScanConfig.basic(),\n ignoreFindings: ['CVE-2023-37920', 'CVE-2024-12345'],\n});\n```\n\n### Scan Logs Output\n\nYou can choose where to output the scan logs using `ScanLogsOutput`: S3 or CloudWatch Logs. If not specified, scan logs are written to the Scanner Lambda function's default log group.\n\n#### S3\n\n```ts\nimport { ScanLogsOutput } from 'ecr-scan-verifier';\n\nconst scanLogsBucket = new Bucket(this, 'ScanLogsBucket');\n\nnew EcrScanVerifier(this, 'Scanner', {\n repository,\n scanConfig: ScanConfig.basic(),\n scanLogsOutput: ScanLogsOutput.s3({\n bucket: scanLogsBucket,\n prefix: 'scan-logs/', // Optional\n }),\n});\n```\n\n#### CloudWatch Logs\n\n```ts\nimport { ScanLogsOutput } from 'ecr-scan-verifier';\n\nconst scanLogsLogGroup = new LogGroup(this, 'ScanLogsLogGroup');\n\nnew EcrScanVerifier(this, 'Scanner', {\n repository,\n scanConfig: ScanConfig.basic(),\n scanLogsOutput: ScanLogsOutput.cloudWatchLogs({ logGroup: scanLogsLogGroup }),\n});\n```\n\n#### Default Log Group\n\nYou can customize the Scanner Lambda function's log group with `defaultLogGroup`.\n\nIf you use `EcrScanVerifier` construct multiple times in the same stack, you have to set the same log group for `defaultLogGroup` for each construct. When you set different log groups for each construct, a warning message will be displayed.\n\n```ts\nconst logGroup = new LogGroup(this, 'LogGroup');\n\nnew EcrScanVerifier(this, 'Scanner1', {\n repository,\n scanConfig: ScanConfig.basic(),\n defaultLogGroup: logGroup,\n});\n\nnew EcrScanVerifier(this, 'Scanner2', {\n repository,\n scanConfig: ScanConfig.basic(),\n defaultLogGroup: new LogGroup(this, 'AnotherLogGroup'), // NG: different log group from Scanner1\n defaultLogGroup: logGroup, // OK: Use the same log group as Scanner1 to avoid warning\n});\n```\n\n### SBOM Output\n\nYou can generate SBOM (Software Bill of Materials) using Amazon Inspector's CreateSbomExport API. This is independent from scan logs output.\n\n**Note**: SBOM export is only available with Enhanced scanning. Using with Basic scanning will throw an error.\n\n```ts\nimport { SbomOutput, ScanConfig } from 'ecr-scan-verifier';\n\nconst sbomBucket = new Bucket(this, 'SbomBucket');\nconst sbomEncryptionKey = new Key(this, 'SbomEncryptionKey');\n\nnew EcrScanVerifier(this, 'Scanner', {\n repository,\n scanConfig: ScanConfig.enhanced(),\n sbomOutput: SbomOutput.cycloneDx14({\n bucket: sbomBucket,\n prefix: 'sbom/', // Optional\n encryptionKey: sbomEncryptionKey,\n }),\n});\n```\n\nAvailable SBOM formats:\n\n- `SbomOutput.cycloneDx14()` — CycloneDX 1.4 JSON format\n- `SbomOutput.spdx23()` — SPDX 2.3 JSON format\n\n### SNS Notification for Vulnerabilities\n\nYou can configure an SNS topic via `vulnsNotificationTopic` to receive notifications when vulnerabilities are detected.\n\nBy default, the construct fails the deployment when vulnerabilities are found.\nYou can set `failOnVulnerability: false` to receive SNS notifications without blocking the deployment.\n\n```ts\nconst notificationTopic = new Topic(this, 'VulnerabilityNotificationTopic');\n\nnew EcrScanVerifier(this, 'Scanner', {\n repository,\n scanConfig: ScanConfig.basic(),\n vulnsNotificationTopic: notificationTopic,\n failOnVulnerability: false, // Notify but don't fail deployment\n});\n```\n"
3965
3965
  },
3966
3966
  "repository": {
3967
3967
  "type": "git",
@@ -5149,6 +5149,6 @@
5149
5149
  "symbolId": "src/types:Severity"
5150
5150
  }
5151
5151
  },
5152
- "version": "0.0.2",
5153
- "fingerprint": "AKN1vQyBsPqbE817727RWsDYMjwOWEzqNBoTQoAdBKI="
5152
+ "version": "0.0.4",
5153
+ "fingerprint": "oJTYMO2fEGG0WnJvgUKvol6vIWaZrAJ5BsxbP9cptoY="
5154
5154
  }
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  An AWS CDK Construct that **blocks deployments** to ECS, Lambda, and other services when **ECR Image Scanning detects vulnerabilities**.
4
4
 
5
- It integrates both Basic and Enhanced (Amazon Inspector) scanning into your CDK deployment pipeline.
5
+ It scans a specified container image during CDK deployment using Basic or Enhanced (Amazon Inspector) scanning.
6
6
 
7
7
  - **Block any construct's deployment** — block ECS, Lambda, or any CDK construct on vulnerability detection via `blockConstructs`
8
8
  - **Notify without failing** — get alerts via SNS without blocking deployment. Great for gradual adoption
@@ -152,6 +152,8 @@ You can choose where to output the scan logs using `ScanLogsOutput`: S3 or Cloud
152
152
  #### S3
153
153
 
154
154
  ```ts
155
+ import { ScanLogsOutput } from 'ecr-scan-verifier';
156
+
155
157
  const scanLogsBucket = new Bucket(this, 'ScanLogsBucket');
156
158
 
157
159
  new EcrScanVerifier(this, 'Scanner', {
@@ -211,6 +213,7 @@ You can generate SBOM (Software Bill of Materials) using Amazon Inspector's Crea
211
213
  import { SbomOutput, ScanConfig } from 'ecr-scan-verifier';
212
214
 
213
215
  const sbomBucket = new Bucket(this, 'SbomBucket');
216
+ const sbomEncryptionKey = new Key(this, 'SbomEncryptionKey');
214
217
 
215
218
  new EcrScanVerifier(this, 'Scanner', {
216
219
  repository,
@@ -218,7 +221,7 @@ new EcrScanVerifier(this, 'Scanner', {
218
221
  sbomOutput: SbomOutput.cycloneDx14({
219
222
  bucket: sbomBucket,
220
223
  prefix: 'sbom/', // Optional
221
- kmsKeyArn: 'arn:aws:kms:...', // Optional: KMS key for encryption
224
+ encryptionKey: sbomEncryptionKey,
222
225
  }),
223
226
  });
224
227
  ```
@@ -1,7 +1,7 @@
1
- "use strict";var O=Object.defineProperty;var H=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var X=Object.prototype.hasOwnProperty;var z=(t,o)=>{for(var n in o)O(t,n,{get:o[n],enumerable:!0})},q=(t,o,n,e)=>{if(o&&typeof o=="object"||typeof o=="function")for(let s of j(o))!X.call(t,s)&&s!==n&&O(t,s,{get:()=>o[s],enumerable:!(e=H(o,s))||e.enumerable});return t};var J=t=>q(O({},"__esModule",{value:!0}),t);var cn={};z(cn,{handler:()=>V});module.exports=J(cn);var w=require("@aws-sdk/client-ecr"),x=new w.ECRClient,L=t=>new Promise(o=>setTimeout(o,t)),k=t=>t.startsWith("sha256:")?{imageDigest:t}:{imageTag:t},R=async(t,o,n,e,s)=>{let i=k(o);console.log(`Starting image scan for ${t}...`);try{await x.send(new w.StartImageScanCommand({repositoryName:t,imageId:i})),console.log("Image scan started successfully.")}catch(a){if(a.name==="LimitExceededException"||a.message&&a.message.includes("scan frequency limit"))console.log("Scan already in progress or recently completed, polling for results...");else throw a.name==="ValidationException"&&a.message&&a.message.includes("This feature is disabled")?new Error("StartImageScan is disabled because Enhanced scanning (Amazon Inspector) is enabled on this account. Use ScanConfig.enhanced() instead of ScanConfig.basic()."):a}return $(t,o,n,e,s)},$=async(t,o,n,e,s)=>{let i=k(o);for(let a=0;a<s;a++){console.log(`Polling scan results (attempt ${a+1}/${s})...`);try{let c=await Y(t,i),r=c.rawResponse.imageScanStatus?.status;if(r==="COMPLETE"||r==="ACTIVE")return console.log(`Scan completed with status: ${r}`),c;if(r==="FAILED"){let u=c.rawResponse.imageScanStatus?.description||"Unknown error";throw new Error(`ECR image scan failed: ${u}`)}if(r==="UNSUPPORTED_IMAGE")throw new Error("ECR image scan failed: Image is not supported for scanning.");console.log(`Scan status: ${r}, waiting ${e}s...`)}catch(c){if(c.name==="ScanNotFoundException"){if(a<s-1){console.log(`Scan not found yet (attempt ${a+1}/${s}), waiting ${e}s before retrying...`),await L(e*1e3);continue}throw new Error(`No scan results found for the image after ${s*e} seconds. Ensure that image scanning is enabled for this repository. If using Enhanced scanning (Amazon Inspector), verify that the repository is included in Inspector's coverage.`)}throw c}await L(e*1e3)}throw new Error(`ECR image scan timed out after ${s*e} seconds. The scan may still be in progress. Check the ECR console for results.`)},Y=async(t,o)=>{let n=[],e=[],s,i;do{let r=await x.send(new w.DescribeImageScanFindingsCommand({repositoryName:t,imageId:o,nextToken:s,maxResults:1e3}));i=r,r.imageScanFindings?.findings&&n.push(...r.imageScanFindings.findings),r.imageScanFindings?.enhancedFindings&&e.push(...r.imageScanFindings.enhancedFindings),s=r.nextToken}while(s);let a=i?.imageScanFindings?.findingSeverityCounts?Object.fromEntries(Object.entries(i.imageScanFindings.findingSeverityCounts).map(([r,u])=>[r,u??0])):{};return{scanType:e.length>0?"ENHANCED":"BASIC",status:i?.imageScanStatus?.status??"UNKNOWN",basicFindings:n,enhancedFindings:e,severityCounts:a,rawResponse:i}};var F=(t,o,n)=>{let e=new Set(n),s=new Set(o);return t.scanType==="ENHANCED"?Z(t,s,e):Q(t,s,e)},Q=(t,o,n)=>{let e=t.basicFindings.filter(c=>!n.has(c.name||"")),s={},i=!1;for(let c of e){let r=c.severity||"UNDEFINED";s[r]=(s[r]||0)+1,o.has(r)&&(i=!0)}let a=T(s);return{hasVulnerabilities:i,summary:a,filteredSeverityCounts:s}},Z=(t,o,n)=>{let e=t.enhancedFindings.filter(c=>{if(n.has(c.findingArn||""))return!1;let r=c.packageVulnerabilityDetails?.vulnerabilityId;return!(r&&n.has(r))}),s={},i=!1;for(let c of e){let r=c.severity||"UNDEFINED";s[r]=(s[r]||0)+1,o.has(r)&&(i=!0)}let a=T(s);return{hasVulnerabilities:i,summary:a,filteredSeverityCounts:s}},T=t=>["CRITICAL","HIGH","MEDIUM","LOW","INFORMATIONAL","UNDEFINED"].filter(n=>t[n]).map(n=>`${n}: ${t[n]}`).join(", "),P=(t,o,n,e)=>{let s=["=== ECR Image Scan Results ===",`Repository: ${n}`,`Image: ${e}`,`Scan Type: ${t.scanType}`,`Scan Status: ${t.status}`,"","--- Severity Summary ---"];return o.summary?s.push(o.summary):s.push("No vulnerabilities found."),s.join(`
1
+ "use strict";var O=Object.defineProperty;var H=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var X=Object.prototype.hasOwnProperty;var z=(t,o)=>{for(var n in o)O(t,n,{get:o[n],enumerable:!0})},q=(t,o,n,e)=>{if(o&&typeof o=="object"||typeof o=="function")for(let s of j(o))!X.call(t,s)&&s!==n&&O(t,s,{get:()=>o[s],enumerable:!(e=H(o,s))||e.enumerable});return t};var J=t=>q(O({},"__esModule",{value:!0}),t);var cn={};z(cn,{handler:()=>U});module.exports=J(cn);var w=require("@aws-sdk/client-ecr"),x=new w.ECRClient,I=t=>new Promise(o=>setTimeout(o,t)),k=t=>t.startsWith("sha256:")?{imageDigest:t}:{imageTag:t},F=async(t,o,n,e,s)=>{let i=k(o);console.log(`Starting image scan for ${t}...`);try{await x.send(new w.StartImageScanCommand({repositoryName:t,imageId:i})),console.log("Image scan started successfully.")}catch(a){if(a.name==="LimitExceededException"||a.message&&a.message.includes("scan frequency limit"))console.log("Scan already in progress or recently completed, polling for results...");else throw a.name==="ValidationException"&&a.message&&a.message.includes("This feature is disabled")?new Error("StartImageScan is disabled because Enhanced scanning (Amazon Inspector) is enabled on this account. Use ScanConfig.enhanced() instead of ScanConfig.basic()."):a}return $(t,o,n,e,s)},$=async(t,o,n,e,s)=>{let i=k(o);for(let a=0;a<s;a++){console.log(`Polling scan results (attempt ${a+1}/${s})...`);try{let c=await Y(t,i),r=c.rawResponse.imageScanStatus?.status;if(r==="COMPLETE"||r==="ACTIVE")return console.log(`Scan completed with status: ${r}`),c;if(r==="FAILED"){let u=c.rawResponse.imageScanStatus?.description||"Unknown error";throw new Error(`ECR image scan failed: ${u}`)}if(r==="UNSUPPORTED_IMAGE")throw new Error("ECR image scan failed: Image is not supported for scanning.");console.log(`Scan status: ${r}, waiting ${e}s...`)}catch(c){if(c.name==="ScanNotFoundException"){if(a<s-1){console.log(`Scan not found yet (attempt ${a+1}/${s}), waiting ${e}s before retrying...`),await I(e*1e3);continue}throw new Error(`No scan results found for the image after ${s*e} seconds. Ensure that image scanning is enabled for this repository. If using Enhanced scanning (Amazon Inspector), verify that the repository is included in Inspector's coverage.`)}throw c}await I(e*1e3)}throw new Error(`ECR image scan timed out after ${s*e} seconds. The scan may still be in progress. Check the ECR console for results.`)},Y=async(t,o)=>{let n=[],e=[],s,i;do{let r=await x.send(new w.DescribeImageScanFindingsCommand({repositoryName:t,imageId:o,nextToken:s,maxResults:1e3}));i=r,r.imageScanFindings?.findings&&n.push(...r.imageScanFindings.findings),r.imageScanFindings?.enhancedFindings&&e.push(...r.imageScanFindings.enhancedFindings),s=r.nextToken}while(s);let a=i?.imageScanFindings?.findingSeverityCounts?Object.fromEntries(Object.entries(i.imageScanFindings.findingSeverityCounts).map(([r,u])=>[r,u??0])):{};return{scanType:e.length>0?"ENHANCED":"BASIC",status:i?.imageScanStatus?.status??"UNKNOWN",basicFindings:n,enhancedFindings:e,severityCounts:a,rawResponse:i}};var R=(t,o,n)=>{let e=new Set(n),s=new Set(o);return t.scanType==="ENHANCED"?Z(t,s,e):Q(t,s,e)},Q=(t,o,n)=>{let e=t.basicFindings.filter(c=>!n.has(c.name||"")),s={},i=!1;for(let c of e){let r=c.severity||"UNDEFINED";s[r]=(s[r]||0)+1,o.has(r)&&(i=!0)}let a=T(s);return{hasVulnerabilities:i,summary:a,filteredSeverityCounts:s}},Z=(t,o,n)=>{let e=t.enhancedFindings.filter(c=>{if(n.has(c.findingArn||""))return!1;let r=c.packageVulnerabilityDetails?.vulnerabilityId;return!(r&&n.has(r))}),s={},i=!1;for(let c of e){let r=c.severity||"UNDEFINED";s[r]=(s[r]||0)+1,o.has(r)&&(i=!0)}let a=T(s);return{hasVulnerabilities:i,summary:a,filteredSeverityCounts:s}},T=t=>["CRITICAL","HIGH","MEDIUM","LOW","INFORMATIONAL","UNDEFINED"].filter(n=>t[n]).map(n=>`${n}: ${t[n]}`).join(", "),P=(t,o,n,e)=>{let s=["=== ECR Image Scan Results ===",`Repository: ${n}`,`Image: ${e}`,`Scan Type: ${t.scanType}`,`Scan Status: ${t.status}`,"","--- Severity Summary ---"];return o.summary?s.push(o.summary):s.push("No vulnerabilities found."),s.join(`
2
2
  `)};var S=require("@aws-sdk/client-cloudwatch-logs"),G=new S.CloudWatchLogsClient,B=async(t,o,n,e)=>{let s=e.replace(/:/g,",").replace(/\//g,"_"),i=`${s}/findings`,a=`${s}/summary`,c=new Date().getTime();return await v(n.logGroupName,i,c,t),await v(n.logGroupName,a,c,o),console.log(`Scan logs output to the log group: ${n.logGroupName}
3
3
  findings stream: ${i}
4
- summary stream: ${a}`),{type:"cloudwatch",logGroupName:n.logGroupName,findingsLogStreamName:i,summaryLogStreamName:a}},A=1048576,nn=t=>{let n=new TextEncoder().encode(t);if(n.length<=A)return[t];let e=[],s=0,a=A-20;for(;s<n.length;){let c=n.slice(s,s+a),r=new TextDecoder("utf-8",{fatal:!1});e.push(r.decode(c)),s+=a}return e},v=async(t,o,n,e)=>{try{await G.send(new S.CreateLogStreamCommand({logGroupName:t,logStreamName:o}))}catch(u){if(u instanceof S.ResourceAlreadyExistsException)console.log(`Log stream ${o} already exists in log group ${t}.`);else throw u}let s=nn(e),i=s.length;i>1&&console.log(`Message size exceeds 1 MB limit. Splitting into ${i} chunks.`);let a=s.map((u,m)=>({timestamp:n+m,message:i>1?`[part ${m+1}/${i}] ${u}`:u})),c={logGroupName:t,logStreamName:o,logEvents:a},r=new S.PutLogEventsCommand(c);await G.send(r)};var C=require("@aws-sdk/client-s3");var I=new C.S3Client,D=async(t,o,n,e,s)=>{let i=new Date().toISOString(),a=e.replace(/:/g,"/").replace(/\//g,"_"),r=`${n.prefix?n.prefix.endsWith("/")?n.prefix:`${n.prefix}/`:""}${a}/${i}`,u=`${r}/findings.json`,m=`${r}/summary.txt`,d=[I.send(new C.PutObjectCommand({Bucket:n.bucketName,Key:u,Body:t,ContentType:"application/json"})),I.send(new C.PutObjectCommand({Bucket:n.bucketName,Key:m,Body:o,ContentType:"text/plain"}))],g;if(s){let f=s.format==="SPDX_2_3"?"spdx.json":"cyclonedx.json";g=`${r}/sbom.${f}`,d.push(I.send(new C.PutObjectCommand({Bucket:n.bucketName,Key:g,Body:s.content,ContentType:"application/json"})))}return await Promise.all(d),console.log(g?`Scan logs and SBOM output to S3:
4
+ summary stream: ${a}`),{type:"cloudwatch",logGroupName:n.logGroupName,findingsLogStreamName:i,summaryLogStreamName:a}},A=1048576,nn=t=>{let n=new TextEncoder().encode(t);if(n.length<=A)return[t];let e=[],s=0,a=A-20;for(;s<n.length;){let c=n.slice(s,s+a),r=new TextDecoder("utf-8",{fatal:!1});e.push(r.decode(c)),s+=a}return e},v=async(t,o,n,e)=>{try{await G.send(new S.CreateLogStreamCommand({logGroupName:t,logStreamName:o}))}catch(u){if(u instanceof S.ResourceAlreadyExistsException)console.log(`Log stream ${o} already exists in log group ${t}.`);else throw u}let s=nn(e),i=s.length;i>1&&console.log(`Message size exceeds 1 MB limit. Splitting into ${i} chunks.`);let a=s.map((u,m)=>({timestamp:n+m,message:i>1?`[part ${m+1}/${i}] ${u}`:u})),c={logGroupName:t,logStreamName:o,logEvents:a},r=new S.PutLogEventsCommand(c);await G.send(r)};var C=require("@aws-sdk/client-s3");var N=new C.S3Client,D=async(t,o,n,e,s)=>{let i=new Date().toISOString(),a=e.replace(/:/g,"/").replace(/\//g,"_"),r=`${n.prefix?n.prefix.endsWith("/")?n.prefix:`${n.prefix}/`:""}${a}/${i}`,u=`${r}/findings.json`,m=`${r}/summary.txt`,d=[N.send(new C.PutObjectCommand({Bucket:n.bucketName,Key:u,Body:t,ContentType:"application/json"})),N.send(new C.PutObjectCommand({Bucket:n.bucketName,Key:m,Body:o,ContentType:"text/plain"}))],g;if(s){let f=s.format==="SPDX_2_3"?"spdx.json":"cyclonedx.json";g=`${r}/sbom.${f}`,d.push(N.send(new C.PutObjectCommand({Bucket:n.bucketName,Key:g,Body:s.content,ContentType:"application/json"})))}return await Promise.all(d),console.log(g?`Scan logs and SBOM output to S3:
5
5
  findings: s3://${n.bucketName}/${u}
6
6
  summary: s3://${n.bucketName}/${m}
7
7
  SBOM: s3://${n.bucketName}/${g}`:`Scan logs output to S3:
@@ -40,18 +40,18 @@ aws logs tail ${e.logGroupName} --since 1h
40
40
  \`\`\``);let a=`${s}
41
41
 
42
42
  How to view logs:
43
- ${i}`,c={version:"1.0",source:"custom",content:{title:"Image Scanner with ECR - Vulnerability Alert",description:`## Scanned Image
43
+ ${i}`,c={version:"1.0",source:"custom",content:{title:"Ecr Scan Verifier - Vulnerability Alert",description:`## Scanned Image
44
44
  ${n}
45
45
 
46
46
  ## Scan Logs
47
47
  ${a}
48
48
 
49
49
  ## Details
50
- ${o}`}},r=`Image Scanner with ECR detected vulnerabilities in ${n}
50
+ ${o}`}},r=`Ecr Scan Verifier detected vulnerabilities in ${n}
51
51
 
52
52
  ${a}
53
53
 
54
- ${o}`,u={default:r,email:r,https:JSON.stringify(c)};try{await en.send(new E.PublishCommand({TopicArn:t,Message:JSON.stringify(u),MessageStructure:"json"})),console.log(`Vulnerability notification sent to SNS topic: ${t}`)}catch(m){console.error(`Failed to send vulnerability notification to SNS: ${m}`)}};var y=require("@aws-sdk/client-cloudformation"),tn=new y.CloudFormationClient,M=async t=>{let o=new y.DescribeStacksCommand({StackName:t}),n=await tn.send(o);if(n.Stacks&&n.Stacks.length>0){let e=n.Stacks[0].StackStatus;return e===y.ResourceStatus.ROLLBACK_IN_PROGRESS||e===y.ResourceStatus.UPDATE_ROLLBACK_IN_PROGRESS}throw new Error(`Stack not found or no stacks returned from DescribeStacks command, stackId: ${t}`)};var l=require("@aws-sdk/client-inspector2"),h=require("@aws-sdk/client-s3"),W=new l.Inspector2Client,K=new h.S3Client,sn=t=>new Promise(o=>setTimeout(o,t)),U=async(t,o,n,e,s)=>{let i=n==="SPDX_2_3"?l.SbomReportFormat.SPDX_2_3:l.SbomReportFormat.CYCLONEDX_1_4;console.log(`Starting SBOM export for ${t} with format ${n}...`);let a={ecrRepositoryName:[{comparison:"EQUALS",value:t}],...o?{ecrImageTags:[{comparison:"EQUALS",value:o}]}:{}},r=(await W.send(new l.CreateSbomExportCommand({reportFormat:i,s3Destination:{bucketName:e,keyPrefix:`sbom-exports/${t}`,kmsKeyArn:s},resourceFilterCriteria:a}))).reportId;if(!r)throw new Error("CreateSbomExport did not return a reportId.");console.log(`SBOM export started with reportId: ${r}`);let u=60,m=5;for(let d=0;d<u;d++){let g=await W.send(new l.GetSbomExportCommand({reportId:r})),f=g.status;if(console.log(`SBOM export status: ${f} (attempt ${d+1}/${u})`),f==="SUCCEEDED"){let p=g.s3Destination?.keyPrefix,b=g.s3Destination?.bucketName;if(!b||!p)throw new Error("SBOM export succeeded but S3 destination is missing.");let N=await on(b,p);if(!N)throw new Error(`SBOM export succeeded but no file found in S3 under prefix: ${p}`);return{sbomContent:await rn(b,N),format:n}}if(f==="FAILED"){let p=g.filterCriteria;throw new Error(`SBOM export failed. Filter criteria: ${JSON.stringify(p)}`)}if(f==="CANCELLED")throw new Error("SBOM export was cancelled.");await sn(m*1e3)}throw new Error(`SBOM export timed out after ${u*m} seconds.`)},on=async(t,o)=>(await K.send(new h.ListObjectsV2Command({Bucket:t,Prefix:o,MaxKeys:1}))).Contents?.[0]?.Key,rn=async(t,o)=>await(await K.send(new h.GetObjectCommand({Bucket:t,Key:o}))).Body?.transformToString()??"";var V=async function(t){let o=t.RequestType,n=t.ResourceProperties;if(!n.addr||!n.repositoryName)throw new Error("addr and repositoryName are required.");let e={PhysicalResourceId:n.addr,Data:{}};if(o!=="Create"&&o!=="Update")return e;let s=5,i=60,a=`${n.repositoryName}:${n.imageTag}`,c;n.startScan==="true"?c=await R(n.repositoryName,n.imageTag,n.scanType,s,i):c=await $(n.repositoryName,n.imageTag,n.scanType,s,i);let r=F(c,n.severity,n.ignoreFindings),u;if(n.sbom)if(n.scanType==="ENHANCED"){let b=await U(n.repositoryName,n.imageTag,n.sbom.format,n.sbom.bucketName,n.sbom.kmsKeyArn);u={content:b.sbomContent,format:b.format}}else console.log("SBOM export is only available with Enhanced scanning. Skipping SBOM generation.");let m=c.enhancedFindings.length>0?c.enhancedFindings:c.basicFindings,d=JSON.stringify(m,null,2),g=P(c,r,n.repositoryName,n.imageTag),f=await an(d,g,a,n.output,n.defaultLogGroupName,u);if(!r.hasVulnerabilities)return e;let p=`ECR Image Scan found vulnerabilities.
54
+ ${o}`,u={default:r,email:r,https:JSON.stringify(c)};try{await en.send(new E.PublishCommand({TopicArn:t,Message:JSON.stringify(u),MessageStructure:"json"})),console.log(`Vulnerability notification sent to SNS topic: ${t}`)}catch(m){console.error(`Failed to send vulnerability notification to SNS: ${m}`)}};var y=require("@aws-sdk/client-cloudformation"),tn=new y.CloudFormationClient,M=async t=>{let o=new y.DescribeStacksCommand({StackName:t}),n=await tn.send(o);if(n.Stacks&&n.Stacks.length>0){let e=n.Stacks[0].StackStatus;return e===y.ResourceStatus.ROLLBACK_IN_PROGRESS||e===y.ResourceStatus.UPDATE_ROLLBACK_IN_PROGRESS}throw new Error(`Stack not found or no stacks returned from DescribeStacks command, stackId: ${t}`)};var l=require("@aws-sdk/client-inspector2"),h=require("@aws-sdk/client-s3"),W=new l.Inspector2Client,K=new h.S3Client,sn=t=>new Promise(o=>setTimeout(o,t)),V=async(t,o,n,e,s)=>{let i=n==="SPDX_2_3"?l.SbomReportFormat.SPDX_2_3:l.SbomReportFormat.CYCLONEDX_1_4;console.log(`Starting SBOM export for ${t} with format ${n}...`);let a={ecrRepositoryName:[{comparison:"EQUALS",value:t}],...o?{ecrImageTags:[{comparison:"EQUALS",value:o}]}:{}},r=(await W.send(new l.CreateSbomExportCommand({reportFormat:i,s3Destination:{bucketName:e,keyPrefix:`sbom-exports/${t}`,kmsKeyArn:s},resourceFilterCriteria:a}))).reportId;if(!r)throw new Error("CreateSbomExport did not return a reportId.");console.log(`SBOM export started with reportId: ${r}`);let u=60,m=5;for(let d=0;d<u;d++){let g=await W.send(new l.GetSbomExportCommand({reportId:r})),f=g.status;if(console.log(`SBOM export status: ${f} (attempt ${d+1}/${u})`),f==="SUCCEEDED"){let p=g.s3Destination?.keyPrefix,b=g.s3Destination?.bucketName;if(!b||!p)throw new Error("SBOM export succeeded but S3 destination is missing.");let L=await on(b,p);if(!L)throw new Error(`SBOM export succeeded but no file found in S3 under prefix: ${p}`);return{sbomContent:await rn(b,L),format:n}}if(f==="FAILED"){let p=g.filterCriteria;throw new Error(`SBOM export failed. Filter criteria: ${JSON.stringify(p)}`)}if(f==="CANCELLED")throw new Error("SBOM export was cancelled.");await sn(m*1e3)}throw new Error(`SBOM export timed out after ${u*m} seconds.`)},on=async(t,o)=>(await K.send(new h.ListObjectsV2Command({Bucket:t,Prefix:o,MaxKeys:1}))).Contents?.[0]?.Key,rn=async(t,o)=>await(await K.send(new h.GetObjectCommand({Bucket:t,Key:o}))).Body?.transformToString()??"";var U=async function(t){let o=t.RequestType,n=t.ResourceProperties;if(!n.addr||!n.repositoryName)throw new Error("addr and repositoryName are required.");let e={PhysicalResourceId:n.addr,Data:{}};if(o!=="Create"&&o!=="Update")return e;let s=5,i=60,a=`${n.repositoryName}:${n.imageTag}`,c;n.startScan==="true"?c=await F(n.repositoryName,n.imageTag,n.scanType,s,i):c=await $(n.repositoryName,n.imageTag,n.scanType,s,i);let r=R(c,n.severity,n.ignoreFindings),u;if(n.sbom)if(n.scanType==="ENHANCED"){let b=await V(n.repositoryName,n.imageTag,n.sbom.format,n.sbom.bucketName,n.sbom.kmsKeyArn);u={content:b.sbomContent,format:b.format}}else console.log("SBOM export is only available with Enhanced scanning. Skipping SBOM generation.");let m=c.enhancedFindings.length>0?c.enhancedFindings:c.basicFindings,d=JSON.stringify(m,null,2),g=P(c,r,n.repositoryName,n.imageTag),f=await an(d,g,a,n.output,n.defaultLogGroupName,u);if(!r.hasVulnerabilities)return e;let p=`ECR Image Scan found vulnerabilities.
55
55
  Image: ${a}
56
56
  Scan Type: ${c.scanType}
57
57
  Findings: ${r.summary}
@@ -122,5 +122,5 @@ class EcrScanVerifier extends constructs_1.Construct {
122
122
  }
123
123
  exports.EcrScanVerifier = EcrScanVerifier;
124
124
  _a = JSII_RTTI_SYMBOL_1;
125
- EcrScanVerifier[_a] = { fqn: "ecr-scan-verifier.EcrScanVerifier", version: "0.0.2" };
125
+ EcrScanVerifier[_a] = { fqn: "ecr-scan-verifier.EcrScanVerifier", version: "0.0.4" };
126
126
  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZWNyLXNjYW4tdmVyaWZpZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvZWNyLXNjYW4tdmVyaWZpZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSwrQkFBNEI7QUFDNUIsNkNBQW9GO0FBRXBGLGlEQUFzRDtBQUN0RCx1REFBd0Y7QUFHeEYsbUVBQXdEO0FBQ3hELDJDQUFtRDtBQUtuRCxtQ0FBbUM7QUErR25DOzs7O0dBSUc7QUFDSCxNQUFhLGVBQWdCLFNBQVEsc0JBQVM7SUFHNUMsWUFBWSxLQUFnQixFQUFFLEVBQVUsRUFBRSxLQUEyQjtRQUNuRSxLQUFLLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBRWpCLElBQUksQ0FBQyxlQUFlLEdBQUcsS0FBSyxDQUFDLGVBQWUsQ0FBQztRQUM3QyxNQUFNLGFBQWEsR0FBRyw2Q0FBNkMsQ0FBQztRQUVwRSxNQUFNLG9CQUFvQixHQUFHLElBQUksOEJBQWlCLENBQUMsSUFBSSxFQUFFLHNCQUFzQixFQUFFO1lBQy9FLElBQUksRUFBRSxzQ0FBc0M7WUFDNUMsYUFBYTtZQUNiLE9BQU8sRUFBRSxvQkFBTyxDQUFDLFdBQVc7WUFDNUIsT0FBTyxFQUFFLGVBQWU7WUFDeEIsSUFBSSxFQUFFLGlCQUFJLENBQUMsU0FBUyxDQUFDLElBQUEsV0FBSSxFQUFDLFNBQVMsRUFBRSx1QkFBdUIsQ0FBQyxFQUFFO2dCQUM3RCx1QkFBdUI7Z0JBQ3ZCLCtGQUErRjtnQkFDL0Ysd0ZBQXdGO2dCQUN4RixPQUFPLEVBQUUsQ0FBQyxjQUFjLENBQUM7YUFDMUIsQ0FBQztZQUNGLFlBQVksRUFBRSx5QkFBWSxDQUFDLE1BQU07WUFDakMsT0FBTyxFQUFFLHNCQUFRLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQztZQUM5QixhQUFhLEVBQUUsQ0FBQztZQUNoQixRQUFRLEVBQUUsSUFBSSxDQUFDLGVBQWU7U0FDL0IsQ0FBQyxDQUFDO1FBRUgsTUFBTSxRQUFRLEdBQUcsS0FBSyxDQUFDLFFBQVEsSUFBSSxRQUFRLENBQUM7UUFFNUMsTUFBTSxnQkFBZ0IsR0FBRyxLQUFLLENBQUMsVUFBVSxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ2pELG1EQUFtRDtRQUNuRCxJQUFJLEtBQUssQ0FBQyxVQUFVLElBQUksZ0JBQWdCLENBQUMsUUFBUSxLQUFLLE9BQU8sRUFBRSxDQUFDO1lBQzlELE1BQU0sSUFBSSxLQUFLLENBQ2IsZ0lBQWdJLENBQ2pJLENBQUM7UUFDSixDQUFDO1FBRUQsTUFBTSxhQUFhLEdBQUcsS0FBSyxDQUFDLGNBQWMsRUFBRSxJQUFJLENBQUMsb0JBQW9CLENBQUMsQ0FBQztRQUV2RSwyQ0FBMkM7UUFDM0MsTUFBTSxVQUFVLEdBQUcsS0FBSyxDQUFDLFVBQVUsRUFBRSxJQUFJLENBQUMsb0JBQW9CLENBQUMsQ0FBQztRQUVoRSx1QkFBdUI7UUFDdkIsb0JBQW9CLENBQUMsZUFBZSxDQUNsQyxJQUFJLHlCQUFlLENBQUM7WUFDbEIsT0FBTyxFQUFFLENBQUMsK0JBQStCLEVBQUUsb0JBQW9CLENBQUM7WUFDaEUsU0FBUyxFQUFFLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxhQUFhLENBQUM7U0FDNUMsQ0FBQyxDQUNILENBQUM7UUFFRixJQUFJLGdCQUFnQixDQUFDLFFBQVEsS0FBSyxVQUFVLEVBQUUsQ0FBQztZQUM3QyxvQkFBb0IsQ0FBQyxlQUFlLENBQ2xDLElBQUkseUJBQWUsQ0FBQztnQkFDbEIsT0FBTyxFQUFFLENBQUMseUJBQXlCLEVBQUUseUJBQXlCLENBQUM7Z0JBQy9ELFNBQVMsRUFBRSxDQUFDLEdBQUcsQ0FBQzthQUNqQixDQUFDLENBQ0gsQ0FBQztRQUNKLENBQUM7UUFFRCxJQUFJLGdCQUFnQixDQUFDLFNBQVMsRUFBRSxDQUFDO1lBQy9CLG9CQUFvQixDQUFDLGVBQWUsQ0FDbEMsSUFBSSx5QkFBZSxDQUFDO2dCQUNsQixPQUFPLEVBQUUsQ0FBQyxvQkFBb0IsQ0FBQztnQkFDL0IsU0FBUyxFQUFFLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxhQUFhLENBQUM7YUFDNUMsQ0FBQyxDQUNILENBQUM7UUFDSixDQUFDO1FBRUQsdURBQXVEO1FBQ3ZELElBQUksVUFBVSxFQUFFLENBQUM7WUFDZixvQkFBb0IsQ0FBQyxlQUFlLENBQ2xDLElBQUkseUJBQWUsQ0FBQztnQkFDbEIsT0FBTyxFQUFFLENBQUMsNkJBQTZCLEVBQUUsMEJBQTBCLENBQUM7Z0JBQ3BFLFNBQVMsRUFBRSxDQUFDLEdBQUcsQ0FBQzthQUNqQixDQUFDLENBQ0gsQ0FBQztRQUNKLENBQUM7UUFFRCxJQUFJLEtBQUssQ0FBQyxzQkFBc0IsRUFBRSxDQUFDO1lBQ2pDLEtBQUssQ0FBQyxzQkFBc0IsQ0FBQyxZQUFZLENBQUMsb0JBQW9CLENBQUMsQ0FBQztRQUNsRSxDQUFDO1FBRUQsTUFBTSx1QkFBdUIsR0FBRyxLQUFLLENBQUMsdUJBQXVCLElBQUksSUFBSSxDQUFDO1FBQ3RFLElBQUksdUJBQXVCLEVBQUUsQ0FBQztZQUM1QixvQkFBb0IsQ0FBQyxlQUFlLENBQ2xDLElBQUkseUJBQWUsQ0FBQztnQkFDbEIsT0FBTyxFQUFFLENBQUMsK0JBQStCLENBQUM7Z0JBQzFDLFNBQVMsRUFBRSxDQUFDLG1CQUFLLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxDQUFDLE9BQU8sQ0FBQzthQUNwQyxDQUFDLENBQ0gsQ0FBQztRQUNKLENBQUM7UUFFRCxvRkFBb0Y7UUFDcEYscUJBQU8sQ0FBQyxFQUFFLENBQUMsbUJBQUssQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUM7WUFDN0IsS0FBSyxFQUFFLENBQUMsSUFBSSxFQUFFLEVBQUU7Z0JBQ2QsSUFDRSxJQUFJLFlBQVksZUFBZTtvQkFDL0IsSUFBSSxDQUFDLGdCQUFnQixFQUFFLElBQUksQ0FBQyxJQUFJLEtBQUssSUFBSSxDQUFDLGVBQWUsRUFBRSxJQUFJLENBQUMsSUFBSSxFQUNwRSxDQUFDO29CQUNELHlCQUFXLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxDQUFDLFlBQVksQ0FDL0IsbURBQW1ELEVBQ25ELGdIQUFnSCxDQUNqSCxDQUFDO2dCQUNKLENBQUM7WUFDSCxDQUFDO1NBQ0YsQ0FBQyxDQUFDO1FBRUgsTUFBTSxnQkFBZ0IsR0FBRyxJQUFJLDJCQUFRLENBQUMsSUFBSSxFQUFFLFVBQVUsRUFBRTtZQUN0RCxjQUFjLEVBQUUsb0JBQW9CO1NBQ3JDLENBQUMsQ0FBQztRQUVILE1BQU0sa0JBQWtCLEdBQStCO1lBQ3JELElBQUksRUFBRSxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUk7WUFDcEIsY0FBYyxFQUFFLEtBQUssQ0FBQyxVQUFVLENBQUMsY0FBYztZQUMvQyxRQUFRO1lBQ1IsUUFBUSxFQUFFLGdCQUFnQixDQUFDLFFBQVE7WUFDbkMsU0FBUyxFQUFFLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxTQUFTLENBQUM7WUFDN0MsUUFBUSxFQUFFLEtBQUssQ0FBQyxRQUFRLElBQUksQ0FBQyxnQkFBUSxDQUFDLFFBQVEsQ0FBQztZQUMvQyxtQkFBbUIsRUFBRSxNQUFNLENBQUMsS0FBSyxDQUFDLG1CQUFtQixJQUFJLElBQUksQ0FBQztZQUM5RCxjQUFjLEVBQUUsS0FBSyxDQUFDLGNBQWMsSUFBSSxFQUFFO1lBQzFDLE1BQU0sRUFBRSxhQUFhO1lBQ3JCLElBQUksRUFBRSxVQUFVO1lBQ2hCLHVCQUF1QixFQUFFLE1BQU0sQ0FBQyx1QkFBdUIsQ0FBQztZQUN4RCxhQUFhLEVBQUUsS0FBSyxDQUFDLHNCQUFzQixFQUFFLFFBQVE7WUFDckQsbUJBQW1CLEVBQ2pCLElBQUksQ0FBQyxlQUFlLEVBQUUsWUFBWSxJQUFJLGVBQWUsb0JBQW9CLENBQUMsWUFBWSxFQUFFO1NBQzNGLENBQUM7UUFFRixJQUFJLDRCQUFjLENBQUMsSUFBSSxFQUFFLFVBQVUsRUFBRTtZQUNuQyxZQUFZLEVBQUUseUJBQXlCO1lBQ3ZDLFVBQVUsRUFBRSxrQkFBa0I7WUFDOUIsWUFBWSxFQUFFLGdCQUFnQixDQUFDLFlBQVk7U0FDNUMsQ0FBQyxDQUFDO1FBRUgsS0FBSyxDQUFDLGVBQWUsRUFBRSxPQUFPLENBQUMsQ0FBQyxTQUFTLEVBQUUsRUFBRTtZQUMzQyxTQUFTLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNyQyxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRCxnQkFBZ0I7SUFDaEIsSUFBSSxnQkFBZ0I7UUFDbEIsT0FBTyxJQUFJLENBQUMsZUFBZSxDQUFDO0lBQzlCLENBQUM7O0FBN0lILDBDQThJQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcbmltcG9ydCB7IEFubm90YXRpb25zLCBBc3BlY3RzLCBDdXN0b21SZXNvdXJjZSwgRHVyYXRpb24sIFN0YWNrIH0gZnJvbSAnYXdzLWNkay1saWInO1xuaW1wb3J0IHsgSVJlcG9zaXRvcnkgfSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtZWNyJztcbmltcG9ydCB7IFBvbGljeVN0YXRlbWVudCB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1pYW0nO1xuaW1wb3J0IHsgQXJjaGl0ZWN0dXJlLCBDb2RlLCBSdW50aW1lLCBTaW5nbGV0b25GdW5jdGlvbiB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1sYW1iZGEnO1xuaW1wb3J0IHsgSUxvZ0dyb3VwIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWxvZ3MnO1xuaW1wb3J0IHsgSVRvcGljIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLXNucyc7XG5pbXBvcnQgeyBQcm92aWRlciB9IGZyb20gJ2F3cy1jZGstbGliL2N1c3RvbS1yZXNvdXJjZXMnO1xuaW1wb3J0IHsgQ29uc3RydWN0LCBJQ29uc3RydWN0IH0gZnJvbSAnY29uc3RydWN0cyc7XG5pbXBvcnQgeyBTY2FubmVyQ3VzdG9tUmVzb3VyY2VQcm9wcyB9IGZyb20gJy4vY3VzdG9tLXJlc291cmNlLXByb3BzJztcbmltcG9ydCB7IFNib21PdXRwdXQgfSBmcm9tICcuL3Nib20tb3V0cHV0JztcbmltcG9ydCB7IFNjYW5Db25maWcgfSBmcm9tICcuL3NjYW4tY29uZmlnJztcbmltcG9ydCB7IFNjYW5Mb2dzT3V0cHV0IH0gZnJvbSAnLi9zY2FuLWxvZ3Mtb3V0cHV0JztcbmltcG9ydCB7IFNldmVyaXR5IH0gZnJvbSAnLi90eXBlcyc7XG5cbi8qKlxuICogUHJvcGVydGllcyBmb3IgRWNyU2NhblZlcmlmaWVyIENvbnN0cnVjdC5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBFY3JTY2FuVmVyaWZpZXJQcm9wcyB7XG4gIC8qKlxuICAgKiBFQ1IgUmVwb3NpdG9yeSB0byBzY2FuLlxuICAgKi9cbiAgcmVhZG9ubHkgcmVwb3NpdG9yeTogSVJlcG9zaXRvcnk7XG5cbiAgLyoqXG4gICAqIEltYWdlIHRhZyBvciBkaWdlc3QgdG8gc2Nhbi5cbiAgICpcbiAgICogWW91IGNhbiBzcGVjaWZ5IGEgdGFnIChlLmcuLCAndjEuMCcsICdsYXRlc3QnKSBvciBhIGRpZ2VzdCAoZS5nLiwgJ3NoYTI1NjphYmMxMjMuLi4nKS5cbiAgICogSWYgdGhlIHZhbHVlIHN0YXJ0cyB3aXRoICdzaGEyNTY6JywgaXQgaXMgdHJlYXRlZCBhcyBhIGRpZ2VzdC5cbiAgICpcbiAgICogQGRlZmF1bHQgJ2xhdGVzdCdcbiAgICovXG4gIHJlYWRvbmx5IGltYWdlVGFnPzogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBTY2FuIGNvbmZpZ3VyYXRpb246IGJhc2ljIChFQ1IgbmF0aXZlKSBvciBlbmhhbmNlZCAoQW1hem9uIEluc3BlY3RvcikuXG4gICAqXG4gICAqIFVzZSBgU2NhbkNvbmZpZy5iYXNpYygpYCBmb3IgRUNSIG5hdGl2ZSBiYXNpYyBzY2FubmluZyxcbiAgICogb3IgYFNjYW5Db25maWcuZW5oYW5jZWQoKWAgZm9yIEFtYXpvbiBJbnNwZWN0b3IgZW5oYW5jZWQgc2Nhbm5pbmcuXG4gICAqL1xuICByZWFkb25seSBzY2FuQ29uZmlnOiBTY2FuQ29uZmlnO1xuXG4gIC8qKlxuICAgKiBTZXZlcml0eSB0aHJlc2hvbGQgZm9yIHZ1bG5lcmFiaWxpdHkgZGV0ZWN0aW9uLlxuICAgKlxuICAgKiBJZiB2dWxuZXJhYmlsaXRpZXMgYXQgb3IgYWJvdmUgYW55IG9mIHRoZSBzcGVjaWZpZWQgc2V2ZXJpdHkgbGV2ZWxzIGFyZSBmb3VuZCxcbiAgICogdGhlIHNjYW4gd2lsbCBiZSBjb25zaWRlcmVkIGFzIGhhdmluZyBmb3VuZCB2dWxuZXJhYmlsaXRpZXMuXG4gICAqXG4gICAqIEBkZWZhdWx0IFtTZXZlcml0eS5DUklUSUNBTF1cbiAgICovXG4gIHJlYWRvbmx5IHNldmVyaXR5PzogU2V2ZXJpdHlbXTtcblxuICAvKipcbiAgICogV2hldGhlciB0byBmYWlsIHRoZSBDbG91ZEZvcm1hdGlvbiBkZXBsb3ltZW50IGlmIHZ1bG5lcmFiaWxpdGllcyBhcmUgZGV0ZWN0ZWRcbiAgICogYWJvdmUgdGhlIHNldmVyaXR5IHRocmVzaG9sZC5cbiAgICpcbiAgICogQGRlZmF1bHQgdHJ1ZVxuICAgKi9cbiAgcmVhZG9ubHkgZmFpbE9uVnVsbmVyYWJpbGl0eT86IGJvb2xlYW47XG5cbiAgLyoqXG4gICAqIEZpbmRpbmcgSURzIHRvIGlnbm9yZSBkdXJpbmcgdnVsbmVyYWJpbGl0eSBldmFsdWF0aW9uLlxuICAgKlxuICAgKiBGb3IgYmFzaWMgc2Nhbm5pbmc6IENWRSBJRHMgKGUuZy4sICdDVkUtMjAyMy0zNzkyMCcpXG4gICAqIEZvciBlbmhhbmNlZCBzY2FubmluZzogZmluZGluZyBBUk5zIG9yIENWRSBJRHNcbiAgICpcbiAgICogQGRlZmF1bHQgLSBubyBmaW5kaW5ncyBpZ25vcmVkXG4gICAqL1xuICByZWFkb25seSBpZ25vcmVGaW5kaW5ncz86IHN0cmluZ1tdO1xuXG4gIC8qKlxuICAgKiBDb25maWd1cmF0aW9uIGZvciBzY2FuIGxvZ3Mgb3V0cHV0LlxuICAgKlxuICAgKiBAZGVmYXVsdCAtIHNjYW4gbG9ncyBvdXRwdXQgdG8gZGVmYXVsdCBsb2cgZ3JvdXAgY3JlYXRlZCBieSBTY2FubmVyIExhbWJkYS5cbiAgICovXG4gIHJlYWRvbmx5IHNjYW5Mb2dzT3V0cHV0PzogU2NhbkxvZ3NPdXRwdXQ7XG5cbiAgLyoqXG4gICAqIFNCT00gKFNvZnR3YXJlIEJpbGwgb2YgTWF0ZXJpYWxzKSBvdXRwdXQgY29uZmlndXJhdGlvbi5cbiAgICpcbiAgICogU0JPTSBleHBvcnQgdXNlcyBBbWF6b24gSW5zcGVjdG9yJ3MgQ3JlYXRlU2JvbUV4cG9ydCBBUEkgdG8gZ2VuZXJhdGUgU0JPTVxuICAgKiBhbmQgdXBsb2FkcyBpdCB0byBTMy5cbiAgICpcbiAgICogKipOb3RlKio6IFNCT00gZXhwb3J0IGlzIG9ubHkgYXZhaWxhYmxlIHdpdGggRW5oYW5jZWQgc2Nhbm5pbmcgKEFtYXpvbiBJbnNwZWN0b3IpLlxuICAgKiBVc2luZyB3aXRoIEJhc2ljIHNjYW5uaW5nIHdpbGwgdGhyb3cgYW4gZXJyb3IuXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gbm8gU0JPTSBvdXRwdXRcbiAgICovXG4gIHJlYWRvbmx5IHNib21PdXRwdXQ/OiBTYm9tT3V0cHV0O1xuXG4gIC8qKlxuICAgKiBUaGUgU2Nhbm5lciBMYW1iZGEgZnVuY3Rpb24ncyBkZWZhdWx0IGxvZyBncm91cC5cbiAgICpcbiAgICogSWYgeW91IHVzZSBFY3JTY2FuVmVyaWZpZXIgY29uc3RydWN0IG11bHRpcGxlIHRpbWVzIGluIHRoZSBzYW1lIHN0YWNrLFxuICAgKiB5b3UgbXVzdCBzcGVjaWZ5IHRoZSBzYW1lIGxvZyBncm91cCBmb3IgZWFjaCBjb25zdHJ1Y3QuXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gU2Nhbm5lciBMYW1iZGEgY3JlYXRlcyB0aGUgZGVmYXVsdCBsb2cgZ3JvdXAuXG4gICAqL1xuICByZWFkb25seSBkZWZhdWx0TG9nR3JvdXA/OiBJTG9nR3JvdXA7XG5cbiAgLyoqXG4gICAqIFN1cHByZXNzIGVycm9ycyBkdXJpbmcgcm9sbGJhY2sgc2Nhbm5lciBMYW1iZGEgZXhlY3V0aW9uLlxuICAgKlxuICAgKiBAZGVmYXVsdCB0cnVlXG4gICAqL1xuICByZWFkb25seSBzdXBwcmVzc0Vycm9yT25Sb2xsYmFjaz86IGJvb2xlYW47XG5cbiAgLyoqXG4gICAqIFNOUyB0b3BpYyBmb3IgdnVsbmVyYWJpbGl0eSBub3RpZmljYXRpb24uXG4gICAqXG4gICAqIFN1cHBvcnRzIEFXUyBDaGF0Ym90IG1lc3NhZ2UgZm9ybWF0LlxuICAgKlxuICAgKiBAZGVmYXVsdCAtIG5vIG5vdGlmaWNhdGlvblxuICAgKi9cbiAgcmVhZG9ubHkgdnVsbnNOb3RpZmljYXRpb25Ub3BpYz86IElUb3BpYztcblxuICAvKipcbiAgICogQ29uc3RydWN0cyB0byBibG9jayBpZiB2dWxuZXJhYmlsaXRpZXMgYXJlIGRldGVjdGVkLlxuICAgKlxuICAgKiBAZGVmYXVsdCAtIG5vIGNvbnN0cnVjdHMgdG8gYmxvY2tcbiAgICovXG4gIHJlYWRvbmx5IGJsb2NrQ29uc3RydWN0cz86IElDb25zdHJ1Y3RbXTtcbn1cblxuLyoqXG4gKiBBIENvbnN0cnVjdCB0aGF0IHZlcmlmaWVzIGNvbnRhaW5lciBpbWFnZSBzY2FuIGZpbmRpbmdzIHdpdGggRUNSIGltYWdlIHNjYW5uaW5nLlxuICogSXQgdXNlcyBhIExhbWJkYSBmdW5jdGlvbiBhcyBhIEN1c3RvbSBSZXNvdXJjZSBwcm92aWRlciB0byBjYWxsIEVDUiBzY2FuIEFQSXNcbiAqIGFuZCBldmFsdWF0ZSBzY2FuIGZpbmRpbmdzLlxuICovXG5leHBvcnQgY2xhc3MgRWNyU2NhblZlcmlmaWVyIGV4dGVuZHMgQ29uc3RydWN0IHtcbiAgcHJpdmF0ZSByZWFkb25seSBkZWZhdWx0TG9nR3JvdXA/OiBJTG9nR3JvdXA7XG5cbiAgY29uc3RydWN0b3Ioc2NvcGU6IENvbnN0cnVjdCwgaWQ6IHN0cmluZywgcHJvcHM6IEVjclNjYW5WZXJpZmllclByb3BzKSB7XG4gICAgc3VwZXIoc2NvcGUsIGlkKTtcblxuICAgIHRoaXMuZGVmYXVsdExvZ0dyb3VwID0gcHJvcHMuZGVmYXVsdExvZ0dyb3VwO1xuICAgIGNvbnN0IGxhbWJkYVB1cnBvc2UgPSAnQ3VzdG9tOjpFY3JTY2FuVmVyaWZpZXJDdXN0b21SZXNvdXJjZUxhbWJkYSc7XG5cbiAgICBjb25zdCBjdXN0b21SZXNvdXJjZUxhbWJkYSA9IG5ldyBTaW5nbGV0b25GdW5jdGlvbih0aGlzLCAnQ3VzdG9tUmVzb3VyY2VMYW1iZGEnLCB7XG4gICAgICB1dWlkOiAnYzU2Y2VlNmItNjc3NS01NDFiLWQxNzktYzE1MzVkODhhMGM4JyxcbiAgICAgIGxhbWJkYVB1cnBvc2UsXG4gICAgICBydW50aW1lOiBSdW50aW1lLk5PREVKU18yMl9YLFxuICAgICAgaGFuZGxlcjogJ2luZGV4LmhhbmRsZXInLFxuICAgICAgY29kZTogQ29kZS5mcm9tQXNzZXQoam9pbihfX2Rpcm5hbWUsICcuLi9hc3NldHMvbGFtYmRhL2Rpc3QnKSwge1xuICAgICAgICAvLyBleGNsdWRlIG5vZGVfbW9kdWxlc1xuICAgICAgICAvLyBiZWNhdXNlIHRoZSBuYXRpdmUgYmluYXJ5IG9mIHRoZSBpbnN0YWxsZWQgZXNidWlsZCBjaGFuZ2VzIGRlcGVuZGluZyBvbiB0aGUgY3B1IGFyY2hpdGVjdHVyZVxuICAgICAgICAvLyBhbmQgdGhlIGhhc2ggdmFsdWUgb2YgdGhlIGltYWdlIGFzc2V0IGNoYW5nZXMgZGVwZW5kaW5nIG9uIHRoZSBleGVjdXRpb24gZW52aXJvbm1lbnQuXG4gICAgICAgIGV4Y2x1ZGU6IFsnbm9kZV9tb2R1bGVzJ10sXG4gICAgICB9KSxcbiAgICAgIGFyY2hpdGVjdHVyZTogQXJjaGl0ZWN0dXJlLkFSTV82NCxcbiAgICAgIHRpbWVvdXQ6IER1cmF0aW9uLnNlY29uZHMoOTAwKSxcbiAgICAgIHJldHJ5QXR0ZW1wdHM6IDAsXG4gICAgICBsb2dHcm91cDogdGhpcy5kZWZhdWx0TG9nR3JvdXAsXG4gICAgfSk7XG5cbiAgICBjb25zdCBpbWFnZVRhZyA9IHByb3BzLmltYWdlVGFnID8/ICdsYXRlc3QnO1xuXG4gICAgY29uc3Qgc2NhbkNvbmZpZ091dHB1dCA9IHByb3BzLnNjYW5Db25maWcuYmluZCgpO1xuICAgIC8vIFZhbGlkYXRlOiBTQk9NIG91dHB1dCByZXF1aXJlcyBFbmhhbmNlZCBzY2FubmluZ1xuICAgIGlmIChwcm9wcy5zYm9tT3V0cHV0ICYmIHNjYW5Db25maWdPdXRwdXQuc2NhblR5cGUgPT09ICdCQVNJQycpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgJ1NCT00gb3V0cHV0IGlzIG9ubHkgYXZhaWxhYmxlIHdpdGggRW5oYW5jZWQgc2Nhbm5pbmcgKFNjYW5Db25maWcuZW5oYW5jZWQoKSkuIEJhc2ljIHNjYW5uaW5nIGRvZXMgbm90IHN1cHBvcnQgU0JPTSBnZW5lcmF0aW9uLicsXG4gICAgICApO1xuICAgIH1cblxuICAgIGNvbnN0IG91dHB1dE9wdGlvbnMgPSBwcm9wcy5zY2FuTG9nc091dHB1dD8uYmluZChjdXN0b21SZXNvdXJjZUxhbWJkYSk7XG5cbiAgICAvLyBTQk9NIG91dHB1dCAoaW5kZXBlbmRlbnQgZnJvbSBzY2FuIGxvZ3MpXG4gICAgY29uc3Qgc2JvbUNvbmZpZyA9IHByb3BzLnNib21PdXRwdXQ/LmJpbmQoY3VzdG9tUmVzb3VyY2VMYW1iZGEpO1xuXG4gICAgLy8gRUNSIHNjYW4gcGVybWlzc2lvbnNcbiAgICBjdXN0b21SZXNvdXJjZUxhbWJkYS5hZGRUb1JvbGVQb2xpY3koXG4gICAgICBuZXcgUG9saWN5U3RhdGVtZW50KHtcbiAgICAgICAgYWN0aW9uczogWydlY3I6RGVzY3JpYmVJbWFnZVNjYW5GaW5kaW5ncycsICdlY3I6RGVzY3JpYmVJbWFnZXMnXSxcbiAgICAgICAgcmVzb3VyY2VzOiBbcHJvcHMucmVwb3NpdG9yeS5yZXBvc2l0b3J5QXJuXSxcbiAgICAgIH0pLFxuICAgICk7XG5cbiAgICBpZiAoc2NhbkNvbmZpZ091dHB1dC5zY2FuVHlwZSA9PT0gJ0VOSEFOQ0VEJykge1xuICAgICAgY3VzdG9tUmVzb3VyY2VMYW1iZGEuYWRkVG9Sb2xlUG9saWN5KFxuICAgICAgICBuZXcgUG9saWN5U3RhdGVtZW50KHtcbiAgICAgICAgICBhY3Rpb25zOiBbJ2luc3BlY3RvcjI6TGlzdENvdmVyYWdlJywgJ2luc3BlY3RvcjI6TGlzdEZpbmRpbmdzJ10sXG4gICAgICAgICAgcmVzb3VyY2VzOiBbJyonXSxcbiAgICAgICAgfSksXG4gICAgICApO1xuICAgIH1cblxuICAgIGlmIChzY2FuQ29uZmlnT3V0cHV0LnN0YXJ0U2Nhbikge1xuICAgICAgY3VzdG9tUmVzb3VyY2VMYW1iZGEuYWRkVG9Sb2xlUG9saWN5KFxuICAgICAgICBuZXcgUG9saWN5U3RhdGVtZW50KHtcbiAgICAgICAgICBhY3Rpb25zOiBbJ2VjcjpTdGFydEltYWdlU2NhbiddLFxuICAgICAgICAgIHJlc291cmNlczogW3Byb3BzLnJlcG9zaXRvcnkucmVwb3NpdG9yeUFybl0sXG4gICAgICAgIH0pLFxuICAgICAgKTtcbiAgICB9XG5cbiAgICAvLyBTQk9NIGV4cG9ydCBwZXJtaXNzaW9ucyAoSW5zcGVjdG9yIENyZWF0ZVNib21FeHBvcnQpXG4gICAgaWYgKHNib21Db25maWcpIHtcbiAgICAgIGN1c3RvbVJlc291cmNlTGFtYmRhLmFkZFRvUm9sZVBvbGljeShcbiAgICAgICAgbmV3IFBvbGljeVN0YXRlbWVudCh7XG4gICAgICAgICAgYWN0aW9uczogWydpbnNwZWN0b3IyOkNyZWF0ZVNib21FeHBvcnQnLCAnaW5zcGVjdG9yMjpHZXRTYm9tRXhwb3J0J10sXG4gICAgICAgICAgcmVzb3VyY2VzOiBbJyonXSxcbiAgICAgICAgfSksXG4gICAgICApO1xuICAgIH1cblxuICAgIGlmIChwcm9wcy52dWxuc05vdGlmaWNhdGlvblRvcGljKSB7XG4gICAgICBwcm9wcy52dWxuc05vdGlmaWNhdGlvblRvcGljLmdyYW50UHVibGlzaChjdXN0b21SZXNvdXJjZUxhbWJkYSk7XG4gICAgfVxuXG4gICAgY29uc3Qgc3VwcHJlc3NFcnJvck9uUm9sbGJhY2sgPSBwcm9wcy5zdXBwcmVzc0Vycm9yT25Sb2xsYmFjayA/PyB0cnVlO1xuICAgIGlmIChzdXBwcmVzc0Vycm9yT25Sb2xsYmFjaykge1xuICAgICAgY3VzdG9tUmVzb3VyY2VMYW1iZGEuYWRkVG9Sb2xlUG9saWN5KFxuICAgICAgICBuZXcgUG9saWN5U3RhdGVtZW50KHtcbiAgICAgICAgICBhY3Rpb25zOiBbJ2Nsb3VkZm9ybWF0aW9uOkRlc2NyaWJlU3RhY2tzJ10sXG4gICAgICAgICAgcmVzb3VyY2VzOiBbU3RhY2sub2YodGhpcykuc3RhY2tJZF0sXG4gICAgICAgIH0pLFxuICAgICAgKTtcbiAgICB9XG5cbiAgICAvLyBDaGVjayBmb3IgZGVmYXVsdExvZ0dyb3VwIGNvbnNpc3RlbmN5IGFjcm9zcyBtdWx0aXBsZSBpbnN0YW5jZXMgaW4gdGhlIHNhbWUgc3RhY2tcbiAgICBBc3BlY3RzLm9mKFN0YWNrLm9mKHRoaXMpKS5hZGQoe1xuICAgICAgdmlzaXQ6IChub2RlKSA9PiB7XG4gICAgICAgIGlmIChcbiAgICAgICAgICBub2RlIGluc3RhbmNlb2YgRWNyU2NhblZlcmlmaWVyICYmXG4gICAgICAgICAgbm9kZS5fZGVmYXVsdExvZ0dyb3VwPy5ub2RlLnBhdGggIT09IHRoaXMuZGVmYXVsdExvZ0dyb3VwPy5ub2RlLnBhdGhcbiAgICAgICAgKSB7XG4gICAgICAgICAgQW5ub3RhdGlvbnMub2YodGhpcykuYWRkV2FybmluZ1YyKFxuICAgICAgICAgICAgJ0BlY3Itc2Nhbi12ZXJpZmllcjpkdXBsaWNhdGVMYW1iZGFEZWZhdWx0TG9nR3JvdXAnLFxuICAgICAgICAgICAgXCJZb3UgaGF2ZSB0byBzZXQgdGhlIHNhbWUgbG9nIGdyb3VwIGZvciAnZGVmYXVsdExvZ0dyb3VwJyBmb3IgZWFjaCBFY3JTY2FuVmVyaWZpZXIgY29uc3RydWN0IGluIHRoZSBzYW1lIHN0YWNrLlwiLFxuICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICBjb25zdCB2ZXJpZmllclByb3ZpZGVyID0gbmV3IFByb3ZpZGVyKHRoaXMsICdQcm92aWRlcicsIHtcbiAgICAgIG9uRXZlbnRIYW5kbGVyOiBjdXN0b21SZXNvdXJjZUxhbWJkYSxcbiAgICB9KTtcblxuICAgIGNvbnN0IHZlcmlmaWVyUHJvcGVydGllczogU2Nhbm5lckN1c3RvbVJlc291cmNlUHJvcHMgPSB7XG4gICAgICBhZGRyOiB0aGlzLm5vZGUuYWRkcixcbiAgICAgIHJlcG9zaXRvcnlOYW1lOiBwcm9wcy5yZXBvc2l0b3J5LnJlcG9zaXRvcnlOYW1lLFxuICAgICAgaW1hZ2VUYWcsXG4gICAgICBzY2FuVHlwZTogc2NhbkNvbmZpZ091dHB1dC5zY2FuVHlwZSxcbiAgICAgIHN0YXJ0U2NhbjogU3RyaW5nKHNjYW5Db25maWdPdXRwdXQuc3RhcnRTY2FuKSxcbiAgICAgIHNldmVyaXR5OiBwcm9wcy5zZXZlcml0eSA/PyBbU2V2ZXJpdHkuQ1JJVElDQUxdLFxuICAgICAgZmFpbE9uVnVsbmVyYWJpbGl0eTogU3RyaW5nKHByb3BzLmZhaWxPblZ1bG5lcmFiaWxpdHkgPz8gdHJ1ZSksXG4gICAgICBpZ25vcmVGaW5kaW5nczogcHJvcHMuaWdub3JlRmluZGluZ3MgPz8gW10sXG4gICAgICBvdXRwdXQ6IG91dHB1dE9wdGlvbnMsXG4gICAgICBzYm9tOiBzYm9tQ29uZmlnLFxuICAgICAgc3VwcHJlc3NFcnJvck9uUm9sbGJhY2s6IFN0cmluZyhzdXBwcmVzc0Vycm9yT25Sb2xsYmFjayksXG4gICAgICB2dWxuc1RvcGljQXJuOiBwcm9wcy52dWxuc05vdGlmaWNhdGlvblRvcGljPy50b3BpY0FybixcbiAgICAgIGRlZmF1bHRMb2dHcm91cE5hbWU6XG4gICAgICAgIHRoaXMuZGVmYXVsdExvZ0dyb3VwPy5sb2dHcm91cE5hbWUgPz8gYC9hd3MvbGFtYmRhLyR7Y3VzdG9tUmVzb3VyY2VMYW1iZGEuZnVuY3Rpb25OYW1lfWAsXG4gICAgfTtcblxuICAgIG5ldyBDdXN0b21SZXNvdXJjZSh0aGlzLCAnUmVzb3VyY2UnLCB7XG4gICAgICByZXNvdXJjZVR5cGU6ICdDdXN0b206OkVjclNjYW5WZXJpZmllcicsXG4gICAgICBwcm9wZXJ0aWVzOiB2ZXJpZmllclByb3BlcnRpZXMsXG4gICAgICBzZXJ2aWNlVG9rZW46IHZlcmlmaWVyUHJvdmlkZXIuc2VydmljZVRva2VuLFxuICAgIH0pO1xuXG4gICAgcHJvcHMuYmxvY2tDb25zdHJ1Y3RzPy5mb3JFYWNoKChjb25zdHJ1Y3QpID0+IHtcbiAgICAgIGNvbnN0cnVjdC5ub2RlLmFkZERlcGVuZGVuY3kodGhpcyk7XG4gICAgfSk7XG4gIH1cblxuICAvKiogQGludGVybmFsICovXG4gIGdldCBfZGVmYXVsdExvZ0dyb3VwKCk6IElMb2dHcm91cCB8IHVuZGVmaW5lZCB7XG4gICAgcmV0dXJuIHRoaXMuZGVmYXVsdExvZ0dyb3VwO1xuICB9XG59XG4iXX0=
@@ -30,7 +30,7 @@ class SbomOutput {
30
30
  }
31
31
  exports.SbomOutput = SbomOutput;
32
32
  _a = JSII_RTTI_SYMBOL_1;
33
- SbomOutput[_a] = { fqn: "ecr-scan-verifier.SbomOutput", version: "0.0.2" };
33
+ SbomOutput[_a] = { fqn: "ecr-scan-verifier.SbomOutput", version: "0.0.4" };
34
34
  class SbomOutputImpl extends SbomOutput {
35
35
  constructor(props, format) {
36
36
  super();
@@ -31,7 +31,7 @@ class ScanConfig {
31
31
  }
32
32
  exports.ScanConfig = ScanConfig;
33
33
  _a = JSII_RTTI_SYMBOL_1;
34
- ScanConfig[_a] = { fqn: "ecr-scan-verifier.ScanConfig", version: "0.0.2" };
34
+ ScanConfig[_a] = { fqn: "ecr-scan-verifier.ScanConfig", version: "0.0.4" };
35
35
  class BasicScanConfig extends ScanConfig {
36
36
  constructor(options) {
37
37
  super();
@@ -43,7 +43,7 @@ class ScanLogsOutput {
43
43
  }
44
44
  exports.ScanLogsOutput = ScanLogsOutput;
45
45
  _a = JSII_RTTI_SYMBOL_1;
46
- ScanLogsOutput[_a] = { fqn: "ecr-scan-verifier.ScanLogsOutput", version: "0.0.2" };
46
+ ScanLogsOutput[_a] = { fqn: "ecr-scan-verifier.ScanLogsOutput", version: "0.0.4" };
47
47
  class CloudWatchLogsOutput extends ScanLogsOutput {
48
48
  constructor(options) {
49
49
  super();
package/package.json CHANGED
@@ -60,7 +60,7 @@
60
60
  "publishConfig": {
61
61
  "access": "public"
62
62
  },
63
- "version": "0.0.2",
63
+ "version": "0.0.4",
64
64
  "types": "lib/index.d.ts",
65
65
  "stability": "stable",
66
66
  "jsii": {