From a450604c13d97a6dae79f2b2b9b12c1335fc2418 Mon Sep 17 00:00:00 2001 From: Minhyeok Park Date: Mon, 23 Dec 2024 21:52:08 +0900 Subject: [PATCH] feat: add many bps --- .gitignore | 2 + build.sh | 6 + package.json | 33 + pnpm-lock.yaml | 2678 +++++++++++++++++ script/build.js | 9 + sea-config.json | 5 + src/Memorizer.ts | 38 + src/WebServer.ts | 17 + src/bpsets/BPSet.ts | 8 + .../alb/ALBHttpDropInvalidHeaderEnabled.ts | 72 + src/bpsets/alb/ALBWAFEnabled.ts | 62 + .../alb/ELBCrossZoneLoadBalancingEnabled.ts | 64 + .../alb/ELBDeletionProtectionEnabled.ts | 64 + src/bpsets/alb/ELBLoggingEnabled.ts | 75 + .../apigw/APIGatewayAssociatedWithWAF.ts | 70 + .../APIGatewayExecutionLoggingEnabled.ts | 75 + .../apigw/APIGatewayV2AccessLogsEnabled.ts | 76 + ...APIGatewayV2AuthorizationTypeConfigured.ts | 78 + .../AutoScalingGroupELBHealthCheckRequired.ts | 48 + src/bpsets/asg/AutoScalingLaunchTemplate.ts | 58 + src/bpsets/asg/AutoScalingMultipleAZ.ts | 54 + .../cloudfront/CloudFrontAccessLogsEnabled.ts | 85 + .../cloudfront/CloudFrontAssociatedWithWAF.ts | 77 + .../CloudFrontDefaultRootObjectConfigured.ts | 78 + .../CloudFrontNoDeprecatedSSLProtocols.ts | 93 + .../CloudFrontS3OriginAccessControlEnabled.ts | 96 + .../cloudfront/CloudFrontViewerPolicyHTTPS.ts | 84 + src/bpsets/lambda/LambdaDLQCheck.ts | 58 + .../LambdaFunctionPublicAccessProhibited.ts | 86 + .../lambda/LambdaFunctionSettingsCheck.ts | 65 + src/bpsets/lambda/LambdaInsideVPC.ts | 69 + src/bpsets/s3/S3AccessPointInVpcOnly.ts | 81 + src/bpsets/s3/S3BucketDefaultLockEnabled.ts | 72 + .../s3/S3BucketLevelPublicAccessProhibited.ts | 75 + src/bpsets/s3/S3BucketLoggingEnabled.ts | 73 + src/bpsets/s3/S3BucketSSLRequestsOnly.ts | 130 + src/bpsets/s3/S3BucketVersioningEnabled.ts | 62 + src/bpsets/s3/S3DefaultEncryptionKMS.ts | 90 + src/bpsets/s3/S3EventNotificationsEnabled.ts | 87 + .../s3/S3LastBackupRecoveryPointCreated.ts | 52 + src/bpsets/s3/S3LifecyclePolicyCheck.ts | 90 + src/main.ts | 9 + tsconfig.json | 14 + 43 files changed, 5218 insertions(+) create mode 100644 .gitignore create mode 100755 build.sh create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 script/build.js create mode 100644 sea-config.json create mode 100644 src/Memorizer.ts create mode 100644 src/WebServer.ts create mode 100644 src/bpsets/BPSet.ts create mode 100644 src/bpsets/alb/ALBHttpDropInvalidHeaderEnabled.ts create mode 100644 src/bpsets/alb/ALBWAFEnabled.ts create mode 100644 src/bpsets/alb/ELBCrossZoneLoadBalancingEnabled.ts create mode 100644 src/bpsets/alb/ELBDeletionProtectionEnabled.ts create mode 100644 src/bpsets/alb/ELBLoggingEnabled.ts create mode 100644 src/bpsets/apigw/APIGatewayAssociatedWithWAF.ts create mode 100644 src/bpsets/apigw/APIGatewayExecutionLoggingEnabled.ts create mode 100644 src/bpsets/apigw/APIGatewayV2AccessLogsEnabled.ts create mode 100644 src/bpsets/apigw/APIGatewayV2AuthorizationTypeConfigured.ts create mode 100644 src/bpsets/asg/AutoScalingGroupELBHealthCheckRequired.ts create mode 100644 src/bpsets/asg/AutoScalingLaunchTemplate.ts create mode 100644 src/bpsets/asg/AutoScalingMultipleAZ.ts create mode 100644 src/bpsets/cloudfront/CloudFrontAccessLogsEnabled.ts create mode 100644 src/bpsets/cloudfront/CloudFrontAssociatedWithWAF.ts create mode 100644 src/bpsets/cloudfront/CloudFrontDefaultRootObjectConfigured.ts create mode 100644 src/bpsets/cloudfront/CloudFrontNoDeprecatedSSLProtocols.ts create mode 100644 src/bpsets/cloudfront/CloudFrontS3OriginAccessControlEnabled.ts create mode 100644 src/bpsets/cloudfront/CloudFrontViewerPolicyHTTPS.ts create mode 100644 src/bpsets/lambda/LambdaDLQCheck.ts create mode 100644 src/bpsets/lambda/LambdaFunctionPublicAccessProhibited.ts create mode 100644 src/bpsets/lambda/LambdaFunctionSettingsCheck.ts create mode 100644 src/bpsets/lambda/LambdaInsideVPC.ts create mode 100644 src/bpsets/s3/S3AccessPointInVpcOnly.ts create mode 100644 src/bpsets/s3/S3BucketDefaultLockEnabled.ts create mode 100644 src/bpsets/s3/S3BucketLevelPublicAccessProhibited.ts create mode 100644 src/bpsets/s3/S3BucketLoggingEnabled.ts create mode 100644 src/bpsets/s3/S3BucketSSLRequestsOnly.ts create mode 100644 src/bpsets/s3/S3BucketVersioningEnabled.ts create mode 100644 src/bpsets/s3/S3DefaultEncryptionKMS.ts create mode 100644 src/bpsets/s3/S3EventNotificationsEnabled.ts create mode 100644 src/bpsets/s3/S3LastBackupRecoveryPointCreated.ts create mode 100644 src/bpsets/s3/S3LifecyclePolicyCheck.ts create mode 100644 src/main.ts create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b947077 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +dist/ diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..249af03 --- /dev/null +++ b/build.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +cp $(which node) dist/bpsets +node script/build.js +node --experimental-sea-config sea-config.json +pnpx postject dist/bpsets NODE_SEA_BLOB dist/sea-prep.blob --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 --overwrite diff --git a/package.json b/package.json new file mode 100644 index 0000000..85c68cc --- /dev/null +++ b/package.json @@ -0,0 +1,33 @@ +{ + "name": "bpsets", + "version": "0.1.0", + "main": "build/main.js", + "scripts": { + "build": "./build.sh", + "start": "./build.sh && ./dist/bpsets" + }, + "author": "Minhyeok Park", + "license": "MIT", + "dependencies": { + "@aws-sdk/client-apigatewayv2": "^3.716.0", + "@aws-sdk/client-auto-scaling": "^3.716.0", + "@aws-sdk/client-backup": "^3.716.0", + "@aws-sdk/client-cloudfront": "^3.716.0", + "@aws-sdk/client-elastic-load-balancing-v2": "^3.716.0", + "@aws-sdk/client-lambda": "^3.716.0", + "@aws-sdk/client-s3": "^3.717.0", + "@aws-sdk/client-s3-control": "^3.716.0", + "@aws-sdk/client-sts": "^3.716.0", + "@aws-sdk/client-wafv2": "^3.716.0", + "@smithy/smithy-client": "^3.5.1", + "express": "^4.21.2", + "sha.js": "^2.4.11" + }, + "devDependencies": { + "@types/express": "^5.0.0", + "@types/node": "^22.10.2", + "@types/sha.js": "^2.4.4", + "esbuild": "^0.24.2", + "typescript": "^5.7.2" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..aea00c9 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,2678 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@aws-sdk/client-apigatewayv2': + specifier: ^3.716.0 + version: 3.716.0 + '@aws-sdk/client-auto-scaling': + specifier: ^3.716.0 + version: 3.716.0 + '@aws-sdk/client-backup': + specifier: ^3.716.0 + version: 3.716.0 + '@aws-sdk/client-cloudfront': + specifier: ^3.716.0 + version: 3.716.0 + '@aws-sdk/client-elastic-load-balancing-v2': + specifier: ^3.716.0 + version: 3.716.0 + '@aws-sdk/client-lambda': + specifier: ^3.716.0 + version: 3.716.0 + '@aws-sdk/client-s3': + specifier: ^3.717.0 + version: 3.717.0 + '@aws-sdk/client-s3-control': + specifier: ^3.716.0 + version: 3.716.0 + '@aws-sdk/client-sts': + specifier: ^3.716.0 + version: 3.716.0 + '@aws-sdk/client-wafv2': + specifier: ^3.716.0 + version: 3.716.0 + '@smithy/smithy-client': + specifier: ^3.5.1 + version: 3.5.1 + express: + specifier: ^4.21.2 + version: 4.21.2 + sha.js: + specifier: ^2.4.11 + version: 2.4.11 + devDependencies: + '@types/express': + specifier: ^5.0.0 + version: 5.0.0 + '@types/node': + specifier: ^22.10.2 + version: 22.10.2 + '@types/sha.js': + specifier: ^2.4.4 + version: 2.4.4 + esbuild: + specifier: ^0.24.2 + version: 0.24.2 + typescript: + specifier: ^5.7.2 + version: 5.7.2 + +packages: + + '@aws-crypto/crc32@5.2.0': + resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/crc32c@5.2.0': + resolution: {integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==} + + '@aws-crypto/sha1-browser@5.2.0': + resolution: {integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==} + + '@aws-crypto/sha256-browser@5.2.0': + resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} + + '@aws-crypto/sha256-js@5.2.0': + resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/supports-web-crypto@5.2.0': + resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} + + '@aws-crypto/util@5.2.0': + resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} + + '@aws-sdk/client-apigatewayv2@3.716.0': + resolution: {integrity: sha512-dv/EgqOh7iAmazzMwcafa8Vs+5lF+MESloBT4RD1TqmHBcxiClxTU6hm9xr+juwOrEApPABEne1L/TSOZwbPLA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-auto-scaling@3.716.0': + resolution: {integrity: sha512-L59pJKPfhAdWZoV4Qks8UjeFw+vMVq/QqDMEhszAgvcL8zMpAiLhB28qey20gxOQhD0ZiBDrrSGzJWc2elf3Fw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-backup@3.716.0': + resolution: {integrity: sha512-Bb3H/A6owfQdJF/wMV6e2fDKVRg+nsRU8fW516ISTABDiDzPlKPm74kh5qAfiPqpRIQJ8Ozxykz/p40DLkHJzw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-cloudfront@3.716.0': + resolution: {integrity: sha512-r9m+gxgjh/Bt+W3z+F8iR9W0TwBCJQIHt9gjyfIzWa2tLFGzrAajFfs5RUrWpFyl+e8Q6wukAXXm9QhQDwTOsQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-elastic-load-balancing-v2@3.716.0': + resolution: {integrity: sha512-bNj4trUbW17K+/UJEqxoqIDkTSjF3ihvgSon6cKH7hCEiVnYFWop4caJwll2DzyylUIF9AchqOyMEkb4ZBQdQw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-lambda@3.716.0': + resolution: {integrity: sha512-pWcnLQFbG4/xmzq7614YBz6Kbx3Skm3yuQOhac9y163hSmey1bfFdr1bZ+FcxquHbRb5KNvhU1RGmwmNVxsOfg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-s3-control@3.716.0': + resolution: {integrity: sha512-xArSyh24V6EjizTOwcZb60fDTjJaaQOOLckbOwDuQ6MJIc31lq0NOoePyB2VmmdHDwo5Dr8rljTLIkwWZ08QPA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-s3@3.717.0': + resolution: {integrity: sha512-jzaH8IskAXVnqlZ3/H/ROwrB2HCnq/atlN7Hi7FIfjWvMPf5nfcJKfzJ1MXFX0EQR5qO6X4TbK7rgi7Bjw9NjQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sso-oidc@3.716.0': + resolution: {integrity: sha512-lA4IB9FzR2KjH7EVCo+mHGFKqdViVyeBQEIX9oVratL/l7P0bMS1fMwgfHOc3ACazqNxBxDES7x08ZCp32y6Lw==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.716.0 + + '@aws-sdk/client-sso@3.716.0': + resolution: {integrity: sha512-5Nb0jJXce2TclbjG7WVPufwhgV1TRydz1QnsuBtKU0AdViEpr787YrZhPpGnNIM1Dx+R1H/tmAHZnOoohS6D8g==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sts@3.716.0': + resolution: {integrity: sha512-i4SVNsrdXudp8T4bkm7Fi3YWlRnvXCSwvNDqf6nLqSJxqr4CN3VlBELueDyjBK7TAt453/qSif+eNx+bHmwo4Q==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-wafv2@3.716.0': + resolution: {integrity: sha512-a4C8M0KwuTw8qk7GMsoKKW8cQr7yPXGCGG7o2ECURAzSoKt4b9oGo2gshA5ffW5FAdWUQ9V7LpradAsW0a7pmg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/core@3.716.0': + resolution: {integrity: sha512-5DkUiTrbyzO8/W4g7UFEqRFpuhgizayHI/Zbh0wtFMcot8801nJV+MP/YMhdjimlvAr/OqYB08FbGsPyWppMTw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-env@3.716.0': + resolution: {integrity: sha512-JI2KQUnn2arICwP9F3CnqP1W3nAbm4+meQg/yOhp9X0DMzQiHrHRd4HIrK2vyVgi2/6hGhONY5uLF26yRTA7nQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-http@3.716.0': + resolution: {integrity: sha512-CZ04pl2z7igQPysQyH2xKZHM3fLwkemxQbKOlje3TmiS1NwXvcKvERhp9PE/H23kOL7beTM19NMRog/Fka/rlw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-ini@3.716.0': + resolution: {integrity: sha512-P37We2GtZvdROxiwP0zrpEL81/HuYK1qlYxp5VCj3uV+G4mG8UQN2gMIU/baYrpOQqa0h81RfyQGRFUjVaDVqw==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.716.0 + + '@aws-sdk/credential-provider-node@3.716.0': + resolution: {integrity: sha512-FGQPK2uKfS53dVvoskN/s/t6m0Po24BGd1PzJdzHBFCOjxbZLM6+8mDMXeyi2hCLVVQOUcuW41kOgmJ0+zMbww==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-process@3.716.0': + resolution: {integrity: sha512-0spcu2MWVVHSTHH3WE2E//ttUJPwXRM3BCp+WyI41xLzpNu1Fd8zjOrDpEo0SnGUzsSiRTIJWgkuu/tqv9NJ2A==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-sso@3.716.0': + resolution: {integrity: sha512-J2IA3WuCpRGGoZm6VHZVFCnrxXP+41iUWb9Ct/1spljegTa1XjiaZ5Jf3+Ubj7WKiyvP9/dgz1L0bu2bYEjliw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.716.0': + resolution: {integrity: sha512-vzgpWKs2gGXZGdbMKRFrMW4PqEFWkGvwWH2T7ZwQv9m+8lQ7P4Dk2uimqu0f37HZAbpn8HFMqRh4CaySjU354A==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.716.0 + + '@aws-sdk/middleware-bucket-endpoint@3.714.0': + resolution: {integrity: sha512-I/xSOskiseJJ8i183Z522BgqbgYzLKP7jGcg2Qeib/IWoG2IP+9DH8pwqagKaPAycyswtnoKBJiiFXY43n0CkA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-expect-continue@3.714.0': + resolution: {integrity: sha512-rlzsXdG8Lzo4Qpl35ZnpOBAWlzvDHpP9++0AXoUwAJA0QmMm7auIRmgxJuNj91VwT9h15ZU6xjU4S7fJl4W0+w==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-flexible-checksums@3.717.0': + resolution: {integrity: sha512-a5kY5r7/7bDZZlOQQGWOR1ulQewdtNexdW1Ex5DD0FLKlFY7RD0va24hxQ6BP7mWHol+Dx4pj6UQ8ahk0ap1tw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-host-header@3.714.0': + resolution: {integrity: sha512-6l68kjNrh5QC8FGX3I3geBDavWN5Tg1RLHJ2HLA8ByGBtJyCwnz3hEkKfaxn0bBx0hF9DzbfjEOUF6cDqy2Kjg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-location-constraint@3.714.0': + resolution: {integrity: sha512-MX7M+V+FblujKck3fyuzePVIAy9530gY719IiSxV6uN1qLHl7VDJxNblpF/KpXakD6rOg8OpvtmqsXj9aBMftw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-logger@3.714.0': + resolution: {integrity: sha512-RkqHlMvQWUaRklU1bMfUuBvdWwxgUtEqpADaHXlGVj3vtEY2UgBjy+57CveC4MByqKIunNvVHBBbjrGVtwY7Lg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-recursion-detection@3.714.0': + resolution: {integrity: sha512-AVU5ixnh93nqtsfgNc284oXsXaadyHGPHpql/jwgaaqQfEXjS/1/j3j9E/vpacfTTz2Vzo7hAOjnvrOXSEVDaA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-sdk-s3-control@3.714.0': + resolution: {integrity: sha512-OvPVrg0gPEi1CZObau/VRULyBZmU3BKcEVzJXZ5LlG/ApO4hmisnigfcnCh7HEq5OWl2NfQoSyc6hymOICFdyw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-sdk-s3@3.716.0': + resolution: {integrity: sha512-Qzz5OfRA/5brqfvq+JHTInwS1EuJ1+tC6qMtwKWJN3czMnVJVdnnsPTf+G5IM/1yYaGEIjY8rC1ExQLcc8ApFQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-ssec@3.714.0': + resolution: {integrity: sha512-RkK8REAVwNUQmYbIDRw8eYbMJ8F1Rw4C9mlME4BBMhFlelGcD3ErU2ce24moQbDxBjNwHNESmIqgmdQk93CDCQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-user-agent@3.716.0': + resolution: {integrity: sha512-FpAtT6nNKrYdkDZndutEraiRMf+TgDzAGvniqRtZ/YTPA+gIsWrsn+TwMKINR81lFC3nQfb9deS5CFtxd021Ew==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/region-config-resolver@3.714.0': + resolution: {integrity: sha512-HJzsQxgMOAzZrbf/YIqEx30or4tZK1oNAk6Wm6xecUQx+23JXIaePRu1YFUOLBBERQ4QBPpISFurZWBMZ5ibAw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/signature-v4-multi-region@3.716.0': + resolution: {integrity: sha512-k0goWotZKKz+kV6Ln0qeAMSeSVi4NipuIIz5R8A0uCF2zBK4CXWdZR7KeaIoLBhJwQnHj1UU7E+2MK74KIUBzA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/token-providers@3.714.0': + resolution: {integrity: sha512-vKN064aLE3kl+Zl16Ony3jltHnMddMBT7JRkP1L+lLywhA0PcAKxpdvComul/sTBWnbnwLnaS5NsDUhcWySH8A==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sso-oidc': ^3.714.0 + + '@aws-sdk/types@3.714.0': + resolution: {integrity: sha512-ZjpP2gYbSFlxxaUDa1Il5AVvfggvUPbjzzB/l3q0gIE5Thd6xKW+yzEpt2mLZ5s5UaYSABZbF94g8NUOF4CVGA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-arn-parser@3.693.0': + resolution: {integrity: sha512-WC8x6ca+NRrtpAH64rWu+ryDZI3HuLwlEr8EU6/dbC/pt+r/zC0PBoC15VEygUaBA+isppCikQpGyEDu0Yj7gQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-endpoints@3.714.0': + resolution: {integrity: sha512-Xv+Z2lhe7w7ZZRsgBwBMZgGTVmS+dkkj2S13uNHAx9lhB5ovM8PhK5G/j28xYf6vIibeuHkRAbb7/ozdZIGR+A==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-locate-window@3.693.0': + resolution: {integrity: sha512-ttrag6haJLWABhLqtg1Uf+4LgHWIMOVSYL+VYZmAp2v4PUGOwWmWQH0Zk8RM7YuQcLfH/EoR72/Yxz6A4FKcuw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-user-agent-browser@3.714.0': + resolution: {integrity: sha512-OdJJ03cP9/MgIVToPJPCPUImbpZzTcwdIgbXC0tUQPJhbD7b7cB4LdnkhNHko+MptpOrCq4CPY/33EpOjRdofw==} + + '@aws-sdk/util-user-agent-node@3.716.0': + resolution: {integrity: sha512-3PqaXmQbxrtHKAsPCdp7kn5FrQktj8j3YyuNsqFZ8rWZeEQ88GWlsvE61PTsr2peYCKzpFqYVddef2x1axHU0w==} + engines: {node: '>=16.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + + '@aws-sdk/xml-builder@3.709.0': + resolution: {integrity: sha512-2GPCwlNxeHspoK/Mc8nbk9cBOkSpp3j2SJUQmFnyQK6V/pR6II2oPRyZkMomug1Rc10hqlBHByMecq4zhV2uUw==} + engines: {node: '>=16.0.0'} + + '@esbuild/aix-ppc64@0.24.2': + resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.24.2': + resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.24.2': + resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.24.2': + resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.24.2': + resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.24.2': + resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.24.2': + resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.24.2': + resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.24.2': + resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.24.2': + resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.24.2': + resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.24.2': + resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.24.2': + resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.24.2': + resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.24.2': + resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.24.2': + resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.24.2': + resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.24.2': + resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.24.2': + resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.24.2': + resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.24.2': + resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.24.2': + resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.24.2': + resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.24.2': + resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.24.2': + resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@smithy/abort-controller@3.1.9': + resolution: {integrity: sha512-yiW0WI30zj8ZKoSYNx90no7ugVn3khlyH/z5W8qtKBtVE6awRALbhSG+2SAHA1r6bO/6M9utxYKVZ3PCJ1rWxw==} + engines: {node: '>=16.0.0'} + + '@smithy/chunked-blob-reader-native@3.0.1': + resolution: {integrity: sha512-VEYtPvh5rs/xlyqpm5NRnfYLZn+q0SRPELbvBV+C/G7IQ+ouTuo+NKKa3ShG5OaFR8NYVMXls9hPYLTvIKKDrQ==} + + '@smithy/chunked-blob-reader@4.0.0': + resolution: {integrity: sha512-jSqRnZvkT4egkq/7b6/QRCNXmmYVcHwnJldqJ3IhVpQE2atObVJ137xmGeuGFhjFUr8gCEVAOKwSY79OvpbDaQ==} + + '@smithy/config-resolver@3.0.13': + resolution: {integrity: sha512-Gr/qwzyPaTL1tZcq8WQyHhTZREER5R1Wytmz4WnVGL4onA3dNk6Btll55c8Vr58pLdvWZmtG8oZxJTw3t3q7Jg==} + engines: {node: '>=16.0.0'} + + '@smithy/core@2.5.5': + resolution: {integrity: sha512-G8G/sDDhXA7o0bOvkc7bgai6POuSld/+XhNnWAbpQTpLv2OZPvyqQ58tLPPlz0bSNsXktldDDREIv1LczFeNEw==} + engines: {node: '>=16.0.0'} + + '@smithy/credential-provider-imds@3.2.8': + resolution: {integrity: sha512-ZCY2yD0BY+K9iMXkkbnjo+08T2h8/34oHd0Jmh6BZUSZwaaGlGCyBT/3wnS7u7Xl33/EEfN4B6nQr3Gx5bYxgw==} + engines: {node: '>=16.0.0'} + + '@smithy/eventstream-codec@3.1.10': + resolution: {integrity: sha512-323B8YckSbUH0nMIpXn7HZsAVKHYHFUODa8gG9cHo0ySvA1fr5iWaNT+iIL0UCqUzG6QPHA3BSsBtRQou4mMqQ==} + + '@smithy/eventstream-serde-browser@3.0.14': + resolution: {integrity: sha512-kbrt0vjOIihW3V7Cqj1SXQvAI5BR8SnyQYsandva0AOR307cXAc+IhPngxIPslxTLfxwDpNu0HzCAq6g42kCPg==} + engines: {node: '>=16.0.0'} + + '@smithy/eventstream-serde-config-resolver@3.0.11': + resolution: {integrity: sha512-P2pnEp4n75O+QHjyO7cbw/vsw5l93K/8EWyjNCAAybYwUmj3M+hjSQZ9P5TVdUgEG08ueMAP5R4FkuSkElZ5tQ==} + engines: {node: '>=16.0.0'} + + '@smithy/eventstream-serde-node@3.0.13': + resolution: {integrity: sha512-zqy/9iwbj8Wysmvi7Lq7XFLeDgjRpTbCfwBhJa8WbrylTAHiAu6oQTwdY7iu2lxigbc9YYr9vPv5SzYny5tCXQ==} + engines: {node: '>=16.0.0'} + + '@smithy/eventstream-serde-universal@3.0.13': + resolution: {integrity: sha512-L1Ib66+gg9uTnqp/18Gz4MDpJPKRE44geOjOQ2SVc0eiaO5l255ADziATZgjQjqumC7yPtp1XnjHlF1srcwjKw==} + engines: {node: '>=16.0.0'} + + '@smithy/fetch-http-handler@4.1.2': + resolution: {integrity: sha512-R7rU7Ae3ItU4rC0c5mB2sP5mJNbCfoDc8I5XlYjIZnquyUwec7fEo78F6DA3SmgJgkU1qTMcZJuGblxZsl10ZA==} + + '@smithy/hash-blob-browser@3.1.10': + resolution: {integrity: sha512-elwslXOoNunmfS0fh55jHggyhccobFkexLYC1ZeZ1xP2BTSrcIBaHV2b4xUQOdctrSNOpMqOZH1r2XzWTEhyfA==} + + '@smithy/hash-node@3.0.11': + resolution: {integrity: sha512-emP23rwYyZhQBvklqTtwetkQlqbNYirDiEEwXl2v0GYWMnCzxst7ZaRAnWuy28njp5kAH54lvkdG37MblZzaHA==} + engines: {node: '>=16.0.0'} + + '@smithy/hash-stream-node@3.1.10': + resolution: {integrity: sha512-olomK/jZQ93OMayW1zfTHwcbwBdhcZOHsyWyiZ9h9IXvc1mCD/VuvzbLb3Gy/qNJwI4MANPLctTp2BucV2oU/Q==} + engines: {node: '>=16.0.0'} + + '@smithy/invalid-dependency@3.0.11': + resolution: {integrity: sha512-NuQmVPEJjUX6c+UELyVz8kUx8Q539EDeNwbRyu4IIF8MeV7hUtq1FB3SHVyki2u++5XLMFqngeMKk7ccspnNyQ==} + + '@smithy/is-array-buffer@2.2.0': + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} + + '@smithy/is-array-buffer@3.0.0': + resolution: {integrity: sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==} + engines: {node: '>=16.0.0'} + + '@smithy/md5-js@3.0.11': + resolution: {integrity: sha512-3NM0L3i2Zm4bbgG6Ymi9NBcxXhryi3uE8fIfHJZIOfZVxOkGdjdgjR9A06SFIZCfnEIWKXZdm6Yq5/aPXFFhsQ==} + + '@smithy/middleware-apply-body-checksum@3.0.13': + resolution: {integrity: sha512-iqnbVTl9iSQCmrEY9ZkXgUitgzkY3+KApmb8mMpIAyxLDG2yAQXt8QnXExNy2Z/6uabr7tjuaKySXfDurwcLiw==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-content-length@3.0.13': + resolution: {integrity: sha512-zfMhzojhFpIX3P5ug7jxTjfUcIPcGjcQYzB9t+rv0g1TX7B0QdwONW+ATouaLoD7h7LOw/ZlXfkq4xJ/g2TrIw==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-endpoint@3.2.6': + resolution: {integrity: sha512-WAqzyulvvSKrT5c6VrQelgNVNNO7BlTQW9Z+s9tcG6G5CaBS1YBpPtT3VuhXLQbewSiGi7oXQROwpw26EG9PLQ==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-retry@3.0.31': + resolution: {integrity: sha512-yq9wawrJLYHAYFpChLujxRN4My+SiKXvZk9Ml/CvTdRSA8ew+hvuR5LT+mjSlSBv3c4XJrkN8CWegkBaeD0Vrg==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-serde@3.0.11': + resolution: {integrity: sha512-KzPAeySp/fOoQA82TpnwItvX8BBURecpx6ZMu75EZDkAcnPtO6vf7q4aH5QHs/F1s3/snQaSFbbUMcFFZ086Mw==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-stack@3.0.11': + resolution: {integrity: sha512-1HGo9a6/ikgOMrTrWL/WiN9N8GSVYpuRQO5kjstAq4CvV59bjqnh7TbdXGQ4vxLD3xlSjfBjq5t1SOELePsLnA==} + engines: {node: '>=16.0.0'} + + '@smithy/node-config-provider@3.1.12': + resolution: {integrity: sha512-O9LVEu5J/u/FuNlZs+L7Ikn3lz7VB9hb0GtPT9MQeiBmtK8RSY3ULmsZgXhe6VAlgTw0YO+paQx4p8xdbs43vQ==} + engines: {node: '>=16.0.0'} + + '@smithy/node-http-handler@3.3.2': + resolution: {integrity: sha512-t4ng1DAd527vlxvOfKFYEe6/QFBcsj7WpNlWTyjorwXXcKw3XlltBGbyHfSJ24QT84nF+agDha9tNYpzmSRZPA==} + engines: {node: '>=16.0.0'} + + '@smithy/property-provider@3.1.11': + resolution: {integrity: sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==} + engines: {node: '>=16.0.0'} + + '@smithy/protocol-http@4.1.8': + resolution: {integrity: sha512-hmgIAVyxw1LySOwkgMIUN0kjN8TG9Nc85LJeEmEE/cNEe2rkHDUWhnJf2gxcSRFLWsyqWsrZGw40ROjUogg+Iw==} + engines: {node: '>=16.0.0'} + + '@smithy/querystring-builder@3.0.11': + resolution: {integrity: sha512-u+5HV/9uJaeLj5XTb6+IEF/dokWWkEqJ0XiaRRogyREmKGUgZnNecLucADLdauWFKUNbQfulHFEZEdjwEBjXRg==} + engines: {node: '>=16.0.0'} + + '@smithy/querystring-parser@3.0.11': + resolution: {integrity: sha512-Je3kFvCsFMnso1ilPwA7GtlbPaTixa3WwC+K21kmMZHsBEOZYQaqxcMqeFFoU7/slFjKDIpiiPydvdJm8Q/MCw==} + engines: {node: '>=16.0.0'} + + '@smithy/service-error-classification@3.0.11': + resolution: {integrity: sha512-QnYDPkyewrJzCyaeI2Rmp7pDwbUETe+hU8ADkXmgNusO1bgHBH7ovXJiYmba8t0fNfJx75fE8dlM6SEmZxheog==} + engines: {node: '>=16.0.0'} + + '@smithy/shared-ini-file-loader@3.1.12': + resolution: {integrity: sha512-1xKSGI+U9KKdbG2qDvIR9dGrw3CNx+baqJfyr0igKEpjbHL5stsqAesYBzHChYHlelWtb87VnLWlhvfCz13H8Q==} + engines: {node: '>=16.0.0'} + + '@smithy/signature-v4@4.2.4': + resolution: {integrity: sha512-5JWeMQYg81TgU4cG+OexAWdvDTs5JDdbEZx+Qr1iPbvo91QFGzjy0IkXAKaXUHqmKUJgSHK0ZxnCkgZpzkeNTA==} + engines: {node: '>=16.0.0'} + + '@smithy/smithy-client@3.5.1': + resolution: {integrity: sha512-PmjskH4Os1Eh3rd5vSsa5uVelZ4DRu+N5CBEgb9AT96hQSJGWSEb6pGxKV/PtKQSIp9ft3+KvnT8ViMKaguzgA==} + engines: {node: '>=16.0.0'} + + '@smithy/types@3.7.2': + resolution: {integrity: sha512-bNwBYYmN8Eh9RyjS1p2gW6MIhSO2rl7X9QeLM8iTdcGRP+eDiIWDt66c9IysCc22gefKszZv+ubV9qZc7hdESg==} + engines: {node: '>=16.0.0'} + + '@smithy/url-parser@3.0.11': + resolution: {integrity: sha512-TmlqXkSk8ZPhfc+SQutjmFr5FjC0av3GZP4B/10caK1SbRwe/v+Wzu/R6xEKxoNqL+8nY18s1byiy6HqPG37Aw==} + + '@smithy/util-base64@3.0.0': + resolution: {integrity: sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==} + engines: {node: '>=16.0.0'} + + '@smithy/util-body-length-browser@3.0.0': + resolution: {integrity: sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==} + + '@smithy/util-body-length-node@3.0.0': + resolution: {integrity: sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==} + engines: {node: '>=16.0.0'} + + '@smithy/util-buffer-from@2.2.0': + resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-buffer-from@3.0.0': + resolution: {integrity: sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==} + engines: {node: '>=16.0.0'} + + '@smithy/util-config-provider@3.0.0': + resolution: {integrity: sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==} + engines: {node: '>=16.0.0'} + + '@smithy/util-defaults-mode-browser@3.0.31': + resolution: {integrity: sha512-eO+zkbqrPnmsagqzrmF7IJrCoU2wTQXWVYxMPqA9Oue55kw9WEvhyuw2XQzTVTCRcYsg6KgmV3YYhLlWQJfK1A==} + engines: {node: '>= 10.0.0'} + + '@smithy/util-defaults-mode-node@3.0.31': + resolution: {integrity: sha512-0/nJfpSpbGZOs6qs42wCe2TdjobbnnD4a3YUUlvTXSQqLy4qa63luDaV04hGvqSHP7wQ7/WGehbvHkDhMZd1MQ==} + engines: {node: '>= 10.0.0'} + + '@smithy/util-endpoints@2.1.7': + resolution: {integrity: sha512-tSfcqKcN/Oo2STEYCABVuKgJ76nyyr6skGl9t15hs+YaiU06sgMkN7QYjo0BbVw+KT26zok3IzbdSOksQ4YzVw==} + engines: {node: '>=16.0.0'} + + '@smithy/util-hex-encoding@3.0.0': + resolution: {integrity: sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==} + engines: {node: '>=16.0.0'} + + '@smithy/util-middleware@3.0.11': + resolution: {integrity: sha512-dWpyc1e1R6VoXrwLoLDd57U1z6CwNSdkM69Ie4+6uYh2GC7Vg51Qtan7ITzczuVpqezdDTKJGJB95fFvvjU/ow==} + engines: {node: '>=16.0.0'} + + '@smithy/util-retry@3.0.11': + resolution: {integrity: sha512-hJUC6W7A3DQgaee3Hp9ZFcOxVDZzmBIRBPlUAk8/fSOEl7pE/aX7Dci0JycNOnm9Mfr0KV2XjIlUOcGWXQUdVQ==} + engines: {node: '>=16.0.0'} + + '@smithy/util-stream@3.3.2': + resolution: {integrity: sha512-sInAqdiVeisUGYAv/FrXpmJ0b4WTFmciTRqzhb7wVuem9BHvhIG7tpiYHLDWrl2stOokNZpTTGqz3mzB2qFwXg==} + engines: {node: '>=16.0.0'} + + '@smithy/util-uri-escape@3.0.0': + resolution: {integrity: sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==} + engines: {node: '>=16.0.0'} + + '@smithy/util-utf8@2.3.0': + resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} + engines: {node: '>=14.0.0'} + + '@smithy/util-utf8@3.0.0': + resolution: {integrity: sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==} + engines: {node: '>=16.0.0'} + + '@smithy/util-waiter@3.2.0': + resolution: {integrity: sha512-PpjSboaDUE6yl+1qlg3Si57++e84oXdWGbuFUSAciXsVfEZJJJupR2Nb0QuXHiunt2vGR+1PTizOMvnUPaG2Qg==} + engines: {node: '>=16.0.0'} + + '@types/body-parser@1.19.5': + resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} + + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + + '@types/express-serve-static-core@5.0.2': + resolution: {integrity: sha512-vluaspfvWEtE4vcSDlKRNer52DvOGrB2xv6diXy6UKyKW0lqZiWHGNApSyxOv+8DE5Z27IzVvE7hNkxg7EXIcg==} + + '@types/express@5.0.0': + resolution: {integrity: sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==} + + '@types/http-errors@2.0.4': + resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} + + '@types/mime@1.3.5': + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + + '@types/node@22.10.2': + resolution: {integrity: sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==} + + '@types/qs@6.9.17': + resolution: {integrity: sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==} + + '@types/range-parser@1.2.7': + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + + '@types/send@0.17.4': + resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} + + '@types/serve-static@1.15.7': + resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} + + '@types/sha.js@2.4.4': + resolution: {integrity: sha512-Qukd+D6S2Hm0wLVt2Vh+/eWBIoUt+wF8jWjBsG4F8EFQRwKtYvtXCPcNl2OEUQ1R+eTr3xuSaBYUyM3WD1x/Qw==} + + '@types/uuid@9.0.8': + resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} + + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + + array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + + body-parser@1.20.3: + resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + bowser@2.11.0: + resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + call-bind-apply-helpers@1.0.1: + resolution: {integrity: sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==} + engines: {node: '>= 0.4'} + + call-bound@1.0.3: + resolution: {integrity: sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==} + engines: {node: '>= 0.4'} + + content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + cookie-signature@1.0.6: + resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + + cookie@0.7.1: + resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} + engines: {node: '>= 0.6'} + + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.0.0: + resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} + engines: {node: '>= 0.4'} + + esbuild@0.24.2: + resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} + engines: {node: '>=18'} + hasBin: true + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + express@4.21.2: + resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} + engines: {node: '>= 0.10.0'} + + fast-xml-parser@4.4.1: + resolution: {integrity: sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==} + hasBin: true + + finalhandler@1.3.1: + resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==} + engines: {node: '>= 0.8'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-intrinsic@1.2.6: + resolution: {integrity: sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA==} + engines: {node: '>= 0.4'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + + merge-descriptors@1.0.3: + resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} + + methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + object-inspect@1.13.3: + resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==} + engines: {node: '>= 0.4'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + path-to-regexp@0.1.12: + resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + qs@6.13.0: + resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} + engines: {node: '>=0.6'} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@2.5.2: + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} + engines: {node: '>= 0.8'} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + send@0.19.0: + resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} + engines: {node: '>= 0.8.0'} + + serve-static@1.16.2: + resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} + engines: {node: '>= 0.8.0'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + sha.js@2.4.11: + resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} + hasBin: true + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + strnum@1.0.5: + resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + + typescript@5.7.2: + resolution: {integrity: sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.20.0: + resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + + uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + +snapshots: + + '@aws-crypto/crc32@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.714.0 + tslib: 2.8.1 + + '@aws-crypto/crc32c@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.714.0 + tslib: 2.8.1 + + '@aws-crypto/sha1-browser@5.2.0': + dependencies: + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.714.0 + '@aws-sdk/util-locate-window': 3.693.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-crypto/sha256-browser@5.2.0': + dependencies: + '@aws-crypto/sha256-js': 5.2.0 + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.714.0 + '@aws-sdk/util-locate-window': 3.693.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-crypto/sha256-js@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.714.0 + tslib: 2.8.1 + + '@aws-crypto/supports-web-crypto@5.2.0': + dependencies: + tslib: 2.8.1 + + '@aws-crypto/util@5.2.0': + dependencies: + '@aws-sdk/types': 3.714.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-sdk/client-apigatewayv2@3.716.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/client-sso-oidc': 3.716.0(@aws-sdk/client-sts@3.716.0) + '@aws-sdk/client-sts': 3.716.0 + '@aws-sdk/core': 3.716.0 + '@aws-sdk/credential-provider-node': 3.716.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0))(@aws-sdk/client-sts@3.716.0) + '@aws-sdk/middleware-host-header': 3.714.0 + '@aws-sdk/middleware-logger': 3.714.0 + '@aws-sdk/middleware-recursion-detection': 3.714.0 + '@aws-sdk/middleware-user-agent': 3.716.0 + '@aws-sdk/region-config-resolver': 3.714.0 + '@aws-sdk/types': 3.714.0 + '@aws-sdk/util-endpoints': 3.714.0 + '@aws-sdk/util-user-agent-browser': 3.714.0 + '@aws-sdk/util-user-agent-node': 3.716.0 + '@smithy/config-resolver': 3.0.13 + '@smithy/core': 2.5.5 + '@smithy/fetch-http-handler': 4.1.2 + '@smithy/hash-node': 3.0.11 + '@smithy/invalid-dependency': 3.0.11 + '@smithy/middleware-content-length': 3.0.13 + '@smithy/middleware-endpoint': 3.2.6 + '@smithy/middleware-retry': 3.0.31 + '@smithy/middleware-serde': 3.0.11 + '@smithy/middleware-stack': 3.0.11 + '@smithy/node-config-provider': 3.1.12 + '@smithy/node-http-handler': 3.3.2 + '@smithy/protocol-http': 4.1.8 + '@smithy/smithy-client': 3.5.1 + '@smithy/types': 3.7.2 + '@smithy/url-parser': 3.0.11 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.31 + '@smithy/util-defaults-mode-node': 3.0.31 + '@smithy/util-endpoints': 2.1.7 + '@smithy/util-middleware': 3.0.11 + '@smithy/util-retry': 3.0.11 + '@smithy/util-stream': 3.3.2 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-auto-scaling@3.716.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/client-sso-oidc': 3.716.0(@aws-sdk/client-sts@3.716.0) + '@aws-sdk/client-sts': 3.716.0 + '@aws-sdk/core': 3.716.0 + '@aws-sdk/credential-provider-node': 3.716.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0))(@aws-sdk/client-sts@3.716.0) + '@aws-sdk/middleware-host-header': 3.714.0 + '@aws-sdk/middleware-logger': 3.714.0 + '@aws-sdk/middleware-recursion-detection': 3.714.0 + '@aws-sdk/middleware-user-agent': 3.716.0 + '@aws-sdk/region-config-resolver': 3.714.0 + '@aws-sdk/types': 3.714.0 + '@aws-sdk/util-endpoints': 3.714.0 + '@aws-sdk/util-user-agent-browser': 3.714.0 + '@aws-sdk/util-user-agent-node': 3.716.0 + '@smithy/config-resolver': 3.0.13 + '@smithy/core': 2.5.5 + '@smithy/fetch-http-handler': 4.1.2 + '@smithy/hash-node': 3.0.11 + '@smithy/invalid-dependency': 3.0.11 + '@smithy/middleware-content-length': 3.0.13 + '@smithy/middleware-endpoint': 3.2.6 + '@smithy/middleware-retry': 3.0.31 + '@smithy/middleware-serde': 3.0.11 + '@smithy/middleware-stack': 3.0.11 + '@smithy/node-config-provider': 3.1.12 + '@smithy/node-http-handler': 3.3.2 + '@smithy/protocol-http': 4.1.8 + '@smithy/smithy-client': 3.5.1 + '@smithy/types': 3.7.2 + '@smithy/url-parser': 3.0.11 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.31 + '@smithy/util-defaults-mode-node': 3.0.31 + '@smithy/util-endpoints': 2.1.7 + '@smithy/util-middleware': 3.0.11 + '@smithy/util-retry': 3.0.11 + '@smithy/util-utf8': 3.0.0 + '@smithy/util-waiter': 3.2.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-backup@3.716.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/client-sso-oidc': 3.716.0(@aws-sdk/client-sts@3.716.0) + '@aws-sdk/client-sts': 3.716.0 + '@aws-sdk/core': 3.716.0 + '@aws-sdk/credential-provider-node': 3.716.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0))(@aws-sdk/client-sts@3.716.0) + '@aws-sdk/middleware-host-header': 3.714.0 + '@aws-sdk/middleware-logger': 3.714.0 + '@aws-sdk/middleware-recursion-detection': 3.714.0 + '@aws-sdk/middleware-user-agent': 3.716.0 + '@aws-sdk/region-config-resolver': 3.714.0 + '@aws-sdk/types': 3.714.0 + '@aws-sdk/util-endpoints': 3.714.0 + '@aws-sdk/util-user-agent-browser': 3.714.0 + '@aws-sdk/util-user-agent-node': 3.716.0 + '@smithy/config-resolver': 3.0.13 + '@smithy/core': 2.5.5 + '@smithy/fetch-http-handler': 4.1.2 + '@smithy/hash-node': 3.0.11 + '@smithy/invalid-dependency': 3.0.11 + '@smithy/middleware-content-length': 3.0.13 + '@smithy/middleware-endpoint': 3.2.6 + '@smithy/middleware-retry': 3.0.31 + '@smithy/middleware-serde': 3.0.11 + '@smithy/middleware-stack': 3.0.11 + '@smithy/node-config-provider': 3.1.12 + '@smithy/node-http-handler': 3.3.2 + '@smithy/protocol-http': 4.1.8 + '@smithy/smithy-client': 3.5.1 + '@smithy/types': 3.7.2 + '@smithy/url-parser': 3.0.11 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.31 + '@smithy/util-defaults-mode-node': 3.0.31 + '@smithy/util-endpoints': 2.1.7 + '@smithy/util-middleware': 3.0.11 + '@smithy/util-retry': 3.0.11 + '@smithy/util-utf8': 3.0.0 + '@types/uuid': 9.0.8 + tslib: 2.8.1 + uuid: 9.0.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-cloudfront@3.716.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/client-sso-oidc': 3.716.0(@aws-sdk/client-sts@3.716.0) + '@aws-sdk/client-sts': 3.716.0 + '@aws-sdk/core': 3.716.0 + '@aws-sdk/credential-provider-node': 3.716.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0))(@aws-sdk/client-sts@3.716.0) + '@aws-sdk/middleware-host-header': 3.714.0 + '@aws-sdk/middleware-logger': 3.714.0 + '@aws-sdk/middleware-recursion-detection': 3.714.0 + '@aws-sdk/middleware-user-agent': 3.716.0 + '@aws-sdk/region-config-resolver': 3.714.0 + '@aws-sdk/types': 3.714.0 + '@aws-sdk/util-endpoints': 3.714.0 + '@aws-sdk/util-user-agent-browser': 3.714.0 + '@aws-sdk/util-user-agent-node': 3.716.0 + '@aws-sdk/xml-builder': 3.709.0 + '@smithy/config-resolver': 3.0.13 + '@smithy/core': 2.5.5 + '@smithy/fetch-http-handler': 4.1.2 + '@smithy/hash-node': 3.0.11 + '@smithy/invalid-dependency': 3.0.11 + '@smithy/middleware-content-length': 3.0.13 + '@smithy/middleware-endpoint': 3.2.6 + '@smithy/middleware-retry': 3.0.31 + '@smithy/middleware-serde': 3.0.11 + '@smithy/middleware-stack': 3.0.11 + '@smithy/node-config-provider': 3.1.12 + '@smithy/node-http-handler': 3.3.2 + '@smithy/protocol-http': 4.1.8 + '@smithy/smithy-client': 3.5.1 + '@smithy/types': 3.7.2 + '@smithy/url-parser': 3.0.11 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.31 + '@smithy/util-defaults-mode-node': 3.0.31 + '@smithy/util-endpoints': 2.1.7 + '@smithy/util-middleware': 3.0.11 + '@smithy/util-retry': 3.0.11 + '@smithy/util-stream': 3.3.2 + '@smithy/util-utf8': 3.0.0 + '@smithy/util-waiter': 3.2.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-elastic-load-balancing-v2@3.716.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/client-sso-oidc': 3.716.0(@aws-sdk/client-sts@3.716.0) + '@aws-sdk/client-sts': 3.716.0 + '@aws-sdk/core': 3.716.0 + '@aws-sdk/credential-provider-node': 3.716.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0))(@aws-sdk/client-sts@3.716.0) + '@aws-sdk/middleware-host-header': 3.714.0 + '@aws-sdk/middleware-logger': 3.714.0 + '@aws-sdk/middleware-recursion-detection': 3.714.0 + '@aws-sdk/middleware-user-agent': 3.716.0 + '@aws-sdk/region-config-resolver': 3.714.0 + '@aws-sdk/types': 3.714.0 + '@aws-sdk/util-endpoints': 3.714.0 + '@aws-sdk/util-user-agent-browser': 3.714.0 + '@aws-sdk/util-user-agent-node': 3.716.0 + '@smithy/config-resolver': 3.0.13 + '@smithy/core': 2.5.5 + '@smithy/fetch-http-handler': 4.1.2 + '@smithy/hash-node': 3.0.11 + '@smithy/invalid-dependency': 3.0.11 + '@smithy/middleware-content-length': 3.0.13 + '@smithy/middleware-endpoint': 3.2.6 + '@smithy/middleware-retry': 3.0.31 + '@smithy/middleware-serde': 3.0.11 + '@smithy/middleware-stack': 3.0.11 + '@smithy/node-config-provider': 3.1.12 + '@smithy/node-http-handler': 3.3.2 + '@smithy/protocol-http': 4.1.8 + '@smithy/smithy-client': 3.5.1 + '@smithy/types': 3.7.2 + '@smithy/url-parser': 3.0.11 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.31 + '@smithy/util-defaults-mode-node': 3.0.31 + '@smithy/util-endpoints': 2.1.7 + '@smithy/util-middleware': 3.0.11 + '@smithy/util-retry': 3.0.11 + '@smithy/util-utf8': 3.0.0 + '@smithy/util-waiter': 3.2.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-lambda@3.716.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/client-sso-oidc': 3.716.0(@aws-sdk/client-sts@3.716.0) + '@aws-sdk/client-sts': 3.716.0 + '@aws-sdk/core': 3.716.0 + '@aws-sdk/credential-provider-node': 3.716.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0))(@aws-sdk/client-sts@3.716.0) + '@aws-sdk/middleware-host-header': 3.714.0 + '@aws-sdk/middleware-logger': 3.714.0 + '@aws-sdk/middleware-recursion-detection': 3.714.0 + '@aws-sdk/middleware-user-agent': 3.716.0 + '@aws-sdk/region-config-resolver': 3.714.0 + '@aws-sdk/types': 3.714.0 + '@aws-sdk/util-endpoints': 3.714.0 + '@aws-sdk/util-user-agent-browser': 3.714.0 + '@aws-sdk/util-user-agent-node': 3.716.0 + '@smithy/config-resolver': 3.0.13 + '@smithy/core': 2.5.5 + '@smithy/eventstream-serde-browser': 3.0.14 + '@smithy/eventstream-serde-config-resolver': 3.0.11 + '@smithy/eventstream-serde-node': 3.0.13 + '@smithy/fetch-http-handler': 4.1.2 + '@smithy/hash-node': 3.0.11 + '@smithy/invalid-dependency': 3.0.11 + '@smithy/middleware-content-length': 3.0.13 + '@smithy/middleware-endpoint': 3.2.6 + '@smithy/middleware-retry': 3.0.31 + '@smithy/middleware-serde': 3.0.11 + '@smithy/middleware-stack': 3.0.11 + '@smithy/node-config-provider': 3.1.12 + '@smithy/node-http-handler': 3.3.2 + '@smithy/protocol-http': 4.1.8 + '@smithy/smithy-client': 3.5.1 + '@smithy/types': 3.7.2 + '@smithy/url-parser': 3.0.11 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.31 + '@smithy/util-defaults-mode-node': 3.0.31 + '@smithy/util-endpoints': 2.1.7 + '@smithy/util-middleware': 3.0.11 + '@smithy/util-retry': 3.0.11 + '@smithy/util-stream': 3.3.2 + '@smithy/util-utf8': 3.0.0 + '@smithy/util-waiter': 3.2.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-s3-control@3.716.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/client-sso-oidc': 3.716.0(@aws-sdk/client-sts@3.716.0) + '@aws-sdk/client-sts': 3.716.0 + '@aws-sdk/core': 3.716.0 + '@aws-sdk/credential-provider-node': 3.716.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0))(@aws-sdk/client-sts@3.716.0) + '@aws-sdk/middleware-host-header': 3.714.0 + '@aws-sdk/middleware-logger': 3.714.0 + '@aws-sdk/middleware-recursion-detection': 3.714.0 + '@aws-sdk/middleware-sdk-s3-control': 3.714.0 + '@aws-sdk/middleware-user-agent': 3.716.0 + '@aws-sdk/region-config-resolver': 3.714.0 + '@aws-sdk/types': 3.714.0 + '@aws-sdk/util-endpoints': 3.714.0 + '@aws-sdk/util-user-agent-browser': 3.714.0 + '@aws-sdk/util-user-agent-node': 3.716.0 + '@aws-sdk/xml-builder': 3.709.0 + '@smithy/config-resolver': 3.0.13 + '@smithy/core': 2.5.5 + '@smithy/fetch-http-handler': 4.1.2 + '@smithy/hash-blob-browser': 3.1.10 + '@smithy/hash-node': 3.0.11 + '@smithy/hash-stream-node': 3.1.10 + '@smithy/invalid-dependency': 3.0.11 + '@smithy/md5-js': 3.0.11 + '@smithy/middleware-apply-body-checksum': 3.0.13 + '@smithy/middleware-content-length': 3.0.13 + '@smithy/middleware-endpoint': 3.2.6 + '@smithy/middleware-retry': 3.0.31 + '@smithy/middleware-serde': 3.0.11 + '@smithy/middleware-stack': 3.0.11 + '@smithy/node-config-provider': 3.1.12 + '@smithy/node-http-handler': 3.3.2 + '@smithy/protocol-http': 4.1.8 + '@smithy/smithy-client': 3.5.1 + '@smithy/types': 3.7.2 + '@smithy/url-parser': 3.0.11 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.31 + '@smithy/util-defaults-mode-node': 3.0.31 + '@smithy/util-endpoints': 2.1.7 + '@smithy/util-middleware': 3.0.11 + '@smithy/util-retry': 3.0.11 + '@smithy/util-utf8': 3.0.0 + '@types/uuid': 9.0.8 + tslib: 2.8.1 + uuid: 9.0.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-s3@3.717.0': + dependencies: + '@aws-crypto/sha1-browser': 5.2.0 + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/client-sso-oidc': 3.716.0(@aws-sdk/client-sts@3.716.0) + '@aws-sdk/client-sts': 3.716.0 + '@aws-sdk/core': 3.716.0 + '@aws-sdk/credential-provider-node': 3.716.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0))(@aws-sdk/client-sts@3.716.0) + '@aws-sdk/middleware-bucket-endpoint': 3.714.0 + '@aws-sdk/middleware-expect-continue': 3.714.0 + '@aws-sdk/middleware-flexible-checksums': 3.717.0 + '@aws-sdk/middleware-host-header': 3.714.0 + '@aws-sdk/middleware-location-constraint': 3.714.0 + '@aws-sdk/middleware-logger': 3.714.0 + '@aws-sdk/middleware-recursion-detection': 3.714.0 + '@aws-sdk/middleware-sdk-s3': 3.716.0 + '@aws-sdk/middleware-ssec': 3.714.0 + '@aws-sdk/middleware-user-agent': 3.716.0 + '@aws-sdk/region-config-resolver': 3.714.0 + '@aws-sdk/signature-v4-multi-region': 3.716.0 + '@aws-sdk/types': 3.714.0 + '@aws-sdk/util-endpoints': 3.714.0 + '@aws-sdk/util-user-agent-browser': 3.714.0 + '@aws-sdk/util-user-agent-node': 3.716.0 + '@aws-sdk/xml-builder': 3.709.0 + '@smithy/config-resolver': 3.0.13 + '@smithy/core': 2.5.5 + '@smithy/eventstream-serde-browser': 3.0.14 + '@smithy/eventstream-serde-config-resolver': 3.0.11 + '@smithy/eventstream-serde-node': 3.0.13 + '@smithy/fetch-http-handler': 4.1.2 + '@smithy/hash-blob-browser': 3.1.10 + '@smithy/hash-node': 3.0.11 + '@smithy/hash-stream-node': 3.1.10 + '@smithy/invalid-dependency': 3.0.11 + '@smithy/md5-js': 3.0.11 + '@smithy/middleware-content-length': 3.0.13 + '@smithy/middleware-endpoint': 3.2.6 + '@smithy/middleware-retry': 3.0.31 + '@smithy/middleware-serde': 3.0.11 + '@smithy/middleware-stack': 3.0.11 + '@smithy/node-config-provider': 3.1.12 + '@smithy/node-http-handler': 3.3.2 + '@smithy/protocol-http': 4.1.8 + '@smithy/smithy-client': 3.5.1 + '@smithy/types': 3.7.2 + '@smithy/url-parser': 3.0.11 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.31 + '@smithy/util-defaults-mode-node': 3.0.31 + '@smithy/util-endpoints': 2.1.7 + '@smithy/util-middleware': 3.0.11 + '@smithy/util-retry': 3.0.11 + '@smithy/util-stream': 3.3.2 + '@smithy/util-utf8': 3.0.0 + '@smithy/util-waiter': 3.2.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0)': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/client-sts': 3.716.0 + '@aws-sdk/core': 3.716.0 + '@aws-sdk/credential-provider-node': 3.716.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0))(@aws-sdk/client-sts@3.716.0) + '@aws-sdk/middleware-host-header': 3.714.0 + '@aws-sdk/middleware-logger': 3.714.0 + '@aws-sdk/middleware-recursion-detection': 3.714.0 + '@aws-sdk/middleware-user-agent': 3.716.0 + '@aws-sdk/region-config-resolver': 3.714.0 + '@aws-sdk/types': 3.714.0 + '@aws-sdk/util-endpoints': 3.714.0 + '@aws-sdk/util-user-agent-browser': 3.714.0 + '@aws-sdk/util-user-agent-node': 3.716.0 + '@smithy/config-resolver': 3.0.13 + '@smithy/core': 2.5.5 + '@smithy/fetch-http-handler': 4.1.2 + '@smithy/hash-node': 3.0.11 + '@smithy/invalid-dependency': 3.0.11 + '@smithy/middleware-content-length': 3.0.13 + '@smithy/middleware-endpoint': 3.2.6 + '@smithy/middleware-retry': 3.0.31 + '@smithy/middleware-serde': 3.0.11 + '@smithy/middleware-stack': 3.0.11 + '@smithy/node-config-provider': 3.1.12 + '@smithy/node-http-handler': 3.3.2 + '@smithy/protocol-http': 4.1.8 + '@smithy/smithy-client': 3.5.1 + '@smithy/types': 3.7.2 + '@smithy/url-parser': 3.0.11 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.31 + '@smithy/util-defaults-mode-node': 3.0.31 + '@smithy/util-endpoints': 2.1.7 + '@smithy/util-middleware': 3.0.11 + '@smithy/util-retry': 3.0.11 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-sso@3.716.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.716.0 + '@aws-sdk/middleware-host-header': 3.714.0 + '@aws-sdk/middleware-logger': 3.714.0 + '@aws-sdk/middleware-recursion-detection': 3.714.0 + '@aws-sdk/middleware-user-agent': 3.716.0 + '@aws-sdk/region-config-resolver': 3.714.0 + '@aws-sdk/types': 3.714.0 + '@aws-sdk/util-endpoints': 3.714.0 + '@aws-sdk/util-user-agent-browser': 3.714.0 + '@aws-sdk/util-user-agent-node': 3.716.0 + '@smithy/config-resolver': 3.0.13 + '@smithy/core': 2.5.5 + '@smithy/fetch-http-handler': 4.1.2 + '@smithy/hash-node': 3.0.11 + '@smithy/invalid-dependency': 3.0.11 + '@smithy/middleware-content-length': 3.0.13 + '@smithy/middleware-endpoint': 3.2.6 + '@smithy/middleware-retry': 3.0.31 + '@smithy/middleware-serde': 3.0.11 + '@smithy/middleware-stack': 3.0.11 + '@smithy/node-config-provider': 3.1.12 + '@smithy/node-http-handler': 3.3.2 + '@smithy/protocol-http': 4.1.8 + '@smithy/smithy-client': 3.5.1 + '@smithy/types': 3.7.2 + '@smithy/url-parser': 3.0.11 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.31 + '@smithy/util-defaults-mode-node': 3.0.31 + '@smithy/util-endpoints': 2.1.7 + '@smithy/util-middleware': 3.0.11 + '@smithy/util-retry': 3.0.11 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-sts@3.716.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/client-sso-oidc': 3.716.0(@aws-sdk/client-sts@3.716.0) + '@aws-sdk/core': 3.716.0 + '@aws-sdk/credential-provider-node': 3.716.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0))(@aws-sdk/client-sts@3.716.0) + '@aws-sdk/middleware-host-header': 3.714.0 + '@aws-sdk/middleware-logger': 3.714.0 + '@aws-sdk/middleware-recursion-detection': 3.714.0 + '@aws-sdk/middleware-user-agent': 3.716.0 + '@aws-sdk/region-config-resolver': 3.714.0 + '@aws-sdk/types': 3.714.0 + '@aws-sdk/util-endpoints': 3.714.0 + '@aws-sdk/util-user-agent-browser': 3.714.0 + '@aws-sdk/util-user-agent-node': 3.716.0 + '@smithy/config-resolver': 3.0.13 + '@smithy/core': 2.5.5 + '@smithy/fetch-http-handler': 4.1.2 + '@smithy/hash-node': 3.0.11 + '@smithy/invalid-dependency': 3.0.11 + '@smithy/middleware-content-length': 3.0.13 + '@smithy/middleware-endpoint': 3.2.6 + '@smithy/middleware-retry': 3.0.31 + '@smithy/middleware-serde': 3.0.11 + '@smithy/middleware-stack': 3.0.11 + '@smithy/node-config-provider': 3.1.12 + '@smithy/node-http-handler': 3.3.2 + '@smithy/protocol-http': 4.1.8 + '@smithy/smithy-client': 3.5.1 + '@smithy/types': 3.7.2 + '@smithy/url-parser': 3.0.11 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.31 + '@smithy/util-defaults-mode-node': 3.0.31 + '@smithy/util-endpoints': 2.1.7 + '@smithy/util-middleware': 3.0.11 + '@smithy/util-retry': 3.0.11 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-wafv2@3.716.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/client-sso-oidc': 3.716.0(@aws-sdk/client-sts@3.716.0) + '@aws-sdk/client-sts': 3.716.0 + '@aws-sdk/core': 3.716.0 + '@aws-sdk/credential-provider-node': 3.716.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0))(@aws-sdk/client-sts@3.716.0) + '@aws-sdk/middleware-host-header': 3.714.0 + '@aws-sdk/middleware-logger': 3.714.0 + '@aws-sdk/middleware-recursion-detection': 3.714.0 + '@aws-sdk/middleware-user-agent': 3.716.0 + '@aws-sdk/region-config-resolver': 3.714.0 + '@aws-sdk/types': 3.714.0 + '@aws-sdk/util-endpoints': 3.714.0 + '@aws-sdk/util-user-agent-browser': 3.714.0 + '@aws-sdk/util-user-agent-node': 3.716.0 + '@smithy/config-resolver': 3.0.13 + '@smithy/core': 2.5.5 + '@smithy/fetch-http-handler': 4.1.2 + '@smithy/hash-node': 3.0.11 + '@smithy/invalid-dependency': 3.0.11 + '@smithy/middleware-content-length': 3.0.13 + '@smithy/middleware-endpoint': 3.2.6 + '@smithy/middleware-retry': 3.0.31 + '@smithy/middleware-serde': 3.0.11 + '@smithy/middleware-stack': 3.0.11 + '@smithy/node-config-provider': 3.1.12 + '@smithy/node-http-handler': 3.3.2 + '@smithy/protocol-http': 4.1.8 + '@smithy/smithy-client': 3.5.1 + '@smithy/types': 3.7.2 + '@smithy/url-parser': 3.0.11 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.31 + '@smithy/util-defaults-mode-node': 3.0.31 + '@smithy/util-endpoints': 2.1.7 + '@smithy/util-middleware': 3.0.11 + '@smithy/util-retry': 3.0.11 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/core@3.716.0': + dependencies: + '@aws-sdk/types': 3.714.0 + '@smithy/core': 2.5.5 + '@smithy/node-config-provider': 3.1.12 + '@smithy/property-provider': 3.1.11 + '@smithy/protocol-http': 4.1.8 + '@smithy/signature-v4': 4.2.4 + '@smithy/smithy-client': 3.5.1 + '@smithy/types': 3.7.2 + '@smithy/util-middleware': 3.0.11 + fast-xml-parser: 4.4.1 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-env@3.716.0': + dependencies: + '@aws-sdk/core': 3.716.0 + '@aws-sdk/types': 3.714.0 + '@smithy/property-provider': 3.1.11 + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-http@3.716.0': + dependencies: + '@aws-sdk/core': 3.716.0 + '@aws-sdk/types': 3.714.0 + '@smithy/fetch-http-handler': 4.1.2 + '@smithy/node-http-handler': 3.3.2 + '@smithy/property-provider': 3.1.11 + '@smithy/protocol-http': 4.1.8 + '@smithy/smithy-client': 3.5.1 + '@smithy/types': 3.7.2 + '@smithy/util-stream': 3.3.2 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-ini@3.716.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0))(@aws-sdk/client-sts@3.716.0)': + dependencies: + '@aws-sdk/client-sts': 3.716.0 + '@aws-sdk/core': 3.716.0 + '@aws-sdk/credential-provider-env': 3.716.0 + '@aws-sdk/credential-provider-http': 3.716.0 + '@aws-sdk/credential-provider-process': 3.716.0 + '@aws-sdk/credential-provider-sso': 3.716.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0)) + '@aws-sdk/credential-provider-web-identity': 3.716.0(@aws-sdk/client-sts@3.716.0) + '@aws-sdk/types': 3.714.0 + '@smithy/credential-provider-imds': 3.2.8 + '@smithy/property-provider': 3.1.11 + '@smithy/shared-ini-file-loader': 3.1.12 + '@smithy/types': 3.7.2 + tslib: 2.8.1 + transitivePeerDependencies: + - '@aws-sdk/client-sso-oidc' + - aws-crt + + '@aws-sdk/credential-provider-node@3.716.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0))(@aws-sdk/client-sts@3.716.0)': + dependencies: + '@aws-sdk/credential-provider-env': 3.716.0 + '@aws-sdk/credential-provider-http': 3.716.0 + '@aws-sdk/credential-provider-ini': 3.716.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0))(@aws-sdk/client-sts@3.716.0) + '@aws-sdk/credential-provider-process': 3.716.0 + '@aws-sdk/credential-provider-sso': 3.716.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0)) + '@aws-sdk/credential-provider-web-identity': 3.716.0(@aws-sdk/client-sts@3.716.0) + '@aws-sdk/types': 3.714.0 + '@smithy/credential-provider-imds': 3.2.8 + '@smithy/property-provider': 3.1.11 + '@smithy/shared-ini-file-loader': 3.1.12 + '@smithy/types': 3.7.2 + tslib: 2.8.1 + transitivePeerDependencies: + - '@aws-sdk/client-sso-oidc' + - '@aws-sdk/client-sts' + - aws-crt + + '@aws-sdk/credential-provider-process@3.716.0': + dependencies: + '@aws-sdk/core': 3.716.0 + '@aws-sdk/types': 3.714.0 + '@smithy/property-provider': 3.1.11 + '@smithy/shared-ini-file-loader': 3.1.12 + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-sso@3.716.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0))': + dependencies: + '@aws-sdk/client-sso': 3.716.0 + '@aws-sdk/core': 3.716.0 + '@aws-sdk/token-providers': 3.714.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0)) + '@aws-sdk/types': 3.714.0 + '@smithy/property-provider': 3.1.11 + '@smithy/shared-ini-file-loader': 3.1.12 + '@smithy/types': 3.7.2 + tslib: 2.8.1 + transitivePeerDependencies: + - '@aws-sdk/client-sso-oidc' + - aws-crt + + '@aws-sdk/credential-provider-web-identity@3.716.0(@aws-sdk/client-sts@3.716.0)': + dependencies: + '@aws-sdk/client-sts': 3.716.0 + '@aws-sdk/core': 3.716.0 + '@aws-sdk/types': 3.714.0 + '@smithy/property-provider': 3.1.11 + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@aws-sdk/middleware-bucket-endpoint@3.714.0': + dependencies: + '@aws-sdk/types': 3.714.0 + '@aws-sdk/util-arn-parser': 3.693.0 + '@smithy/node-config-provider': 3.1.12 + '@smithy/protocol-http': 4.1.8 + '@smithy/types': 3.7.2 + '@smithy/util-config-provider': 3.0.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-expect-continue@3.714.0': + dependencies: + '@aws-sdk/types': 3.714.0 + '@smithy/protocol-http': 4.1.8 + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@aws-sdk/middleware-flexible-checksums@3.717.0': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@aws-crypto/crc32c': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/core': 3.716.0 + '@aws-sdk/types': 3.714.0 + '@smithy/is-array-buffer': 3.0.0 + '@smithy/node-config-provider': 3.1.12 + '@smithy/protocol-http': 4.1.8 + '@smithy/types': 3.7.2 + '@smithy/util-middleware': 3.0.11 + '@smithy/util-stream': 3.3.2 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-host-header@3.714.0': + dependencies: + '@aws-sdk/types': 3.714.0 + '@smithy/protocol-http': 4.1.8 + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@aws-sdk/middleware-location-constraint@3.714.0': + dependencies: + '@aws-sdk/types': 3.714.0 + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@aws-sdk/middleware-logger@3.714.0': + dependencies: + '@aws-sdk/types': 3.714.0 + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@aws-sdk/middleware-recursion-detection@3.714.0': + dependencies: + '@aws-sdk/types': 3.714.0 + '@smithy/protocol-http': 4.1.8 + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@aws-sdk/middleware-sdk-s3-control@3.714.0': + dependencies: + '@aws-sdk/middleware-bucket-endpoint': 3.714.0 + '@aws-sdk/types': 3.714.0 + '@aws-sdk/util-arn-parser': 3.693.0 + '@aws-sdk/util-endpoints': 3.714.0 + '@smithy/protocol-http': 4.1.8 + '@smithy/types': 3.7.2 + '@smithy/util-endpoints': 2.1.7 + tslib: 2.8.1 + + '@aws-sdk/middleware-sdk-s3@3.716.0': + dependencies: + '@aws-sdk/core': 3.716.0 + '@aws-sdk/types': 3.714.0 + '@aws-sdk/util-arn-parser': 3.693.0 + '@smithy/core': 2.5.5 + '@smithy/node-config-provider': 3.1.12 + '@smithy/protocol-http': 4.1.8 + '@smithy/signature-v4': 4.2.4 + '@smithy/smithy-client': 3.5.1 + '@smithy/types': 3.7.2 + '@smithy/util-config-provider': 3.0.0 + '@smithy/util-middleware': 3.0.11 + '@smithy/util-stream': 3.3.2 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-ssec@3.714.0': + dependencies: + '@aws-sdk/types': 3.714.0 + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@aws-sdk/middleware-user-agent@3.716.0': + dependencies: + '@aws-sdk/core': 3.716.0 + '@aws-sdk/types': 3.714.0 + '@aws-sdk/util-endpoints': 3.714.0 + '@smithy/core': 2.5.5 + '@smithy/protocol-http': 4.1.8 + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@aws-sdk/region-config-resolver@3.714.0': + dependencies: + '@aws-sdk/types': 3.714.0 + '@smithy/node-config-provider': 3.1.12 + '@smithy/types': 3.7.2 + '@smithy/util-config-provider': 3.0.0 + '@smithy/util-middleware': 3.0.11 + tslib: 2.8.1 + + '@aws-sdk/signature-v4-multi-region@3.716.0': + dependencies: + '@aws-sdk/middleware-sdk-s3': 3.716.0 + '@aws-sdk/types': 3.714.0 + '@smithy/protocol-http': 4.1.8 + '@smithy/signature-v4': 4.2.4 + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@aws-sdk/token-providers@3.714.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0))': + dependencies: + '@aws-sdk/client-sso-oidc': 3.716.0(@aws-sdk/client-sts@3.716.0) + '@aws-sdk/types': 3.714.0 + '@smithy/property-provider': 3.1.11 + '@smithy/shared-ini-file-loader': 3.1.12 + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@aws-sdk/types@3.714.0': + dependencies: + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@aws-sdk/util-arn-parser@3.693.0': + dependencies: + tslib: 2.8.1 + + '@aws-sdk/util-endpoints@3.714.0': + dependencies: + '@aws-sdk/types': 3.714.0 + '@smithy/types': 3.7.2 + '@smithy/util-endpoints': 2.1.7 + tslib: 2.8.1 + + '@aws-sdk/util-locate-window@3.693.0': + dependencies: + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-browser@3.714.0': + dependencies: + '@aws-sdk/types': 3.714.0 + '@smithy/types': 3.7.2 + bowser: 2.11.0 + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-node@3.716.0': + dependencies: + '@aws-sdk/middleware-user-agent': 3.716.0 + '@aws-sdk/types': 3.714.0 + '@smithy/node-config-provider': 3.1.12 + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@aws-sdk/xml-builder@3.709.0': + dependencies: + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@esbuild/aix-ppc64@0.24.2': + optional: true + + '@esbuild/android-arm64@0.24.2': + optional: true + + '@esbuild/android-arm@0.24.2': + optional: true + + '@esbuild/android-x64@0.24.2': + optional: true + + '@esbuild/darwin-arm64@0.24.2': + optional: true + + '@esbuild/darwin-x64@0.24.2': + optional: true + + '@esbuild/freebsd-arm64@0.24.2': + optional: true + + '@esbuild/freebsd-x64@0.24.2': + optional: true + + '@esbuild/linux-arm64@0.24.2': + optional: true + + '@esbuild/linux-arm@0.24.2': + optional: true + + '@esbuild/linux-ia32@0.24.2': + optional: true + + '@esbuild/linux-loong64@0.24.2': + optional: true + + '@esbuild/linux-mips64el@0.24.2': + optional: true + + '@esbuild/linux-ppc64@0.24.2': + optional: true + + '@esbuild/linux-riscv64@0.24.2': + optional: true + + '@esbuild/linux-s390x@0.24.2': + optional: true + + '@esbuild/linux-x64@0.24.2': + optional: true + + '@esbuild/netbsd-arm64@0.24.2': + optional: true + + '@esbuild/netbsd-x64@0.24.2': + optional: true + + '@esbuild/openbsd-arm64@0.24.2': + optional: true + + '@esbuild/openbsd-x64@0.24.2': + optional: true + + '@esbuild/sunos-x64@0.24.2': + optional: true + + '@esbuild/win32-arm64@0.24.2': + optional: true + + '@esbuild/win32-ia32@0.24.2': + optional: true + + '@esbuild/win32-x64@0.24.2': + optional: true + + '@smithy/abort-controller@3.1.9': + dependencies: + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@smithy/chunked-blob-reader-native@3.0.1': + dependencies: + '@smithy/util-base64': 3.0.0 + tslib: 2.8.1 + + '@smithy/chunked-blob-reader@4.0.0': + dependencies: + tslib: 2.8.1 + + '@smithy/config-resolver@3.0.13': + dependencies: + '@smithy/node-config-provider': 3.1.12 + '@smithy/types': 3.7.2 + '@smithy/util-config-provider': 3.0.0 + '@smithy/util-middleware': 3.0.11 + tslib: 2.8.1 + + '@smithy/core@2.5.5': + dependencies: + '@smithy/middleware-serde': 3.0.11 + '@smithy/protocol-http': 4.1.8 + '@smithy/types': 3.7.2 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-middleware': 3.0.11 + '@smithy/util-stream': 3.3.2 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.1 + + '@smithy/credential-provider-imds@3.2.8': + dependencies: + '@smithy/node-config-provider': 3.1.12 + '@smithy/property-provider': 3.1.11 + '@smithy/types': 3.7.2 + '@smithy/url-parser': 3.0.11 + tslib: 2.8.1 + + '@smithy/eventstream-codec@3.1.10': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@smithy/types': 3.7.2 + '@smithy/util-hex-encoding': 3.0.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-browser@3.0.14': + dependencies: + '@smithy/eventstream-serde-universal': 3.0.13 + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@smithy/eventstream-serde-config-resolver@3.0.11': + dependencies: + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@smithy/eventstream-serde-node@3.0.13': + dependencies: + '@smithy/eventstream-serde-universal': 3.0.13 + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@smithy/eventstream-serde-universal@3.0.13': + dependencies: + '@smithy/eventstream-codec': 3.1.10 + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@smithy/fetch-http-handler@4.1.2': + dependencies: + '@smithy/protocol-http': 4.1.8 + '@smithy/querystring-builder': 3.0.11 + '@smithy/types': 3.7.2 + '@smithy/util-base64': 3.0.0 + tslib: 2.8.1 + + '@smithy/hash-blob-browser@3.1.10': + dependencies: + '@smithy/chunked-blob-reader': 4.0.0 + '@smithy/chunked-blob-reader-native': 3.0.1 + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@smithy/hash-node@3.0.11': + dependencies: + '@smithy/types': 3.7.2 + '@smithy/util-buffer-from': 3.0.0 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.1 + + '@smithy/hash-stream-node@3.1.10': + dependencies: + '@smithy/types': 3.7.2 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.1 + + '@smithy/invalid-dependency@3.0.11': + dependencies: + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@smithy/is-array-buffer@2.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/is-array-buffer@3.0.0': + dependencies: + tslib: 2.8.1 + + '@smithy/md5-js@3.0.11': + dependencies: + '@smithy/types': 3.7.2 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.1 + + '@smithy/middleware-apply-body-checksum@3.0.13': + dependencies: + '@smithy/is-array-buffer': 3.0.0 + '@smithy/protocol-http': 4.1.8 + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@smithy/middleware-content-length@3.0.13': + dependencies: + '@smithy/protocol-http': 4.1.8 + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@smithy/middleware-endpoint@3.2.6': + dependencies: + '@smithy/core': 2.5.5 + '@smithy/middleware-serde': 3.0.11 + '@smithy/node-config-provider': 3.1.12 + '@smithy/shared-ini-file-loader': 3.1.12 + '@smithy/types': 3.7.2 + '@smithy/url-parser': 3.0.11 + '@smithy/util-middleware': 3.0.11 + tslib: 2.8.1 + + '@smithy/middleware-retry@3.0.31': + dependencies: + '@smithy/node-config-provider': 3.1.12 + '@smithy/protocol-http': 4.1.8 + '@smithy/service-error-classification': 3.0.11 + '@smithy/smithy-client': 3.5.1 + '@smithy/types': 3.7.2 + '@smithy/util-middleware': 3.0.11 + '@smithy/util-retry': 3.0.11 + tslib: 2.8.1 + uuid: 9.0.1 + + '@smithy/middleware-serde@3.0.11': + dependencies: + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@smithy/middleware-stack@3.0.11': + dependencies: + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@smithy/node-config-provider@3.1.12': + dependencies: + '@smithy/property-provider': 3.1.11 + '@smithy/shared-ini-file-loader': 3.1.12 + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@smithy/node-http-handler@3.3.2': + dependencies: + '@smithy/abort-controller': 3.1.9 + '@smithy/protocol-http': 4.1.8 + '@smithy/querystring-builder': 3.0.11 + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@smithy/property-provider@3.1.11': + dependencies: + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@smithy/protocol-http@4.1.8': + dependencies: + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@smithy/querystring-builder@3.0.11': + dependencies: + '@smithy/types': 3.7.2 + '@smithy/util-uri-escape': 3.0.0 + tslib: 2.8.1 + + '@smithy/querystring-parser@3.0.11': + dependencies: + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@smithy/service-error-classification@3.0.11': + dependencies: + '@smithy/types': 3.7.2 + + '@smithy/shared-ini-file-loader@3.1.12': + dependencies: + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@smithy/signature-v4@4.2.4': + dependencies: + '@smithy/is-array-buffer': 3.0.0 + '@smithy/protocol-http': 4.1.8 + '@smithy/types': 3.7.2 + '@smithy/util-hex-encoding': 3.0.0 + '@smithy/util-middleware': 3.0.11 + '@smithy/util-uri-escape': 3.0.0 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.1 + + '@smithy/smithy-client@3.5.1': + dependencies: + '@smithy/core': 2.5.5 + '@smithy/middleware-endpoint': 3.2.6 + '@smithy/middleware-stack': 3.0.11 + '@smithy/protocol-http': 4.1.8 + '@smithy/types': 3.7.2 + '@smithy/util-stream': 3.3.2 + tslib: 2.8.1 + + '@smithy/types@3.7.2': + dependencies: + tslib: 2.8.1 + + '@smithy/url-parser@3.0.11': + dependencies: + '@smithy/querystring-parser': 3.0.11 + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@smithy/util-base64@3.0.0': + dependencies: + '@smithy/util-buffer-from': 3.0.0 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.1 + + '@smithy/util-body-length-browser@3.0.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-body-length-node@3.0.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-buffer-from@2.2.0': + dependencies: + '@smithy/is-array-buffer': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-buffer-from@3.0.0': + dependencies: + '@smithy/is-array-buffer': 3.0.0 + tslib: 2.8.1 + + '@smithy/util-config-provider@3.0.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-defaults-mode-browser@3.0.31': + dependencies: + '@smithy/property-provider': 3.1.11 + '@smithy/smithy-client': 3.5.1 + '@smithy/types': 3.7.2 + bowser: 2.11.0 + tslib: 2.8.1 + + '@smithy/util-defaults-mode-node@3.0.31': + dependencies: + '@smithy/config-resolver': 3.0.13 + '@smithy/credential-provider-imds': 3.2.8 + '@smithy/node-config-provider': 3.1.12 + '@smithy/property-provider': 3.1.11 + '@smithy/smithy-client': 3.5.1 + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@smithy/util-endpoints@2.1.7': + dependencies: + '@smithy/node-config-provider': 3.1.12 + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@smithy/util-hex-encoding@3.0.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-middleware@3.0.11': + dependencies: + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@smithy/util-retry@3.0.11': + dependencies: + '@smithy/service-error-classification': 3.0.11 + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@smithy/util-stream@3.3.2': + dependencies: + '@smithy/fetch-http-handler': 4.1.2 + '@smithy/node-http-handler': 3.3.2 + '@smithy/types': 3.7.2 + '@smithy/util-base64': 3.0.0 + '@smithy/util-buffer-from': 3.0.0 + '@smithy/util-hex-encoding': 3.0.0 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.1 + + '@smithy/util-uri-escape@3.0.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-utf8@2.3.0': + dependencies: + '@smithy/util-buffer-from': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-utf8@3.0.0': + dependencies: + '@smithy/util-buffer-from': 3.0.0 + tslib: 2.8.1 + + '@smithy/util-waiter@3.2.0': + dependencies: + '@smithy/abort-controller': 3.1.9 + '@smithy/types': 3.7.2 + tslib: 2.8.1 + + '@types/body-parser@1.19.5': + dependencies: + '@types/connect': 3.4.38 + '@types/node': 22.10.2 + + '@types/connect@3.4.38': + dependencies: + '@types/node': 22.10.2 + + '@types/express-serve-static-core@5.0.2': + dependencies: + '@types/node': 22.10.2 + '@types/qs': 6.9.17 + '@types/range-parser': 1.2.7 + '@types/send': 0.17.4 + + '@types/express@5.0.0': + dependencies: + '@types/body-parser': 1.19.5 + '@types/express-serve-static-core': 5.0.2 + '@types/qs': 6.9.17 + '@types/serve-static': 1.15.7 + + '@types/http-errors@2.0.4': {} + + '@types/mime@1.3.5': {} + + '@types/node@22.10.2': + dependencies: + undici-types: 6.20.0 + + '@types/qs@6.9.17': {} + + '@types/range-parser@1.2.7': {} + + '@types/send@0.17.4': + dependencies: + '@types/mime': 1.3.5 + '@types/node': 22.10.2 + + '@types/serve-static@1.15.7': + dependencies: + '@types/http-errors': 2.0.4 + '@types/node': 22.10.2 + '@types/send': 0.17.4 + + '@types/sha.js@2.4.4': + dependencies: + '@types/node': 22.10.2 + + '@types/uuid@9.0.8': {} + + accepts@1.3.8: + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + + array-flatten@1.1.1: {} + + body-parser@1.20.3: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.13.0 + raw-body: 2.5.2 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + bowser@2.11.0: {} + + bytes@3.1.2: {} + + call-bind-apply-helpers@1.0.1: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bound@1.0.3: + dependencies: + call-bind-apply-helpers: 1.0.1 + get-intrinsic: 1.2.6 + + content-disposition@0.5.4: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + + cookie-signature@1.0.6: {} + + cookie@0.7.1: {} + + debug@2.6.9: + dependencies: + ms: 2.0.0 + + depd@2.0.0: {} + + destroy@1.2.0: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + ee-first@1.1.1: {} + + encodeurl@1.0.2: {} + + encodeurl@2.0.0: {} + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.0.0: + dependencies: + es-errors: 1.3.0 + + esbuild@0.24.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.24.2 + '@esbuild/android-arm': 0.24.2 + '@esbuild/android-arm64': 0.24.2 + '@esbuild/android-x64': 0.24.2 + '@esbuild/darwin-arm64': 0.24.2 + '@esbuild/darwin-x64': 0.24.2 + '@esbuild/freebsd-arm64': 0.24.2 + '@esbuild/freebsd-x64': 0.24.2 + '@esbuild/linux-arm': 0.24.2 + '@esbuild/linux-arm64': 0.24.2 + '@esbuild/linux-ia32': 0.24.2 + '@esbuild/linux-loong64': 0.24.2 + '@esbuild/linux-mips64el': 0.24.2 + '@esbuild/linux-ppc64': 0.24.2 + '@esbuild/linux-riscv64': 0.24.2 + '@esbuild/linux-s390x': 0.24.2 + '@esbuild/linux-x64': 0.24.2 + '@esbuild/netbsd-arm64': 0.24.2 + '@esbuild/netbsd-x64': 0.24.2 + '@esbuild/openbsd-arm64': 0.24.2 + '@esbuild/openbsd-x64': 0.24.2 + '@esbuild/sunos-x64': 0.24.2 + '@esbuild/win32-arm64': 0.24.2 + '@esbuild/win32-ia32': 0.24.2 + '@esbuild/win32-x64': 0.24.2 + + escape-html@1.0.3: {} + + etag@1.8.1: {} + + express@4.21.2: + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.3 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.7.1 + cookie-signature: 1.0.6 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.3.1 + fresh: 0.5.2 + http-errors: 2.0.0 + merge-descriptors: 1.0.3 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.12 + proxy-addr: 2.0.7 + qs: 6.13.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.19.0 + serve-static: 1.16.2 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + fast-xml-parser@4.4.1: + dependencies: + strnum: 1.0.5 + + finalhandler@1.3.1: + dependencies: + debug: 2.6.9 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + forwarded@0.2.0: {} + + fresh@0.5.2: {} + + function-bind@1.1.2: {} + + get-intrinsic@1.2.6: + dependencies: + call-bind-apply-helpers: 1.0.1 + dunder-proto: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + function-bind: 1.1.2 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + gopd@1.2.0: {} + + has-symbols@1.1.0: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + + inherits@2.0.4: {} + + ipaddr.js@1.9.1: {} + + math-intrinsics@1.1.0: {} + + media-typer@0.3.0: {} + + merge-descriptors@1.0.3: {} + + methods@1.1.2: {} + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime@1.6.0: {} + + ms@2.0.0: {} + + ms@2.1.3: {} + + negotiator@0.6.3: {} + + object-inspect@1.13.3: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + parseurl@1.3.3: {} + + path-to-regexp@0.1.12: {} + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + qs@6.13.0: + dependencies: + side-channel: 1.1.0 + + range-parser@1.2.1: {} + + raw-body@2.5.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + + safe-buffer@5.2.1: {} + + safer-buffer@2.1.2: {} + + send@0.19.0: + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.0 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + + serve-static@1.16.2: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.19.0 + transitivePeerDependencies: + - supports-color + + setprototypeof@1.2.0: {} + + sha.js@2.4.11: + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.3 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.3 + es-errors: 1.3.0 + get-intrinsic: 1.2.6 + object-inspect: 1.13.3 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.3 + es-errors: 1.3.0 + get-intrinsic: 1.2.6 + object-inspect: 1.13.3 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.3 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + statuses@2.0.1: {} + + strnum@1.0.5: {} + + toidentifier@1.0.1: {} + + tslib@2.8.1: {} + + type-is@1.6.18: + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + + typescript@5.7.2: {} + + undici-types@6.20.0: {} + + unpipe@1.0.0: {} + + utils-merge@1.0.1: {} + + uuid@9.0.1: {} + + vary@1.1.2: {} diff --git a/script/build.js b/script/build.js new file mode 100644 index 0000000..7a83df5 --- /dev/null +++ b/script/build.js @@ -0,0 +1,9 @@ +const { build } = require("esbuild") + +build({ + entryPoints: ["src/main.ts"], + outfile: "dist/main.js", + platform: 'node', + bundle: true, + minify: true +}) diff --git a/sea-config.json b/sea-config.json new file mode 100644 index 0000000..76c3f2f --- /dev/null +++ b/sea-config.json @@ -0,0 +1,5 @@ +{ + "main": "dist/main.js", + "output": "dist/sea-prep.blob", + "disableExperimentalSEAWarning": true +} diff --git a/src/Memorizer.ts b/src/Memorizer.ts new file mode 100644 index 0000000..b1fe269 --- /dev/null +++ b/src/Memorizer.ts @@ -0,0 +1,38 @@ +import { Client } from '@smithy/smithy-client' +import shajs from 'sha.js' + +export class Memorizer { + private static memorized = new Map() + + public static memo (client: Client) { + const memorized = this.memorized.get(client.constructor.name) + + if (memorized !== undefined) + return memorized + + const newMemo = new Memorizer(client) + this.memorized.set(client.constructor.name, newMemo) + + return newMemo + } + + private memorized = new Map() + + private constructor ( + private client: Client + ) {} + + public readonly send: typeof this.client.send = async (command) => { + const serialized = JSON.stringify([command.constructor.name, command.input]) + const hashed = shajs('sha256').update(serialized).digest('hex') + + const memorized = this.memorized.get(hashed) + if (memorized !== undefined) + return memorized + + const newMemo = await this.client.send(command) + this.memorized.set(hashed, newMemo) + + return newMemo + } +} diff --git a/src/WebServer.ts b/src/WebServer.ts new file mode 100644 index 0000000..bd9985d --- /dev/null +++ b/src/WebServer.ts @@ -0,0 +1,17 @@ +import express from 'express' + +export class WebServer { + private readonly app = express() + + public WebServer () { + + } + + private initRoutes () { + + } + + public listen() { + this.app.listen() + } +} diff --git a/src/bpsets/BPSet.ts b/src/bpsets/BPSet.ts new file mode 100644 index 0000000..77c8665 --- /dev/null +++ b/src/bpsets/BPSet.ts @@ -0,0 +1,8 @@ +export interface BPSet { + check: () => Promise<{ + compliantResources: string[] + nonCompliantResources: string[] + requiredParametersForFix: {name: string}[] + }>, + fix: (nonCompliantResources: string[], requiredParametersForFix: {name: string, value: string}[]) => Promise +} diff --git a/src/bpsets/alb/ALBHttpDropInvalidHeaderEnabled.ts b/src/bpsets/alb/ALBHttpDropInvalidHeaderEnabled.ts new file mode 100644 index 0000000..b5e9665 --- /dev/null +++ b/src/bpsets/alb/ALBHttpDropInvalidHeaderEnabled.ts @@ -0,0 +1,72 @@ +import { + ElasticLoadBalancingV2Client, + DescribeLoadBalancersCommand, + DescribeLoadBalancerAttributesCommand, + ModifyLoadBalancerAttributesCommand +} from '@aws-sdk/client-elastic-load-balancing-v2' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class ALBHttpDropInvalidHeaderEnabled implements BPSet { + private readonly client = new ElasticLoadBalancingV2Client({}) + private readonly memoClient = Memorizer.memo(this.client) + + private readonly getLoadBalancers = async () => { + const response = await this.memoClient.send(new DescribeLoadBalancersCommand({})) + return response.LoadBalancers || [] + } + + private readonly getLoadBalancerAttributes = async ( + loadBalancerArn: string + ) => { + const response = await this.memoClient.send( + new DescribeLoadBalancerAttributesCommand({ LoadBalancerArn: loadBalancerArn }) + ) + return response.Attributes || [] + } + + public readonly check = async (): Promise<{ + compliantResources: string[] + nonCompliantResources: string[] + requiredParametersForFix: { name: string }[] + }> => { + const compliantResources: string[] = [] + const nonCompliantResources: string[] = [] + const loadBalancers = await this.getLoadBalancers() + + for (const lb of loadBalancers) { + const attributes = await this.getLoadBalancerAttributes(lb.LoadBalancerArn!) + const isEnabled = attributes.some( + attr => attr.Key === 'routing.http.drop_invalid_header_fields.enabled' && attr.Value === 'true' + ) + + if (isEnabled) { + compliantResources.push(lb.LoadBalancerArn!) + } else { + nonCompliantResources.push(lb.LoadBalancerArn!) + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [] + } + } + + public readonly fix = async ( + nonCompliantResources: string[], + requiredParametersForFix: { name: string; value: string }[] + ): Promise => { + for (const lbArn of nonCompliantResources) { + await this.client.send( + new ModifyLoadBalancerAttributesCommand({ + LoadBalancerArn: lbArn, + Attributes: [ + { Key: 'routing.http.drop_invalid_header_fields.enabled', Value: 'true' } + ] + }) + ) + } + } +} diff --git a/src/bpsets/alb/ALBWAFEnabled.ts b/src/bpsets/alb/ALBWAFEnabled.ts new file mode 100644 index 0000000..647416b --- /dev/null +++ b/src/bpsets/alb/ALBWAFEnabled.ts @@ -0,0 +1,62 @@ +import { ElasticLoadBalancingV2Client, DescribeLoadBalancersCommand } from '@aws-sdk/client-elastic-load-balancing-v2' +import { WAFV2Client, GetWebACLForResourceCommand, AssociateWebACLCommand } from '@aws-sdk/client-wafv2' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class ALBWAFEnabled implements BPSet { + private readonly client = new ElasticLoadBalancingV2Client({}) + private readonly memoClient = Memorizer.memo(this.client) + private readonly wafClient = Memorizer.memo(new WAFV2Client({})) + + private readonly getLoadBalancers = async () => { + const response = await this.memoClient.send(new DescribeLoadBalancersCommand({})) + return response.LoadBalancers || [] + } + + public readonly check = async (): Promise<{ + compliantResources: string[] + nonCompliantResources: string[] + requiredParametersForFix: { name: string }[] + }> => { + const compliantResources: string[] = [] + const nonCompliantResources: string[] = [] + const loadBalancers = await this.getLoadBalancers() + + for (const lb of loadBalancers) { + const response = await this.wafClient.send( + new GetWebACLForResourceCommand({ ResourceArn: lb.LoadBalancerArn }) + ) + if (response.WebACL) { + compliantResources.push(lb.LoadBalancerArn!) + } else { + nonCompliantResources.push(lb.LoadBalancerArn!) + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [{ name: 'web-acl-arn' }] + } + } + + public readonly fix = async ( + nonCompliantResources: string[], + requiredParametersForFix: { name: string; value: string }[] + ): Promise => { + const webAclArn = requiredParametersForFix.find(param => param.name === 'web-acl-arn')?.value + + if (!webAclArn) { + throw new Error("Required parameter 'web-acl-arn' is missing.") + } + + for (const lbArn of nonCompliantResources) { + await this.wafClient.send( + new AssociateWebACLCommand({ + ResourceArn: lbArn, + WebACLArn: webAclArn + }) + ) + } + } +} diff --git a/src/bpsets/alb/ELBCrossZoneLoadBalancingEnabled.ts b/src/bpsets/alb/ELBCrossZoneLoadBalancingEnabled.ts new file mode 100644 index 0000000..aa290f6 --- /dev/null +++ b/src/bpsets/alb/ELBCrossZoneLoadBalancingEnabled.ts @@ -0,0 +1,64 @@ +import { + ElasticLoadBalancingV2Client, + DescribeLoadBalancersCommand, + DescribeLoadBalancerAttributesCommand, + ModifyLoadBalancerAttributesCommand +} from '@aws-sdk/client-elastic-load-balancing-v2' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class ELBCrossZoneLoadBalancingEnabled implements BPSet { + private readonly client = new ElasticLoadBalancingV2Client({}) + private readonly memoClient = Memorizer.memo(this.client) + + private readonly getLoadBalancers = async () => { + const response = await this.memoClient.send(new DescribeLoadBalancersCommand({})) + return response.LoadBalancers || [] + } + + private readonly getLoadBalancerAttributes = async (loadBalancerArn: string) => { + const response = await this.memoClient.send( + new DescribeLoadBalancerAttributesCommand({ LoadBalancerArn: loadBalancerArn }) + ) + return response.Attributes || [] + } + + public readonly check = async () => { + const compliantResources = [] + const nonCompliantResources = [] + const loadBalancers = await this.getLoadBalancers() + + for (const lb of loadBalancers) { + const attributes = await this.getLoadBalancerAttributes(lb.LoadBalancerArn!) + const isEnabled = attributes.some( + attr => attr.Key === 'load_balancing.cross_zone.enabled' && attr.Value === 'true' + ) + + if (isEnabled) { + compliantResources.push(lb.LoadBalancerArn!) + } else { + nonCompliantResources.push(lb.LoadBalancerArn!) + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [] + } + } + + public readonly fix = async ( + nonCompliantResources: string[], + requiredParametersForFix: { name: string; value: string }[] + ) => { + for (const lbArn of nonCompliantResources) { + await this.client.send( + new ModifyLoadBalancerAttributesCommand({ + LoadBalancerArn: lbArn, + Attributes: [{ Key: 'load_balancing.cross_zone.enabled', Value: 'true' }] + }) + ) + } + } +} diff --git a/src/bpsets/alb/ELBDeletionProtectionEnabled.ts b/src/bpsets/alb/ELBDeletionProtectionEnabled.ts new file mode 100644 index 0000000..1000709 --- /dev/null +++ b/src/bpsets/alb/ELBDeletionProtectionEnabled.ts @@ -0,0 +1,64 @@ +import { + ElasticLoadBalancingV2Client, + DescribeLoadBalancersCommand, + DescribeLoadBalancerAttributesCommand, + ModifyLoadBalancerAttributesCommand +} from '@aws-sdk/client-elastic-load-balancing-v2' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class ELBDeletionProtectionEnabled implements BPSet { + private readonly client = new ElasticLoadBalancingV2Client({}) + private readonly memoClient = Memorizer.memo(this.client) + + private readonly getLoadBalancers = async () => { + const response = await this.memoClient.send(new DescribeLoadBalancersCommand({})) + return response.LoadBalancers || [] + } + + private readonly getLoadBalancerAttributes = async (loadBalancerArn: string) => { + const response = await this.memoClient.send( + new DescribeLoadBalancerAttributesCommand({ LoadBalancerArn: loadBalancerArn }) + ) + return response.Attributes || [] + } + + public readonly check = async () => { + const compliantResources = [] + const nonCompliantResources = [] + const loadBalancers = await this.getLoadBalancers() + + for (const lb of loadBalancers) { + const attributes = await this.getLoadBalancerAttributes(lb.LoadBalancerArn!) + const isEnabled = attributes.some( + attr => attr.Key === 'deletion_protection.enabled' && attr.Value === 'true' + ) + + if (isEnabled) { + compliantResources.push(lb.LoadBalancerArn!) + } else { + nonCompliantResources.push(lb.LoadBalancerArn!) + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [] + } + } + + public readonly fix = async ( + nonCompliantResources: string[], + requiredParametersForFix: { name: string; value: string }[] + ) => { + for (const lbArn of nonCompliantResources) { + await this.client.send( + new ModifyLoadBalancerAttributesCommand({ + LoadBalancerArn: lbArn, + Attributes: [{ Key: 'deletion_protection.enabled', Value: 'true' }] + }) + ) + } + } +} diff --git a/src/bpsets/alb/ELBLoggingEnabled.ts b/src/bpsets/alb/ELBLoggingEnabled.ts new file mode 100644 index 0000000..e7e681f --- /dev/null +++ b/src/bpsets/alb/ELBLoggingEnabled.ts @@ -0,0 +1,75 @@ +import { + ElasticLoadBalancingV2Client, + DescribeLoadBalancersCommand, + DescribeLoadBalancerAttributesCommand, + ModifyLoadBalancerAttributesCommand +} from '@aws-sdk/client-elastic-load-balancing-v2' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class ELBLoggingEnabled implements BPSet { + private readonly client = new ElasticLoadBalancingV2Client({}) + private readonly memoClient = Memorizer.memo(this.client) + + private readonly getLoadBalancers = async () => { + const response = await this.memoClient.send(new DescribeLoadBalancersCommand({})) + return response.LoadBalancers || [] + } + + private readonly getLoadBalancerAttributes = async (loadBalancerArn: string) => { + const response = await this.memoClient.send( + new DescribeLoadBalancerAttributesCommand({ LoadBalancerArn: loadBalancerArn }) + ) + return response.Attributes || [] + } + + public readonly check = async () => { + const compliantResources = [] + const nonCompliantResources = [] + const loadBalancers = await this.getLoadBalancers() + + for (const lb of loadBalancers) { + const attributes = await this.getLoadBalancerAttributes(lb.LoadBalancerArn!) + const isEnabled = attributes.some( + attr => attr.Key === 'access_logs.s3.enabled' && attr.Value === 'true' + ) + + if (isEnabled) { + compliantResources.push(lb.LoadBalancerArn!) + } else { + nonCompliantResources.push(lb.LoadBalancerArn!) + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [{ name: 's3-bucket-name' }, { name: 's3-prefix' }] + } + } + + public readonly fix = async ( + nonCompliantResources: string[], + requiredParametersForFix: { name: string; value: string }[] + ) => { + const bucketName = requiredParametersForFix.find(param => param.name === 's3-bucket-name')?.value + const bucketPrefix = requiredParametersForFix.find(param => param.name === 's3-prefix')?.value + + if (!bucketName || !bucketPrefix) { + throw new Error("Required parameters 's3-bucket-name' and/or 's3-prefix' are missing.") + } + + for (const lbArn of nonCompliantResources) { + await this.client.send( + new ModifyLoadBalancerAttributesCommand({ + LoadBalancerArn: lbArn, + Attributes: [ + { Key: 'access_logs.s3.enabled', Value: 'true' }, + { Key: 'access_logs.s3.bucket', Value: bucketName }, + { Key: 'access_logs.s3.prefix', Value: bucketPrefix } + ] + }) + ) + } + } +} diff --git a/src/bpsets/apigw/APIGatewayAssociatedWithWAF.ts b/src/bpsets/apigw/APIGatewayAssociatedWithWAF.ts new file mode 100644 index 0000000..a8e21e4 --- /dev/null +++ b/src/bpsets/apigw/APIGatewayAssociatedWithWAF.ts @@ -0,0 +1,70 @@ +import { + ApiGatewayV2Client, + GetApisCommand, + GetStagesCommand +} from '@aws-sdk/client-apigatewayv2' +import { WAFV2Client, GetWebACLForResourceCommand, AssociateWebACLCommand } from '@aws-sdk/client-wafv2' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class APIGatewayAssociatedWithWAF implements BPSet { + private readonly client = new ApiGatewayV2Client({}) + private readonly memoClient = Memorizer.memo(this.client) + private readonly wafClient = Memorizer.memo(new WAFV2Client({})) + + private readonly getHttpApis = async () => { + const response = await this.memoClient.send(new GetApisCommand({})) + return response.Items || [] + } + + private readonly getStages = async (apiId: string) => { + const response = await this.memoClient.send(new GetStagesCommand({ ApiId: apiId })) + return response.Items || [] + } + + public readonly check = async () => { + const compliantResources = [] + const nonCompliantResources = [] + const apis = await this.getHttpApis() + + for (const api of apis) { + const stages = await this.getStages(api.ApiId!) + for (const stage of stages) { + const stageArn = `arn:aws:apigateway:${this.client.config.region}::/apis/${api.ApiId}/stages/${stage.StageName}` + const response = await this.wafClient.send(new GetWebACLForResourceCommand({ ResourceArn: stageArn })) + + if (response.WebACL) { + compliantResources.push(stageArn) + } else { + nonCompliantResources.push(stageArn) + } + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [{ name: 'web-acl-arn' }] + } + } + + public readonly fix = async ( + nonCompliantResources: string[], + requiredParametersForFix: { name: string; value: string }[] + ) => { + const webAclArn = requiredParametersForFix.find(param => param.name === 'web-acl-arn')?.value + + if (!webAclArn) { + throw new Error("Required parameter 'web-acl-arn' is missing.") + } + + for (const stageArn of nonCompliantResources) { + await this.wafClient.send( + new AssociateWebACLCommand({ + ResourceArn: stageArn, + WebACLArn: webAclArn + }) + ) + } + } +} diff --git a/src/bpsets/apigw/APIGatewayExecutionLoggingEnabled.ts b/src/bpsets/apigw/APIGatewayExecutionLoggingEnabled.ts new file mode 100644 index 0000000..998c1bf --- /dev/null +++ b/src/bpsets/apigw/APIGatewayExecutionLoggingEnabled.ts @@ -0,0 +1,75 @@ +import { + ApiGatewayV2Client, + GetApisCommand, + GetStagesCommand, + UpdateStageCommand +} from '@aws-sdk/client-apigatewayv2' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class APIGatewayExecutionLoggingEnabled implements BPSet { + private readonly client = new ApiGatewayV2Client({}) + private readonly memoClient = Memorizer.memo(this.client) + + private readonly getHttpApis = async () => { + const response = await this.memoClient.send(new GetApisCommand({})) + return response.Items || [] + } + + private readonly getStages = async (apiId: string) => { + const response = await this.memoClient.send(new GetStagesCommand({ ApiId: apiId })) + return response.Items || [] + } + + public readonly check = async () => { + const compliantResources = [] + const nonCompliantResources = [] + const apis = await this.getHttpApis() + + for (const api of apis) { + const stages = await this.getStages(api.ApiId!) + for (const stage of stages) { + const stageArn = `arn:aws:apigateway:${this.client.config.region}::/apis/${api.ApiId}/stages/${stage.StageName}` + const loggingLevel = stage.AccessLogSettings?.Format + + if (loggingLevel && loggingLevel !== 'OFF') { + compliantResources.push(stageArn) + } else { + nonCompliantResources.push(stageArn) + } + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [{ name: 'log-destination-arn' }] + } + } + + public readonly fix = async ( + nonCompliantResources: string[], + requiredParametersForFix: { name: string; value: string }[] + ) => { + const logDestinationArn = requiredParametersForFix.find(param => param.name === 'log-destination-arn')?.value + + if (!logDestinationArn) { + throw new Error("Required parameter 'log-destination-arn' is missing.") + } + + for (const stageArn of nonCompliantResources) { + const [apiId, stageName] = stageArn.split('/').slice(-2) + + await this.client.send( + new UpdateStageCommand({ + ApiId: apiId, + StageName: stageName, + AccessLogSettings: { + DestinationArn: logDestinationArn, + Format: '$context.requestId' + } + }) + ) + } + } +} diff --git a/src/bpsets/apigw/APIGatewayV2AccessLogsEnabled.ts b/src/bpsets/apigw/APIGatewayV2AccessLogsEnabled.ts new file mode 100644 index 0000000..e71f7e8 --- /dev/null +++ b/src/bpsets/apigw/APIGatewayV2AccessLogsEnabled.ts @@ -0,0 +1,76 @@ +import { + ApiGatewayV2Client, + GetApisCommand, + GetStagesCommand, + UpdateStageCommand +} from '@aws-sdk/client-apigatewayv2' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class APIGatewayV2AccessLogsEnabled implements BPSet { + private readonly client = new ApiGatewayV2Client({}) + private readonly memoClient = Memorizer.memo(this.client) + + private readonly getHttpApis = async () => { + const response = await this.memoClient.send(new GetApisCommand({})) + return response.Items || [] + } + + private readonly getStages = async (apiId: string) => { + const response = await this.memoClient.send(new GetStagesCommand({ ApiId: apiId })) + return response.Items || [] + } + + public readonly check = async () => { + const compliantResources = [] + const nonCompliantResources = [] + const apis = await this.getHttpApis() + + for (const api of apis) { + const stages = await this.getStages(api.ApiId!) + for (const stage of stages) { + const stageIdentifier = `${api.Name!} / ${stage.StageName!}` + if (!stage.AccessLogSettings) { + nonCompliantResources.push(stageIdentifier) + } else { + compliantResources.push(stageIdentifier) + } + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [{ name: 'log-destination-arn' }] + } + } + + public readonly fix = async ( + nonCompliantResources: string[], + requiredParametersForFix: { name: string; value: string }[] + ) => { + const logDestinationArn = requiredParametersForFix.find(param => param.name === 'log-destination-arn')?.value + + if (!logDestinationArn) { + throw new Error("Required parameter 'log-destination-arn' is missing.") + } + + for (const resource of nonCompliantResources) { + const [apiName, stageName] = resource.split(' / ') + const api = (await this.getHttpApis()).find(a => a.Name === apiName) + + if (!api) continue + + await this.client.send( + new UpdateStageCommand({ + ApiId: api.ApiId!, + StageName: stageName, + AccessLogSettings: { + DestinationArn: logDestinationArn, + Format: '$context.requestId' + } + }) + ) + } + } +} diff --git a/src/bpsets/apigw/APIGatewayV2AuthorizationTypeConfigured.ts b/src/bpsets/apigw/APIGatewayV2AuthorizationTypeConfigured.ts new file mode 100644 index 0000000..a35fc2d --- /dev/null +++ b/src/bpsets/apigw/APIGatewayV2AuthorizationTypeConfigured.ts @@ -0,0 +1,78 @@ +import { + ApiGatewayV2Client, + GetApisCommand, + GetRoutesCommand, + UpdateRouteCommand +} from '@aws-sdk/client-apigatewayv2' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class APIGatewayV2AuthorizationTypeConfigured implements BPSet { + private readonly client = new ApiGatewayV2Client({}) + private readonly memoClient = Memorizer.memo(this.client) + + private readonly getHttpApis = async () => { + const response = await this.memoClient.send(new GetApisCommand({})) + return response.Items || [] + } + + private readonly getRoutes = async (apiId: string) => { + const response = await this.memoClient.send(new GetRoutesCommand({ ApiId: apiId })) + return response.Items || [] + } + + public readonly check = async () => { + const compliantResources = [] + const nonCompliantResources = [] + const apis = await this.getHttpApis() + + for (const api of apis) { + const routes = await this.getRoutes(api.ApiId!) + for (const route of routes) { + const routeIdentifier = `${api.Name!} / ${route.RouteKey!}` + if (route.AuthorizationType === 'NONE') { + nonCompliantResources.push(routeIdentifier) + } else { + compliantResources.push(routeIdentifier) + } + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [{ name: 'authorization-type' }] + } + } + + public readonly fix = async ( + nonCompliantResources: string[], + requiredParametersForFix: { name: string; value: string }[] + ) => { + const authorizationType = requiredParametersForFix.find(param => param.name === 'authorization-type')?.value + + if (!authorizationType) { + throw new Error("Required parameter 'authorization-type' is missing.") + } + + for (const resource of nonCompliantResources) { + const [apiName, routeKey] = resource.split(' / ') + const api = (await this.getHttpApis()).find(a => a.Name === apiName) + + if (!api) continue + + const routes = await this.getRoutes(api.ApiId!) + const route = routes.find(r => r.RouteKey === routeKey) + + if (!route) continue + + await this.client.send( + new UpdateRouteCommand({ + ApiId: api.ApiId!, + RouteId: route.RouteId!, // Use RouteId instead of RouteKey + AuthorizationType: authorizationType as any + }) + ) + } + } +} diff --git a/src/bpsets/asg/AutoScalingGroupELBHealthCheckRequired.ts b/src/bpsets/asg/AutoScalingGroupELBHealthCheckRequired.ts new file mode 100644 index 0000000..7984cdc --- /dev/null +++ b/src/bpsets/asg/AutoScalingGroupELBHealthCheckRequired.ts @@ -0,0 +1,48 @@ +import { AutoScalingClient, DescribeAutoScalingGroupsCommand, UpdateAutoScalingGroupCommand } from '@aws-sdk/client-auto-scaling' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class AutoScalingGroupELBHealthCheckRequired implements BPSet { + private readonly client = new AutoScalingClient({}) + private readonly memoClient = Memorizer.memo(this.client) + + private readonly getAutoScalingGroups = async () => { + const response = await this.memoClient.send(new DescribeAutoScalingGroupsCommand({})) + return response.AutoScalingGroups || [] + } + + public readonly check = async () => { + const compliantResources = [] + const nonCompliantResources = [] + const asgs = await this.getAutoScalingGroups() + + for (const asg of asgs) { + if ( + (asg.LoadBalancerNames?.length || asg.TargetGroupARNs?.length) && + asg.HealthCheckType !== 'ELB' + ) { + nonCompliantResources.push(asg.AutoScalingGroupARN!) + } else { + compliantResources.push(asg.AutoScalingGroupARN!) + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [] + } + } + + public readonly fix = async (nonCompliantResources: string[]) => { + for (const asgArn of nonCompliantResources) { + const asgName = asgArn.split(':').pop()! + await this.client.send( + new UpdateAutoScalingGroupCommand({ + AutoScalingGroupName: asgName, + HealthCheckType: 'ELB' + }) + ) + } + } +} diff --git a/src/bpsets/asg/AutoScalingLaunchTemplate.ts b/src/bpsets/asg/AutoScalingLaunchTemplate.ts new file mode 100644 index 0000000..1ebad9d --- /dev/null +++ b/src/bpsets/asg/AutoScalingLaunchTemplate.ts @@ -0,0 +1,58 @@ +import { AutoScalingClient, DescribeAutoScalingGroupsCommand, UpdateAutoScalingGroupCommand } from '@aws-sdk/client-auto-scaling' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class AutoScalingLaunchTemplate implements BPSet { + private readonly client = new AutoScalingClient({}) + private readonly memoClient = Memorizer.memo(this.client) + + private readonly getAutoScalingGroups = async () => { + const response = await this.memoClient.send(new DescribeAutoScalingGroupsCommand({})) + return response.AutoScalingGroups || [] + } + + public readonly check = async () => { + const compliantResources = [] + const nonCompliantResources = [] + const asgs = await this.getAutoScalingGroups() + + for (const asg of asgs) { + if (asg.LaunchConfigurationName) { + nonCompliantResources.push(asg.AutoScalingGroupARN!) + } else { + compliantResources.push(asg.AutoScalingGroupARN!) + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [{ name: 'launch-template-id' }, { name: 'version' }] + } + } + + public readonly fix = async ( + nonCompliantResources: string[], + requiredParametersForFix: { name: string; value: string }[] + ) => { + const launchTemplateId = requiredParametersForFix.find(param => param.name === 'launch-template-id')?.value + const version = requiredParametersForFix.find(param => param.name === 'version')?.value + + if (!launchTemplateId || !version) { + throw new Error("Required parameters 'launch-template-id' and/or 'version' are missing.") + } + + for (const asgArn of nonCompliantResources) { + const asgName = asgArn.split(':').pop()! + await this.client.send( + new UpdateAutoScalingGroupCommand({ + AutoScalingGroupName: asgName, + LaunchTemplate: { + LaunchTemplateId: launchTemplateId, + Version: version + } + }) + ) + } + } +} diff --git a/src/bpsets/asg/AutoScalingMultipleAZ.ts b/src/bpsets/asg/AutoScalingMultipleAZ.ts new file mode 100644 index 0000000..45b329b --- /dev/null +++ b/src/bpsets/asg/AutoScalingMultipleAZ.ts @@ -0,0 +1,54 @@ +import { AutoScalingClient, DescribeAutoScalingGroupsCommand, UpdateAutoScalingGroupCommand } from '@aws-sdk/client-auto-scaling' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class AutoScalingMultipleAZ implements BPSet { + private readonly client = new AutoScalingClient({}) + private readonly memoClient = Memorizer.memo(this.client) + + private readonly getAutoScalingGroups = async () => { + const response = await this.memoClient.send(new DescribeAutoScalingGroupsCommand({})) + return response.AutoScalingGroups || [] + } + + public readonly check = async () => { + const compliantResources = [] + const nonCompliantResources = [] + const asgs = await this.getAutoScalingGroups() + + for (const asg of asgs) { + if (asg.AvailabilityZones?.length! > 1) { + compliantResources.push(asg.AutoScalingGroupARN!) + } else { + nonCompliantResources.push(asg.AutoScalingGroupARN!) + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [{ name: 'availability-zones' }] + } + } + + public readonly fix = async ( + nonCompliantResources: string[], + requiredParametersForFix: { name: string; value: string }[] + ) => { + const availabilityZones = requiredParametersForFix.find(param => param.name === 'availability-zones')?.value + + if (!availabilityZones) { + throw new Error("Required parameter 'availability-zones' is missing.") + } + + for (const asgArn of nonCompliantResources) { + const asgName = asgArn.split(':').pop()! + await this.client.send( + new UpdateAutoScalingGroupCommand({ + AutoScalingGroupName: asgName, + AvailabilityZones: availabilityZones.split(',') + }) + ) + } + } +} diff --git a/src/bpsets/cloudfront/CloudFrontAccessLogsEnabled.ts b/src/bpsets/cloudfront/CloudFrontAccessLogsEnabled.ts new file mode 100644 index 0000000..29d3d08 --- /dev/null +++ b/src/bpsets/cloudfront/CloudFrontAccessLogsEnabled.ts @@ -0,0 +1,85 @@ +import { + CloudFrontClient, + ListDistributionsCommand, + GetDistributionCommand, + UpdateDistributionCommand +} from '@aws-sdk/client-cloudfront' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class CloudFrontAccessLogsEnabled implements BPSet { + private readonly client = new CloudFrontClient({}) + private readonly memoClient = Memorizer.memo(this.client) + + private readonly getDistributions = async () => { + const response = await this.memoClient.send(new ListDistributionsCommand({})) + return response.DistributionList?.Items || [] + } + + private readonly getDistributionDetails = async (distributionId: string) => { + const response = await this.memoClient.send( + new GetDistributionCommand({ Id: distributionId }) + ) + return { + distribution: response.Distribution!, + etag: response.ETag! + } + } + + public readonly check = async () => { + const compliantResources = [] + const nonCompliantResources = [] + const distributions = await this.getDistributions() + + for (const distribution of distributions) { + const { distribution: details } = await this.getDistributionDetails(distribution.Id!) + if ( + details.DistributionConfig?.Logging?.Enabled + ) { + compliantResources.push(details.ARN!) + } else { + nonCompliantResources.push(details.ARN!) + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [{ name: 'log-bucket-name' }] + } + } + + public readonly fix = async ( + nonCompliantResources: string[], + requiredParametersForFix: { name: string; value: string }[] + ) => { + const logBucketName = requiredParametersForFix.find(param => param.name === 'log-bucket-name')?.value + + if (!logBucketName) { + throw new Error("Required parameter 'log-bucket-name' is missing.") + } + + for (const arn of nonCompliantResources) { + const distributionId = arn.split('/').pop()! + const { distribution, etag } = await this.getDistributionDetails(distributionId) + + const updatedConfig = { + ...distribution.DistributionConfig, + Logging: { + Enabled: true, + Bucket: logBucketName, + IncludeCookies: false, + Prefix: '' + } + } + + await this.client.send( + new UpdateDistributionCommand({ + Id: distributionId, + IfMatch: etag, + DistributionConfig: updatedConfig as any // Include all required properties + }) + ) + } + } +} diff --git a/src/bpsets/cloudfront/CloudFrontAssociatedWithWAF.ts b/src/bpsets/cloudfront/CloudFrontAssociatedWithWAF.ts new file mode 100644 index 0000000..5edaf41 --- /dev/null +++ b/src/bpsets/cloudfront/CloudFrontAssociatedWithWAF.ts @@ -0,0 +1,77 @@ +import { + CloudFrontClient, + ListDistributionsCommand, + GetDistributionCommand, + UpdateDistributionCommand +} from '@aws-sdk/client-cloudfront' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class CloudFrontAssociatedWithWAF implements BPSet { + private readonly client = new CloudFrontClient({}) + private readonly memoClient = Memorizer.memo(this.client) + + private readonly getDistributions = async () => { + const response = await this.memoClient.send(new ListDistributionsCommand({})) + return response.DistributionList?.Items || [] + } + + private readonly getDistributionDetails = async (distributionId: string) => { + const response = await this.memoClient.send( + new GetDistributionCommand({ Id: distributionId }) + ) + return { + distribution: response.Distribution!, + etag: response.ETag! + } + } + + public readonly check = async () => { + const compliantResources = [] + const nonCompliantResources = [] + const distributions = await this.getDistributions() + + for (const distribution of distributions) { + if (distribution.WebACLId && distribution.WebACLId !== '') { + compliantResources.push(distribution.ARN!) + } else { + nonCompliantResources.push(distribution.ARN!) + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [{ name: 'web-acl-id' }] + } + } + + public readonly fix = async ( + nonCompliantResources: string[], + requiredParametersForFix: { name: string; value: string }[] + ) => { + const webAclId = requiredParametersForFix.find(param => param.name === 'web-acl-id')?.value + + if (!webAclId) { + throw new Error("Required parameter 'web-acl-id' is missing.") + } + + for (const arn of nonCompliantResources) { + const distributionId = arn.split('/').pop()! + const { distribution, etag } = await this.getDistributionDetails(distributionId) + + const updatedConfig = { + ...distribution.DistributionConfig, + WebACLId: webAclId + } + + await this.client.send( + new UpdateDistributionCommand({ + Id: distributionId, + IfMatch: etag, + DistributionConfig: updatedConfig as any // Include all required properties + }) + ) + } + } +} diff --git a/src/bpsets/cloudfront/CloudFrontDefaultRootObjectConfigured.ts b/src/bpsets/cloudfront/CloudFrontDefaultRootObjectConfigured.ts new file mode 100644 index 0000000..5f20350 --- /dev/null +++ b/src/bpsets/cloudfront/CloudFrontDefaultRootObjectConfigured.ts @@ -0,0 +1,78 @@ +import { + CloudFrontClient, + ListDistributionsCommand, + GetDistributionCommand, + UpdateDistributionCommand +} from '@aws-sdk/client-cloudfront' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class CloudFrontDefaultRootObjectConfigured implements BPSet { + private readonly client = new CloudFrontClient({}) + private readonly memoClient = Memorizer.memo(this.client) + + private readonly getDistributions = async () => { + const response = await this.memoClient.send(new ListDistributionsCommand({})) + return response.DistributionList?.Items || [] + } + + private readonly getDistributionDetails = async (distributionId: string) => { + const response = await this.memoClient.send( + new GetDistributionCommand({ Id: distributionId }) + ) + return { + distribution: response.Distribution!, + etag: response.ETag! + } + } + + public readonly check = async () => { + const compliantResources = [] + const nonCompliantResources = [] + const distributions = await this.getDistributions() + + for (const distribution of distributions) { + const { distribution: details } = await this.getDistributionDetails(distribution.Id!) + if (details.DistributionConfig?.DefaultRootObject !== '') { + compliantResources.push(details.ARN!) + } else { + nonCompliantResources.push(details.ARN!) + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [{ name: 'default-root-object' }] + } + } + + public readonly fix = async ( + nonCompliantResources: string[], + requiredParametersForFix: { name: string; value: string }[] + ) => { + const defaultRootObject = requiredParametersForFix.find(param => param.name === 'default-root-object')?.value + + if (!defaultRootObject) { + throw new Error("Required parameter 'default-root-object' is missing.") + } + + for (const arn of nonCompliantResources) { + const distributionId = arn.split('/').pop()! + const { distribution, etag } = await this.getDistributionDetails(distributionId) + + const updatedConfig = { + ...distribution.DistributionConfig, + DefaultRootObject: defaultRootObject + } + + await this.client.send( + new UpdateDistributionCommand({ + Id: distributionId, + IfMatch: etag, + DistributionConfig: updatedConfig as any + }) + ) + } + } +} diff --git a/src/bpsets/cloudfront/CloudFrontNoDeprecatedSSLProtocols.ts b/src/bpsets/cloudfront/CloudFrontNoDeprecatedSSLProtocols.ts new file mode 100644 index 0000000..ff31cff --- /dev/null +++ b/src/bpsets/cloudfront/CloudFrontNoDeprecatedSSLProtocols.ts @@ -0,0 +1,93 @@ +import { + CloudFrontClient, + ListDistributionsCommand, + GetDistributionCommand, + UpdateDistributionCommand +} from '@aws-sdk/client-cloudfront' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class CloudFrontNoDeprecatedSSLProtocols implements BPSet { + private readonly client = new CloudFrontClient({}) + private readonly memoClient = Memorizer.memo(this.client) + + private readonly getDistributions = async () => { + const response = await this.memoClient.send(new ListDistributionsCommand({})) + return response.DistributionList?.Items || [] + } + + private readonly getDistributionDetails = async (distributionId: string) => { + const response = await this.memoClient.send( + new GetDistributionCommand({ Id: distributionId }) + ) + return { + distribution: response.Distribution!, + etag: response.ETag! + } + } + + public readonly check = async () => { + const compliantResources = [] + const nonCompliantResources = [] + const distributions = await this.getDistributions() + + for (const distribution of distributions) { + const { distribution: details } = await this.getDistributionDetails(distribution.Id!) + const hasDeprecatedSSL = details.DistributionConfig?.Origins?.Items?.some( + origin => + origin.CustomOriginConfig && + origin.CustomOriginConfig.OriginSslProtocols?.Items?.includes('SSLv3') + ) + + if (hasDeprecatedSSL) { + nonCompliantResources.push(details.ARN!) + } else { + compliantResources.push(details.ARN!) + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [] + } + } + + public readonly fix = async (nonCompliantResources: string[]) => { + for (const arn of nonCompliantResources) { + const distributionId = arn.split('/').pop()! + const { distribution, etag } = await this.getDistributionDetails(distributionId) + + const updatedConfig = { + ...distribution.DistributionConfig, + Origins: { + Items: distribution.DistributionConfig?.Origins?.Items?.map(origin => { + if (origin.CustomOriginConfig) { + return { + ...origin, + CustomOriginConfig: { + ...origin.CustomOriginConfig, + OriginSslProtocols: { + ...origin.CustomOriginConfig.OriginSslProtocols, + Items: origin.CustomOriginConfig.OriginSslProtocols?.Items?.filter( + protocol => protocol !== 'SSLv3' + ) + } + } + } + } + return origin + }) + } + } + + await this.client.send( + new UpdateDistributionCommand({ + Id: distributionId, + IfMatch: etag, + DistributionConfig: updatedConfig as any + }) + ) + } + } +} diff --git a/src/bpsets/cloudfront/CloudFrontS3OriginAccessControlEnabled.ts b/src/bpsets/cloudfront/CloudFrontS3OriginAccessControlEnabled.ts new file mode 100644 index 0000000..8b22494 --- /dev/null +++ b/src/bpsets/cloudfront/CloudFrontS3OriginAccessControlEnabled.ts @@ -0,0 +1,96 @@ +import { + CloudFrontClient, + ListDistributionsCommand, + GetDistributionCommand, + UpdateDistributionCommand +} from '@aws-sdk/client-cloudfront' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class CloudFrontS3OriginAccessControlEnabled implements BPSet { + private readonly client = new CloudFrontClient({}) + private readonly memoClient = Memorizer.memo(this.client) + + private readonly getDistributions = async () => { + const response = await this.memoClient.send(new ListDistributionsCommand({})) + return response.DistributionList?.Items || [] + } + + private readonly getDistributionDetails = async (distributionId: string) => { + const response = await this.memoClient.send( + new GetDistributionCommand({ Id: distributionId }) + ) + return { + distribution: response.Distribution!, + etag: response.ETag! + } + } + + public readonly check = async () => { + const compliantResources = [] + const nonCompliantResources = [] + const distributions = await this.getDistributions() + + for (const distribution of distributions) { + const { distribution: details } = await this.getDistributionDetails(distribution.Id!) + const hasNonCompliantOrigin = details.DistributionConfig?.Origins?.Items?.some( + origin => + origin.S3OriginConfig && + (!origin.OriginAccessControlId || origin.OriginAccessControlId === '') + ) + + if (hasNonCompliantOrigin) { + nonCompliantResources.push(details.ARN!) + } else { + compliantResources.push(details.ARN!) + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [{ name: 'origin-access-control-id' }] + } + } + + public readonly fix = async ( + nonCompliantResources: string[], + requiredParametersForFix: { name: string; value: string }[] + ) => { + const originAccessControlId = requiredParametersForFix.find( + param => param.name === 'origin-access-control-id' + )?.value + + if (!originAccessControlId) { + throw new Error("Required parameter 'origin-access-control-id' is missing.") + } + + for (const arn of nonCompliantResources) { + const distributionId = arn.split('/').pop()! + const { distribution, etag } = await this.getDistributionDetails(distributionId) + + const updatedConfig = { + ...distribution.DistributionConfig, + Origins: { + Items: distribution.DistributionConfig?.Origins?.Items?.map(origin => { + if (origin.S3OriginConfig) { + return { + ...origin, + OriginAccessControlId: originAccessControlId + } + } + return origin + }) + } + } + + await this.client.send( + new UpdateDistributionCommand({ + Id: distributionId, + IfMatch: etag, + DistributionConfig: updatedConfig as any + }) + ) + } + } +} diff --git a/src/bpsets/cloudfront/CloudFrontViewerPolicyHTTPS.ts b/src/bpsets/cloudfront/CloudFrontViewerPolicyHTTPS.ts new file mode 100644 index 0000000..cafe64a --- /dev/null +++ b/src/bpsets/cloudfront/CloudFrontViewerPolicyHTTPS.ts @@ -0,0 +1,84 @@ +import { + CloudFrontClient, + ListDistributionsCommand, + GetDistributionCommand, + UpdateDistributionCommand +} from '@aws-sdk/client-cloudfront' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class CloudFrontViewerPolicyHTTPS implements BPSet { + private readonly client = new CloudFrontClient({}) + private readonly memoClient = Memorizer.memo(this.client) + + private readonly getDistributions = async () => { + const response = await this.memoClient.send(new ListDistributionsCommand({})) + return response.DistributionList?.Items || [] + } + + private readonly getDistributionDetails = async (distributionId: string) => { + const response = await this.memoClient.send( + new GetDistributionCommand({ Id: distributionId }) + ) + return { + distribution: response.Distribution!, + etag: response.ETag! + } + } + + public readonly check = async () => { + const compliantResources = [] + const nonCompliantResources = [] + const distributions = await this.getDistributions() + + for (const distribution of distributions) { + const { distribution: details } = await this.getDistributionDetails(distribution.Id!) + const hasNonCompliantViewerPolicy = + details.DistributionConfig?.DefaultCacheBehavior?.ViewerProtocolPolicy === 'allow-all' || + details.DistributionConfig?.CacheBehaviors?.Items?.some( + behavior => behavior.ViewerProtocolPolicy === 'allow-all' + ) + + if (hasNonCompliantViewerPolicy) { + nonCompliantResources.push(details.ARN!) + } else { + compliantResources.push(details.ARN!) + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [] + } + } + + public readonly fix = async (nonCompliantResources: string[]) => { + for (const arn of nonCompliantResources) { + const distributionId = arn.split('/').pop()! + const { distribution, etag } = await this.getDistributionDetails(distributionId) + + const updatedConfig = { + ...distribution.DistributionConfig, + DefaultCacheBehavior: { + ...distribution.DistributionConfig?.DefaultCacheBehavior, + ViewerProtocolPolicy: 'redirect-to-https' + }, + CacheBehaviors: { + Items: distribution.DistributionConfig?.CacheBehaviors?.Items?.map(behavior => ({ + ...behavior, + ViewerProtocolPolicy: 'redirect-to-https' + })) + } + } + + await this.client.send( + new UpdateDistributionCommand({ + Id: distributionId, + IfMatch: etag, + DistributionConfig: updatedConfig as any + }) + ) + } + } +} diff --git a/src/bpsets/lambda/LambdaDLQCheck.ts b/src/bpsets/lambda/LambdaDLQCheck.ts new file mode 100644 index 0000000..2c246e1 --- /dev/null +++ b/src/bpsets/lambda/LambdaDLQCheck.ts @@ -0,0 +1,58 @@ +import { LambdaClient, ListFunctionsCommand, UpdateFunctionConfigurationCommand } from '@aws-sdk/client-lambda' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class LambdaDLQCheck implements BPSet { + private readonly client = new LambdaClient({}) + private readonly memoClient = Memorizer.memo(this.client) + + private readonly getFunctions = async () => { + const response = await this.memoClient.send(new ListFunctionsCommand({})) + return response.Functions || [] + } + + public readonly check = async (): Promise<{ + compliantResources: string[] + nonCompliantResources: string[] + requiredParametersForFix: { name: string }[] + }> => { + const compliantResources: string[] = [] + const nonCompliantResources: string[] = [] + const functions = await this.getFunctions() + + for (const func of functions) { + if (func.DeadLetterConfig) { + compliantResources.push(func.FunctionArn!) + } else { + nonCompliantResources.push(func.FunctionArn!) + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [{ name: 'dlq-arn' }] + } + } + + public readonly fix = async ( + nonCompliantResources: string[], + requiredParametersForFix: { name: string; value: string }[] + ): Promise => { + const dlqArn = requiredParametersForFix.find(param => param.name === 'dlq-arn')?.value + + if (!dlqArn) { + throw new Error("Required parameter 'dlq-arn' is missing.") + } + + for (const functionArn of nonCompliantResources) { + const functionName = functionArn.split(':').pop()! + await this.client.send( + new UpdateFunctionConfigurationCommand({ + FunctionName: functionName, + DeadLetterConfig: { TargetArn: dlqArn } + }) + ) + } + } +} diff --git a/src/bpsets/lambda/LambdaFunctionPublicAccessProhibited.ts b/src/bpsets/lambda/LambdaFunctionPublicAccessProhibited.ts new file mode 100644 index 0000000..b1e1593 --- /dev/null +++ b/src/bpsets/lambda/LambdaFunctionPublicAccessProhibited.ts @@ -0,0 +1,86 @@ +import { + LambdaClient, + ListFunctionsCommand, + GetPolicyCommand, + RemovePermissionCommand +} from '@aws-sdk/client-lambda' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class LambdaFunctionPublicAccessProhibited implements BPSet { + private readonly client = new LambdaClient({}) + private readonly memoClient = Memorizer.memo(this.client) + + private readonly getFunctions = async () => { + const response = await this.memoClient.send(new ListFunctionsCommand({})) + return response.Functions || [] + } + + public readonly check = async (): Promise<{ + compliantResources: string[] + nonCompliantResources: string[] + requiredParametersForFix: { name: string }[] + }> => { + const compliantResources: string[] = [] + const nonCompliantResources: string[] = [] + const functions = await this.getFunctions() + + for (const func of functions) { + try { + const response = await this.memoClient.send(new GetPolicyCommand({ FunctionName: func.FunctionName! })) + const policy = JSON.parse(response.Policy!) + + const hasPublicAccess = policy.Statement.some( + (statement: any) => statement.Principal === '*' || statement.Principal?.AWS === '*' + ) + + if (hasPublicAccess) { + nonCompliantResources.push(func.FunctionArn!) + } else { + compliantResources.push(func.FunctionArn!) + } + } catch (error) { + if ((error as any).name === 'ResourceNotFoundException') { + nonCompliantResources.push(func.FunctionArn!) + } else { + throw error + } + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [] + } + } + + public readonly fix = async ( + nonCompliantResources: string[], + requiredParametersForFix: { name: string; value: string }[] + ): Promise => { + for (const functionArn of nonCompliantResources) { + const functionName = functionArn.split(':').pop()! + + try { + const response = await this.memoClient.send(new GetPolicyCommand({ FunctionName: functionName })) + const policy = JSON.parse(response.Policy!) + + for (const statement of policy.Statement) { + if (statement.Principal === '*' || statement.Principal?.AWS === '*') { + await this.client.send( + new RemovePermissionCommand({ + FunctionName: functionName, + StatementId: statement.Sid // Use the actual StatementId from the policy + }) + ) + } + } + } catch (error) { + if ((error as any).name !== 'ResourceNotFoundException') { + throw error + } + } + } + } +} diff --git a/src/bpsets/lambda/LambdaFunctionSettingsCheck.ts b/src/bpsets/lambda/LambdaFunctionSettingsCheck.ts new file mode 100644 index 0000000..8bd96d6 --- /dev/null +++ b/src/bpsets/lambda/LambdaFunctionSettingsCheck.ts @@ -0,0 +1,65 @@ +import { LambdaClient, ListFunctionsCommand, UpdateFunctionConfigurationCommand } from '@aws-sdk/client-lambda' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class LambdaFunctionSettingsCheck implements BPSet { + private readonly client = new LambdaClient({}) + private readonly memoClient = Memorizer.memo(this.client) + + private readonly getFunctions = async () => { + const response = await this.memoClient.send(new ListFunctionsCommand({})) + return response.Functions || [] + } + + public readonly check = async (): Promise<{ + compliantResources: string[] + nonCompliantResources: string[] + requiredParametersForFix: { name: string }[] + }> => { + const compliantResources: string[] = [] + const nonCompliantResources: string[] = [] + const defaultTimeout = 3 + const defaultMemorySize = 128 + const functions = await this.getFunctions() + + for (const func of functions) { + if (func.Timeout === defaultTimeout || func.MemorySize === defaultMemorySize) { + nonCompliantResources.push(func.FunctionArn!) + } else { + compliantResources.push(func.FunctionArn!) + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [ + { name: 'timeout' }, + { name: 'memory-size' } + ] + } + } + + public readonly fix = async ( + nonCompliantResources: string[], + requiredParametersForFix: { name: string; value: string }[] + ): Promise => { + const timeout = requiredParametersForFix.find(param => param.name === 'timeout')?.value + const memorySize = requiredParametersForFix.find(param => param.name === 'memory-size')?.value + + if (!timeout || !memorySize) { + throw new Error("Required parameters 'timeout' and/or 'memory-size' are missing.") + } + + for (const functionArn of nonCompliantResources) { + const functionName = functionArn.split(':').pop()! + await this.client.send( + new UpdateFunctionConfigurationCommand({ + FunctionName: functionName, + Timeout: parseInt(timeout, 10), + MemorySize: parseInt(memorySize, 10) + }) + ) + } + } +} diff --git a/src/bpsets/lambda/LambdaInsideVPC.ts b/src/bpsets/lambda/LambdaInsideVPC.ts new file mode 100644 index 0000000..f58cba0 --- /dev/null +++ b/src/bpsets/lambda/LambdaInsideVPC.ts @@ -0,0 +1,69 @@ +import { + LambdaClient, + ListFunctionsCommand, + UpdateFunctionConfigurationCommand +} from '@aws-sdk/client-lambda' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class LambdaInsideVPC implements BPSet { + private readonly client = new LambdaClient({}) + private readonly memoClient = Memorizer.memo(this.client) + + private readonly getFunctions = async () => { + const response = await this.memoClient.send(new ListFunctionsCommand({})) + return response.Functions || [] + } + + public readonly check = async (): Promise<{ + compliantResources: string[] + nonCompliantResources: string[] + requiredParametersForFix: { name: string }[] + }> => { + const compliantResources: string[] = [] + const nonCompliantResources: string[] = [] + const functions = await this.getFunctions() + + for (const func of functions) { + if (func.VpcConfig && Object.keys(func.VpcConfig).length > 0) { + compliantResources.push(func.FunctionArn!) + } else { + nonCompliantResources.push(func.FunctionArn!) + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [ + { name: 'subnet-ids' }, + { name: 'security-group-ids' } + ] + } + } + + public readonly fix = async ( + nonCompliantResources: string[], + requiredParametersForFix: { name: string; value: string }[] + ): Promise => { + const subnetIds = requiredParametersForFix.find(param => param.name === 'subnet-ids')?.value + const securityGroupIds = requiredParametersForFix.find(param => param.name === 'security-group-ids')?.value + + if (!subnetIds || !securityGroupIds) { + throw new Error("Required parameters 'subnet-ids' and/or 'security-group-ids' are missing.") + } + + for (const functionArn of nonCompliantResources) { + const functionName = functionArn.split(':').pop()! + await this.client.send( + new UpdateFunctionConfigurationCommand({ + FunctionName: functionName, + VpcConfig: { + SubnetIds: subnetIds.split(','), + SecurityGroupIds: securityGroupIds.split(',') + } + }) + ) + } + } +} diff --git a/src/bpsets/s3/S3AccessPointInVpcOnly.ts b/src/bpsets/s3/S3AccessPointInVpcOnly.ts new file mode 100644 index 0000000..5454322 --- /dev/null +++ b/src/bpsets/s3/S3AccessPointInVpcOnly.ts @@ -0,0 +1,81 @@ +import { + S3ControlClient, + ListAccessPointsCommand, + DeleteAccessPointCommand, + CreateAccessPointCommand +} from '@aws-sdk/client-s3-control' +import { STSClient, GetCallerIdentityCommand } from '@aws-sdk/client-sts' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class S3AccessPointInVpcOnly implements BPSet { + private readonly client = new S3ControlClient({}) + private readonly memoClient = Memorizer.memo(this.client) + private readonly stsClient = Memorizer.memo(new STSClient({})) + + private readonly getAccountId = async (): Promise => { + const response = await this.stsClient.send(new GetCallerIdentityCommand({})) + return response.Account! + } + + public readonly check = async (): Promise<{ + compliantResources: string[] + nonCompliantResources: string[] + requiredParametersForFix: { name: string }[] + }> => { + const compliantResources: string[] = [] + const nonCompliantResources: string[] = [] + const requiredParametersForFix = [{ name: 'your-vpc-id' }] + + const accountId = await this.getAccountId() + + const response = await this.memoClient.send( + new ListAccessPointsCommand({ AccountId: accountId }) + ) + + for (const accessPoint of response.AccessPointList || []) { + if (accessPoint.NetworkOrigin === 'VPC') { + compliantResources.push(accessPoint.AccessPointArn!) + } else { + nonCompliantResources.push(accessPoint.AccessPointArn!) + } + } + + return { compliantResources, nonCompliantResources, requiredParametersForFix } + } + + public readonly fix = async ( + nonCompliantResources: string[], + requiredParametersForFix: { name: string; value: string }[] + ): Promise => { + const accountId = await this.getAccountId() + const vpcId = requiredParametersForFix.find(param => param.name === 'your-vpc-id')?.value + + if (!vpcId) { + throw new Error("Required parameter 'your-vpc-id' is missing.") + } + + for (const accessPointArn of nonCompliantResources) { + const accessPointName = accessPointArn.split(':').pop()! + const bucketName = accessPointArn.split('/')[1]! + + await this.client.send( + new DeleteAccessPointCommand({ + AccountId: accountId, + Name: accessPointName + }) + ) + + await this.client.send( + new CreateAccessPointCommand({ + AccountId: accountId, + Name: accessPointName, + Bucket: bucketName, + VpcConfiguration: { + VpcId: vpcId + } + }) + ) + } + } +} diff --git a/src/bpsets/s3/S3BucketDefaultLockEnabled.ts b/src/bpsets/s3/S3BucketDefaultLockEnabled.ts new file mode 100644 index 0000000..d75fa2a --- /dev/null +++ b/src/bpsets/s3/S3BucketDefaultLockEnabled.ts @@ -0,0 +1,72 @@ +import { + S3Client, + ListBucketsCommand, + GetObjectLockConfigurationCommand, + PutObjectLockConfigurationCommand +} from '@aws-sdk/client-s3' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class S3BucketDefaultLockEnabled implements BPSet { + private readonly client = new S3Client({}) + private readonly memoClient = Memorizer.memo(this.client) + + private readonly getBuckets = async () => { + const response = await this.memoClient.send(new ListBucketsCommand({})) + return response.Buckets || [] + } + + public readonly check = async (): Promise<{ + compliantResources: string[] + nonCompliantResources: string[] + requiredParametersForFix: { name: string }[] + }> => { + const compliantResources: string[] = [] + const nonCompliantResources: string[] = [] + const buckets = await this.getBuckets() + + for (const bucket of buckets) { + try { + await this.memoClient.send( + new GetObjectLockConfigurationCommand({ Bucket: bucket.Name! }) + ) + compliantResources.push(`arn:aws:s3:::${bucket.Name!}`) + } catch (error) { + if ((error as any).name === 'ObjectLockConfigurationNotFoundError') { + nonCompliantResources.push(`arn:aws:s3:::${bucket.Name!}`) + } else { + throw error + } + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [] + } + } + + public readonly fix = async ( + nonCompliantResources: string[], + requiredParametersForFix: { name: string; value: string }[] + ): Promise => { + for (const bucketArn of nonCompliantResources) { + const bucketName = bucketArn.split(':::')[1]! + await this.client.send( + new PutObjectLockConfigurationCommand({ + Bucket: bucketName, + ObjectLockConfiguration: { + ObjectLockEnabled: 'Enabled', + Rule: { + DefaultRetention: { + Mode: 'GOVERNANCE', + Days: 365 + } + } + } + }) + ) + } + } +} diff --git a/src/bpsets/s3/S3BucketLevelPublicAccessProhibited.ts b/src/bpsets/s3/S3BucketLevelPublicAccessProhibited.ts new file mode 100644 index 0000000..4020a2f --- /dev/null +++ b/src/bpsets/s3/S3BucketLevelPublicAccessProhibited.ts @@ -0,0 +1,75 @@ +import { + S3Client, + ListBucketsCommand, + GetPublicAccessBlockCommand, + PutPublicAccessBlockCommand +} from '@aws-sdk/client-s3' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class S3BucketLevelPublicAccessProhibited implements BPSet { + private readonly client = new S3Client({}) + private readonly memoClient = Memorizer.memo(this.client) + + private readonly getBuckets = async () => { + const response = await this.memoClient.send(new ListBucketsCommand({})) + return response.Buckets || [] + } + + public readonly check = async (): Promise<{ + compliantResources: string[] + nonCompliantResources: string[] + requiredParametersForFix: { name: string }[] + }> => { + const compliantResources: string[] = [] + const nonCompliantResources: string[] = [] + const buckets = await this.getBuckets() + + for (const bucket of buckets) { + try { + const response = await this.memoClient.send( + new GetPublicAccessBlockCommand({ Bucket: bucket.Name! }) + ) + const config = response.PublicAccessBlockConfiguration + if ( + config?.BlockPublicAcls && + config?.IgnorePublicAcls && + config?.BlockPublicPolicy && + config?.RestrictPublicBuckets + ) { + compliantResources.push(`arn:aws:s3:::${bucket.Name!}`) + } else { + nonCompliantResources.push(`arn:aws:s3:::${bucket.Name!}`) + } + } catch (error) { + nonCompliantResources.push(`arn:aws:s3:::${bucket.Name!}`) + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [] + } + } + + public readonly fix = async ( + nonCompliantResources: string[], + requiredParametersForFix: { name: string; value: string }[] + ): Promise => { + for (const bucketArn of nonCompliantResources) { + const bucketName = bucketArn.split(':::')[1]! + await this.client.send( + new PutPublicAccessBlockCommand({ + Bucket: bucketName, + PublicAccessBlockConfiguration: { + BlockPublicAcls: true, + IgnorePublicAcls: true, + BlockPublicPolicy: true, + RestrictPublicBuckets: true + } + }) + ) + } + } +} diff --git a/src/bpsets/s3/S3BucketLoggingEnabled.ts b/src/bpsets/s3/S3BucketLoggingEnabled.ts new file mode 100644 index 0000000..c48a29d --- /dev/null +++ b/src/bpsets/s3/S3BucketLoggingEnabled.ts @@ -0,0 +1,73 @@ +import { + S3Client, + ListBucketsCommand, + GetBucketLoggingCommand, + PutBucketLoggingCommand +} from '@aws-sdk/client-s3' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class S3BucketLoggingEnabled implements BPSet { + private readonly client = new S3Client({}) + private readonly memoClient = Memorizer.memo(this.client) + + private readonly getBuckets = async () => { + const response = await this.memoClient.send(new ListBucketsCommand({})) + return response.Buckets || [] + } + + public readonly check = async (): Promise<{ + compliantResources: string[] + nonCompliantResources: string[] + requiredParametersForFix: { name: string }[] + }> => { + const compliantResources: string[] = [] + const nonCompliantResources: string[] = [] + const buckets = await this.getBuckets() + + for (const bucket of buckets) { + const response = await this.memoClient.send( + new GetBucketLoggingCommand({ Bucket: bucket.Name! }) + ) + if (response.LoggingEnabled) { + compliantResources.push(`arn:aws:s3:::${bucket.Name!}`) + } else { + nonCompliantResources.push(`arn:aws:s3:::${bucket.Name!}`) + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [{ name: 'log-destination-bucket' }] + } + } + + public readonly fix = async ( + nonCompliantResources: string[], + requiredParametersForFix: { name: string; value: string }[] + ): Promise => { + const logDestinationBucket = requiredParametersForFix.find( + param => param.name === 'log-destination-bucket' + )?.value + + if (!logDestinationBucket) { + throw new Error("Required parameter 'log-destination-bucket' is missing.") + } + + for (const bucketArn of nonCompliantResources) { + const bucketName = bucketArn.split(':::')[1]! + await this.client.send( + new PutBucketLoggingCommand({ + Bucket: bucketName, + BucketLoggingStatus: { + LoggingEnabled: { + TargetBucket: logDestinationBucket, + TargetPrefix: `${bucketName}/logs/` + } + } + }) + ) + } + } +} diff --git a/src/bpsets/s3/S3BucketSSLRequestsOnly.ts b/src/bpsets/s3/S3BucketSSLRequestsOnly.ts new file mode 100644 index 0000000..b27b6bf --- /dev/null +++ b/src/bpsets/s3/S3BucketSSLRequestsOnly.ts @@ -0,0 +1,130 @@ +import { + S3Client, + ListBucketsCommand, + GetBucketPolicyCommand, + PutBucketPolicyCommand +} from '@aws-sdk/client-s3' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class S3BucketSSLRequestsOnly implements BPSet { + private readonly client = new S3Client({}) + private readonly memoClient = Memorizer.memo(this.client) + + private readonly getBuckets = async () => { + const response = await this.memoClient.send(new ListBucketsCommand({})) + return response.Buckets || [] + } + + private readonly createSSLOnlyPolicy = (bucketName: string): string => { + return JSON.stringify({ + Version: '2012-10-17', + Statement: [ + { + Sid: 'DenyNonSSLRequests', + Effect: 'Deny', + Principal: '*', + Action: 's3:*', + Resource: [`arn:aws:s3:::${bucketName}/*`, `arn:aws:s3:::${bucketName}`], + Condition: { + Bool: { + 'aws:SecureTransport': 'false' + } + } + } + ] + }) + } + + public readonly check = async (): Promise<{ + compliantResources: string[] + nonCompliantResources: string[] + requiredParametersForFix: { name: string }[] + }> => { + const compliantResources: string[] = [] + const nonCompliantResources: string[] = [] + const buckets = await this.getBuckets() + + for (const bucket of buckets) { + try { + const response = await this.memoClient.send( + new GetBucketPolicyCommand({ Bucket: bucket.Name! }) + ) + const policy = JSON.parse(response.Policy!) + const hasSSLCondition = policy.Statement.some( + (stmt: any) => + stmt.Condition && + stmt.Condition.Bool && + stmt.Condition.Bool['aws:SecureTransport'] === 'false' + ) + + if (hasSSLCondition) { + compliantResources.push(`arn:aws:s3:::${bucket.Name!}`) + } else { + nonCompliantResources.push(`arn:aws:s3:::${bucket.Name!}`) + } + } catch (error) { + if ((error as any).name === 'NoSuchBucketPolicy') { + nonCompliantResources.push(`arn:aws:s3:::${bucket.Name!}`) + } else { + throw error + } + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [] + } + } + + public readonly fix = async ( + nonCompliantResources: string[], + requiredParametersForFix: { name: string; value: string }[] + ): Promise => { + for (const bucketArn of nonCompliantResources) { + const bucketName = bucketArn.split(':::')[1]! + let existingPolicy: any + + try { + const response = await this.memoClient.send( + new GetBucketPolicyCommand({ Bucket: bucketName }) + ) + existingPolicy = JSON.parse(response.Policy!) + } catch (error) { + if ((error as any).name !== 'NoSuchBucketPolicy') { + throw error + } + } + + const sslPolicyStatement = { + Sid: 'DenyNonSSLRequests', + Effect: 'Deny', + Principal: '*', + Action: 's3:*', + Resource: [`arn:aws:s3:::${bucketName}/*`, `arn:aws:s3:::${bucketName}`], + Condition: { + Bool: { + 'aws:SecureTransport': 'false' + } + } + } + + let updatedPolicy + if (existingPolicy) { + existingPolicy.Statement.push(sslPolicyStatement) + updatedPolicy = JSON.stringify(existingPolicy) + } else { + updatedPolicy = this.createSSLOnlyPolicy(bucketName) + } + + await this.client.send( + new PutBucketPolicyCommand({ + Bucket: bucketName, + Policy: updatedPolicy + }) + ) + } + } +} diff --git a/src/bpsets/s3/S3BucketVersioningEnabled.ts b/src/bpsets/s3/S3BucketVersioningEnabled.ts new file mode 100644 index 0000000..e9bf8a1 --- /dev/null +++ b/src/bpsets/s3/S3BucketVersioningEnabled.ts @@ -0,0 +1,62 @@ +import { + S3Client, + ListBucketsCommand, + GetBucketVersioningCommand, + PutBucketVersioningCommand +} from '@aws-sdk/client-s3' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class S3BucketVersioningEnabled implements BPSet { + private readonly client = new S3Client({}) + private readonly memoClient = Memorizer.memo(this.client) + + private readonly getBuckets = async () => { + const response = await this.memoClient.send(new ListBucketsCommand({})) + return response.Buckets || [] + } + + public readonly check = async (): Promise<{ + compliantResources: string[] + nonCompliantResources: string[] + requiredParametersForFix: { name: string }[] + }> => { + const compliantResources: string[] = [] + const nonCompliantResources: string[] = [] + const buckets = await this.getBuckets() + + for (const bucket of buckets) { + const response = await this.memoClient.send( + new GetBucketVersioningCommand({ Bucket: bucket.Name! }) + ) + if (response.Status === 'Enabled') { + compliantResources.push(`arn:aws:s3:::${bucket.Name!}`) + } else { + nonCompliantResources.push(`arn:aws:s3:::${bucket.Name!}`) + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [] + } + } + + public readonly fix = async ( + nonCompliantResources: string[], + requiredParametersForFix: { name: string; value: string }[] + ): Promise => { + for (const bucketArn of nonCompliantResources) { + const bucketName = bucketArn.split(':::')[1]! + await this.client.send( + new PutBucketVersioningCommand({ + Bucket: bucketName, + VersioningConfiguration: { + Status: 'Enabled' + } + }) + ) + } + } +} diff --git a/src/bpsets/s3/S3DefaultEncryptionKMS.ts b/src/bpsets/s3/S3DefaultEncryptionKMS.ts new file mode 100644 index 0000000..93da7ef --- /dev/null +++ b/src/bpsets/s3/S3DefaultEncryptionKMS.ts @@ -0,0 +1,90 @@ +import { + S3Client, + ListBucketsCommand, + GetBucketEncryptionCommand, + PutBucketEncryptionCommand +} from '@aws-sdk/client-s3' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class S3DefaultEncryptionKMS implements BPSet { + private readonly client = new S3Client({}) + private readonly memoClient = Memorizer.memo(this.client) + + private readonly getBuckets = async () => { + const response = await this.memoClient.send(new ListBucketsCommand({})) + return response.Buckets || [] + } + + public readonly check = async (): Promise<{ + compliantResources: string[] + nonCompliantResources: string[] + requiredParametersForFix: { name: string }[] + }> => { + const compliantResources: string[] = [] + const nonCompliantResources: string[] = [] + const buckets = await this.getBuckets() + + for (const bucket of buckets) { + try { + const response = await this.memoClient.send( + new GetBucketEncryptionCommand({ Bucket: bucket.Name! }) + ) + const encryption = response.ServerSideEncryptionConfiguration! + const isKmsEnabled = encryption.Rules?.some( + rule => + rule.ApplyServerSideEncryptionByDefault && + rule.ApplyServerSideEncryptionByDefault.SSEAlgorithm === 'aws:kms' + ) + + if (isKmsEnabled) { + compliantResources.push(`arn:aws:s3:::${bucket.Name!}`) + } else { + nonCompliantResources.push(`arn:aws:s3:::${bucket.Name!}`) + } + } catch (error) { + if ((error as any).name === 'ServerSideEncryptionConfigurationNotFoundError') { + nonCompliantResources.push(`arn:aws:s3:::${bucket.Name!}`) + } else { + throw error + } + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [{ name: 'kms-key-id' }] + } + } + + public readonly fix = async ( + nonCompliantResources: string[], + requiredParametersForFix: { name: string; value: string }[] + ): Promise => { + const kmsKeyId = requiredParametersForFix.find(param => param.name === 'kms-key-id')?.value + + if (!kmsKeyId) { + throw new Error("Required parameter 'kms-key-id' is missing.") + } + + for (const bucketArn of nonCompliantResources) { + const bucketName = bucketArn.split(':::')[1]! + await this.client.send( + new PutBucketEncryptionCommand({ + Bucket: bucketName, + ServerSideEncryptionConfiguration: { + Rules: [ + { + ApplyServerSideEncryptionByDefault: { + SSEAlgorithm: 'aws:kms', + KMSMasterKeyID: kmsKeyId + } + } + ] + } + }) + ) + } + } +} diff --git a/src/bpsets/s3/S3EventNotificationsEnabled.ts b/src/bpsets/s3/S3EventNotificationsEnabled.ts new file mode 100644 index 0000000..56ce14e --- /dev/null +++ b/src/bpsets/s3/S3EventNotificationsEnabled.ts @@ -0,0 +1,87 @@ +import { + S3Client, + ListBucketsCommand, + GetBucketNotificationConfigurationCommand, + PutBucketNotificationConfigurationCommand +} from '@aws-sdk/client-s3' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class S3EventNotificationsEnabled implements BPSet { + private readonly client = new S3Client({}) + private readonly memoClient = Memorizer.memo(this.client) + + private readonly getBuckets = async () => { + const response = await this.memoClient.send(new ListBucketsCommand({})) + return response.Buckets || [] + } + + public readonly check = async (): Promise<{ + compliantResources: string[] + nonCompliantResources: string[] + requiredParametersForFix: { name: string }[] + }> => { + const compliantResources: string[] = [] + const nonCompliantResources: string[] = [] + const buckets = await this.getBuckets() + + for (const bucket of buckets) { + const response = await this.memoClient.send( + new GetBucketNotificationConfigurationCommand({ Bucket: bucket.Name! }) + ) + if ( + response.LambdaFunctionConfigurations || + response.QueueConfigurations || + response.TopicConfigurations + ) { + compliantResources.push(`arn:aws:s3:::${bucket.Name!}`) + } else { + nonCompliantResources.push(`arn:aws:s3:::${bucket.Name!}`) + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [ + { name: 'lambda-function-arn' }, + { name: 'event-type' } + ] + } + } + + public readonly fix = async ( + nonCompliantResources: string[], + requiredParametersForFix: { name: string; value: string }[] + ): Promise => { + const lambdaArn = requiredParametersForFix.find( + param => param.name === 'lambda-function-arn' + )?.value + const eventType = requiredParametersForFix.find( + param => param.name === 'event-type' + )?.value + + if (!lambdaArn || !eventType) { + throw new Error( + "Required parameters 'lambda-function-arn' and/or 'event-type' are missing." + ) + } + + for (const bucketArn of nonCompliantResources) { + const bucketName = bucketArn.split(':::')[1]! + await this.client.send( + new PutBucketNotificationConfigurationCommand({ + Bucket: bucketName, + NotificationConfiguration: { + LambdaFunctionConfigurations: [ + { + LambdaFunctionArn: lambdaArn, + Events: [eventType as any] + } + ] + } + }) + ) + } + } +} diff --git a/src/bpsets/s3/S3LastBackupRecoveryPointCreated.ts b/src/bpsets/s3/S3LastBackupRecoveryPointCreated.ts new file mode 100644 index 0000000..e58ff62 --- /dev/null +++ b/src/bpsets/s3/S3LastBackupRecoveryPointCreated.ts @@ -0,0 +1,52 @@ +import { + S3Client, + ListBucketsCommand +} from '@aws-sdk/client-s3' +import { BackupClient, ListRecoveryPointsByResourceCommand } from '@aws-sdk/client-backup' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class S3LastBackupRecoveryPointCreated implements BPSet { + private readonly client = new S3Client({}) + private readonly memoClient = Memorizer.memo(this.client) + private readonly backupClient = Memorizer.memo(new BackupClient({})) + + private readonly getBuckets = async () => { + const response = await this.memoClient.send(new ListBucketsCommand({})) + return response.Buckets || [] + } + + public readonly check = async (): Promise<{ + compliantResources: string[] + nonCompliantResources: string[] + requiredParametersForFix: { name: string }[] + }> => { + const compliantResources: string[] = [] + const nonCompliantResources: string[] = [] + const buckets = await this.getBuckets() + + for (const bucket of buckets) { + const recoveryPoints = await this.memoClient.send( + new ListRecoveryPointsByResourceCommand({ + ResourceArn: `arn:aws:s3:::${bucket.Name!}` + }) + ) + + if (recoveryPoints.RecoveryPoints && recoveryPoints.RecoveryPoints.length > 0) { + compliantResources.push(`arn:aws:s3:::${bucket.Name!}`) + } else { + nonCompliantResources.push(`arn:aws:s3:::${bucket.Name!}`) + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [] + } + } + + public readonly fix = async (): Promise => { + throw new Error('Fixing recovery points requires custom implementation for backup setup.') + } +} diff --git a/src/bpsets/s3/S3LifecyclePolicyCheck.ts b/src/bpsets/s3/S3LifecyclePolicyCheck.ts new file mode 100644 index 0000000..47725da --- /dev/null +++ b/src/bpsets/s3/S3LifecyclePolicyCheck.ts @@ -0,0 +1,90 @@ +import { + S3Client, + ListBucketsCommand, + GetBucketLifecycleConfigurationCommand, + PutBucketLifecycleConfigurationCommand +} from '@aws-sdk/client-s3' +import { BPSet } from '../BPSet' +import { Memorizer } from '../../Memorizer' + +export class S3LifecyclePolicyCheck implements BPSet { + private readonly client = new S3Client({}) + private readonly memoClient = Memorizer.memo(this.client) + + private readonly getBuckets = async () => { + const response = await this.memoClient.send(new ListBucketsCommand({})) + return response.Buckets || [] + } + + public readonly check = async (): Promise<{ + compliantResources: string[] + nonCompliantResources: string[] + requiredParametersForFix: { name: string }[] + }> => { + const compliantResources: string[] = [] + const nonCompliantResources: string[] = [] + const buckets = await this.getBuckets() + + for (const bucket of buckets) { + try { + await this.memoClient.send( + new GetBucketLifecycleConfigurationCommand({ Bucket: bucket.Name! }) + ) + compliantResources.push(`arn:aws:s3:::${bucket.Name!}`) + } catch (error) { + if ((error as any).name === 'NoSuchLifecycleConfiguration') { + nonCompliantResources.push(`arn:aws:s3:::${bucket.Name!}`) + } else { + throw error + } + } + } + + return { + compliantResources, + nonCompliantResources, + requiredParametersForFix: [ + { name: 'lifecycle-policy-rule-id' }, + { name: 'expiration-days' } + ] + } + } + + public readonly fix = async ( + nonCompliantResources: string[], + requiredParametersForFix: { name: string; value: string }[] + ): Promise => { + const ruleId = requiredParametersForFix.find( + param => param.name === 'lifecycle-policy-rule-id' + )?.value + const expirationDays = requiredParametersForFix.find( + param => param.name === 'expiration-days' + )?.value + + if (!ruleId || !expirationDays) { + throw new Error( + "Required parameters 'lifecycle-policy-rule-id' and/or 'expiration-days' are missing." + ) + } + + for (const bucketArn of nonCompliantResources) { + const bucketName = bucketArn.split(':::')[1]! + await this.client.send( + new PutBucketLifecycleConfigurationCommand({ + Bucket: bucketName, + LifecycleConfiguration: { + Rules: [ + { + ID: ruleId, + Status: 'Enabled', + Expiration: { + Days: parseInt(expirationDays, 10) + } + } + ] + } + }) + ) + } + } +} diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..334cd08 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,9 @@ +import { S3BucketVersioningEnabled } from "./bpsets/s3/S3BucketVersioningEnabled"; + +new S3BucketVersioningEnabled() + .check() + .then(({ nonCompliantResources }) => { + new S3BucketVersioningEnabled() + .fix(nonCompliantResources, []) + .then(() => console.log('Done')) + }) diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..609326f --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2016", + "module": "CommonJS", + "outDir": "./dist", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true, + "experimentalDecorators": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules/**/*"] +}