├── .eslintrc ├── .github └── workflows │ ├── pr-conventional-title.yml │ ├── publish.yml │ ├── release-please.yml │ └── test.yml ├── .gitignore ├── .npmignore ├── .prettierrc ├── .release-please-manifest.json ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.md ├── lambda ├── build ├── index.ts ├── package-lock.json ├── package.json └── types.ts ├── lib ├── index.ts └── types.ts ├── package-lock.json ├── package.json ├── release-please-config.json ├── renovate.json ├── test ├── .npmignore ├── Makefile ├── bin │ └── test.ts ├── cdk.json ├── lib │ └── test-stack.ts ├── package-lock.json ├── package.json └── tsconfig.json └── tsconfig-lint.json /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "eslint:recommended", 4 | "plugin:@typescript-eslint/recommended-type-checked", 5 | "plugin:@typescript-eslint/stylistic-type-checked", 6 | "plugin:@typescript-eslint/recommended", 7 | "plugin:prettier/recommended" 8 | ], 9 | "parser": "@typescript-eslint/parser", 10 | "plugins": ["@typescript-eslint", "deprecation"], 11 | "root": true, 12 | "parserOptions": { 13 | "project": "./tsconfig-lint.json", 14 | "tsconfigRootDir": "." 15 | }, 16 | "ignorePatterns": ["**/*.js", "**/*.d.ts"], 17 | "rules": { 18 | "no-unused-vars": "off", 19 | "@typescript-eslint/no-unused-vars": [ 20 | "error", 21 | { 22 | "argsIgnorePattern": "^_", 23 | "varsIgnorePattern": "^_", 24 | "caughtErrorsIgnorePattern": "^_" 25 | } 26 | ], 27 | "deprecation/deprecation": "error", 28 | "prefer-template": "error", 29 | "@typescript-eslint/naming-convention": "error" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.github/workflows/pr-conventional-title.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Conventional PR Title 3 | 4 | on: 5 | pull_request_target: 6 | types: 7 | - opened 8 | - reopened 9 | - edited 10 | - synchronize 11 | 12 | jobs: 13 | conventional-pr-title: 14 | runs-on: ubuntu-latest 15 | permissions: 16 | statuses: write 17 | steps: 18 | - uses: aslafy-z/conventional-pr-title-action@v3 19 | env: 20 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 21 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Publish packages 3 | 4 | on: 5 | push: 6 | tags: 7 | - "v*" 8 | workflow_dispatch: 9 | 10 | jobs: 11 | publish: 12 | runs-on: ubuntu-latest 13 | 14 | container: 15 | image: public.ecr.aws/jsii/superchain:1-bookworm-slim-node20 16 | 17 | steps: 18 | - name: Checkout code 19 | uses: actions/checkout@v4 20 | with: 21 | fetch-depth: 1 22 | 23 | - name: Install dependencies 24 | run: | 25 | make install 26 | sudo apt-get update 27 | sudo apt-get install -y tree 28 | 29 | - name: Build source w/ jsii 30 | run: | 31 | lambda/build 32 | npx jsii 33 | make validate-package 34 | 35 | - name: Build packages w/ jsii-pacmak 36 | run: | 37 | npx jsii-pacmak --verbose 38 | tree dist 39 | 40 | - name: Publish to npm 41 | run: npx publib-npm 42 | env: 43 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 44 | 45 | - name: Publish to PyPI 46 | run: | 47 | export PIP_BREAK_SYSTEM_PACKAGES=1 48 | npx publib-pypi 49 | env: 50 | TWINE_USERNAME: __token__ 51 | TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} 52 | 53 | - name: Publish to NuGet 54 | run: npx publib-nuget 55 | env: 56 | NUGET_API_KEY: ${{ secrets.NUGET_TOKEN }} 57 | # 58 | # - name: Publish to Maven GitHub 59 | # run: npx publib-maven 60 | # env: 61 | # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 62 | # GITHUB_REPOSITORY: ${{ github.repository }} 63 | -------------------------------------------------------------------------------- /.github/workflows/release-please.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: release-please 3 | 4 | on: 5 | workflow_dispatch: 6 | push: 7 | branches: 8 | - main 9 | 10 | permissions: 11 | contents: write 12 | pull-requests: write 13 | 14 | jobs: 15 | release-please: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: google-github-actions/release-please-action@v4 19 | with: 20 | token: ${{ secrets.OVERRIDE_TOKEN }} 21 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Test 3 | 4 | concurrency: 5 | group: test 6 | cancel-in-progress: false 7 | 8 | on: 9 | pull_request: 10 | branches: 11 | - main 12 | workflow_dispatch: 13 | 14 | jobs: 15 | test: 16 | runs-on: ubuntu-latest 17 | 18 | defaults: 19 | run: 20 | shell: bash 21 | 22 | container: 23 | image: public.ecr.aws/jsii/superchain:1-bookworm-slim-node20 24 | 25 | steps: 26 | - uses: actions/setup-node@v4 27 | with: 28 | node-version: 20.x 29 | 30 | - name: Checkout code 31 | uses: actions/checkout@v4 32 | with: 33 | fetch-depth: 1 34 | 35 | - name: Cache node modules 36 | id: cache-npm 37 | uses: actions/cache@v4 38 | env: 39 | cache-name: cache-node-modules 40 | with: 41 | path: .npm 42 | key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} 43 | restore-keys: | 44 | ${{ runner.os }}-build-${{ env.cache-name }}- 45 | ${{ runner.os }}-build- 46 | ${{ runner.os }}- 47 | 48 | - name: Install dependencies 49 | run: | 50 | set -euo pipefail 51 | make install 52 | (cd test && make install) 53 | sudo apt-get update 54 | sudo apt-get install -y tree 55 | 56 | - name: Linting 57 | run: make eslint 58 | 59 | - name: Set Build Permissions 60 | run: | 61 | set -euo pipefail 62 | mkdir $GITHUB_WORKSPACE/test/cdk.out 63 | chmod 777 $GITHUB_WORKSPACE/test/cdk.out --recursive 64 | chmod 777 $GITHUB_WORKSPACE/lambda --recursive 65 | 66 | - name: Deploy & Destroy 67 | run: | 68 | set -euo pipefail 69 | 70 | function get_secret_values() { 71 | AWS_REGION=us-east-1 aws secretsmanager describe-secret \ 72 | --secret-id ec2-ssh-key/CFN-signing-key/private \ 73 | --query 'Tags[?Key==`Test`].Value' --output text 74 | } 75 | 76 | cd test 77 | make deploy get-secret-values 78 | if [ "$(get_secret_values)" != "default" ]; then 79 | echo "Unexpected secret value" 80 | exit 1 81 | fi 82 | 83 | TAG_VALUE=working make deploy 84 | if [ "$(get_secret_values)" != "working" ]; then 85 | echo "Unexpected secret value" 86 | exit 1 87 | fi 88 | 89 | make DESTROY 90 | env: 91 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 92 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 93 | 94 | - name: Build source w/ jsii 95 | run: | 96 | lambda/build 97 | npx jsii 98 | make validate-package 99 | 100 | - name: Build packages w/ jsii-pacmak 101 | run: | 102 | set -euo pipefail 103 | npx jsii-pacmak --verbose 104 | tree dist 105 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .jsii 2 | node_modules 3 | dist 4 | /tsconfig.json 5 | lambda/* 6 | !lambda/*.ts 7 | !lambda/build 8 | !lambda/package.json 9 | !lambda/package-lock.json 10 | **/*.js 11 | **/*.d.ts 12 | **/cdk.out 13 | **/cdk.context.json 14 | tsconfig.tsbuildinfo 15 | .npm 16 | *.tgz 17 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | 2 | # Exclude typescript source and config 3 | *.ts 4 | tsconfig.json 5 | 6 | # Include javascript files and typescript declarations 7 | !*.js 8 | !*.d.ts 9 | 10 | # Exclude jsii outdir 11 | dist 12 | 13 | # Include .jsii 14 | !.jsii 15 | 16 | test 17 | .npm 18 | .github 19 | .release-please-manifest.json 20 | CHANGELOG.md 21 | Makefile 22 | release-please-config.json 23 | renovate.json 24 | tsconfig.tsbuildinfo 25 | *.tgz 26 | .eslintrc 27 | .prettierrc 28 | tsconfig-lint.json 29 | publish_output.txt 30 | 31 | lambda/* 32 | !lambda/code.zip 33 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "overrides": [ 4 | { 5 | "files": ["*.yaml", "*.yml"], 6 | "options": { 7 | "singleQuote": false 8 | } 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.release-please-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | ".": "4.0.1" 3 | } 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [4.0.1](https://github.com/udondan/cdk-ec2-key-pair/compare/v4.0.0...v4.0.1) (2024-03-25) 4 | 5 | 6 | ### Bug Fixes 7 | 8 | * ensure all expected files are included in the resulting package ([#300](https://github.com/udondan/cdk-ec2-key-pair/issues/300)) ([509e42c](https://github.com/udondan/cdk-ec2-key-pair/commit/509e42c4fb9fd3d1667babffd11c3bfce0761b75)) 9 | 10 | ## [4.0.0](https://github.com/udondan/cdk-ec2-key-pair/compare/v3.3.3...v4.0.0) (2024-03-23) 11 | 12 | 13 | ### ⚠ BREAKING CHANGES 14 | 15 | * adds support for ED25519 Key Pairs and a wide range of public key formats ([#290](https://github.com/udondan/cdk-ec2-key-pair/issues/290)) 16 | * implements IKeyPair interface ([#279](https://github.com/udondan/cdk-ec2-key-pair/issues/279)) 17 | * renames lambda property to lambdaFunction ([#277](https://github.com/udondan/cdk-ec2-key-pair/issues/277)) 18 | * for consistency, the property name now is renamed to keyPairName ([#258](https://github.com/udondan/cdk-ec2-key-pair/issues/258)) 19 | * removes fixed name from lambda function ([#253](https://github.com/udondan/cdk-ec2-key-pair/issues/253)) 20 | 21 | ### Features 22 | 23 | * adds fingerprint and public key format as resource properties ([#291](https://github.com/udondan/cdk-ec2-key-pair/issues/291)) ([046e41d](https://github.com/udondan/cdk-ec2-key-pair/commit/046e41da8dd3b55f52f86f665b8857236373bc50)) 24 | * adds logLevel option, so users can debug lambda functions ([#286](https://github.com/udondan/cdk-ec2-key-pair/issues/286)) ([6f28d82](https://github.com/udondan/cdk-ec2-key-pair/commit/6f28d8224f1c2810d869c3bf2069a62bf4a6adcb)) 25 | * adds support for ED25519 Key Pairs and a wide range of public key formats ([#290](https://github.com/udondan/cdk-ec2-key-pair/issues/290)) ([35ece30](https://github.com/udondan/cdk-ec2-key-pair/commit/35ece30b405ce0d4e39980c328c2308b6218c70e)) 26 | * for consistency, the property name now is renamed to keyPairName ([#258](https://github.com/udondan/cdk-ec2-key-pair/issues/258)) ([a39e251](https://github.com/udondan/cdk-ec2-key-pair/commit/a39e2519d1c4b8e4de510f913e9d8464cdb2a480)) 27 | * implements IKeyPair interface ([#279](https://github.com/udondan/cdk-ec2-key-pair/issues/279)) ([0457985](https://github.com/udondan/cdk-ec2-key-pair/commit/045798526a71587640fc1e52156a79cc49ddff16)) 28 | * removes fixed name from lambda function ([#253](https://github.com/udondan/cdk-ec2-key-pair/issues/253)) ([56e17ef](https://github.com/udondan/cdk-ec2-key-pair/commit/56e17ef736dd174a1732745d484f1bd06731b13a)) 29 | 30 | 31 | ### Miscellaneous Chores 32 | 33 | * renames lambda property to lambdaFunction ([#277](https://github.com/udondan/cdk-ec2-key-pair/issues/277)) ([e43879a](https://github.com/udondan/cdk-ec2-key-pair/commit/e43879a83595ae9c3d1fa4aa9a64baecd9250af4)) 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash -euo pipefail 2 | 3 | NO_COLOR=\x1b[0m 4 | TARGET_COLOR=\x1b[96m 5 | 6 | build: 7 | @echo -e "$(TARGET_COLOR)Running build$(NO_COLOR)" 8 | @npm run build 9 | 10 | package: build 11 | @echo -e "$(TARGET_COLOR)Running package$(NO_COLOR)" 12 | @npm run package 13 | 14 | clean: 15 | @echo -e "$(TARGET_COLOR)Running clean$(NO_COLOR)" 16 | @rm -rf node_modules package-lock.json 17 | 18 | install: 19 | @echo -e "$(TARGET_COLOR)Running install$(NO_COLOR)" 20 | @npm clean-install --prefer-offline --cache .npm 21 | @npm list 22 | 23 | eslint: 24 | @echo -e "$(TARGET_COLOR)Running eslint $$(npx eslint --version)$(NO_COLOR)" 25 | @npx eslint .; \ 26 | echo "Passed" 27 | 28 | validate-package: 29 | @echo -e "$(TARGET_COLOR)Checking package content$(NO_COLOR)" 30 | @npm publish --dry-run 2>&1 | tee publish_output.txt 31 | @\ 32 | FILES_TO_CHECK="lambda/code.zip lib/index.d.ts lib/index.js lib/types.d.ts lib/types.js"; \ 33 | MISSING_FILES=""; \ 34 | for file in $$FILES_TO_CHECK; do \ 35 | if ! grep -q $$file publish_output.txt; then \ 36 | MISSING_FILES="$$MISSING_FILES $$file"; \ 37 | fi; \ 38 | done; \ 39 | if [ -n "$$MISSING_FILES" ]; then \ 40 | echo "❌ The following files are NOT included in the package:$$MISSING_FILES"; \ 41 | rm publish_output.txt; \ 42 | exit 1; \ 43 | fi 44 | @rm publish_output.txt 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CDK EC2 Key Pair 2 | 3 | [![Source](https://img.shields.io/badge/Source-GitHub-blue?logo=github)][source] 4 | [![Test](https://github.com/udondan/cdk-ec2-key-pair/workflows/Test/badge.svg)](https://github.com/udondan/cdk-ec2-key-pair/actions?query=workflow%3ATest) 5 | [![GitHub](https://img.shields.io/github/license/udondan/cdk-ec2-key-pair)][license] 6 | [![Docs](https://img.shields.io/badge/Construct%20Hub-cdk--ec2--key--pair-orange)][docs] 7 | 8 | [![npm package](https://img.shields.io/npm/v/cdk-ec2-key-pair?color=brightgreen)][npm] 9 | [![PyPI package](https://img.shields.io/pypi/v/cdk-ec2-key-pair?color=brightgreen)][PyPI] 10 | 11 | ![Downloads](https://img.shields.io/badge/-DOWNLOADS:-brightgreen?color=gray) 12 | [![npm](https://img.shields.io/npm/dt/cdk-ec2-key-pair?label=npm&color=blueviolet)][npm] 13 | [![PyPI](https://img.shields.io/pypi/dm/cdk-ec2-key-pair?label=pypi&color=blueviolet)][PyPI] 14 | 15 | [AWS CDK] L3 construct for managing [EC2 Key Pairs]. 16 | 17 | Manages RSA and ED25519 Key Pairs in EC2 through a Lambda function. 18 | 19 | Support for public key format in: 20 | 21 | - OpenSSH 22 | - ssh 23 | - PEM 24 | - PKCS#1 25 | - PKCS#8 26 | - RFC4253 (Base64 encoded) 27 | - PuTTY ppk 28 | 29 | > [!NOTE] 30 | > Please be aware, CloudFormation now natively supports creating EC2 Key Pairs via [AWS::EC2::KeyPair](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-keypair.html), so you can generally use [CDK's own KeyPair construct](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2.KeyPair.html). There are a few differences, though, and this is why the custom construct remains valuable: 31 | > 32 | > - Instead of SSM Parameter Store, keys are stored in [AWS Secrets Manager] 33 | > - Secrets can be **KMS encrypted** - even different KMS keys for the private and public keys. Of course, SSM parameters _can_ be encrypted too, CloudFormation just doesn't do it 34 | > - Optionally, this construct can store and expose the public key, enabling the user to directly use it as input for other resources, e.g. for CloudFront signed urls 35 | 36 | ## Installation 37 | 38 | This package has peer dependencies, which need to be installed along in the expected version. 39 | 40 | For TypeScript/NodeJS, add these to your `dependencies` in `package.json`. For Python, add these to your `requirements.txt`: 41 | 42 | - cdk-ec2-key-pair 43 | - aws-cdk-lib (^2.116.0) 44 | - constructs (^10.0.0) 45 | 46 | ## Usage 47 | 48 | ```typescript 49 | import cdk = require('aws-cdk-lib'); 50 | import { Construct } from 'constructs'; 51 | import { KeyPair } from 'cdk-ec2-key-pair'; 52 | 53 | // ... 54 | 55 | // Create the Key Pair 56 | const key = new KeyPair(this, 'A-Key-Pair', { 57 | keyPairName: 'a-key-pair', 58 | description: 'This is a Key Pair', 59 | storePublicKey: true, // by default the public key will not be stored in Secrets Manager 60 | }); 61 | 62 | // Grant read access to the private key to a role or user 63 | key.grantReadOnPrivateKey(someRole); 64 | 65 | // Grant read access to the public key to another role or user 66 | key.grantReadOnPublicKey(anotherRole); 67 | 68 | // Use Key Pair on an EC2 instance 69 | new ec2.Instance(this, 'An-Instance', { 70 | keyPair: key, 71 | // ... 72 | }); 73 | ``` 74 | 75 | The private (and optionally the public) key will be stored in AWS Secrets Manager. The secret names by default are prefixed with `ec2-ssh-key/`. The private key is suffixed with `/private`, the public key is suffixed with `/public`. So in this example they will be stored as `ec2-ssh-key/a-key-pair/private` and `ec2-ssh-key/a-key-pair/public`. 76 | 77 | To download the private key via AWS cli you can run: 78 | 79 | ```bash 80 | aws secretsmanager get-secret-value \ 81 | --secret-id ec2-ssh-key/a-key-pair/private \ 82 | --query SecretString \ 83 | --output text 84 | ``` 85 | 86 | ### Tag support 87 | 88 | The construct supports tagging: 89 | 90 | ```typescript 91 | cdk.Tags.of(key).add('someTag', 'some value'); 92 | ``` 93 | 94 | We also use tags to restrict update/delete actions to those, the construct created itself. The Lambda function, which backs the custom CFN resource, is not able to manipulate other keys/secrets. The tag we use for identifying these resources is `CreatedByCfnCustomResource` with value `CFN::Resource::Custom::EC2-Key-Pair`. 95 | 96 | ### Updates 97 | 98 | Since an EC2 KeyPair cannot be updated, you cannot change any property related to the KeyPair. The code has checks in place which will prevent any attempt to do so. If you try, the stack will end in a failed state. In that case you can safely continue the rollback in the AWS console and ignore the key resource. 99 | 100 | You can, however, change properties that only relate to the secrets. These are the KMS keys used for encryption, the `secretPrefix`, `description` and `removeKeySecretsAfterDays`. 101 | 102 | ### Encryption 103 | 104 | Secrets in the AWS Secrets Manager by default are encrypted with the key `alias/aws/secretsmanager`. 105 | 106 | To use a custom KMS key you can pass it to the Key Pair: 107 | 108 | ```typescript 109 | const kmsKey = new kms.Key(this, 'KMS-key'); 110 | 111 | const keyPair = new KeyPair(this, 'A-Key-Pair', { 112 | keyPairName: 'a-key-pair', 113 | kms: kmsKey, 114 | }); 115 | ``` 116 | 117 | This KMS key needs to be created in the same stack. You cannot use a key imported via ARN, because the keys access policy will need to be modified. 118 | 119 | To use different KMS keys for the private and public key, use the `kmsPrivateKey` and `kmsPublicKey` instead: 120 | 121 | ```typescript 122 | const kmsKeyPrivate = new kms.Key(this, 'KMS-key-private'); 123 | const kmsKeyPublic = new kms.Key(this, 'KMS-key-public'); 124 | 125 | const keyPair = new KeyPair(this, 'A-Key-Pair', { 126 | keyPairName: 'a-key-pair', 127 | kmsPrivateKey: kmsKeyPrivate, 128 | kmsPublicKey: kmsKeyPublic, 129 | }); 130 | ``` 131 | 132 | ### Importing public key 133 | 134 | You can create a key pair by importing the public key. Obviously, in this case the private key won't be available in secrets manager. 135 | 136 | The public key has to be in OpenSSH format. 137 | 138 | ```typescript 139 | new KeyPair(this, 'Test-Key-Pair', { 140 | keyPairName: 'imported-key-pair', 141 | publicKey: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCuMmbK...', 142 | }); 143 | ``` 144 | 145 | ### Using the key pair for CloudFront signed url/cookies 146 | 147 | You can use this library for generating keys for CloudFront signed url/cookies. 148 | 149 | Make sure to set `publicKeyFormat` to `PublicKeyFormat.PEM` as that is the format required for CloudFront. 150 | You also have to set `exposePublicKey` to `true` so you can actually get the public key. 151 | 152 | ```typescript 153 | const key = new KeyPair(this, 'Signing-Key-Pair', { 154 | keyPairName: 'CFN-signing-key', 155 | exposePublicKey: true, 156 | storePublicKey: true, 157 | publicKeyFormat: PublicKeyFormat.PEM, 158 | }); 159 | 160 | const pubKey = new cloudfront.PublicKey(this, 'Signing-Public-Key', { 161 | encodedKey: key.publicKeyValue, 162 | }); 163 | const trustedKeyGroupForCF = new cloudfront.KeyGroup( 164 | this, 165 | 'Signing-Key-Group', 166 | { 167 | items: [pubKey], 168 | }, 169 | ); 170 | ``` 171 | 172 | [AWS CDK]: https://aws.amazon.com/cdk/ 173 | [EC2 Key Pairs]: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html 174 | [AWS Secrets Manager]: https://aws.amazon.com/secrets-manager/ 175 | [npm]: https://www.npmjs.com/package/cdk-ec2-key-pair 176 | [PyPI]: https://pypi.org/project/cdk-ec2-key-pair/ 177 | [docs]: https://constructs.dev/packages/cdk-ec2-key-pair 178 | [source]: https://github.com/udondan/cdk-ec2-key-pair 179 | [license]: https://github.com/udondan/cdk-ec2-key-pair/blob/main/LICENSE 180 | -------------------------------------------------------------------------------- /lambda/build: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | cd "$( dirname "${BASH_SOURCE[0]}" )" 5 | npm clean-install --prefer-offline --cache ../.npm 6 | npx tsc ./*.ts 7 | zip -r code.zip ./*.js node_modules 8 | rm ./*.js 9 | -------------------------------------------------------------------------------- /lambda/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CreateKeyPairCommand, 3 | CreateKeyPairCommandInput, 4 | CreateTagsCommand, 5 | CreateTagsCommandInput, 6 | DeleteKeyPairCommand, 7 | DeleteKeyPairCommandInput, 8 | DeleteTagsCommand, 9 | DeleteTagsCommandInput, 10 | DescribeKeyPairsCommand, 11 | DescribeKeyPairsCommandInput, 12 | EC2Client, 13 | ImportKeyPairCommand, 14 | ImportKeyPairCommandInput, 15 | Tag as Ec2Tag, 16 | CreateKeyPairCommandOutput, 17 | KeyPairInfo, 18 | } from '@aws-sdk/client-ec2'; 19 | import { 20 | CreateSecretCommand, 21 | CreateSecretCommandInput, 22 | DeleteSecretCommand, 23 | DeleteSecretCommandInput, 24 | DeleteSecretCommandOutput, 25 | GetSecretValueCommand, 26 | GetSecretValueCommandInput, 27 | ListSecretsCommand, 28 | ListSecretsCommandInput, 29 | ResourceNotFoundException, 30 | SecretsManagerClient, 31 | Tag as SecretManagerTag, 32 | TagResourceCommand, 33 | TagResourceCommandInput, 34 | UntagResourceCommand, 35 | UntagResourceCommandInput, 36 | UpdateSecretCommand, 37 | UpdateSecretCommandInput, 38 | } from '@aws-sdk/client-secrets-manager'; 39 | import { 40 | Context, 41 | Callback, 42 | CustomResource, 43 | Event, 44 | LogLevel, 45 | Logger, 46 | StandardLogger, 47 | } from 'aws-cloudformation-custom-resource'; 48 | import { parsePrivateKey } from 'sshpk'; 49 | import { PublicKeyFormat, ResourceProperties } from './types'; 50 | 51 | const ec2Client = new EC2Client({}); 52 | const secretsManagerClient = new SecretsManagerClient({}); 53 | export const handler = function ( 54 | event: Event, 55 | context: Context, 56 | callback: Callback, 57 | ) { 58 | const resource = new CustomResource( 59 | event, 60 | context, 61 | callback, 62 | createResource, 63 | updateResource, 64 | deleteResource, 65 | ); 66 | 67 | if (event.ResourceProperties.LogLevel) { 68 | resource.setLogger( 69 | new StandardLogger( 70 | // because jsii is forcing us to expose enums with all capitals and the enum in aws-cloudformation-custom-resource is all lowercase, we need to cast here. Other than the capitalization, the enums are identical 71 | event.ResourceProperties.LogLevel as unknown as LogLevel, 72 | ), 73 | ); 74 | } 75 | }; 76 | 77 | async function createResource( 78 | resource: CustomResource, 79 | log: Logger, 80 | ): Promise { 81 | log.debug('called function createResource'); 82 | log.info( 83 | `Attempting to create EC2 Key Pair ${resource.properties.Name.value}`, 84 | ); 85 | 86 | const keyPair = await createKeyPair(resource, log); 87 | await createPrivateKeySecret(resource, keyPair, log); 88 | await createPublicKeySecret(resource, log, keyPair); 89 | await exposePublicKey(resource, log, keyPair); 90 | } 91 | 92 | async function updateResource( 93 | resource: CustomResource, 94 | log: Logger, 95 | ): Promise { 96 | log.debug('called function updateResource'); 97 | log.info( 98 | `Attempting to update EC2 Key Pair ${resource.properties.Name.value}`, 99 | ); 100 | 101 | if (resource.properties.Name.changed) { 102 | throw new Error( 103 | 'A Key Pair cannot be renamed. Please create a new Key Pair instead', 104 | ); 105 | } else if (resource.properties.StorePublicKey?.changed) { 106 | throw new Error( 107 | 'Once created, a key cannot be modified or accessed. Therefore the public key can only be stored, when the key is created.', 108 | ); 109 | } else if (resource.properties.PublicKey.changed) { 110 | throw new Error( 111 | 'You cannot change the public key of an exiting key pair. Please delete the key pair and create a new one.', 112 | ); 113 | } 114 | 115 | const oldKeyType = resource.event.OldResourceProperties?.KeyType ?? 'rsa'; // we added this feature later, so there might be keys w/o a stored type 116 | if (resource.event.ResourceProperties.KeyType !== oldKeyType) { 117 | throw new Error( 118 | 'The type of a key pair cannot be changed. Please create a new key pair instead', 119 | ); 120 | } 121 | 122 | const keyPair = await updateKeyPair(resource, log); 123 | await updateKeyPairAddTags(resource, log, keyPair.KeyPairId!); 124 | await updateKeyPairRemoveTags(resource, log, keyPair.KeyPairId!); 125 | if (!resource.properties.PublicKey.value.length) { 126 | // in case we imported a public key, there is no private key secret 127 | const secretId = `${resource.properties.SecretPrefix.value}${resource.properties.Name.value}/private`; 128 | await updatePrivateKeySecret(resource, log); 129 | await updateSecretAddTags(resource, log, secretId); 130 | await updateSecretRemoveTags(resource, log, secretId); 131 | } 132 | if (resource.properties.StorePublicKey?.value === 'true') { 133 | // in case user did not want to store the public key, there is no public key secret 134 | const secretId = `${resource.properties.SecretPrefix.value}${resource.properties.Name.value}/public`; 135 | await updatePublicKeySecret(resource, log); 136 | await updateSecretAddTags(resource, log, secretId); 137 | await updateSecretRemoveTags(resource, log, secretId); 138 | } 139 | await exposePublicKey(resource, log, keyPair); 140 | } 141 | 142 | async function deleteResource( 143 | resource: CustomResource, 144 | log: Logger, 145 | ): Promise { 146 | log.debug('called function deleteResource'); 147 | log.info( 148 | `Attempting to delete EC2 Key Pair ${resource.properties.Name.value}`, 149 | ); 150 | 151 | await deleteKeyPair(resource, log); 152 | await deletePrivateKeySecret(resource, log); 153 | await deletePublicKeySecret(resource, log); 154 | } 155 | 156 | async function createKeyPair( 157 | resource: CustomResource, 158 | log: Logger, 159 | ): Promise { 160 | log.debug('called function createKeyPair'); 161 | if ( 162 | // public key provided, let's import 163 | resource.properties.PublicKey?.value.length 164 | ) { 165 | const params: ImportKeyPairCommandInput = { 166 | /* eslint-disable @typescript-eslint/naming-convention */ 167 | KeyName: resource.properties.Name.value, 168 | PublicKeyMaterial: Buffer.from(resource.properties.PublicKey.value), 169 | TagSpecifications: [ 170 | { 171 | ResourceType: 'key-pair', 172 | Tags: makeTags(resource, resource.properties.Tags.value), 173 | }, 174 | ], 175 | /* eslint-enable @typescript-eslint/naming-convention */ 176 | }; 177 | log.debug('ec2.importKeyPair:', JSON.stringify(params, null, 2)); 178 | try { 179 | const result = await ec2Client.send(new ImportKeyPairCommand(params)); 180 | log.debug('Import successful', JSON.stringify(result, null, 2)); 181 | resource.addResponseValue('KeyPairName', result.KeyName!); 182 | resource.addResponseValue('KeyPairID', result.KeyPairId!); 183 | resource.addResponseValue('KeyFingerprint', result.KeyFingerprint!); 184 | return result; 185 | } catch (error) { 186 | log.error('Import failed', error); 187 | throw error; 188 | } 189 | } else { 190 | // no public key provided. create new key 191 | const params: CreateKeyPairCommandInput = { 192 | /* eslint-disable @typescript-eslint/naming-convention */ 193 | KeyName: resource.properties.Name.value, 194 | KeyType: resource.properties.KeyType.value, 195 | TagSpecifications: [ 196 | { 197 | ResourceType: 'key-pair', 198 | Tags: makeTags(resource, resource.properties.Tags.value), 199 | }, 200 | ], 201 | /* eslint-enable @typescript-eslint/naming-convention */ 202 | }; 203 | log.debug('ec2.createKeyPair:', JSON.stringify(params, null, 2)); 204 | const result = await ec2Client.send(new CreateKeyPairCommand(params)); 205 | resource.addResponseValue('KeyPairName', result.KeyName!); 206 | resource.addResponseValue('KeyPairID', result.KeyPairId!); 207 | resource.addResponseValue('KeyPairFingerprint', result.KeyFingerprint!); 208 | return result; 209 | } 210 | } 211 | 212 | async function updateKeyPair( 213 | resource: CustomResource, 214 | log: Logger, 215 | ): Promise { 216 | log.debug('called function updateKeyPair'); 217 | 218 | // there is nothing to update. a key cannot be changed 219 | // though we use this step to enrich the event with the keyId 220 | const params: DescribeKeyPairsCommandInput = { 221 | /* eslint-disable-next-line @typescript-eslint/naming-convention */ 222 | KeyNames: [resource.properties.Name.value], 223 | }; 224 | log.debug('ec2.describeKeyPairs:', JSON.stringify(params, null, 2)); 225 | const result = await ec2Client.send(new DescribeKeyPairsCommand(params)); 226 | 227 | if (result.KeyPairs?.length != 1) { 228 | throw new Error('Key pair was not found'); 229 | } 230 | 231 | const keyPair = result.KeyPairs[0]; 232 | resource.addResponseValue('KeyPairName', keyPair.KeyName!); 233 | resource.addResponseValue('KeyPairID', keyPair.KeyPairId!); 234 | resource.addResponseValue('KeyPairFingerprint', keyPair.KeyFingerprint!); 235 | return keyPair; 236 | } 237 | 238 | async function updateKeyPairAddTags( 239 | resource: CustomResource, 240 | log: Logger, 241 | keyPairId: string, 242 | ): Promise { 243 | log.debug('called function updateKeyPairAddTags'); 244 | log.info( 245 | `Attempting to update tags for Key Pair ${resource.properties.Name.value}`, 246 | ); 247 | 248 | if (!resource.properties.Tags.changed) { 249 | log.info( 250 | `No changes of tags detected for Key Pair ${resource.properties.Name.value}. Not attempting any update`, 251 | ); 252 | return; 253 | } 254 | 255 | const params: CreateTagsCommandInput = { 256 | /* eslint-disable @typescript-eslint/naming-convention */ 257 | Resources: [keyPairId], 258 | Tags: makeTags(resource, resource.properties.Tags.value), 259 | /* eslint-enable @typescript-eslint/naming-convention */ 260 | }; 261 | log.debug('ec2.createTags:', JSON.stringify(params, null, 2)); 262 | await ec2Client.send(new CreateTagsCommand(params)); 263 | } 264 | 265 | async function updateKeyPairRemoveTags( 266 | resource: CustomResource, 267 | log: Logger, 268 | keyPairId: string, 269 | ): Promise { 270 | log.debug('called function updateKeyPairRemoveTags'); 271 | log.info( 272 | `Attempting to remove some tags for Key Pair ${resource.properties.Name.value}`, 273 | ); 274 | 275 | if (!resource.properties.Tags.changed) { 276 | log.info( 277 | `No changes of tags detected for Key Pair ${resource.properties.Name.value}. Not attempting any update`, 278 | ); 279 | return; 280 | } 281 | 282 | const oldTags = makeTags( 283 | resource, 284 | resource.properties.Tags.before, 285 | ); 286 | const newTags = makeTags( 287 | resource, 288 | resource.properties.Tags.value, 289 | ); 290 | const tagsToRemove = getMissingTags(oldTags, newTags); 291 | if (!tagsToRemove.length) { 292 | log.info( 293 | `No changes of tags detected for Key Pair ${resource.properties.Name.value}. Not attempting any update`, 294 | ); 295 | return; 296 | } 297 | 298 | log.info( 299 | 'Will remove the following tags:', 300 | JSON.stringify(tagsToRemove, null, 2), 301 | ); 302 | const params: DeleteTagsCommandInput = { 303 | /* eslint-disable @typescript-eslint/naming-convention */ 304 | Resources: [keyPairId], 305 | Tags: tagsToRemove.map((key) => { 306 | return { 307 | Key: key, 308 | Value: resource.properties.Tags.before![key], 309 | } as Ec2Tag; 310 | }), 311 | /* eslint-enable @typescript-eslint/naming-convention */ 312 | }; 313 | log.debug('ec2.deleteTags:', JSON.stringify(params, null, 2)); 314 | await ec2Client.send(new DeleteTagsCommand(params)); 315 | } 316 | 317 | async function deleteKeyPair( 318 | resource: CustomResource, 319 | log: Logger, 320 | ): Promise { 321 | log.debug('called function deleteKeyPair'); 322 | const keyPairName = resource.properties.Name.value; 323 | if (!(await keyPairExists(keyPairName, log))) { 324 | log.warn(`Key Pair "${keyPairName}" does not exist. Nothing to delete`); 325 | return; 326 | } 327 | const params: DeleteKeyPairCommandInput = { 328 | /* eslint-disable-next-line @typescript-eslint/naming-convention */ 329 | KeyName: resource.properties.Name.value, 330 | }; 331 | log.debug('ec2.deleteKeyPair:', JSON.stringify(params, null, 2)); 332 | await ec2Client.send(new DeleteKeyPairCommand(params)); 333 | } 334 | 335 | async function createPrivateKeySecret( 336 | resource: CustomResource, 337 | keyPair: CreateKeyPairCommandOutput, 338 | log: Logger, 339 | ): Promise { 340 | log.debug('called function createPrivateKeySecret'); 341 | if (resource.properties.PublicKey.value.length) { 342 | resource.addResponseValue('PrivateKeyARN', ''); 343 | return; 344 | } 345 | 346 | try { 347 | const params: CreateSecretCommandInput = { 348 | /* eslint-disable @typescript-eslint/naming-convention */ 349 | Name: `${resource.properties.SecretPrefix.value}${resource.properties.Name.value}/private`, 350 | Description: `${resource.properties.Description.value} (Private Key)`, 351 | SecretString: keyPair.KeyMaterial, 352 | KmsKeyId: resource.properties.KmsPrivate.value, 353 | Tags: makeTags(resource, resource.properties.Tags.value), 354 | /* eslint-enable @typescript-eslint/naming-convention */ 355 | }; 356 | log.debug('secretsmanager.createSecret:', JSON.stringify(params, null, 2)); 357 | const result = await secretsManagerClient.send( 358 | new CreateSecretCommand(params), 359 | ); 360 | resource.addResponseValue('PrivateKeyARN', result.ARN!); 361 | } catch (error) { 362 | log.error('FAILED TO CREATE PRIVATE KEY', error); 363 | throw error; 364 | } 365 | } 366 | 367 | async function createPublicKeySecret( 368 | resource: CustomResource, 369 | log: Logger, 370 | keyPair: CreateKeyPairCommandOutput, 371 | ): Promise { 372 | log.debug('called function createPublicKeySecret'); 373 | 374 | let publicKey: string; 375 | if (resource.properties.PublicKey.value.length) 376 | publicKey = resource.properties.PublicKey.value; 377 | else { 378 | publicKey = await makePublicKey(resource, log, keyPair); 379 | } 380 | 381 | if (resource.properties.StorePublicKey?.value !== 'true') { 382 | return; 383 | } 384 | 385 | const params: CreateSecretCommandInput = { 386 | /* eslint-disable @typescript-eslint/naming-convention */ 387 | Name: `${resource.properties.SecretPrefix.value}${resource.properties.Name.value}/public`, 388 | Description: `${resource.properties.Description.value} (Public Key)`, 389 | SecretString: publicKey, 390 | KmsKeyId: resource.properties.KmsPublic.value, 391 | Tags: makeTags(resource, resource.properties.Tags.value), 392 | /* eslint-enable @typescript-eslint/naming-convention */ 393 | }; 394 | log.debug('secretsmanager.createSecret:', JSON.stringify(params, null, 2)); 395 | const result = await secretsManagerClient.send( 396 | new CreateSecretCommand(params), 397 | ); 398 | resource.addResponseValue('PublicKeyARN', result.ARN!); 399 | } 400 | 401 | async function updatePrivateKeySecret( 402 | resource: CustomResource, 403 | log: Logger, 404 | ): Promise { 405 | log.debug('called function updatePrivateKeySecret'); 406 | const params: UpdateSecretCommandInput = { 407 | /* eslint-disable @typescript-eslint/naming-convention */ 408 | SecretId: `${resource.properties.SecretPrefix.value}${resource.properties.Name.value}/private`, 409 | Description: `${resource.properties.Description.value} (Private key)`, 410 | KmsKeyId: resource.properties.KmsPrivate.value, 411 | /* eslint-enable @typescript-eslint/naming-convention */ 412 | }; 413 | log.debug('secretsmanager.updateSecret:', JSON.stringify(params, null, 2)); 414 | const result = await secretsManagerClient.send( 415 | new UpdateSecretCommand(params), 416 | ); 417 | resource.addResponseValue('PrivateKeyARN', result.ARN!); 418 | } 419 | 420 | async function updatePublicKeySecret( 421 | resource: CustomResource, 422 | log: Logger, 423 | ): Promise { 424 | log.debug('called function updatePublicKeySecret'); 425 | const arn = `${resource.properties.SecretPrefix.value}${resource.properties.Name.value}/public`; 426 | if (!(await secretExists(arn, log))) { 427 | return; 428 | } 429 | 430 | const params: UpdateSecretCommandInput = { 431 | /* eslint-disable @typescript-eslint/naming-convention */ 432 | SecretId: arn, 433 | Description: `${resource.properties.Description.value} (Public Key)`, 434 | KmsKeyId: resource.properties.KmsPublic.value, 435 | /* eslint-enable @typescript-eslint/naming-convention */ 436 | }; 437 | log.debug('secretsmanager.updateSecret:', JSON.stringify(params, null, 2)); 438 | const data = await secretsManagerClient.send(new UpdateSecretCommand(params)); 439 | resource.addResponseValue('PublicKeyARN', data.ARN!); 440 | } 441 | 442 | async function updateSecretAddTags( 443 | resource: CustomResource, 444 | log: Logger, 445 | secretId: string, 446 | ): Promise { 447 | log.debug('called function updateSecretAddTags'); 448 | log.info(`Attempting to update tags for secret ${secretId}`); 449 | if (!resource.properties.Tags.changed) { 450 | log.info( 451 | `No changes of tags detected for secret ${secretId}. Not attempting any update`, 452 | ); 453 | return; 454 | } 455 | const params: TagResourceCommandInput = { 456 | /* eslint-disable @typescript-eslint/naming-convention */ 457 | SecretId: secretId, 458 | Tags: makeTags(resource, resource.properties.Tags.value), 459 | /* eslint-enable @typescript-eslint/naming-convention */ 460 | }; 461 | log.debug('secretsmanager.tagResource:', JSON.stringify(params, null, 2)); 462 | await secretsManagerClient.send(new TagResourceCommand(params)); 463 | } 464 | 465 | async function getPrivateKey( 466 | resource: CustomResource, 467 | log: Logger, 468 | ): Promise { 469 | log.debug('called function getPrivateKey'); 470 | const params: GetSecretValueCommandInput = { 471 | /* eslint-disable-next-line @typescript-eslint/naming-convention */ 472 | SecretId: `${resource.properties.SecretPrefix.value}${resource.properties.Name.value}/private`, 473 | }; 474 | log.debug('secretsmanager.getSecretValue:', JSON.stringify(params, null, 2)); 475 | const result = await secretsManagerClient.send( 476 | new GetSecretValueCommand(params), 477 | ); 478 | return result.SecretString!; 479 | } 480 | 481 | async function makePublicKey( 482 | resource: CustomResource, 483 | log: Logger, 484 | keyPair: CreateKeyPairCommandOutput | KeyPairInfo, 485 | ): Promise { 486 | log.debug('called function makePublicKey'); 487 | 488 | const keyMaterial = 489 | (keyPair as CreateKeyPairCommandOutput).KeyMaterial ?? 490 | (await getPrivateKey(resource, log)); 491 | 492 | const privateKey = parsePrivateKey(keyMaterial); 493 | privateKey.comment = resource.properties.Name.value; 494 | return privateKey 495 | .toPublic() 496 | .toString(resource.properties.PublicKeyFormat.value); 497 | } 498 | 499 | async function exposePublicKey( 500 | resource: CustomResource, 501 | log: Logger, 502 | keyPair: CreateKeyPairCommandOutput | KeyPairInfo, 503 | ): Promise { 504 | log.debug('called function exposePublicKey'); 505 | if (resource.properties.ExposePublicKey?.value == 'true') { 506 | let publicKey: string; 507 | if (resource.properties.PublicKey.value.length) { 508 | publicKey = resource.properties.PublicKey.value; 509 | } else { 510 | publicKey = await makePublicKey(resource, log, keyPair); 511 | } 512 | if (resource.properties.PublicKeyFormat.value === PublicKeyFormat.RFC4253) { 513 | // CloudFormation cannot deal with binary data, so we need to encode the public key 514 | publicKey = Buffer.from(publicKey).toString('base64'); 515 | } 516 | resource.addResponseValue('PublicKeyValue', publicKey); 517 | } else { 518 | resource.addResponseValue( 519 | 'PublicKeyValue', 520 | 'Not requested - Set ExposePublicKey to true', 521 | ); 522 | } 523 | } 524 | 525 | async function updateSecretRemoveTags( 526 | resource: CustomResource, 527 | log: Logger, 528 | secretId: string, 529 | ): Promise { 530 | log.debug('called function updateSecretRemoveTags'); 531 | log.info(`Attempting to remove some tags for secret ${secretId}`); 532 | if (!resource.properties.Tags.changed) { 533 | log.info( 534 | `No changes of tags detected for secret ${secretId}. Not attempting any update`, 535 | ); 536 | return; 537 | } 538 | 539 | const oldTags = makeTags( 540 | resource, 541 | resource.properties.Tags.before, 542 | ); 543 | const newTags = makeTags( 544 | resource, 545 | resource.properties.Tags.value, 546 | ); 547 | const tagsToRemove = getMissingTags(oldTags, newTags); 548 | if (!tagsToRemove.length) { 549 | log.info( 550 | `No changes of tags detected for secret ${secretId}. Not attempting any update`, 551 | ); 552 | return; 553 | } 554 | 555 | log.info( 556 | 'Will remove the following tags:', 557 | JSON.stringify(tagsToRemove, null, 2), 558 | ); 559 | const params: UntagResourceCommandInput = { 560 | /* eslint-disable @typescript-eslint/naming-convention */ 561 | SecretId: secretId, 562 | TagKeys: tagsToRemove, 563 | /* eslint-enable @typescript-eslint/naming-convention */ 564 | }; 565 | log.debug('secretsmanager.untagResource:', JSON.stringify(params, null, 2)); 566 | await secretsManagerClient.send(new UntagResourceCommand(params)); 567 | } 568 | 569 | async function deletePrivateKeySecret( 570 | resource: CustomResource, 571 | log: Logger, 572 | ): Promise { 573 | log.debug('called function deletePrivateKeySecret'); 574 | const arn = `${resource.properties.SecretPrefix.value}${resource.properties.Name.value}/private`; 575 | if (!(await secretExists(arn, log))) { 576 | log.warn(`Secret "${arn}" does not exist. Nothing to delete`); 577 | return; 578 | } 579 | const result = await deleteSecret(resource, log, arn); 580 | resource.addResponseValue('PrivateKeyARN', result.ARN!); 581 | } 582 | 583 | async function deletePublicKeySecret( 584 | resource: CustomResource, 585 | log: Logger, 586 | ): Promise { 587 | log.debug('called function deletePublicKeySecret'); 588 | const arn = `${resource.properties.SecretPrefix.value}${resource.properties.Name.value}/public`; 589 | if (!(await secretExists(arn, log))) { 590 | log.warn(`Secret "${arn}" does not exist. Nothing to delete`); 591 | return; 592 | } 593 | const result = await deleteSecret(resource, log, arn); 594 | resource.addResponseValue('PublicKeyARN', result.ARN!); 595 | } 596 | 597 | async function secretExists(name: string, log: Logger): Promise { 598 | log.debug('called function secretExists'); 599 | const params: ListSecretsCommandInput = { 600 | /* eslint-disable @typescript-eslint/naming-convention */ 601 | Filters: [ 602 | { 603 | Key: 'name', 604 | Values: [name], 605 | }, 606 | ], 607 | /* eslint-enable @typescript-eslint/naming-convention */ 608 | }; 609 | log.debug('secretsmanager.listSecrets:', JSON.stringify(params, null, 2)); 610 | try { 611 | const result = await secretsManagerClient.send( 612 | new ListSecretsCommand(params), 613 | ); 614 | return (result.SecretList?.length ?? 0) > 0; 615 | } catch (error) { 616 | if (error instanceof ResourceNotFoundException) { 617 | return false; 618 | } else { 619 | throw error; 620 | } 621 | } 622 | } 623 | 624 | async function keyPairExists(name: string, log: Logger): Promise { 625 | log.debug('called function keyPairExists'); 626 | const params: DescribeKeyPairsCommandInput = { 627 | /* eslint-disable-next-line @typescript-eslint/naming-convention */ 628 | KeyNames: [name], 629 | }; 630 | log.debug('ec2.describeKeyPairs:', JSON.stringify(params, null, 2)); 631 | try { 632 | const result = await ec2Client.send(new DescribeKeyPairsCommand(params)); 633 | return (result.KeyPairs?.length ?? 0) > 0; 634 | } catch (error) { 635 | if (error.name && error.name == 'InvalidKeyPair.NotFound') { 636 | return false; 637 | } 638 | if (error instanceof ResourceNotFoundException) { 639 | return false; 640 | } else { 641 | throw error; 642 | } 643 | } 644 | } 645 | 646 | function deleteSecret( 647 | resource: CustomResource, 648 | log: Logger, 649 | secretId: string, 650 | ): Promise { 651 | log.debug('called function deleteSecret'); 652 | const params: DeleteSecretCommandInput = { 653 | /* eslint-disable-next-line @typescript-eslint/naming-convention */ 654 | SecretId: secretId, 655 | }; 656 | 657 | const removeKeySecretsAfterDays = parseInt( 658 | String(resource.properties.RemoveKeySecretsAfterDays.value), 659 | ); 660 | 661 | if (removeKeySecretsAfterDays > 0) { 662 | params.RecoveryWindowInDays = removeKeySecretsAfterDays; 663 | } else { 664 | params.ForceDeleteWithoutRecovery = true; 665 | } 666 | 667 | log.debug('secretsmanager.deleteSecret:', JSON.stringify(params, null, 2)); 668 | return secretsManagerClient.send(new DeleteSecretCommand(params)); 669 | } 670 | 671 | function makeTags( 672 | resource: CustomResource, 673 | eventTags?: Record, 674 | ): TagType[] { 675 | const tags: TagType[] = [ 676 | /* eslint-disable @typescript-eslint/naming-convention */ 677 | { 678 | Key: 'aws-cloudformation:stack-id', 679 | Value: resource.event.StackId, 680 | } as TagType, 681 | { 682 | Key: 'aws-cloudformation:stack-name', 683 | Value: resource.properties.StackName.value, 684 | } as TagType, 685 | { 686 | Key: 'aws-cloudformation:logical-id', 687 | Value: resource.event.LogicalResourceId, 688 | } as TagType, 689 | /* eslint-enable @typescript-eslint/naming-convention */ 690 | ]; 691 | if (eventTags && Object.keys(eventTags).length) { 692 | Object.keys(eventTags).forEach(function (key: string) { 693 | tags.push({ 694 | /* eslint-disable @typescript-eslint/naming-convention */ 695 | Key: key, 696 | Value: eventTags[key], 697 | /* eslint-enable @typescript-eslint/naming-convention */ 698 | } as TagType); 699 | }); 700 | } 701 | return tags; 702 | } 703 | 704 | function getMissingTags( 705 | oldTags: SecretManagerTag[], 706 | newTags: SecretManagerTag[], 707 | ): string[] { 708 | const missing = oldTags.filter(missingTags(newTags)); 709 | return missing.map(function (tag: SecretManagerTag) { 710 | return tag.Key!; 711 | }); 712 | } 713 | 714 | function missingTags(newTags: SecretManagerTag[]) { 715 | return (currentTag: SecretManagerTag) => { 716 | return ( 717 | newTags.filter((newTag: SecretManagerTag) => { 718 | return newTag.Key == currentTag.Key; 719 | }).length == 0 720 | ); 721 | }; 722 | } 723 | -------------------------------------------------------------------------------- /lambda/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lambda", 3 | "lockfileVersion": 2, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "dependencies": { 8 | "aws-cloudformation-custom-resource": "5.0.0", 9 | "sshpk": "1.18.0" 10 | } 11 | }, 12 | "node_modules/asn1": { 13 | "version": "0.2.6", 14 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", 15 | "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", 16 | "dependencies": { 17 | "safer-buffer": "~2.1.0" 18 | } 19 | }, 20 | "node_modules/assert-plus": { 21 | "version": "1.0.0", 22 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 23 | "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", 24 | "engines": { 25 | "node": ">=0.8" 26 | } 27 | }, 28 | "node_modules/aws-cloudformation-custom-resource": { 29 | "version": "5.0.0", 30 | "resolved": "https://registry.npmjs.org/aws-cloudformation-custom-resource/-/aws-cloudformation-custom-resource-5.0.0.tgz", 31 | "integrity": "sha512-ekTL9j/Pb5T7S6RVnlWDiDoBH0uT4OBMJ2d1AMATX2ULvXz6hhr3OL2aRLZB1kf8ekrNghJtOYcmiMmlZ7s8Ow==" 32 | }, 33 | "node_modules/bcrypt-pbkdf": { 34 | "version": "1.0.2", 35 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", 36 | "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", 37 | "dependencies": { 38 | "tweetnacl": "^0.14.3" 39 | } 40 | }, 41 | "node_modules/dashdash": { 42 | "version": "1.14.1", 43 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 44 | "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", 45 | "dependencies": { 46 | "assert-plus": "^1.0.0" 47 | }, 48 | "engines": { 49 | "node": ">=0.10" 50 | } 51 | }, 52 | "node_modules/ecc-jsbn": { 53 | "version": "0.1.2", 54 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", 55 | "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", 56 | "dependencies": { 57 | "jsbn": "~0.1.0", 58 | "safer-buffer": "^2.1.0" 59 | } 60 | }, 61 | "node_modules/getpass": { 62 | "version": "0.1.7", 63 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", 64 | "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", 65 | "dependencies": { 66 | "assert-plus": "^1.0.0" 67 | } 68 | }, 69 | "node_modules/jsbn": { 70 | "version": "0.1.1", 71 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", 72 | "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" 73 | }, 74 | "node_modules/safer-buffer": { 75 | "version": "2.1.2", 76 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 77 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 78 | }, 79 | "node_modules/sshpk": { 80 | "version": "1.18.0", 81 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", 82 | "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", 83 | "dependencies": { 84 | "asn1": "~0.2.3", 85 | "assert-plus": "^1.0.0", 86 | "bcrypt-pbkdf": "^1.0.0", 87 | "dashdash": "^1.12.0", 88 | "ecc-jsbn": "~0.1.1", 89 | "getpass": "^0.1.1", 90 | "jsbn": "~0.1.0", 91 | "safer-buffer": "^2.0.2", 92 | "tweetnacl": "~0.14.0" 93 | }, 94 | "bin": { 95 | "sshpk-conv": "bin/sshpk-conv", 96 | "sshpk-sign": "bin/sshpk-sign", 97 | "sshpk-verify": "bin/sshpk-verify" 98 | }, 99 | "engines": { 100 | "node": ">=0.10.0" 101 | } 102 | }, 103 | "node_modules/tweetnacl": { 104 | "version": "0.14.5", 105 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 106 | "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" 107 | } 108 | }, 109 | "dependencies": { 110 | "asn1": { 111 | "version": "0.2.6", 112 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", 113 | "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", 114 | "requires": { 115 | "safer-buffer": "~2.1.0" 116 | } 117 | }, 118 | "assert-plus": { 119 | "version": "1.0.0", 120 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 121 | "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" 122 | }, 123 | "aws-cloudformation-custom-resource": { 124 | "version": "5.0.0", 125 | "resolved": "https://registry.npmjs.org/aws-cloudformation-custom-resource/-/aws-cloudformation-custom-resource-5.0.0.tgz", 126 | "integrity": "sha512-ekTL9j/Pb5T7S6RVnlWDiDoBH0uT4OBMJ2d1AMATX2ULvXz6hhr3OL2aRLZB1kf8ekrNghJtOYcmiMmlZ7s8Ow==" 127 | }, 128 | "bcrypt-pbkdf": { 129 | "version": "1.0.2", 130 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", 131 | "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", 132 | "requires": { 133 | "tweetnacl": "^0.14.3" 134 | } 135 | }, 136 | "dashdash": { 137 | "version": "1.14.1", 138 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 139 | "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", 140 | "requires": { 141 | "assert-plus": "^1.0.0" 142 | } 143 | }, 144 | "ecc-jsbn": { 145 | "version": "0.1.2", 146 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", 147 | "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", 148 | "requires": { 149 | "jsbn": "~0.1.0", 150 | "safer-buffer": "^2.1.0" 151 | } 152 | }, 153 | "getpass": { 154 | "version": "0.1.7", 155 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", 156 | "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", 157 | "requires": { 158 | "assert-plus": "^1.0.0" 159 | } 160 | }, 161 | "jsbn": { 162 | "version": "0.1.1", 163 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", 164 | "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" 165 | }, 166 | "safer-buffer": { 167 | "version": "2.1.2", 168 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 169 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 170 | }, 171 | "sshpk": { 172 | "version": "1.18.0", 173 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", 174 | "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", 175 | "requires": { 176 | "asn1": "~0.2.3", 177 | "assert-plus": "^1.0.0", 178 | "bcrypt-pbkdf": "^1.0.0", 179 | "dashdash": "^1.12.0", 180 | "ecc-jsbn": "~0.1.1", 181 | "getpass": "^0.1.1", 182 | "jsbn": "~0.1.0", 183 | "safer-buffer": "^2.0.2", 184 | "tweetnacl": "~0.14.0" 185 | } 186 | }, 187 | "tweetnacl": { 188 | "version": "0.14.5", 189 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 190 | "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" 191 | } 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /lambda/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "aws-cloudformation-custom-resource": "5.0.0", 4 | "sshpk": "1.18.0" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /lambda/types.ts: -------------------------------------------------------------------------------- 1 | // this file is physically present in /lambda, as it is required for build the lambda zip 2 | // the file is symlinked into /lib, as otherwise jsii is refusing to find it, even when the whole lambda directory is not ignored 3 | 4 | export enum LogLevel { 5 | /* eslint-disable @typescript-eslint/naming-convention */ 6 | ERROR, 7 | WARN, 8 | INFO, 9 | DEBUG, 10 | /* eslint-enable @typescript-eslint/naming-convention */ 11 | } 12 | 13 | export enum PublicKeyFormat { 14 | /* eslint-disable @typescript-eslint/naming-convention */ 15 | /** 16 | * OpenSSH format 17 | */ 18 | OPENSSH = 'openssh', 19 | 20 | /** 21 | * SSH format 22 | */ 23 | SSH = 'ssh', 24 | 25 | /** 26 | * PEM format 27 | */ 28 | PEM = 'pem', 29 | 30 | /** 31 | * PKCS#1 format 32 | */ 33 | PKCS1 = 'pkcs1', 34 | 35 | /** 36 | * PKCS#8 format 37 | */ 38 | PKCS8 = 'pkcs8', 39 | 40 | /** 41 | * Raw OpenSSH wire format 42 | * 43 | * As CloudFormation cannot handle binary data, if the public key is exposed in the template, the value is base64 encoded 44 | */ 45 | RFC4253 = 'rfc4253', 46 | 47 | /** 48 | * PuTTY ppk format 49 | */ 50 | PUTTY = 'putty', 51 | 52 | /* eslint-enable @typescript-eslint/naming-convention */ 53 | } 54 | 55 | export enum KeyType { 56 | /* eslint-disable @typescript-eslint/naming-convention */ 57 | RSA = 'rsa', 58 | ED25519 = 'ed25519', 59 | /* eslint-enable @typescript-eslint/naming-convention */ 60 | } 61 | 62 | export interface ResourceProperties { 63 | /* eslint-disable @typescript-eslint/naming-convention */ 64 | Name: string; 65 | StorePublicKey?: 'true' | 'false'; // props passed via lambda always are of type string 66 | ExposePublicKey?: 'true' | 'false'; 67 | PublicKey: string; 68 | SecretPrefix: string; 69 | Description: string; 70 | KmsPrivate: string; 71 | KmsPublic: string; 72 | KeyType: KeyType; 73 | PublicKeyFormat: PublicKeyFormat; 74 | RemoveKeySecretsAfterDays: number; 75 | StackName: string; 76 | Tags: Record; 77 | LogLevel?: LogLevel; 78 | /* eslint-enable @typescript-eslint/naming-convention */ 79 | } 80 | -------------------------------------------------------------------------------- /lib/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Annotations, 3 | aws_iam, 4 | aws_kms, 5 | aws_lambda, 6 | CustomResource, 7 | Duration, 8 | ITaggable, 9 | Lazy, 10 | Resource, 11 | ResourceProps, 12 | Stack, 13 | TagManager, 14 | TagType, 15 | } from 'aws-cdk-lib'; 16 | import { IKeyPair, OperatingSystemType } from 'aws-cdk-lib/aws-ec2'; 17 | import { Construct } from 'constructs'; 18 | import * as path from 'path'; 19 | import { 20 | KeyType, 21 | LogLevel, 22 | PublicKeyFormat, 23 | ResourceProperties, 24 | } from './types'; 25 | export { KeyType, LogLevel, PublicKeyFormat } from './types'; 26 | 27 | const resourceType = 'Custom::EC2-Key-Pair'; 28 | const ID = `CFN::Resource::${resourceType}`; 29 | const createdByTag = 'CreatedByCfnCustomResource'; 30 | const cleanID = ID.replace(/:+/g, '-'); 31 | const lambdaTimeout = 3; // minutes 32 | 33 | /** 34 | * Definition of EC2 Key Pair 35 | */ 36 | export interface KeyPairProps extends ResourceProps { 37 | /** 38 | * Name of the Key Pair 39 | * 40 | * In AWS Secrets Manager the key will be prefixed with `ec2-ssh-key/`. 41 | * 42 | * The name can be up to 255 characters long. Valid characters include _, -, a-z, A-Z, and 0-9. 43 | */ 44 | readonly keyPairName: string; 45 | 46 | /** 47 | * The description for the key in AWS Secrets Manager 48 | * @default - '' 49 | */ 50 | readonly description?: string; 51 | 52 | /** 53 | * The KMS key used to encrypt the Secrets Manager secrets with 54 | * 55 | * This needs to be a key created in the same stack. You cannot use a key imported via ARN, because the keys access policy will need to be modified. 56 | * 57 | * @default - `alias/aws/secretsmanager` 58 | */ 59 | readonly kms?: aws_kms.Key; 60 | 61 | /** 62 | * The KMS key to use to encrypt the private key with 63 | * 64 | * This needs to be a key created in the same stack. You cannot use a key imported via ARN, because the keys access policy will need to be modified. 65 | * 66 | * If no value is provided, the property `kms` will be used instead. 67 | * 68 | * @default - `this.kms` 69 | */ 70 | readonly kmsPrivateKey?: aws_kms.Key; 71 | 72 | /** 73 | * The KMS key to use to encrypt the public key with 74 | * 75 | * This needs to be a key created in the same stack. You cannot use a key imported via ARN, because the keys access policy will need to be modified. 76 | * 77 | * If no value is provided, the property `kms` will be used instead. 78 | * 79 | * @default - `this.kms` 80 | */ 81 | readonly kmsPublicKey?: aws_kms.Key; 82 | 83 | /** 84 | * Import a public key instead of creating it 85 | * 86 | * If no public key is provided, a new key pair will be created. 87 | */ 88 | readonly publicKey?: string; 89 | 90 | /** 91 | * Store the public key as a secret 92 | * 93 | * @default - false 94 | */ 95 | readonly storePublicKey?: boolean; 96 | 97 | /** 98 | * Expose the public key as property `publicKeyValue` 99 | * 100 | * @default - false 101 | */ 102 | readonly exposePublicKey?: boolean; 103 | 104 | /** 105 | * The type of key pair 106 | * 107 | * @default - RSA 108 | */ 109 | readonly keyType?: KeyType; 110 | 111 | /** 112 | * Format for public key. 113 | * 114 | * Relevant only if the public key is stored and/or exposed. 115 | * 116 | * @default - SSH 117 | */ 118 | readonly publicKeyFormat?: PublicKeyFormat; 119 | 120 | /** 121 | * When the resource is destroyed, after how many days the private and public key in the AWS Secrets Manager should be deleted. 122 | * 123 | * Valid values are 0 and 7 to 30 124 | * 125 | * @default 0 126 | */ 127 | readonly removeKeySecretsAfterDays?: number; 128 | 129 | /** 130 | * Prefix for the secret in AWS Secrets Manager. 131 | * 132 | * @default `ec2-ssh-key/` 133 | */ 134 | readonly secretPrefix?: string; 135 | 136 | /** 137 | * A prefix for all resource names. 138 | * 139 | * By default all resources are prefixed with the stack name to avoid collisions with other stacks. This might cause problems when you work with long stack names and can be overridden through this parameter. 140 | * 141 | * @default Name of the stack 142 | */ 143 | readonly resourcePrefix?: string; 144 | 145 | /** 146 | * Whether to use the legacy name for the Lambda function, which backs the custom resource. 147 | * 148 | * Starting with v4 of this package, the Lambda function by default has no longer a fixed name. 149 | * 150 | * If you migrate from v3 to v4, you need to set this to `true` as CloudFormation does not allow to change the name of the Lambda function used by custom resource. 151 | * 152 | * @default false 153 | */ 154 | readonly legacyLambdaName?: boolean; 155 | 156 | /** 157 | * The log level of the Lambda function 158 | * 159 | * @default LogLevel.warn 160 | */ 161 | readonly logLevel?: LogLevel; 162 | } 163 | 164 | /** 165 | * An EC2 Key Pair 166 | */ 167 | export class KeyPair extends Resource implements ITaggable, IKeyPair { 168 | /** 169 | * The lambda function that is created 170 | */ 171 | public readonly lambdaFunction: aws_lambda.IFunction; 172 | 173 | /** 174 | * ARN of the private key in AWS Secrets Manager 175 | */ 176 | public readonly privateKeyArn: string = ''; 177 | 178 | /** 179 | * ARN of the public key in AWS Secrets Manager 180 | */ 181 | public readonly publicKeyArn: string = ''; 182 | 183 | /** 184 | * The public key. 185 | * 186 | * Only filled, when `exposePublicKey = true` 187 | */ 188 | public readonly publicKeyValue: string = ''; 189 | 190 | /** 191 | * Name of the Key Pair 192 | */ 193 | public readonly keyPairName: string = ''; 194 | 195 | /** 196 | * ID of the Key Pair 197 | */ 198 | public readonly keyPairID: string = ''; 199 | 200 | /** 201 | * Fingerprint of the Key Pair 202 | */ 203 | public readonly keyPairFingerprint: string = ''; 204 | 205 | /** 206 | * Format of the public key 207 | */ 208 | public readonly publicKeyFormat: PublicKeyFormat; 209 | 210 | /** 211 | * Type of the Key Pair 212 | */ 213 | public readonly keyType: KeyType; 214 | 215 | /** 216 | * Resource tags 217 | */ 218 | public readonly tags: TagManager; 219 | 220 | public readonly prefix: string = ''; 221 | 222 | /** 223 | * Defines a new EC2 Key Pair. The private key will be stored in AWS Secrets Manager 224 | */ 225 | constructor(scope: Construct, id: string, props: KeyPairProps) { 226 | super(scope, id); 227 | 228 | if ( 229 | props.removeKeySecretsAfterDays && 230 | (props.removeKeySecretsAfterDays < 0 || 231 | (props.removeKeySecretsAfterDays > 0 && 232 | props.removeKeySecretsAfterDays < 7) || 233 | props.removeKeySecretsAfterDays > 30) 234 | ) { 235 | Annotations.of(this).addError( 236 | `Parameter removeKeySecretsAfterDays must be 0 or between 7 and 30. Got ${props.removeKeySecretsAfterDays}`, 237 | ); 238 | } 239 | 240 | if ( 241 | props.publicKey?.length && 242 | props.publicKeyFormat !== undefined && 243 | props.publicKeyFormat !== PublicKeyFormat.OPENSSH 244 | ) { 245 | Annotations.of(this).addError( 246 | 'When importing a key, the format has to be of type OpenSSH', 247 | ); 248 | } 249 | 250 | if ( 251 | props.keyType == KeyType.ED25519 && 252 | props.publicKeyFormat == PublicKeyFormat.PKCS1 253 | ) { 254 | Annotations.of(this).addError( 255 | 'The public key format PKCS1 is not supported for key type ED25519', 256 | ); 257 | } 258 | 259 | const stack = Stack.of(this).stackName; 260 | 261 | if (props.legacyLambdaName) { 262 | this.prefix = props.resourcePrefix ?? stack; 263 | if (this.prefix.length + cleanID.length > 62) { 264 | // Cloudformation limits names to 63 characters. 265 | Annotations.of(this).addError( 266 | `Cloudformation limits names to 63 characters. 267 | Prefix ${this.prefix} is too long to be used as a prefix for your roleName. Define parameter resourcePrefix?:`, 268 | ); 269 | } 270 | } 271 | this.lambdaFunction = this.ensureLambda(props.legacyLambdaName ?? false); 272 | 273 | this.tags = new TagManager(TagType.MAP, 'Custom::EC2-Key-Pair'); 274 | this.tags.setTag(createdByTag, ID); 275 | 276 | this.keyType = props.keyType ?? KeyType.RSA; 277 | this.publicKeyFormat = props.publicKeyFormat ?? PublicKeyFormat.SSH; 278 | 279 | const kmsPrivate = props.kmsPrivateKey ?? props.kms; 280 | const kmsPublic = props.kmsPublicKey ?? props.kms; 281 | 282 | const lambdaProperties: ResourceProperties = { 283 | /* eslint-disable @typescript-eslint/naming-convention */ 284 | Name: props.keyPairName, 285 | Description: props.description ?? '', 286 | KmsPrivate: kmsPrivate?.keyArn ?? 'alias/aws/secretsmanager', 287 | KmsPublic: kmsPublic?.keyArn ?? 'alias/aws/secretsmanager', 288 | PublicKey: props.publicKey ?? '', 289 | StorePublicKey: props.storePublicKey ? 'true' : 'false', 290 | ExposePublicKey: props.exposePublicKey ? 'true' : 'false', 291 | KeyType: this.keyType, 292 | PublicKeyFormat: props.publicKeyFormat ?? PublicKeyFormat.SSH, 293 | RemoveKeySecretsAfterDays: props.removeKeySecretsAfterDays ?? 0, 294 | SecretPrefix: props.secretPrefix ?? 'ec2-ssh-key/', 295 | StackName: stack, 296 | Tags: Lazy.any({ 297 | produce: () => this.tags.renderTags() as Record, 298 | }) as unknown as Record, 299 | LogLevel: props.logLevel, 300 | /* eslint-enable @typescript-eslint/naming-convention */ 301 | }; 302 | 303 | const key = new CustomResource(this, `EC2-Key-Pair-${props.keyPairName}`, { 304 | serviceToken: this.lambdaFunction.functionArn, 305 | resourceType: resourceType, 306 | properties: lambdaProperties, 307 | }); 308 | 309 | if (typeof props.kms !== 'undefined') { 310 | props.kms.grantEncryptDecrypt(this.lambdaFunction.role!); 311 | key.node.addDependency(props.kms); 312 | key.node.addDependency(this.lambdaFunction.role!); 313 | } 314 | 315 | if (typeof props.kmsPrivateKey !== 'undefined') { 316 | props.kmsPrivateKey.grantEncryptDecrypt(this.lambdaFunction.role!); 317 | key.node.addDependency(props.kmsPrivateKey); 318 | key.node.addDependency(this.lambdaFunction.role!); 319 | } 320 | 321 | if (typeof props.kmsPublicKey !== 'undefined') { 322 | props.kmsPublicKey.grantEncryptDecrypt(this.lambdaFunction.role!); 323 | key.node.addDependency(props.kmsPublicKey); 324 | key.node.addDependency(this.lambdaFunction.role!); 325 | } 326 | 327 | this.privateKeyArn = key.getAttString('PrivateKeyARN'); 328 | this.publicKeyArn = key.getAttString('PublicKeyARN'); 329 | this.publicKeyValue = key.getAttString('PublicKeyValue'); 330 | this.keyPairName = key.getAttString('KeyPairName'); 331 | this.keyPairID = key.getAttString('KeyPairID'); 332 | this.keyPairFingerprint = key.getAttString('KeyPairFingerprint'); 333 | } 334 | 335 | private ensureLambda(legacyLambdaName: boolean): aws_lambda.Function { 336 | const stack = Stack.of(this); 337 | const constructName = legacyLambdaName 338 | ? 'EC2-Key-Name-Manager-Lambda' // this name was not intentional but we keep it for legacy resources 339 | : 'EC2-Key-Pair-Manager-Lambda'; 340 | const existing = stack.node.tryFindChild(constructName); 341 | if (existing) { 342 | return existing as aws_lambda.Function; 343 | } 344 | 345 | const resources = [`arn:${stack.partition}:ec2:*:*:key-pair/*`]; 346 | 347 | const statements = [ 348 | new aws_iam.PolicyStatement({ 349 | actions: ['ec2:DescribeKeyPairs'], 350 | resources: ['*'], 351 | }), 352 | new aws_iam.PolicyStatement({ 353 | actions: ['ec2:CreateKeyPair', 'ec2:CreateTags', 'ec2:ImportKeyPair'], 354 | conditions: { 355 | /* eslint-disable @typescript-eslint/naming-convention */ 356 | StringLike: { 357 | 'aws:RequestTag/CreatedByCfnCustomResource': ID, 358 | }, 359 | /* eslint-enable @typescript-eslint/naming-convention */ 360 | }, 361 | resources, 362 | }), 363 | new aws_iam.PolicyStatement({ 364 | // allow delete/update, only if createdByTag is set 365 | actions: ['ec2:CreateTags', 'ec2:DeleteKeyPair', 'ec2:DeleteTags'], 366 | conditions: { 367 | /* eslint-disable @typescript-eslint/naming-convention */ 368 | StringLike: { 369 | 'ec2:ResourceTag/CreatedByCfnCustomResource': ID, 370 | }, 371 | /* eslint-enable @typescript-eslint/naming-convention */ 372 | }, 373 | resources, 374 | }), 375 | 376 | new aws_iam.PolicyStatement({ 377 | // we need this to check if a secret exists before attempting to delete it 378 | actions: ['secretsmanager:ListSecrets'], 379 | resources: ['*'], 380 | }), 381 | new aws_iam.PolicyStatement({ 382 | actions: ['secretsmanager:CreateSecret', 'secretsmanager:TagResource'], 383 | conditions: { 384 | /* eslint-disable @typescript-eslint/naming-convention */ 385 | StringLike: { 386 | 'aws:RequestTag/CreatedByCfnCustomResource': ID, 387 | }, 388 | /* eslint-enable @typescript-eslint/naming-convention */ 389 | }, 390 | resources: ['*'], 391 | }), 392 | new aws_iam.PolicyStatement({ 393 | // allow delete/update, only if createdByTag is set 394 | actions: [ 395 | 'secretsmanager:DeleteResourcePolicy', 396 | 'secretsmanager:DeleteSecret', 397 | 'secretsmanager:DescribeSecret', 398 | 'secretsmanager:GetResourcePolicy', 399 | 'secretsmanager:GetSecretValue', 400 | 'secretsmanager:ListSecretVersionIds', 401 | 'secretsmanager:PutResourcePolicy', 402 | 'secretsmanager:PutSecretValue', 403 | 'secretsmanager:RestoreSecret', 404 | 'secretsmanager:UntagResource', 405 | 'secretsmanager:UpdateSecret', 406 | 'secretsmanager:UpdateSecretVersionStage', 407 | ], 408 | conditions: { 409 | /* eslint-disable @typescript-eslint/naming-convention */ 410 | StringLike: { 411 | 'secretsmanager:ResourceTag/CreatedByCfnCustomResource': ID, 412 | }, 413 | /* eslint-enable @typescript-eslint/naming-convention */ 414 | }, 415 | resources: ['*'], 416 | }), 417 | ]; 418 | 419 | const fn = new aws_lambda.Function(stack, constructName, { 420 | functionName: legacyLambdaName ? `${this.prefix}-${cleanID}` : undefined, 421 | description: 'Custom CFN resource: Manage EC2 Key Pairs', 422 | runtime: aws_lambda.Runtime.NODEJS_20_X, 423 | handler: 'index.handler', 424 | code: aws_lambda.Code.fromAsset( 425 | path.join(__dirname, '../lambda/code.zip'), 426 | ), 427 | timeout: Duration.minutes(lambdaTimeout), 428 | }); 429 | statements.forEach((statement) => { 430 | fn.role?.addToPrincipalPolicy(statement); 431 | }); 432 | 433 | return fn; 434 | } 435 | 436 | /** 437 | * Grants read access to the private key in AWS Secrets Manager 438 | */ 439 | grantReadOnPrivateKey(grantee: aws_iam.IGrantable) { 440 | return this.grantRead(this.privateKeyArn, grantee); 441 | } 442 | 443 | /** 444 | * Grants read access to the public key in AWS Secrets Manager 445 | */ 446 | grantReadOnPublicKey(grantee: aws_iam.IGrantable) { 447 | return this.grantRead(this.publicKeyArn, grantee); 448 | } 449 | 450 | private grantRead(arn: string, grantee: aws_iam.IGrantable) { 451 | const result = aws_iam.Grant.addToPrincipal({ 452 | grantee, 453 | actions: [ 454 | 'secretsmanager:DescribeSecret', 455 | 'secretsmanager:GetResourcePolicy', 456 | 'secretsmanager:GetSecretValue', 457 | 'secretsmanager:ListSecretVersionIds', 458 | ], 459 | resourceArns: [arn], 460 | scope: this, 461 | }); 462 | return result; 463 | } 464 | 465 | /** 466 | * Used internally to determine whether the key pair is compatible with an OS type. 467 | * 468 | * @internal 469 | */ 470 | public _isOsCompatible(osType: OperatingSystemType): boolean { 471 | switch (this.keyType) { 472 | case KeyType.ED25519: 473 | return osType !== OperatingSystemType.WINDOWS; 474 | default: 475 | return true; 476 | } 477 | } 478 | } 479 | -------------------------------------------------------------------------------- /lib/types.ts: -------------------------------------------------------------------------------- 1 | ../lambda/types.ts -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cdk-ec2-key-pair", 3 | "description": "CDK Construct for managing EC2 key pairs", 4 | "version": "4.0.1", 5 | "license": "Apache-2.0", 6 | "author": { 7 | "name": "Daniel Schroeder", 8 | "url": "https://www.udondan.com/", 9 | "twitter": "udondan" 10 | }, 11 | "awscdkio": { 12 | "twitter": "udondan" 13 | }, 14 | "homepage": "https://github.com/udondan/cdk-ec2-key-pair", 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/udondan/cdk-ec2-key-pair.git" 18 | }, 19 | "bugs": { 20 | "url": "https://github.com/udondan/cdk-ec2-key-pair/issues" 21 | }, 22 | "keywords": [ 23 | "aws", 24 | "cdk", 25 | "aws-cdk", 26 | "aws-cdk-construct", 27 | "ec2", 28 | "ec2-key-pair", 29 | "cloudformation", 30 | "cloudformation-custom-resource" 31 | ], 32 | "stability": "stable", 33 | "main": "lib/index.js", 34 | "types": "lib/index.d.ts", 35 | "scripts": { 36 | "build": "jsii", 37 | "build:watch": "jsii -w", 38 | "package": "jsii-pacmak", 39 | "prepackage": "jsii && lambda/build" 40 | }, 41 | "jsii": { 42 | "excludeTypescript": [ 43 | "lambda" 44 | ], 45 | "outdir": "dist", 46 | "targets": { 47 | "python": { 48 | "distName": "cdk-ec2-key-pair", 49 | "module": "cdk_ec2_key_pair" 50 | }, 51 | "dotnet": { 52 | "namespace": "CDK.EC2.KeyPair", 53 | "packageId": "CDK.EC2.KeyPair" 54 | } 55 | } 56 | }, 57 | "devDependencies": { 58 | "@aws-sdk/client-ec2": "3.823.0", 59 | "@aws-sdk/client-secrets-manager": "3.823.0", 60 | "@swc/core": "1.11.31", 61 | "@swc/helpers": "0.5.17", 62 | "@types/node": "20.17.57", 63 | "@types/node-forge": "1.3.11", 64 | "@types/sshpk": "^1.17.4", 65 | "@typescript-eslint/eslint-plugin": "7.18.0", 66 | "@typescript-eslint/parser": "7.18.0", 67 | "aws-cdk-lib": "2.200.1", 68 | "aws-cloudformation-custom-resource": "5.0.0", 69 | "constructs": "10.3.0", 70 | "eslint": "8.57.1", 71 | "eslint-config-prettier": "9.1.0", 72 | "eslint-plugin-deprecation": "3.0.0", 73 | "eslint-plugin-prettier": "5.4.1", 74 | "jsii": "5.8.11", 75 | "jsii-pacmak": "1.110.0", 76 | "node-forge": "1.3.1", 77 | "prettier": "3.5.3", 78 | "publib": "0.2.997", 79 | "regenerator-runtime": "0.14.1", 80 | "ts-node": "10.9.2", 81 | "typescript": "5.7.3" 82 | }, 83 | "peerDependencies": { 84 | "aws-cdk-lib": "^2.116.0", 85 | "constructs": "^10.0.0" 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /release-please-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", 3 | "release-type": "node", 4 | "changelog-path": "CHANGELOG.md", 5 | "include-component-in-tag": false, 6 | "bump-minor-pre-major": false, 7 | "bump-patch-for-minor-pre-major": false, 8 | "draft": false, 9 | "prerelease": false, 10 | "packages": { 11 | ".": {} 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["config:recommended", ":semanticCommitTypeAll(chore)"], 4 | "dependencyDashboard": false, 5 | "ignorePaths": [ 6 | "**/node_modules/**", 7 | "**/bower_components/**", 8 | "**/vendor/**", 9 | "**/examples/**", 10 | "**/__tests__/**", 11 | "**/__fixtures__/**" 12 | ], 13 | "packageRules": [ 14 | { 15 | "matchUpdateTypes": ["minor", "patch"], 16 | "automerge": true 17 | } 18 | ], 19 | "rebaseWhen": "auto" 20 | } 21 | -------------------------------------------------------------------------------- /test/.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash -euo pipefail 2 | 3 | NO_COLOR=\x1b[0m 4 | TARGET_COLOR=\x1b[96m 5 | 6 | clean: 7 | @echo -e "$(TARGET_COLOR)Running clean$(NO_COLOR)" 8 | @rm -rf ../node_modules ../package-lock.json 9 | @rm -rf node_modules package-lock.json 10 | 11 | install: 12 | @echo -e "$(TARGET_COLOR)Running install$(NO_COLOR)" 13 | @cd .. && make install 14 | @npm clean-install --prefer-offline --cache ../.npm 15 | @npm list 16 | 17 | build: lambda 18 | @echo Building application... 19 | @npm run build 20 | 21 | diff: build 22 | @echo Running diff... 23 | @AWS_REGION=us-east-1 npm run cdk -- diff --color 24 | 25 | deploy: build 26 | @echo Deploying application... 27 | @AWS_REGION=us-east-1 npm run cdk -- deploy --color --require-approval never 28 | 29 | DESTROY: build 30 | @echo Destroying application... 31 | @AWS_REGION=us-east-1 npm run cdk -- destroy --color --force 32 | 33 | lambda: 34 | @echo Building Lambda... 35 | @../lambda/build 36 | 37 | get-secret-values: 38 | @AWS_REGION=us-east-1 aws secretsmanager get-secret-value \ 39 | --secret-id ec2-ssh-key/CFN-signing-key/private \ 40 | --query SecretString \ 41 | --output text 42 | @AWS_REGION=us-east-1 aws secretsmanager get-secret-value \ 43 | --secret-id ec2-ssh-key/CFN-signing-key/public \ 44 | --query SecretString \ 45 | --output text 46 | -------------------------------------------------------------------------------- /test/bin/test.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { 3 | GetCallerIdentityCommand, 4 | STSClient, 5 | STSClientConfig, 6 | } from '@aws-sdk/client-sts'; 7 | import cdk = require('aws-cdk-lib'); 8 | 9 | import { TestStack } from '../lib/test-stack'; 10 | 11 | const region = 'us-east-1'; 12 | 13 | const clientConfig: STSClientConfig = { 14 | region, 15 | }; 16 | if ( 17 | process.env.AWS_ACCESS_KEY_ID && 18 | process.env.AWS_SECRET_ACCESS_KEY && 19 | process.env.AWS_SESSION_TOKEN 20 | ) { 21 | clientConfig.credentials = { 22 | accessKeyId: process.env.AWS_ACCESS_KEY_ID, 23 | secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, 24 | sessionToken: process.env.AWS_SESSION_TOKEN, 25 | }; 26 | } 27 | 28 | async function getIdentity() { 29 | const stsClient = new STSClient(clientConfig); 30 | const callerIdentity = await stsClient.send(new GetCallerIdentityCommand({})); 31 | const arn = callerIdentity.Arn; 32 | if (!arn) { 33 | throw new Error('Unable to get caller identity'); 34 | } 35 | return arn.split('/')[1]; 36 | } 37 | 38 | async function main() { 39 | const userName = await getIdentity(); 40 | const app = new cdk.App(); 41 | new TestStack(app, 'EC2KeyPair', { 42 | env: { 43 | account: process.env.CDK_DEFAULT_ACCOUNT, 44 | region: process.env.CDK_DEFAULT_REGION, 45 | }, 46 | currentUserName: userName, 47 | }); 48 | } 49 | 50 | main().catch((error) => { 51 | console.error('An error occurred:', error); 52 | }); 53 | -------------------------------------------------------------------------------- /test/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node bin/test.ts", 3 | "progress": "events" 4 | } 5 | -------------------------------------------------------------------------------- /test/lib/test-stack.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Tags, 3 | StackProps, 4 | Stack, 5 | CfnOutput, 6 | aws_iam, 7 | aws_ec2, 8 | } from 'aws-cdk-lib'; 9 | import cloudfront = require('aws-cdk-lib/aws-cloudfront'); 10 | import { Construct } from 'constructs'; 11 | import { KeyType, LogLevel, PublicKeyFormat } from '../../lambda/types'; 12 | import { KeyPair } from '../../lib'; 13 | 14 | interface Props extends StackProps { 15 | currentUserName: string; 16 | } 17 | 18 | const logLevel = LogLevel.DEBUG; 19 | 20 | export class TestStack extends Stack { 21 | constructor(scope: Construct, id: string, props: Props) { 22 | super(scope, id, props); 23 | 24 | Tags.of(scope).add('Hello', 'World'); 25 | Tags.of(scope).add('Test', process.env.TAG_VALUE ?? 'default'); 26 | 27 | const keyPair = new KeyPair(this, 'Test-Key-Pair', { 28 | keyPairName: 'test-key-pair', 29 | description: 'A test Key Pair', 30 | removeKeySecretsAfterDays: 0, 31 | storePublicKey: false, 32 | exposePublicKey: true, 33 | }); 34 | 35 | Tags.of(keyPair).add('a', 'b'); 36 | Tags.of(keyPair).add('c', 'd'); 37 | 38 | new CfnOutput(this, 'Test-Public-Key', { 39 | exportName: 'TestPublicKey', 40 | value: keyPair.publicKeyValue, 41 | }); 42 | 43 | new CfnOutput(this, 'Test-Public-Key-Fingerprint', { 44 | exportName: 'TestPublicKeyFingerprint', 45 | value: keyPair.keyPairFingerprint, 46 | }); 47 | 48 | new CfnOutput(this, 'Test-Public-Key-Format', { 49 | exportName: 'TestPublicKeyFormat', 50 | value: keyPair.publicKeyFormat, 51 | }); 52 | 53 | // import public key 54 | 55 | const keyPairImport = new KeyPair(this, 'Test-Key-Pair-Import', { 56 | keyPairName: 'test-key-pair-import', 57 | description: 'A test Key Pair, imported via public key', 58 | removeKeySecretsAfterDays: 0, 59 | storePublicKey: false, 60 | exposePublicKey: true, 61 | publicKey: keyPair.publicKeyValue, 62 | logLevel, 63 | }); 64 | 65 | if (process.env.with_ec2 === 'true') { 66 | new aws_ec2.Instance(this, 'Test-Instance', { 67 | vpc: aws_ec2.Vpc.fromLookup(this, 'VPC', { 68 | vpcName: 'default', 69 | }), 70 | instanceType: aws_ec2.InstanceType.of( 71 | aws_ec2.InstanceClass.T2, 72 | aws_ec2.InstanceSize.MICRO, 73 | ), 74 | machineImage: aws_ec2.MachineImage.latestAmazonLinux2(), 75 | keyPair: keyPairImport, 76 | }); 77 | } 78 | 79 | new CfnOutput(this, 'Test-Public-Key-Import', { 80 | exportName: 'TestPublicKeyImport', 81 | value: keyPairImport.publicKeyValue, 82 | }); 83 | 84 | // PEM && CloudFront 85 | 86 | const keyPairPem = new KeyPair(this, 'Test-Key-Pair-PEM', { 87 | keyPairName: 'CFN-signing-key', 88 | exposePublicKey: true, 89 | storePublicKey: true, 90 | publicKeyFormat: PublicKeyFormat.PEM, 91 | legacyLambdaName: true, 92 | logLevel, 93 | }); 94 | 95 | const currentUser = aws_iam.User.fromUserName( 96 | this, 97 | 'Current-User', 98 | props.currentUserName, 99 | ); 100 | 101 | keyPairPem.grantReadOnPrivateKey(currentUser); 102 | keyPairPem.grantReadOnPublicKey(currentUser); 103 | 104 | new CfnOutput(this, 'Test-Public-Key-PEM', { 105 | exportName: 'TestPublicKeyPEM', 106 | value: keyPairPem.publicKeyValue, 107 | }); 108 | 109 | const pubKey = new cloudfront.PublicKey(this, 'Signing-Public-Key', { 110 | encodedKey: keyPairPem.publicKeyValue, 111 | }); 112 | 113 | new cloudfront.KeyGroup(this, 'Signing-Key-Group', { 114 | items: [pubKey], 115 | }); 116 | 117 | for (const [_key, publicKeyFormat] of Object.entries(PublicKeyFormat)) { 118 | for (const [_key, keyType] of Object.entries(KeyType)) { 119 | if ( 120 | keyType === KeyType.ED25519 && 121 | publicKeyFormat == PublicKeyFormat.PKCS1 122 | ) { 123 | // combination not supported 124 | continue; 125 | } 126 | 127 | const keyPairName = `Test-Key-Pair-${keyType}-${publicKeyFormat}`; 128 | const keyPair = new KeyPair(this, keyPairName, { 129 | keyPairName, 130 | keyType, 131 | publicKeyFormat, 132 | exposePublicKey: true, 133 | storePublicKey: true, 134 | logLevel, 135 | }); 136 | new CfnOutput(this, `${keyPairName}-Public-Key`, { 137 | exportName: `${keyPairName}-Public-Key`, 138 | value: keyPair.publicKeyValue, 139 | }); 140 | } 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /test/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "version": "0.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "test", 9 | "version": "0.0.0", 10 | "dependencies": { 11 | "@aws-sdk/client-sts": "^3.427.0", 12 | "aws-cdk": "^2.0.0", 13 | "aws-cdk-lib": "2.x", 14 | "cdk-ssm-document": "file:..", 15 | "ts-node": "^10.4.0", 16 | "typescript": "^5.2.2" 17 | }, 18 | "bin": { 19 | "test": "bin/test.js" 20 | } 21 | }, 22 | "..": { 23 | "version": "4.0.1", 24 | "license": "Apache-2.0", 25 | "devDependencies": { 26 | "@aws-sdk/client-ec2": "3.823.0", 27 | "@aws-sdk/client-secrets-manager": "3.823.0", 28 | "@swc/core": "1.11.31", 29 | "@swc/helpers": "0.5.17", 30 | "@types/node": "20.17.57", 31 | "@types/node-forge": "1.3.11", 32 | "@types/sshpk": "^1.17.4", 33 | "@typescript-eslint/eslint-plugin": "7.18.0", 34 | "@typescript-eslint/parser": "7.18.0", 35 | "aws-cdk-lib": "2.200.1", 36 | "aws-cloudformation-custom-resource": "5.0.0", 37 | "constructs": "10.3.0", 38 | "eslint": "8.57.1", 39 | "eslint-config-prettier": "9.1.0", 40 | "eslint-plugin-deprecation": "3.0.0", 41 | "eslint-plugin-prettier": "5.4.1", 42 | "jsii": "5.8.11", 43 | "jsii-pacmak": "1.110.0", 44 | "node-forge": "1.3.1", 45 | "prettier": "3.5.3", 46 | "publib": "0.2.997", 47 | "regenerator-runtime": "0.14.1", 48 | "ts-node": "10.9.2", 49 | "typescript": "5.7.3" 50 | }, 51 | "peerDependencies": { 52 | "aws-cdk-lib": "^2.116.0", 53 | "constructs": "^10.0.0" 54 | } 55 | }, 56 | "node_modules/@aws-cdk/asset-awscli-v1": { 57 | "version": "2.2.237", 58 | "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.237.tgz", 59 | "integrity": "sha512-OlXylbXI52lboFVJBFLae+WB99qWmI121x/wXQHEMj2RaVNVbWE+OAHcDk2Um1BitUQCaTf9ki57B0Fuqx0Rvw==", 60 | "license": "Apache-2.0" 61 | }, 62 | "node_modules/@aws-cdk/asset-node-proxy-agent-v6": { 63 | "version": "2.1.0", 64 | "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v6/-/asset-node-proxy-agent-v6-2.1.0.tgz", 65 | "integrity": "sha512-7bY3J8GCVxLupn/kNmpPc5VJz8grx+4RKfnnJiO1LG+uxkZfANZG3RMHhE+qQxxwkyQ9/MfPtTpf748UhR425A==", 66 | "license": "Apache-2.0" 67 | }, 68 | "node_modules/@aws-cdk/cloud-assembly-schema": { 69 | "version": "44.1.0", 70 | "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-44.1.0.tgz", 71 | "integrity": "sha512-WvesvSbBw5FrVbH8LZfjX5iDDRdixDkEnbsFGN8H2GNR9geBo4kIBI1nlOiqoGB6dwPwif8qDEM/4NOfuzIChQ==", 72 | "bundleDependencies": [ 73 | "jsonschema", 74 | "semver" 75 | ], 76 | "license": "Apache-2.0", 77 | "dependencies": { 78 | "jsonschema": "~1.4.1", 79 | "semver": "^7.7.2" 80 | }, 81 | "engines": { 82 | "node": ">= 14.15.0" 83 | } 84 | }, 85 | "node_modules/@aws-cdk/cloud-assembly-schema/node_modules/jsonschema": { 86 | "version": "1.4.1", 87 | "inBundle": true, 88 | "license": "MIT", 89 | "engines": { 90 | "node": "*" 91 | } 92 | }, 93 | "node_modules/@aws-cdk/cloud-assembly-schema/node_modules/semver": { 94 | "version": "7.7.2", 95 | "inBundle": true, 96 | "license": "ISC", 97 | "bin": { 98 | "semver": "bin/semver.js" 99 | }, 100 | "engines": { 101 | "node": ">=10" 102 | } 103 | }, 104 | "node_modules/@aws-crypto/sha256-browser": { 105 | "version": "5.2.0", 106 | "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", 107 | "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", 108 | "license": "Apache-2.0", 109 | "dependencies": { 110 | "@aws-crypto/sha256-js": "^5.2.0", 111 | "@aws-crypto/supports-web-crypto": "^5.2.0", 112 | "@aws-crypto/util": "^5.2.0", 113 | "@aws-sdk/types": "^3.222.0", 114 | "@aws-sdk/util-locate-window": "^3.0.0", 115 | "@smithy/util-utf8": "^2.0.0", 116 | "tslib": "^2.6.2" 117 | } 118 | }, 119 | "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { 120 | "version": "2.2.0", 121 | "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", 122 | "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", 123 | "license": "Apache-2.0", 124 | "dependencies": { 125 | "tslib": "^2.6.2" 126 | }, 127 | "engines": { 128 | "node": ">=14.0.0" 129 | } 130 | }, 131 | "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { 132 | "version": "2.2.0", 133 | "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", 134 | "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", 135 | "license": "Apache-2.0", 136 | "dependencies": { 137 | "@smithy/is-array-buffer": "^2.2.0", 138 | "tslib": "^2.6.2" 139 | }, 140 | "engines": { 141 | "node": ">=14.0.0" 142 | } 143 | }, 144 | "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { 145 | "version": "2.3.0", 146 | "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", 147 | "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", 148 | "license": "Apache-2.0", 149 | "dependencies": { 150 | "@smithy/util-buffer-from": "^2.2.0", 151 | "tslib": "^2.6.2" 152 | }, 153 | "engines": { 154 | "node": ">=14.0.0" 155 | } 156 | }, 157 | "node_modules/@aws-crypto/sha256-js": { 158 | "version": "5.2.0", 159 | "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", 160 | "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", 161 | "license": "Apache-2.0", 162 | "dependencies": { 163 | "@aws-crypto/util": "^5.2.0", 164 | "@aws-sdk/types": "^3.222.0", 165 | "tslib": "^2.6.2" 166 | }, 167 | "engines": { 168 | "node": ">=16.0.0" 169 | } 170 | }, 171 | "node_modules/@aws-crypto/supports-web-crypto": { 172 | "version": "5.2.0", 173 | "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", 174 | "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", 175 | "license": "Apache-2.0", 176 | "dependencies": { 177 | "tslib": "^2.6.2" 178 | } 179 | }, 180 | "node_modules/@aws-crypto/util": { 181 | "version": "5.2.0", 182 | "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", 183 | "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", 184 | "license": "Apache-2.0", 185 | "dependencies": { 186 | "@aws-sdk/types": "^3.222.0", 187 | "@smithy/util-utf8": "^2.0.0", 188 | "tslib": "^2.6.2" 189 | } 190 | }, 191 | "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { 192 | "version": "2.2.0", 193 | "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", 194 | "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", 195 | "license": "Apache-2.0", 196 | "dependencies": { 197 | "tslib": "^2.6.2" 198 | }, 199 | "engines": { 200 | "node": ">=14.0.0" 201 | } 202 | }, 203 | "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { 204 | "version": "2.2.0", 205 | "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", 206 | "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", 207 | "license": "Apache-2.0", 208 | "dependencies": { 209 | "@smithy/is-array-buffer": "^2.2.0", 210 | "tslib": "^2.6.2" 211 | }, 212 | "engines": { 213 | "node": ">=14.0.0" 214 | } 215 | }, 216 | "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { 217 | "version": "2.3.0", 218 | "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", 219 | "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", 220 | "license": "Apache-2.0", 221 | "dependencies": { 222 | "@smithy/util-buffer-from": "^2.2.0", 223 | "tslib": "^2.6.2" 224 | }, 225 | "engines": { 226 | "node": ">=14.0.0" 227 | } 228 | }, 229 | "node_modules/@aws-sdk/client-sso": { 230 | "version": "3.823.0", 231 | "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.823.0.tgz", 232 | "integrity": "sha512-dBWdsbyGw8rPfdCsZySNtTOGQK4EZ8lxB/CneSQWRBPHgQ+Ys88NXxImO8xfWO7Itt1eh8O7UDTZ9+smcvw2pw==", 233 | "license": "Apache-2.0", 234 | "dependencies": { 235 | "@aws-crypto/sha256-browser": "5.2.0", 236 | "@aws-crypto/sha256-js": "5.2.0", 237 | "@aws-sdk/core": "3.823.0", 238 | "@aws-sdk/middleware-host-header": "3.821.0", 239 | "@aws-sdk/middleware-logger": "3.821.0", 240 | "@aws-sdk/middleware-recursion-detection": "3.821.0", 241 | "@aws-sdk/middleware-user-agent": "3.823.0", 242 | "@aws-sdk/region-config-resolver": "3.821.0", 243 | "@aws-sdk/types": "3.821.0", 244 | "@aws-sdk/util-endpoints": "3.821.0", 245 | "@aws-sdk/util-user-agent-browser": "3.821.0", 246 | "@aws-sdk/util-user-agent-node": "3.823.0", 247 | "@smithy/config-resolver": "^4.1.4", 248 | "@smithy/core": "^3.5.1", 249 | "@smithy/fetch-http-handler": "^5.0.4", 250 | "@smithy/hash-node": "^4.0.4", 251 | "@smithy/invalid-dependency": "^4.0.4", 252 | "@smithy/middleware-content-length": "^4.0.4", 253 | "@smithy/middleware-endpoint": "^4.1.9", 254 | "@smithy/middleware-retry": "^4.1.10", 255 | "@smithy/middleware-serde": "^4.0.8", 256 | "@smithy/middleware-stack": "^4.0.4", 257 | "@smithy/node-config-provider": "^4.1.3", 258 | "@smithy/node-http-handler": "^4.0.6", 259 | "@smithy/protocol-http": "^5.1.2", 260 | "@smithy/smithy-client": "^4.4.1", 261 | "@smithy/types": "^4.3.1", 262 | "@smithy/url-parser": "^4.0.4", 263 | "@smithy/util-base64": "^4.0.0", 264 | "@smithy/util-body-length-browser": "^4.0.0", 265 | "@smithy/util-body-length-node": "^4.0.0", 266 | "@smithy/util-defaults-mode-browser": "^4.0.17", 267 | "@smithy/util-defaults-mode-node": "^4.0.17", 268 | "@smithy/util-endpoints": "^3.0.6", 269 | "@smithy/util-middleware": "^4.0.4", 270 | "@smithy/util-retry": "^4.0.5", 271 | "@smithy/util-utf8": "^4.0.0", 272 | "tslib": "^2.6.2" 273 | }, 274 | "engines": { 275 | "node": ">=18.0.0" 276 | } 277 | }, 278 | "node_modules/@aws-sdk/client-sts": { 279 | "version": "3.823.0", 280 | "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.823.0.tgz", 281 | "integrity": "sha512-8LPgj+vNEQB8xRLRYzgXzfX9SxkETtTGCZrk7iSIXwDz38JSp010gjYml6MOnv1M1hR4VT/gM3465Y1opvKaIw==", 282 | "license": "Apache-2.0", 283 | "dependencies": { 284 | "@aws-crypto/sha256-browser": "5.2.0", 285 | "@aws-crypto/sha256-js": "5.2.0", 286 | "@aws-sdk/core": "3.823.0", 287 | "@aws-sdk/credential-provider-node": "3.823.0", 288 | "@aws-sdk/middleware-host-header": "3.821.0", 289 | "@aws-sdk/middleware-logger": "3.821.0", 290 | "@aws-sdk/middleware-recursion-detection": "3.821.0", 291 | "@aws-sdk/middleware-user-agent": "3.823.0", 292 | "@aws-sdk/region-config-resolver": "3.821.0", 293 | "@aws-sdk/types": "3.821.0", 294 | "@aws-sdk/util-endpoints": "3.821.0", 295 | "@aws-sdk/util-user-agent-browser": "3.821.0", 296 | "@aws-sdk/util-user-agent-node": "3.823.0", 297 | "@smithy/config-resolver": "^4.1.4", 298 | "@smithy/core": "^3.5.1", 299 | "@smithy/fetch-http-handler": "^5.0.4", 300 | "@smithy/hash-node": "^4.0.4", 301 | "@smithy/invalid-dependency": "^4.0.4", 302 | "@smithy/middleware-content-length": "^4.0.4", 303 | "@smithy/middleware-endpoint": "^4.1.9", 304 | "@smithy/middleware-retry": "^4.1.10", 305 | "@smithy/middleware-serde": "^4.0.8", 306 | "@smithy/middleware-stack": "^4.0.4", 307 | "@smithy/node-config-provider": "^4.1.3", 308 | "@smithy/node-http-handler": "^4.0.6", 309 | "@smithy/protocol-http": "^5.1.2", 310 | "@smithy/smithy-client": "^4.4.1", 311 | "@smithy/types": "^4.3.1", 312 | "@smithy/url-parser": "^4.0.4", 313 | "@smithy/util-base64": "^4.0.0", 314 | "@smithy/util-body-length-browser": "^4.0.0", 315 | "@smithy/util-body-length-node": "^4.0.0", 316 | "@smithy/util-defaults-mode-browser": "^4.0.17", 317 | "@smithy/util-defaults-mode-node": "^4.0.17", 318 | "@smithy/util-endpoints": "^3.0.6", 319 | "@smithy/util-middleware": "^4.0.4", 320 | "@smithy/util-retry": "^4.0.5", 321 | "@smithy/util-utf8": "^4.0.0", 322 | "tslib": "^2.6.2" 323 | }, 324 | "engines": { 325 | "node": ">=18.0.0" 326 | } 327 | }, 328 | "node_modules/@aws-sdk/core": { 329 | "version": "3.823.0", 330 | "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.823.0.tgz", 331 | "integrity": "sha512-1Cf4w8J7wYexz0KU3zpaikHvldGXQEjFldHOhm0SBGRy7qfYNXecfJAamccF7RdgLxKGgkv5Pl9zX/Z/DcW9zg==", 332 | "license": "Apache-2.0", 333 | "dependencies": { 334 | "@aws-sdk/types": "3.821.0", 335 | "@aws-sdk/xml-builder": "3.821.0", 336 | "@smithy/core": "^3.5.1", 337 | "@smithy/node-config-provider": "^4.1.3", 338 | "@smithy/property-provider": "^4.0.4", 339 | "@smithy/protocol-http": "^5.1.2", 340 | "@smithy/signature-v4": "^5.1.2", 341 | "@smithy/smithy-client": "^4.4.1", 342 | "@smithy/types": "^4.3.1", 343 | "@smithy/util-base64": "^4.0.0", 344 | "@smithy/util-body-length-browser": "^4.0.0", 345 | "@smithy/util-middleware": "^4.0.4", 346 | "@smithy/util-utf8": "^4.0.0", 347 | "fast-xml-parser": "4.4.1", 348 | "tslib": "^2.6.2" 349 | }, 350 | "engines": { 351 | "node": ">=18.0.0" 352 | } 353 | }, 354 | "node_modules/@aws-sdk/credential-provider-env": { 355 | "version": "3.823.0", 356 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.823.0.tgz", 357 | "integrity": "sha512-AIrLLwumObge+U1klN4j5ToIozI+gE9NosENRyHe0GIIZgTLOG/8jxrMFVYFeNHs7RUtjDTxxewislhFyGxJ/w==", 358 | "license": "Apache-2.0", 359 | "dependencies": { 360 | "@aws-sdk/core": "3.823.0", 361 | "@aws-sdk/types": "3.821.0", 362 | "@smithy/property-provider": "^4.0.4", 363 | "@smithy/types": "^4.3.1", 364 | "tslib": "^2.6.2" 365 | }, 366 | "engines": { 367 | "node": ">=18.0.0" 368 | } 369 | }, 370 | "node_modules/@aws-sdk/credential-provider-http": { 371 | "version": "3.823.0", 372 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.823.0.tgz", 373 | "integrity": "sha512-u4DXvB/J/o2bcvP1JP6n3ch7V3/NngmiJFPsM0hKUyRlLuWM37HEDEdjPRs3/uL/soTxrEhWKTA9//YVkvzI0w==", 374 | "license": "Apache-2.0", 375 | "dependencies": { 376 | "@aws-sdk/core": "3.823.0", 377 | "@aws-sdk/types": "3.821.0", 378 | "@smithy/fetch-http-handler": "^5.0.4", 379 | "@smithy/node-http-handler": "^4.0.6", 380 | "@smithy/property-provider": "^4.0.4", 381 | "@smithy/protocol-http": "^5.1.2", 382 | "@smithy/smithy-client": "^4.4.1", 383 | "@smithy/types": "^4.3.1", 384 | "@smithy/util-stream": "^4.2.2", 385 | "tslib": "^2.6.2" 386 | }, 387 | "engines": { 388 | "node": ">=18.0.0" 389 | } 390 | }, 391 | "node_modules/@aws-sdk/credential-provider-ini": { 392 | "version": "3.823.0", 393 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.823.0.tgz", 394 | "integrity": "sha512-C0o63qviK5yFvjH9zKWAnCUBkssJoQ1A1XAHe0IAQkurzoNBSmu9oVemqwnKKHA4H6QrmusaEERfL00yohIkJA==", 395 | "license": "Apache-2.0", 396 | "dependencies": { 397 | "@aws-sdk/core": "3.823.0", 398 | "@aws-sdk/credential-provider-env": "3.823.0", 399 | "@aws-sdk/credential-provider-http": "3.823.0", 400 | "@aws-sdk/credential-provider-process": "3.823.0", 401 | "@aws-sdk/credential-provider-sso": "3.823.0", 402 | "@aws-sdk/credential-provider-web-identity": "3.823.0", 403 | "@aws-sdk/nested-clients": "3.823.0", 404 | "@aws-sdk/types": "3.821.0", 405 | "@smithy/credential-provider-imds": "^4.0.6", 406 | "@smithy/property-provider": "^4.0.4", 407 | "@smithy/shared-ini-file-loader": "^4.0.4", 408 | "@smithy/types": "^4.3.1", 409 | "tslib": "^2.6.2" 410 | }, 411 | "engines": { 412 | "node": ">=18.0.0" 413 | } 414 | }, 415 | "node_modules/@aws-sdk/credential-provider-node": { 416 | "version": "3.823.0", 417 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.823.0.tgz", 418 | "integrity": "sha512-nfSxXVuZ+2GJDpVFlflNfh55Yb4BtDsXLGNssXF5YU6UgSPsi8j2YkaE92Jv2s7dlUK07l0vRpLyPuXMaGeiRQ==", 419 | "license": "Apache-2.0", 420 | "dependencies": { 421 | "@aws-sdk/credential-provider-env": "3.823.0", 422 | "@aws-sdk/credential-provider-http": "3.823.0", 423 | "@aws-sdk/credential-provider-ini": "3.823.0", 424 | "@aws-sdk/credential-provider-process": "3.823.0", 425 | "@aws-sdk/credential-provider-sso": "3.823.0", 426 | "@aws-sdk/credential-provider-web-identity": "3.823.0", 427 | "@aws-sdk/types": "3.821.0", 428 | "@smithy/credential-provider-imds": "^4.0.6", 429 | "@smithy/property-provider": "^4.0.4", 430 | "@smithy/shared-ini-file-loader": "^4.0.4", 431 | "@smithy/types": "^4.3.1", 432 | "tslib": "^2.6.2" 433 | }, 434 | "engines": { 435 | "node": ">=18.0.0" 436 | } 437 | }, 438 | "node_modules/@aws-sdk/credential-provider-process": { 439 | "version": "3.823.0", 440 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.823.0.tgz", 441 | "integrity": "sha512-U/A10/7zu2FbMFFVpIw95y0TZf+oYyrhZTBn9eL8zgWcrYRqxrxdqtPj/zMrfIfyIvQUhuJSENN4dx4tfpCMWQ==", 442 | "license": "Apache-2.0", 443 | "dependencies": { 444 | "@aws-sdk/core": "3.823.0", 445 | "@aws-sdk/types": "3.821.0", 446 | "@smithy/property-provider": "^4.0.4", 447 | "@smithy/shared-ini-file-loader": "^4.0.4", 448 | "@smithy/types": "^4.3.1", 449 | "tslib": "^2.6.2" 450 | }, 451 | "engines": { 452 | "node": ">=18.0.0" 453 | } 454 | }, 455 | "node_modules/@aws-sdk/credential-provider-sso": { 456 | "version": "3.823.0", 457 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.823.0.tgz", 458 | "integrity": "sha512-ff8IM80Wqz1V7VVMaMUqO2iR417jggfGWLPl8j2l7uCgwpEyop1ZZl5CFVYEwSupRBtwp+VlW1gTCk7ke56MUw==", 459 | "license": "Apache-2.0", 460 | "dependencies": { 461 | "@aws-sdk/client-sso": "3.823.0", 462 | "@aws-sdk/core": "3.823.0", 463 | "@aws-sdk/token-providers": "3.823.0", 464 | "@aws-sdk/types": "3.821.0", 465 | "@smithy/property-provider": "^4.0.4", 466 | "@smithy/shared-ini-file-loader": "^4.0.4", 467 | "@smithy/types": "^4.3.1", 468 | "tslib": "^2.6.2" 469 | }, 470 | "engines": { 471 | "node": ">=18.0.0" 472 | } 473 | }, 474 | "node_modules/@aws-sdk/credential-provider-web-identity": { 475 | "version": "3.823.0", 476 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.823.0.tgz", 477 | "integrity": "sha512-lzoZdJMQq9w7i4lXVka30cVBe/dZoUDZST8Xz/soEd73gg7RTKgG+0szL4xFWgdBDgcJDWLfZfJzlbyIVyAyOA==", 478 | "license": "Apache-2.0", 479 | "dependencies": { 480 | "@aws-sdk/core": "3.823.0", 481 | "@aws-sdk/nested-clients": "3.823.0", 482 | "@aws-sdk/types": "3.821.0", 483 | "@smithy/property-provider": "^4.0.4", 484 | "@smithy/types": "^4.3.1", 485 | "tslib": "^2.6.2" 486 | }, 487 | "engines": { 488 | "node": ">=18.0.0" 489 | } 490 | }, 491 | "node_modules/@aws-sdk/middleware-host-header": { 492 | "version": "3.821.0", 493 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.821.0.tgz", 494 | "integrity": "sha512-xSMR+sopSeWGx5/4pAGhhfMvGBHioVBbqGvDs6pG64xfNwM5vq5s5v6D04e2i+uSTj4qGa71dLUs5I0UzAK3sw==", 495 | "license": "Apache-2.0", 496 | "dependencies": { 497 | "@aws-sdk/types": "3.821.0", 498 | "@smithy/protocol-http": "^5.1.2", 499 | "@smithy/types": "^4.3.1", 500 | "tslib": "^2.6.2" 501 | }, 502 | "engines": { 503 | "node": ">=18.0.0" 504 | } 505 | }, 506 | "node_modules/@aws-sdk/middleware-logger": { 507 | "version": "3.821.0", 508 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.821.0.tgz", 509 | "integrity": "sha512-0cvI0ipf2tGx7fXYEEN5fBeZDz2RnHyb9xftSgUsEq7NBxjV0yTZfLJw6Za5rjE6snC80dRN8+bTNR1tuG89zA==", 510 | "license": "Apache-2.0", 511 | "dependencies": { 512 | "@aws-sdk/types": "3.821.0", 513 | "@smithy/types": "^4.3.1", 514 | "tslib": "^2.6.2" 515 | }, 516 | "engines": { 517 | "node": ">=18.0.0" 518 | } 519 | }, 520 | "node_modules/@aws-sdk/middleware-recursion-detection": { 521 | "version": "3.821.0", 522 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.821.0.tgz", 523 | "integrity": "sha512-efmaifbhBoqKG3bAoEfDdcM8hn1psF+4qa7ykWuYmfmah59JBeqHLfz5W9m9JoTwoKPkFcVLWZxnyZzAnVBOIg==", 524 | "license": "Apache-2.0", 525 | "dependencies": { 526 | "@aws-sdk/types": "3.821.0", 527 | "@smithy/protocol-http": "^5.1.2", 528 | "@smithy/types": "^4.3.1", 529 | "tslib": "^2.6.2" 530 | }, 531 | "engines": { 532 | "node": ">=18.0.0" 533 | } 534 | }, 535 | "node_modules/@aws-sdk/middleware-user-agent": { 536 | "version": "3.823.0", 537 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.823.0.tgz", 538 | "integrity": "sha512-TKRQK09ld1LrIPExC9rIDpqnMsWcv+eq8ABKFHVo8mDLTSuWx/IiQ4eCh9T5zDuEZcLY4nNYCSzXKqw6XKcMCA==", 539 | "license": "Apache-2.0", 540 | "dependencies": { 541 | "@aws-sdk/core": "3.823.0", 542 | "@aws-sdk/types": "3.821.0", 543 | "@aws-sdk/util-endpoints": "3.821.0", 544 | "@smithy/core": "^3.5.1", 545 | "@smithy/protocol-http": "^5.1.2", 546 | "@smithy/types": "^4.3.1", 547 | "tslib": "^2.6.2" 548 | }, 549 | "engines": { 550 | "node": ">=18.0.0" 551 | } 552 | }, 553 | "node_modules/@aws-sdk/nested-clients": { 554 | "version": "3.823.0", 555 | "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.823.0.tgz", 556 | "integrity": "sha512-/BcyOBubrJnd2gxlbbmNJR1w0Z3OVN/UE8Yz20e+ou+Mijjv7EbtVwmWvio1e3ZjphwdA8tVfPYZKwXmrvHKmQ==", 557 | "license": "Apache-2.0", 558 | "dependencies": { 559 | "@aws-crypto/sha256-browser": "5.2.0", 560 | "@aws-crypto/sha256-js": "5.2.0", 561 | "@aws-sdk/core": "3.823.0", 562 | "@aws-sdk/middleware-host-header": "3.821.0", 563 | "@aws-sdk/middleware-logger": "3.821.0", 564 | "@aws-sdk/middleware-recursion-detection": "3.821.0", 565 | "@aws-sdk/middleware-user-agent": "3.823.0", 566 | "@aws-sdk/region-config-resolver": "3.821.0", 567 | "@aws-sdk/types": "3.821.0", 568 | "@aws-sdk/util-endpoints": "3.821.0", 569 | "@aws-sdk/util-user-agent-browser": "3.821.0", 570 | "@aws-sdk/util-user-agent-node": "3.823.0", 571 | "@smithy/config-resolver": "^4.1.4", 572 | "@smithy/core": "^3.5.1", 573 | "@smithy/fetch-http-handler": "^5.0.4", 574 | "@smithy/hash-node": "^4.0.4", 575 | "@smithy/invalid-dependency": "^4.0.4", 576 | "@smithy/middleware-content-length": "^4.0.4", 577 | "@smithy/middleware-endpoint": "^4.1.9", 578 | "@smithy/middleware-retry": "^4.1.10", 579 | "@smithy/middleware-serde": "^4.0.8", 580 | "@smithy/middleware-stack": "^4.0.4", 581 | "@smithy/node-config-provider": "^4.1.3", 582 | "@smithy/node-http-handler": "^4.0.6", 583 | "@smithy/protocol-http": "^5.1.2", 584 | "@smithy/smithy-client": "^4.4.1", 585 | "@smithy/types": "^4.3.1", 586 | "@smithy/url-parser": "^4.0.4", 587 | "@smithy/util-base64": "^4.0.0", 588 | "@smithy/util-body-length-browser": "^4.0.0", 589 | "@smithy/util-body-length-node": "^4.0.0", 590 | "@smithy/util-defaults-mode-browser": "^4.0.17", 591 | "@smithy/util-defaults-mode-node": "^4.0.17", 592 | "@smithy/util-endpoints": "^3.0.6", 593 | "@smithy/util-middleware": "^4.0.4", 594 | "@smithy/util-retry": "^4.0.5", 595 | "@smithy/util-utf8": "^4.0.0", 596 | "tslib": "^2.6.2" 597 | }, 598 | "engines": { 599 | "node": ">=18.0.0" 600 | } 601 | }, 602 | "node_modules/@aws-sdk/region-config-resolver": { 603 | "version": "3.821.0", 604 | "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.821.0.tgz", 605 | "integrity": "sha512-t8og+lRCIIy5nlId0bScNpCkif8sc0LhmtaKsbm0ZPm3sCa/WhCbSZibjbZ28FNjVCV+p0D9RYZx0VDDbtWyjw==", 606 | "license": "Apache-2.0", 607 | "dependencies": { 608 | "@aws-sdk/types": "3.821.0", 609 | "@smithy/node-config-provider": "^4.1.3", 610 | "@smithy/types": "^4.3.1", 611 | "@smithy/util-config-provider": "^4.0.0", 612 | "@smithy/util-middleware": "^4.0.4", 613 | "tslib": "^2.6.2" 614 | }, 615 | "engines": { 616 | "node": ">=18.0.0" 617 | } 618 | }, 619 | "node_modules/@aws-sdk/token-providers": { 620 | "version": "3.823.0", 621 | "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.823.0.tgz", 622 | "integrity": "sha512-vz6onCb/+g4y+owxGGPMEMdN789dTfBOgz/c9pFv0f01840w9Rrt46l+gjQlnXnx+0KG6wNeBIVhFdbCfV3HyQ==", 623 | "license": "Apache-2.0", 624 | "dependencies": { 625 | "@aws-sdk/core": "3.823.0", 626 | "@aws-sdk/nested-clients": "3.823.0", 627 | "@aws-sdk/types": "3.821.0", 628 | "@smithy/property-provider": "^4.0.4", 629 | "@smithy/shared-ini-file-loader": "^4.0.4", 630 | "@smithy/types": "^4.3.1", 631 | "tslib": "^2.6.2" 632 | }, 633 | "engines": { 634 | "node": ">=18.0.0" 635 | } 636 | }, 637 | "node_modules/@aws-sdk/types": { 638 | "version": "3.821.0", 639 | "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.821.0.tgz", 640 | "integrity": "sha512-Znroqdai1a90TlxGaJ+FK1lwC0fHpo97Xjsp5UKGR5JODYm7f9+/fF17ebO1KdoBr/Rm0UIFiF5VmI8ts9F1eA==", 641 | "license": "Apache-2.0", 642 | "dependencies": { 643 | "@smithy/types": "^4.3.1", 644 | "tslib": "^2.6.2" 645 | }, 646 | "engines": { 647 | "node": ">=18.0.0" 648 | } 649 | }, 650 | "node_modules/@aws-sdk/util-endpoints": { 651 | "version": "3.821.0", 652 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.821.0.tgz", 653 | "integrity": "sha512-Uknt/zUZnLE76zaAAPEayOeF5/4IZ2puTFXvcSCWHsi9m3tqbb9UozlnlVqvCZLCRWfQryZQoG2W4XSS3qgk5A==", 654 | "license": "Apache-2.0", 655 | "dependencies": { 656 | "@aws-sdk/types": "3.821.0", 657 | "@smithy/types": "^4.3.1", 658 | "@smithy/util-endpoints": "^3.0.6", 659 | "tslib": "^2.6.2" 660 | }, 661 | "engines": { 662 | "node": ">=18.0.0" 663 | } 664 | }, 665 | "node_modules/@aws-sdk/util-locate-window": { 666 | "version": "3.568.0", 667 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.568.0.tgz", 668 | "integrity": "sha512-3nh4TINkXYr+H41QaPelCceEB2FXP3fxp93YZXB/kqJvX0U9j0N0Uk45gvsjmEPzG8XxkPEeLIfT2I1M7A6Lig==", 669 | "license": "Apache-2.0", 670 | "dependencies": { 671 | "tslib": "^2.6.2" 672 | }, 673 | "engines": { 674 | "node": ">=16.0.0" 675 | } 676 | }, 677 | "node_modules/@aws-sdk/util-user-agent-browser": { 678 | "version": "3.821.0", 679 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.821.0.tgz", 680 | "integrity": "sha512-irWZHyM0Jr1xhC+38OuZ7JB6OXMLPZlj48thElpsO1ZSLRkLZx5+I7VV6k3sp2yZ7BYbKz/G2ojSv4wdm7XTLw==", 681 | "license": "Apache-2.0", 682 | "dependencies": { 683 | "@aws-sdk/types": "3.821.0", 684 | "@smithy/types": "^4.3.1", 685 | "bowser": "^2.11.0", 686 | "tslib": "^2.6.2" 687 | } 688 | }, 689 | "node_modules/@aws-sdk/util-user-agent-node": { 690 | "version": "3.823.0", 691 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.823.0.tgz", 692 | "integrity": "sha512-WvNeRz7HV3JLBVGTXW4Qr5QvvWY0vtggH5jW/NqHFH+ZEliVQaUIJ/HNLMpMoCSiu/DlpQAyAjRZXAptJ0oqbw==", 693 | "license": "Apache-2.0", 694 | "dependencies": { 695 | "@aws-sdk/middleware-user-agent": "3.823.0", 696 | "@aws-sdk/types": "3.821.0", 697 | "@smithy/node-config-provider": "^4.1.3", 698 | "@smithy/types": "^4.3.1", 699 | "tslib": "^2.6.2" 700 | }, 701 | "engines": { 702 | "node": ">=18.0.0" 703 | }, 704 | "peerDependencies": { 705 | "aws-crt": ">=1.0.0" 706 | }, 707 | "peerDependenciesMeta": { 708 | "aws-crt": { 709 | "optional": true 710 | } 711 | } 712 | }, 713 | "node_modules/@aws-sdk/xml-builder": { 714 | "version": "3.821.0", 715 | "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.821.0.tgz", 716 | "integrity": "sha512-DIIotRnefVL6DiaHtO6/21DhJ4JZnnIwdNbpwiAhdt/AVbttcE4yw925gsjur0OGv5BTYXQXU3YnANBYnZjuQA==", 717 | "license": "Apache-2.0", 718 | "dependencies": { 719 | "@smithy/types": "^4.3.1", 720 | "tslib": "^2.6.2" 721 | }, 722 | "engines": { 723 | "node": ">=18.0.0" 724 | } 725 | }, 726 | "node_modules/@cspotcode/source-map-support": { 727 | "version": "0.8.1", 728 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", 729 | "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", 730 | "dependencies": { 731 | "@jridgewell/trace-mapping": "0.3.9" 732 | }, 733 | "engines": { 734 | "node": ">=12" 735 | } 736 | }, 737 | "node_modules/@jridgewell/resolve-uri": { 738 | "version": "3.1.1", 739 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", 740 | "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", 741 | "engines": { 742 | "node": ">=6.0.0" 743 | } 744 | }, 745 | "node_modules/@jridgewell/sourcemap-codec": { 746 | "version": "1.4.15", 747 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", 748 | "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" 749 | }, 750 | "node_modules/@jridgewell/trace-mapping": { 751 | "version": "0.3.9", 752 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", 753 | "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", 754 | "dependencies": { 755 | "@jridgewell/resolve-uri": "^3.0.3", 756 | "@jridgewell/sourcemap-codec": "^1.4.10" 757 | } 758 | }, 759 | "node_modules/@smithy/abort-controller": { 760 | "version": "4.0.4", 761 | "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.4.tgz", 762 | "integrity": "sha512-gJnEjZMvigPDQWHrW3oPrFhQtkrgqBkyjj3pCIdF3A5M6vsZODG93KNlfJprv6bp4245bdT32fsHK4kkH3KYDA==", 763 | "license": "Apache-2.0", 764 | "dependencies": { 765 | "@smithy/types": "^4.3.1", 766 | "tslib": "^2.6.2" 767 | }, 768 | "engines": { 769 | "node": ">=18.0.0" 770 | } 771 | }, 772 | "node_modules/@smithy/config-resolver": { 773 | "version": "4.1.4", 774 | "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.1.4.tgz", 775 | "integrity": "sha512-prmU+rDddxHOH0oNcwemL+SwnzcG65sBF2yXRO7aeXIn/xTlq2pX7JLVbkBnVLowHLg4/OL4+jBmv9hVrVGS+w==", 776 | "license": "Apache-2.0", 777 | "dependencies": { 778 | "@smithy/node-config-provider": "^4.1.3", 779 | "@smithy/types": "^4.3.1", 780 | "@smithy/util-config-provider": "^4.0.0", 781 | "@smithy/util-middleware": "^4.0.4", 782 | "tslib": "^2.6.2" 783 | }, 784 | "engines": { 785 | "node": ">=18.0.0" 786 | } 787 | }, 788 | "node_modules/@smithy/core": { 789 | "version": "3.5.1", 790 | "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.5.1.tgz", 791 | "integrity": "sha512-xSw7bZEFKwOKrm/iv8e2BLt2ur98YZdrRD6nII8ditQeUsY2Q1JmIQ0rpILOhaLKYxxG2ivnoOpokzr9qLyDWA==", 792 | "license": "Apache-2.0", 793 | "dependencies": { 794 | "@smithy/middleware-serde": "^4.0.8", 795 | "@smithy/protocol-http": "^5.1.2", 796 | "@smithy/types": "^4.3.1", 797 | "@smithy/util-base64": "^4.0.0", 798 | "@smithy/util-body-length-browser": "^4.0.0", 799 | "@smithy/util-middleware": "^4.0.4", 800 | "@smithy/util-stream": "^4.2.2", 801 | "@smithy/util-utf8": "^4.0.0", 802 | "tslib": "^2.6.2" 803 | }, 804 | "engines": { 805 | "node": ">=18.0.0" 806 | } 807 | }, 808 | "node_modules/@smithy/credential-provider-imds": { 809 | "version": "4.0.6", 810 | "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.6.tgz", 811 | "integrity": "sha512-hKMWcANhUiNbCJouYkZ9V3+/Qf9pteR1dnwgdyzR09R4ODEYx8BbUysHwRSyex4rZ9zapddZhLFTnT4ZijR4pw==", 812 | "license": "Apache-2.0", 813 | "dependencies": { 814 | "@smithy/node-config-provider": "^4.1.3", 815 | "@smithy/property-provider": "^4.0.4", 816 | "@smithy/types": "^4.3.1", 817 | "@smithy/url-parser": "^4.0.4", 818 | "tslib": "^2.6.2" 819 | }, 820 | "engines": { 821 | "node": ">=18.0.0" 822 | } 823 | }, 824 | "node_modules/@smithy/fetch-http-handler": { 825 | "version": "5.0.4", 826 | "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.0.4.tgz", 827 | "integrity": "sha512-AMtBR5pHppYMVD7z7G+OlHHAcgAN7v0kVKEpHuTO4Gb199Gowh0taYi9oDStFeUhetkeP55JLSVlTW1n9rFtUw==", 828 | "license": "Apache-2.0", 829 | "dependencies": { 830 | "@smithy/protocol-http": "^5.1.2", 831 | "@smithy/querystring-builder": "^4.0.4", 832 | "@smithy/types": "^4.3.1", 833 | "@smithy/util-base64": "^4.0.0", 834 | "tslib": "^2.6.2" 835 | }, 836 | "engines": { 837 | "node": ">=18.0.0" 838 | } 839 | }, 840 | "node_modules/@smithy/hash-node": { 841 | "version": "4.0.4", 842 | "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.4.tgz", 843 | "integrity": "sha512-qnbTPUhCVnCgBp4z4BUJUhOEkVwxiEi1cyFM+Zj6o+aY8OFGxUQleKWq8ltgp3dujuhXojIvJWdoqpm6dVO3lQ==", 844 | "license": "Apache-2.0", 845 | "dependencies": { 846 | "@smithy/types": "^4.3.1", 847 | "@smithy/util-buffer-from": "^4.0.0", 848 | "@smithy/util-utf8": "^4.0.0", 849 | "tslib": "^2.6.2" 850 | }, 851 | "engines": { 852 | "node": ">=18.0.0" 853 | } 854 | }, 855 | "node_modules/@smithy/invalid-dependency": { 856 | "version": "4.0.4", 857 | "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.4.tgz", 858 | "integrity": "sha512-bNYMi7WKTJHu0gn26wg8OscncTt1t2b8KcsZxvOv56XA6cyXtOAAAaNP7+m45xfppXfOatXF3Sb1MNsLUgVLTw==", 859 | "license": "Apache-2.0", 860 | "dependencies": { 861 | "@smithy/types": "^4.3.1", 862 | "tslib": "^2.6.2" 863 | }, 864 | "engines": { 865 | "node": ">=18.0.0" 866 | } 867 | }, 868 | "node_modules/@smithy/is-array-buffer": { 869 | "version": "4.0.0", 870 | "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz", 871 | "integrity": "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==", 872 | "license": "Apache-2.0", 873 | "dependencies": { 874 | "tslib": "^2.6.2" 875 | }, 876 | "engines": { 877 | "node": ">=18.0.0" 878 | } 879 | }, 880 | "node_modules/@smithy/middleware-content-length": { 881 | "version": "4.0.4", 882 | "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.4.tgz", 883 | "integrity": "sha512-F7gDyfI2BB1Kc+4M6rpuOLne5LOcEknH1n6UQB69qv+HucXBR1rkzXBnQTB2q46sFy1PM/zuSJOB532yc8bg3w==", 884 | "license": "Apache-2.0", 885 | "dependencies": { 886 | "@smithy/protocol-http": "^5.1.2", 887 | "@smithy/types": "^4.3.1", 888 | "tslib": "^2.6.2" 889 | }, 890 | "engines": { 891 | "node": ">=18.0.0" 892 | } 893 | }, 894 | "node_modules/@smithy/middleware-endpoint": { 895 | "version": "4.1.9", 896 | "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.9.tgz", 897 | "integrity": "sha512-AjDgX4UjORLltD/LZCBQTwjQqEfyrx/GeDTHcYLzIgf87pIT70tMWnN87NQpJru1K4ITirY2htSOxNECZJCBOg==", 898 | "license": "Apache-2.0", 899 | "dependencies": { 900 | "@smithy/core": "^3.5.1", 901 | "@smithy/middleware-serde": "^4.0.8", 902 | "@smithy/node-config-provider": "^4.1.3", 903 | "@smithy/shared-ini-file-loader": "^4.0.4", 904 | "@smithy/types": "^4.3.1", 905 | "@smithy/url-parser": "^4.0.4", 906 | "@smithy/util-middleware": "^4.0.4", 907 | "tslib": "^2.6.2" 908 | }, 909 | "engines": { 910 | "node": ">=18.0.0" 911 | } 912 | }, 913 | "node_modules/@smithy/middleware-retry": { 914 | "version": "4.1.10", 915 | "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.10.tgz", 916 | "integrity": "sha512-RyhcA3sZIIvAo6r48b2Nx2qfg0OnyohlaV0fw415xrQyx5HQ2bvHl9vs/WBiDXIP49mCfws5wX4308c9Pi/isw==", 917 | "license": "Apache-2.0", 918 | "dependencies": { 919 | "@smithy/node-config-provider": "^4.1.3", 920 | "@smithy/protocol-http": "^5.1.2", 921 | "@smithy/service-error-classification": "^4.0.5", 922 | "@smithy/smithy-client": "^4.4.1", 923 | "@smithy/types": "^4.3.1", 924 | "@smithy/util-middleware": "^4.0.4", 925 | "@smithy/util-retry": "^4.0.5", 926 | "tslib": "^2.6.2", 927 | "uuid": "^9.0.1" 928 | }, 929 | "engines": { 930 | "node": ">=18.0.0" 931 | } 932 | }, 933 | "node_modules/@smithy/middleware-serde": { 934 | "version": "4.0.8", 935 | "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.8.tgz", 936 | "integrity": "sha512-iSSl7HJoJaGyMIoNn2B7czghOVwJ9nD7TMvLhMWeSB5vt0TnEYyRRqPJu/TqW76WScaNvYYB8nRoiBHR9S1Ddw==", 937 | "license": "Apache-2.0", 938 | "dependencies": { 939 | "@smithy/protocol-http": "^5.1.2", 940 | "@smithy/types": "^4.3.1", 941 | "tslib": "^2.6.2" 942 | }, 943 | "engines": { 944 | "node": ">=18.0.0" 945 | } 946 | }, 947 | "node_modules/@smithy/middleware-stack": { 948 | "version": "4.0.4", 949 | "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.4.tgz", 950 | "integrity": "sha512-kagK5ggDrBUCCzI93ft6DjteNSfY8Ulr83UtySog/h09lTIOAJ/xUSObutanlPT0nhoHAkpmW9V5K8oPyLh+QA==", 951 | "license": "Apache-2.0", 952 | "dependencies": { 953 | "@smithy/types": "^4.3.1", 954 | "tslib": "^2.6.2" 955 | }, 956 | "engines": { 957 | "node": ">=18.0.0" 958 | } 959 | }, 960 | "node_modules/@smithy/node-config-provider": { 961 | "version": "4.1.3", 962 | "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.1.3.tgz", 963 | "integrity": "sha512-HGHQr2s59qaU1lrVH6MbLlmOBxadtzTsoO4c+bF5asdgVik3I8o7JIOzoeqWc5MjVa+vD36/LWE0iXKpNqooRw==", 964 | "license": "Apache-2.0", 965 | "dependencies": { 966 | "@smithy/property-provider": "^4.0.4", 967 | "@smithy/shared-ini-file-loader": "^4.0.4", 968 | "@smithy/types": "^4.3.1", 969 | "tslib": "^2.6.2" 970 | }, 971 | "engines": { 972 | "node": ">=18.0.0" 973 | } 974 | }, 975 | "node_modules/@smithy/node-http-handler": { 976 | "version": "4.0.6", 977 | "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.6.tgz", 978 | "integrity": "sha512-NqbmSz7AW2rvw4kXhKGrYTiJVDHnMsFnX4i+/FzcZAfbOBauPYs2ekuECkSbtqaxETLLTu9Rl/ex6+I2BKErPA==", 979 | "license": "Apache-2.0", 980 | "dependencies": { 981 | "@smithy/abort-controller": "^4.0.4", 982 | "@smithy/protocol-http": "^5.1.2", 983 | "@smithy/querystring-builder": "^4.0.4", 984 | "@smithy/types": "^4.3.1", 985 | "tslib": "^2.6.2" 986 | }, 987 | "engines": { 988 | "node": ">=18.0.0" 989 | } 990 | }, 991 | "node_modules/@smithy/property-provider": { 992 | "version": "4.0.4", 993 | "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.4.tgz", 994 | "integrity": "sha512-qHJ2sSgu4FqF4U/5UUp4DhXNmdTrgmoAai6oQiM+c5RZ/sbDwJ12qxB1M6FnP+Tn/ggkPZf9ccn4jqKSINaquw==", 995 | "license": "Apache-2.0", 996 | "dependencies": { 997 | "@smithy/types": "^4.3.1", 998 | "tslib": "^2.6.2" 999 | }, 1000 | "engines": { 1001 | "node": ">=18.0.0" 1002 | } 1003 | }, 1004 | "node_modules/@smithy/protocol-http": { 1005 | "version": "5.1.2", 1006 | "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.1.2.tgz", 1007 | "integrity": "sha512-rOG5cNLBXovxIrICSBm95dLqzfvxjEmuZx4KK3hWwPFHGdW3lxY0fZNXfv2zebfRO7sJZ5pKJYHScsqopeIWtQ==", 1008 | "license": "Apache-2.0", 1009 | "dependencies": { 1010 | "@smithy/types": "^4.3.1", 1011 | "tslib": "^2.6.2" 1012 | }, 1013 | "engines": { 1014 | "node": ">=18.0.0" 1015 | } 1016 | }, 1017 | "node_modules/@smithy/querystring-builder": { 1018 | "version": "4.0.4", 1019 | "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.4.tgz", 1020 | "integrity": "sha512-SwREZcDnEYoh9tLNgMbpop+UTGq44Hl9tdj3rf+yeLcfH7+J8OXEBaMc2kDxtyRHu8BhSg9ADEx0gFHvpJgU8w==", 1021 | "license": "Apache-2.0", 1022 | "dependencies": { 1023 | "@smithy/types": "^4.3.1", 1024 | "@smithy/util-uri-escape": "^4.0.0", 1025 | "tslib": "^2.6.2" 1026 | }, 1027 | "engines": { 1028 | "node": ">=18.0.0" 1029 | } 1030 | }, 1031 | "node_modules/@smithy/querystring-parser": { 1032 | "version": "4.0.4", 1033 | "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.4.tgz", 1034 | "integrity": "sha512-6yZf53i/qB8gRHH/l2ZwUG5xgkPgQF15/KxH0DdXMDHjesA9MeZje/853ifkSY0x4m5S+dfDZ+c4x439PF0M2w==", 1035 | "license": "Apache-2.0", 1036 | "dependencies": { 1037 | "@smithy/types": "^4.3.1", 1038 | "tslib": "^2.6.2" 1039 | }, 1040 | "engines": { 1041 | "node": ">=18.0.0" 1042 | } 1043 | }, 1044 | "node_modules/@smithy/service-error-classification": { 1045 | "version": "4.0.5", 1046 | "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.5.tgz", 1047 | "integrity": "sha512-LvcfhrnCBvCmTee81pRlh1F39yTS/+kYleVeLCwNtkY8wtGg8V/ca9rbZZvYIl8OjlMtL6KIjaiL/lgVqHD2nA==", 1048 | "license": "Apache-2.0", 1049 | "dependencies": { 1050 | "@smithy/types": "^4.3.1" 1051 | }, 1052 | "engines": { 1053 | "node": ">=18.0.0" 1054 | } 1055 | }, 1056 | "node_modules/@smithy/shared-ini-file-loader": { 1057 | "version": "4.0.4", 1058 | "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.4.tgz", 1059 | "integrity": "sha512-63X0260LoFBjrHifPDs+nM9tV0VMkOTl4JRMYNuKh/f5PauSjowTfvF3LogfkWdcPoxsA9UjqEOgjeYIbhb7Nw==", 1060 | "license": "Apache-2.0", 1061 | "dependencies": { 1062 | "@smithy/types": "^4.3.1", 1063 | "tslib": "^2.6.2" 1064 | }, 1065 | "engines": { 1066 | "node": ">=18.0.0" 1067 | } 1068 | }, 1069 | "node_modules/@smithy/signature-v4": { 1070 | "version": "5.1.2", 1071 | "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.1.2.tgz", 1072 | "integrity": "sha512-d3+U/VpX7a60seHziWnVZOHuEgJlclufjkS6zhXvxcJgkJq4UWdH5eOBLzHRMx6gXjsdT9h6lfpmLzbrdupHgQ==", 1073 | "license": "Apache-2.0", 1074 | "dependencies": { 1075 | "@smithy/is-array-buffer": "^4.0.0", 1076 | "@smithy/protocol-http": "^5.1.2", 1077 | "@smithy/types": "^4.3.1", 1078 | "@smithy/util-hex-encoding": "^4.0.0", 1079 | "@smithy/util-middleware": "^4.0.4", 1080 | "@smithy/util-uri-escape": "^4.0.0", 1081 | "@smithy/util-utf8": "^4.0.0", 1082 | "tslib": "^2.6.2" 1083 | }, 1084 | "engines": { 1085 | "node": ">=18.0.0" 1086 | } 1087 | }, 1088 | "node_modules/@smithy/smithy-client": { 1089 | "version": "4.4.1", 1090 | "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.4.1.tgz", 1091 | "integrity": "sha512-XPbcHRfd0iwx8dY5XCBCGyI7uweMW0oezYezxXcG8ANgvZ5YPuC6Ylh+n0bTHpdU3SCMZOnhzgVklYz+p3fIhw==", 1092 | "license": "Apache-2.0", 1093 | "dependencies": { 1094 | "@smithy/core": "^3.5.1", 1095 | "@smithy/middleware-endpoint": "^4.1.9", 1096 | "@smithy/middleware-stack": "^4.0.4", 1097 | "@smithy/protocol-http": "^5.1.2", 1098 | "@smithy/types": "^4.3.1", 1099 | "@smithy/util-stream": "^4.2.2", 1100 | "tslib": "^2.6.2" 1101 | }, 1102 | "engines": { 1103 | "node": ">=18.0.0" 1104 | } 1105 | }, 1106 | "node_modules/@smithy/types": { 1107 | "version": "4.3.1", 1108 | "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.3.1.tgz", 1109 | "integrity": "sha512-UqKOQBL2x6+HWl3P+3QqFD4ncKq0I8Nuz9QItGv5WuKuMHuuwlhvqcZCoXGfc+P1QmfJE7VieykoYYmrOoFJxA==", 1110 | "license": "Apache-2.0", 1111 | "dependencies": { 1112 | "tslib": "^2.6.2" 1113 | }, 1114 | "engines": { 1115 | "node": ">=18.0.0" 1116 | } 1117 | }, 1118 | "node_modules/@smithy/url-parser": { 1119 | "version": "4.0.4", 1120 | "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.4.tgz", 1121 | "integrity": "sha512-eMkc144MuN7B0TDA4U2fKs+BqczVbk3W+qIvcoCY6D1JY3hnAdCuhCZODC+GAeaxj0p6Jroz4+XMUn3PCxQQeQ==", 1122 | "license": "Apache-2.0", 1123 | "dependencies": { 1124 | "@smithy/querystring-parser": "^4.0.4", 1125 | "@smithy/types": "^4.3.1", 1126 | "tslib": "^2.6.2" 1127 | }, 1128 | "engines": { 1129 | "node": ">=18.0.0" 1130 | } 1131 | }, 1132 | "node_modules/@smithy/util-base64": { 1133 | "version": "4.0.0", 1134 | "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.0.0.tgz", 1135 | "integrity": "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==", 1136 | "license": "Apache-2.0", 1137 | "dependencies": { 1138 | "@smithy/util-buffer-from": "^4.0.0", 1139 | "@smithy/util-utf8": "^4.0.0", 1140 | "tslib": "^2.6.2" 1141 | }, 1142 | "engines": { 1143 | "node": ">=18.0.0" 1144 | } 1145 | }, 1146 | "node_modules/@smithy/util-body-length-browser": { 1147 | "version": "4.0.0", 1148 | "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.0.0.tgz", 1149 | "integrity": "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==", 1150 | "license": "Apache-2.0", 1151 | "dependencies": { 1152 | "tslib": "^2.6.2" 1153 | }, 1154 | "engines": { 1155 | "node": ">=18.0.0" 1156 | } 1157 | }, 1158 | "node_modules/@smithy/util-body-length-node": { 1159 | "version": "4.0.0", 1160 | "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.0.0.tgz", 1161 | "integrity": "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==", 1162 | "license": "Apache-2.0", 1163 | "dependencies": { 1164 | "tslib": "^2.6.2" 1165 | }, 1166 | "engines": { 1167 | "node": ">=18.0.0" 1168 | } 1169 | }, 1170 | "node_modules/@smithy/util-buffer-from": { 1171 | "version": "4.0.0", 1172 | "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.0.0.tgz", 1173 | "integrity": "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==", 1174 | "license": "Apache-2.0", 1175 | "dependencies": { 1176 | "@smithy/is-array-buffer": "^4.0.0", 1177 | "tslib": "^2.6.2" 1178 | }, 1179 | "engines": { 1180 | "node": ">=18.0.0" 1181 | } 1182 | }, 1183 | "node_modules/@smithy/util-config-provider": { 1184 | "version": "4.0.0", 1185 | "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz", 1186 | "integrity": "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==", 1187 | "license": "Apache-2.0", 1188 | "dependencies": { 1189 | "tslib": "^2.6.2" 1190 | }, 1191 | "engines": { 1192 | "node": ">=18.0.0" 1193 | } 1194 | }, 1195 | "node_modules/@smithy/util-defaults-mode-browser": { 1196 | "version": "4.0.17", 1197 | "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.17.tgz", 1198 | "integrity": "sha512-HXq5181qnXmIwB7VrwqwP8rsJybHMoYuJnNoXy4PROs2pfSI4sWDMASF2i+7Lo+u64Y6xowhegcdxczowgJtZg==", 1199 | "license": "Apache-2.0", 1200 | "dependencies": { 1201 | "@smithy/property-provider": "^4.0.4", 1202 | "@smithy/smithy-client": "^4.4.1", 1203 | "@smithy/types": "^4.3.1", 1204 | "bowser": "^2.11.0", 1205 | "tslib": "^2.6.2" 1206 | }, 1207 | "engines": { 1208 | "node": ">=18.0.0" 1209 | } 1210 | }, 1211 | "node_modules/@smithy/util-defaults-mode-node": { 1212 | "version": "4.0.17", 1213 | "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.17.tgz", 1214 | "integrity": "sha512-RfU2A5LjFhEHw4Nwl1GZNitK4AUWu5jGtigAUDoQtfDUvYHpQxcuLw2QGAdKDtKRflIiHSZ8wXBDR36H9R2Ang==", 1215 | "license": "Apache-2.0", 1216 | "dependencies": { 1217 | "@smithy/config-resolver": "^4.1.4", 1218 | "@smithy/credential-provider-imds": "^4.0.6", 1219 | "@smithy/node-config-provider": "^4.1.3", 1220 | "@smithy/property-provider": "^4.0.4", 1221 | "@smithy/smithy-client": "^4.4.1", 1222 | "@smithy/types": "^4.3.1", 1223 | "tslib": "^2.6.2" 1224 | }, 1225 | "engines": { 1226 | "node": ">=18.0.0" 1227 | } 1228 | }, 1229 | "node_modules/@smithy/util-endpoints": { 1230 | "version": "3.0.6", 1231 | "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.6.tgz", 1232 | "integrity": "sha512-YARl3tFL3WgPuLzljRUnrS2ngLiUtkwhQtj8PAL13XZSyUiNLQxwG3fBBq3QXFqGFUXepIN73pINp3y8c2nBmA==", 1233 | "license": "Apache-2.0", 1234 | "dependencies": { 1235 | "@smithy/node-config-provider": "^4.1.3", 1236 | "@smithy/types": "^4.3.1", 1237 | "tslib": "^2.6.2" 1238 | }, 1239 | "engines": { 1240 | "node": ">=18.0.0" 1241 | } 1242 | }, 1243 | "node_modules/@smithy/util-hex-encoding": { 1244 | "version": "4.0.0", 1245 | "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz", 1246 | "integrity": "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==", 1247 | "license": "Apache-2.0", 1248 | "dependencies": { 1249 | "tslib": "^2.6.2" 1250 | }, 1251 | "engines": { 1252 | "node": ">=18.0.0" 1253 | } 1254 | }, 1255 | "node_modules/@smithy/util-middleware": { 1256 | "version": "4.0.4", 1257 | "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.4.tgz", 1258 | "integrity": "sha512-9MLKmkBmf4PRb0ONJikCbCwORACcil6gUWojwARCClT7RmLzF04hUR4WdRprIXal7XVyrddadYNfp2eF3nrvtQ==", 1259 | "license": "Apache-2.0", 1260 | "dependencies": { 1261 | "@smithy/types": "^4.3.1", 1262 | "tslib": "^2.6.2" 1263 | }, 1264 | "engines": { 1265 | "node": ">=18.0.0" 1266 | } 1267 | }, 1268 | "node_modules/@smithy/util-retry": { 1269 | "version": "4.0.5", 1270 | "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.5.tgz", 1271 | "integrity": "sha512-V7MSjVDTlEt/plmOFBn1762Dyu5uqMrV2Pl2X0dYk4XvWfdWJNe9Bs5Bzb56wkCuiWjSfClVMGcsuKrGj7S/yg==", 1272 | "license": "Apache-2.0", 1273 | "dependencies": { 1274 | "@smithy/service-error-classification": "^4.0.5", 1275 | "@smithy/types": "^4.3.1", 1276 | "tslib": "^2.6.2" 1277 | }, 1278 | "engines": { 1279 | "node": ">=18.0.0" 1280 | } 1281 | }, 1282 | "node_modules/@smithy/util-stream": { 1283 | "version": "4.2.2", 1284 | "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.2.2.tgz", 1285 | "integrity": "sha512-aI+GLi7MJoVxg24/3J1ipwLoYzgkB4kUfogZfnslcYlynj3xsQ0e7vk4TnTro9hhsS5PvX1mwmkRqqHQjwcU7w==", 1286 | "license": "Apache-2.0", 1287 | "dependencies": { 1288 | "@smithy/fetch-http-handler": "^5.0.4", 1289 | "@smithy/node-http-handler": "^4.0.6", 1290 | "@smithy/types": "^4.3.1", 1291 | "@smithy/util-base64": "^4.0.0", 1292 | "@smithy/util-buffer-from": "^4.0.0", 1293 | "@smithy/util-hex-encoding": "^4.0.0", 1294 | "@smithy/util-utf8": "^4.0.0", 1295 | "tslib": "^2.6.2" 1296 | }, 1297 | "engines": { 1298 | "node": ">=18.0.0" 1299 | } 1300 | }, 1301 | "node_modules/@smithy/util-uri-escape": { 1302 | "version": "4.0.0", 1303 | "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.0.0.tgz", 1304 | "integrity": "sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==", 1305 | "license": "Apache-2.0", 1306 | "dependencies": { 1307 | "tslib": "^2.6.2" 1308 | }, 1309 | "engines": { 1310 | "node": ">=18.0.0" 1311 | } 1312 | }, 1313 | "node_modules/@smithy/util-utf8": { 1314 | "version": "4.0.0", 1315 | "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.0.0.tgz", 1316 | "integrity": "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==", 1317 | "license": "Apache-2.0", 1318 | "dependencies": { 1319 | "@smithy/util-buffer-from": "^4.0.0", 1320 | "tslib": "^2.6.2" 1321 | }, 1322 | "engines": { 1323 | "node": ">=18.0.0" 1324 | } 1325 | }, 1326 | "node_modules/@tsconfig/node10": { 1327 | "version": "1.0.9", 1328 | "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", 1329 | "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==" 1330 | }, 1331 | "node_modules/@tsconfig/node12": { 1332 | "version": "1.0.11", 1333 | "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", 1334 | "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" 1335 | }, 1336 | "node_modules/@tsconfig/node14": { 1337 | "version": "1.0.3", 1338 | "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", 1339 | "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" 1340 | }, 1341 | "node_modules/@tsconfig/node16": { 1342 | "version": "1.0.4", 1343 | "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", 1344 | "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==" 1345 | }, 1346 | "node_modules/@types/node": { 1347 | "version": "20.8.5", 1348 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.5.tgz", 1349 | "integrity": "sha512-SPlobFgbidfIeOYlzXiEjSYeIJiOCthv+9tSQVpvk4PAdIIc+2SmjNVzWXk9t0Y7dl73Zdf+OgXKHX9XtkqUpw==", 1350 | "peer": true, 1351 | "dependencies": { 1352 | "undici-types": "~5.25.1" 1353 | } 1354 | }, 1355 | "node_modules/acorn": { 1356 | "version": "8.10.0", 1357 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", 1358 | "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", 1359 | "bin": { 1360 | "acorn": "bin/acorn" 1361 | }, 1362 | "engines": { 1363 | "node": ">=0.4.0" 1364 | } 1365 | }, 1366 | "node_modules/acorn-walk": { 1367 | "version": "8.2.0", 1368 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", 1369 | "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", 1370 | "engines": { 1371 | "node": ">=0.4.0" 1372 | } 1373 | }, 1374 | "node_modules/arg": { 1375 | "version": "4.1.3", 1376 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 1377 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" 1378 | }, 1379 | "node_modules/aws-cdk": { 1380 | "version": "2.1018.0", 1381 | "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1018.0.tgz", 1382 | "integrity": "sha512-sppVsNtFJTW4wawS/PBudHCSNHb8xwaZ2WX1mpsfwaPNyTWm0eSUVJsRbRiRBu9O/Us8pgrd4woUjfM1lgD7Kw==", 1383 | "license": "Apache-2.0", 1384 | "bin": { 1385 | "cdk": "bin/cdk" 1386 | }, 1387 | "engines": { 1388 | "node": ">= 18.0.0" 1389 | }, 1390 | "optionalDependencies": { 1391 | "fsevents": "2.3.2" 1392 | } 1393 | }, 1394 | "node_modules/aws-cdk-lib": { 1395 | "version": "2.200.1", 1396 | "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.200.1.tgz", 1397 | "integrity": "sha512-kLeDtMJPYX3qSAGPONNa3XZk8Z/K3d0As8ui10/Hbv0ohsEsphxSy0xRoxdyj58/hGxOwj1TZsBezMp+TuPPrg==", 1398 | "bundleDependencies": [ 1399 | "@balena/dockerignore", 1400 | "case", 1401 | "fs-extra", 1402 | "ignore", 1403 | "jsonschema", 1404 | "minimatch", 1405 | "punycode", 1406 | "semver", 1407 | "table", 1408 | "yaml", 1409 | "mime-types" 1410 | ], 1411 | "license": "Apache-2.0", 1412 | "dependencies": { 1413 | "@aws-cdk/asset-awscli-v1": "2.2.237", 1414 | "@aws-cdk/asset-node-proxy-agent-v6": "^2.1.0", 1415 | "@aws-cdk/cloud-assembly-schema": "^44.1.0", 1416 | "@balena/dockerignore": "^1.0.2", 1417 | "case": "1.6.3", 1418 | "fs-extra": "^11.3.0", 1419 | "ignore": "^5.3.2", 1420 | "jsonschema": "^1.5.0", 1421 | "mime-types": "^2.1.35", 1422 | "minimatch": "^3.1.2", 1423 | "punycode": "^2.3.1", 1424 | "semver": "^7.7.2", 1425 | "table": "^6.9.0", 1426 | "yaml": "1.10.2" 1427 | }, 1428 | "engines": { 1429 | "node": ">= 14.15.0" 1430 | }, 1431 | "peerDependencies": { 1432 | "constructs": "^10.0.0" 1433 | } 1434 | }, 1435 | "node_modules/aws-cdk-lib/node_modules/@balena/dockerignore": { 1436 | "version": "1.0.2", 1437 | "inBundle": true, 1438 | "license": "Apache-2.0" 1439 | }, 1440 | "node_modules/aws-cdk-lib/node_modules/ajv": { 1441 | "version": "8.17.1", 1442 | "inBundle": true, 1443 | "license": "MIT", 1444 | "dependencies": { 1445 | "fast-deep-equal": "^3.1.3", 1446 | "fast-uri": "^3.0.1", 1447 | "json-schema-traverse": "^1.0.0", 1448 | "require-from-string": "^2.0.2" 1449 | }, 1450 | "funding": { 1451 | "type": "github", 1452 | "url": "https://github.com/sponsors/epoberezkin" 1453 | } 1454 | }, 1455 | "node_modules/aws-cdk-lib/node_modules/ansi-regex": { 1456 | "version": "5.0.1", 1457 | "inBundle": true, 1458 | "license": "MIT", 1459 | "engines": { 1460 | "node": ">=8" 1461 | } 1462 | }, 1463 | "node_modules/aws-cdk-lib/node_modules/ansi-styles": { 1464 | "version": "4.3.0", 1465 | "inBundle": true, 1466 | "license": "MIT", 1467 | "dependencies": { 1468 | "color-convert": "^2.0.1" 1469 | }, 1470 | "engines": { 1471 | "node": ">=8" 1472 | }, 1473 | "funding": { 1474 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 1475 | } 1476 | }, 1477 | "node_modules/aws-cdk-lib/node_modules/astral-regex": { 1478 | "version": "2.0.0", 1479 | "inBundle": true, 1480 | "license": "MIT", 1481 | "engines": { 1482 | "node": ">=8" 1483 | } 1484 | }, 1485 | "node_modules/aws-cdk-lib/node_modules/balanced-match": { 1486 | "version": "1.0.2", 1487 | "inBundle": true, 1488 | "license": "MIT" 1489 | }, 1490 | "node_modules/aws-cdk-lib/node_modules/brace-expansion": { 1491 | "version": "1.1.11", 1492 | "inBundle": true, 1493 | "license": "MIT", 1494 | "dependencies": { 1495 | "balanced-match": "^1.0.0", 1496 | "concat-map": "0.0.1" 1497 | } 1498 | }, 1499 | "node_modules/aws-cdk-lib/node_modules/case": { 1500 | "version": "1.6.3", 1501 | "inBundle": true, 1502 | "license": "(MIT OR GPL-3.0-or-later)", 1503 | "engines": { 1504 | "node": ">= 0.8.0" 1505 | } 1506 | }, 1507 | "node_modules/aws-cdk-lib/node_modules/color-convert": { 1508 | "version": "2.0.1", 1509 | "inBundle": true, 1510 | "license": "MIT", 1511 | "dependencies": { 1512 | "color-name": "~1.1.4" 1513 | }, 1514 | "engines": { 1515 | "node": ">=7.0.0" 1516 | } 1517 | }, 1518 | "node_modules/aws-cdk-lib/node_modules/color-name": { 1519 | "version": "1.1.4", 1520 | "inBundle": true, 1521 | "license": "MIT" 1522 | }, 1523 | "node_modules/aws-cdk-lib/node_modules/concat-map": { 1524 | "version": "0.0.1", 1525 | "inBundle": true, 1526 | "license": "MIT" 1527 | }, 1528 | "node_modules/aws-cdk-lib/node_modules/emoji-regex": { 1529 | "version": "8.0.0", 1530 | "inBundle": true, 1531 | "license": "MIT" 1532 | }, 1533 | "node_modules/aws-cdk-lib/node_modules/fast-deep-equal": { 1534 | "version": "3.1.3", 1535 | "inBundle": true, 1536 | "license": "MIT" 1537 | }, 1538 | "node_modules/aws-cdk-lib/node_modules/fast-uri": { 1539 | "version": "3.0.6", 1540 | "funding": [ 1541 | { 1542 | "type": "github", 1543 | "url": "https://github.com/sponsors/fastify" 1544 | }, 1545 | { 1546 | "type": "opencollective", 1547 | "url": "https://opencollective.com/fastify" 1548 | } 1549 | ], 1550 | "inBundle": true, 1551 | "license": "BSD-3-Clause" 1552 | }, 1553 | "node_modules/aws-cdk-lib/node_modules/fs-extra": { 1554 | "version": "11.3.0", 1555 | "inBundle": true, 1556 | "license": "MIT", 1557 | "dependencies": { 1558 | "graceful-fs": "^4.2.0", 1559 | "jsonfile": "^6.0.1", 1560 | "universalify": "^2.0.0" 1561 | }, 1562 | "engines": { 1563 | "node": ">=14.14" 1564 | } 1565 | }, 1566 | "node_modules/aws-cdk-lib/node_modules/graceful-fs": { 1567 | "version": "4.2.11", 1568 | "inBundle": true, 1569 | "license": "ISC" 1570 | }, 1571 | "node_modules/aws-cdk-lib/node_modules/ignore": { 1572 | "version": "5.3.2", 1573 | "inBundle": true, 1574 | "license": "MIT", 1575 | "engines": { 1576 | "node": ">= 4" 1577 | } 1578 | }, 1579 | "node_modules/aws-cdk-lib/node_modules/is-fullwidth-code-point": { 1580 | "version": "3.0.0", 1581 | "inBundle": true, 1582 | "license": "MIT", 1583 | "engines": { 1584 | "node": ">=8" 1585 | } 1586 | }, 1587 | "node_modules/aws-cdk-lib/node_modules/json-schema-traverse": { 1588 | "version": "1.0.0", 1589 | "inBundle": true, 1590 | "license": "MIT" 1591 | }, 1592 | "node_modules/aws-cdk-lib/node_modules/jsonfile": { 1593 | "version": "6.1.0", 1594 | "inBundle": true, 1595 | "license": "MIT", 1596 | "dependencies": { 1597 | "universalify": "^2.0.0" 1598 | }, 1599 | "optionalDependencies": { 1600 | "graceful-fs": "^4.1.6" 1601 | } 1602 | }, 1603 | "node_modules/aws-cdk-lib/node_modules/jsonschema": { 1604 | "version": "1.5.0", 1605 | "inBundle": true, 1606 | "license": "MIT", 1607 | "engines": { 1608 | "node": "*" 1609 | } 1610 | }, 1611 | "node_modules/aws-cdk-lib/node_modules/lodash.truncate": { 1612 | "version": "4.4.2", 1613 | "inBundle": true, 1614 | "license": "MIT" 1615 | }, 1616 | "node_modules/aws-cdk-lib/node_modules/mime-db": { 1617 | "version": "1.52.0", 1618 | "inBundle": true, 1619 | "license": "MIT", 1620 | "engines": { 1621 | "node": ">= 0.6" 1622 | } 1623 | }, 1624 | "node_modules/aws-cdk-lib/node_modules/mime-types": { 1625 | "version": "2.1.35", 1626 | "inBundle": true, 1627 | "license": "MIT", 1628 | "dependencies": { 1629 | "mime-db": "1.52.0" 1630 | }, 1631 | "engines": { 1632 | "node": ">= 0.6" 1633 | } 1634 | }, 1635 | "node_modules/aws-cdk-lib/node_modules/minimatch": { 1636 | "version": "3.1.2", 1637 | "inBundle": true, 1638 | "license": "ISC", 1639 | "dependencies": { 1640 | "brace-expansion": "^1.1.7" 1641 | }, 1642 | "engines": { 1643 | "node": "*" 1644 | } 1645 | }, 1646 | "node_modules/aws-cdk-lib/node_modules/punycode": { 1647 | "version": "2.3.1", 1648 | "inBundle": true, 1649 | "license": "MIT", 1650 | "engines": { 1651 | "node": ">=6" 1652 | } 1653 | }, 1654 | "node_modules/aws-cdk-lib/node_modules/require-from-string": { 1655 | "version": "2.0.2", 1656 | "inBundle": true, 1657 | "license": "MIT", 1658 | "engines": { 1659 | "node": ">=0.10.0" 1660 | } 1661 | }, 1662 | "node_modules/aws-cdk-lib/node_modules/semver": { 1663 | "version": "7.7.2", 1664 | "inBundle": true, 1665 | "license": "ISC", 1666 | "bin": { 1667 | "semver": "bin/semver.js" 1668 | }, 1669 | "engines": { 1670 | "node": ">=10" 1671 | } 1672 | }, 1673 | "node_modules/aws-cdk-lib/node_modules/slice-ansi": { 1674 | "version": "4.0.0", 1675 | "inBundle": true, 1676 | "license": "MIT", 1677 | "dependencies": { 1678 | "ansi-styles": "^4.0.0", 1679 | "astral-regex": "^2.0.0", 1680 | "is-fullwidth-code-point": "^3.0.0" 1681 | }, 1682 | "engines": { 1683 | "node": ">=10" 1684 | }, 1685 | "funding": { 1686 | "url": "https://github.com/chalk/slice-ansi?sponsor=1" 1687 | } 1688 | }, 1689 | "node_modules/aws-cdk-lib/node_modules/string-width": { 1690 | "version": "4.2.3", 1691 | "inBundle": true, 1692 | "license": "MIT", 1693 | "dependencies": { 1694 | "emoji-regex": "^8.0.0", 1695 | "is-fullwidth-code-point": "^3.0.0", 1696 | "strip-ansi": "^6.0.1" 1697 | }, 1698 | "engines": { 1699 | "node": ">=8" 1700 | } 1701 | }, 1702 | "node_modules/aws-cdk-lib/node_modules/strip-ansi": { 1703 | "version": "6.0.1", 1704 | "inBundle": true, 1705 | "license": "MIT", 1706 | "dependencies": { 1707 | "ansi-regex": "^5.0.1" 1708 | }, 1709 | "engines": { 1710 | "node": ">=8" 1711 | } 1712 | }, 1713 | "node_modules/aws-cdk-lib/node_modules/table": { 1714 | "version": "6.9.0", 1715 | "inBundle": true, 1716 | "license": "BSD-3-Clause", 1717 | "dependencies": { 1718 | "ajv": "^8.0.1", 1719 | "lodash.truncate": "^4.4.2", 1720 | "slice-ansi": "^4.0.0", 1721 | "string-width": "^4.2.3", 1722 | "strip-ansi": "^6.0.1" 1723 | }, 1724 | "engines": { 1725 | "node": ">=10.0.0" 1726 | } 1727 | }, 1728 | "node_modules/aws-cdk-lib/node_modules/universalify": { 1729 | "version": "2.0.1", 1730 | "inBundle": true, 1731 | "license": "MIT", 1732 | "engines": { 1733 | "node": ">= 10.0.0" 1734 | } 1735 | }, 1736 | "node_modules/aws-cdk-lib/node_modules/yaml": { 1737 | "version": "1.10.2", 1738 | "inBundle": true, 1739 | "license": "ISC", 1740 | "engines": { 1741 | "node": ">= 6" 1742 | } 1743 | }, 1744 | "node_modules/bowser": { 1745 | "version": "2.11.0", 1746 | "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", 1747 | "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", 1748 | "license": "MIT" 1749 | }, 1750 | "node_modules/cdk-ssm-document": { 1751 | "resolved": "..", 1752 | "link": true 1753 | }, 1754 | "node_modules/constructs": { 1755 | "version": "10.3.0", 1756 | "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.3.0.tgz", 1757 | "integrity": "sha512-vbK8i3rIb/xwZxSpTjz3SagHn1qq9BChLEfy5Hf6fB3/2eFbrwt2n9kHwQcS0CPTRBesreeAcsJfMq2229FnbQ==", 1758 | "peer": true, 1759 | "engines": { 1760 | "node": ">= 16.14.0" 1761 | } 1762 | }, 1763 | "node_modules/create-require": { 1764 | "version": "1.1.1", 1765 | "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", 1766 | "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" 1767 | }, 1768 | "node_modules/diff": { 1769 | "version": "4.0.2", 1770 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 1771 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 1772 | "engines": { 1773 | "node": ">=0.3.1" 1774 | } 1775 | }, 1776 | "node_modules/fast-xml-parser": { 1777 | "version": "4.4.1", 1778 | "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", 1779 | "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", 1780 | "funding": [ 1781 | { 1782 | "type": "github", 1783 | "url": "https://github.com/sponsors/NaturalIntelligence" 1784 | }, 1785 | { 1786 | "type": "paypal", 1787 | "url": "https://paypal.me/naturalintelligence" 1788 | } 1789 | ], 1790 | "license": "MIT", 1791 | "dependencies": { 1792 | "strnum": "^1.0.5" 1793 | }, 1794 | "bin": { 1795 | "fxparser": "src/cli/cli.js" 1796 | } 1797 | }, 1798 | "node_modules/fsevents": { 1799 | "version": "2.3.2", 1800 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 1801 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 1802 | "hasInstallScript": true, 1803 | "optional": true, 1804 | "os": [ 1805 | "darwin" 1806 | ], 1807 | "engines": { 1808 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 1809 | } 1810 | }, 1811 | "node_modules/make-error": { 1812 | "version": "1.3.6", 1813 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 1814 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" 1815 | }, 1816 | "node_modules/strnum": { 1817 | "version": "1.0.5", 1818 | "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", 1819 | "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", 1820 | "license": "MIT" 1821 | }, 1822 | "node_modules/ts-node": { 1823 | "version": "10.9.2", 1824 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", 1825 | "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", 1826 | "dependencies": { 1827 | "@cspotcode/source-map-support": "^0.8.0", 1828 | "@tsconfig/node10": "^1.0.7", 1829 | "@tsconfig/node12": "^1.0.7", 1830 | "@tsconfig/node14": "^1.0.0", 1831 | "@tsconfig/node16": "^1.0.2", 1832 | "acorn": "^8.4.1", 1833 | "acorn-walk": "^8.1.1", 1834 | "arg": "^4.1.0", 1835 | "create-require": "^1.1.0", 1836 | "diff": "^4.0.1", 1837 | "make-error": "^1.1.1", 1838 | "v8-compile-cache-lib": "^3.0.1", 1839 | "yn": "3.1.1" 1840 | }, 1841 | "bin": { 1842 | "ts-node": "dist/bin.js", 1843 | "ts-node-cwd": "dist/bin-cwd.js", 1844 | "ts-node-esm": "dist/bin-esm.js", 1845 | "ts-node-script": "dist/bin-script.js", 1846 | "ts-node-transpile-only": "dist/bin-transpile.js", 1847 | "ts-script": "dist/bin-script-deprecated.js" 1848 | }, 1849 | "peerDependencies": { 1850 | "@swc/core": ">=1.2.50", 1851 | "@swc/wasm": ">=1.2.50", 1852 | "@types/node": "*", 1853 | "typescript": ">=2.7" 1854 | }, 1855 | "peerDependenciesMeta": { 1856 | "@swc/core": { 1857 | "optional": true 1858 | }, 1859 | "@swc/wasm": { 1860 | "optional": true 1861 | } 1862 | } 1863 | }, 1864 | "node_modules/tslib": { 1865 | "version": "2.6.2", 1866 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", 1867 | "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" 1868 | }, 1869 | "node_modules/typescript": { 1870 | "version": "5.6.3", 1871 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", 1872 | "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", 1873 | "license": "Apache-2.0", 1874 | "bin": { 1875 | "tsc": "bin/tsc", 1876 | "tsserver": "bin/tsserver" 1877 | }, 1878 | "engines": { 1879 | "node": ">=14.17" 1880 | } 1881 | }, 1882 | "node_modules/undici-types": { 1883 | "version": "5.25.3", 1884 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.25.3.tgz", 1885 | "integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==", 1886 | "peer": true 1887 | }, 1888 | "node_modules/uuid": { 1889 | "version": "9.0.1", 1890 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", 1891 | "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", 1892 | "funding": [ 1893 | "https://github.com/sponsors/broofa", 1894 | "https://github.com/sponsors/ctavan" 1895 | ], 1896 | "license": "MIT", 1897 | "bin": { 1898 | "uuid": "dist/bin/uuid" 1899 | } 1900 | }, 1901 | "node_modules/v8-compile-cache-lib": { 1902 | "version": "3.0.1", 1903 | "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", 1904 | "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" 1905 | }, 1906 | "node_modules/yn": { 1907 | "version": "3.1.1", 1908 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", 1909 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", 1910 | "engines": { 1911 | "node": ">=6" 1912 | } 1913 | } 1914 | } 1915 | } 1916 | -------------------------------------------------------------------------------- /test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "version": "0.0.0", 4 | "bin": { 5 | "test": "bin/test.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "cdk": "cdk" 10 | }, 11 | "dependencies": { 12 | "aws-cdk": "^2.0.0", 13 | "ts-node": "^10.4.0", 14 | "typescript": "^5.2.2", 15 | "@aws-sdk/client-sts": "^3.427.0", 16 | "aws-cdk-lib": "2.x", 17 | "cdk-ssm-document": "file:.." 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": ["es2018"], 6 | "declaration": true, 7 | "strict": true, 8 | "noImplicitAny": true, 9 | "strictNullChecks": true, 10 | "noImplicitThis": true, 11 | "alwaysStrict": true, 12 | "noUnusedLocals": false, 13 | "noUnusedParameters": false, 14 | "noImplicitReturns": true, 15 | "noFallthroughCasesInSwitch": false, 16 | "inlineSourceMap": true, 17 | "inlineSources": true, 18 | "experimentalDecorators": true, 19 | "strictPropertyInitialization": false, 20 | "typeRoots": ["./node_modules/@types"], 21 | "types": ["node"] 22 | }, 23 | "exclude": ["cdk.out"], 24 | "ts-node": { 25 | "swc": true 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tsconfig-lint.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "alwaysStrict": true, 4 | "declaration": true, 5 | "experimentalDecorators": true, 6 | "inlineSourceMap": true, 7 | "inlineSources": true, 8 | "lib": ["es2018"], 9 | "module": "CommonJS", 10 | "noEmitOnError": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "noImplicitAny": true, 13 | "noImplicitReturns": true, 14 | "noImplicitThis": true, 15 | "noUnusedLocals": true, 16 | "noUnusedParameters": true, 17 | "resolveJsonModule": true, 18 | "strict": true, 19 | "strictNullChecks": true, 20 | "strictPropertyInitialization": true, 21 | "stripInternal": true, 22 | "target": "ES2018" 23 | }, 24 | "include": ["**/*.ts"], 25 | "exclude": ["node_modules"] 26 | } 27 | --------------------------------------------------------------------------------